mirror of
https://github.com/p-stream/p-stream.git
synced 2026-03-11 17:55:33 +00:00
add minimal cards setting
This commit is contained in:
parent
41947f8da8
commit
342219b461
8 changed files with 122 additions and 42 deletions
|
|
@ -1104,6 +1104,9 @@
|
||||||
"carouselView": "Carousel view",
|
"carouselView": "Carousel view",
|
||||||
"carouselViewDescription": "Display your currently watching and bookmark sections as carousels instead of a grid. Disabled by default.",
|
"carouselViewDescription": "Display your currently watching and bookmark sections as carousels instead of a grid. Disabled by default.",
|
||||||
"carouselViewLabel": "Carousel view",
|
"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",
|
"forceCompactEpisodeView": "Force compact episode view",
|
||||||
"forceCompactEpisodeViewDescription": "Force the episode carousel in the player to use the \"classic\" compact vertical view. Disabled by default.",
|
"forceCompactEpisodeViewDescription": "Force the episode carousel in the player to use the \"classic\" compact vertical view. Disabled by default.",
|
||||||
"homeSectionOrder": "Home section order",
|
"homeSectionOrder": "Home section order",
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ export interface SettingsInput {
|
||||||
enableDetailsModal?: boolean;
|
enableDetailsModal?: boolean;
|
||||||
enableImageLogos?: boolean;
|
enableImageLogos?: boolean;
|
||||||
enableCarouselView?: boolean;
|
enableCarouselView?: boolean;
|
||||||
|
enableMinimalCards?: boolean;
|
||||||
forceCompactEpisodeView?: boolean;
|
forceCompactEpisodeView?: boolean;
|
||||||
sourceOrder?: string[] | null;
|
sourceOrder?: string[] | null;
|
||||||
enableSourceOrder?: boolean;
|
enableSourceOrder?: boolean;
|
||||||
|
|
@ -56,6 +57,7 @@ export interface SettingsResponse {
|
||||||
enableDetailsModal?: boolean;
|
enableDetailsModal?: boolean;
|
||||||
enableImageLogos?: boolean;
|
enableImageLogos?: boolean;
|
||||||
enableCarouselView?: boolean;
|
enableCarouselView?: boolean;
|
||||||
|
enableMinimalCards?: boolean;
|
||||||
forceCompactEpisodeView?: boolean;
|
forceCompactEpisodeView?: boolean;
|
||||||
sourceOrder?: string[] | null;
|
sourceOrder?: string[] | null;
|
||||||
enableSourceOrder?: boolean;
|
enableSourceOrder?: boolean;
|
||||||
|
|
|
||||||
|
|
@ -136,6 +136,7 @@ function MediaCardContent({
|
||||||
const dotListContent = [t(`media.types.${media.type}`)];
|
const dotListContent = [t(`media.types.${media.type}`)];
|
||||||
|
|
||||||
const [searchQuery] = useSearchQuery();
|
const [searchQuery] = useSearchQuery();
|
||||||
|
const enableMinimalCards = usePreferencesStore((s) => s.enableMinimalCards);
|
||||||
|
|
||||||
// Simple intersection observer for lazy loading images
|
// Simple intersection observer for lazy loading images
|
||||||
const { targetRef, isIntersecting } = useIntersectionObserver({
|
const { targetRef, isIntersecting } = useIntersectionObserver({
|
||||||
|
|
@ -185,10 +186,11 @@ function MediaCardContent({
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
"relative mb-4 pb-[150%] w-full overflow-hidden rounded-xl bg-mediaCard-hoverBackground bg-cover bg-center transition-[border-radius] duration-300",
|
"relative pb-[150%] w-full overflow-hidden rounded-xl bg-mediaCard-hoverBackground bg-cover bg-center transition-[border-radius] duration-300",
|
||||||
{
|
{
|
||||||
"group-hover:rounded-lg": canLink,
|
"group-hover:rounded-lg": canLink,
|
||||||
},
|
},
|
||||||
|
enableMinimalCards ? "" : "mb-4",
|
||||||
)}
|
)}
|
||||||
style={{
|
style={{
|
||||||
backgroundImage: isIntersecting
|
backgroundImage: isIntersecting
|
||||||
|
|
@ -272,48 +274,52 @@ function MediaCardContent({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 className="mb-1 line-clamp-3 max-h-[4.5rem] text-ellipsis break-words font-bold text-white">
|
{!enableMinimalCards && (
|
||||||
<span>{media.title}</span>
|
<>
|
||||||
</h1>
|
<h1 className="mb-1 line-clamp-3 max-h-[4.5rem] text-ellipsis break-words font-bold text-white">
|
||||||
<div className="media-info-container justify-content-center flex flex-wrap">
|
<span>{media.title}</span>
|
||||||
<DotList className="text-xs" content={dotListContent} />
|
</h1>
|
||||||
</div>
|
<div className="media-info-container justify-content-center flex flex-wrap">
|
||||||
|
<DotList className="text-xs" content={dotListContent} />
|
||||||
|
</div>
|
||||||
|
|
||||||
{!closable && (
|
{!closable && (
|
||||||
<div className="absolute bottom-0 translate-y-1 right-1">
|
<div className="absolute bottom-0 translate-y-1 right-1">
|
||||||
<button
|
<button
|
||||||
className="media-more-button p-2"
|
className="media-more-button p-2"
|
||||||
type="button"
|
type="button"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
onShowDetails?.(media);
|
onShowDetails?.(media);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
className="text-xs font-semibold text-type-secondary"
|
className="text-xs font-semibold text-type-secondary"
|
||||||
icon={Icons.ELLIPSIS}
|
icon={Icons.ELLIPSIS}
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{editable && closable && (
|
{editable && closable && (
|
||||||
<div className="absolute bottom-0 translate-y-1 right-1">
|
<div className="absolute bottom-0 translate-y-1 right-1">
|
||||||
<button
|
<button
|
||||||
className="media-more-button p-2"
|
className="media-more-button p-2"
|
||||||
type="button"
|
type="button"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
onEdit?.();
|
onEdit?.();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
className="text-xs font-semibold text-type-secondary"
|
className="text-xs font-semibold text-type-secondary"
|
||||||
icon={Icons.EDIT}
|
icon={Icons.EDIT}
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</Flare.Child>
|
</Flare.Child>
|
||||||
</Flare.Base>
|
</Flare.Base>
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,9 @@ export function useAuthData() {
|
||||||
const setKeyboardShortcuts = usePreferencesStore(
|
const setKeyboardShortcuts = usePreferencesStore(
|
||||||
(s) => s.setKeyboardShortcuts,
|
(s) => s.setKeyboardShortcuts,
|
||||||
);
|
);
|
||||||
|
const setEnableMinimalCards = usePreferencesStore(
|
||||||
|
(s) => s.setEnableMinimalCards,
|
||||||
|
);
|
||||||
|
|
||||||
const login = useCallback(
|
const login = useCallback(
|
||||||
async (
|
async (
|
||||||
|
|
@ -282,6 +285,10 @@ export function useAuthData() {
|
||||||
if (settings.keyboardShortcuts !== undefined) {
|
if (settings.keyboardShortcuts !== undefined) {
|
||||||
setKeyboardShortcuts(settings.keyboardShortcuts);
|
setKeyboardShortcuts(settings.keyboardShortcuts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (settings.enableMinimalCards !== undefined) {
|
||||||
|
setEnableMinimalCards(settings.enableMinimalCards);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
replaceBookmarks,
|
replaceBookmarks,
|
||||||
|
|
@ -319,6 +326,7 @@ export function useAuthData() {
|
||||||
setEnableDoubleClickToSeek,
|
setEnableDoubleClickToSeek,
|
||||||
setEnableAutoResumeOnPlaybackError,
|
setEnableAutoResumeOnPlaybackError,
|
||||||
setKeyboardShortcuts,
|
setKeyboardShortcuts,
|
||||||
|
setEnableMinimalCards,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,7 @@ export function useSettingsState(
|
||||||
enableSkipCredits: boolean,
|
enableSkipCredits: boolean,
|
||||||
enableImageLogos: boolean,
|
enableImageLogos: boolean,
|
||||||
enableCarouselView: boolean,
|
enableCarouselView: boolean,
|
||||||
|
enableMinimalCards: boolean,
|
||||||
forceCompactEpisodeView: boolean,
|
forceCompactEpisodeView: boolean,
|
||||||
enableLowPerformanceMode: boolean,
|
enableLowPerformanceMode: boolean,
|
||||||
enableNativeSubtitles: boolean,
|
enableNativeSubtitles: boolean,
|
||||||
|
|
@ -221,6 +222,12 @@ export function useSettingsState(
|
||||||
resetEnableCarouselView,
|
resetEnableCarouselView,
|
||||||
enableCarouselViewChanged,
|
enableCarouselViewChanged,
|
||||||
] = useDerived(enableCarouselView);
|
] = useDerived(enableCarouselView);
|
||||||
|
const [
|
||||||
|
enableMinimalCardsState,
|
||||||
|
setEnableMinimalCardsState,
|
||||||
|
resetEnableMinimalCards,
|
||||||
|
enableMinimalCardsChanged,
|
||||||
|
] = useDerived(enableMinimalCards);
|
||||||
const [
|
const [
|
||||||
forceCompactEpisodeViewState,
|
forceCompactEpisodeViewState,
|
||||||
setForceCompactEpisodeViewState,
|
setForceCompactEpisodeViewState,
|
||||||
|
|
@ -299,6 +306,7 @@ export function useSettingsState(
|
||||||
resetDisabledEmbeds();
|
resetDisabledEmbeds();
|
||||||
resetProxyTmdb();
|
resetProxyTmdb();
|
||||||
resetEnableCarouselView();
|
resetEnableCarouselView();
|
||||||
|
resetEnableMinimalCards();
|
||||||
resetForceCompactEpisodeView();
|
resetForceCompactEpisodeView();
|
||||||
resetEnableLowPerformanceMode();
|
resetEnableLowPerformanceMode();
|
||||||
resetEnableNativeSubtitles();
|
resetEnableNativeSubtitles();
|
||||||
|
|
@ -338,6 +346,7 @@ export function useSettingsState(
|
||||||
disabledEmbedsChanged ||
|
disabledEmbedsChanged ||
|
||||||
proxyTmdbChanged ||
|
proxyTmdbChanged ||
|
||||||
enableCarouselViewChanged ||
|
enableCarouselViewChanged ||
|
||||||
|
enableMinimalCardsChanged ||
|
||||||
forceCompactEpisodeViewChanged ||
|
forceCompactEpisodeViewChanged ||
|
||||||
enableLowPerformanceModeChanged ||
|
enableLowPerformanceModeChanged ||
|
||||||
enableNativeSubtitlesChanged ||
|
enableNativeSubtitlesChanged ||
|
||||||
|
|
@ -490,6 +499,11 @@ export function useSettingsState(
|
||||||
set: setEnableCarouselViewState,
|
set: setEnableCarouselViewState,
|
||||||
changed: enableCarouselViewChanged,
|
changed: enableCarouselViewChanged,
|
||||||
},
|
},
|
||||||
|
enableMinimalCards: {
|
||||||
|
state: enableMinimalCardsState,
|
||||||
|
set: setEnableMinimalCardsState,
|
||||||
|
changed: enableMinimalCardsChanged,
|
||||||
|
},
|
||||||
forceCompactEpisodeView: {
|
forceCompactEpisodeView: {
|
||||||
state: forceCompactEpisodeViewState,
|
state: forceCompactEpisodeViewState,
|
||||||
set: setForceCompactEpisodeViewState,
|
set: setForceCompactEpisodeViewState,
|
||||||
|
|
|
||||||
|
|
@ -448,6 +448,11 @@ export function SettingsPage() {
|
||||||
(s) => s.setEnableCarouselView,
|
(s) => s.setEnableCarouselView,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const enableMinimalCards = usePreferencesStore((s) => s.enableMinimalCards);
|
||||||
|
const setEnableMinimalCards = usePreferencesStore(
|
||||||
|
(s) => s.setEnableMinimalCards,
|
||||||
|
);
|
||||||
|
|
||||||
const forceCompactEpisodeView = usePreferencesStore(
|
const forceCompactEpisodeView = usePreferencesStore(
|
||||||
(s) => s.forceCompactEpisodeView,
|
(s) => s.forceCompactEpisodeView,
|
||||||
);
|
);
|
||||||
|
|
@ -563,6 +568,7 @@ export function SettingsPage() {
|
||||||
enableSkipCredits,
|
enableSkipCredits,
|
||||||
enableImageLogos,
|
enableImageLogos,
|
||||||
enableCarouselView,
|
enableCarouselView,
|
||||||
|
enableMinimalCards,
|
||||||
forceCompactEpisodeView,
|
forceCompactEpisodeView,
|
||||||
enableLowPerformanceMode,
|
enableLowPerformanceMode,
|
||||||
enableNativeSubtitles,
|
enableNativeSubtitles,
|
||||||
|
|
@ -631,6 +637,7 @@ export function SettingsPage() {
|
||||||
state.disabledSources.changed ||
|
state.disabledSources.changed ||
|
||||||
state.proxyTmdb.changed ||
|
state.proxyTmdb.changed ||
|
||||||
state.enableCarouselView.changed ||
|
state.enableCarouselView.changed ||
|
||||||
|
state.enableMinimalCards.changed ||
|
||||||
state.forceCompactEpisodeView.changed ||
|
state.forceCompactEpisodeView.changed ||
|
||||||
state.enableLowPerformanceMode.changed ||
|
state.enableLowPerformanceMode.changed ||
|
||||||
state.enableHoldToBoost.changed ||
|
state.enableHoldToBoost.changed ||
|
||||||
|
|
@ -660,6 +667,7 @@ export function SettingsPage() {
|
||||||
disabledSources: state.disabledSources.state,
|
disabledSources: state.disabledSources.state,
|
||||||
proxyTmdb: state.proxyTmdb.state,
|
proxyTmdb: state.proxyTmdb.state,
|
||||||
enableCarouselView: state.enableCarouselView.state,
|
enableCarouselView: state.enableCarouselView.state,
|
||||||
|
enableMinimalCards: state.enableMinimalCards.state,
|
||||||
forceCompactEpisodeView: state.forceCompactEpisodeView.state,
|
forceCompactEpisodeView: state.forceCompactEpisodeView.state,
|
||||||
enableLowPerformanceMode: state.enableLowPerformanceMode.state,
|
enableLowPerformanceMode: state.enableLowPerformanceMode.state,
|
||||||
enableHoldToBoost: state.enableHoldToBoost.state,
|
enableHoldToBoost: state.enableHoldToBoost.state,
|
||||||
|
|
@ -716,6 +724,7 @@ export function SettingsPage() {
|
||||||
setdebridService(state.debridService.state);
|
setdebridService(state.debridService.state);
|
||||||
setProxyTmdb(state.proxyTmdb.state);
|
setProxyTmdb(state.proxyTmdb.state);
|
||||||
setEnableCarouselView(state.enableCarouselView.state);
|
setEnableCarouselView(state.enableCarouselView.state);
|
||||||
|
setEnableMinimalCards(state.enableMinimalCards.state);
|
||||||
setForceCompactEpisodeView(state.forceCompactEpisodeView.state);
|
setForceCompactEpisodeView(state.forceCompactEpisodeView.state);
|
||||||
setEnableLowPerformanceMode(state.enableLowPerformanceMode.state);
|
setEnableLowPerformanceMode(state.enableLowPerformanceMode.state);
|
||||||
setEnableHoldToBoost(state.enableHoldToBoost.state);
|
setEnableHoldToBoost(state.enableHoldToBoost.state);
|
||||||
|
|
@ -771,6 +780,7 @@ export function SettingsPage() {
|
||||||
setBackendUrl,
|
setBackendUrl,
|
||||||
setProxyTmdb,
|
setProxyTmdb,
|
||||||
setEnableCarouselView,
|
setEnableCarouselView,
|
||||||
|
setEnableMinimalCards,
|
||||||
setForceCompactEpisodeView,
|
setForceCompactEpisodeView,
|
||||||
setEnableLowPerformanceMode,
|
setEnableLowPerformanceMode,
|
||||||
setEnableHoldToBoost,
|
setEnableHoldToBoost,
|
||||||
|
|
@ -886,6 +896,8 @@ export function SettingsPage() {
|
||||||
setEnableImageLogos={state.enableImageLogos.set}
|
setEnableImageLogos={state.enableImageLogos.set}
|
||||||
enableCarouselView={state.enableCarouselView.state}
|
enableCarouselView={state.enableCarouselView.state}
|
||||||
setEnableCarouselView={state.enableCarouselView.set}
|
setEnableCarouselView={state.enableCarouselView.set}
|
||||||
|
enableMinimalCards={state.enableMinimalCards.state}
|
||||||
|
setEnableMinimalCards={state.enableMinimalCards.set}
|
||||||
forceCompactEpisodeView={state.forceCompactEpisodeView.state}
|
forceCompactEpisodeView={state.forceCompactEpisodeView.state}
|
||||||
setForceCompactEpisodeView={state.forceCompactEpisodeView.set}
|
setForceCompactEpisodeView={state.forceCompactEpisodeView.set}
|
||||||
homeSectionOrder={state.homeSectionOrder.state}
|
homeSectionOrder={state.homeSectionOrder.state}
|
||||||
|
|
|
||||||
|
|
@ -245,6 +245,9 @@ export function AppearancePart(props: {
|
||||||
enableCarouselView: boolean;
|
enableCarouselView: boolean;
|
||||||
setEnableCarouselView: (v: boolean) => void;
|
setEnableCarouselView: (v: boolean) => void;
|
||||||
|
|
||||||
|
enableMinimalCards: boolean;
|
||||||
|
setEnableMinimalCards: (v: boolean) => void;
|
||||||
|
|
||||||
forceCompactEpisodeView: boolean;
|
forceCompactEpisodeView: boolean;
|
||||||
setForceCompactEpisodeView: (v: boolean) => void;
|
setForceCompactEpisodeView: (v: boolean) => void;
|
||||||
|
|
||||||
|
|
@ -510,6 +513,30 @@ export function AppearancePart(props: {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Minimal Cards */}
|
||||||
|
<div>
|
||||||
|
<p className="text-white font-bold mb-3">
|
||||||
|
{t("settings.appearance.options.minimalCards")}
|
||||||
|
</p>
|
||||||
|
<p className="max-w-[25rem] font-medium">
|
||||||
|
{t("settings.appearance.options.minimalCardsDescription")}
|
||||||
|
</p>
|
||||||
|
<div
|
||||||
|
onClick={() =>
|
||||||
|
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",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Toggle enabled={props.enableMinimalCards} />
|
||||||
|
<p className="flex-1 text-white font-bold">
|
||||||
|
{t("settings.appearance.options.minimalCardsLabel")}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Force Compact Episode View */}
|
{/* Force Compact Episode View */}
|
||||||
<div>
|
<div>
|
||||||
<p className="text-white font-bold mb-3">
|
<p className="text-white font-bold mb-3">
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ export interface PreferencesStore {
|
||||||
enableDetailsModal: boolean;
|
enableDetailsModal: boolean;
|
||||||
enableImageLogos: boolean;
|
enableImageLogos: boolean;
|
||||||
enableCarouselView: boolean;
|
enableCarouselView: boolean;
|
||||||
|
enableMinimalCards: boolean;
|
||||||
forceCompactEpisodeView: boolean;
|
forceCompactEpisodeView: boolean;
|
||||||
sourceOrder: string[];
|
sourceOrder: string[];
|
||||||
enableSourceOrder: boolean;
|
enableSourceOrder: boolean;
|
||||||
|
|
@ -46,6 +47,7 @@ export interface PreferencesStore {
|
||||||
setEnableDetailsModal(v: boolean): void;
|
setEnableDetailsModal(v: boolean): void;
|
||||||
setEnableImageLogos(v: boolean): void;
|
setEnableImageLogos(v: boolean): void;
|
||||||
setEnableCarouselView(v: boolean): void;
|
setEnableCarouselView(v: boolean): void;
|
||||||
|
setEnableMinimalCards(v: boolean): void;
|
||||||
setForceCompactEpisodeView(v: boolean): void;
|
setForceCompactEpisodeView(v: boolean): void;
|
||||||
setSourceOrder(v: string[]): void;
|
setSourceOrder(v: string[]): void;
|
||||||
setEnableSourceOrder(v: boolean): void;
|
setEnableSourceOrder(v: boolean): void;
|
||||||
|
|
@ -80,6 +82,7 @@ export const usePreferencesStore = create(
|
||||||
enableDetailsModal: false,
|
enableDetailsModal: false,
|
||||||
enableImageLogos: true,
|
enableImageLogos: true,
|
||||||
enableCarouselView: false,
|
enableCarouselView: false,
|
||||||
|
enableMinimalCards: false,
|
||||||
forceCompactEpisodeView: false,
|
forceCompactEpisodeView: false,
|
||||||
sourceOrder: [],
|
sourceOrder: [],
|
||||||
enableSourceOrder: false,
|
enableSourceOrder: false,
|
||||||
|
|
@ -141,6 +144,11 @@ export const usePreferencesStore = create(
|
||||||
s.enableCarouselView = v;
|
s.enableCarouselView = v;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
setEnableMinimalCards(v) {
|
||||||
|
set((s) => {
|
||||||
|
s.enableMinimalCards = v;
|
||||||
|
});
|
||||||
|
},
|
||||||
setForceCompactEpisodeView(v) {
|
setForceCompactEpisodeView(v) {
|
||||||
set((s) => {
|
set((s) => {
|
||||||
s.forceCompactEpisodeView = v;
|
s.forceCompactEpisodeView = v;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue