feat: add "Convert to Thread" action on room messages (#370)
Co-authored-by: Aditya Chaudhary <30+useradityaa@noreply.coracle.social>
This commit is contained in:
parent
01a79d570b
commit
dfb971e7dd
3 changed files with 37 additions and 2 deletions
|
|
@ -6,6 +6,7 @@
|
||||||
import TrashBin2 from "@assets/icons/trash-bin-2.svg?dataurl"
|
import TrashBin2 from "@assets/icons/trash-bin-2.svg?dataurl"
|
||||||
import Danger from "@assets/icons/danger.svg?dataurl"
|
import Danger from "@assets/icons/danger.svg?dataurl"
|
||||||
import Pin from "@assets/icons/pin.svg?dataurl"
|
import Pin from "@assets/icons/pin.svg?dataurl"
|
||||||
|
import NotesMinimalistic from "@assets/icons/notes-minimalistic.svg?dataurl"
|
||||||
import Button from "@lib/components/Button.svelte"
|
import Button from "@lib/components/Button.svelte"
|
||||||
import Icon from "@lib/components/Icon.svelte"
|
import Icon from "@lib/components/Icon.svelte"
|
||||||
import Confirm from "@lib/components/Confirm.svelte"
|
import Confirm from "@lib/components/Confirm.svelte"
|
||||||
|
|
@ -13,6 +14,7 @@
|
||||||
import Report from "@app/components/Report.svelte"
|
import Report from "@app/components/Report.svelte"
|
||||||
import PinboardSelect from "@app/components/PinboardSelect.svelte"
|
import PinboardSelect from "@app/components/PinboardSelect.svelte"
|
||||||
import EventDeleteConfirm from "@app/components/EventDeleteConfirm.svelte"
|
import EventDeleteConfirm from "@app/components/EventDeleteConfirm.svelte"
|
||||||
|
import ThreadCreate from "@app/components/ThreadCreate.svelte"
|
||||||
import {app, relayManagement, roomPinLists, user} from "@app/core"
|
import {app, relayManagement, roomPinLists, user} from "@app/core"
|
||||||
import {deriveUserIsSpaceAdmin} from "@app/management"
|
import {deriveUserIsSpaceAdmin} from "@app/management"
|
||||||
import {ROOM, deriveUserIsRoomAdmin} from "@app/rooms"
|
import {ROOM, deriveUserIsRoomAdmin} from "@app/rooms"
|
||||||
|
|
@ -38,6 +40,11 @@
|
||||||
pushModal(PinboardSelect, {url, event})
|
pushModal(PinboardSelect, {url, event})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const createThread = () => {
|
||||||
|
onClick()
|
||||||
|
pushModal(ThreadCreate, {url, h, parent: event})
|
||||||
|
}
|
||||||
|
|
||||||
const report = () => {
|
const report = () => {
|
||||||
onClick()
|
onClick()
|
||||||
pushModal(Report, {url, event})
|
pushModal(Report, {url, event})
|
||||||
|
|
@ -100,6 +107,14 @@
|
||||||
Add to Library
|
Add to Library
|
||||||
</Button>
|
</Button>
|
||||||
</li>
|
</li>
|
||||||
|
{#if h}
|
||||||
|
<li>
|
||||||
|
<Button onclick={createThread}>
|
||||||
|
<Icon size={4} icon={NotesMinimalistic} />
|
||||||
|
Create a Thread
|
||||||
|
</Button>
|
||||||
|
</li>
|
||||||
|
{/if}
|
||||||
{#if h && $userIsRoomAdmin}
|
{#if h && $userIsRoomAdmin}
|
||||||
<li>
|
<li>
|
||||||
<Button onclick={togglePin}>
|
<Button onclick={togglePin}>
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
import SmileCircle from "@assets/icons/smile-circle.svg?dataurl"
|
import SmileCircle from "@assets/icons/smile-circle.svg?dataurl"
|
||||||
import MenuDots from "@assets/icons/menu-dots.svg?dataurl"
|
import MenuDots from "@assets/icons/menu-dots.svg?dataurl"
|
||||||
import Pin from "@assets/icons/pin.svg?dataurl"
|
import Pin from "@assets/icons/pin.svg?dataurl"
|
||||||
|
import NotesMinimalistic from "@assets/icons/notes-minimalistic.svg?dataurl"
|
||||||
import Button from "@lib/components/Button.svelte"
|
import Button from "@lib/components/Button.svelte"
|
||||||
import Link from "@lib/components/Link.svelte"
|
import Link from "@lib/components/Link.svelte"
|
||||||
import Icon from "@lib/components/Icon.svelte"
|
import Icon from "@lib/components/Icon.svelte"
|
||||||
|
|
@ -18,6 +19,7 @@
|
||||||
import ZapButton from "@app/components/ZapButton.svelte"
|
import ZapButton from "@app/components/ZapButton.svelte"
|
||||||
import EventInfo from "@app/components/EventInfo.svelte"
|
import EventInfo from "@app/components/EventInfo.svelte"
|
||||||
import EventDeleteConfirm from "@app/components/EventDeleteConfirm.svelte"
|
import EventDeleteConfirm from "@app/components/EventDeleteConfirm.svelte"
|
||||||
|
import ThreadCreate from "@app/components/ThreadCreate.svelte"
|
||||||
import {reactions, relays, roomPinLists, user} from "@app/core"
|
import {reactions, relays, roomPinLists, user} from "@app/core"
|
||||||
import {ROOM, deriveUserIsRoomAdmin} from "@app/rooms"
|
import {ROOM, deriveUserIsRoomAdmin} from "@app/rooms"
|
||||||
import {ENABLE_ZAPS} from "@app/env"
|
import {ENABLE_ZAPS} from "@app/env"
|
||||||
|
|
@ -50,6 +52,8 @@
|
||||||
|
|
||||||
const showEmojiPicker = () => pushModal(EmojiPicker, {onClick: onEmoji}, {replaceState: true})
|
const showEmojiPicker = () => pushModal(EmojiPicker, {onClick: onEmoji}, {replaceState: true})
|
||||||
|
|
||||||
|
const createThread = () => pushModal(ThreadCreate, {url, h, parent: event}, {replaceState: true})
|
||||||
|
|
||||||
const sendReply = () => {
|
const sendReply = () => {
|
||||||
history.back()
|
history.back()
|
||||||
reply()
|
reply()
|
||||||
|
|
@ -101,6 +105,12 @@
|
||||||
{isPinned ? "Unpin Message" : "Pin Message"}
|
{isPinned ? "Unpin Message" : "Pin Message"}
|
||||||
</Button>
|
</Button>
|
||||||
{/if}
|
{/if}
|
||||||
|
{#if h}
|
||||||
|
<Button class="button button-neutral w-full" onclick={createThread}>
|
||||||
|
<Icon size={4} icon={NotesMinimalistic} />
|
||||||
|
Create a Thread
|
||||||
|
</Button>
|
||||||
|
{/if}
|
||||||
{#if ENABLE_ZAPS}
|
{#if ENABLE_ZAPS}
|
||||||
<ZapButton replaceState {url} {event} class="button button-neutral w-full">
|
<ZapButton replaceState {url} {event} class="button button-neutral w-full">
|
||||||
<Icon size={4} icon={Bolt} />
|
<Icon size={4} icon={Bolt} />
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {writable} from "svelte/store"
|
import {writable} from "svelte/store"
|
||||||
|
import * as nip19 from "nostr-tools/nip19"
|
||||||
import {Thread} from "@welshman/domain"
|
import {Thread} from "@welshman/domain"
|
||||||
import {publish} from "@welshman/app"
|
import {publish} from "@welshman/app"
|
||||||
|
import {toNostrURI} from "@welshman/util"
|
||||||
|
import type {TrustedEvent} from "@welshman/util"
|
||||||
import {isMobile, preventDefault} from "@lib/html"
|
import {isMobile, preventDefault} from "@lib/html"
|
||||||
import Paperclip from "@assets/icons/paperclip-2.svg?dataurl"
|
import Paperclip from "@assets/icons/paperclip-2.svg?dataurl"
|
||||||
import AltArrowLeft from "@assets/icons/alt-arrow-left.svg?dataurl"
|
import AltArrowLeft from "@assets/icons/alt-arrow-left.svg?dataurl"
|
||||||
|
|
@ -31,9 +34,16 @@
|
||||||
url: string
|
url: string
|
||||||
h?: string
|
h?: string
|
||||||
shareToChat?: boolean
|
shareToChat?: boolean
|
||||||
|
// pre-fills the thread body with an embedded quote of this event (see
|
||||||
|
// "Create a Thread" in RoomItemMenu/RoomItemMenuMobile) — an unrelated
|
||||||
|
// in-progress draft still wins, same as opening this modal any other way.
|
||||||
|
parent?: TrustedEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
const {url, h, shareToChat = false}: Props = $props()
|
const {url, h, shareToChat = false, parent}: Props = $props()
|
||||||
|
const pointerContent = parent
|
||||||
|
? toNostrURI(nip19.neventEncode({...parent, relays: [url]}))
|
||||||
|
: undefined
|
||||||
const draftKey = new DraftKey<Values>(`thread:${url}:${h ?? ""}`)
|
const draftKey = new DraftKey<Values>(`thread:${url}:${h ?? ""}`)
|
||||||
const initialValues = draftKey.get()
|
const initialValues = draftKey.get()
|
||||||
const shouldProtect = $relays.hasNip(url, 70)
|
const shouldProtect = $relays.hasNip(url, 70)
|
||||||
|
|
@ -100,7 +110,7 @@
|
||||||
let loading = $state(false)
|
let loading = $state(false)
|
||||||
|
|
||||||
let title = $state(initialValues?.title ?? "")
|
let title = $state(initialValues?.title ?? "")
|
||||||
let content = $state(initialValues?.content ?? "")
|
let content = $state(initialValues?.content ?? pointerContent ?? "")
|
||||||
|
|
||||||
const onChange = (json: object) => {
|
const onChange = (json: object) => {
|
||||||
content = json
|
content = json
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue