diff --git a/frontend/src/lib/components/favorites/FavoritesView.svelte b/frontend/src/lib/components/favorites/FavoritesView.svelte index 9a802bb4..09c19d94 100644 --- a/frontend/src/lib/components/favorites/FavoritesView.svelte +++ b/frontend/src/lib/components/favorites/FavoritesView.svelte @@ -22,11 +22,16 @@ import ViewHeader from '$lib/components/shared/ViewHeader.svelte'; import {applyWallpaperOnly} from '$lib/actions/themeActions'; import {getIsApplying} from '$lib/stores/theme.svelte'; - import type {favorites as favoritesNs} from '../../../../wailsjs/go/models'; - - type Favorite = favoritesNs.Favorite; + import { + getFavorites, + getFavoritesError, + refreshFavorites, + toggleFavorite, + type Favorite, + } from '$lib/stores/favorites.svelte'; - let favorites = $state([]); + let favorites = $derived(getFavorites()); + let loadError = $derived(getFavoritesError()); let isLoading = $state(true); let filterTag = $state(''); let previewIndex = $state(-1); @@ -45,17 +50,13 @@ loadFavorites(); }); + // Re-sync with the backend on every mount so favourites added via the + // CLI/IPC while this tab was closed show up. async function loadFavorites() { isLoading = true; try { - const {GetFavorites} = await import( - '../../../../wailsjs/go/main/App' - ); - const result = await GetFavorites(); - favorites = Array.isArray(result) ? result : []; + await refreshFavorites(); loadThumbnails(); - } catch { - favorites = []; } finally { isLoading = false; } @@ -103,12 +104,11 @@ async function handleRemove(fav: Favorite) { try { - const {ToggleFavorite} = await import( - '../../../../wailsjs/go/main/App' - ); - await ToggleFavorite(fav.path, fav.type ?? '', {}); - favorites = favorites.filter(f => f.path !== fav.path); - } catch {} + await toggleFavorite(fav.path, fav.type ?? ''); + } catch (err) { + console.error('ToggleFavorite failed', err); + showToast('Could not update favorites'); + } } async function handleAddExtra(fav: Favorite) { @@ -199,9 +199,22 @@
+ {#if loadError} + + {/if} {#if isLoading} - {:else if filtered.length === 0} + {:else if filtered.length === 0 && !loadError} {#if filterTag} handleSelect(fav)} onwallpaperonly={() => applyWallpaperOnly(fav.path)} onpreview={() => handlePreview(i)} onaddextra={() => handleAddExtra(fav)} + onfavorite={() => handleRemove(fav)} > {#snippet thumb()} {#if getCachedThumbnail(fav.path)} @@ -279,27 +294,6 @@ > {/if} {/snippet} - {#snippet topRight()} - - {/snippet} {/each}
diff --git a/frontend/src/lib/components/local/LocalBrowser.svelte b/frontend/src/lib/components/local/LocalBrowser.svelte index 7a64e739..0866c6ab 100644 --- a/frontend/src/lib/components/local/LocalBrowser.svelte +++ b/frontend/src/lib/components/local/LocalBrowser.svelte @@ -17,6 +17,7 @@ loadFullImage, getCachedFullImage, } from '$lib/stores/imagecache.svelte'; + import {isFavorite, toggleFavorite} from '$lib/stores/favorites.svelte'; import LazyImage from '$lib/components/shared/LazyImage.svelte'; import WallpaperTile from '$lib/components/shared/WallpaperTile.svelte'; import ImagePreview from '$lib/components/shared/ImagePreview.svelte'; @@ -89,7 +90,9 @@ } catch (err) { wallpapers = []; loadError = - err instanceof Error ? err.message : 'Wallpaper folder unavailable'; + err instanceof Error + ? err.message + : 'Wallpaper folder unavailable'; } finally { isLoading = false; } @@ -186,6 +189,20 @@ showToast('Added to additional images'); } + async function handleFavorite(wp: Wallpaper) { + try { + const nowFavorited = await toggleFavorite(wp.path, 'local', { + name: wp.name, + }); + showToast( + nowFavorited ? 'Added to favorites' : 'Removed from favorites' + ); + } catch (err) { + console.error('ToggleFavorite failed', err); + showToast('Could not update favorites'); + } + } + async function handlePreview(index: number) { const wp = filtered[index]; const cached = getCachedFullImage(wp.path); @@ -371,11 +388,13 @@ path={wp.path} name={wp.name} isAdded={getAdditionalImages().includes(wp.path)} + isFavorited={isFavorite(wp.path)} applying={getIsApplying()} onuse={() => selectWallpaper(wp.path)} onwallpaperonly={() => applyWallpaperOnly(wp.path)} onpreview={() => handlePreview(i)} onaddextra={() => handleAddExtra(wp.path)} + onfavorite={() => handleFavorite(wp)} > {#snippet thumb()} diff --git a/frontend/src/lib/components/shared/WallpaperTile.svelte b/frontend/src/lib/components/shared/WallpaperTile.svelte index a7cd2d1c..da722b9d 100644 --- a/frontend/src/lib/components/shared/WallpaperTile.svelte +++ b/frontend/src/lib/components/shared/WallpaperTile.svelte @@ -4,31 +4,33 @@ // Shared card for the Local and Favorites wallpaper grids. The thumbnail // source differs per view (lazy-loaded vs preloaded cache), so it arrives - // as a `thumb` snippet; `topRight` is an optional slot for view-specific - // actions (e.g. the favourites remove button). Clicking the thumbnail or - // the Use button selects the wallpaper. + // as a `thumb` snippet. The favourite heart renders only when `onfavorite` + // is supplied. Clicking the thumbnail or the Use button selects the + // wallpaper. let { path, name, isAdded = false, + isFavorited = false, applying = false, onuse, onwallpaperonly, onpreview, onaddextra, + onfavorite, thumb, - topRight, }: { path: string; name: string; isAdded?: boolean; + isFavorited?: boolean; applying?: boolean; onuse: () => void; onwallpaperonly: () => void; onpreview: () => void; onaddextra: () => void; + onfavorite?: () => void; thumb: Snippet; - topRight?: Snippet; } = $props(); @@ -51,7 +53,7 @@ class="absolute left-1.5 top-1.5 z-10 flex h-7 w-7 items-center justify-center transition-all duration-150 {isAdded ? 'opacity-100' - : 'opacity-0 hover:!opacity-100 group-hover:opacity-60'}" + : 'opacity-0 hover:!opacity-100 focus-visible:opacity-100 group-focus-within:opacity-100 group-hover:opacity-60'}" onclick={e => { e.stopPropagation(); onaddextra(); @@ -73,12 +75,40 @@ - {#if topRight} - {@render topRight()} + {#if onfavorite} + {/if}