diff --git a/src/assets/locales/en.json b/src/assets/locales/en.json index c134d2fe..a0f93d16 100644 --- a/src/assets/locales/en.json +++ b/src/assets/locales/en.json @@ -1104,6 +1104,9 @@ "carouselView": "Carousel view", "carouselViewDescription": "Display your currently watching and bookmark sections as carousels instead of a grid. Disabled by default.", "carouselViewLabel": "Carousel view", + "minimalCards": "Minimal cards", + "minimalCardsDescription": "Hide text content (title, type, year) on media cards, showing only the poster image.", + "minimalCardsLabel": "Minimal cards", "forceCompactEpisodeView": "Force compact episode view", "forceCompactEpisodeViewDescription": "Force the episode carousel in the player to use the \"classic\" compact vertical view. Disabled by default.", "homeSectionOrder": "Home section order", diff --git a/src/backend/accounts/settings.ts b/src/backend/accounts/settings.ts index 53fe9120..bac9b02c 100644 --- a/src/backend/accounts/settings.ts +++ b/src/backend/accounts/settings.ts @@ -20,6 +20,7 @@ export interface SettingsInput { enableDetailsModal?: boolean; enableImageLogos?: boolean; enableCarouselView?: boolean; + enableMinimalCards?: boolean; forceCompactEpisodeView?: boolean; sourceOrder?: string[] | null; enableSourceOrder?: boolean; @@ -56,6 +57,7 @@ export interface SettingsResponse { enableDetailsModal?: boolean; enableImageLogos?: boolean; enableCarouselView?: boolean; + enableMinimalCards?: boolean; forceCompactEpisodeView?: boolean; sourceOrder?: string[] | null; enableSourceOrder?: boolean; diff --git a/src/components/media/MediaCard.tsx b/src/components/media/MediaCard.tsx index 4fd28646..bc426739 100644 --- a/src/components/media/MediaCard.tsx +++ b/src/components/media/MediaCard.tsx @@ -136,6 +136,7 @@ function MediaCardContent({ const dotListContent = [t(`media.types.${media.type}`)]; const [searchQuery] = useSearchQuery(); + const enableMinimalCards = usePreferencesStore((s) => s.enableMinimalCards); // Simple intersection observer for lazy loading images const { targetRef, isIntersecting } = useIntersectionObserver({ @@ -185,10 +186,11 @@ function MediaCardContent({ >
-

- {media.title} -

-
- -
+ {!enableMinimalCards && ( + <> +

+ {media.title} +

+
+ +
- {!closable && ( -
- -
- )} - {editable && closable && ( -
- -
+ {!closable && ( +
+ +
+ )} + {editable && closable && ( +
+ +
+ )} + )} diff --git a/src/hooks/auth/useAuthData.ts b/src/hooks/auth/useAuthData.ts index 0de5e210..b8325f83 100644 --- a/src/hooks/auth/useAuthData.ts +++ b/src/hooks/auth/useAuthData.ts @@ -94,6 +94,9 @@ export function useAuthData() { const setKeyboardShortcuts = usePreferencesStore( (s) => s.setKeyboardShortcuts, ); + const setEnableMinimalCards = usePreferencesStore( + (s) => s.setEnableMinimalCards, + ); const login = useCallback( async ( @@ -282,6 +285,10 @@ export function useAuthData() { if (settings.keyboardShortcuts !== undefined) { setKeyboardShortcuts(settings.keyboardShortcuts); } + + if (settings.enableMinimalCards !== undefined) { + setEnableMinimalCards(settings.enableMinimalCards); + } }, [ replaceBookmarks, @@ -319,6 +326,7 @@ export function useAuthData() { setEnableDoubleClickToSeek, setEnableAutoResumeOnPlaybackError, setKeyboardShortcuts, + setEnableMinimalCards, ], ); diff --git a/src/hooks/useSettingsState.ts b/src/hooks/useSettingsState.ts index b9e7dd37..40b3ea7b 100644 --- a/src/hooks/useSettingsState.ts +++ b/src/hooks/useSettingsState.ts @@ -72,6 +72,7 @@ export function useSettingsState( enableSkipCredits: boolean, enableImageLogos: boolean, enableCarouselView: boolean, + enableMinimalCards: boolean, forceCompactEpisodeView: boolean, enableLowPerformanceMode: boolean, enableNativeSubtitles: boolean, @@ -221,6 +222,12 @@ export function useSettingsState( resetEnableCarouselView, enableCarouselViewChanged, ] = useDerived(enableCarouselView); + const [ + enableMinimalCardsState, + setEnableMinimalCardsState, + resetEnableMinimalCards, + enableMinimalCardsChanged, + ] = useDerived(enableMinimalCards); const [ forceCompactEpisodeViewState, setForceCompactEpisodeViewState, @@ -299,6 +306,7 @@ export function useSettingsState( resetDisabledEmbeds(); resetProxyTmdb(); resetEnableCarouselView(); + resetEnableMinimalCards(); resetForceCompactEpisodeView(); resetEnableLowPerformanceMode(); resetEnableNativeSubtitles(); @@ -338,6 +346,7 @@ export function useSettingsState( disabledEmbedsChanged || proxyTmdbChanged || enableCarouselViewChanged || + enableMinimalCardsChanged || forceCompactEpisodeViewChanged || enableLowPerformanceModeChanged || enableNativeSubtitlesChanged || @@ -490,6 +499,11 @@ export function useSettingsState( set: setEnableCarouselViewState, changed: enableCarouselViewChanged, }, + enableMinimalCards: { + state: enableMinimalCardsState, + set: setEnableMinimalCardsState, + changed: enableMinimalCardsChanged, + }, forceCompactEpisodeView: { state: forceCompactEpisodeViewState, set: setForceCompactEpisodeViewState, diff --git a/src/pages/Settings.tsx b/src/pages/Settings.tsx index 4b3b97e0..96d24cf0 100644 --- a/src/pages/Settings.tsx +++ b/src/pages/Settings.tsx @@ -448,6 +448,11 @@ export function SettingsPage() { (s) => s.setEnableCarouselView, ); + const enableMinimalCards = usePreferencesStore((s) => s.enableMinimalCards); + const setEnableMinimalCards = usePreferencesStore( + (s) => s.setEnableMinimalCards, + ); + const forceCompactEpisodeView = usePreferencesStore( (s) => s.forceCompactEpisodeView, ); @@ -563,6 +568,7 @@ export function SettingsPage() { enableSkipCredits, enableImageLogos, enableCarouselView, + enableMinimalCards, forceCompactEpisodeView, enableLowPerformanceMode, enableNativeSubtitles, @@ -631,6 +637,7 @@ export function SettingsPage() { state.disabledSources.changed || state.proxyTmdb.changed || state.enableCarouselView.changed || + state.enableMinimalCards.changed || state.forceCompactEpisodeView.changed || state.enableLowPerformanceMode.changed || state.enableHoldToBoost.changed || @@ -660,6 +667,7 @@ export function SettingsPage() { disabledSources: state.disabledSources.state, proxyTmdb: state.proxyTmdb.state, enableCarouselView: state.enableCarouselView.state, + enableMinimalCards: state.enableMinimalCards.state, forceCompactEpisodeView: state.forceCompactEpisodeView.state, enableLowPerformanceMode: state.enableLowPerformanceMode.state, enableHoldToBoost: state.enableHoldToBoost.state, @@ -716,6 +724,7 @@ export function SettingsPage() { setdebridService(state.debridService.state); setProxyTmdb(state.proxyTmdb.state); setEnableCarouselView(state.enableCarouselView.state); + setEnableMinimalCards(state.enableMinimalCards.state); setForceCompactEpisodeView(state.forceCompactEpisodeView.state); setEnableLowPerformanceMode(state.enableLowPerformanceMode.state); setEnableHoldToBoost(state.enableHoldToBoost.state); @@ -771,6 +780,7 @@ export function SettingsPage() { setBackendUrl, setProxyTmdb, setEnableCarouselView, + setEnableMinimalCards, setForceCompactEpisodeView, setEnableLowPerformanceMode, setEnableHoldToBoost, @@ -886,6 +896,8 @@ export function SettingsPage() { setEnableImageLogos={state.enableImageLogos.set} enableCarouselView={state.enableCarouselView.state} setEnableCarouselView={state.enableCarouselView.set} + enableMinimalCards={state.enableMinimalCards.state} + setEnableMinimalCards={state.enableMinimalCards.set} forceCompactEpisodeView={state.forceCompactEpisodeView.state} setForceCompactEpisodeView={state.forceCompactEpisodeView.set} homeSectionOrder={state.homeSectionOrder.state} diff --git a/src/pages/parts/settings/AppearancePart.tsx b/src/pages/parts/settings/AppearancePart.tsx index bae45075..fb72bc28 100644 --- a/src/pages/parts/settings/AppearancePart.tsx +++ b/src/pages/parts/settings/AppearancePart.tsx @@ -245,6 +245,9 @@ export function AppearancePart(props: { enableCarouselView: boolean; setEnableCarouselView: (v: boolean) => void; + enableMinimalCards: boolean; + setEnableMinimalCards: (v: boolean) => void; + forceCompactEpisodeView: boolean; setForceCompactEpisodeView: (v: boolean) => void; @@ -510,6 +513,30 @@ export function AppearancePart(props: { + {/* Minimal Cards */} +
+

+ {t("settings.appearance.options.minimalCards")} +

+

+ {t("settings.appearance.options.minimalCardsDescription")} +

+
+ props.setEnableMinimalCards(!props.enableMinimalCards) + } + className={classNames( + "bg-dropdown-background hover:bg-dropdown-hoverBackground select-none my-4 cursor-pointer space-x-3 flex items-center max-w-[25rem] py-3 px-4 rounded-lg", + "cursor-pointer opacity-100 pointer-events-auto", + )} + > + +

+ {t("settings.appearance.options.minimalCardsLabel")} +

+
+
+ {/* Force Compact Episode View */}

diff --git a/src/stores/preferences/index.tsx b/src/stores/preferences/index.tsx index 9a16bd35..7bc02648 100644 --- a/src/stores/preferences/index.tsx +++ b/src/stores/preferences/index.tsx @@ -16,6 +16,7 @@ export interface PreferencesStore { enableDetailsModal: boolean; enableImageLogos: boolean; enableCarouselView: boolean; + enableMinimalCards: boolean; forceCompactEpisodeView: boolean; sourceOrder: string[]; enableSourceOrder: boolean; @@ -46,6 +47,7 @@ export interface PreferencesStore { setEnableDetailsModal(v: boolean): void; setEnableImageLogos(v: boolean): void; setEnableCarouselView(v: boolean): void; + setEnableMinimalCards(v: boolean): void; setForceCompactEpisodeView(v: boolean): void; setSourceOrder(v: string[]): void; setEnableSourceOrder(v: boolean): void; @@ -80,6 +82,7 @@ export const usePreferencesStore = create( enableDetailsModal: false, enableImageLogos: true, enableCarouselView: false, + enableMinimalCards: false, forceCompactEpisodeView: false, sourceOrder: [], enableSourceOrder: false, @@ -141,6 +144,11 @@ export const usePreferencesStore = create( s.enableCarouselView = v; }); }, + setEnableMinimalCards(v) { + set((s) => { + s.enableMinimalCards = v; + }); + }, setForceCompactEpisodeView(v) { set((s) => { s.forceCompactEpisodeView = v;