diff --git a/README.md b/README.md index 28c462ae..7a348843 100644 --- a/README.md +++ b/README.md @@ -6,3 +6,4 @@ A discord-like nostr client. WIP. - [ ] Delete events when leaving a space - [ ] Add topic and event tags to compose +- [ ] If the user isn't following anyone, show warning/fallback on people/notes pages diff --git a/src/app/components/NoteCard.svelte b/src/app/components/NoteCard.svelte index e21e173b..a038597c 100644 --- a/src/app/components/NoteCard.svelte +++ b/src/app/components/NoteCard.svelte @@ -1,16 +1,25 @@
- {formatTimestamp(event.created_at)} + + {formatTimestamp(event.created_at)} +
diff --git a/src/app/components/PrimaryNav.svelte b/src/app/components/PrimaryNav.svelte index 289790fb..c17b8bb1 100644 --- a/src/app/components/PrimaryNav.svelte +++ b/src/app/components/PrimaryNav.svelte @@ -10,12 +10,13 @@ import {tweened} from "svelte/motion" import {quintOut} from "svelte/easing" import {displayRelayUrl} from "@welshman/util" + import {userProfile} from "@welshman/app" import Icon from "@lib/components/Icon.svelte" import Avatar from "@lib/components/Avatar.svelte" import PrimaryNavItem from "@lib/components/PrimaryNavItem.svelte" import SpaceAdd from "@app/components/SpaceAdd.svelte" import SpaceAvatar from "@app/components/SpaceAvatar.svelte" - import {userProfile, userMembership} from "@app/state" + import {userMembership} from "@app/state" import {pushModal} from "@app/modal" import {makeSpacePath, getPrimaryNavItemIndex} from "@app/routes" diff --git a/src/app/state.ts b/src/app/state.ts index 9f910fb0..b26ee225 100644 --- a/src/app/state.ts +++ b/src/app/state.ts @@ -1,19 +1,22 @@ import {nip19} from "nostr-tools" import {get, derived} from "svelte/store" import type {Maybe} from "@welshman/lib" -import {setContext, partition, nth, max, pushToMapKey, nthEq} from "@welshman/lib" +import {setContext, sort, uniq, partition, nth, max, pushToMapKey, nthEq} from "@welshman/lib" import { getIdFilters, NOTE, + WRAP, RELAYS, REACTION, ZAP_RESPONSE, + DIRECT_MESSAGE, EVENT_DATE, EVENT_TIME, getRelayTagValues, isShareableRelayUrl, getAncestorTags, getAncestorTagValues, + getPubkeyTagValues, } from "@welshman/util" import type {TrustedEvent} from "@welshman/util" import { @@ -152,12 +155,12 @@ export const { // Messages -export type Message = { +export type ChatMessage = { room: string event: TrustedEvent } -export const readMessage = (event: TrustedEvent): Maybe => { +export const readMessage = (event: TrustedEvent): Maybe => { const rooms = event.tags.filter(nthEq(0, ROOM)).map(nth(1)) if (rooms.length > 1) return undefined @@ -165,7 +168,7 @@ export const readMessage = (event: TrustedEvent): Maybe => { return {room: rooms[0] || "", event} } -export const messages = deriveEventsMapped(repository, { +export const chatMessages = deriveEventsMapped(repository, { filters: [{kinds: [MESSAGE, REPLY]}], eventToItem: readMessage, itemToEvent: item => item.event, @@ -177,17 +180,17 @@ export type Chat = { id: string url: string room: string - messages: Message[] + messages: ChatMessage[] } export const makeChatId = (url: string, room: string) => `${url}'${room}` export const splitChatId = (id: string) => id.split("'") -export const chats = derived([trackerStore, messages], ([$tracker, $messages]) => { - const messagesByChatId = new Map() +export const chats = derived([trackerStore, chatMessages], ([$tracker, $chatMessages]) => { + const messagesByChatId = new Map() - for (const message of $messages) { + for (const message of $chatMessages) { for (const url of $tracker.getRelays(message.event.id)) { const chatId = makeChatId(url, message.room) @@ -220,6 +223,57 @@ export const { }, }) +// Channels + +export const channelMessages = deriveEvents(repository, {filters: [{kinds: [DIRECT_MESSAGE]}]}) + +export type Channel = { + id: string + pubkeys: string[] + messages: TrustedEvent[] +} + +export const makeChannelId = (pubkeys: string[]) => sort(uniq(pubkeys)).join(",") + +export const splitChannelId = (id: string) => id.split(",") + +export const channels = derived(channelMessages, $messages => { + const messagesByChannelId = new Map() + + for (const message of $messages) { + const channelId = makeChannelId(getPubkeyTagValues(message.tags)) + + pushToMapKey(messagesByChannelId, channelId, message) + } + + return Array.from(messagesByChannelId.entries()).map(([id, messages]): Channel => { + const pubkeys = splitChannelId(id) + + return {id, pubkeys, messages} + }) +}) + +export const { + indexStore: channelsById, + deriveItem: deriveChannel, + loadItem: loadChannel, +} = collection({ + name: "channels", + store: channels, + getKey: channel => channel.id, + load: async (id: string, request: Partial = {}) => { + const $pubkey = pubkey.get() + const [url, room] = splitChannelId(id) + const channel = get(channelsById).get(id) + const timestamps = channel?.messages.map(e => e.created_at) || [] + const since = Math.max(0, max(timestamps) - 3600) + + if ($pubkey) { + await load({...request, filters: [{kinds: [WRAP], '#p': [$pubkey], since}]}) + } + }, +}) + // Calendar events export const events = deriveEvents(repository, {filters: [{kinds: [EVENT_DATE, EVENT_TIME]}]}) @@ -289,14 +343,6 @@ export const roomsByUrl = derived(chats, $chats => { // User stuff -export const userProfile = derived([pubkey, profilesByPubkey], ([$pubkey, $profilesByPubkey]) => { - if (!$pubkey) return null - - loadProfile($pubkey) - - return $profilesByPubkey.get($pubkey) -}) - export const userMembership = withGetter( derived([pubkey, membershipByPubkey], ([$pubkey, $membershipByPubkey]) => { if (!$pubkey) return null diff --git a/src/routes/home/+layout.svelte b/src/routes/home/+layout.svelte index ed2df802..b9bd0c19 100644 --- a/src/routes/home/+layout.svelte +++ b/src/routes/home/+layout.svelte @@ -6,6 +6,7 @@ import SecondaryNavItem from "@lib/components/SecondaryNavItem.svelte" import SecondaryNavHeader from "@lib/components/SecondaryNavHeader.svelte" import SecondaryNavSection from "@lib/components/SecondaryNavSection.svelte" + import {channels} from '@app/state' @@ -21,6 +22,11 @@
+ + Notes + +
+
Chats
@@ -28,6 +34,13 @@
+ {#each $channels as {id, pubkeys}, i (id)} +
+ + {id} + +
+ {/each}
diff --git a/src/routes/home/+page.svelte b/src/routes/home/+page.svelte index 39e45395..738edcd3 100644 --- a/src/routes/home/+page.svelte +++ b/src/routes/home/+page.svelte @@ -7,6 +7,10 @@ const createSpace = () => pushModal(SpaceCreateExternal) const browseSpaces = () => goto("/discover") + + const leaveFeedback = () => goto("/home/97c70a44366a6535c145b333f973ea86dfdc2d7a99da618c40c64705ad98e322") + + const donate = () => window.open('https://geyser.fund/project/flotilla')
@@ -21,10 +25,10 @@ Find a community based on your hobbies or interests. - + Let us know how we can improve by giving us feedback. - + Support the project by donating to the developer.
diff --git a/src/routes/home/notes/+page.svelte b/src/routes/home/notes/+page.svelte index e69de29b..20960616 100644 --- a/src/routes/home/notes/+page.svelte +++ b/src/routes/home/notes/+page.svelte @@ -0,0 +1,93 @@ + + + +
+ {#await loading} +
+ Loading notes... +
+ {:then} +
+ {#each events as event (event.id)} + +
+ +
+
+ {/each} +
+ {/await} +