import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; import { Sparkles, X, ArrowRight } from "lucide-react"; import { getPreference, setPreference, } from "../../services/userPreferenceService"; // Preference keys used to decide whether to show the banner. // - CATEGORIES_SCHEMA_VERSION is set by migration v8 (see src-tauri/src/lib.rs): // existing profiles are tagged 'v2' (legacy seed), new profiles are 'v1' // (IPC taxonomy). Only v2 profiles are invited to discover the new v1 guide. // - BANNER_DISMISSED is a persistent flag set when the user dismisses the // banner; once set, the banner never reappears for this profile. const CATEGORIES_SCHEMA_VERSION_KEY = "categories_schema_version"; const BANNER_DISMISSED_KEY = "categories_v1_banner_dismissed"; type Visibility = "loading" | "visible" | "hidden"; /** * Dashboard banner that invites users on the legacy v2 category seed to * discover the new v1 IPC taxonomy guide. It is: * - Non-destructive (read-only CTA that navigates to the guide page). * - Dismissable — the dismissal is persisted in `user_preferences` and * survives app restarts / profile reloads. * - Only visible on profiles tagged `categories_schema_version='v2'`. * Profiles on the new v1 seed never see it (they already have the IPC * taxonomy). */ export default function CategoriesV1DiscoveryBanner() { const { t } = useTranslation(); const [visibility, setVisibility] = useState("loading"); useEffect(() => { let cancelled = false; (async () => { try { const [schemaVersion, dismissed] = await Promise.all([ getPreference(CATEGORIES_SCHEMA_VERSION_KEY), getPreference(BANNER_DISMISSED_KEY), ]); if (cancelled) return; const shouldShow = schemaVersion === "v2" && dismissed !== "1"; setVisibility(shouldShow ? "visible" : "hidden"); } catch { // If prefs cannot be read (e.g. DB not ready), hide the banner // silently — it is a purely informational, non-critical UI element. if (!cancelled) setVisibility("hidden"); } })(); return () => { cancelled = true; }; }, []); const dismiss = async () => { // Optimistically hide the banner, then persist the flag. If persistence // fails we still keep it hidden for this session. setVisibility("hidden"); try { await setPreference(BANNER_DISMISSED_KEY, "1"); } catch { // Ignore — the banner will reappear on next launch if the write failed, // which is an acceptable degradation. } }; if (visibility !== "visible") return null; return (

{t("dashboard.categoriesBanner.title")}

{t("dashboard.categoriesBanner.description")}

{t("dashboard.categoriesBanner.cta")}
); }