mirror of
https://github.com/coracle-social/coracle.git
synced 2024-09-18 19:23:40 +00:00
Add relay reviews
This commit is contained in:
parent
a83a5d25be
commit
dd07b0480a
@ -4,9 +4,8 @@
|
|||||||
- [ ] Use vida to stream development
|
- [ ] Use vida to stream development
|
||||||
- [ ] Fix connection management stuff. Have GPT help
|
- [ ] Fix connection management stuff. Have GPT help
|
||||||
- [ ] Relay reviews
|
- [ ] Relay reviews
|
||||||
- New kind, d as url? Combine with labeling?
|
|
||||||
- Show reviews in relay detail rather than events. Warn about events tab
|
|
||||||
- Add curated relay list, and an easy way to view content on the relays
|
- Add curated relay list, and an easy way to view content on the relays
|
||||||
|
- Deploy ontology.coracle.social
|
||||||
- [ ] Add preview proxy thing
|
- [ ] Add preview proxy thing
|
||||||
- [ ] White-labeled
|
- [ ] White-labeled
|
||||||
- [ ] Add invite code registration for relay
|
- [ ] Add invite code registration for relay
|
||||||
@ -71,6 +70,10 @@
|
|||||||
- [ ] NIP 39 Support https://github.com/nostr-protocol/nips/pull/201/files
|
- [ ] NIP 39 Support https://github.com/nostr-protocol/nips/pull/201/files
|
||||||
- [ ] Integrate plephy https://plebhy.com/
|
- [ ] Integrate plephy https://plebhy.com/
|
||||||
- [ ] Add support for website kind0
|
- [ ] Add support for website kind0
|
||||||
|
- [ ] Schedule notes for x seconds in the future with a queue
|
||||||
|
- In the queue, add send now and cancel buttons
|
||||||
|
- maybe put all user events here too - keep track of what relays they were published to
|
||||||
|
- Add un-delete using event log
|
||||||
|
|
||||||
# UI/Features
|
# UI/Features
|
||||||
|
|
||||||
|
@ -84,6 +84,8 @@ const requestZap = (relays, content, pubkey, eventId, amount, lnurl) => {
|
|||||||
|
|
||||||
const deleteEvent = ids => new PublishableEvent(5, {tags: ids.map(id => ["e", id])})
|
const deleteEvent = ids => new PublishableEvent(5, {tags: ids.map(id => ["e", id])})
|
||||||
|
|
||||||
|
const createLabel = payload => new PublishableEvent(1985, payload)
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
|
|
||||||
const tagsFromContent = (content, tags) => {
|
const tagsFromContent = (content, tags) => {
|
||||||
@ -158,7 +160,6 @@ class PublishableEvent {
|
|||||||
}
|
}
|
||||||
async publish(relays, onProgress = null, verb = "EVENT") {
|
async publish(relays, onProgress = null, verb = "EVENT") {
|
||||||
const event = await this.getSignedEvent()
|
const event = await this.getSignedEvent()
|
||||||
// console.log(event); return
|
|
||||||
const promise = pool.publish({relays, event, onProgress, verb})
|
const promise = pool.publish({relays, event, onProgress, verb})
|
||||||
|
|
||||||
// Copy the event since loki mutates it to add metadata
|
// Copy the event since loki mutates it to add metadata
|
||||||
@ -186,5 +187,6 @@ export default {
|
|||||||
createReply,
|
createReply,
|
||||||
requestZap,
|
requestZap,
|
||||||
deleteEvent,
|
deleteEvent,
|
||||||
|
createLabel,
|
||||||
PublishableEvent,
|
PublishableEvent,
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
import ListEdit from "src/app/views/ListEdit.svelte"
|
import ListEdit from "src/app/views/ListEdit.svelte"
|
||||||
import RelayAdd from "src/app/views/RelayAdd.svelte"
|
import RelayAdd from "src/app/views/RelayAdd.svelte"
|
||||||
import RelayDetail from "src/app/views/RelayDetail.svelte"
|
import RelayDetail from "src/app/views/RelayDetail.svelte"
|
||||||
|
import RelayReview from "src/app/views/RelayReview.svelte"
|
||||||
|
|
||||||
export let m
|
export let m
|
||||||
</script>
|
</script>
|
||||||
@ -41,6 +42,8 @@
|
|||||||
<RelayAdd url={m.url} />
|
<RelayAdd url={m.url} />
|
||||||
{:else if m.type === "relay/detail"}
|
{:else if m.type === "relay/detail"}
|
||||||
<RelayDetail url={m.url} />
|
<RelayDetail url={m.url} />
|
||||||
|
{:else if m.type === "relay/review"}
|
||||||
|
<RelayReview url={m.url} />
|
||||||
{:else if m.type === "onboarding"}
|
{:else if m.type === "onboarding"}
|
||||||
<Onboarding stage={m.stage} />
|
<Onboarding stage={m.stage} />
|
||||||
{:else if m.type === "room/edit"}
|
{:else if m.type === "room/edit"}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {onMount} from "svelte"
|
import {onMount} from "svelte"
|
||||||
import {last, partition, always, propEq, uniqBy, sortBy, prop} from "ramda"
|
import {last, partition, always, uniqBy, sortBy, prop} from "ramda"
|
||||||
import {fly} from "svelte/transition"
|
import {fly} from "svelte/transition"
|
||||||
import {quantify} from "hurdak/lib/hurdak"
|
import {quantify} from "hurdak/lib/hurdak"
|
||||||
import {createScroller, now, timedelta} from "src/util/misc"
|
import {createScroller, now, timedelta} from "src/util/misc"
|
||||||
@ -12,10 +12,11 @@
|
|||||||
import Note from "src/app/shared/Note.svelte"
|
import Note from "src/app/shared/Note.svelte"
|
||||||
import user from "src/agent/user"
|
import user from "src/agent/user"
|
||||||
import network from "src/agent/network"
|
import network from "src/agent/network"
|
||||||
|
import {getUserReadRelays} from "src/agent/relays"
|
||||||
import {mergeParents} from "src/app/state"
|
import {mergeParents} from "src/app/state"
|
||||||
|
|
||||||
export let filter
|
export let filter
|
||||||
export let relays = []
|
export let relays = getUserReadRelays()
|
||||||
export let delta = timedelta(6, "hours")
|
export let delta = timedelta(6, "hours")
|
||||||
export let shouldDisplay = always(true)
|
export let shouldDisplay = always(true)
|
||||||
export let parentsTimeout = 500
|
export let parentsTimeout = 500
|
||||||
@ -26,12 +27,12 @@
|
|||||||
let feedRelay = null
|
let feedRelay = null
|
||||||
let feedScroller = null
|
let feedScroller = null
|
||||||
|
|
||||||
// Add a short buffer so we can get the most possible results for recent notes
|
|
||||||
const since = now()
|
const since = now()
|
||||||
const maxNotes = 100
|
const maxNotes = 100
|
||||||
const seen = new Set()
|
const seen = new Set()
|
||||||
const cursor = new network.Cursor({relays, delta})
|
const cursor = new network.Cursor({relays, delta})
|
||||||
const getModal = () => last(document.querySelectorAll(".modal-content"))
|
const getModal = () => last(document.querySelectorAll(".modal-content"))
|
||||||
|
const canDisplay = e => [1, 1985].includes(e.kind)
|
||||||
|
|
||||||
const setFeedRelay = relay => {
|
const setFeedRelay = relay => {
|
||||||
feedRelay = relay
|
feedRelay = relay
|
||||||
@ -71,13 +72,13 @@
|
|||||||
// Combine notes and parents into a single collection
|
// Combine notes and parents into a single collection
|
||||||
const combined = uniqBy(
|
const combined = uniqBy(
|
||||||
prop("id"),
|
prop("id"),
|
||||||
filtered.filter(propEq("kind", 1)).concat(parents).map(asDisplayEvent)
|
filtered.filter(canDisplay).concat(parents).map(asDisplayEvent)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Stream in additional data and merge it in
|
// Stream in additional data and merge it in
|
||||||
network.streamContext({
|
network.streamContext({
|
||||||
maxDepth: 2,
|
maxDepth: 2,
|
||||||
notes: combined.filter(propEq("kind", 1)),
|
notes: combined.filter(canDisplay),
|
||||||
onChunk: context => {
|
onChunk: context => {
|
||||||
context = user.applyMutes(context)
|
context = user.applyMutes(context)
|
||||||
|
|
||||||
@ -129,7 +130,7 @@
|
|||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Content size="inherit" class="pt-6">
|
<Content size="inherit">
|
||||||
{#if notesBuffer.length > 0}
|
{#if notesBuffer.length > 0}
|
||||||
<div class="pointer-events-none fixed left-0 top-0 z-10 mt-20 flex w-full justify-center">
|
<div class="pointer-events-none fixed left-0 top-0 z-10 mt-20 flex w-full justify-center">
|
||||||
<button
|
<button
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {find, last, propEq} from "ramda"
|
import {find, last, propEq} from "ramda"
|
||||||
|
import {modal} from "src/partials/state"
|
||||||
import OverflowMenu from "src/partials/OverflowMenu.svelte"
|
import OverflowMenu from "src/partials/OverflowMenu.svelte"
|
||||||
import user from "src/agent/user"
|
import user from "src/agent/user"
|
||||||
import {getRelayWithFallback} from "src/agent/db"
|
import {getRelayWithFallback} from "src/agent/db"
|
||||||
@ -37,6 +38,12 @@
|
|||||||
label: "Add to list",
|
label: "Add to list",
|
||||||
icon: "scroll",
|
icon: "scroll",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
actions.push({
|
||||||
|
onClick: () => modal.push({type: "relay/review", url: relay.url}),
|
||||||
|
label: "Review",
|
||||||
|
icon: "feather",
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (relay.contact) {
|
if (relay.contact) {
|
||||||
|
@ -20,9 +20,10 @@
|
|||||||
["https://nostr.watch", "Nostr Watch", "A directory of nostr relays."],
|
["https://nostr.watch", "Nostr Watch", "A directory of nostr relays."],
|
||||||
["https://nostr.directory", "Nostr Directory", "Validate your nostr pubkey with Twitter."],
|
["https://nostr.directory", "Nostr Directory", "Validate your nostr pubkey with Twitter."],
|
||||||
["https://getalby.com", "Alby", "Sign in to nostr apps without sharing your private key."],
|
["https://getalby.com", "Alby", "Sign in to nostr apps without sharing your private key."],
|
||||||
["https://nak.nostr.com", "NAK", "A Nostr Army Knife, for advanced users."],
|
|
||||||
["https://nostrplebs.com", "NostrPlebs", "Get verified at nostrplebs.com."],
|
["https://nostrplebs.com", "NostrPlebs", "Get verified at nostrplebs.com."],
|
||||||
["https://nadar.tigerville.no", "Nadar", "Find out what relays know about your post."],
|
["https://nadar.tigerville.no", "Nadar", "Find out what relays know about your post."],
|
||||||
|
["https://pinstr.app", "Pinstr", "Create and manage collections of notes."],
|
||||||
|
["https://advancednostrsearch.vercel.app", "Advanced Search", "Find what you're looking for."],
|
||||||
]
|
]
|
||||||
|
|
||||||
document.title = "Apps"
|
document.title = "Apps"
|
||||||
|
@ -69,46 +69,44 @@
|
|||||||
</p>
|
</p>
|
||||||
</Content>
|
</Content>
|
||||||
{/if}
|
{/if}
|
||||||
<div>
|
<Tabs tabs={visibleTabs} activeTab={$feedsTab} {setActiveTab}>
|
||||||
<Tabs tabs={visibleTabs} activeTab={$feedsTab} {setActiveTab}>
|
{#if $canPublish}
|
||||||
{#if $canPublish}
|
{#if $lists.length > 0}
|
||||||
{#if $lists.length > 0}
|
<Popover placement="bottom" opts={{hideOnClick: true}} theme="transparent">
|
||||||
<Popover placement="bottom" opts={{hideOnClick: true}} theme="transparent">
|
<i slot="trigger" class="fa fa-ellipsis-v cursor-pointer p-2" />
|
||||||
<i slot="trigger" class="fa fa-ellipsis-v cursor-pointer p-2" />
|
<div
|
||||||
<div
|
slot="tooltip"
|
||||||
slot="tooltip"
|
class="flex flex-col items-start overflow-hidden rounded border border-solid border-gray-8 bg-black">
|
||||||
class="flex flex-col items-start overflow-hidden rounded border border-solid border-gray-8 bg-black">
|
{#each $lists as e (e.id)}
|
||||||
{#each $lists as e (e.id)}
|
{@const meta = Tags.from(e).asMeta()}
|
||||||
{@const meta = Tags.from(e).asMeta()}
|
|
||||||
<button
|
|
||||||
class={cx("w-full py-2 px-3 text-left transition-colors", {
|
|
||||||
"hover:bg-gray-7": $theme === "dark",
|
|
||||||
"hover:bg-gray-1": $theme === "light",
|
|
||||||
})}
|
|
||||||
on:click={() => {
|
|
||||||
$feedsTab = meta.d
|
|
||||||
}}>
|
|
||||||
<i class="fa fa-scroll fa-sm mr-1" />
|
|
||||||
{meta.d}
|
|
||||||
</button>
|
|
||||||
{/each}
|
|
||||||
<button
|
<button
|
||||||
on:click={showLists}
|
|
||||||
class={cx("w-full py-2 px-3 text-left transition-colors", {
|
class={cx("w-full py-2 px-3 text-left transition-colors", {
|
||||||
"hover:bg-gray-7": $theme === "dark",
|
"hover:bg-gray-7": $theme === "dark",
|
||||||
"hover:bg-gray-1": $theme === "light",
|
"hover:bg-gray-1": $theme === "light",
|
||||||
})}>
|
})}
|
||||||
<i class="fa fa-cog fa-sm mr-1" /> Customize
|
on:click={() => {
|
||||||
|
$feedsTab = meta.d
|
||||||
|
}}>
|
||||||
|
<i class="fa fa-scroll fa-sm mr-1" />
|
||||||
|
{meta.d}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
{/each}
|
||||||
</Popover>
|
<button
|
||||||
{:else}
|
on:click={showLists}
|
||||||
<i class="fa fa-ellipsis-v cursor-pointer p-1" on:click={showLists} />
|
class={cx("w-full py-2 px-3 text-left transition-colors", {
|
||||||
{/if}
|
"hover:bg-gray-7": $theme === "dark",
|
||||||
|
"hover:bg-gray-1": $theme === "light",
|
||||||
|
})}>
|
||||||
|
<i class="fa fa-cog fa-sm mr-1" /> Customize
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</Popover>
|
||||||
|
{:else}
|
||||||
|
<i class="fa fa-ellipsis-v cursor-pointer p-1" on:click={showLists} />
|
||||||
{/if}
|
{/if}
|
||||||
</Tabs>
|
{/if}
|
||||||
{#key $feedsTab}
|
</Tabs>
|
||||||
<Feed {relays} {filter} />
|
{#key $feedsTab}
|
||||||
{/key}
|
<Feed {relays} {filter} />
|
||||||
</div>
|
{/key}
|
||||||
</Content>
|
</Content>
|
||||||
|
@ -2,15 +2,22 @@
|
|||||||
import {displayRelay, normalizeRelayUrl} from "src/util/nostr"
|
import {displayRelay, normalizeRelayUrl} from "src/util/nostr"
|
||||||
import Content from "src/partials/Content.svelte"
|
import Content from "src/partials/Content.svelte"
|
||||||
import Feed from "src/app/shared/Feed.svelte"
|
import Feed from "src/app/shared/Feed.svelte"
|
||||||
|
import Tabs from "src/partials/Tabs.svelte"
|
||||||
import RelayTitle from "src/app/shared/RelayTitle.svelte"
|
import RelayTitle from "src/app/shared/RelayTitle.svelte"
|
||||||
import RelayActions from "src/app/shared/RelayActions.svelte"
|
import RelayActions from "src/app/shared/RelayActions.svelte"
|
||||||
import {relays} from "src/agent/db"
|
import {relays} from "src/agent/db"
|
||||||
|
|
||||||
export let url
|
export let url
|
||||||
|
|
||||||
|
let activeTab = "reviews"
|
||||||
|
|
||||||
url = normalizeRelayUrl(url)
|
url = normalizeRelayUrl(url)
|
||||||
|
|
||||||
const relay = relays.get(url) || {url}
|
const relay = relays.get(url) || {url}
|
||||||
|
const tabs = ["reviews", "notes"]
|
||||||
|
const setActiveTab = tab => {
|
||||||
|
activeTab = tab
|
||||||
|
}
|
||||||
|
|
||||||
document.title = displayRelay(relay)
|
document.title = displayRelay(relay)
|
||||||
</script>
|
</script>
|
||||||
@ -23,8 +30,17 @@
|
|||||||
{#if relay.description}
|
{#if relay.description}
|
||||||
<p>{relay.description}</p>
|
<p>{relay.description}</p>
|
||||||
{/if}
|
{/if}
|
||||||
</Content>
|
<Tabs borderClass="border-gray-6" {tabs} {activeTab} {setActiveTab} />
|
||||||
<div class="border-b border-solid border-gray-6" />
|
{#if activeTab === "reviews"}
|
||||||
<Content>
|
<Feed
|
||||||
<Feed relays={[relay]} filter={{kinds: [1]}} />
|
invertColors
|
||||||
|
filter={{
|
||||||
|
kinds: [1985],
|
||||||
|
"#l": ["review"],
|
||||||
|
"#L": ["social.coracle.ontology"],
|
||||||
|
"#r": [relay.url],
|
||||||
|
}} />
|
||||||
|
{:else}
|
||||||
|
<Feed invertColors relays={[relay]} filter={{kinds: [1]}} />
|
||||||
|
{/if}
|
||||||
</Content>
|
</Content>
|
||||||
|
48
src/app/views/RelayReview.svelte
Normal file
48
src/app/views/RelayReview.svelte
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {fly} from "svelte/transition"
|
||||||
|
import {modal} from "src/partials/state"
|
||||||
|
import Button from "src/partials/Button.svelte"
|
||||||
|
import Content from "src/partials/Content.svelte"
|
||||||
|
import Heading from "src/partials/Heading.svelte"
|
||||||
|
import Textarea from "src/partials/Textarea.svelte"
|
||||||
|
import Rating from "src/partials/Rating.svelte"
|
||||||
|
import user from "src/agent/user"
|
||||||
|
import cmd from "src/agent/cmd"
|
||||||
|
|
||||||
|
export let url
|
||||||
|
|
||||||
|
let review
|
||||||
|
let rating
|
||||||
|
|
||||||
|
const onSubmit = () => {
|
||||||
|
cmd
|
||||||
|
.createLabel({
|
||||||
|
content: review,
|
||||||
|
tagClient: false,
|
||||||
|
tags: [
|
||||||
|
["L", "social.coracle.ontology"],
|
||||||
|
["l", "review", "social.coracle.ontology"],
|
||||||
|
["r", url],
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.publish(user.getRelays())
|
||||||
|
|
||||||
|
modal.pop()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<form on:submit|preventDefault={onSubmit} in:fly={{y: 20}}>
|
||||||
|
<Content size="lg">
|
||||||
|
<Heading class="text-center">Leave a review</Heading>
|
||||||
|
<div class="flex w-full flex-col gap-4">
|
||||||
|
<div class="flex flex-col gap-1">
|
||||||
|
<div class="flex items-center justify-between gap-2">
|
||||||
|
<strong>Your rating:</strong>
|
||||||
|
<Rating bind:value={rating} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Textarea bind:value={review} placeholder="Share your thoughts..." />
|
||||||
|
<Button type="submit" class="flex-grow text-center">Send</Button>
|
||||||
|
</div>
|
||||||
|
</Content>
|
||||||
|
</form>
|
16
src/partials/Rating.svelte
Normal file
16
src/partials/Rating.svelte
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {range} from "hurdak/lib/hurdak"
|
||||||
|
|
||||||
|
export let value = 1
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex gap-1">
|
||||||
|
{#each range(0, 5) as x}
|
||||||
|
<i
|
||||||
|
class="fa fa-star cursor-pointer text-gray-5 hover:text-warning hover:opacity-75"
|
||||||
|
class:text-warning={value >= x / 5}
|
||||||
|
on:click={() => {
|
||||||
|
value = x / 5
|
||||||
|
}} />
|
||||||
|
{/each}
|
||||||
|
</div>
|
8
src/partials/Subheading.svelte
Normal file
8
src/partials/Subheading.svelte
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<script>
|
||||||
|
export let icon
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<i class={`fa {icon} fa-lg`} />
|
||||||
|
<h2 class="staatliches text-2xl"><slot /></h2>
|
||||||
|
</div>
|
@ -6,10 +6,11 @@
|
|||||||
export let activeTab
|
export let activeTab
|
||||||
export let setActiveTab
|
export let setActiveTab
|
||||||
export let getDisplay = tab => ({title: toTitle(tab), badge: null})
|
export let getDisplay = tab => ({title: toTitle(tab), badge: null})
|
||||||
|
export let borderClass = "border-gray-7"
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="flex items-center justify-between overflow-auto border-b border-solid border-gray-7 pt-2"
|
class={`flex items-center justify-between overflow-auto border-b border-solid pt-2 ${borderClass}`}
|
||||||
in:fly={{y: 20}}>
|
in:fly={{y: 20}}>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
{#each tabs as tab}
|
{#each tabs as tab}
|
||||||
|
Loading…
Reference in New Issue
Block a user