perf(tasks): show completion sticker instantly
Roll the sticker client-side and render the popup immediately on completion instead of waiting for the POST roundtrip + DB writes to return it. Preload the sticker image so the cat is decoded before the bounce-in finishes. The chosen stickerId is sent to the server, which persists it (falling back to a server-side roll if missing/invalid).
This commit is contained in:
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "homepage",
|
||||
"version": "1.95.2",
|
||||
"version": "1.95.4",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
@@ -8,7 +8,7 @@
|
||||
"dev": "vite dev",
|
||||
"prebuild": "bash scripts/subset-emoji-font.sh && pnpm exec vite-node scripts/generate-mystery-verses.ts && pnpm exec vite-node scripts/download-models.ts && pnpm exec vite-node scripts/generate-loyalty-cards.ts && pnpm exec vite-node scripts/generate-error-quotes.ts && pnpm exec vite-node scripts/build-hikes.ts && pnpm exec vite-node scripts/build-private-images.ts",
|
||||
"build": "vite build",
|
||||
"postbuild": "pnpm exec vite-node scripts/build-error-page.ts",
|
||||
"postbuild": "pnpm exec vite-node scripts/build-error-page.ts && pnpm exec vite-node scripts/precompress.ts",
|
||||
"preview": "vite preview",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Task } from '$models/Task';
|
||||
import { TaskCompletion } from '$models/TaskCompletion';
|
||||
import { dbConnect } from '$utils/db';
|
||||
import { error, json } from '@sveltejs/kit';
|
||||
import { getStickerForTags } from '$lib/utils/stickers';
|
||||
import { getStickerForTags, getStickerById } from '$lib/utils/stickers';
|
||||
import { addDays } from 'date-fns';
|
||||
|
||||
function getNextDueDate(completedAt: Date, frequencyType: string, customDays?: number): Date {
|
||||
@@ -37,8 +37,11 @@ export const POST: RequestHandler = async ({ params, request, locals }) => {
|
||||
|
||||
const now = new Date();
|
||||
|
||||
// Award a sticker based on task tags and difficulty
|
||||
const sticker = getStickerForTags(task.tags, task.difficulty || 'medium');
|
||||
// Award a sticker. The client rolls + displays it optimistically and passes
|
||||
// the id here; fall back to a server-side roll if it's missing or invalid.
|
||||
const sticker =
|
||||
(typeof body.stickerId === 'string' && getStickerById(body.stickerId)) ||
|
||||
getStickerForTags(task.tags, task.difficulty || 'medium');
|
||||
|
||||
// Record the completion
|
||||
const completion = await TaskCompletion.create({
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
import { flip } from 'svelte/animate';
|
||||
import TaskForm from '$lib/components/tasks/TaskForm.svelte';
|
||||
import StickerPopup from '$lib/components/tasks/StickerPopup.svelte';
|
||||
import { getStickerForTags } from '$lib/utils/stickers';
|
||||
import ProfilePicture from '$lib/components/cospend/ProfilePicture.svelte';
|
||||
|
||||
let { data } = $props();
|
||||
@@ -112,16 +113,25 @@
|
||||
* @param {string} [forUser]
|
||||
*/
|
||||
async function completeTask(task, forUser) {
|
||||
// Roll the sticker client-side and show it immediately — don't wait on the
|
||||
// POST roundtrip (DB writes) just to learn which sticker to display.
|
||||
const sticker = getStickerForTags(task.tags, task.difficulty || 'medium');
|
||||
// Warm the image cache so the cat is decoded by the time the popup finishes
|
||||
// its bounce-in, instead of fading into an empty circle.
|
||||
if (typeof Image !== 'undefined') {
|
||||
const img = new Image();
|
||||
img.src = `/stickers/${sticker.image}`;
|
||||
}
|
||||
awardedSticker = sticker;
|
||||
completeForTaskId = null;
|
||||
|
||||
// Persist in the background; tell the server which sticker we showed.
|
||||
const res = await fetch(`/api/tasks/${task._id}/complete`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(forUser ? { completedFor: forUser } : {})
|
||||
body: JSON.stringify({ stickerId: sticker.id, ...(forUser ? { completedFor: forUser } : {}) })
|
||||
});
|
||||
if (!res.ok) return;
|
||||
const result = await res.json();
|
||||
|
||||
awardedSticker = result.sticker;
|
||||
completeForTaskId = null;
|
||||
await refreshTasks();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user