From a44ad66d466dec1a115e28501805eb677a6a2de5 Mon Sep 17 00:00:00 2001 From: Lyall Date: Thu, 31 Jul 2025 16:12:03 +0100 Subject: [PATCH] add private mode toggle to app menu --- src/i18n/locales/en.json | 2 ++ .../features/discord-rpc/use-discord-rpc.ts | 10 +++++--- .../features/player/hooks/use-scrobble.ts | 17 +++++++------ .../features/titlebar/components/app-menu.tsx | 25 +++++++++++++++++-- src/renderer/store/app.store.ts | 8 ++++++ src/shared/components/icon/icon.tsx | 2 ++ 6 files changed, 51 insertions(+), 13 deletions(-) diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index f0709093..414f277a 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -321,6 +321,8 @@ "goBack": "go back", "goForward": "go forward", "manageServers": "manage servers", + "privateModeOff": "turn private mode off", + "privateModeOn": "turn private mode on", "openBrowserDevtools": "open browser devtools", "quit": "$t(common.quit)", "selectServer": "select server", diff --git a/src/renderer/features/discord-rpc/use-discord-rpc.ts b/src/renderer/features/discord-rpc/use-discord-rpc.ts index a0586159..96ec67d7 100644 --- a/src/renderer/features/discord-rpc/use-discord-rpc.ts +++ b/src/renderer/features/discord-rpc/use-discord-rpc.ts @@ -5,6 +5,7 @@ import { useCallback, useEffect, useState } from 'react'; import { controller } from '/@/renderer/api/controller'; import { getServerById, + useAppStore, useDiscordSetttings, useGeneralSettings, usePlayerStore, @@ -17,6 +18,7 @@ const discordRpc = isElectron() ? window.api.discordRpc : null; export const useDiscordRpc = () => { const discordSettings = useDiscordSetttings(); const generalSettings = useGeneralSettings(); + const { privateMode } = useAppStore(); const [lastUniqueId, setlastUniqueId] = useState(''); const setActivity = useCallback( @@ -139,15 +141,15 @@ export const useDiscordRpc = () => { ); useEffect(() => { - if (!discordSettings.enabled) return discordRpc?.quit(); + if (!discordSettings.enabled || privateMode) return discordRpc?.quit(); return () => { discordRpc?.quit(); }; - }, [discordSettings.clientId, discordSettings.enabled]); + }, [discordSettings.clientId, privateMode, discordSettings.enabled]); useEffect(() => { - if (!discordSettings.enabled) return; + if (!discordSettings.enabled || privateMode) return; const unsubSongChange = usePlayerStore.subscribe( (state) => [state.current.song, state.current.time, state.current.status], setActivity, @@ -155,5 +157,5 @@ export const useDiscordRpc = () => { return () => { unsubSongChange(); }; - }, [discordSettings.enabled, setActivity]); + }, [discordSettings.enabled, privateMode, setActivity]); }; diff --git a/src/renderer/features/player/hooks/use-scrobble.ts b/src/renderer/features/player/hooks/use-scrobble.ts index 3df9ab01..bb52cbde 100644 --- a/src/renderer/features/player/hooks/use-scrobble.ts +++ b/src/renderer/features/player/hooks/use-scrobble.ts @@ -1,8 +1,7 @@ import { useCallback, useEffect, useRef, useState } from 'react'; import { useSendScrobble } from '/@/renderer/features/player/mutations/scrobble-mutation'; -import { usePlayerStore } from '/@/renderer/store'; -import { usePlaybackSettings } from '/@/renderer/store/settings.store'; +import { useAppStore, usePlaybackSettings, usePlayerStore } from '/@/renderer/store'; import { QueueSong, ServerType } from '/@/shared/types/domain-types'; import { PlayerStatus } from '/@/shared/types/types'; @@ -59,13 +58,14 @@ const checkScrobbleConditions = (args: { export const useScrobble = () => { const scrobbleSettings = usePlaybackSettings().scrobble; const isScrobbleEnabled = scrobbleSettings?.enabled; + const isPrivateModeEnabled = useAppStore().privateMode; const sendScrobble = useSendScrobble(); const [isCurrentSongScrobbled, setIsCurrentSongScrobbled] = useState(false); const handleScrobbleFromSeek = useCallback( (currentTime: number) => { - if (!isScrobbleEnabled) return; + if (!isScrobbleEnabled || isPrivateModeEnabled) return; const currentSong = usePlayerStore.getState().current.song; @@ -84,7 +84,7 @@ export const useScrobble = () => { serverId: currentSong?.serverId, }); }, - [isScrobbleEnabled, sendScrobble], + [isScrobbleEnabled, isPrivateModeEnabled, sendScrobble], ); const progressIntervalId = useRef>(null); @@ -119,7 +119,7 @@ export const useScrobble = () => { }, 1000); } - if (!isScrobbleEnabled) return; + if (!isScrobbleEnabled || isPrivateModeEnabled) return; if (progressIntervalId.current) { clearInterval(progressIntervalId.current); @@ -201,6 +201,7 @@ export const useScrobble = () => { scrobbleSettings?.scrobbleAtDuration, scrobbleSettings?.scrobbleAtPercentage, isScrobbleEnabled, + isPrivateModeEnabled, isCurrentSongScrobbled, sendScrobble, handleScrobbleFromSeek, @@ -209,7 +210,7 @@ export const useScrobble = () => { const handleScrobbleFromStatusChange = useCallback( (current: PlayerEvent, previous: PlayerEvent) => { - if (!isScrobbleEnabled) return; + if (!isScrobbleEnabled || isPrivateModeEnabled) return; const currentSong = usePlayerStore.getState().current.song; @@ -293,6 +294,7 @@ export const useScrobble = () => { }, [ isScrobbleEnabled, + isPrivateModeEnabled, sendScrobble, handleScrobbleFromSeek, scrobbleSettings?.scrobbleAtDuration, @@ -306,7 +308,7 @@ export const useScrobble = () => { // need to perform another check to see if the scrobble conditions are met const handleScrobbleFromSongRestart = useCallback( (currentTime: number) => { - if (!isScrobbleEnabled) return; + if (!isScrobbleEnabled || isPrivateModeEnabled) return; const currentSong = usePlayerStore.getState().current.song; @@ -349,6 +351,7 @@ export const useScrobble = () => { }, [ isScrobbleEnabled, + isPrivateModeEnabled, scrobbleSettings?.scrobbleAtDuration, scrobbleSettings?.scrobbleAtPercentage, isCurrentSongScrobbled, diff --git a/src/renderer/features/titlebar/components/app-menu.tsx b/src/renderer/features/titlebar/components/app-menu.tsx index 1d0733ae..a240d22d 100644 --- a/src/renderer/features/titlebar/components/app-menu.tsx +++ b/src/renderer/features/titlebar/components/app-menu.tsx @@ -10,6 +10,7 @@ import { ServerList } from '/@/renderer/features/servers'; import { EditServerForm } from '/@/renderer/features/servers/components/edit-server-form'; import { AppRoute } from '/@/renderer/router/routes'; import { + useAppStore, useAppStoreActions, useAuthStoreActions, useCurrentServer, @@ -30,7 +31,8 @@ export const AppMenu = () => { const serverList = useServerList(); const { setCurrentServer } = useAuthStoreActions(); const { collapsed } = useSidebarStore(); - const { setSideBar } = useAppStoreActions(); + const { privateMode } = useAppStore(); + const { setPrivateMode, setSideBar } = useAppStoreActions(); const handleSetCurrentServer = (server: ServerListItem) => { navigate(AppRoute.HOME); @@ -80,6 +82,14 @@ export const AppMenu = () => { setSideBar({ collapsed: false }); }; + const handlePrivateModeOff = () => { + setPrivateMode(false); + }; + + const handlePrivateModeOn = () => { + setPrivateMode(true); + }; + const handleQuit = () => { browser?.quit(); }; @@ -127,7 +137,18 @@ export const AppMenu = () => { > {t('page.appMenu.manageServers', { postProcess: 'sentenceCase' })} - + {privateMode ? ( + } + onClick={handlePrivateModeOff} + > + {t('page.appMenu.privateModeOff', { postProcess: 'sentenceCase' })} + + ) : ( + } onClick={handlePrivateModeOn}> + {t('page.appMenu.privateModeOn', { postProcess: 'sentenceCase' })} + + )} {t('page.appMenu.selectServer', { postProcess: 'sentenceCase' })} diff --git a/src/renderer/store/app.store.ts b/src/renderer/store/app.store.ts index 83074f5d..68f5bfd3 100644 --- a/src/renderer/store/app.store.ts +++ b/src/renderer/store/app.store.ts @@ -8,6 +8,7 @@ import { Platform } from '/@/shared/types/types'; export interface AppSlice extends AppState { actions: { setAppStore: (data: Partial) => void; + setPrivateMode: (enabled: boolean) => void; setSideBar: (options: Partial) => void; setTitleBar: (options: Partial) => void; }; @@ -17,6 +18,7 @@ export interface AppState { commandPalette: CommandPaletteProps; isReorderingQueue: boolean; platform: Platform; + privateMode: boolean; sidebar: SidebarProps; titlebar: TitlebarProps; } @@ -50,6 +52,11 @@ export const useAppStore = createWithEqualityFn()( setAppStore: (data) => { set({ ...get(), ...data }); }, + setPrivateMode: (privateMode) => { + set((state) => { + state.privateMode = privateMode; + }); + }, setSideBar: (options) => { set((state) => { state.sidebar = { ...state.sidebar, ...options }; @@ -81,6 +88,7 @@ export const useAppStore = createWithEqualityFn()( }, isReorderingQueue: false, platform: Platform.WINDOWS, + privateMode: false, sidebar: { collapsed: false, expanded: [], diff --git a/src/shared/components/icon/icon.tsx b/src/shared/components/icon/icon.tsx index d77b5552..fc4e3174 100644 --- a/src/shared/components/icon/icon.tsx +++ b/src/shared/components/icon/icon.tsx @@ -59,6 +59,7 @@ import { LuListPlus, LuLoader, LuLock, + LuLockOpen, LuLogIn, LuLogOut, LuMenu, @@ -163,6 +164,7 @@ export const AppIcon = { listInfinite: LuInfinity, listPaginated: LuArrowRightToLine, lock: LuLock, + lockOpen: LuLockOpen, mediaNext: LuSkipForward, mediaPause: LuPause, mediaPlay: LuPlay,