Merge remote-tracking branch 'origin/pr-229' into agent/release-4.9.19

This commit is contained in:
kuekhaoyang
2026-07-31 23:08:41 +08:00
4 changed files with 44 additions and 6 deletions
+8 -4
View File
@@ -1,6 +1,7 @@
import { useState, useCallback } from 'react';
import { useHistoryStore, usePremiumHistoryStore } from '@/lib/store/history-store';
import { useFavoritesStore, usePremiumFavoritesStore } from '@/lib/store/favorites-store';
import { keepRenderableFavorites, keepRenderableHistory } from '@/lib/utils/sync-records';
import { getProfileId } from '@/lib/store/auth-store';
export function useCloudSync(isPremium = false) {
@@ -19,11 +20,14 @@ export function useCloudSync(isPremium = false) {
const result = await response.json();
if (result.success && result.data) {
if (result.data.history?.length > 0) {
historyStore.getState().importHistory(result.data.history);
const history = keepRenderableHistory(result.data.history);
const favorites = keepRenderableFavorites(result.data.favorites);
if (history.length > 0) {
historyStore.getState().importHistory(history);
}
if (result.data.favorites?.length > 0) {
favoritesStore.getState().importFavorites(result.data.favorites);
if (favorites.length > 0) {
favoritesStore.getState().importFavorites(favorites);
}
}
} catch (error) {
+32
View File
@@ -0,0 +1,32 @@
import type { FavoriteItem, VideoHistoryItem } from '@/lib/types';
function hasIdentity(value: unknown): boolean {
if (!value || typeof value !== 'object') return false;
const record = value as { videoId?: unknown; source?: unknown; title?: unknown };
const videoId = record.videoId;
const hasVideoId = typeof videoId === 'string' ? videoId.length > 0 : typeof videoId === 'number';
return hasVideoId && typeof record.source === 'string' && typeof record.title === 'string';
}
/**
* Records restored from server-side sync are not guaranteed to be well formed —
* they may come from an older schema or a partial write. Rendering one without
* `videoId` throws while building the player URL, which takes down the whole
* page, so anything lacking a usable identity is dropped on the way in.
*/
export function isRenderableHistoryItem(value: unknown): value is VideoHistoryItem {
return hasIdentity(value);
}
export function isRenderableFavoriteItem(value: unknown): value is FavoriteItem {
return hasIdentity(value);
}
export function keepRenderableHistory(items: unknown): VideoHistoryItem[] {
return Array.isArray(items) ? items.filter(isRenderableHistoryItem) : [];
}
export function keepRenderableFavorites(items: unknown): FavoriteItem[] {
return Array.isArray(items) ? items.filter(isRenderableFavoriteItem) : [];
}