forked from coracle/flotilla
Rename chats/channels
This commit is contained in:
@@ -27,8 +27,8 @@
|
|||||||
import Button from "@lib/components/Button.svelte"
|
import Button from "@lib/components/Button.svelte"
|
||||||
import Avatar from "@lib/components/Avatar.svelte"
|
import Avatar from "@lib/components/Avatar.svelte"
|
||||||
import Content from "@app/components/Content.svelte"
|
import Content from "@app/components/Content.svelte"
|
||||||
import ChatThread from '@app/components/ChatThread.svelte'
|
import ChannelThread from '@app/components/ChannelThread.svelte'
|
||||||
import ChatMessageEmojiButton from "@app/components/ChatMessageEmojiButton.svelte"
|
import ChannelMessageEmojiButton from "@app/components/ChannelMessageEmojiButton.svelte"
|
||||||
import {tagRoom, REPLY, deriveEvent, displayReaction} from "@app/state"
|
import {tagRoom, REPLY, deriveEvent, displayReaction} from "@app/state"
|
||||||
import {publishDelete, publishReaction} from "@app/commands"
|
import {publishDelete, publishReaction} from "@app/commands"
|
||||||
import {pushModal} from '@app/modal'
|
import {pushModal} from '@app/modal'
|
||||||
@@ -78,7 +78,7 @@
|
|||||||
const openThread = () => {
|
const openThread = () => {
|
||||||
const root = $rootEvent || event
|
const root = $rootEvent || event
|
||||||
|
|
||||||
pushModal(ChatThread, {url, room, event: root}, {drawer: true})
|
pushModal(ChannelThread, {url, room, event: root}, {drawer: true})
|
||||||
}
|
}
|
||||||
|
|
||||||
const onReactionClick = (content: string, events: TrustedEvent[]) => {
|
const onReactionClick = (content: string, events: TrustedEvent[]) => {
|
||||||
@@ -175,7 +175,7 @@
|
|||||||
<button
|
<button
|
||||||
class="join absolute -top-2 right-0 border border-solid border-neutral text-xs opacity-0 transition-all group-hover:opacity-100"
|
class="join absolute -top-2 right-0 border border-solid border-neutral text-xs opacity-0 transition-all group-hover:opacity-100"
|
||||||
on:click|stopPropagation>
|
on:click|stopPropagation>
|
||||||
<ChatMessageEmojiButton {url} {room} {event} />
|
<ChannelMessageEmojiButton {url} {room} {event} />
|
||||||
<button class="btn join-item btn-xs">
|
<button class="btn join-item btn-xs">
|
||||||
<Icon icon="menu-dots" size={4} />
|
<Icon icon="menu-dots" size={4} />
|
||||||
</button>
|
</button>
|
||||||
@@ -4,8 +4,8 @@
|
|||||||
import type {EventContent, TrustedEvent} from '@welshman/util'
|
import type {EventContent, TrustedEvent} from '@welshman/util'
|
||||||
import {repository, makeThunk, publishThunk} from '@welshman/app'
|
import {repository, makeThunk, publishThunk} from '@welshman/app'
|
||||||
import {deriveEvents} from '@welshman/store'
|
import {deriveEvents} from '@welshman/store'
|
||||||
import ChatMessage from '@app/components/ChatMessage.svelte'
|
import ChannelMessage from '@app/components/ChannelMessage.svelte'
|
||||||
import ChatCompose from '@app/components/ChatCompose.svelte'
|
import ChannelCompose from '@app/components/ChannelCompose.svelte'
|
||||||
import {tagRoom, REPLY} from '@app/state'
|
import {tagRoom, REPLY} from '@app/state'
|
||||||
|
|
||||||
export let url, room, event: TrustedEvent
|
export let url, room, event: TrustedEvent
|
||||||
@@ -45,12 +45,12 @@
|
|||||||
|
|
||||||
<div class="fixed flex flex-col max-h-screen w-full gap-2">
|
<div class="fixed flex flex-col max-h-screen w-full gap-2">
|
||||||
<div class="overflow-auto pt-3">
|
<div class="overflow-auto pt-3">
|
||||||
<ChatMessage {url} {room} {event} showPubkey />
|
<ChannelMessage {url} {room} {event} showPubkey />
|
||||||
{#each sortBy(e => e.created_at, $replies) as reply (reply.id)}
|
{#each sortBy(e => e.created_at, $replies) as reply (reply.id)}
|
||||||
<ChatMessage {url} {room} event={reply} showPubkey hideParent />
|
<ChannelMessage {url} {room} event={reply} showPubkey hideParent />
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
<div class="bottom-0 left-0 right-0">
|
<div class="bottom-0 left-0 right-0">
|
||||||
<ChatCompose {onSubmit} />
|
<ChannelCompose {onSubmit} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
+73
-73
@@ -155,12 +155,12 @@ export const {
|
|||||||
|
|
||||||
// Messages
|
// Messages
|
||||||
|
|
||||||
export type ChatMessage = {
|
export type ChannelMessage = {
|
||||||
room: string
|
room: string
|
||||||
event: TrustedEvent
|
event: TrustedEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
export const readMessage = (event: TrustedEvent): Maybe<ChatMessage> => {
|
export const readMessage = (event: TrustedEvent): Maybe<ChannelMessage> => {
|
||||||
const rooms = event.tags.filter(nthEq(0, ROOM)).map(nth(1))
|
const rooms = event.tags.filter(nthEq(0, ROOM)).map(nth(1))
|
||||||
|
|
||||||
if (rooms.length > 1) return undefined
|
if (rooms.length > 1) return undefined
|
||||||
@@ -168,88 +168,40 @@ export const readMessage = (event: TrustedEvent): Maybe<ChatMessage> => {
|
|||||||
return {room: rooms[0] || "", event}
|
return {room: rooms[0] || "", event}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const chatMessages = deriveEventsMapped<ChatMessage>(repository, {
|
export const channelMessages = deriveEventsMapped<ChannelMessage>(repository, {
|
||||||
filters: [{kinds: [MESSAGE, REPLY]}],
|
filters: [{kinds: [MESSAGE, REPLY]}],
|
||||||
eventToItem: readMessage,
|
eventToItem: readMessage,
|
||||||
itemToEvent: item => item.event,
|
itemToEvent: item => item.event,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Chats
|
|
||||||
|
|
||||||
export type Chat = {
|
|
||||||
id: string
|
|
||||||
url: string
|
|
||||||
room: string
|
|
||||||
messages: ChatMessage[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export const makeChatId = (url: string, room: string) => `${url}'${room}`
|
|
||||||
|
|
||||||
export const splitChatId = (id: string) => id.split("'")
|
|
||||||
|
|
||||||
export const chats = derived([trackerStore, chatMessages], ([$tracker, $chatMessages]) => {
|
|
||||||
const messagesByChatId = new Map<string, ChatMessage[]>()
|
|
||||||
|
|
||||||
for (const message of $chatMessages) {
|
|
||||||
for (const url of $tracker.getRelays(message.event.id)) {
|
|
||||||
const chatId = makeChatId(url, message.room)
|
|
||||||
|
|
||||||
pushToMapKey(messagesByChatId, chatId, message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Array.from(messagesByChatId.entries()).map(([id, messages]) => {
|
|
||||||
const [url, room] = splitChatId(id)
|
|
||||||
|
|
||||||
return {id, url, room, messages}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
export const {
|
|
||||||
indexStore: chatsById,
|
|
||||||
deriveItem: deriveChat,
|
|
||||||
loadItem: loadChat,
|
|
||||||
} = collection({
|
|
||||||
name: "chats",
|
|
||||||
store: chats,
|
|
||||||
getKey: chat => chat.id,
|
|
||||||
load: (id: string, request: Partial<SubscribeRequestWithHandlers> = {}) => {
|
|
||||||
const [url, room] = splitChatId(id)
|
|
||||||
const chat = get(chatsById).get(id)
|
|
||||||
const timestamps = chat?.messages.map(m => m.event.created_at) || []
|
|
||||||
const since = Math.max(0, max(timestamps) - 3600)
|
|
||||||
|
|
||||||
return load({...request, relays: [url], filters: [{"#~": [room], since}]})
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
// Channels
|
// Channels
|
||||||
|
|
||||||
export const channelMessages = deriveEvents(repository, {filters: [{kinds: [DIRECT_MESSAGE]}]})
|
|
||||||
|
|
||||||
export type Channel = {
|
export type Channel = {
|
||||||
id: string
|
id: string
|
||||||
pubkeys: string[]
|
url: string
|
||||||
messages: TrustedEvent[]
|
room: string
|
||||||
|
messages: ChannelMessage[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const makeChannelId = (pubkeys: string[]) => sort(uniq(pubkeys)).join(",")
|
export const makeChannelId = (url: string, room: string) => `${url}'${room}`
|
||||||
|
|
||||||
export const splitChannelId = (id: string) => id.split(",")
|
export const splitChannelId = (id: string) => id.split("'")
|
||||||
|
|
||||||
export const channels = derived(channelMessages, $messages => {
|
export const channels = derived([trackerStore, channelMessages], ([$tracker, $channelMessages]) => {
|
||||||
const messagesByChannelId = new Map<string, TrustedEvent[]>()
|
const messagesByChannelId = new Map<string, ChannelMessage[]>()
|
||||||
|
|
||||||
for (const message of $messages) {
|
for (const message of $channelMessages) {
|
||||||
const channelId = makeChannelId(getPubkeyTagValues(message.tags))
|
for (const url of $tracker.getRelays(message.event.id)) {
|
||||||
|
const channelId = makeChannelId(url, message.room)
|
||||||
|
|
||||||
pushToMapKey(messagesByChannelId, channelId, message)
|
pushToMapKey(messagesByChannelId, channelId, message)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Array.from(messagesByChannelId.entries()).map(([id, messages]): Channel => {
|
return Array.from(messagesByChannelId.entries()).map(([id, messages]) => {
|
||||||
const pubkeys = splitChannelId(id)
|
const [url, room] = splitChannelId(id)
|
||||||
|
|
||||||
return {id, pubkeys, messages}
|
return {id, url, room, messages}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -261,11 +213,59 @@ export const {
|
|||||||
name: "channels",
|
name: "channels",
|
||||||
store: channels,
|
store: channels,
|
||||||
getKey: channel => channel.id,
|
getKey: channel => channel.id,
|
||||||
load: async (id: string, request: Partial<SubscribeRequestWithHandlers> = {}) => {
|
load: (id: string, request: Partial<SubscribeRequestWithHandlers> = {}) => {
|
||||||
const $pubkey = pubkey.get()
|
|
||||||
const [url, room] = splitChannelId(id)
|
const [url, room] = splitChannelId(id)
|
||||||
const channel = get(channelsById).get(id)
|
const channel = get(channelsById).get(id)
|
||||||
const timestamps = channel?.messages.map(e => e.created_at) || []
|
const timestamps = channel?.messages.map(m => m.event.created_at) || []
|
||||||
|
const since = Math.max(0, max(timestamps) - 3600)
|
||||||
|
|
||||||
|
return load({...request, relays: [url], filters: [{"#~": [room], since}]})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// Encrypted Chats
|
||||||
|
|
||||||
|
export const chatMessages = deriveEvents(repository, {filters: [{kinds: [DIRECT_MESSAGE]}]})
|
||||||
|
|
||||||
|
export type Chat = {
|
||||||
|
id: string
|
||||||
|
pubkeys: string[]
|
||||||
|
messages: TrustedEvent[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const makeChatId = (pubkeys: string[]) => sort(uniq(pubkeys)).join(",")
|
||||||
|
|
||||||
|
export const splitChatId = (id: string) => id.split(",")
|
||||||
|
|
||||||
|
export const chats = derived(chatMessages, $messages => {
|
||||||
|
const messagesByChatId = new Map<string, TrustedEvent[]>()
|
||||||
|
|
||||||
|
for (const message of $messages) {
|
||||||
|
const chatId = makeChatId(getPubkeyTagValues(message.tags))
|
||||||
|
|
||||||
|
pushToMapKey(messagesByChatId, chatId, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from(messagesByChatId.entries()).map(([id, messages]): Chat => {
|
||||||
|
const pubkeys = splitChatId(id)
|
||||||
|
|
||||||
|
return {id, pubkeys, messages}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
export const {
|
||||||
|
indexStore: chatsById,
|
||||||
|
deriveItem: deriveChat,
|
||||||
|
loadItem: loadChat,
|
||||||
|
} = collection({
|
||||||
|
name: "chats",
|
||||||
|
store: chats,
|
||||||
|
getKey: chat => chat.id,
|
||||||
|
load: async (id: string, request: Partial<SubscribeRequestWithHandlers> = {}) => {
|
||||||
|
const $pubkey = pubkey.get()
|
||||||
|
const [url, room] = splitChatId(id)
|
||||||
|
const chat = get(chatsById).get(id)
|
||||||
|
const timestamps = chat?.messages.map(e => e.created_at) || []
|
||||||
const since = Math.max(0, max(timestamps) - 3600)
|
const since = Math.max(0, max(timestamps) - 3600)
|
||||||
|
|
||||||
if ($pubkey) {
|
if ($pubkey) {
|
||||||
@@ -329,12 +329,12 @@ export const threadsByUrl = derived([trackerStore, notes], ([$tracker, $notes])
|
|||||||
|
|
||||||
// Rooms
|
// Rooms
|
||||||
|
|
||||||
export const roomsByUrl = derived(chats, $chats => {
|
export const roomsByUrl = derived(channels, $channels => {
|
||||||
const $roomsByUrl = new Map<string, string[]>()
|
const $roomsByUrl = new Map<string, string[]>()
|
||||||
|
|
||||||
for (const chat of $chats) {
|
for (const channel of $channels) {
|
||||||
if (chat.room) {
|
if (channel.room) {
|
||||||
pushToMapKey($roomsByUrl, chat.url, chat.room)
|
pushToMapKey($roomsByUrl, channel.url, channel.room)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
import SecondaryNavItem from "@lib/components/SecondaryNavItem.svelte"
|
import SecondaryNavItem from "@lib/components/SecondaryNavItem.svelte"
|
||||||
import SecondaryNavHeader from "@lib/components/SecondaryNavHeader.svelte"
|
import SecondaryNavHeader from "@lib/components/SecondaryNavHeader.svelte"
|
||||||
import SecondaryNavSection from "@lib/components/SecondaryNavSection.svelte"
|
import SecondaryNavSection from "@lib/components/SecondaryNavSection.svelte"
|
||||||
import {channels} from '@app/state'
|
import {chats} from '@app/state'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<SecondaryNav>
|
<SecondaryNav>
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</SecondaryNavHeader>
|
</SecondaryNavHeader>
|
||||||
</div>
|
</div>
|
||||||
{#each $channels as {id, pubkeys}, i (id)}
|
{#each $chats as {id, pubkeys}, i (id)}
|
||||||
<div in:fly={{delay: 200 + i * 50}}>
|
<div in:fly={{delay: 200 + i * 50}}>
|
||||||
<SecondaryNavItem href="/home/{id}">
|
<SecondaryNavItem href="/home/{id}">
|
||||||
{id}
|
{id}
|
||||||
|
|||||||
@@ -18,14 +18,14 @@
|
|||||||
import Button from "@lib/components/Button.svelte"
|
import Button from "@lib/components/Button.svelte"
|
||||||
import Spinner from "@lib/components/Spinner.svelte"
|
import Spinner from "@lib/components/Spinner.svelte"
|
||||||
import Divider from "@lib/components/Divider.svelte"
|
import Divider from "@lib/components/Divider.svelte"
|
||||||
import ChatMessage from "@app/components/ChatMessage.svelte"
|
import ChannelMessage from "@app/components/ChannelMessage.svelte"
|
||||||
import ChatCompose from "@app/components/ChatCompose.svelte"
|
import ChannelCompose from "@app/components/ChannelCompose.svelte"
|
||||||
import {userMembership, decodeNRelay, makeChatId, deriveChat, GENERAL, tagRoom, MESSAGE} from "@app/state"
|
import {userMembership, decodeNRelay, makeChannelId, deriveChannel, GENERAL, tagRoom, MESSAGE} from "@app/state"
|
||||||
import {addRoomMembership, removeRoomMembership} from "@app/commands"
|
import {addRoomMembership, removeRoomMembership} from "@app/commands"
|
||||||
|
|
||||||
const {nrelay, room = GENERAL} = $page.params
|
const {nrelay, room = GENERAL} = $page.params
|
||||||
const url = decodeNRelay(nrelay)
|
const url = decodeNRelay(nrelay)
|
||||||
const chat = deriveChat(makeChatId(url, room))
|
const channel = deriveChannel(makeChannelId(url, room))
|
||||||
|
|
||||||
const assertEvent = (e: any) => e as TrustedEvent
|
const assertEvent = (e: any) => e as TrustedEvent
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
let previousDate
|
let previousDate
|
||||||
let previousPubkey
|
let previousPubkey
|
||||||
|
|
||||||
for (const {event} of sortBy(m => m.event.created_at, $chat?.messages || [])) {
|
for (const {event} of sortBy(m => m.event.created_at, $channel?.messages || [])) {
|
||||||
const {id, pubkey, created_at} = event
|
const {id, pubkey, created_at} = event
|
||||||
const date = formatTimestampAsDate(created_at)
|
const date = formatTimestampAsDate(created_at)
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@
|
|||||||
<Divider>{value}</Divider>
|
<Divider>{value}</Divider>
|
||||||
{:else}
|
{:else}
|
||||||
<div in:fly>
|
<div in:fly>
|
||||||
<ChatMessage {url} {room} event={assertEvent(value)} {showPubkey} />
|
<ChannelMessage {url} {room} event={assertEvent(value)} {showPubkey} />
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
@@ -117,6 +117,6 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="shadow-top-xl border-t border-solid border-base-100 bg-base-100">
|
<div class="shadow-top-xl border-t border-solid border-base-100 bg-base-100">
|
||||||
<ChatCompose {onSubmit} />
|
<ChannelCompose {onSubmit} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user