use custom emoji for payday
This commit is contained in:
@@ -29,6 +29,7 @@ function sessionFrom(record: any, freshToken: string) {
|
|||||||
pattern: record.pattern || '',
|
pattern: record.pattern || '',
|
||||||
themeSize: record.themeSize || '',
|
themeSize: record.themeSize || '',
|
||||||
themeOpacity: record.themeOpacity || '',
|
themeOpacity: record.themeOpacity || '',
|
||||||
|
partyEmoji: record.partyEmoji || '',
|
||||||
token: freshToken
|
token: freshToken
|
||||||
} satisfies SessionUser & { token: string };
|
} satisfies SessionUser & { token: string };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -479,6 +479,9 @@ async function ensureUserFields(): Promise<void> {
|
|||||||
if (!has('passwordResetExpiry')) {
|
if (!has('passwordResetExpiry')) {
|
||||||
needed.push({ name: 'passwordResetExpiry', type: 'text', required: false });
|
needed.push({ name: 'passwordResetExpiry', type: 'text', required: false });
|
||||||
}
|
}
|
||||||
|
if (!has('partyEmoji')) {
|
||||||
|
needed.push({ name: 'partyEmoji', type: 'text', required: false });
|
||||||
|
}
|
||||||
if (needed.length) {
|
if (needed.length) {
|
||||||
await updateCollection(usersCol.id, { ...usersCol, fields: [...usersCol.fields, ...needed] });
|
await updateCollection(usersCol.id, { ...usersCol, fields: [...usersCol.fields, ...needed] });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ export async function getProfile(pb: any, famId: string, userId: string) {
|
|||||||
pattern: rec.pattern || '',
|
pattern: rec.pattern || '',
|
||||||
themeSize: rec.themeSize || '',
|
themeSize: rec.themeSize || '',
|
||||||
themeOpacity: rec.themeOpacity || '',
|
themeOpacity: rec.themeOpacity || '',
|
||||||
|
partyEmoji: rec.partyEmoji || '',
|
||||||
email: rec.email || ''
|
email: rec.email || ''
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -92,6 +93,8 @@ export async function updateProfile(
|
|||||||
if (data.pattern !== undefined) patch.pattern = data.pattern;
|
if (data.pattern !== undefined) patch.pattern = data.pattern;
|
||||||
if (data.themeSize !== undefined) patch.themeSize = data.themeSize;
|
if (data.themeSize !== undefined) patch.themeSize = data.themeSize;
|
||||||
if (data.themeOpacity !== undefined) patch.themeOpacity = data.themeOpacity;
|
if (data.themeOpacity !== undefined) patch.themeOpacity = data.themeOpacity;
|
||||||
|
if (data.partyEmoji !== undefined)
|
||||||
|
patch.partyEmoji = String(data.partyEmoji).trim().slice(0, 8) || '🐖';
|
||||||
if (data.email !== undefined) patch.email = data.email;
|
if (data.email !== undefined) patch.email = data.email;
|
||||||
const rec = await pb.collection('users').update(userId, patch);
|
const rec = await pb.collection('users').update(userId, patch);
|
||||||
return {
|
return {
|
||||||
@@ -101,6 +104,7 @@ export async function updateProfile(
|
|||||||
pattern: rec.pattern || '',
|
pattern: rec.pattern || '',
|
||||||
themeSize: rec.themeSize || '',
|
themeSize: rec.themeSize || '',
|
||||||
themeOpacity: rec.themeOpacity || '',
|
themeOpacity: rec.themeOpacity || '',
|
||||||
|
partyEmoji: rec.partyEmoji || '',
|
||||||
email: rec.email || ''
|
email: rec.email || ''
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,4 +8,5 @@ export interface SessionUser {
|
|||||||
pattern?: string;
|
pattern?: string;
|
||||||
themeSize?: string;
|
themeSize?: string;
|
||||||
themeOpacity?: string;
|
themeOpacity?: string;
|
||||||
|
partyEmoji?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ export async function load(event) {
|
|||||||
verified: false,
|
verified: false,
|
||||||
memberName: session?.name || '',
|
memberName: session?.name || '',
|
||||||
memberColor: session?.color || '',
|
memberColor: session?.color || '',
|
||||||
|
partyEmoji: (session as any)?.partyEmoji || '',
|
||||||
templates: [] as never[],
|
templates: [] as never[],
|
||||||
assigned: [] as never[],
|
assigned: [] as never[],
|
||||||
completions: [] as never[],
|
completions: [] as never[],
|
||||||
@@ -111,6 +112,7 @@ export async function load(event) {
|
|||||||
verified: true,
|
verified: true,
|
||||||
memberName: session?.name || '',
|
memberName: session?.name || '',
|
||||||
memberColor: session?.color || '',
|
memberColor: session?.color || '',
|
||||||
|
partyEmoji: (session as any)?.partyEmoji || '',
|
||||||
templates: chores.templates || [],
|
templates: chores.templates || [],
|
||||||
assigned: chores.assigned || [],
|
assigned: chores.assigned || [],
|
||||||
completions: chores.completions || [],
|
completions: chores.completions || [],
|
||||||
|
|||||||
@@ -209,6 +209,15 @@
|
|||||||
let famId = $state(data.famId || '');
|
let famId = $state(data.famId || '');
|
||||||
let memberName = $state(data.memberName || '');
|
let memberName = $state(data.memberName || '');
|
||||||
let memberColor = $state(data.memberColor || '#6366f1');
|
let memberColor = $state(data.memberColor || '#6366f1');
|
||||||
|
// Party mascot: live member record first (updates without re-login),
|
||||||
|
// server load as fallback, pig as default.
|
||||||
|
let partyEmoji = $derived(
|
||||||
|
(famStore.initialized
|
||||||
|
? (famStore.members as any[]).find((m) => m.id === memberId)?.partyEmoji
|
||||||
|
: null) ||
|
||||||
|
(data as any).partyEmoji ||
|
||||||
|
'🐖'
|
||||||
|
);
|
||||||
let editingName = $state(false);
|
let editingName = $state(false);
|
||||||
let nameInput = $state('');
|
let nameInput = $state('');
|
||||||
let showColorPicker = $state(false);
|
let showColorPicker = $state(false);
|
||||||
@@ -1285,7 +1294,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
{#if paydayParty}
|
{#if paydayParty}
|
||||||
<div class="payday-party" role="status" onclick={() => (paydayParty = false)}>
|
<div class="payday-party" role="status" onclick={() => (paydayParty = false)}>
|
||||||
<span class="party-cat">🐖</span>
|
<span class="party-cat">{partyEmoji}</span>
|
||||||
<div class="party-body">
|
<div class="party-body">
|
||||||
<p class="party-title">PAYDAY!!</p>
|
<p class="party-title">PAYDAY!!</p>
|
||||||
<p class="party-sub">Your cash is ready — go collect it! 🎉</p>
|
<p class="party-sub">Your cash is ready — go collect it! 🎉</p>
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ export async function load(event) {
|
|||||||
pattern: me.pattern || '',
|
pattern: me.pattern || '',
|
||||||
themeSize: me.themeSize || '',
|
themeSize: me.themeSize || '',
|
||||||
themeOpacity: me.themeOpacity || '',
|
themeOpacity: me.themeOpacity || '',
|
||||||
|
partyEmoji: me.partyEmoji || '',
|
||||||
email: me.email || '',
|
email: me.email || '',
|
||||||
hasPin: false,
|
hasPin: false,
|
||||||
session: true
|
session: true
|
||||||
@@ -94,6 +95,7 @@ export async function load(event) {
|
|||||||
pattern: session.pattern || '',
|
pattern: session.pattern || '',
|
||||||
themeSize: session.themeSize || '',
|
themeSize: session.themeSize || '',
|
||||||
themeOpacity: session.themeOpacity || '',
|
themeOpacity: session.themeOpacity || '',
|
||||||
|
partyEmoji: session.partyEmoji || '',
|
||||||
email: '',
|
email: '',
|
||||||
hasPin: await hasPin(session.id).catch(() => false),
|
hasPin: await hasPin(session.id).catch(() => false),
|
||||||
session: true
|
session: true
|
||||||
@@ -112,6 +114,7 @@ export const actions = {
|
|||||||
const pattern = (fd.get('pattern') as string) || '';
|
const pattern = (fd.get('pattern') as string) || '';
|
||||||
const themeSize = (fd.get('themeSize') as string) || '';
|
const themeSize = (fd.get('themeSize') as string) || '';
|
||||||
const themeOpacity = (fd.get('themeOpacity') as string) || '';
|
const themeOpacity = (fd.get('themeOpacity') as string) || '';
|
||||||
|
const partyEmoji = String(fd.get('partyEmoji') || '').trim().slice(0, 8);
|
||||||
|
|
||||||
const s = servicesFor(event);
|
const s = servicesFor(event);
|
||||||
const famId = session.famId;
|
const famId = session.famId;
|
||||||
@@ -124,6 +127,7 @@ export const actions = {
|
|||||||
data.pattern = pattern;
|
data.pattern = pattern;
|
||||||
data.themeSize = themeSize;
|
data.themeSize = themeSize;
|
||||||
data.themeOpacity = themeOpacity;
|
data.themeOpacity = themeOpacity;
|
||||||
|
if (partyEmoji) data.partyEmoji = partyEmoji;
|
||||||
data.email = email || '';
|
data.email = email || '';
|
||||||
const me = await s.fam.updateProfile(famId, data);
|
const me = await s.fam.updateProfile(famId, data);
|
||||||
return {
|
return {
|
||||||
@@ -133,6 +137,7 @@ export const actions = {
|
|||||||
pattern: me.pattern,
|
pattern: me.pattern,
|
||||||
themeSize: me.themeSize,
|
themeSize: me.themeSize,
|
||||||
themeOpacity: me.themeOpacity,
|
themeOpacity: me.themeOpacity,
|
||||||
|
partyEmoji: me.partyEmoji,
|
||||||
email: me.email
|
email: me.email
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -148,6 +153,7 @@ export const actions = {
|
|||||||
data.pattern = pattern;
|
data.pattern = pattern;
|
||||||
data.themeSize = themeSize;
|
data.themeSize = themeSize;
|
||||||
data.themeOpacity = themeOpacity;
|
data.themeOpacity = themeOpacity;
|
||||||
|
if (partyEmoji) data.partyEmoji = partyEmoji;
|
||||||
const me = await s.fam.updateProfile(famId, data);
|
const me = await s.fam.updateProfile(famId, data);
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
@@ -156,6 +162,7 @@ export const actions = {
|
|||||||
pattern: me.pattern,
|
pattern: me.pattern,
|
||||||
themeSize: me.themeSize,
|
themeSize: me.themeSize,
|
||||||
themeOpacity: me.themeOpacity,
|
themeOpacity: me.themeOpacity,
|
||||||
|
partyEmoji: me.partyEmoji,
|
||||||
email: ''
|
email: ''
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -70,6 +70,14 @@
|
|||||||
'#8b5cf6',
|
'#8b5cf6',
|
||||||
'#14b8a6'
|
'#14b8a6'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Payday-party mascot — the emoji that leads the child's payday celebration.
|
||||||
|
let memberEmoji = $state(data.partyEmoji || '🐖');
|
||||||
|
const partyEmojis = [
|
||||||
|
'🐖', '🐱', '🐶', '🦄', '🐸', '🐵', '🦊', '🐼',
|
||||||
|
'🦁', '🐯', '🐰', '🐨', '🐷', '🐮', '🦖', '🐙',
|
||||||
|
'🚀', '🌈', '⭐', '🎮', '⚽', '🍕', '🍦', '🎉'
|
||||||
|
];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ViewHeader title="Preferences" subtitle={data.verified ? data.memberName : ''} hero />
|
<ViewHeader title="Preferences" subtitle={data.verified ? data.memberName : ''} hero />
|
||||||
@@ -145,6 +153,28 @@
|
|||||||
<input type="hidden" name="color" value={memberColor} />
|
<input type="hidden" name="color" value={memberColor} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div style="margin-bottom:1rem">
|
||||||
|
<label style="display:block;font-size:0.85rem;color:#374151;margin-bottom:0.25rem">
|
||||||
|
Party emoji — leads your payday celebration 🎉
|
||||||
|
</label>
|
||||||
|
<div style="display:flex;gap:0.3rem;flex-wrap:wrap">
|
||||||
|
{#each partyEmojis as e}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onclick={() => (memberEmoji = e)}
|
||||||
|
aria-label="Pick {e}"
|
||||||
|
style="font-size:1.5rem;line-height:1;padding:0.25rem;border-radius:8px;border:{e ===
|
||||||
|
memberEmoji
|
||||||
|
? '2px solid #111827'
|
||||||
|
: '2px solid transparent'};background:{e === memberEmoji
|
||||||
|
? '#f3f4f6'
|
||||||
|
: 'transparent'};cursor:pointer"
|
||||||
|
>{e}</button>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="partyEmoji" value={memberEmoji} />
|
||||||
|
</div>
|
||||||
|
|
||||||
{#if data.role === 'parent'}
|
{#if data.role === 'parent'}
|
||||||
<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">
|
||||||
|
|||||||
Reference in New Issue
Block a user