Apply layout changes to chat

This commit is contained in:
Jon Staab
2025-03-04 10:20:06 -08:00
parent 1c0b2a09df
commit a582b1ea73
5 changed files with 177 additions and 140 deletions
+1
View File
@@ -10,6 +10,7 @@
* Fix nevent hints for url-specific stuff * Fix nevent hints for url-specific stuff
* Add alerts via Anchor * Add alerts via Anchor
* Fix confirm and reactions on mobile * Fix confirm and reactions on mobile
* Add reply to chat on mobile
# 0.2.11 # 0.2.11
+2
View File
@@ -99,6 +99,7 @@
<div class="row-2 ml-10 mt-1"> <div class="row-2 ml-10 mt-1">
<ReactionSummary {url} {event} {onReactionClick} reactionClass="tooltip-right" /> <ReactionSummary {url} {event} {onReactionClick} reactionClass="tooltip-right" />
</div> </div>
{#if !isMobile}
<button <button
class="join absolute right-1 top-1 border border-solid border-neutral text-xs opacity-0 transition-all" class="join absolute right-1 top-1 border border-solid border-neutral text-xs opacity-0 transition-all"
class:group-hover:opacity-100={!isMobile}> class:group-hover:opacity-100={!isMobile}>
@@ -110,4 +111,5 @@
{/if} {/if}
<ChannelMessageMenuButton {url} {event} /> <ChannelMessageMenuButton {url} {event} />
</button> </button>
{/if}
</TapTarget> </TapTarget>
+25 -10
View File
@@ -70,6 +70,8 @@
let loading = $state(true) let loading = $state(true)
let compose: ChatCompose | undefined = $state() let compose: ChatCompose | undefined = $state()
let parent: TrustedEvent | undefined = $state() let parent: TrustedEvent | undefined = $state()
let parentPreview: HTMLElement | undefined = $state()
let dynamicPadding: HTMLElement | undefined = $state()
const elements = $derived.by(() => { const elements = $derived.by(() => {
const elements = [] const elements = []
@@ -104,6 +106,16 @@
onMount(() => { onMount(() => {
// Don't use loadInboxRelaySelection because we want to force reload // Don't use loadInboxRelaySelection because we want to force reload
load({filters: [{kinds: [INBOX_RELAYS], authors: others}]}) load({filters: [{kinds: [INBOX_RELAYS], authors: others}]})
const observer = new ResizeObserver(() => {
dynamicPadding!.style.minHeight = `${parentPreview!.offsetHeight}px`
})
observer.observe(parentPreview!)
return () => {
observer.unobserve(parentPreview!)
}
}) })
setTimeout(() => { setTimeout(() => {
@@ -111,9 +123,8 @@
}, 5000) }, 5000)
</script> </script>
<div class="relative flex h-full w-full flex-col"> {#if others.length > 0}
{#if others.length > 0} <PageBar class="chat__page-bar">
<PageBar>
{#snippet title()} {#snippet title()}
<div class="flex flex-col gap-1 sm:flex-row sm:gap-2"> <div class="flex flex-col gap-1 sm:flex-row sm:gap-2">
{#if others.length === 1} {#if others.length === 1}
@@ -159,8 +170,10 @@
</div> </div>
{/snippet} {/snippet}
</PageBar> </PageBar>
{/if} {/if}
<div class="-mt-2 flex flex-grow flex-col-reverse overflow-auto py-2">
<div class="chat__messages scroll-container">
<div bind:this={dynamicPadding}></div>
{#if missingInboxes.includes($pubkey!)} {#if missingInboxes.includes($pubkey!)}
<div class="py-12"> <div class="py-12">
<div class="card2 col-2 m-auto max-w-md items-center text-center"> <div class="card2 col-2 m-auto max-w-md items-center text-center">
@@ -170,8 +183,7 @@
</p> </p>
<p> <p>
In order to deliver messages, {PLATFORM_NAME} needs to know where to send them. Please visit In order to deliver messages, {PLATFORM_NAME} needs to know where to send them. Please visit
your <Link class="link" href="/settings/relays">relay settings page</Link> to set up your your <Link class="link" href="/settings/relays">relay settings page</Link> to set up your inbox.
inbox.
</p> </p>
</div> </div>
</div> </div>
@@ -201,8 +213,7 @@
{replyTo} /> {replyTo} />
{/if} {/if}
{/each} {/each}
<p <p class="m-auto flex h-10 max-w-sm flex-col items-center justify-center gap-4 py-20 text-center">
class="m-auto flex h-10 max-w-sm flex-col items-center justify-center gap-4 py-20 text-center">
<Spinner {loading}> <Spinner {loading}>
{#if loading} {#if loading}
Looking for messages... Looking for messages...
@@ -212,9 +223,13 @@
</Spinner> </Spinner>
{@render info?.()} {@render info?.()}
</p> </p>
</div> </div>
<div class="chat__compose bg-base-200">
<div bind:this={parentPreview}>
{#if parent} {#if parent}
<ChatComposeParent event={parent} clear={clearParent} verb="Replying to" /> <ChatComposeParent event={parent} clear={clearParent} verb="Replying to" />
{/if} {/if}
</div>
<ChatCompose bind:this={compose} {onSubmit} /> <ChatCompose bind:this={compose} {onSubmit} />
</div> </div>
+8 -4
View File
@@ -27,12 +27,12 @@
interface Props { interface Props {
event: TrustedEvent event: TrustedEvent
replyTo?: any replyTo: (event: TrustedEvent) => void
pubkeys: string[] pubkeys: string[]
showPubkey?: boolean showPubkey?: boolean
} }
const {event, replyTo = undefined, pubkeys, showPubkey = false}: Props = $props() const {event, replyTo, pubkeys, showPubkey = false}: Props = $props()
const thunk = $thunks[event.id] const thunk = $thunks[event.id]
const isOwn = event.pubkey === $pubkey const isOwn = event.pubkey === $pubkey
@@ -40,6 +40,8 @@
const profileDisplay = deriveProfileDisplay(event.pubkey) const profileDisplay = deriveProfileDisplay(event.pubkey)
const [_, colorValue] = colors[parseInt(hash(event.pubkey)) % colors.length] const [_, colorValue] = colors[parseInt(hash(event.pubkey)) % colors.length]
const reply = () => replyTo(event)
const onReactionClick = async (content: string, events: TrustedEvent[]) => { const onReactionClick = async (content: string, events: TrustedEvent[]) => {
const reaction = events.find(e => e.pubkey === $pubkey) const reaction = events.find(e => e.pubkey === $pubkey)
const template = reaction ? makeDelete({event: reaction}) : makeReaction({event, content}) const template = reaction ? makeDelete({event: reaction}) : makeReaction({event, content})
@@ -49,7 +51,7 @@
const openProfile = () => pushModal(ProfileDetail, {pubkey: event.pubkey}) const openProfile = () => pushModal(ProfileDetail, {pubkey: event.pubkey})
const showMobileMenu = () => pushModal(ChatMessageMenuMobile, {event, pubkeys}) const showMobileMenu = () => pushModal(ChatMessageMenuMobile, {event, pubkeys, reply})
const togglePopover = () => { const togglePopover = () => {
if (popoverIsVisible) { if (popoverIsVisible) {
@@ -72,6 +74,7 @@
class:chat-start={!isOwn} class:chat-start={!isOwn}
class:flex-row-reverse={!isOwn} class:flex-row-reverse={!isOwn}
class:chat-end={isOwn}> class:chat-end={isOwn}>
{#if !isMobile}
<Tippy <Tippy
bind:popover bind:popover
component={ChatMessageMenu} component={ChatMessageMenu}
@@ -94,9 +97,10 @@
<Icon icon="menu-dots" size={4} /> <Icon icon="menu-dots" size={4} />
</button> </button>
</Tippy> </Tippy>
{/if}
<div class="flex min-w-0 flex-col" class:items-end={isOwn}> <div class="flex min-w-0 flex-col" class:items-end={isOwn}>
<TapTarget <TapTarget
class="bg-alt chat-bubble mx-1 flex cursor-auto flex-col gap-1 text-left lg:max-w-2xl" class="bg-alt chat-bubble mx-1 mb-2 flex cursor-auto flex-col gap-1 text-left lg:max-w-2xl"
onTap={showMobileMenu}> onTap={showMobileMenu}>
{#if showPubkey} {#if showPubkey}
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
@@ -9,7 +9,13 @@
import {pushModal} from "@app/modal" import {pushModal} from "@app/modal"
import {clip} from "@app/toast" import {clip} from "@app/toast"
const {event, pubkeys} = $props() type Props = {
pubkeys: string[]
event: TrustedEvent
reply: () => void
}
const {event, pubkeys, reply}: Props = $props()
const onEmoji = ((event: TrustedEvent, emoji: NativeEmoji) => { const onEmoji = ((event: TrustedEvent, emoji: NativeEmoji) => {
history.back() history.back()
@@ -18,6 +24,11 @@
const showEmojiPicker = () => pushModal(EmojiPicker, {onClick: onEmoji}, {replaceState: true}) const showEmojiPicker = () => pushModal(EmojiPicker, {onClick: onEmoji}, {replaceState: true})
const sendReply = () => {
history.back()
reply()
}
const copyText = () => { const copyText = () => {
history.back() history.back()
clip(event.content) clip(event.content)
@@ -31,6 +42,10 @@
<Icon size={4} icon="smile-circle" /> <Icon size={4} icon="smile-circle" />
Send Reaction Send Reaction
</Button> </Button>
<Button class="btn btn-neutral w-full" onclick={sendReply}>
<Icon size={4} icon="reply" />
Send Reply
</Button>
<Button class="btn btn-neutral w-full" onclick={copyText}> <Button class="btn btn-neutral w-full" onclick={copyText}>
<Icon size={4} icon="copy" /> <Icon size={4} icon="copy" />
Copy Text Copy Text