add new chat logic and message service
This commit is contained in:
@@ -3,7 +3,9 @@
|
||||
import { onMount } from 'svelte';
|
||||
import { initPbFromCookie } from '$lib/pocketbase';
|
||||
import { famStore } from '$lib/stores/fam.svelte';
|
||||
import { Sidebar, TopNav, Footer } from '$lib/components';
|
||||
import { chatStore } from '$lib/stores/chat.svelte';
|
||||
import { Sidebar, TopNav, Footer, Chat } from '$lib/components';
|
||||
import { chatIcon } from '$lib/components/icons';
|
||||
import type { Session } from '$lib/types';
|
||||
|
||||
let { children, data } = $props();
|
||||
@@ -24,7 +26,9 @@
|
||||
const rewards = famStore.rewards;
|
||||
if (!mounted) {
|
||||
mounted = true;
|
||||
prevClaimedIds = new Set(rewards.filter((r: any) => r.status === 'requested').map((r: any) => r.id));
|
||||
prevClaimedIds = new Set(
|
||||
rewards.filter((r: any) => r.status === 'requested').map((r: any) => r.id)
|
||||
);
|
||||
return;
|
||||
}
|
||||
for (const r of rewards) {
|
||||
@@ -35,25 +39,52 @@
|
||||
setTimeout(() => (claimToast = ''), 5000);
|
||||
}
|
||||
}
|
||||
prevClaimedIds = new Set(rewards.filter((r: any) => r.status === 'requested').map((r: any) => r.id));
|
||||
prevClaimedIds = new Set(
|
||||
rewards.filter((r: any) => r.status === 'requested').map((r: any) => r.id)
|
||||
);
|
||||
});
|
||||
|
||||
onMount(() => {
|
||||
initPbFromCookie();
|
||||
if (page.data.famId) famStore.init(page.data.famId);
|
||||
const chat = page.data.chat;
|
||||
if (chat?.famId && chat?.actor) {
|
||||
chatStore.init({
|
||||
famId: chat.famId,
|
||||
actorId: chat.actor.id,
|
||||
actorType: chat.actor.type,
|
||||
actorName: chat.actor.name,
|
||||
actorColor: chat.actor.color || '#6366f1',
|
||||
deviceToken: page.data.deviceToken || ''
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="app-shell">
|
||||
<Sidebar {famName} session={data.session} {isParent} {role} />
|
||||
<TopNav {role} seasons={famStore.seasons} />
|
||||
<main class="app-main">
|
||||
{@render children()}
|
||||
</main>
|
||||
{#if claimToast}
|
||||
<div class="claim-toast">{claimToast}</div>
|
||||
<div class="layout-stage" class:chat-open={chatStore.open}>
|
||||
<div class="app-shell">
|
||||
<Sidebar {famName} session={data.session} {isParent} {role} />
|
||||
<TopNav {role} seasons={famStore.seasons}>
|
||||
<button class="chat-toggle" onclick={() => chatStore.toggle()} aria-label="Open chat">
|
||||
{@html chatIcon}
|
||||
{#if chatStore.unread > 0}
|
||||
<span class="chat-badge">{chatStore.unread > 9 ? '9+' : chatStore.unread}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</TopNav>
|
||||
<main class="app-main">
|
||||
{@render children()}
|
||||
</main>
|
||||
{#if claimToast}
|
||||
<div class="claim-toast">{claimToast}</div>
|
||||
{/if}
|
||||
<Footer />
|
||||
</div>
|
||||
{#if chatStore.open}
|
||||
<button class="chat-backdrop" onclick={() => chatStore.closeChat()} aria-label="Close chat"
|
||||
></button>
|
||||
{/if}
|
||||
<Footer />
|
||||
<Chat {role} />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
@@ -75,6 +106,28 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: whitesmoke;
|
||||
width: 100%;
|
||||
transition: transform 0.25s ease;
|
||||
}
|
||||
/* Desktop: slide the whole app-shell left to reveal the 500px chat on the right. */
|
||||
@media (min-width: 768px) {
|
||||
.layout-stage.chat-open .app-shell {
|
||||
transform: translateX(-500px);
|
||||
}
|
||||
}
|
||||
/* Mobile: chat covers the screen; app stays put under a dimmed backdrop. */
|
||||
.chat-backdrop {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
border: none;
|
||||
z-index: 105;
|
||||
display: none;
|
||||
}
|
||||
@media (max-width: 767.98px) {
|
||||
.chat-backdrop {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
.app-main {
|
||||
margin-left: 220px;
|
||||
@@ -83,4 +136,37 @@
|
||||
flex: 1;
|
||||
transition: margin-left 0.2s;
|
||||
}
|
||||
.chat-toggle {
|
||||
position: relative;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
background: #f3f4f6;
|
||||
color: #374151;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
.chat-toggle:hover {
|
||||
background: #e5e7eb;
|
||||
color: #4338ca;
|
||||
}
|
||||
.chat-badge {
|
||||
position: absolute;
|
||||
top: -4px;
|
||||
right: -4px;
|
||||
background: #10b981;
|
||||
color: #fff;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 700;
|
||||
min-width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 999px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 4px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user