feat:(#145) add start chat FAB #152
@@ -1,6 +1,5 @@
|
||||
<script lang="ts">
|
||||
import {assoc} from "@welshman/lib"
|
||||
import ChatSquare from "@assets/icons/chat-square.svg?dataurl"
|
||||
import Check from "@assets/icons/check.svg?dataurl"
|
||||
import Bell from "@assets/icons/bell.svg?dataurl"
|
||||
import BellOff from "@assets/icons/bell-off.svg?dataurl"
|
||||
@@ -8,13 +7,9 @@
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import Modal from "@lib/components/Modal.svelte"
|
||||
import ModalBody from "@lib/components/ModalBody.svelte"
|
||||
import ChatStart from "@app/components/ChatStart.svelte"
|
||||
import {setChecked} from "@app/util/notifications"
|
||||
import {pushModal} from "@app/util/modal"
|
||||
import {notificationSettings} from "@app/core/state"
|
||||
|
||||
const startChat = () => pushModal(ChatStart, {}, {replaceState: true})
|
||||
|
||||
const markAsRead = () => {
|
||||
setChecked("/chat/*")
|
||||
history.back()
|
||||
@@ -28,10 +23,6 @@
|
||||
<Modal>
|
||||
<ModalBody>
|
||||
<div class="flex flex-col gap-2">
|
||||
<Button class="btn btn-primary" onclick={startChat}>
|
||||
<Icon size={5} icon={ChatSquare} />
|
||||
Start chat
|
||||
</Button>
|
||||
<Button class="btn btn-neutral" onclick={markAsRead}>
|
||||
<Icon size={5} icon={Check} />
|
||||
Mark all read
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
<script lang="ts">
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
|
||||
const {
|
||||
onclick = () => {},
|
||||
className = "",
|
||||
size = 54,
|
||||
}: {
|
||||
onclick?: () => void
|
||||
className?: string
|
||||
size?: number
|
||||
} = $props()
|
||||
|
||||
const bubbleSize = $derived(Math.round(size * 0.55))
|
||||
const plusSize = $derived(Math.round(size * 0.33))
|
||||
</script>
|
||||
|
||||
<div class="fixed bottom-20 right-sai z-nav m-4 md:m-8 hide-on-keyboard {className}">
|
||||
<Button
|
||||
class="btn bg-[#7161ff] border-none text-white shadow-xl hover:bg-[#5e51d6] transition-all p-0"
|
||||
style="width: {size}px; height: {size}px; border-radius: {size * 0.33}px;"
|
||||
{onclick}>
|
||||
<div class="relative" style="width: {bubbleSize}px; height: {bubbleSize}px;" data-node-id="2:8">
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="white"
|
||||
stroke="white"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="absolute inset-0 size-full">
|
||||
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
|
||||
</svg>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="#7161ff"
|
||||
stroke-width="2.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="absolute left-1/2 top-[42%] -translate-x-1/2 -translate-y-1/2"
|
||||
style="width: {plusSize}px; height: {plusSize}px;">
|
||||
<line x1="12" y1="5" x2="12" y2="19" />
|
||||
<line x1="5" y1="12" x2="19" y2="12" />
|
||||
</svg>
|
||||
|
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
@@ -10,10 +10,12 @@
|
||||
import Page from "@lib/components/Page.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import Spinner from "@lib/components/Spinner.svelte"
|
||||
import FAB from "@lib/components/FAB.svelte"
|
||||
import SecondaryNav from "@lib/components/SecondaryNav.svelte"
|
||||
import SecondaryNavHeader from "@lib/components/SecondaryNavHeader.svelte"
|
||||
import SecondaryNavSection from "@lib/components/SecondaryNavSection.svelte"
|
||||
import ChatMenu from "@app/components/ChatMenu.svelte"
|
||||
import ChatStart from "@app/components/ChatStart.svelte"
|
||||
import ChatItem from "@app/components/ChatItem.svelte"
|
||||
import {chatSearch} from "@app/core/state"
|
||||
import {pushModal} from "@app/util/modal"
|
||||
@@ -26,18 +28,22 @@
|
||||
|
||||
const openMenu = () => pushModal(ChatMenu)
|
||||
|
||||
const startChat = () => pushModal(ChatStart)
|
||||
|
||||
let term = $state("")
|
||||
let isClient = $state(false)
|
||||
|
hodlbod marked this conversation as resolved
Outdated
hodlbod
commented
We don't do SSR, so there's no need to add this check We don't do SSR, so there's no need to add this check
|
||||
|
||||
const chats = $derived($chatSearch.searchOptions(term))
|
||||
|
||||
const promise = sleep(10000)
|
||||
|
||||
onMount(() => {
|
||||
isClient = true
|
||||
shouldUnwrap.set(true)
|
||||
})
|
||||
</script>
|
||||
|
||||
<SecondaryNav>
|
||||
<SecondaryNav class="relative">
|
||||
<SecondaryNavSection>
|
||||
<SecondaryNavHeader>
|
||||
Chats
|
||||
@@ -50,7 +56,7 @@
|
||||
<Icon icon={Magnifier} />
|
||||
<input bind:value={term} class="grow" type="text" />
|
||||
</label>
|
||||
<div class="overflow-auto">
|
||||
<div class="overflow-auto pb-20">
|
||||
{#each chats as { id, pubkeys, messages } (id)}
|
||||
<ChatItem {id} {pubkeys} {messages} />
|
||||
{/each}
|
||||
@@ -60,6 +66,11 @@
|
||||
</div>
|
||||
{/await}
|
||||
</div>
|
||||
{#if isClient}
|
||||
<div class="absolute bottom-10 right-4 hidden md:block">
|
||||
<FAB onclick={startChat} size={44} className="!static !m-0" />
|
||||
</div>
|
||||
|
hodlbod
commented
We shouldn't need a wrapper and important overrides. I think it would make sense to show the FAB on the bottom right of the screen on desktop, rather than in the secondary nav. We shouldn't need a wrapper and important overrides. I think it would make sense to show the FAB on the bottom right of the screen on desktop, rather than in the secondary nav.
Prat_09
commented
Thanks for the feedback @hodlbod ! I’ll refactor the FAB to use the Icon component and clean up the SSR-related logic. Regarding FAB placement, I noticed that having it fixed at the bottom-right works well generally, but in desktop during an active conversation it can overlap message content or interfere with interactions near the input area. Would it make sense to:
This keeps the action easily accessible while adapting to the context. Happy to implement this if it aligns with your vision. Thanks for the feedback @hodlbod ! I’ll refactor the FAB to use the Icon component and clean up the SSR-related logic.
Regarding FAB placement, I noticed that having it fixed at the bottom-right works well generally, but in desktop during an active conversation it can overlap message content or interfere with interactions near the input area.
Would it make sense to:
* Show the FAB as a floating button (bottom-right) when the user is not inside a conversation, and
* Place it in the secondary nav when inside an active conversation to avoid overlap?
This keeps the action easily accessible while adapting to the context. Happy to implement this if it aligns with your vision.
|
||||
{/if}
|
||||
</SecondaryNav>
|
||||
<Page>
|
||||
{#key $page.url.pathname}
|
||||
|
||||
@@ -11,8 +11,15 @@
|
||||
import ChatMenu from "@app/components/ChatMenu.svelte"
|
||||
import {chatSearch} from "@app/core/state"
|
||||
import {pushModal} from "@app/util/modal"
|
||||
import FAB from "@lib/components/FAB.svelte"
|
||||
import {onMount} from "svelte"
|
||||
|
||||
let term = $state("")
|
||||
let isClient = $state(false)
|
||||
|
||||
onMount(() => {
|
||||
isClient = true
|
||||
})
|
||||
|
hodlbod
commented
No need to include this No need to include this
|
||||
|
||||
const startChat = () => pushModal(ChatStart)
|
||||
|
||||
@@ -64,6 +71,9 @@
|
||||
</Button>
|
||||
</div>
|
||||
{/each}
|
||||
{#if isClient}
|
||||
<FAB onclick={startChat} className="md:hidden" />
|
||||
{/if}
|
||||
</div>
|
||||
{/snippet}
|
||||
</ContentSearch>
|
||||
|
||||
Reference in New Issue
Block a user
The SVGs shouldn't be hard-coded in here. Instead, include a
childrensnippet in props and use theIconcomponent with one of the icons already included in the project.