mirror of
https://github.com/coracle-social/coracle.git
synced 2024-09-29 08:21:20 +00:00
Add render support for feeds and new hud
This commit is contained in:
parent
55db5f174f
commit
d74790a4da
@ -1,7 +1,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {equals} from "ramda"
|
import {equals} from "ramda"
|
||||||
import {randomId} from "@welshman/lib"
|
import {seconds} from "hurdak"
|
||||||
|
import {randomId, now} from "@welshman/lib"
|
||||||
import {makeScopeFeed, Scope} from "@welshman/feeds"
|
import {makeScopeFeed, Scope} from "@welshman/feeds"
|
||||||
|
import {PublishStatus} from "@welshman/net"
|
||||||
import {fly} from "src/util/transition"
|
import {fly} from "src/util/transition"
|
||||||
import {toggleTheme, theme} from "src/partials/state"
|
import {toggleTheme, theme} from "src/partials/state"
|
||||||
import MenuItem from "src/partials/MenuItem.svelte"
|
import MenuItem from "src/partials/MenuItem.svelte"
|
||||||
@ -26,12 +28,37 @@
|
|||||||
displayPubkey,
|
displayPubkey,
|
||||||
userListFeeds,
|
userListFeeds,
|
||||||
userFeeds,
|
userFeeds,
|
||||||
|
publishes,
|
||||||
} from "src/engine"
|
} from "src/engine"
|
||||||
|
|
||||||
const {page} = router
|
const {page} = router
|
||||||
const followsFeed = makeFeed({definition: normalizeFeedDefinition(makeScopeFeed(Scope.Follows))})
|
const followsFeed = makeFeed({definition: normalizeFeedDefinition(makeScopeFeed(Scope.Follows))})
|
||||||
const networkFeed = makeFeed({definition: normalizeFeedDefinition(makeScopeFeed(Scope.Network))})
|
const networkFeed = makeFeed({definition: normalizeFeedDefinition(makeScopeFeed(Scope.Network))})
|
||||||
|
|
||||||
|
const hud = publishes.derived($publishes => {
|
||||||
|
const pending = []
|
||||||
|
const success = []
|
||||||
|
const failure = []
|
||||||
|
|
||||||
|
for (const {created_at, request, status} of $publishes) {
|
||||||
|
if (created_at < now() - seconds(5, "minute")) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const statuses = Array.from(status.values())
|
||||||
|
|
||||||
|
if (statuses.includes(PublishStatus.Success)) {
|
||||||
|
success.push(request.event)
|
||||||
|
} else if (statuses.includes(PublishStatus.Pending)) {
|
||||||
|
pending.push(request.event)
|
||||||
|
} else {
|
||||||
|
failure.push(request.event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {pending, success, failure}
|
||||||
|
})
|
||||||
|
|
||||||
const closeSubMenu = () => {
|
const closeSubMenu = () => {
|
||||||
subMenu = null
|
subMenu = null
|
||||||
}
|
}
|
||||||
@ -228,6 +255,26 @@
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
</MenuDesktopSecondary>
|
</MenuDesktopSecondary>
|
||||||
{/if}
|
{/if}
|
||||||
|
<div>
|
||||||
|
<Anchor
|
||||||
|
modal
|
||||||
|
href="/publishes"
|
||||||
|
class="flex h-12 cursor-pointer items-center justify-between border-t border-solid border-neutral-600 pl-7 pr-12">
|
||||||
|
<div class="flex items-center gap-1" class:text-tinted-500={$hud.pending.length === 0}>
|
||||||
|
<i class="fa fa-hourglass" />
|
||||||
|
{$hud.pending.length}
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-1" class:text-tinted-500={$hud.success.length === 0}>
|
||||||
|
<i class="fa fa-cloud-arrow-up" />
|
||||||
|
{$hud.success.length}
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-1"
|
||||||
|
class:text-accent={$hud.failure.length > 0}
|
||||||
|
class:text-tinted-500={$hud.failure.length === 0}>
|
||||||
|
<i class="fa fa-triangle-exclamation" />
|
||||||
|
{$hud.failure.length}
|
||||||
|
</div>
|
||||||
|
</Anchor>
|
||||||
<div class="h-20 cursor-pointer border-t border-solid border-neutral-600 px-7 py-4">
|
<div class="h-20 cursor-pointer border-t border-solid border-neutral-600 px-7 py-4">
|
||||||
{#if $pubkey}
|
{#if $pubkey}
|
||||||
<Anchor class="flex items-center gap-2" on:click={() => setSubMenu("account")}>
|
<Anchor class="flex items-center gap-2" on:click={() => setSubMenu("account")}>
|
||||||
@ -241,5 +288,6 @@
|
|||||||
<Anchor modal button accent href="/login">Log In</Anchor>
|
<Anchor modal button accent href="/login">Log In</Anchor>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</FlexColumn>
|
</FlexColumn>
|
||||||
</div>
|
</div>
|
||||||
|
@ -122,7 +122,7 @@
|
|||||||
<i class="fa fa-rss" /> Feeds
|
<i class="fa fa-rss" /> Feeds
|
||||||
</MenuMobileItem>
|
</MenuMobileItem>
|
||||||
</div>
|
</div>
|
||||||
<div class="staatliches mt-8 block flex h-8 justify-center gap-2 px-8 text-neutral-300">
|
<div class="staatliches mt-8 block flex h-8 justify-center gap-2 px-8 text-tinted-400">
|
||||||
<Anchor class="hover:text-tinted-200" href="/about">About</Anchor> /
|
<Anchor class="hover:text-tinted-200" href="/about">About</Anchor> /
|
||||||
<Anchor external class="hover:text-tinted-200" href="/terms.html">Terms</Anchor> /
|
<Anchor external class="hover:text-tinted-200" href="/terms.html">Terms</Anchor> /
|
||||||
<Anchor external class="hover:text-tinted-200" href="/privacy.html">Privacy</Anchor>
|
<Anchor external class="hover:text-tinted-200" href="/privacy.html">Privacy</Anchor>
|
||||||
@ -192,7 +192,7 @@
|
|||||||
<i class="fa fa-paper-plane" /> Create Invite
|
<i class="fa fa-paper-plane" /> Create Invite
|
||||||
</MenuMobileItem>
|
</MenuMobileItem>
|
||||||
</div>
|
</div>
|
||||||
<div class="staatliches block flex h-8 justify-center gap-2 px-8 text-neutral-300">
|
<div class="staatliches block flex h-8 justify-center gap-2 px-8 text-tinted-400">
|
||||||
<Anchor class="hover:text-tinted-200" href="/logout" on:click={closeMenu}>Logout</Anchor> /
|
<Anchor class="hover:text-tinted-200" href="/logout" on:click={closeMenu}>Logout</Anchor> /
|
||||||
<Anchor class="hover:text-tinted-200" stopPropagation on:click={() => setSubMenu("accounts")}>
|
<Anchor class="hover:text-tinted-200" stopPropagation on:click={() => setSubMenu("accounts")}>
|
||||||
Switch Accounts
|
Switch Accounts
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {now} from "@welshman/lib"
|
|
||||||
import {PublishStatus} from "@welshman/net"
|
|
||||||
import {quantify, seconds} from "hurdak"
|
|
||||||
import Input from "src/partials/Input.svelte"
|
import Input from "src/partials/Input.svelte"
|
||||||
import Anchor from "src/partials/Anchor.svelte"
|
import Anchor from "src/partials/Anchor.svelte"
|
||||||
import SearchResults from "src/app/shared/SearchResults.svelte"
|
import SearchResults from "src/app/shared/SearchResults.svelte"
|
||||||
@ -9,37 +6,13 @@
|
|||||||
import PersonBadge from "src/app/shared/PersonBadge.svelte"
|
import PersonBadge from "src/app/shared/PersonBadge.svelte"
|
||||||
import {menuIsOpen, searchTerm} from "src/app/state"
|
import {menuIsOpen, searchTerm} from "src/app/state"
|
||||||
import {router} from "src/app/util/router"
|
import {router} from "src/app/util/router"
|
||||||
import {env, pubkey, canSign, hasNewNotifications, hasNewMessages, publishes} from "src/engine"
|
import {env, pubkey, canSign, hasNewNotifications, hasNewMessages} from "src/engine"
|
||||||
|
|
||||||
let innerWidth = 0
|
let innerWidth = 0
|
||||||
let searchInput
|
let searchInput
|
||||||
|
|
||||||
const {page} = router
|
const {page} = router
|
||||||
|
|
||||||
const hud = publishes.derived($publishes => {
|
|
||||||
const pending = []
|
|
||||||
const success = []
|
|
||||||
const failure = []
|
|
||||||
|
|
||||||
for (const {created_at, request, status} of $publishes) {
|
|
||||||
if (created_at < now() - seconds(5, "minute")) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
const statuses = Array.from(status.values())
|
|
||||||
|
|
||||||
if (statuses.includes(PublishStatus.Pending)) {
|
|
||||||
pending.push(request.event)
|
|
||||||
} else if (statuses.includes(PublishStatus.Success)) {
|
|
||||||
success.push(request.event)
|
|
||||||
} else {
|
|
||||||
failure.push(request.event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {pending, success, failure}
|
|
||||||
})
|
|
||||||
|
|
||||||
const openMenu = () => menuIsOpen.set(true)
|
const openMenu = () => menuIsOpen.set(true)
|
||||||
|
|
||||||
const openSearch = () => router.at("/search").open()
|
const openSearch = () => router.at("/search").open()
|
||||||
@ -78,22 +51,6 @@
|
|||||||
{#if innerWidth >= 1024}
|
{#if innerWidth >= 1024}
|
||||||
<div
|
<div
|
||||||
class="fixed left-0 right-0 top-0 z-nav flex h-16 items-center justify-end gap-8 bg-neutral-900 pl-4 pr-8">
|
class="fixed left-0 right-0 top-0 z-nav flex h-16 items-center justify-end gap-8 bg-neutral-900 pl-4 pr-8">
|
||||||
<div class="absolute left-72 flex items-center gap-2 px-4 text-sm text-neutral-500">
|
|
||||||
{#if $hud.pending.length > 0}
|
|
||||||
<i class="fa fa-circle-notch fa-spin" />
|
|
||||||
Sending {quantify($hud.pending.length, "note")}.
|
|
||||||
{:else if $hud.failure.length > 0}
|
|
||||||
<i class="fa fa-triangle-exclamation" />
|
|
||||||
Failed to publish {quantify($hud.failure.length, "note")}.
|
|
||||||
{:else if $hud.success.length > 0}
|
|
||||||
<i class="fa fa-check" />
|
|
||||||
Successfully published {quantify($hud.success.length, "note")}.
|
|
||||||
{:else}
|
|
||||||
<i class="fa fa-check" />
|
|
||||||
No recent notes.
|
|
||||||
{/if}
|
|
||||||
<Anchor underline modal href="/publishes">Details</Anchor>
|
|
||||||
</div>
|
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<Input
|
<Input
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {NAMED_BOOKMARKS} from "@welshman/util"
|
import {NAMED_BOOKMARKS, addressToNaddr, decodeAddress} from "@welshman/util"
|
||||||
import FlexColumn from "src/partials/FlexColumn.svelte"
|
import FlexColumn from "src/partials/FlexColumn.svelte"
|
||||||
import Card from "src/partials/Card.svelte"
|
import Card from "src/partials/Card.svelte"
|
||||||
|
import Chip from "src/partials/Chip.svelte"
|
||||||
import Anchor from "src/partials/Anchor.svelte"
|
import Anchor from "src/partials/Anchor.svelte"
|
||||||
|
import CopyValueSimple from "src/partials/CopyValueSimple.svelte"
|
||||||
import FeedSummary from "src/app/shared/FeedSummary.svelte"
|
import FeedSummary from "src/app/shared/FeedSummary.svelte"
|
||||||
import {readFeed, readList, displayFeed, mapListToFeed} from "src/domain"
|
import {readFeed, readList, displayFeed, mapListToFeed} from "src/domain"
|
||||||
import {repository} from "src/engine"
|
import {repository} from "src/engine"
|
||||||
@ -12,6 +14,7 @@
|
|||||||
export let address
|
export let address
|
||||||
|
|
||||||
const event = repository.getEvent(address)
|
const event = repository.getEvent(address)
|
||||||
|
const deleted = repository.isDeleted(event)
|
||||||
const feed = address.startsWith(NAMED_BOOKMARKS)
|
const feed = address.startsWith(NAMED_BOOKMARKS)
|
||||||
? mapListToFeed(readList(event))
|
? mapListToFeed(readList(event))
|
||||||
: readFeed(event)
|
: readFeed(event)
|
||||||
@ -27,15 +30,27 @@
|
|||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<span class="staatliches flex items-center gap-3 text-xl">
|
<span class="staatliches flex items-center gap-3 text-xl">
|
||||||
<i class="fa fa-rss" />
|
<i class="fa fa-rss" />
|
||||||
<Anchor on:click={loadFeed} class={feed.title ? "" : "text-neutral-500"}>
|
<span class:text-neutral-400={!feed.title} class:line-through={deleted}>
|
||||||
{displayFeed(feed)}
|
{displayFeed(feed)}
|
||||||
</Anchor>
|
|
||||||
</span>
|
</span>
|
||||||
<slot name="controls" />
|
{#if deleted}
|
||||||
|
<Chip danger small>Deleted</Chip>
|
||||||
|
{/if}
|
||||||
|
</span>
|
||||||
|
<slot name="controls">
|
||||||
|
<Anchor on:click={loadFeed}>
|
||||||
|
Load feed
|
||||||
|
</Anchor>
|
||||||
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
{#if feed.description}
|
{#if feed.description}
|
||||||
<p>{feed.description}</p>
|
<p>{feed.description}</p>
|
||||||
{/if}
|
{/if}
|
||||||
|
<div class="flex items-start justify-between">
|
||||||
<FeedSummary feed={feed.definition} />
|
<FeedSummary feed={feed.definition} />
|
||||||
|
<div class="py-2">
|
||||||
|
<CopyValueSimple label="Feed address" value={addressToNaddr(decodeAddress(address))} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</FlexColumn>
|
</FlexColumn>
|
||||||
</Card>
|
</Card>
|
||||||
|
@ -117,12 +117,7 @@
|
|||||||
{#if listMenuIsOpen}
|
{#if listMenuIsOpen}
|
||||||
<Popover2 absolute hideOnClick onClose={closeListMenu} class="right-0 top-8 w-60">
|
<Popover2 absolute hideOnClick onClose={closeListMenu} class="right-0 top-8 w-60">
|
||||||
<Menu>
|
<Menu>
|
||||||
<MenuItem inert class="flex items-center justify-between bg-neutral-800 shadow">
|
<MenuItem inert class="staatliches bg-neutral-800 text-lg shadow">Your Feeds</MenuItem>
|
||||||
<span class="staatliches text-lg">Your Feeds</span>
|
|
||||||
<Anchor href={router.at("feeds").toString()}>
|
|
||||||
<i class="fa fa-cog" />
|
|
||||||
</Anchor>
|
|
||||||
</MenuItem>
|
|
||||||
<div class="max-h-96 overflow-auto">
|
<div class="max-h-96 overflow-auto">
|
||||||
<MenuItem on:click={() => setFeed(followsFeed)}>Follows</MenuItem>
|
<MenuItem on:click={() => setFeed(followsFeed)}>Follows</MenuItem>
|
||||||
<MenuItem on:click={() => setFeed(networkFeed)}>Network</MenuItem>
|
<MenuItem on:click={() => setFeed(networkFeed)}>Network</MenuItem>
|
||||||
@ -136,6 +131,13 @@
|
|||||||
{displayList(feed.list)}
|
{displayList(feed.list)}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
{/each}
|
{/each}
|
||||||
|
<div class="h-px bg-neutral-600" />
|
||||||
|
<MenuItem href={router.at("feeds").toString()} class="flex items-center gap-2">
|
||||||
|
<i class="fa fa-rss" /> Manage feeds
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem href={router.at("lists").toString()} class="flex items-center gap-2">
|
||||||
|
<i class="fa fa-list" /> Manage lists
|
||||||
|
</MenuItem>
|
||||||
</div>
|
</div>
|
||||||
</Menu>
|
</Menu>
|
||||||
</Popover2>
|
</Popover2>
|
||||||
|
@ -168,7 +168,7 @@
|
|||||||
search={searchFeeds}
|
search={searchFeeds}
|
||||||
onChange={addFeed}
|
onChange={addFeed}
|
||||||
displayItem={displayPubkey}>
|
displayItem={displayPubkey}>
|
||||||
<i slot="before" class="fa fa-scroll" />
|
<i slot="before" class="fa fa-rss" />
|
||||||
<span slot="item" let:item>
|
<span slot="item" let:item>
|
||||||
<PersonBadge inert pubkey={item} />
|
<PersonBadge inert pubkey={item} />
|
||||||
</span>
|
</span>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {quantify} from 'hurdak'
|
import {quantify} from "hurdak"
|
||||||
import {first} from '@welshman/lib'
|
import {first} from "@welshman/lib"
|
||||||
import {Tags} from '@welshman/util'
|
import {Tags, addressToNaddr, decodeAddress} from "@welshman/util"
|
||||||
import {defaultTagFeedMappings} from '@welshman/feeds'
|
import {defaultTagFeedMappings} from "@welshman/feeds"
|
||||||
import FlexColumn from "src/partials/FlexColumn.svelte"
|
import FlexColumn from "src/partials/FlexColumn.svelte"
|
||||||
import Card from "src/partials/Card.svelte"
|
import Card from "src/partials/Card.svelte"
|
||||||
import Chip from "src/partials/Chip.svelte"
|
import Chip from "src/partials/Chip.svelte"
|
||||||
@ -14,6 +14,7 @@
|
|||||||
import {router} from "src/app/util"
|
import {router} from "src/app/util"
|
||||||
|
|
||||||
export let address
|
export let address
|
||||||
|
export let inert = false
|
||||||
|
|
||||||
const tagTypes = defaultTagFeedMappings.map(first) as string[]
|
const tagTypes = defaultTagFeedMappings.map(first) as string[]
|
||||||
const event = repository.getEvent(address)
|
const event = repository.getEvent(address)
|
||||||
@ -22,9 +23,11 @@
|
|||||||
const list = readList(event)
|
const list = readList(event)
|
||||||
|
|
||||||
const loadFeed = () => {
|
const loadFeed = () => {
|
||||||
|
if (!inert) {
|
||||||
globalFeed.set(mapListToFeed(list))
|
globalFeed.set(mapListToFeed(list))
|
||||||
router.at("notes").push()
|
router.at("notes").push()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Card>
|
<Card>
|
||||||
@ -32,25 +35,28 @@
|
|||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<span class="staatliches flex items-center gap-3 text-xl">
|
<span class="staatliches flex items-center gap-3 text-xl">
|
||||||
<i class="fa fa-list" />
|
<i class="fa fa-list" />
|
||||||
<span
|
<span class:text-neutral-400={!list.title} class:line-through={deleted}>
|
||||||
class:text-neutral-400={!list.title}
|
|
||||||
class:line-through={deleted}>
|
|
||||||
<Anchor on:click={loadFeed}>
|
|
||||||
{displayList(list)}
|
{displayList(list)}
|
||||||
</Anchor>
|
|
||||||
</span>
|
</span>
|
||||||
{#if deleted}
|
{#if deleted}
|
||||||
<Chip danger small>Deleted</Chip>
|
<Chip danger small>Deleted</Chip>
|
||||||
{/if}
|
{/if}
|
||||||
</span>
|
</span>
|
||||||
<slot name="controls" />
|
<slot name="controls">
|
||||||
|
<Anchor on:click={loadFeed}>
|
||||||
|
Load as feed
|
||||||
|
</Anchor>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{#if list.description}
|
{#if list.description}
|
||||||
<p>{list.description}</p>
|
<p>{list.description}</p>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
{quantify(tags.filterByKey(tagTypes).count(), 'item')}
|
{quantify(tags.filterByKey(tagTypes).count(), "item")}
|
||||||
<CopyValueSimple label="List address" value={address} class="text-neutral-400" />
|
<CopyValueSimple
|
||||||
|
label="List address"
|
||||||
|
value={addressToNaddr(decodeAddress(address))}
|
||||||
|
class="text-neutral-400" />
|
||||||
</div>
|
</div>
|
||||||
</FlexColumn>
|
</FlexColumn>
|
||||||
</Card>
|
</Card>
|
||||||
|
@ -45,6 +45,7 @@
|
|||||||
const submit = async () => {
|
const submit = async () => {
|
||||||
const relays = hints.WriteRelays().getUrls()
|
const relays = hints.WriteRelays().getUrls()
|
||||||
const template = list.event ? editList(list) : createList(list)
|
const template = list.event ? editList(list) : createList(list)
|
||||||
|
console.log(template)
|
||||||
const pub = await createAndPublish({...template, relays})
|
const pub = await createAndPublish({...template, relays})
|
||||||
|
|
||||||
showInfo("Your list has been saved!")
|
showInfo("Your list has been saved!")
|
||||||
|
@ -16,13 +16,14 @@
|
|||||||
import NoteContentKind30311 from "src/app/shared/NoteContentKind30311.svelte"
|
import NoteContentKind30311 from "src/app/shared/NoteContentKind30311.svelte"
|
||||||
import NoteContentKind30402 from "src/app/shared/NoteContentKind30402.svelte"
|
import NoteContentKind30402 from "src/app/shared/NoteContentKind30402.svelte"
|
||||||
import NoteContentKind31337 from "src/app/shared/NoteContentKind31337.svelte"
|
import NoteContentKind31337 from "src/app/shared/NoteContentKind31337.svelte"
|
||||||
|
import NoteContentKind31890 from "src/app/shared/NoteContentKind31890.svelte"
|
||||||
import NoteContentKind31923 from "src/app/shared/NoteContentKind31923.svelte"
|
import NoteContentKind31923 from "src/app/shared/NoteContentKind31923.svelte"
|
||||||
import NoteContentKind32123 from "src/app/shared/NoteContentKind32123.svelte"
|
import NoteContentKind32123 from "src/app/shared/NoteContentKind32123.svelte"
|
||||||
import NoteContentKind34550 from "src/app/shared/NoteContentKind34550.svelte"
|
import NoteContentKind34550 from "src/app/shared/NoteContentKind34550.svelte"
|
||||||
import NoteContentKind35834 from "src/app/shared/NoteContentKind35834.svelte"
|
import NoteContentKind35834 from "src/app/shared/NoteContentKind35834.svelte"
|
||||||
import NoteContentKindList from "src/app/shared/NoteContentKindList.svelte"
|
import NoteContentKindList from "src/app/shared/NoteContentKindList.svelte"
|
||||||
import {getSetting} from "src/engine"
|
import {getSetting} from "src/engine"
|
||||||
import {LIST_KINDS} from 'src/domain'
|
import {LIST_KINDS} from "src/domain"
|
||||||
|
|
||||||
export let note
|
export let note
|
||||||
export let isQuote = false
|
export let isQuote = false
|
||||||
@ -74,6 +75,8 @@
|
|||||||
<NoteContentKind30402 {note} {showEntire} {showMedia} />
|
<NoteContentKind30402 {note} {showEntire} {showMedia} />
|
||||||
{:else if note.kind === 31337}
|
{:else if note.kind === 31337}
|
||||||
<NoteContentKind31337 {note} {showMedia} />
|
<NoteContentKind31337 {note} {showMedia} />
|
||||||
|
{:else if note.kind === 31890}
|
||||||
|
<NoteContentKind31890 {note} />
|
||||||
{:else if note.kind === 31923}
|
{:else if note.kind === 31923}
|
||||||
<NoteContentKind31923 {note} />
|
<NoteContentKind31923 {note} />
|
||||||
{:else if note.kind === 32123}
|
{:else if note.kind === 32123}
|
||||||
|
8
src/app/shared/NoteContentKind31890.svelte
Normal file
8
src/app/shared/NoteContentKind31890.svelte
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {getAddress} from "@welshman/util"
|
||||||
|
import FeedCard from "src/app/shared/FeedCard.svelte"
|
||||||
|
|
||||||
|
export let note
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<FeedCard address={getAddress(note)} />
|
@ -40,7 +40,7 @@
|
|||||||
actions.push({
|
actions.push({
|
||||||
onClick: () => router.at("lists/select").qp({type: "p", value: pubkey}).open(),
|
onClick: () => router.at("lists/select").qp({type: "p", value: pubkey}).open(),
|
||||||
label: "Add to list",
|
label: "Add to list",
|
||||||
icon: "scroll",
|
icon: "list",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@
|
|||||||
actions.push({
|
actions.push({
|
||||||
onClick: () => router.at("lists/select").qp({type: "r", value: url}).open(),
|
onClick: () => router.at("lists/select").qp({type: "r", value: url}).open(),
|
||||||
label: "Add to list",
|
label: "Add to list",
|
||||||
icon: "scroll",
|
icon: "list",
|
||||||
})
|
})
|
||||||
|
|
||||||
actions.push({
|
actions.push({
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
actions.push({
|
actions.push({
|
||||||
onClick: () => router.at("lists/select").qp({type: "t", value: topic}).open(),
|
onClick: () => router.at("lists/select").qp({type: "t", value: topic}).open(),
|
||||||
label: "Add to list",
|
label: "Add to list",
|
||||||
icon: "scroll",
|
icon: "list",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,5 +43,5 @@
|
|||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
{#if $userFeeds.length === 0 && $userListFeeds.length === 0}
|
{#if $userFeeds.length === 0 && $userListFeeds.length === 0}
|
||||||
<p class="py-12 text-center">No feeds found.</p>
|
<p class="py-12 text-center">You don't have any lists yet.</p>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -1,13 +1,17 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Subheading from 'src/partials/Subheading.svelte'
|
import Subheading from "src/partials/Subheading.svelte"
|
||||||
import ListForm from "src/app/shared/ListForm.svelte"
|
import ListForm from "src/app/shared/ListForm.svelte"
|
||||||
import {router} from "src/app/util"
|
import {router} from "src/app/util"
|
||||||
import {makeList} from "src/domain"
|
import {makeList} from "src/domain"
|
||||||
|
|
||||||
const list = makeList()
|
export let tags = []
|
||||||
|
|
||||||
|
const list = makeList({tags})
|
||||||
|
|
||||||
|
const hide = tags.length > 0 ? ["type"] : []
|
||||||
|
|
||||||
const exit = () => router.clearModals()
|
const exit = () => router.clearModals()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Subheading class="text-center">Create list</Subheading>
|
<Subheading class="text-center">Create list</Subheading>
|
||||||
<ListForm {list} {exit} />
|
<ListForm {list} {exit} {hide} />
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import {uniqBy, nth} from "@welshman/lib"
|
||||||
import Subheading from "src/partials/Subheading.svelte"
|
import Subheading from "src/partials/Subheading.svelte"
|
||||||
import ListForm from "src/app/shared/ListForm.svelte"
|
import ListForm from "src/app/shared/ListForm.svelte"
|
||||||
import {router} from "src/app/util"
|
import {router} from "src/app/util"
|
||||||
@ -6,15 +7,18 @@
|
|||||||
import {repository} from "src/engine"
|
import {repository} from "src/engine"
|
||||||
|
|
||||||
export let address
|
export let address
|
||||||
|
export let tags = []
|
||||||
|
|
||||||
const event = repository.getEvent(address)
|
const event = repository.getEvent(address)
|
||||||
|
|
||||||
|
const list = {...readList(event), tags: uniqBy(nth(1), [...event.tags, ...tags])}
|
||||||
|
|
||||||
const exit = () => router.clearModals()
|
const exit = () => router.clearModals()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if event}
|
{#if event}
|
||||||
<Subheading class="text-center">Edit list</Subheading>
|
<Subheading class="text-center">Edit list</Subheading>
|
||||||
<ListForm showDelete list={readList(event)} {exit} />
|
<ListForm showDelete {list} {exit} hide={["type"]} />
|
||||||
{:else}
|
{:else}
|
||||||
<p class="text-center">Sorry, we weren't able to find that list.</p>
|
<p class="text-center">Sorry, we weren't able to find that list.</p>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
<i class="fa fa-plus" /> List
|
<i class="fa fa-plus" /> List
|
||||||
</Anchor>
|
</Anchor>
|
||||||
</div>
|
</div>
|
||||||
{#each $userLists as list}
|
{#each $userLists as list (getAddress(list.event))}
|
||||||
{@const address = getAddress(list.event)}
|
{@const address = getAddress(list.event)}
|
||||||
<div in:fly={{y: 20}}>
|
<div in:fly={{y: 20}}>
|
||||||
<ListCard {address}>
|
<ListCard {address}>
|
||||||
@ -29,5 +29,5 @@
|
|||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
{#if $userLists.length === 0}
|
{#if $userLists.length === 0}
|
||||||
<p class="py-12 text-center">No lists found.</p>
|
<p class="py-12 text-center">You don't have any lists yet.</p>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -1,39 +1,55 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {append, randomId} from "@welshman/lib"
|
import {first} from "@welshman/lib"
|
||||||
import {getAddress} from "@welshman/util"
|
import {getAddress, Tags} from "@welshman/util"
|
||||||
import {updateIn} from "src/util/misc"
|
import {defaultTagFeedMappings} from "@welshman/feeds"
|
||||||
|
import {quantify} from "hurdak"
|
||||||
import Subheading from "src/partials/Subheading.svelte"
|
import Subheading from "src/partials/Subheading.svelte"
|
||||||
import Anchor from "src/partials/Anchor.svelte"
|
import Anchor from "src/partials/Anchor.svelte"
|
||||||
import Content from "src/partials/Content.svelte"
|
import Card from "src/partials/Card.svelte"
|
||||||
import ListCard from "src/app/shared/ListCard.svelte"
|
import FlexColumn from "src/partials/FlexColumn.svelte"
|
||||||
import {router} from "src/app/util/router"
|
import {router} from "src/app/util/router"
|
||||||
import {pubkey, userLists} from "src/engine"
|
import {userLists} from "src/engine"
|
||||||
|
import {displayList} from "src/domain"
|
||||||
|
|
||||||
export let type
|
export let type
|
||||||
export let value
|
export let value
|
||||||
|
|
||||||
const label = type === "p" ? "person" : "topic"
|
const tags = [[type, value]]
|
||||||
|
|
||||||
const modifyList = updateIn("tags", tags => append([type, value], tags))
|
const tagTypes = defaultTagFeedMappings.map(first) as string[]
|
||||||
|
|
||||||
const newList = () => ({address: `30003:${$pubkey}:${randomId()}`, tags: []})
|
const createList = () => router.at("lists/create").cx({tags}).replaceModal()
|
||||||
|
|
||||||
const selectlist = list => router.at("lists").of(getAddress(list.event)).at("edit").replaceModal()
|
const selectList = list =>
|
||||||
|
router.at("lists").of(getAddress(list.event)).at("edit").cx({tags}).replaceModal()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Content size="lg">
|
<FlexColumn>
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<Subheading>Select a List</Subheading>
|
<Subheading>Select a List</Subheading>
|
||||||
<Anchor button accent on:click={() => selectlist(newList())}>
|
<Anchor button accent on:click={createList}>
|
||||||
<i class="fa fa-plus" /> List
|
<i class="fa fa-plus" /> List
|
||||||
</Anchor>
|
</Anchor>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p>Select a list to add your selection to.</p>
|
||||||
Select a list to modify. The selected {label} will be added to it as an additional filter.
|
|
||||||
</p>
|
|
||||||
{#each $userLists as list (getAddress(list.event))}
|
{#each $userLists as list (getAddress(list.event))}
|
||||||
<ListCard address={getAddress(list.event)} />
|
<Card interactive on:click={() => selectList(list)}>
|
||||||
|
<FlexColumn>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span class="staatliches flex items-center gap-3 text-xl">
|
||||||
|
<i class="fa fa-list" />
|
||||||
|
<span class:text-neutral-400={!list.title}>
|
||||||
|
{displayList(list)}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{#if list.description}
|
||||||
|
<p>{list.description}</p>
|
||||||
|
{/if}
|
||||||
|
{quantify(Tags.wrap(list.tags).filterByKey(tagTypes).count(), "item")}
|
||||||
|
</FlexColumn>
|
||||||
|
</Card>
|
||||||
{:else}
|
{:else}
|
||||||
<p class="text-center py-12">You don't have any custom lists yet.</p>
|
<p class="text-center py-12">You don't have any lists yet.</p>
|
||||||
{/each}
|
{/each}
|
||||||
</Content>
|
</FlexColumn>
|
||||||
|
@ -13,8 +13,10 @@
|
|||||||
|
|
||||||
$: recent = $publishes.filter(p => p.created_at > now() - seconds(24, "hour"))
|
$: recent = $publishes.filter(p => p.created_at > now() - seconds(24, "hour"))
|
||||||
$: relays = new Set(recent.flatMap(({request}) => request.relays))
|
$: relays = new Set(recent.flatMap(({request}) => request.relays))
|
||||||
$: pending = recent.filter(p => hasStatus(p, [PublishStatus.Pending]))
|
|
||||||
$: success = recent.filter(p => hasStatus(p, [PublishStatus.Success]))
|
$: success = recent.filter(p => hasStatus(p, [PublishStatus.Success]))
|
||||||
|
$: pending = recent.filter(
|
||||||
|
p => hasStatus(p, [PublishStatus.Pending]) && !hasStatus(p, [PublishStatus.Success]),
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Subheading>Published Events</Subheading>
|
<Subheading>Published Events</Subheading>
|
||||||
|
@ -1094,7 +1094,7 @@ export const feeds = repository.filter([{kinds: [FEED]}]).derived($events => $ev
|
|||||||
|
|
||||||
export const userFeeds = new Derived([feeds, pubkey], ([$feeds, $pubkey]: [Feed[], string]) =>
|
export const userFeeds = new Derived([feeds, pubkey], ([$feeds, $pubkey]: [Feed[], string]) =>
|
||||||
sortBy(
|
sortBy(
|
||||||
prop("title"),
|
f => f.title.toLowerCase(),
|
||||||
$feeds.filter(feed => feed.event.pubkey === $pubkey),
|
$feeds.filter(feed => feed.event.pubkey === $pubkey),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -1105,7 +1105,7 @@ export const lists = repository
|
|||||||
|
|
||||||
export const userLists = new Derived([lists, pubkey], ([$lists, $pubkey]: [List[], string]) =>
|
export const userLists = new Derived([lists, pubkey], ([$lists, $pubkey]: [List[], string]) =>
|
||||||
sortBy(
|
sortBy(
|
||||||
prop("title"),
|
l => l.title.toLowerCase(),
|
||||||
$lists.filter(list => list.event.pubkey === $pubkey),
|
$lists.filter(list => list.event.pubkey === $pubkey),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -1120,7 +1120,7 @@ export const userListFeeds = new Derived(
|
|||||||
[listFeeds, pubkey],
|
[listFeeds, pubkey],
|
||||||
([$listFeeds, $pubkey]: [Feed[], string]) =>
|
([$listFeeds, $pubkey]: [Feed[], string]) =>
|
||||||
sortBy(
|
sortBy(
|
||||||
prop("title"),
|
l => l.title.toLowerCase(),
|
||||||
$listFeeds.filter(feed => feed.list.event.pubkey === $pubkey),
|
$listFeeds.filter(feed => feed.list.event.pubkey === $pubkey),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -15,7 +15,11 @@
|
|||||||
const share = () => router.at("qrcode").at(value).open()
|
const share = () => router.at("qrcode").at(value).open()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class={cx($$props.class, "flex items-center gap-2")}>
|
<div class={cx($$props.class, "flex items-center gap-1")}>
|
||||||
<i class="fa-solid fa-copy cursor-pointer" on:click={copy} />
|
<div class="cursor-pointer px-1 text-neutral-400 transition-colors hover:text-neutral-100">
|
||||||
<i class="fa-solid fa-qrcode cursor-pointer" on:click={share} />
|
<i class="fa-solid fa-copy" on:click={copy} />
|
||||||
|
</div>
|
||||||
|
<div class="cursor-pointer px-1 text-neutral-400 transition-colors hover:text-neutral-100">
|
||||||
|
<i class="fa-solid fa-qrcode" on:click={share} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -18,6 +18,8 @@ import {
|
|||||||
WRAP_NIP04,
|
WRAP_NIP04,
|
||||||
ZAP_RESPONSE,
|
ZAP_RESPONSE,
|
||||||
Tags,
|
Tags,
|
||||||
|
decodeAddress,
|
||||||
|
addressToNaddr,
|
||||||
} from "@welshman/util"
|
} from "@welshman/util"
|
||||||
import type {TrustedEvent} from "@welshman/util"
|
import type {TrustedEvent} from "@welshman/util"
|
||||||
import {schnorr} from "@noble/curves/secp256k1"
|
import {schnorr} from "@noble/curves/secp256k1"
|
||||||
@ -146,6 +148,11 @@ export const parseAnything = async entity => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Interpret addresses as naddrs
|
||||||
|
if (entity.match(/^\d+:\w+:.*$/)) {
|
||||||
|
entity = addressToNaddr(decodeAddress(entity))
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return nip19.decode(entity)
|
return nip19.decode(entity)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
Loading…
Reference in New Issue
Block a user