This commit is contained in:
JCEEE
2026-09-14 21:29:10 +01:00
parent 49658bd791
commit f9fa99f5ff
5 changed files with 31 additions and 24 deletions
+1 -1
View File
@@ -208,7 +208,7 @@
<span class="msg-author" style="color:{colorOf(msg)}">{msg.authorName}</span> <span class="msg-author" style="color:{colorOf(msg)}">{msg.authorName}</span>
{/if} {/if}
<div class="bubble" style="background:{colorOf(msg) + '33'}; color:{colorOf(msg)}"> <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, i (msg.id + ':' + i)}
{#if seg.mention} {#if seg.mention}
<button class="mention" onclick={() => insertMention(seg.text.slice(1))} <button class="mention" onclick={() => insertMention(seg.text.slice(1))}
>{seg.text}</button >{seg.text}</button
@@ -1,16 +1,19 @@
<script lang="ts"> <script lang="ts">
import type { Snippet } from 'svelte';
let { let {
announcement = '', announcement = '',
role = '', role = '',
seasons = [], seasons = [],
connected = true, connected = true,
children, children,
left
}: { }: {
announcement?: string announcement?: string
role?: string role?: string
seasons?: { id: string; name: string; color: string; active: boolean }[] seasons?: { id: string; name: string; color: string; active: boolean }[]
connected?: boolean connected?: boolean
children?: any children?: any
left?: Snippet
} = $props(); } = $props();
</script> </script>
@@ -19,6 +22,7 @@
{#if role} {#if role}
<span class="role-pill {role}">{role}</span> <span class="role-pill {role}">{role}</span>
{/if} {/if}
{@render left?.()}
<span <span
class="conn-dot" class="conn-dot"
class:up={connected} class:up={connected}
+10 -10
View File
@@ -55,14 +55,13 @@ export async function toggle(pb: any, famId: string, memberId: string, body: { a
const chore = choreList?.[0]; const chore = choreList?.[0];
const isTodo = chore?.isTodo; const isTodo = chore?.isTodo;
const isShared = chore?.shared === true || !chore?.memberId; const isShared = chore?.shared === true || !chore?.memberId;
// Only one-off shared todos are claimed (winner takes it). Recurring
// shared chores stay unassigned so every member completes independently
// each period — claiming them permanently hides the chore from siblings.
const isClaimable = isShared && !!isTodo;
let filter: string; let filter: string;
if (isShared && !isTodo) { if (isTodo) {
// Shared non-todo: any completion in the period = already claimed
const { payday, tz } = await famMetaSU(famId);
const { from, to } = periodWindow(chore?.frequency, payday, tz);
filter = `assignedChoreId = '${assignedChoreId}' && date >= '${from}' && date < '${to}'`;
} else if (isTodo) {
filter = `assignedChoreId = '${assignedChoreId}' && memberId = '${memberId}'`; filter = `assignedChoreId = '${assignedChoreId}' && memberId = '${memberId}'`;
} else { } else {
const { payday, tz } = await famMetaSU(famId); const { payday, tz } = await famMetaSU(famId);
@@ -78,8 +77,8 @@ export async function toggle(pb: any, famId: string, memberId: string, body: { a
if (isTodo && existing[0].rewardId) { if (isTodo && existing[0].rewardId) {
await pb.collection('rewards').delete(existing[0].rewardId).catch(() => {}); await pb.collection('rewards').delete(existing[0].rewardId).catch(() => {});
} }
// If this was a shared chore claimed by this member, un-share it // If this was a claimed one-off todo, release it back to shared.
if (isShared && existing[0].memberId === memberId) { if (isClaimable && existing[0].memberId === memberId) {
await claimChore(assignedChoreId, famId, ''); await claimChore(assignedChoreId, famId, '');
} }
evaluateFam(pb, famId).catch(() => {}); evaluateFam(pb, famId).catch(() => {});
@@ -107,8 +106,9 @@ export async function toggle(pb: any, famId: string, memberId: string, body: { a
completedAt: new Date().toISOString(), completedAt: new Date().toISOString(),
...(rewardId ? { rewardId } : {}) ...(rewardId ? { rewardId } : {})
}); });
// Claim the shared chore by setting memberId // Claim a shared one-off todo by setting memberId (recurring shared
if (isShared) { // chores stay unassigned — see isClaimable above).
if (isClaimable) {
await claimChore(assignedChoreId, famId, memberId); await claimChore(assignedChoreId, famId, memberId);
} }
evaluateFam(pb, famId).catch(() => {}); evaluateFam(pb, famId).catch(() => {});
+12 -10
View File
@@ -306,6 +306,18 @@
announcement={disabled && !activating ? accessMessage(accessReason, isParent) : ''} announcement={disabled && !activating ? accessMessage(accessReason, isParent) : ''}
connected={!famStore.connectionDown} connected={!famStore.connectionDown}
> >
{#snippet left()}
<button
class="shared-toggle"
class:on={sharedOn}
onclick={toggleShared}
aria-label="Toggle shared computer mode"
aria-pressed={sharedOn}
title={sharedOn ? 'Shared computer mode is ON' : 'Mark this as a shared computer'}
>
{@html monitorIcon}
</button>
{/snippet}
<button class="chat-toggle" onclick={() => chatStore.toggle()} aria-label="Open chat"> <button class="chat-toggle" onclick={() => chatStore.toggle()} aria-label="Open chat">
{@html chatIcon} {@html chatIcon}
{#if chatStore.unread > 0} {#if chatStore.unread > 0}
@@ -323,16 +335,6 @@
></span> ></span>
</button> </button>
{/if} {/if}
<button
class="shared-toggle"
class:on={sharedOn}
onclick={toggleShared}
aria-label="Toggle shared computer mode"
aria-pressed={sharedOn}
title={sharedOn ? 'Shared computer mode is ON' : 'Mark this as a shared computer'}
>
{@html monitorIcon}
</button>
</TopNav> </TopNav>
<main class="app-main" class:join-page={page.url.pathname.split('/')[2] === 'join'}> <main class="app-main" class:join-page={page.url.pathname.split('/')[2] === 'join'}>
<div class="page-wrap"> <div class="page-wrap">
@@ -550,9 +550,10 @@
const name = bonusConfigs.find((b: BonusConfig) => b.id === r.bonusConfigId)?.name || r.label; const name = bonusConfigs.find((b: BonusConfig) => b.id === r.bonusConfigId)?.name || r.label;
map.set(name, (map.get(name) || 0) + 1); map.set(name, (map.get(name) || 0) + 1);
} }
// Only badge recurring/multiple bonuses — a single occurrence (×1) is // Badge every bonus earned this week — including a single occurrence
// already obvious from the wallet, so it just adds noise here. // (×1): the hero is the child's only at-a-glance signal, so nothing
return [...map.entries()].map(([name, count]) => ({ name, count })).filter((t) => t.count > 1); // earned should stay invisible here.
return [...map.entries()].map(([name, count]) => ({ name, count }));
}); });
let anyWeekBonuses = $derived(weekRewards.length > 0); let anyWeekBonuses = $derived(weekRewards.length > 0);