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",
|
||||
"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",
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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({
|
|||
>
|
||||
<div
|
||||
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,
|
||||
},
|
||||
enableMinimalCards ? "" : "mb-4",
|
||||
)}
|
||||
style={{
|
||||
backgroundImage: isIntersecting
|
||||
|
|
@ -272,48 +274,52 @@ function MediaCardContent({
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<h1 className="mb-1 line-clamp-3 max-h-[4.5rem] text-ellipsis break-words font-bold text-white">
|
||||
<span>{media.title}</span>
|
||||
</h1>
|
||||
<div className="media-info-container justify-content-center flex flex-wrap">
|
||||
<DotList className="text-xs" content={dotListContent} />
|
||||
</div>
|
||||
{!enableMinimalCards && (
|
||||
<>
|
||||
<h1 className="mb-1 line-clamp-3 max-h-[4.5rem] text-ellipsis break-words font-bold text-white">
|
||||
<span>{media.title}</span>
|
||||
</h1>
|
||||
<div className="media-info-container justify-content-center flex flex-wrap">
|
||||
<DotList className="text-xs" content={dotListContent} />
|
||||
</div>
|
||||
|
||||
{!closable && (
|
||||
<div className="absolute bottom-0 translate-y-1 right-1">
|
||||
<button
|
||||
className="media-more-button p-2"
|
||||
type="button"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
onShowDetails?.(media);
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
className="text-xs font-semibold text-type-secondary"
|
||||
icon={Icons.ELLIPSIS}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{editable && closable && (
|
||||
<div className="absolute bottom-0 translate-y-1 right-1">
|
||||
<button
|
||||
className="media-more-button p-2"
|
||||
type="button"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
onEdit?.();
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
className="text-xs font-semibold text-type-secondary"
|
||||
icon={Icons.EDIT}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
{!closable && (
|
||||
<div className="absolute bottom-0 translate-y-1 right-1">
|
||||
<button
|
||||
className="media-more-button p-2"
|
||||
type="button"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
onShowDetails?.(media);
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
className="text-xs font-semibold text-type-secondary"
|
||||
icon={Icons.ELLIPSIS}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{editable && closable && (
|
||||
<div className="absolute bottom-0 translate-y-1 right-1">
|
||||
<button
|
||||
className="media-more-button p-2"
|
||||
type="button"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
onEdit?.();
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
className="text-xs font-semibold text-type-secondary"
|
||||
icon={Icons.EDIT}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Flare.Child>
|
||||
</Flare.Base>
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
],
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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: {
|
|||
</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 */}
|
||||
<div>
|
||||
<p className="text-white font-bold mb-3">
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue