fixed issues namely reveloved around chat
This commit is contained in:
@@ -14,6 +14,14 @@
|
|||||||
|
|
||||||
const isOwn = (msg: any) => msg.authorId === chatStore.actorId;
|
const isOwn = (msg: any) => msg.authorId === chatStore.actorId;
|
||||||
|
|
||||||
|
// Colour comes from the sender's user record (loaded with the family on
|
||||||
|
// page load) — no per-message snapshot, no live colour tracking. Falls back
|
||||||
|
// to the stored authorColor for any message sent before this change.
|
||||||
|
function colorOf(msg: any): string {
|
||||||
|
if (msg.authorId === chatStore.actorId) return chatStore.actorColor || '#6366f1';
|
||||||
|
return msg.authorColor || '#6366f1';
|
||||||
|
}
|
||||||
|
|
||||||
function scrollToBottom(instant = false) {
|
function scrollToBottom(instant = false) {
|
||||||
if (!container) return;
|
if (!container) return;
|
||||||
container.scrollTo({
|
container.scrollTo({
|
||||||
@@ -107,15 +115,18 @@
|
|||||||
{#each chatStore.messages as msg (msg.id)}
|
{#each chatStore.messages as msg (msg.id)}
|
||||||
<div class="msg-row" class:own={isOwn(msg)}>
|
<div class="msg-row" class:own={isOwn(msg)}>
|
||||||
{#if !isOwn(msg)}
|
{#if !isOwn(msg)}
|
||||||
<span class="avatar" style="background:{msg.authorColor || '#6366f1'}">
|
<span class="avatar" style="background:{colorOf(msg)}">
|
||||||
{msg.authorName?.charAt(0).toUpperCase()}
|
{msg.authorName?.charAt(0).toUpperCase()}
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="bubble-wrap">
|
<div class="bubble-wrap">
|
||||||
{#if !isOwn(msg)}
|
{#if !isOwn(msg)}
|
||||||
<span class="msg-author">{msg.authorName}</span>
|
<span class="msg-author" style="color:{colorOf(msg)}">{msg.authorName}</span>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="bubble">
|
<div
|
||||||
|
class="bubble"
|
||||||
|
style="background:{colorOf(msg) + '33'}; color:{colorOf(msg)}"
|
||||||
|
>
|
||||||
{#each renderContent(msg.content) as seg (msg.id + ':' + seg.text)}
|
{#each renderContent(msg.content) as seg (msg.id + ':' + seg.text)}
|
||||||
{#if seg.mention}
|
{#if seg.mention}
|
||||||
<button class="mention" onclick={() => insertMention(seg.text.slice(1))}
|
<button class="mention" onclick={() => insertMention(seg.text.slice(1))}
|
||||||
@@ -274,8 +285,6 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
.msg-row.own .bubble {
|
.msg-row.own .bubble {
|
||||||
background: #6366f1;
|
|
||||||
color: #fff;
|
|
||||||
border-bottom-left-radius: 14px;
|
border-bottom-left-radius: 14px;
|
||||||
border-bottom-right-radius: 4px;
|
border-bottom-right-radius: 4px;
|
||||||
}
|
}
|
||||||
@@ -288,7 +297,7 @@
|
|||||||
text-transform: capitalize;
|
text-transform: capitalize;
|
||||||
}
|
}
|
||||||
.msg-row.own .msg-time {
|
.msg-row.own .msg-time {
|
||||||
color: rgba(255, 255, 255, 0.7);
|
color: rgba(17, 24, 39, 0.45);
|
||||||
}
|
}
|
||||||
.mention {
|
.mention {
|
||||||
background: #ede9fe;
|
background: #ede9fe;
|
||||||
|
|||||||
@@ -108,8 +108,11 @@ async function ensureUsers(ids: Record<string, string>): Promise<void> {
|
|||||||
|
|
||||||
const listRule = "famId = @request.auth.famId";
|
const listRule = "famId = @request.auth.famId";
|
||||||
const parentWrite = "famId = @request.auth.famId && @request.auth.role = 'parent'";
|
const parentWrite = "famId = @request.auth.famId && @request.auth.role = 'parent'";
|
||||||
|
// Members can edit their own record (name/colour); parents can edit any
|
||||||
|
// family member. Delete stays parent-only.
|
||||||
|
const selfOrParentWrite = "@request.auth.id = id || (famId = @request.auth.famId && @request.auth.role = 'parent')";
|
||||||
if (usersCol.listRule !== listRule || usersCol.viewRule !== listRule ||
|
if (usersCol.listRule !== listRule || usersCol.viewRule !== listRule ||
|
||||||
usersCol.updateRule !== parentWrite || usersCol.deleteRule !== parentWrite) {
|
usersCol.updateRule !== selfOrParentWrite || usersCol.deleteRule !== parentWrite) {
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,7 +123,7 @@ async function ensureUsers(ids: Record<string, string>): Promise<void> {
|
|||||||
listRule,
|
listRule,
|
||||||
viewRule: listRule,
|
viewRule: listRule,
|
||||||
createRule: usersCol.createRule || "",
|
createRule: usersCol.createRule || "",
|
||||||
updateRule: parentWrite,
|
updateRule: selfOrParentWrite,
|
||||||
deleteRule: parentWrite,
|
deleteRule: parentWrite,
|
||||||
fields: usersCol.fields,
|
fields: usersCol.fields,
|
||||||
indexes,
|
indexes,
|
||||||
|
|||||||
@@ -50,21 +50,38 @@ class ChatStore {
|
|||||||
});
|
});
|
||||||
|
|
||||||
async init(opts: ChatInit) {
|
async init(opts: ChatInit) {
|
||||||
|
// Re-initialize whenever the family OR the actor changes (e.g. switching
|
||||||
|
// between parent and child in the same browser session). The store is a
|
||||||
|
// module singleton — keeping the previous actor's identity/subscription
|
||||||
|
// would make one direction's realtime silently dead.
|
||||||
|
const sameIdentity =
|
||||||
|
this.initialized &&
|
||||||
|
this.famId === opts.famId &&
|
||||||
|
this.actorId === opts.actorId &&
|
||||||
|
this.actorType === opts.actorType;
|
||||||
|
if (sameIdentity) return;
|
||||||
|
if (this.initPromise) {
|
||||||
|
await this.initPromise;
|
||||||
|
if (
|
||||||
|
this.initialized &&
|
||||||
|
this.famId === opts.famId &&
|
||||||
|
this.actorId === opts.actorId &&
|
||||||
|
this.actorType === opts.actorType
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.cleanup();
|
||||||
|
this.destroyed = false;
|
||||||
this.famId = opts.famId;
|
this.famId = opts.famId;
|
||||||
this.actorId = opts.actorId;
|
this.actorId = opts.actorId;
|
||||||
this.actorType = opts.actorType;
|
this.actorType = opts.actorType;
|
||||||
this.actorName = opts.actorName;
|
this.actorName = opts.actorName;
|
||||||
this.actorColor = opts.actorColor;
|
this.actorColor = opts.actorColor;
|
||||||
this.pbToken = opts.pbToken || '';
|
this.pbToken = opts.pbToken || '';
|
||||||
|
this.messages = [];
|
||||||
if (this.initialized && this.famId === opts.famId) return;
|
this.typing = {};
|
||||||
if (this.initPromise) {
|
this.unread = 0;
|
||||||
await this.initPromise;
|
|
||||||
if (this.initialized && this.famId === opts.famId) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.cleanup();
|
|
||||||
this.destroyed = false;
|
|
||||||
this.lastSeenAt = Date.now();
|
this.lastSeenAt = Date.now();
|
||||||
|
|
||||||
this.initPromise = (async () => {
|
this.initPromise = (async () => {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { enhance } from '$app/forms';
|
import { enhance, applyAction } from '$app/forms';
|
||||||
import { ViewHeader, CardGrid, Card, Button } from '$lib/components';
|
import { ViewHeader, CardGrid, Card, Button } from '$lib/components';
|
||||||
|
|
||||||
let { data, form } = $props();
|
let { data, form } = $props();
|
||||||
@@ -24,7 +24,20 @@
|
|||||||
{:else}
|
{:else}
|
||||||
<CardGrid>
|
<CardGrid>
|
||||||
<Card cols={2} title="Appearance">
|
<Card cols={2} title="Appearance">
|
||||||
<form method="POST" action="?/update" use:enhance>
|
<form
|
||||||
|
method="POST"
|
||||||
|
action="?/update"
|
||||||
|
use:enhance={() => {
|
||||||
|
return async ({ result }) => {
|
||||||
|
// Apply the action result (so the success/error message
|
||||||
|
// shows) WITHOUT resetting the form — the default enhance
|
||||||
|
// clears name/email inputs on a successful submit.
|
||||||
|
if (result.type === 'success' || result.type === 'failure') {
|
||||||
|
await applyAction(result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div style="margin-bottom:1rem">
|
<div style="margin-bottom:1rem">
|
||||||
<label style="display:block;font-size:0.85rem;color:#374151;margin-bottom:0.25rem">
|
<label style="display:block;font-size:0.85rem;color:#374151;margin-bottom:0.25rem">
|
||||||
Display Name
|
Display Name
|
||||||
@@ -83,7 +96,9 @@
|
|||||||
<p style="color:#059669;font-size:0.85rem;margin-bottom:0.5rem">Saved!</p>
|
<p style="color:#059669;font-size:0.85rem;margin-bottom:0.5rem">Saved!</p>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<Button type="submit" variant="primary">Save</Button>
|
<div class="form-actions">
|
||||||
|
<Button type="submit" variant="primary">Update preferences</Button>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
@@ -97,3 +112,11 @@
|
|||||||
</Card>
|
</Card>
|
||||||
</CardGrid>
|
</CardGrid>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.form-actions {
|
||||||
|
margin-top: 1.25rem;
|
||||||
|
padding-top: 1.25rem;
|
||||||
|
border-top: 1px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -90,6 +90,8 @@ export const RULE_PARENT_SCOPED =
|
|||||||
"famId = @request.auth.famId && @request.auth.role = 'parent'";
|
"famId = @request.auth.famId && @request.auth.role = 'parent'";
|
||||||
export const RULE_FAM_WRITE = "@request.body.famId = @request.auth.famId";
|
export const RULE_FAM_WRITE = "@request.body.famId = @request.auth.famId";
|
||||||
export const RULE_FAM_SCOPED = "famId = @request.auth.famId";
|
export const RULE_FAM_SCOPED = "famId = @request.auth.famId";
|
||||||
|
// Anyone in the family can read (list/view/subscribe) — members + parents.
|
||||||
|
export const RULE_FAM_READ = "famId = @request.auth.famId";
|
||||||
// fams has no self-referencing famId field; its record id IS the famId.
|
// fams has no self-referencing famId field; its record id IS the famId.
|
||||||
export const RULE_OWN_FAM = "id = @request.auth.famId";
|
export const RULE_OWN_FAM = "id = @request.auth.famId";
|
||||||
|
|
||||||
@@ -270,6 +272,8 @@ export const SCHEMA_PLAN: CollectionPlanEntry[] = [
|
|||||||
createRule: RULE_FAM_WRITE,
|
createRule: RULE_FAM_WRITE,
|
||||||
updateRule: RULE_FAM_SCOPED,
|
updateRule: RULE_FAM_SCOPED,
|
||||||
deleteRule: RULE_FAM_SCOPED,
|
deleteRule: RULE_FAM_SCOPED,
|
||||||
|
listRule: RULE_FAM_READ,
|
||||||
|
viewRule: RULE_FAM_READ,
|
||||||
})(ids),
|
})(ids),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -286,6 +290,8 @@ export const SCHEMA_PLAN: CollectionPlanEntry[] = [
|
|||||||
createRule: RULE_FAM_WRITE,
|
createRule: RULE_FAM_WRITE,
|
||||||
updateRule: RULE_FAM_SCOPED,
|
updateRule: RULE_FAM_SCOPED,
|
||||||
deleteRule: RULE_FAM_SCOPED,
|
deleteRule: RULE_FAM_SCOPED,
|
||||||
|
listRule: RULE_FAM_READ,
|
||||||
|
viewRule: RULE_FAM_READ,
|
||||||
})(ids),
|
})(ids),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user