mirror of
https://github.com/p-stream/p-stream.git
synced 2026-04-18 09:42:04 +00:00
fix groups and order initialization for new accounts
This commit is contained in:
parent
100d7cfe40
commit
0acb043960
10 changed files with 141 additions and 53 deletions
|
|
@ -10,12 +10,12 @@ export interface BookmarkMetaInput {
|
|||
year: number;
|
||||
poster?: string;
|
||||
type: string;
|
||||
group?: string[];
|
||||
}
|
||||
|
||||
export interface BookmarkInput {
|
||||
tmdbId: string;
|
||||
meta: BookmarkMetaInput;
|
||||
group?: string[];
|
||||
}
|
||||
|
||||
export function bookmarkMediaToInput(
|
||||
|
|
@ -30,6 +30,7 @@ export function bookmarkMediaToInput(
|
|||
year: item.year ?? 0,
|
||||
},
|
||||
tmdbId,
|
||||
group: item.group,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,8 +33,8 @@ export interface BookmarkResponse {
|
|||
year: number;
|
||||
poster?: string;
|
||||
type: "show" | "movie";
|
||||
group?: string[];
|
||||
};
|
||||
group: string[];
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
|
|
@ -63,6 +63,7 @@ export function bookmarkResponsesToEntries(responses: BookmarkResponse[]) {
|
|||
const entries = responses.map((bookmark) => {
|
||||
const item: BookmarkMediaItem = {
|
||||
...bookmark.meta,
|
||||
group: bookmark.group.length > 0 ? bookmark.group : undefined,
|
||||
updatedAt: new Date(bookmark.updatedAt).getTime(),
|
||||
};
|
||||
return [bookmark.tmdbId, item] as const;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import {
|
|||
} from "@/backend/accounts/user";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import { useBookmarkStore } from "@/stores/bookmarks";
|
||||
import { useGroupOrderStore } from "@/stores/groupOrder";
|
||||
import { useLanguageStore } from "@/stores/language";
|
||||
import { usePreferencesStore } from "@/stores/preferences";
|
||||
import { useProgressStore } from "@/stores/progress";
|
||||
|
|
@ -102,7 +103,7 @@ export function useAuthData() {
|
|||
replaceItems(progressResponsesToEntries(progress));
|
||||
|
||||
if (groupOrder?.groupOrder) {
|
||||
useBookmarkStore.getState().setGroupOrder(groupOrder.groupOrder);
|
||||
useGroupOrderStore.getState().setGroupOrder(groupOrder.groupOrder);
|
||||
}
|
||||
|
||||
if (settings.applicationLanguage) {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import App from "@/setup/App";
|
|||
import { conf } from "@/setup/config";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import { BookmarkSyncer } from "@/stores/bookmarks/BookmarkSyncer";
|
||||
import { GroupSyncer } from "@/stores/groupOrder/GroupSyncer";
|
||||
import { changeAppLanguage, useLanguageStore } from "@/stores/language";
|
||||
import { ProgressSyncer } from "@/stores/progress/ProgressSyncer";
|
||||
import { SettingsSyncer } from "@/stores/subtitles/SettingsSyncer";
|
||||
|
|
@ -185,6 +186,7 @@ root.render(
|
|||
<ThemeProvider applyGlobal>
|
||||
<ProgressSyncer />
|
||||
<BookmarkSyncer />
|
||||
<GroupSyncer />
|
||||
<SettingsSyncer />
|
||||
<TheRouter>
|
||||
<MigrationRunner />
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import { useIsMobile } from "@/hooks/useIsMobile";
|
|||
import { CarouselNavButtons } from "@/pages/discover/components/CarouselNavButtons";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import { useBookmarkStore } from "@/stores/bookmarks";
|
||||
import { useGroupOrderStore } from "@/stores/groupOrder";
|
||||
import { useProgressStore } from "@/stores/progress";
|
||||
import { MediaItem } from "@/utils/mediaTypes";
|
||||
|
||||
|
|
@ -64,8 +65,8 @@ export function BookmarksCarousel({
|
|||
const account = useAuthStore((s) => s.account);
|
||||
|
||||
// Group order editing state
|
||||
const groupOrder = useBookmarkStore((s) => s.groupOrder);
|
||||
const setGroupOrder = useBookmarkStore((s) => s.setGroupOrder);
|
||||
const groupOrder = useGroupOrderStore((s) => s.groupOrder);
|
||||
const setGroupOrder = useGroupOrderStore((s) => s.setGroupOrder);
|
||||
const editOrderModal = useModal("bookmark-edit-order-carousel");
|
||||
const [tempGroupOrder, setTempGroupOrder] = useState<string[]>([]);
|
||||
|
||||
|
|
@ -323,7 +324,9 @@ export function BookmarksCarousel({
|
|||
|
||||
// Save to backend
|
||||
if (backendUrl && account) {
|
||||
useBookmarkStore.getState().saveGroupOrderToBackend(backendUrl, account);
|
||||
useGroupOrderStore
|
||||
.getState()
|
||||
.saveGroupOrderToBackend(backendUrl, account);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import { Heading2, Paragraph } from "@/components/utils/Text";
|
|||
import { useBackendUrl } from "@/hooks/auth/useBackendUrl";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import { useBookmarkStore } from "@/stores/bookmarks";
|
||||
import { useGroupOrderStore } from "@/stores/groupOrder";
|
||||
import { useProgressStore } from "@/stores/progress";
|
||||
import { MediaItem } from "@/utils/mediaTypes";
|
||||
|
||||
|
|
@ -42,8 +43,8 @@ export function BookmarksPart({
|
|||
const { t } = useTranslation();
|
||||
const progressItems = useProgressStore((s) => s.items);
|
||||
const bookmarks = useBookmarkStore((s) => s.bookmarks);
|
||||
const groupOrder = useBookmarkStore((s) => s.groupOrder);
|
||||
const setGroupOrder = useBookmarkStore((s) => s.setGroupOrder);
|
||||
const groupOrder = useGroupOrderStore((s) => s.groupOrder);
|
||||
const setGroupOrder = useGroupOrderStore((s) => s.setGroupOrder);
|
||||
const removeBookmark = useBookmarkStore((s) => s.removeBookmark);
|
||||
const [editing, setEditing] = useState(false);
|
||||
const [gridRef] = useAutoAnimate<HTMLDivElement>();
|
||||
|
|
@ -278,7 +279,9 @@ export function BookmarksPart({
|
|||
|
||||
// Save to backend
|
||||
if (backendUrl && account) {
|
||||
useBookmarkStore.getState().saveGroupOrderToBackend(backendUrl, account);
|
||||
useGroupOrderStore
|
||||
.getState()
|
||||
.saveGroupOrderToBackend(backendUrl, account);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -32,9 +32,9 @@ async function syncBookmarks(
|
|||
title: item.title ?? "",
|
||||
type: item.type ?? "",
|
||||
year: item.year ?? NaN,
|
||||
group: item.group,
|
||||
},
|
||||
tmdbId: item.tmdbId,
|
||||
group: item.group,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,6 @@ import { create } from "zustand";
|
|||
import { persist } from "zustand/middleware";
|
||||
import { immer } from "zustand/middleware/immer";
|
||||
|
||||
import { getGroupOrder, updateGroupOrder } from "@/backend/accounts/groupOrder";
|
||||
import { useBackendUrl } from "@/hooks/auth/useBackendUrl";
|
||||
import { AccountWithToken, useAuthStore } from "@/stores/auth";
|
||||
import { PlayerMeta } from "@/stores/player/slices/source";
|
||||
|
||||
export interface BookmarkMediaItem {
|
||||
|
|
@ -30,20 +27,10 @@ export interface BookmarkUpdateItem {
|
|||
export interface BookmarkStore {
|
||||
bookmarks: Record<string, BookmarkMediaItem>;
|
||||
updateQueue: BookmarkUpdateItem[];
|
||||
groupOrder: string[];
|
||||
addBookmark(meta: PlayerMeta): void;
|
||||
addBookmarkWithGroups(meta: PlayerMeta, groups?: string[]): void;
|
||||
removeBookmark(id: string): void;
|
||||
replaceBookmarks(items: Record<string, BookmarkMediaItem>): void;
|
||||
setGroupOrder(order: string[]): void;
|
||||
saveGroupOrderToBackend(
|
||||
backendUrl: string,
|
||||
account: AccountWithToken,
|
||||
): Promise<void>;
|
||||
loadGroupOrderFromBackend(
|
||||
backendUrl: string,
|
||||
account: AccountWithToken,
|
||||
): Promise<void>;
|
||||
clear(): void;
|
||||
clearUpdateQueue(): void;
|
||||
removeUpdateItem(id: string): void;
|
||||
|
|
@ -56,7 +43,6 @@ export const useBookmarkStore = create(
|
|||
immer<BookmarkStore>((set) => ({
|
||||
bookmarks: {},
|
||||
updateQueue: [],
|
||||
groupOrder: [],
|
||||
removeBookmark(id) {
|
||||
set((s) => {
|
||||
updateId += 1;
|
||||
|
|
@ -135,35 +121,6 @@ export const useBookmarkStore = create(
|
|||
s.updateQueue = [...s.updateQueue.filter((v) => v.id !== id)];
|
||||
});
|
||||
},
|
||||
setGroupOrder(order: string[]) {
|
||||
set((s) => {
|
||||
s.groupOrder = order;
|
||||
});
|
||||
},
|
||||
async saveGroupOrderToBackend(
|
||||
backendUrl: string,
|
||||
account: AccountWithToken,
|
||||
) {
|
||||
if (!account || !backendUrl) {
|
||||
throw new Error("No authenticated account or backend URL");
|
||||
}
|
||||
|
||||
const currentState = useBookmarkStore.getState();
|
||||
await updateGroupOrder(backendUrl, account, currentState.groupOrder);
|
||||
},
|
||||
async loadGroupOrderFromBackend(
|
||||
backendUrl: string,
|
||||
account: AccountWithToken,
|
||||
) {
|
||||
if (!account || !backendUrl) {
|
||||
throw new Error("No authenticated account or backend URL");
|
||||
}
|
||||
|
||||
const response = await getGroupOrder(backendUrl, account);
|
||||
set((s) => {
|
||||
s.groupOrder = response.groupOrder;
|
||||
});
|
||||
},
|
||||
})),
|
||||
{
|
||||
name: "__MW::bookmarks",
|
||||
|
|
|
|||
55
src/stores/groupOrder/GroupSyncer.tsx
Normal file
55
src/stores/groupOrder/GroupSyncer.tsx
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import { useEffect, useRef } from "react";
|
||||
|
||||
import { updateGroupOrder } from "@/backend/accounts/groupOrder";
|
||||
import { useBackendUrl } from "@/hooks/auth/useBackendUrl";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import { useGroupOrderStore } from "@/stores/groupOrder";
|
||||
|
||||
const syncIntervalMs = 5 * 1000;
|
||||
|
||||
export function GroupSyncer() {
|
||||
const url = useBackendUrl();
|
||||
const groupOrder = useGroupOrderStore((s) => s.groupOrder);
|
||||
const lastSyncedOrder = useRef<string[]>([]);
|
||||
const isInitialized = useRef(false);
|
||||
|
||||
// Initialize lastSyncedOrder on first render
|
||||
useEffect(() => {
|
||||
if (!isInitialized.current) {
|
||||
lastSyncedOrder.current = [...groupOrder];
|
||||
isInitialized.current = true;
|
||||
}
|
||||
}, [groupOrder]);
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
(async () => {
|
||||
if (!url) return;
|
||||
|
||||
const user = useAuthStore.getState();
|
||||
if (!user.account) return; // not logged in, dont sync to server
|
||||
|
||||
// Check if group order has changed since last sync
|
||||
const currentOrder = useGroupOrderStore.getState().groupOrder;
|
||||
const hasChanged =
|
||||
JSON.stringify(currentOrder) !==
|
||||
JSON.stringify(lastSyncedOrder.current);
|
||||
|
||||
if (hasChanged) {
|
||||
try {
|
||||
await updateGroupOrder(url, user.account, currentOrder);
|
||||
lastSyncedOrder.current = [...currentOrder];
|
||||
} catch (err) {
|
||||
console.error("Failed to sync group order:", err);
|
||||
}
|
||||
}
|
||||
})();
|
||||
}, syncIntervalMs);
|
||||
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
};
|
||||
}, [url]);
|
||||
|
||||
return null;
|
||||
}
|
||||
65
src/stores/groupOrder/index.ts
Normal file
65
src/stores/groupOrder/index.ts
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
import { create } from "zustand";
|
||||
import { persist } from "zustand/middleware";
|
||||
import { immer } from "zustand/middleware/immer";
|
||||
|
||||
import { getGroupOrder, updateGroupOrder } from "@/backend/accounts/groupOrder";
|
||||
import { AccountWithToken } from "@/stores/auth";
|
||||
|
||||
export interface GroupOrderStore {
|
||||
groupOrder: string[];
|
||||
setGroupOrder(order: string[]): void;
|
||||
saveGroupOrderToBackend(
|
||||
backendUrl: string,
|
||||
account: AccountWithToken,
|
||||
): Promise<void>;
|
||||
loadGroupOrderFromBackend(
|
||||
backendUrl: string,
|
||||
account: AccountWithToken,
|
||||
): Promise<void>;
|
||||
clear(): void;
|
||||
}
|
||||
|
||||
export const useGroupOrderStore = create(
|
||||
persist(
|
||||
immer<GroupOrderStore>((set) => ({
|
||||
groupOrder: [],
|
||||
setGroupOrder(order: string[]) {
|
||||
set((s) => {
|
||||
s.groupOrder = order;
|
||||
});
|
||||
},
|
||||
async saveGroupOrderToBackend(
|
||||
backendUrl: string,
|
||||
account: AccountWithToken,
|
||||
) {
|
||||
if (!account || !backendUrl) {
|
||||
throw new Error("No authenticated account or backend URL");
|
||||
}
|
||||
|
||||
const currentState = useGroupOrderStore.getState();
|
||||
await updateGroupOrder(backendUrl, account, currentState.groupOrder);
|
||||
},
|
||||
async loadGroupOrderFromBackend(
|
||||
backendUrl: string,
|
||||
account: AccountWithToken,
|
||||
) {
|
||||
if (!account || !backendUrl) {
|
||||
throw new Error("No authenticated account or backend URL");
|
||||
}
|
||||
|
||||
const response = await getGroupOrder(backendUrl, account);
|
||||
set((s) => {
|
||||
s.groupOrder = response.groupOrder;
|
||||
});
|
||||
},
|
||||
clear() {
|
||||
set((s) => {
|
||||
s.groupOrder = [];
|
||||
});
|
||||
},
|
||||
})),
|
||||
{
|
||||
name: "__MW::groupOrder",
|
||||
},
|
||||
),
|
||||
);
|
||||
Loading…
Reference in a new issue