2026-06-19 14:16:48 +02:00
|
|
|
import { useState } from "react";
|
feat(i18n): translate remaining components (HU/EN/DE)
Feed, VideoCard, Sidebar, PlayerModal, Channels, Stats, SettingsPanel,
OnboardingWizard, NotificationCenter, Toaster, ErrorBoundary and the relativeTime
helper are now fully translated in Hungarian, English and German, each with its own
locale area file (auto-loaded). Key parity verified across all three languages.
2026-06-15 00:47:04 +02:00
|
|
|
import { useTranslation } from "react-i18next";
|
2026-07-09 14:42:32 +02:00
|
|
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
|
|
|
import { Bell, Monitor, RefreshCw, Trash2, User } from "lucide-react";
|
feat(m5a): channel manager, tabbed settings panel, per-user sync status
Backend: /api/channels (list + PATCH priority/hidden + attach/detach user tags),
user-tag CRUD on /api/tags, /api/sync/my-status (per-user channel sync counts).
Frontend: feed|channels page nav (URL-synced) from the account menu; a slide-in
tabbed Settings panel (Appearance, Notifications=6b sound+duration, Sync status +
actions + admin pause/resume, Account); a channel manager with priority, hide,
per-channel user tags, sync badges and 'view in feed'. Notifications now honor the
configurable sound + auto-dismiss settings.
2026-06-11 20:45:48 +02:00
|
|
|
import { SCHEMES, type Scheme, type ThemePrefs } from "../lib/theme";
|
2026-06-19 15:46:49 +02:00
|
|
|
import { api, type Me } from "../lib/api";
|
fix(state): scope all per-account localStorage by account id
Multi-account-in-one-browser (esp. with per-tab accounts) leaked one account's client state
into another via shared localStorage keys. Scope every account-specific key by the tab's active
account (accountKey/readAccount/writeAccount/useAccountPersistedState helpers), so nothing bleeds
across accounts or tabs:
- Real leaks: selected playlist, client notification history + settings, onboarding-dismissed.
- UI position: feed page, channel-manager filter/view + tables, playlist sort, Settings/Stats/
Users/Config tabs.
- Previously DB-adopted caches (theme, hints, performance mode, sidebar layout, nav/filter
collapse) — now the cache is per-account too, so there's no flash of the other account's value
on login.
Kept intentionally global: siftlode.lang (needed pre-login on the Welcome page; the DB pref still
scopes it per-account after sign-in) and siftlode.seenVersion (a per-browser 'new version' banner).
E2EE private keys (IndexedDB) and the chat-dock key were already per-user.
2026-07-02 01:45:16 +02:00
|
|
|
import { LS, useAccountPersistedState } from "../lib/storage";
|
2026-06-12 18:01:43 +02:00
|
|
|
import Avatar from "./Avatar";
|
2026-06-18 23:59:40 +02:00
|
|
|
import { notify, type NotifSettings } from "../lib/notifications";
|
feat(ui): liquid-glass design system, settings polish, hints, notif fixes
- Add a theme-aware glass surface system (.glass/.glass-card + ambient backdrop,
performance-mode opt-out) and apply it across panels, popovers, toasts, cards,
sidebar widgets, channel rows, video cards and login.
- SettingsPanel: slide in/out animation, glass styling, wrapping pill tabs (no
horizontal scrollbar) with a prominent active state.
- Notifications: auto-dismiss can be switched off (stays until closed); the test
notification now also triggers the alert sound; resume a suspended AudioContext.
- Add an app-wide, toggleable hint/tooltip system (lib/hints + Tooltip) and wire
hints across the settings and channel-manager surfaces; persisted per account.
2026-06-11 21:08:35 +02:00
|
|
|
import Tooltip from "./Tooltip";
|
2026-06-29 00:04:45 +02:00
|
|
|
import { Section, SettingRow, Switch } from "./ui/form";
|
2026-06-29 00:07:37 +02:00
|
|
|
import { DraftSaveBar } from "./ui/DraftSaveBar";
|
2026-06-19 15:46:49 +02:00
|
|
|
import { useConfirm } from "./ConfirmProvider";
|
feat(m5a): channel manager, tabbed settings panel, per-user sync status
Backend: /api/channels (list + PATCH priority/hidden + attach/detach user tags),
user-tag CRUD on /api/tags, /api/sync/my-status (per-user channel sync counts).
Frontend: feed|channels page nav (URL-synced) from the account menu; a slide-in
tabbed Settings panel (Appearance, Notifications=6b sound+duration, Sync status +
actions + admin pause/resume, Account); a channel manager with priority, hide,
per-channel user tags, sync badges and 'view in feed'. Notifications now honor the
configurable sound + auto-dismiss settings.
2026-06-11 20:45:48 +02:00
|
|
|
|
2026-06-18 23:59:40 +02:00
|
|
|
// The Settings page edits server-persisted preferences as a draft: changes apply locally
|
|
|
|
|
// for instant preview but reach the server only on an explicit Save (or revert on Discard).
|
|
|
|
|
// App owns the draft + baseline (so the leave-the-page guard can see it); this controller is
|
|
|
|
|
// how the panel reads/writes it. See App.tsx.
|
|
|
|
|
export type PrefsSaveState = "idle" | "saving" | "saved" | "error";
|
|
|
|
|
export interface PrefsController {
|
|
|
|
|
theme: ThemePrefs;
|
|
|
|
|
setTheme: (t: ThemePrefs) => void;
|
|
|
|
|
view: "grid" | "list";
|
|
|
|
|
setView: (v: "grid" | "list") => void;
|
|
|
|
|
perf: boolean;
|
|
|
|
|
setPerf: (v: boolean) => void;
|
|
|
|
|
hints: boolean;
|
|
|
|
|
setHints: (v: boolean) => void;
|
|
|
|
|
notif: NotifSettings;
|
|
|
|
|
setNotif: (n: NotifSettings) => void;
|
|
|
|
|
dirty: boolean;
|
|
|
|
|
save: () => void;
|
|
|
|
|
discard: () => void;
|
|
|
|
|
saveState: PrefsSaveState;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-19 11:48:20 +02:00
|
|
|
type TabId = "appearance" | "notifications" | "account";
|
feat(i18n): translate remaining components (HU/EN/DE)
Feed, VideoCard, Sidebar, PlayerModal, Channels, Stats, SettingsPanel,
OnboardingWizard, NotificationCenter, Toaster, ErrorBoundary and the relativeTime
helper are now fully translated in Hungarian, English and German, each with its own
locale area file (auto-loaded). Key parity verified across all three languages.
2026-06-15 00:47:04 +02:00
|
|
|
const TABS: { id: TabId; icon: typeof Monitor }[] = [
|
|
|
|
|
{ id: "appearance", icon: Monitor },
|
|
|
|
|
{ id: "notifications", icon: Bell },
|
|
|
|
|
{ id: "account", icon: User },
|
feat(m5a): channel manager, tabbed settings panel, per-user sync status
Backend: /api/channels (list + PATCH priority/hidden + attach/detach user tags),
user-tag CRUD on /api/tags, /api/sync/my-status (per-user channel sync counts).
Frontend: feed|channels page nav (URL-synced) from the account menu; a slide-in
tabbed Settings panel (Appearance, Notifications=6b sound+duration, Sync status +
actions + admin pause/resume, Account); a channel manager with priority, hide,
per-channel user tags, sync badges and 'view in feed'. Notifications now honor the
configurable sound + auto-dismiss settings.
2026-06-11 20:45:48 +02:00
|
|
|
];
|
|
|
|
|
|
2026-06-16 01:05:05 +02:00
|
|
|
// Settings as a page (Design B): rendered in the main content area, not an overlay. It keeps
|
|
|
|
|
// the frosted `.glass` surface so it still refracts the ambient backdrop. The page title is
|
|
|
|
|
// shown by the Header; the tab rail stays as in-page sub-navigation.
|
feat(m5a): channel manager, tabbed settings panel, per-user sync status
Backend: /api/channels (list + PATCH priority/hidden + attach/detach user tags),
user-tag CRUD on /api/tags, /api/sync/my-status (per-user channel sync counts).
Frontend: feed|channels page nav (URL-synced) from the account menu; a slide-in
tabbed Settings panel (Appearance, Notifications=6b sound+duration, Sync status +
actions + admin pause/resume, Account); a channel manager with priority, hide,
per-channel user tags, sync badges and 'view in feed'. Notifications now honor the
configurable sound + auto-dismiss settings.
2026-06-11 20:45:48 +02:00
|
|
|
export default function SettingsPanel({
|
|
|
|
|
me,
|
2026-06-18 23:59:40 +02:00
|
|
|
prefs,
|
2026-06-14 01:11:29 +02:00
|
|
|
onOpenWizard,
|
feat(m5a): channel manager, tabbed settings panel, per-user sync status
Backend: /api/channels (list + PATCH priority/hidden + attach/detach user tags),
user-tag CRUD on /api/tags, /api/sync/my-status (per-user channel sync counts).
Frontend: feed|channels page nav (URL-synced) from the account menu; a slide-in
tabbed Settings panel (Appearance, Notifications=6b sound+duration, Sync status +
actions + admin pause/resume, Account); a channel manager with priority, hide,
per-channel user tags, sync badges and 'view in feed'. Notifications now honor the
configurable sound + auto-dismiss settings.
2026-06-11 20:45:48 +02:00
|
|
|
}: {
|
|
|
|
|
me: Me;
|
2026-06-18 23:59:40 +02:00
|
|
|
prefs: PrefsController;
|
2026-06-14 01:11:29 +02:00
|
|
|
onOpenWizard: () => void;
|
feat(m5a): channel manager, tabbed settings panel, per-user sync status
Backend: /api/channels (list + PATCH priority/hidden + attach/detach user tags),
user-tag CRUD on /api/tags, /api/sync/my-status (per-user channel sync counts).
Frontend: feed|channels page nav (URL-synced) from the account menu; a slide-in
tabbed Settings panel (Appearance, Notifications=6b sound+duration, Sync status +
actions + admin pause/resume, Account); a channel manager with priority, hide,
per-channel user tags, sync badges and 'view in feed'. Notifications now honor the
configurable sound + auto-dismiss settings.
2026-06-11 20:45:48 +02:00
|
|
|
}) {
|
feat(i18n): translate remaining components (HU/EN/DE)
Feed, VideoCard, Sidebar, PlayerModal, Channels, Stats, SettingsPanel,
OnboardingWizard, NotificationCenter, Toaster, ErrorBoundary and the relativeTime
helper are now fully translated in Hungarian, English and German, each with its own
locale area file (auto-loaded). Key parity verified across all three languages.
2026-06-15 00:47:04 +02:00
|
|
|
const { t } = useTranslation();
|
fix(state): scope all per-account localStorage by account id
Multi-account-in-one-browser (esp. with per-tab accounts) leaked one account's client state
into another via shared localStorage keys. Scope every account-specific key by the tab's active
account (accountKey/readAccount/writeAccount/useAccountPersistedState helpers), so nothing bleeds
across accounts or tabs:
- Real leaks: selected playlist, client notification history + settings, onboarding-dismissed.
- UI position: feed page, channel-manager filter/view + tables, playlist sort, Settings/Stats/
Users/Config tabs.
- Previously DB-adopted caches (theme, hints, performance mode, sidebar layout, nav/filter
collapse) — now the cache is per-account too, so there's no flash of the other account's value
on login.
Kept intentionally global: siftlode.lang (needed pre-login on the Welcome page; the DB pref still
scopes it per-account after sign-in) and siftlode.seenVersion (a per-browser 'new version' banner).
E2EE private keys (IndexedDB) and the chat-dock key were already per-user.
2026-07-02 01:45:16 +02:00
|
|
|
const [tabRaw, setTab] = useAccountPersistedState(LS.settingsTab, "appearance");
|
2026-06-26 03:30:19 +02:00
|
|
|
// Clamp at render so a stale/removed tab id falls back to Appearance.
|
|
|
|
|
const tab: TabId = TABS.some((tabItem) => tabItem.id === tabRaw)
|
|
|
|
|
? (tabRaw as TabId)
|
|
|
|
|
: "appearance";
|
feat(m5a): channel manager, tabbed settings panel, per-user sync status
Backend: /api/channels (list + PATCH priority/hidden + attach/detach user tags),
user-tag CRUD on /api/tags, /api/sync/my-status (per-user channel sync counts).
Frontend: feed|channels page nav (URL-synced) from the account menu; a slide-in
tabbed Settings panel (Appearance, Notifications=6b sound+duration, Sync status +
actions + admin pause/resume, Account); a channel manager with priority, hide,
per-channel user tags, sync badges and 'view in feed'. Notifications now honor the
configurable sound + auto-dismiss settings.
2026-06-11 20:45:48 +02:00
|
|
|
|
|
|
|
|
return (
|
2026-06-16 01:05:05 +02:00
|
|
|
<div className="p-4 max-w-3xl w-full mx-auto">
|
2026-06-18 23:59:40 +02:00
|
|
|
<div className="glass rounded-2xl overflow-hidden">
|
|
|
|
|
<div className="flex">
|
|
|
|
|
{/* Vertical tab rail — never wraps; active is a clear accent pill. */}
|
|
|
|
|
<nav className="w-32 sm:w-36 shrink-0 border-r border-border/60 p-2 flex flex-col gap-1">
|
|
|
|
|
{TABS.map((tabItem) => {
|
|
|
|
|
const active = tab === tabItem.id;
|
|
|
|
|
return (
|
|
|
|
|
<button
|
|
|
|
|
key={tabItem.id}
|
|
|
|
|
onClick={() => setTab(tabItem.id)}
|
|
|
|
|
className={`flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-left transition ${
|
|
|
|
|
active
|
|
|
|
|
? "bg-accent text-accent-fg font-medium shadow-md shadow-accent/20"
|
|
|
|
|
: "text-muted hover:text-fg hover:bg-card/60"
|
|
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
<tabItem.icon className="w-4 h-4 shrink-0" />
|
|
|
|
|
{t(`settings.tabs.${tabItem.id}`)}
|
|
|
|
|
</button>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
</nav>
|
|
|
|
|
|
2026-06-19 00:04:19 +02:00
|
|
|
{/* Render only the active tab so the panel sizes to its actual content. (Stacking
|
|
|
|
|
every tab in one grid cell made the whole panel as tall as the tallest tab —
|
|
|
|
|
Account — leaving the short tabs with dead space and a needless scrollbar.) */}
|
|
|
|
|
<div className="flex-1 min-w-0 p-4">
|
|
|
|
|
{tab === "appearance" && <Appearance prefs={prefs} />}
|
|
|
|
|
{tab === "notifications" && <Notifications prefs={prefs} />}
|
|
|
|
|
{tab === "account" && <Account me={me} onOpenWizard={onOpenWizard} />}
|
2026-06-18 23:59:40 +02:00
|
|
|
</div>
|
feat(m5a): channel manager, tabbed settings panel, per-user sync status
Backend: /api/channels (list + PATCH priority/hidden + attach/detach user tags),
user-tag CRUD on /api/tags, /api/sync/my-status (per-user channel sync counts).
Frontend: feed|channels page nav (URL-synced) from the account menu; a slide-in
tabbed Settings panel (Appearance, Notifications=6b sound+duration, Sync status +
actions + admin pause/resume, Account); a channel manager with priority, hide,
per-channel user tags, sync badges and 'view in feed'. Notifications now honor the
configurable sound + auto-dismiss settings.
2026-06-11 20:45:48 +02:00
|
|
|
</div>
|
2026-06-18 23:59:40 +02:00
|
|
|
|
|
|
|
|
{/* Save/Discard bar — spans the whole card (a change can come from any tab). Only the
|
|
|
|
|
Appearance/Notifications prefs are drafted; the Sync/Account tabs act immediately. */}
|
|
|
|
|
<PrefsSaveBar prefs={prefs} />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function PrefsSaveBar({ prefs }: { prefs: PrefsController }) {
|
|
|
|
|
const { t } = useTranslation();
|
|
|
|
|
return (
|
2026-06-29 00:07:37 +02:00
|
|
|
<DraftSaveBar
|
|
|
|
|
variant="inline"
|
|
|
|
|
dirty={prefs.dirty}
|
|
|
|
|
state={prefs.saveState}
|
|
|
|
|
onSave={prefs.save}
|
|
|
|
|
onDiscard={prefs.discard}
|
|
|
|
|
labels={{
|
|
|
|
|
saved: t("settings.save.saved"),
|
|
|
|
|
failed: t("settings.save.failed"),
|
|
|
|
|
unsaved: t("settings.save.unsaved"),
|
|
|
|
|
discard: t("settings.save.discard"),
|
|
|
|
|
save: t("settings.save.save"),
|
|
|
|
|
saving: t("settings.save.saving"),
|
|
|
|
|
}}
|
|
|
|
|
/>
|
feat(m5a): channel manager, tabbed settings panel, per-user sync status
Backend: /api/channels (list + PATCH priority/hidden + attach/detach user tags),
user-tag CRUD on /api/tags, /api/sync/my-status (per-user channel sync counts).
Frontend: feed|channels page nav (URL-synced) from the account menu; a slide-in
tabbed Settings panel (Appearance, Notifications=6b sound+duration, Sync status +
actions + admin pause/resume, Account); a channel manager with priority, hide,
per-channel user tags, sync badges and 'view in feed'. Notifications now honor the
configurable sound + auto-dismiss settings.
2026-06-11 20:45:48 +02:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-18 23:59:40 +02:00
|
|
|
function Appearance({ prefs }: { prefs: PrefsController }) {
|
feat(i18n): translate remaining components (HU/EN/DE)
Feed, VideoCard, Sidebar, PlayerModal, Channels, Stats, SettingsPanel,
OnboardingWizard, NotificationCenter, Toaster, ErrorBoundary and the relativeTime
helper are now fully translated in Hungarian, English and German, each with its own
locale area file (auto-loaded). Key parity verified across all three languages.
2026-06-15 00:47:04 +02:00
|
|
|
const { t } = useTranslation();
|
2026-06-18 23:59:40 +02:00
|
|
|
// Controlled by App's prefs draft: each change applies locally for preview but is only
|
|
|
|
|
// persisted on an explicit Save (handled by the panel's Save/Discard bar).
|
|
|
|
|
const { theme, setTheme, view, setView, perf, setPerf, hints, setHints } = prefs;
|
feat(m5a): channel manager, tabbed settings panel, per-user sync status
Backend: /api/channels (list + PATCH priority/hidden + attach/detach user tags),
user-tag CRUD on /api/tags, /api/sync/my-status (per-user channel sync counts).
Frontend: feed|channels page nav (URL-synced) from the account menu; a slide-in
tabbed Settings panel (Appearance, Notifications=6b sound+duration, Sync status +
actions + admin pause/resume, Account); a channel manager with priority, hide,
per-channel user tags, sync badges and 'view in feed'. Notifications now honor the
configurable sound + auto-dismiss settings.
2026-06-11 20:45:48 +02:00
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<>
|
feat(i18n): translate remaining components (HU/EN/DE)
Feed, VideoCard, Sidebar, PlayerModal, Channels, Stats, SettingsPanel,
OnboardingWizard, NotificationCenter, Toaster, ErrorBoundary and the relativeTime
helper are now fully translated in Hungarian, English and German, each with its own
locale area file (auto-loaded). Key parity verified across all three languages.
2026-06-15 00:47:04 +02:00
|
|
|
<Section title={t("settings.appearance.colorScheme")}>
|
feat(m5a): channel manager, tabbed settings panel, per-user sync status
Backend: /api/channels (list + PATCH priority/hidden + attach/detach user tags),
user-tag CRUD on /api/tags, /api/sync/my-status (per-user channel sync counts).
Frontend: feed|channels page nav (URL-synced) from the account menu; a slide-in
tabbed Settings panel (Appearance, Notifications=6b sound+duration, Sync status +
actions + admin pause/resume, Account); a channel manager with priority, hide,
per-channel user tags, sync badges and 'view in feed'. Notifications now honor the
configurable sound + auto-dismiss settings.
2026-06-11 20:45:48 +02:00
|
|
|
<div className="grid grid-cols-4 gap-2">
|
|
|
|
|
{SCHEMES.map((s) => (
|
feat(ui): liquid-glass design system, settings polish, hints, notif fixes
- Add a theme-aware glass surface system (.glass/.glass-card + ambient backdrop,
performance-mode opt-out) and apply it across panels, popovers, toasts, cards,
sidebar widgets, channel rows, video cards and login.
- SettingsPanel: slide in/out animation, glass styling, wrapping pill tabs (no
horizontal scrollbar) with a prominent active state.
- Notifications: auto-dismiss can be switched off (stays until closed); the test
notification now also triggers the alert sound; resume a suspended AudioContext.
- Add an app-wide, toggleable hint/tooltip system (lib/hints + Tooltip) and wire
hints across the settings and channel-manager surfaces; persisted per account.
2026-06-11 21:08:35 +02:00
|
|
|
<Tooltip key={s.id} hint={s.name}>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => setTheme({ ...theme, scheme: s.id as Scheme })}
|
2026-07-04 19:13:11 +02:00
|
|
|
aria-label={s.name}
|
|
|
|
|
aria-pressed={theme.scheme === s.id}
|
feat(ui): liquid-glass design system, settings polish, hints, notif fixes
- Add a theme-aware glass surface system (.glass/.glass-card + ambient backdrop,
performance-mode opt-out) and apply it across panels, popovers, toasts, cards,
sidebar widgets, channel rows, video cards and login.
- SettingsPanel: slide in/out animation, glass styling, wrapping pill tabs (no
horizontal scrollbar) with a prominent active state.
- Notifications: auto-dismiss can be switched off (stays until closed); the test
notification now also triggers the alert sound; resume a suspended AudioContext.
- Add an app-wide, toggleable hint/tooltip system (lib/hints + Tooltip) and wire
hints across the settings and channel-manager surfaces; persisted per account.
2026-06-11 21:08:35 +02:00
|
|
|
className={`h-9 w-full rounded-lg border-2 transition ${
|
|
|
|
|
theme.scheme === s.id ? "border-fg" : "border-transparent"
|
|
|
|
|
}`}
|
|
|
|
|
style={{ background: s.swatch }}
|
|
|
|
|
/>
|
|
|
|
|
</Tooltip>
|
feat(m5a): channel manager, tabbed settings panel, per-user sync status
Backend: /api/channels (list + PATCH priority/hidden + attach/detach user tags),
user-tag CRUD on /api/tags, /api/sync/my-status (per-user channel sync counts).
Frontend: feed|channels page nav (URL-synced) from the account menu; a slide-in
tabbed Settings panel (Appearance, Notifications=6b sound+duration, Sync status +
actions + admin pause/resume, Account); a channel manager with priority, hide,
per-channel user tags, sync badges and 'view in feed'. Notifications now honor the
configurable sound + auto-dismiss settings.
2026-06-11 20:45:48 +02:00
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</Section>
|
|
|
|
|
|
feat(i18n): translate remaining components (HU/EN/DE)
Feed, VideoCard, Sidebar, PlayerModal, Channels, Stats, SettingsPanel,
OnboardingWizard, NotificationCenter, Toaster, ErrorBoundary and the relativeTime
helper are now fully translated in Hungarian, English and German, each with its own
locale area file (auto-loaded). Key parity verified across all three languages.
2026-06-15 00:47:04 +02:00
|
|
|
<Section title={t("settings.appearance.display")}>
|
2026-06-29 00:04:45 +02:00
|
|
|
<SettingRow label={t("settings.appearance.darkMode")}>
|
feat(m5a): channel manager, tabbed settings panel, per-user sync status
Backend: /api/channels (list + PATCH priority/hidden + attach/detach user tags),
user-tag CRUD on /api/tags, /api/sync/my-status (per-user channel sync counts).
Frontend: feed|channels page nav (URL-synced) from the account menu; a slide-in
tabbed Settings panel (Appearance, Notifications=6b sound+duration, Sync status +
actions + admin pause/resume, Account); a channel manager with priority, hide,
per-channel user tags, sync badges and 'view in feed'. Notifications now honor the
configurable sound + auto-dismiss settings.
2026-06-11 20:45:48 +02:00
|
|
|
<Switch
|
2026-07-04 19:13:11 +02:00
|
|
|
label={t("settings.appearance.darkMode")}
|
feat(m5a): channel manager, tabbed settings panel, per-user sync status
Backend: /api/channels (list + PATCH priority/hidden + attach/detach user tags),
user-tag CRUD on /api/tags, /api/sync/my-status (per-user channel sync counts).
Frontend: feed|channels page nav (URL-synced) from the account menu; a slide-in
tabbed Settings panel (Appearance, Notifications=6b sound+duration, Sync status +
actions + admin pause/resume, Account); a channel manager with priority, hide,
per-channel user tags, sync badges and 'view in feed'. Notifications now honor the
configurable sound + auto-dismiss settings.
2026-06-11 20:45:48 +02:00
|
|
|
checked={theme.mode === "dark"}
|
|
|
|
|
onChange={(v) => setTheme({ ...theme, mode: v ? "dark" : "light" })}
|
|
|
|
|
/>
|
2026-06-29 00:04:45 +02:00
|
|
|
</SettingRow>
|
|
|
|
|
<SettingRow label={t("settings.appearance.listView")} hint={t("settings.appearance.listViewHint")}>
|
2026-07-04 19:13:11 +02:00
|
|
|
<Switch
|
|
|
|
|
label={t("settings.appearance.listView")}
|
|
|
|
|
checked={view === "list"}
|
|
|
|
|
onChange={(v) => setView(v ? "list" : "grid")}
|
|
|
|
|
/>
|
2026-06-29 00:04:45 +02:00
|
|
|
</SettingRow>
|
|
|
|
|
<SettingRow
|
feat(i18n): translate remaining components (HU/EN/DE)
Feed, VideoCard, Sidebar, PlayerModal, Channels, Stats, SettingsPanel,
OnboardingWizard, NotificationCenter, Toaster, ErrorBoundary and the relativeTime
helper are now fully translated in Hungarian, English and German, each with its own
locale area file (auto-loaded). Key parity verified across all three languages.
2026-06-15 00:47:04 +02:00
|
|
|
label={t("settings.appearance.performanceMode")}
|
|
|
|
|
hint={t("settings.appearance.performanceModeHint")}
|
feat(ui): liquid-glass design system, settings polish, hints, notif fixes
- Add a theme-aware glass surface system (.glass/.glass-card + ambient backdrop,
performance-mode opt-out) and apply it across panels, popovers, toasts, cards,
sidebar widgets, channel rows, video cards and login.
- SettingsPanel: slide in/out animation, glass styling, wrapping pill tabs (no
horizontal scrollbar) with a prominent active state.
- Notifications: auto-dismiss can be switched off (stays until closed); the test
notification now also triggers the alert sound; resume a suspended AudioContext.
- Add an app-wide, toggleable hint/tooltip system (lib/hints + Tooltip) and wire
hints across the settings and channel-manager surfaces; persisted per account.
2026-06-11 21:08:35 +02:00
|
|
|
>
|
2026-07-04 19:13:11 +02:00
|
|
|
<Switch label={t("settings.appearance.performanceMode")} checked={perf} onChange={setPerf} />
|
2026-06-29 00:04:45 +02:00
|
|
|
</SettingRow>
|
|
|
|
|
<SettingRow
|
feat(i18n): translate remaining components (HU/EN/DE)
Feed, VideoCard, Sidebar, PlayerModal, Channels, Stats, SettingsPanel,
OnboardingWizard, NotificationCenter, Toaster, ErrorBoundary and the relativeTime
helper are now fully translated in Hungarian, English and German, each with its own
locale area file (auto-loaded). Key parity verified across all three languages.
2026-06-15 00:47:04 +02:00
|
|
|
label={t("settings.appearance.showHints")}
|
|
|
|
|
hint={t("settings.appearance.showHintsHint")}
|
feat(ui): liquid-glass design system, settings polish, hints, notif fixes
- Add a theme-aware glass surface system (.glass/.glass-card + ambient backdrop,
performance-mode opt-out) and apply it across panels, popovers, toasts, cards,
sidebar widgets, channel rows, video cards and login.
- SettingsPanel: slide in/out animation, glass styling, wrapping pill tabs (no
horizontal scrollbar) with a prominent active state.
- Notifications: auto-dismiss can be switched off (stays until closed); the test
notification now also triggers the alert sound; resume a suspended AudioContext.
- Add an app-wide, toggleable hint/tooltip system (lib/hints + Tooltip) and wire
hints across the settings and channel-manager surfaces; persisted per account.
2026-06-11 21:08:35 +02:00
|
|
|
>
|
2026-07-04 19:13:11 +02:00
|
|
|
<Switch label={t("settings.appearance.showHints")} checked={hints} onChange={setHints} />
|
2026-06-29 00:04:45 +02:00
|
|
|
</SettingRow>
|
feat(m5a): channel manager, tabbed settings panel, per-user sync status
Backend: /api/channels (list + PATCH priority/hidden + attach/detach user tags),
user-tag CRUD on /api/tags, /api/sync/my-status (per-user channel sync counts).
Frontend: feed|channels page nav (URL-synced) from the account menu; a slide-in
tabbed Settings panel (Appearance, Notifications=6b sound+duration, Sync status +
actions + admin pause/resume, Account); a channel manager with priority, hide,
per-channel user tags, sync badges and 'view in feed'. Notifications now honor the
configurable sound + auto-dismiss settings.
2026-06-11 20:45:48 +02:00
|
|
|
</Section>
|
|
|
|
|
|
feat(i18n): translate remaining components (HU/EN/DE)
Feed, VideoCard, Sidebar, PlayerModal, Channels, Stats, SettingsPanel,
OnboardingWizard, NotificationCenter, Toaster, ErrorBoundary and the relativeTime
helper are now fully translated in Hungarian, English and German, each with its own
locale area file (auto-loaded). Key parity verified across all three languages.
2026-06-15 00:47:04 +02:00
|
|
|
<Section title={t("settings.appearance.textSize")}>
|
feat(m5a): channel manager, tabbed settings panel, per-user sync status
Backend: /api/channels (list + PATCH priority/hidden + attach/detach user tags),
user-tag CRUD on /api/tags, /api/sync/my-status (per-user channel sync counts).
Frontend: feed|channels page nav (URL-synced) from the account menu; a slide-in
tabbed Settings panel (Appearance, Notifications=6b sound+duration, Sync status +
actions + admin pause/resume, Account); a channel manager with priority, hide,
per-channel user tags, sync badges and 'view in feed'. Notifications now honor the
configurable sound + auto-dismiss settings.
2026-06-11 20:45:48 +02:00
|
|
|
<input
|
|
|
|
|
type="range"
|
|
|
|
|
min={0.9}
|
|
|
|
|
max={1.3}
|
|
|
|
|
step={0.02}
|
|
|
|
|
value={theme.fontScale}
|
|
|
|
|
onChange={(e) => setTheme({ ...theme, fontScale: Number(e.target.value) })}
|
2026-07-04 19:13:11 +02:00
|
|
|
aria-label={t("settings.appearance.textSize")}
|
feat(m5a): channel manager, tabbed settings panel, per-user sync status
Backend: /api/channels (list + PATCH priority/hidden + attach/detach user tags),
user-tag CRUD on /api/tags, /api/sync/my-status (per-user channel sync counts).
Frontend: feed|channels page nav (URL-synced) from the account menu; a slide-in
tabbed Settings panel (Appearance, Notifications=6b sound+duration, Sync status +
actions + admin pause/resume, Account); a channel manager with priority, hide,
per-channel user tags, sync badges and 'view in feed'. Notifications now honor the
configurable sound + auto-dismiss settings.
2026-06-11 20:45:48 +02:00
|
|
|
className="w-full accent-accent"
|
|
|
|
|
/>
|
|
|
|
|
<div className="text-right text-xs text-muted">{Math.round(theme.fontScale * 100)}%</div>
|
|
|
|
|
</Section>
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-18 23:59:40 +02:00
|
|
|
function Notifications({ prefs }: { prefs: PrefsController }) {
|
feat(i18n): translate remaining components (HU/EN/DE)
Feed, VideoCard, Sidebar, PlayerModal, Channels, Stats, SettingsPanel,
OnboardingWizard, NotificationCenter, Toaster, ErrorBoundary and the relativeTime
helper are now fully translated in Hungarian, English and German, each with its own
locale area file (auto-loaded). Key parity verified across all three languages.
2026-06-15 00:47:04 +02:00
|
|
|
const { t } = useTranslation();
|
2026-06-18 23:59:40 +02:00
|
|
|
// Controlled by App's prefs draft (live preview via configureNotifications there); the
|
|
|
|
|
// panel's Save bar persists it. The "send test" button below still fires immediately.
|
|
|
|
|
const { notif: cfg, setNotif } = prefs;
|
|
|
|
|
const update = (p: Partial<NotifSettings>) => setNotif({ ...cfg, ...p });
|
feat(ui): liquid-glass design system, settings polish, hints, notif fixes
- Add a theme-aware glass surface system (.glass/.glass-card + ambient backdrop,
performance-mode opt-out) and apply it across panels, popovers, toasts, cards,
sidebar widgets, channel rows, video cards and login.
- SettingsPanel: slide in/out animation, glass styling, wrapping pill tabs (no
horizontal scrollbar) with a prominent active state.
- Notifications: auto-dismiss can be switched off (stays until closed); the test
notification now also triggers the alert sound; resume a suspended AudioContext.
- Add an app-wide, toggleable hint/tooltip system (lib/hints + Tooltip) and wire
hints across the settings and channel-manager surfaces; persisted per account.
2026-06-11 21:08:35 +02:00
|
|
|
const autoOn = cfg.durationMs > 0;
|
|
|
|
|
|
feat(m5a): channel manager, tabbed settings panel, per-user sync status
Backend: /api/channels (list + PATCH priority/hidden + attach/detach user tags),
user-tag CRUD on /api/tags, /api/sync/my-status (per-user channel sync counts).
Frontend: feed|channels page nav (URL-synced) from the account menu; a slide-in
tabbed Settings panel (Appearance, Notifications=6b sound+duration, Sync status +
actions + admin pause/resume, Account); a channel manager with priority, hide,
per-channel user tags, sync badges and 'view in feed'. Notifications now honor the
configurable sound + auto-dismiss settings.
2026-06-11 20:45:48 +02:00
|
|
|
return (
|
feat(i18n): translate remaining components (HU/EN/DE)
Feed, VideoCard, Sidebar, PlayerModal, Channels, Stats, SettingsPanel,
OnboardingWizard, NotificationCenter, Toaster, ErrorBoundary and the relativeTime
helper are now fully translated in Hungarian, English and German, each with its own
locale area file (auto-loaded). Key parity verified across all three languages.
2026-06-15 00:47:04 +02:00
|
|
|
<Section title={t("settings.notifications.title")}>
|
2026-06-29 00:04:45 +02:00
|
|
|
<SettingRow
|
feat(i18n): translate remaining components (HU/EN/DE)
Feed, VideoCard, Sidebar, PlayerModal, Channels, Stats, SettingsPanel,
OnboardingWizard, NotificationCenter, Toaster, ErrorBoundary and the relativeTime
helper are now fully translated in Hungarian, English and German, each with its own
locale area file (auto-loaded). Key parity verified across all three languages.
2026-06-15 00:47:04 +02:00
|
|
|
label={t("settings.notifications.sound")}
|
|
|
|
|
hint={t("settings.notifications.soundHint")}
|
feat(ui): liquid-glass design system, settings polish, hints, notif fixes
- Add a theme-aware glass surface system (.glass/.glass-card + ambient backdrop,
performance-mode opt-out) and apply it across panels, popovers, toasts, cards,
sidebar widgets, channel rows, video cards and login.
- SettingsPanel: slide in/out animation, glass styling, wrapping pill tabs (no
horizontal scrollbar) with a prominent active state.
- Notifications: auto-dismiss can be switched off (stays until closed); the test
notification now also triggers the alert sound; resume a suspended AudioContext.
- Add an app-wide, toggleable hint/tooltip system (lib/hints + Tooltip) and wire
hints across the settings and channel-manager surfaces; persisted per account.
2026-06-11 21:08:35 +02:00
|
|
|
>
|
2026-07-04 19:13:11 +02:00
|
|
|
<Switch
|
|
|
|
|
label={t("settings.notifications.sound")}
|
|
|
|
|
checked={cfg.sound}
|
|
|
|
|
onChange={(v) => update({ sound: v })}
|
|
|
|
|
/>
|
2026-06-29 00:04:45 +02:00
|
|
|
</SettingRow>
|
|
|
|
|
<SettingRow
|
feat(i18n): translate remaining components (HU/EN/DE)
Feed, VideoCard, Sidebar, PlayerModal, Channels, Stats, SettingsPanel,
OnboardingWizard, NotificationCenter, Toaster, ErrorBoundary and the relativeTime
helper are now fully translated in Hungarian, English and German, each with its own
locale area file (auto-loaded). Key parity verified across all three languages.
2026-06-15 00:47:04 +02:00
|
|
|
label={t("settings.notifications.autoDismiss")}
|
|
|
|
|
hint={t("settings.notifications.autoDismissHint")}
|
feat(ui): liquid-glass design system, settings polish, hints, notif fixes
- Add a theme-aware glass surface system (.glass/.glass-card + ambient backdrop,
performance-mode opt-out) and apply it across panels, popovers, toasts, cards,
sidebar widgets, channel rows, video cards and login.
- SettingsPanel: slide in/out animation, glass styling, wrapping pill tabs (no
horizontal scrollbar) with a prominent active state.
- Notifications: auto-dismiss can be switched off (stays until closed); the test
notification now also triggers the alert sound; resume a suspended AudioContext.
- Add an app-wide, toggleable hint/tooltip system (lib/hints + Tooltip) and wire
hints across the settings and channel-manager surfaces; persisted per account.
2026-06-11 21:08:35 +02:00
|
|
|
>
|
2026-07-04 19:13:11 +02:00
|
|
|
<Switch
|
|
|
|
|
label={t("settings.notifications.autoDismiss")}
|
|
|
|
|
checked={autoOn}
|
|
|
|
|
onChange={(v) => update({ durationMs: v ? 6000 : 0 })}
|
|
|
|
|
/>
|
2026-06-29 00:04:45 +02:00
|
|
|
</SettingRow>
|
feat(ui): liquid-glass design system, settings polish, hints, notif fixes
- Add a theme-aware glass surface system (.glass/.glass-card + ambient backdrop,
performance-mode opt-out) and apply it across panels, popovers, toasts, cards,
sidebar widgets, channel rows, video cards and login.
- SettingsPanel: slide in/out animation, glass styling, wrapping pill tabs (no
horizontal scrollbar) with a prominent active state.
- Notifications: auto-dismiss can be switched off (stays until closed); the test
notification now also triggers the alert sound; resume a suspended AudioContext.
- Add an app-wide, toggleable hint/tooltip system (lib/hints + Tooltip) and wire
hints across the settings and channel-manager surfaces; persisted per account.
2026-06-11 21:08:35 +02:00
|
|
|
{autoOn && (
|
|
|
|
|
<div className="py-1.5 text-sm">
|
|
|
|
|
<div className="flex items-center justify-between">
|
feat(i18n): translate remaining components (HU/EN/DE)
Feed, VideoCard, Sidebar, PlayerModal, Channels, Stats, SettingsPanel,
OnboardingWizard, NotificationCenter, Toaster, ErrorBoundary and the relativeTime
helper are now fully translated in Hungarian, English and German, each with its own
locale area file (auto-loaded). Key parity verified across all three languages.
2026-06-15 00:47:04 +02:00
|
|
|
<span className="text-muted text-xs">{t("settings.notifications.dismissAfter")}</span>
|
feat(ui): liquid-glass design system, settings polish, hints, notif fixes
- Add a theme-aware glass surface system (.glass/.glass-card + ambient backdrop,
performance-mode opt-out) and apply it across panels, popovers, toasts, cards,
sidebar widgets, channel rows, video cards and login.
- SettingsPanel: slide in/out animation, glass styling, wrapping pill tabs (no
horizontal scrollbar) with a prominent active state.
- Notifications: auto-dismiss can be switched off (stays until closed); the test
notification now also triggers the alert sound; resume a suspended AudioContext.
- Add an app-wide, toggleable hint/tooltip system (lib/hints + Tooltip) and wire
hints across the settings and channel-manager surfaces; persisted per account.
2026-06-11 21:08:35 +02:00
|
|
|
<span className="text-muted text-xs">{(cfg.durationMs / 1000).toFixed(0)}s</span>
|
|
|
|
|
</div>
|
|
|
|
|
<input
|
|
|
|
|
type="range"
|
|
|
|
|
min={2000}
|
|
|
|
|
max={15000}
|
|
|
|
|
step={1000}
|
|
|
|
|
value={cfg.durationMs}
|
|
|
|
|
onChange={(e) => update({ durationMs: Number(e.target.value) })}
|
2026-07-04 19:13:11 +02:00
|
|
|
aria-label={t("settings.notifications.dismissAfter")}
|
feat(ui): liquid-glass design system, settings polish, hints, notif fixes
- Add a theme-aware glass surface system (.glass/.glass-card + ambient backdrop,
performance-mode opt-out) and apply it across panels, popovers, toasts, cards,
sidebar widgets, channel rows, video cards and login.
- SettingsPanel: slide in/out animation, glass styling, wrapping pill tabs (no
horizontal scrollbar) with a prominent active state.
- Notifications: auto-dismiss can be switched off (stays until closed); the test
notification now also triggers the alert sound; resume a suspended AudioContext.
- Add an app-wide, toggleable hint/tooltip system (lib/hints + Tooltip) and wire
hints across the settings and channel-manager surfaces; persisted per account.
2026-06-11 21:08:35 +02:00
|
|
|
className="w-full accent-accent mt-1"
|
|
|
|
|
/>
|
feat(m5a): channel manager, tabbed settings panel, per-user sync status
Backend: /api/channels (list + PATCH priority/hidden + attach/detach user tags),
user-tag CRUD on /api/tags, /api/sync/my-status (per-user channel sync counts).
Frontend: feed|channels page nav (URL-synced) from the account menu; a slide-in
tabbed Settings panel (Appearance, Notifications=6b sound+duration, Sync status +
actions + admin pause/resume, Account); a channel manager with priority, hide,
per-channel user tags, sync badges and 'view in feed'. Notifications now honor the
configurable sound + auto-dismiss settings.
2026-06-11 20:45:48 +02:00
|
|
|
</div>
|
feat(ui): liquid-glass design system, settings polish, hints, notif fixes
- Add a theme-aware glass surface system (.glass/.glass-card + ambient backdrop,
performance-mode opt-out) and apply it across panels, popovers, toasts, cards,
sidebar widgets, channel rows, video cards and login.
- SettingsPanel: slide in/out animation, glass styling, wrapping pill tabs (no
horizontal scrollbar) with a prominent active state.
- Notifications: auto-dismiss can be switched off (stays until closed); the test
notification now also triggers the alert sound; resume a suspended AudioContext.
- Add an app-wide, toggleable hint/tooltip system (lib/hints + Tooltip) and wire
hints across the settings and channel-manager surfaces; persisted per account.
2026-06-11 21:08:35 +02:00
|
|
|
)}
|
feat(m5a): channel manager, tabbed settings panel, per-user sync status
Backend: /api/channels (list + PATCH priority/hidden + attach/detach user tags),
user-tag CRUD on /api/tags, /api/sync/my-status (per-user channel sync counts).
Frontend: feed|channels page nav (URL-synced) from the account menu; a slide-in
tabbed Settings panel (Appearance, Notifications=6b sound+duration, Sync status +
actions + admin pause/resume, Account); a channel manager with priority, hide,
per-channel user tags, sync badges and 'view in feed'. Notifications now honor the
configurable sound + auto-dismiss settings.
2026-06-11 20:45:48 +02:00
|
|
|
<button
|
feat(ui): liquid-glass design system, settings polish, hints, notif fixes
- Add a theme-aware glass surface system (.glass/.glass-card + ambient backdrop,
performance-mode opt-out) and apply it across panels, popovers, toasts, cards,
sidebar widgets, channel rows, video cards and login.
- SettingsPanel: slide in/out animation, glass styling, wrapping pill tabs (no
horizontal scrollbar) with a prominent active state.
- Notifications: auto-dismiss can be switched off (stays until closed); the test
notification now also triggers the alert sound; resume a suspended AudioContext.
- Add an app-wide, toggleable hint/tooltip system (lib/hints + Tooltip) and wire
hints across the settings and channel-manager surfaces; persisted per account.
2026-06-11 21:08:35 +02:00
|
|
|
onClick={() =>
|
|
|
|
|
notify({
|
|
|
|
|
level: "info",
|
feat(i18n): translate remaining components (HU/EN/DE)
Feed, VideoCard, Sidebar, PlayerModal, Channels, Stats, SettingsPanel,
OnboardingWizard, NotificationCenter, Toaster, ErrorBoundary and the relativeTime
helper are now fully translated in Hungarian, English and German, each with its own
locale area file (auto-loaded). Key parity verified across all three languages.
2026-06-15 00:47:04 +02:00
|
|
|
title: t("settings.notifications.testTitle"),
|
|
|
|
|
message: t("settings.notifications.testMessage"),
|
2026-06-11 21:30:25 +02:00
|
|
|
sound: true,
|
feat(ui): liquid-glass design system, settings polish, hints, notif fixes
- Add a theme-aware glass surface system (.glass/.glass-card + ambient backdrop,
performance-mode opt-out) and apply it across panels, popovers, toasts, cards,
sidebar widgets, channel rows, video cards and login.
- SettingsPanel: slide in/out animation, glass styling, wrapping pill tabs (no
horizontal scrollbar) with a prominent active state.
- Notifications: auto-dismiss can be switched off (stays until closed); the test
notification now also triggers the alert sound; resume a suspended AudioContext.
- Add an app-wide, toggleable hint/tooltip system (lib/hints + Tooltip) and wire
hints across the settings and channel-manager surfaces; persisted per account.
2026-06-11 21:08:35 +02:00
|
|
|
})
|
|
|
|
|
}
|
feat(m5a): channel manager, tabbed settings panel, per-user sync status
Backend: /api/channels (list + PATCH priority/hidden + attach/detach user tags),
user-tag CRUD on /api/tags, /api/sync/my-status (per-user channel sync counts).
Frontend: feed|channels page nav (URL-synced) from the account menu; a slide-in
tabbed Settings panel (Appearance, Notifications=6b sound+duration, Sync status +
actions + admin pause/resume, Account); a channel manager with priority, hide,
per-channel user tags, sync badges and 'view in feed'. Notifications now honor the
configurable sound + auto-dismiss settings.
2026-06-11 20:45:48 +02:00
|
|
|
className="mt-2 text-sm text-accent hover:underline"
|
|
|
|
|
>
|
feat(i18n): translate remaining components (HU/EN/DE)
Feed, VideoCard, Sidebar, PlayerModal, Channels, Stats, SettingsPanel,
OnboardingWizard, NotificationCenter, Toaster, ErrorBoundary and the relativeTime
helper are now fully translated in Hungarian, English and German, each with its own
locale area file (auto-loaded). Key parity verified across all three languages.
2026-06-15 00:47:04 +02:00
|
|
|
{t("settings.notifications.sendTest")}
|
feat(m5a): channel manager, tabbed settings panel, per-user sync status
Backend: /api/channels (list + PATCH priority/hidden + attach/detach user tags),
user-tag CRUD on /api/tags, /api/sync/my-status (per-user channel sync counts).
Frontend: feed|channels page nav (URL-synced) from the account menu; a slide-in
tabbed Settings panel (Appearance, Notifications=6b sound+duration, Sync status +
actions + admin pause/resume, Account); a channel manager with priority, hide,
per-channel user tags, sync badges and 'view in feed'. Notifications now honor the
configurable sound + auto-dismiss settings.
2026-06-11 20:45:48 +02:00
|
|
|
</button>
|
|
|
|
|
</Section>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-14 01:11:29 +02:00
|
|
|
function AccessRow({
|
|
|
|
|
title,
|
|
|
|
|
granted,
|
|
|
|
|
grantedHint,
|
|
|
|
|
enableHint,
|
|
|
|
|
onEnable,
|
|
|
|
|
}: {
|
|
|
|
|
title: string;
|
|
|
|
|
granted: boolean;
|
|
|
|
|
grantedHint: string;
|
|
|
|
|
enableHint: string;
|
|
|
|
|
onEnable: () => void;
|
|
|
|
|
}) {
|
feat(i18n): translate remaining components (HU/EN/DE)
Feed, VideoCard, Sidebar, PlayerModal, Channels, Stats, SettingsPanel,
OnboardingWizard, NotificationCenter, Toaster, ErrorBoundary and the relativeTime
helper are now fully translated in Hungarian, English and German, each with its own
locale area file (auto-loaded). Key parity verified across all three languages.
2026-06-15 00:47:04 +02:00
|
|
|
const { t } = useTranslation();
|
2026-06-14 01:11:29 +02:00
|
|
|
return (
|
|
|
|
|
<div className="flex items-start justify-between gap-3 py-2">
|
|
|
|
|
<div className="min-w-0">
|
|
|
|
|
<div className="text-sm font-medium">{title}</div>
|
|
|
|
|
<p className="text-xs text-muted leading-relaxed mt-0.5">
|
|
|
|
|
{granted ? grantedHint : enableHint}
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
{granted ? (
|
|
|
|
|
<span className="shrink-0 text-[11px] px-2 py-1 rounded-full border border-accent/40 text-accent">
|
feat(i18n): translate remaining components (HU/EN/DE)
Feed, VideoCard, Sidebar, PlayerModal, Channels, Stats, SettingsPanel,
OnboardingWizard, NotificationCenter, Toaster, ErrorBoundary and the relativeTime
helper are now fully translated in Hungarian, English and German, each with its own
locale area file (auto-loaded). Key parity verified across all three languages.
2026-06-15 00:47:04 +02:00
|
|
|
{t("settings.account.granted")}
|
2026-06-14 01:11:29 +02:00
|
|
|
</span>
|
|
|
|
|
) : (
|
feat(i18n): translate remaining components (HU/EN/DE)
Feed, VideoCard, Sidebar, PlayerModal, Channels, Stats, SettingsPanel,
OnboardingWizard, NotificationCenter, Toaster, ErrorBoundary and the relativeTime
helper are now fully translated in Hungarian, English and German, each with its own
locale area file (auto-loaded). Key parity verified across all three languages.
2026-06-15 00:47:04 +02:00
|
|
|
<Tooltip hint={t("settings.account.enableHint")}>
|
2026-06-14 01:11:29 +02:00
|
|
|
<button
|
|
|
|
|
onClick={onEnable}
|
|
|
|
|
className="shrink-0 glass-card glass-hover px-3 py-1.5 rounded-xl text-sm transition"
|
|
|
|
|
>
|
feat(i18n): translate remaining components (HU/EN/DE)
Feed, VideoCard, Sidebar, PlayerModal, Channels, Stats, SettingsPanel,
OnboardingWizard, NotificationCenter, Toaster, ErrorBoundary and the relativeTime
helper are now fully translated in Hungarian, English and German, each with its own
locale area file (auto-loaded). Key parity verified across all three languages.
2026-06-15 00:47:04 +02:00
|
|
|
{t("settings.account.enable")}
|
2026-06-14 01:11:29 +02:00
|
|
|
</button>
|
|
|
|
|
</Tooltip>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-19 19:52:02 +02:00
|
|
|
// Sign-in methods: link a Google account to a password account (or vice-versa set a password),
|
|
|
|
|
// so either method can reach the same account. Google connect is a full-page OAuth round-trip
|
|
|
|
|
// (auth.py attaches the identity to the current session via /auth/link); the password form posts
|
|
|
|
|
// directly with inline errors. Demo accounts never see this (handled by the caller).
|
|
|
|
|
function SignInMethods({ me }: { me: Me }) {
|
|
|
|
|
const { t } = useTranslation();
|
|
|
|
|
const qc = useQueryClient();
|
|
|
|
|
const [open, setOpen] = useState(false);
|
|
|
|
|
const [current, setCurrent] = useState("");
|
|
|
|
|
const [next, setNext] = useState("");
|
|
|
|
|
const [err, setErr] = useState<string | null>(null);
|
|
|
|
|
const [busy, setBusy] = useState(false);
|
|
|
|
|
|
|
|
|
|
const submit = async (e: React.FormEvent) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
setErr(null);
|
|
|
|
|
setBusy(true);
|
|
|
|
|
try {
|
|
|
|
|
await api.setPassword(next, me.has_password ? current : undefined);
|
|
|
|
|
setOpen(false);
|
|
|
|
|
setCurrent("");
|
|
|
|
|
setNext("");
|
|
|
|
|
// Refresh `me` so has_password flips and the section switches to "Change password".
|
|
|
|
|
await qc.invalidateQueries({ queryKey: ["me"] });
|
|
|
|
|
notify({ level: "success", message: t("settings.account.password.saved", { email: me.email }) });
|
|
|
|
|
} catch (e: any) {
|
|
|
|
|
setErr(e?.detail ?? t("settings.account.password.failed"));
|
|
|
|
|
} finally {
|
|
|
|
|
setBusy(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const inputCls =
|
|
|
|
|
"w-full bg-card border border-border rounded-xl px-3 py-2 text-sm outline-none focus:border-accent";
|
|
|
|
|
|
2026-06-21 02:06:37 +02:00
|
|
|
// Offer Google only when this instance has Google OAuth configured (or the account is already
|
|
|
|
|
// linked — then we still show its "Connected" status even if the instance creds were removed).
|
|
|
|
|
const showGoogle = me.google_enabled || me.has_google;
|
|
|
|
|
|
2026-06-19 19:52:02 +02:00
|
|
|
return (
|
|
|
|
|
<Section title={t("settings.account.signInMethods")}>
|
2026-06-21 02:06:37 +02:00
|
|
|
{showGoogle && (
|
|
|
|
|
<div className="flex items-start justify-between gap-3 py-2">
|
|
|
|
|
<div className="min-w-0">
|
|
|
|
|
<div className="text-sm font-medium">{t("settings.account.googleLink.title")}</div>
|
|
|
|
|
<p className="text-xs text-muted leading-relaxed mt-0.5">
|
|
|
|
|
{me.has_google
|
|
|
|
|
? t("settings.account.googleLink.connectedHint")
|
|
|
|
|
: t("settings.account.googleLink.connectHint")}
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
{me.has_google ? (
|
|
|
|
|
<span className="shrink-0 text-[11px] px-2 py-1 rounded-full border border-accent/40 text-accent">
|
|
|
|
|
{t("settings.account.googleLink.connected")}
|
|
|
|
|
</span>
|
|
|
|
|
) : (
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => {
|
|
|
|
|
window.location.href = "/auth/link";
|
|
|
|
|
}}
|
|
|
|
|
className="shrink-0 glass-card glass-hover px-3 py-1.5 rounded-xl text-sm transition"
|
|
|
|
|
>
|
|
|
|
|
{t("settings.account.googleLink.connect")}
|
|
|
|
|
</button>
|
|
|
|
|
)}
|
2026-06-19 19:52:02 +02:00
|
|
|
</div>
|
2026-06-21 02:06:37 +02:00
|
|
|
)}
|
2026-06-19 19:52:02 +02:00
|
|
|
|
2026-06-21 02:06:37 +02:00
|
|
|
<div className={showGoogle ? "border-t border-border mt-1 pt-1" : ""}>
|
2026-06-19 19:52:02 +02:00
|
|
|
<div className="flex items-start justify-between gap-3 py-2">
|
|
|
|
|
<div className="min-w-0">
|
|
|
|
|
<div className="text-sm font-medium">{t("settings.account.password.title")}</div>
|
|
|
|
|
<p className="text-xs text-muted leading-relaxed mt-0.5">
|
|
|
|
|
{me.has_password
|
|
|
|
|
? t("settings.account.password.setHint")
|
|
|
|
|
: t("settings.account.password.unsetHint")}
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => {
|
|
|
|
|
setOpen((o) => !o);
|
|
|
|
|
setErr(null);
|
|
|
|
|
}}
|
|
|
|
|
className="shrink-0 glass-card glass-hover px-3 py-1.5 rounded-xl text-sm transition"
|
|
|
|
|
>
|
|
|
|
|
{me.has_password
|
|
|
|
|
? t("settings.account.password.change")
|
|
|
|
|
: t("settings.account.password.set")}
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
{open && (
|
|
|
|
|
<form onSubmit={submit} className="space-y-2 pt-1 pb-1">
|
|
|
|
|
{me.has_password && (
|
|
|
|
|
<input
|
|
|
|
|
type="password"
|
|
|
|
|
value={current}
|
|
|
|
|
onChange={(e) => setCurrent(e.target.value)}
|
|
|
|
|
placeholder={t("settings.account.password.current")}
|
|
|
|
|
autoComplete="current-password"
|
|
|
|
|
className={inputCls}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
<input
|
|
|
|
|
type="password"
|
|
|
|
|
value={next}
|
|
|
|
|
onChange={(e) => setNext(e.target.value)}
|
|
|
|
|
placeholder={t("settings.account.password.new")}
|
|
|
|
|
autoComplete="new-password"
|
|
|
|
|
className={inputCls}
|
|
|
|
|
/>
|
|
|
|
|
{err && <p className="text-xs text-red-400">{err}</p>}
|
|
|
|
|
<button
|
|
|
|
|
type="submit"
|
|
|
|
|
disabled={busy || !next}
|
|
|
|
|
className="px-3 py-1.5 rounded-lg text-sm bg-accent text-accent-fg font-medium disabled:opacity-40 transition"
|
|
|
|
|
>
|
|
|
|
|
{busy ? t("settings.account.password.saving") : t("settings.account.password.save")}
|
|
|
|
|
</button>
|
|
|
|
|
</form>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</Section>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-09 14:42:32 +02:00
|
|
|
// Two-way Plex watch-state sync (Phase A): the owner links their account to the Plex admin account
|
|
|
|
|
// and does a one-time "Plex is master" import. Admin-only (rides the server admin token) and only
|
|
|
|
|
// shown when the Plex module is enabled. Two-way push/pull arrive in later phases.
|
|
|
|
|
function PlexWatchSync() {
|
|
|
|
|
const { t, i18n } = useTranslation();
|
|
|
|
|
const qc = useQueryClient();
|
|
|
|
|
const link = useQuery({ queryKey: ["plex-watch-link"], queryFn: () => api.plexWatchLink() });
|
|
|
|
|
const enabled = link.data?.sync_enabled ?? false;
|
|
|
|
|
|
|
|
|
|
const announce = (imp: { watched: number; in_progress: number } | null | undefined) => {
|
|
|
|
|
if (imp) {
|
|
|
|
|
notify({
|
|
|
|
|
level: "success",
|
|
|
|
|
message: t("settings.plexSync.imported", { watched: imp.watched, in_progress: imp.in_progress }),
|
|
|
|
|
});
|
|
|
|
|
// The browse grid's watch badges read plex_states — refresh them.
|
|
|
|
|
qc.invalidateQueries({ queryKey: ["plex"] });
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const toggle = useMutation({
|
|
|
|
|
mutationFn: (next: boolean) => api.plexWatchSetLink(next),
|
|
|
|
|
onSuccess: (res) => {
|
|
|
|
|
qc.setQueryData(["plex-watch-link"], res);
|
|
|
|
|
announce(res.import);
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
const reimport = useMutation({
|
|
|
|
|
mutationFn: () => api.plexWatchImport(),
|
|
|
|
|
onSuccess: (res) => {
|
|
|
|
|
qc.setQueryData(["plex-watch-link"], res);
|
|
|
|
|
announce(res.import);
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const last = link.data?.last_watch_sync_at
|
|
|
|
|
? new Date(link.data.last_watch_sync_at).toLocaleString(i18n.language)
|
|
|
|
|
: null;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Section title={t("settings.plexSync.title")}>
|
|
|
|
|
<p className="text-xs text-muted leading-relaxed mb-2">{t("settings.plexSync.intro")}</p>
|
|
|
|
|
<SettingRow label={t("settings.plexSync.toggle")} hint={t("settings.plexSync.toggleHint")}>
|
|
|
|
|
<Switch checked={enabled} onChange={(v) => toggle.mutate(v)} />
|
|
|
|
|
</SettingRow>
|
|
|
|
|
{enabled && (
|
|
|
|
|
<div className="mt-2 flex items-center justify-between gap-3">
|
|
|
|
|
<p className="text-xs text-muted">
|
|
|
|
|
{last ? t("settings.plexSync.lastSync", { when: last }) : t("settings.plexSync.never")}
|
|
|
|
|
</p>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => reimport.mutate()}
|
|
|
|
|
disabled={reimport.isPending}
|
|
|
|
|
className="inline-flex items-center gap-1.5 px-3 py-2 rounded-xl text-sm border border-border hover:bg-card/60 disabled:opacity-50 transition shrink-0"
|
|
|
|
|
>
|
|
|
|
|
<RefreshCw className={`w-4 h-4 ${reimport.isPending ? "animate-spin" : ""}`} />
|
|
|
|
|
{t("settings.plexSync.importNow")}
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</Section>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-14 01:11:29 +02:00
|
|
|
function Account({ me, onOpenWizard }: { me: Me; onOpenWizard: () => void }) {
|
feat(i18n): translate remaining components (HU/EN/DE)
Feed, VideoCard, Sidebar, PlayerModal, Channels, Stats, SettingsPanel,
OnboardingWizard, NotificationCenter, Toaster, ErrorBoundary and the relativeTime
helper are now fully translated in Hungarian, English and German, each with its own
locale area file (auto-loaded). Key parity verified across all three languages.
2026-06-15 00:47:04 +02:00
|
|
|
const { t } = useTranslation();
|
2026-06-19 15:46:49 +02:00
|
|
|
const confirm = useConfirm();
|
|
|
|
|
const onDeleteAccount = async () => {
|
|
|
|
|
const ok = await confirm({
|
|
|
|
|
title: t("settings.account.deleteTitle"),
|
|
|
|
|
message: t("settings.account.deleteConfirm"),
|
|
|
|
|
confirmLabel: t("settings.account.deleteConfirmButton"),
|
|
|
|
|
danger: true,
|
|
|
|
|
});
|
|
|
|
|
if (!ok) return;
|
|
|
|
|
try {
|
|
|
|
|
await api.deleteAccount();
|
2026-06-19 19:52:02 +02:00
|
|
|
// Session cleared server-side → /api/me 401s → Welcome. The flag shows the confirmation banner.
|
|
|
|
|
window.location.href = "/?deleted=1";
|
2026-06-19 15:46:49 +02:00
|
|
|
} catch {
|
|
|
|
|
/* the global error dialog surfaces the reason (e.g. last admin) */
|
|
|
|
|
}
|
|
|
|
|
};
|
feat(m5a): channel manager, tabbed settings panel, per-user sync status
Backend: /api/channels (list + PATCH priority/hidden + attach/detach user tags),
user-tag CRUD on /api/tags, /api/sync/my-status (per-user channel sync counts).
Frontend: feed|channels page nav (URL-synced) from the account menu; a slide-in
tabbed Settings panel (Appearance, Notifications=6b sound+duration, Sync status +
actions + admin pause/resume, Account); a channel manager with priority, hide,
per-channel user tags, sync badges and 'view in feed'. Notifications now honor the
configurable sound + auto-dismiss settings.
2026-06-11 20:45:48 +02:00
|
|
|
return (
|
2026-06-12 01:43:07 +02:00
|
|
|
<>
|
feat(i18n): translate remaining components (HU/EN/DE)
Feed, VideoCard, Sidebar, PlayerModal, Channels, Stats, SettingsPanel,
OnboardingWizard, NotificationCenter, Toaster, ErrorBoundary and the relativeTime
helper are now fully translated in Hungarian, English and German, each with its own
locale area file (auto-loaded). Key parity verified across all three languages.
2026-06-15 00:47:04 +02:00
|
|
|
<Section title={t("settings.account.title")}>
|
feat(m5a): channel manager, tabbed settings panel, per-user sync status
Backend: /api/channels (list + PATCH priority/hidden + attach/detach user tags),
user-tag CRUD on /api/tags, /api/sync/my-status (per-user channel sync counts).
Frontend: feed|channels page nav (URL-synced) from the account menu; a slide-in
tabbed Settings panel (Appearance, Notifications=6b sound+duration, Sync status +
actions + admin pause/resume, Account); a channel manager with priority, hide,
per-channel user tags, sync badges and 'view in feed'. Notifications now honor the
configurable sound + auto-dismiss settings.
2026-06-11 20:45:48 +02:00
|
|
|
<div className="flex items-center gap-3 mb-3">
|
2026-06-12 18:01:43 +02:00
|
|
|
<Avatar
|
|
|
|
|
src={me.avatar_url}
|
|
|
|
|
fallback={me.display_name ?? me.email}
|
|
|
|
|
className="w-12 h-12 rounded-full"
|
|
|
|
|
/>
|
feat(m5a): channel manager, tabbed settings panel, per-user sync status
Backend: /api/channels (list + PATCH priority/hidden + attach/detach user tags),
user-tag CRUD on /api/tags, /api/sync/my-status (per-user channel sync counts).
Frontend: feed|channels page nav (URL-synced) from the account menu; a slide-in
tabbed Settings panel (Appearance, Notifications=6b sound+duration, Sync status +
actions + admin pause/resume, Account); a channel manager with priority, hide,
per-channel user tags, sync badges and 'view in feed'. Notifications now honor the
configurable sound + auto-dismiss settings.
2026-06-11 20:45:48 +02:00
|
|
|
<div className="min-w-0">
|
|
|
|
|
<div className="font-semibold truncate">{me.display_name ?? me.email.split("@")[0]}</div>
|
|
|
|
|
<div className="text-xs text-muted truncate">{me.email}</div>
|
|
|
|
|
<div className="text-xs text-muted capitalize">{me.role}</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-06-14 01:11:29 +02:00
|
|
|
</Section>
|
|
|
|
|
|
2026-06-19 19:52:02 +02:00
|
|
|
{!me.is_demo && <SignInMethods me={me} />}
|
|
|
|
|
|
2026-07-09 14:42:32 +02:00
|
|
|
{me.role === "admin" && me.plex_enabled && <PlexWatchSync />}
|
|
|
|
|
|
2026-06-16 09:17:34 +02:00
|
|
|
{me.is_demo ? (
|
|
|
|
|
<Section title={t("settings.account.youtubeAccess")}>
|
|
|
|
|
<p className="text-xs text-muted leading-relaxed">
|
|
|
|
|
{t("settings.account.demoNotice")}
|
|
|
|
|
</p>
|
|
|
|
|
</Section>
|
2026-06-21 02:06:37 +02:00
|
|
|
) : me.google_enabled ? (
|
|
|
|
|
// YouTube access requires Google OAuth; hidden when this instance has no Google configured.
|
2026-06-16 09:17:34 +02:00
|
|
|
<Section title={t("settings.account.youtubeAccess")}>
|
|
|
|
|
<p className="text-xs text-muted leading-relaxed mb-1">
|
|
|
|
|
{t("settings.account.youtubeAccessIntro")}
|
|
|
|
|
</p>
|
|
|
|
|
<div className="divide-y divide-border">
|
|
|
|
|
<AccessRow
|
|
|
|
|
title={t("settings.account.readTitle")}
|
|
|
|
|
granted={me.can_read}
|
|
|
|
|
grantedHint={t("settings.account.readGrantedHint")}
|
|
|
|
|
enableHint={t("settings.account.readEnableHint")}
|
|
|
|
|
onEnable={() => {
|
|
|
|
|
window.location.href = "/auth/upgrade?access=read";
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
<AccessRow
|
|
|
|
|
title={t("settings.account.writeTitle")}
|
|
|
|
|
granted={me.can_write}
|
|
|
|
|
grantedHint={t("settings.account.writeGrantedHint")}
|
|
|
|
|
enableHint={t("settings.account.writeEnableHint")}
|
|
|
|
|
onEnable={() => {
|
|
|
|
|
window.location.href = "/auth/upgrade?access=write";
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<button
|
|
|
|
|
onClick={onOpenWizard}
|
|
|
|
|
className="mt-2 text-sm text-accent hover:underline"
|
|
|
|
|
>
|
|
|
|
|
{t("settings.account.walkMeThrough")}
|
|
|
|
|
</button>
|
|
|
|
|
</Section>
|
2026-06-21 02:06:37 +02:00
|
|
|
) : null}
|
2026-06-19 15:46:49 +02:00
|
|
|
|
|
|
|
|
{!me.is_demo && (
|
|
|
|
|
<Section title={t("settings.account.dangerZone")}>
|
|
|
|
|
<p className="text-xs text-muted leading-relaxed mb-2">{t("settings.account.deleteHint")}</p>
|
|
|
|
|
<button
|
|
|
|
|
onClick={onDeleteAccount}
|
|
|
|
|
className="inline-flex items-center gap-1.5 px-3 py-2 rounded-xl text-sm border border-red-500/40 text-red-400 hover:bg-red-500/10 transition"
|
|
|
|
|
>
|
|
|
|
|
<Trash2 className="w-4 h-4" />
|
|
|
|
|
{t("settings.account.deleteAccount")}
|
|
|
|
|
</button>
|
|
|
|
|
</Section>
|
|
|
|
|
)}
|
2026-06-12 01:43:07 +02:00
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
}
|