add: local wishlists stored in local storage for anonymous users

This commit is contained in:
rasmusq
2025-11-27 21:35:28 +01:00
parent 8dcf26b1d3
commit 85f8671c72
9 changed files with 264 additions and 13 deletions

View File

@@ -0,0 +1,93 @@
<script lang="ts">
import { Button } from '$lib/components/ui/button';
import WishlistSection from '$lib/components/dashboard/WishlistSection.svelte';
import { getLocalWishlists, forgetLocalWishlist, toggleLocalFavorite, type LocalWishlist } from '$lib/utils/localWishlists';
import { languageStore } from '$lib/stores/language.svelte';
import { Star } from 'lucide-svelte';
import { onMount } from 'svelte';
let {
isAuthenticated = false
}: {
isAuthenticated?: boolean;
} = $props();
const t = $derived(languageStore.t);
let localWishlists = $state<LocalWishlist[]>([]);
// Load local wishlists on mount (client-side only)
onMount(() => {
localWishlists = getLocalWishlists();
});
function handleForget(ownerToken: string) {
forgetLocalWishlist(ownerToken);
localWishlists = getLocalWishlists();
}
function handleToggleFavorite(ownerToken: string) {
toggleLocalFavorite(ownerToken);
localWishlists = getLocalWishlists();
}
// Transform LocalWishlist to match the format expected by WishlistSection
const transformedWishlists = $derived(() => {
return localWishlists.map(w => ({
id: w.ownerToken,
title: w.title,
ownerToken: w.ownerToken,
publicToken: w.publicToken,
createdAt: w.createdAt,
isFavorite: w.isFavorite || false,
items: [] // We don't have item data in localStorage
}));
});
</script>
{#if localWishlists.length > 0}
<WishlistSection
title={t.dashboard.localWishlists || "Local Wishlists"}
description={t.dashboard.localWishlistsDescription || "Wishlists stored in your browser. Sign in to save them permanently."}
items={transformedWishlists()}
emptyMessage=""
>
{#snippet actions(wishlist, unlocked)}
<div class="flex gap-2 flex-wrap">
<Button
size="sm"
variant="outline"
onclick={() => handleToggleFavorite(wishlist.ownerToken)}
>
<Star class={wishlist.isFavorite ? "fill-yellow-500 text-yellow-500" : ""} />
</Button>
<Button
size="sm"
onclick={() => (window.location.href = `/wishlist/${wishlist.ownerToken}/edit`)}
>
{t.dashboard.manage}
</Button>
<Button
size="sm"
variant="outline"
onclick={() => {
navigator.clipboard.writeText(
`${window.location.origin}/wishlist/${wishlist.publicToken}`
);
}}
>
{t.dashboard.copyLink}
</Button>
{#if unlocked}
<Button
size="sm"
variant="destructive"
onclick={() => handleForget(wishlist.ownerToken)}
>
{t.dashboard.forget || "Forget"}
</Button>
{/if}
</div>
{/snippet}
</WishlistSection>
{/if}

View File

@@ -8,16 +8,25 @@
let { userName, userEmail }: { userName?: string | null; userEmail?: string | null } = $props();
const t = $derived(languageStore.t);
const isAuthenticated = $derived(!!userName || !!userEmail);
</script>
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<div class="flex-1 min-w-0">
<h1 class="text-3xl font-bold">{t.nav.dashboard}</h1>
<p class="text-muted-foreground truncate">{t.dashboard.welcomeBack}, {userName || userEmail}</p>
{#if isAuthenticated}
<p class="text-muted-foreground truncate">{t.dashboard.welcomeBack}, {userName || userEmail}</p>
{:else}
<p class="text-muted-foreground">{t.dashboard.anonymousDashboard || "Your local wishlists"}</p>
{/if}
</div>
<div class="flex items-center gap-1 sm:gap-2 flex-shrink-0">
<LanguageToggle />
<ThemeToggle />
<Button variant="outline" onclick={() => signOut({ callbackUrl: '/' })}>{t.auth.signOut}</Button>
{#if isAuthenticated}
<Button variant="outline" onclick={() => signOut({ callbackUrl: '/' })}>{t.auth.signOut}</Button>
{:else}
<Button variant="outline" onclick={() => (window.location.href = '/signin')}>{t.auth.signIn}</Button>
{/if}
</div>
</div>

View File

@@ -2,18 +2,24 @@
import { Button } from '$lib/components/ui/button';
import { enhance } from '$app/forms';
import { languageStore } from '$lib/stores/language.svelte';
import { isLocalWishlist } from '$lib/utils/localWishlists';
let {
isAuthenticated,
isOwner,
hasClaimed
hasClaimed,
ownerToken
}: {
isAuthenticated: boolean;
isOwner: boolean;
hasClaimed: boolean;
ownerToken: string;
} = $props();
const t = $derived(languageStore.t);
// Check if this wishlist is in localStorage
const isLocal = $derived(isLocalWishlist(ownerToken));
</script>
{#if isAuthenticated}
@@ -33,7 +39,11 @@
<form
method="POST"
action={hasClaimed ? "?/unclaimWishlist" : "?/claimWishlist"}
use:enhance
use:enhance={() => {
return async ({ update }) => {
await update({ reset: false });
};
}}
>
<Button
type="submit"
@@ -48,6 +58,10 @@
You have claimed this wishlist. It will appear in your dashboard.
{:else}
Claim this wishlist to add it to your dashboard for easy access.
{#if isLocal}
<br />
<span class="text-xs">It will remain in your local wishlists and also appear in your claimed wishlists.</span>
{/if}
{/if}
</p>
{/if}