forked from coracle/flotilla
Add drawer, start working on replies
This commit is contained in:
+31
-1
@@ -1,5 +1,6 @@
|
|||||||
import {uniqBy, sleep, chunk, equals, choice} from "@welshman/lib"
|
import {uniqBy, sleep, chunk, equals, choice} from "@welshman/lib"
|
||||||
import {getPubkeyTagValues, createEvent, displayProfile} from "@welshman/util"
|
import {DELETE, REACTION, getPubkeyTagValues, createEvent, displayProfile} from "@welshman/util"
|
||||||
|
import type {TrustedEvent} from "@welshman/util"
|
||||||
import type {SubscribeRequestWithHandlers} from "@welshman/net"
|
import type {SubscribeRequestWithHandlers} from "@welshman/net"
|
||||||
import {
|
import {
|
||||||
pubkey,
|
pubkey,
|
||||||
@@ -13,6 +14,8 @@ import {
|
|||||||
loadFollows,
|
loadFollows,
|
||||||
loadMutes,
|
loadMutes,
|
||||||
getFollows,
|
getFollows,
|
||||||
|
tagEvent,
|
||||||
|
tagReactionTo,
|
||||||
} from "@welshman/app"
|
} from "@welshman/app"
|
||||||
import {ROOM, MEMBERSHIPS, INDEXER_RELAYS} from "@app/state"
|
import {ROOM, MEMBERSHIPS, INDEXER_RELAYS} from "@app/state"
|
||||||
|
|
||||||
@@ -106,3 +109,30 @@ export const removeSpaceMembership = (url: string) =>
|
|||||||
|
|
||||||
export const removeRoomMembership = (url: string, room: string) =>
|
export const removeRoomMembership = (url: string, room: string) =>
|
||||||
updateList(MEMBERSHIPS, (tags: string[][]) => tags.filter(t => !equals([ROOM, room, url], t)))
|
updateList(MEMBERSHIPS, (tags: string[][]) => tags.filter(t => !equals([ROOM, room, url], t)))
|
||||||
|
|
||||||
|
// Actions
|
||||||
|
|
||||||
|
export const publishReaction = ({relays, event, content, tags = []}: {
|
||||||
|
relays: string[]
|
||||||
|
event: TrustedEvent,
|
||||||
|
content: string,
|
||||||
|
tags?: string[][]
|
||||||
|
}) => {
|
||||||
|
const reaction = createEvent(REACTION, {
|
||||||
|
content,
|
||||||
|
tags: [
|
||||||
|
...tags,
|
||||||
|
...tagReactionTo(event),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
publishThunk(makeThunk({event: reaction, relays}))
|
||||||
|
}
|
||||||
|
|
||||||
|
export const publishDelete = ({relays, event}: {relays: string[], event: TrustedEvent}) => {
|
||||||
|
const deleteEvent = createEvent(DELETE, {
|
||||||
|
tags: [["k", String(event.kind)], ...tagEvent(event)],
|
||||||
|
})
|
||||||
|
|
||||||
|
publishThunk(makeThunk({event: deleteEvent, relays}))
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {onMount} from "svelte"
|
import {onMount} from "svelte"
|
||||||
|
import type {SvelteComponent} from "svelte"
|
||||||
import type {NativeEmoji} from 'emoji-picker-element/shared'
|
import type {NativeEmoji} from 'emoji-picker-element/shared'
|
||||||
import twColors from "tailwindcss/colors"
|
import twColors from "tailwindcss/colors"
|
||||||
import type {Readable} from "svelte/store"
|
import type {Readable} from "svelte/store"
|
||||||
@@ -26,14 +27,19 @@
|
|||||||
import Icon from "@lib/components/Icon.svelte"
|
import Icon from "@lib/components/Icon.svelte"
|
||||||
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 Drawer from "@lib/components/Drawer.svelte"
|
||||||
import Content from "@app/components/Content.svelte"
|
import Content from "@app/components/Content.svelte"
|
||||||
import ChatMessageEmojiButton from "@app/components/ChatMessageEmojiButton.svelte"
|
import ChatMessageEmojiButton from "@app/components/ChatMessageEmojiButton.svelte"
|
||||||
|
import ChatMessageReplies from "@app/components/ChatMessageReplies.svelte"
|
||||||
|
import ChatMessageReply from "@app/components/ChatMessageReply.svelte"
|
||||||
import {ROOM, REPLY, deriveEvent, displayReaction} from "@app/state"
|
import {ROOM, REPLY, deriveEvent, displayReaction} from "@app/state"
|
||||||
|
import {publishDelete, publishReaction} from "@app/commands"
|
||||||
|
|
||||||
export let url
|
export let url
|
||||||
export let room
|
export let room
|
||||||
export let event: TrustedEvent
|
export let event: TrustedEvent
|
||||||
export let showPubkey: boolean
|
export let showPubkey: boolean
|
||||||
|
export let isReply = false
|
||||||
|
|
||||||
const colors = [
|
const colors = [
|
||||||
["amber", twColors.amber[600]],
|
["amber", twColors.amber[600]],
|
||||||
@@ -71,33 +77,23 @@
|
|||||||
const findStatus = ($ps: PublishStatusData[], statuses: PublishStatus[]) =>
|
const findStatus = ($ps: PublishStatusData[], statuses: PublishStatus[]) =>
|
||||||
$ps.find(({status}) => statuses.includes(status))
|
$ps.find(({status}) => statuses.includes(status))
|
||||||
|
|
||||||
const createReaction = (content: string) => {
|
|
||||||
const reaction = createEvent(REACTION, {
|
|
||||||
content,
|
|
||||||
tags: [
|
|
||||||
[ROOM, room, url],
|
|
||||||
...tagReactionTo(event),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
publishThunk(makeThunk({event: reaction, relays: [url]}))
|
|
||||||
}
|
|
||||||
|
|
||||||
const onReactionClick = (content: string, events: TrustedEvent[]) => {
|
const onReactionClick = (content: string, events: TrustedEvent[]) => {
|
||||||
const reaction = events.find(e => e.pubkey === $pubkey)
|
const reaction = events.find(e => e.pubkey === $pubkey)
|
||||||
|
|
||||||
if (reaction) {
|
if (reaction) {
|
||||||
const deleteEvent = createEvent(DELETE, {
|
publishDelete({relays: [url], event: reaction})
|
||||||
tags: [["k", String(reaction.kind)], ...tagEvent(reaction)],
|
|
||||||
})
|
|
||||||
|
|
||||||
publishThunk(makeThunk({event: deleteEvent, relays: [url]}))
|
|
||||||
} else {
|
} else {
|
||||||
createReaction(content)
|
publishReaction({
|
||||||
|
event,
|
||||||
|
content,
|
||||||
|
relays: [url],
|
||||||
|
tags: [[ROOM, room, url]],
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onEmoji = (emoji: NativeEmoji) => createReaction(emoji.unicode)
|
let drawer: SvelteComponent
|
||||||
|
|
||||||
$: parentPubkey = $parentEvent?.pubkey || replies[0]?.[4]
|
$: parentPubkey = $parentEvent?.pubkey || replies[0]?.[4]
|
||||||
$: parentProfile = deriveProfile(parentPubkey || "")
|
$: parentProfile = deriveProfile(parentPubkey || "")
|
||||||
@@ -108,7 +104,11 @@
|
|||||||
!isPending && !isPublished && findStatus($ps, [PublishStatus.Failure, PublishStatus.Timeout])
|
!isPending && !isPublished && findStatus($ps, [PublishStatus.Failure, PublishStatus.Timeout])
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div in:fly class="group relative flex flex-col gap-1 p-2 transition-colors hover:bg-base-300">
|
<div
|
||||||
|
in:fly
|
||||||
|
class="group relative flex flex-col gap-1 p-2 transition-colors"
|
||||||
|
class:hover:bg-base-300={!isReply}
|
||||||
|
class:mt-4={isReply}>
|
||||||
{#if event.kind === REPLY}
|
{#if event.kind === REPLY}
|
||||||
<div class="flex items-center gap-1 pl-12 text-xs">
|
<div class="flex items-center gap-1 pl-12 text-xs">
|
||||||
<Icon icon="arrow-right" />
|
<Icon icon="arrow-right" />
|
||||||
@@ -168,12 +168,22 @@
|
|||||||
{/if}
|
{/if}
|
||||||
<div
|
<div
|
||||||
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">
|
||||||
<button class="btn join-item btn-xs">
|
{#if !isReply}
|
||||||
<Icon icon="reply" size={4} />
|
<button class="btn join-item btn-xs" on:click={() => drawer.open()}>
|
||||||
</button>
|
<Icon icon="reply" size={4} />
|
||||||
<ChatMessageEmojiButton onEmoji={onEmoji} />
|
</button>
|
||||||
|
{/if}
|
||||||
|
<ChatMessageEmojiButton {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>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{#if !isReply}
|
||||||
|
<Drawer bind:this={drawer}>
|
||||||
|
<svelte:self {...$$props} isReply />
|
||||||
|
<ChatMessageReplies {url} {room} {event} />
|
||||||
|
<ChatMessageReply {url} {room} {event} />
|
||||||
|
</Drawer>
|
||||||
|
{/if}
|
||||||
|
|||||||
@@ -1,18 +1,26 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import tippy, {type Instance} from "tippy.js"
|
import tippy, {type Instance} from "tippy.js"
|
||||||
import type {Emoji} from 'emoji-picker-element/shared'
|
import type {NativeEmoji} from 'emoji-picker-element/shared'
|
||||||
import {between} from '@welshman/lib'
|
import {between} from '@welshman/lib'
|
||||||
import Icon from "@lib/components/Icon.svelte"
|
import Icon from "@lib/components/Icon.svelte"
|
||||||
import Button from "@lib/components/Button.svelte"
|
import Button from "@lib/components/Button.svelte"
|
||||||
import Tippy from "@lib/components/Tippy.svelte"
|
import Tippy from "@lib/components/Tippy.svelte"
|
||||||
import EmojiPicker from "@lib/components/EmojiPicker.svelte"
|
import EmojiPicker from "@lib/components/EmojiPicker.svelte"
|
||||||
|
import {ROOM} from '@app/state'
|
||||||
|
import {publishReaction} from '@app/commands'
|
||||||
|
|
||||||
export let onEmoji
|
export let url, room, event
|
||||||
|
|
||||||
const open = () => popover.show()
|
const open = () => popover.show()
|
||||||
|
|
||||||
const onClick = (emoji: Emoji) => {
|
const onClick = (emoji: NativeEmoji) => {
|
||||||
onEmoji(emoji)
|
publishReaction({
|
||||||
|
event,
|
||||||
|
relays: [url],
|
||||||
|
content: emoji.unicode,
|
||||||
|
tags: [[ROOM, room, url]],
|
||||||
|
})
|
||||||
|
|
||||||
popover.hide()
|
popover.hide()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
hi
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
hi
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {randomId} from '@welshman/lib'
|
||||||
|
import Icon from '@lib/components/Icon.svelte'
|
||||||
|
|
||||||
|
const id = randomId()
|
||||||
|
|
||||||
|
let input: any
|
||||||
|
let label: any
|
||||||
|
|
||||||
|
export const open = () => {
|
||||||
|
if (!input.checked) {
|
||||||
|
label.click()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const close = () => {
|
||||||
|
if (input.checked) {
|
||||||
|
label.click()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const toggle = () => {
|
||||||
|
label.click()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="drawer drawer-end">
|
||||||
|
<input {id} type="checkbox" class="drawer-toggle" bind:this={input} />
|
||||||
|
<div class="drawer-content">
|
||||||
|
<label for={id} bind:this={label} />
|
||||||
|
</div>
|
||||||
|
<div class="drawer-side z-modal">
|
||||||
|
<label for={id} aria-label="close sidebar" class="drawer-overlay"></label>
|
||||||
|
<div class="menu bg-base-200 text-base-content min-h-full w-80">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
Reference in New Issue
Block a user