From e9a2f137847650fad05e056768b6abb698c56489 Mon Sep 17 00:00:00 2001 From: Carter Mintey Date: Mon, 20 Nov 2023 20:46:08 -0600 Subject: [PATCH] add indicator when there are new items added --- src/lib/components/UserCard.svelte | 8 ++++++++ src/lib/stores/viewed-items.ts | 19 +++++++++++++++++++ src/routes/+page.server.ts | 1 + src/routes/+page.svelte | 16 +++++++++++++++- .../wishlists/[username]/+page.server.ts | 7 ++++--- src/routes/wishlists/[username]/+page.svelte | 7 ++++++- 6 files changed, 53 insertions(+), 5 deletions(-) create mode 100644 src/lib/stores/viewed-items.ts diff --git a/src/lib/components/UserCard.svelte b/src/lib/components/UserCard.svelte index ca5d1e5..ff20b5a 100644 --- a/src/lib/components/UserCard.svelte +++ b/src/lib/components/UserCard.svelte @@ -10,6 +10,7 @@ items: number; }; }; + export let newItems = false; @@ -25,6 +26,13 @@ {!hideCount && user._count ? `${user._count.items}/` : ""}{user.items.length} + {#if newItems} + + {/if} diff --git a/src/lib/stores/viewed-items.ts b/src/lib/stores/viewed-items.ts new file mode 100644 index 0000000..3e2608c --- /dev/null +++ b/src/lib/stores/viewed-items.ts @@ -0,0 +1,19 @@ +import type { Item } from "@prisma/client"; +import { localStorageStore } from "@skeletonlabs/skeleton"; +import type { Writable } from "svelte/store"; + +export const viewedItems: Writable> = localStorageStore("viewedItems", {}); + +export const hashItems = async (items: Partial[]): Promise => { + const itemIds = items + .map(({ id }) => id) + .toSorted() + .join(""); + return await hash(itemIds); +}; + +export const hash = async (data: string): Promise => { + const encoder = new TextEncoder(); + const digest = await crypto.subtle.digest("SHA-256", encoder.encode(data)); + return btoa(String.fromCharCode(...new Uint8Array(digest))); +}; diff --git a/src/routes/+page.server.ts b/src/routes/+page.server.ts index 238d5fe..3a1aa51 100644 --- a/src/routes/+page.server.ts +++ b/src/routes/+page.server.ts @@ -59,6 +59,7 @@ export const load = (async ({ locals }) => { } }, select: { + id: true, username: true, name: true, picture: true, diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 6b7dae7..f40d580 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -3,8 +3,18 @@ import UserCard from "$lib/components/UserCard.svelte"; import { fade } from "svelte/transition"; import ApprovalAlert from "$lib/components/wishlists/ApprovalAlert.svelte"; + import { hash, hashItems, viewedItems } from "$lib/stores/viewed-items"; export let data: PageData; + type PageUserData = (typeof data)["users"][0]; + + const hasNewItems = async (user: PageUserData) => { + if (user.items.length === 0) return false; + const userHash = await hash(user.id); + const currentHash = await hashItems(user.items); + const viewedHash = $viewedItems[userHash]; + return currentHash !== viewedHash; + };
@@ -12,7 +22,11 @@ {#each data.users as user} - + {#await hasNewItems(user)} + + {:then newItems} + + {/await} {/each}
diff --git a/src/routes/wishlists/[username]/+page.server.ts b/src/routes/wishlists/[username]/+page.server.ts index eabedc8..4d311ff 100644 --- a/src/routes/wishlists/[username]/+page.server.ts +++ b/src/routes/wishlists/[username]/+page.server.ts @@ -90,12 +90,13 @@ export const load: PageServerLoad = async ({ locals, params, depends, url }) => } }); - const listOwner = await client.user.findUnique({ + const listOwner = await client.user.findUniqueOrThrow({ where: { username: params.username }, select: { - name: true + name: true, + id: true } }); @@ -103,7 +104,7 @@ export const load: PageServerLoad = async ({ locals, params, depends, url }) => user: session.user, listOwner: { isMe: params.username === session.user.username, - name: listOwner?.name + ...listOwner }, items: wishlistItems.filter((item) => item.approved), approvals: wishlistItems.filter((item) => !item.approved), diff --git a/src/routes/wishlists/[username]/+page.svelte b/src/routes/wishlists/[username]/+page.svelte index 99a66e9..3879085 100644 --- a/src/routes/wishlists/[username]/+page.svelte +++ b/src/routes/wishlists/[username]/+page.svelte @@ -12,6 +12,7 @@ import { isInstalled } from "$lib/stores/is-installed"; import empty from "$lib/assets/no_wishes.svg"; import SortBy from "$lib/components/wishlists/chips/SortBy.svelte"; + import { hash, hashItems, viewedItems } from "$lib/stores/viewed-items"; export let data: PageData; @@ -54,7 +55,11 @@ }, 5000); }; - onMount(pollUpdate); + onMount(async () => { + const userHash = await hash(data.listOwner.id); + $viewedItems[userHash] = await hashItems(data.items); + pollUpdate(); + }); onDestroy(() => clearTimeout(pollTimeout)); $: if (!$idle && !polling) {