diff --git a/index.html b/index.html
index 8d7e8b0..9401e39 100644
--- a/index.html
+++ b/index.html
@@ -870,6 +870,7 @@
const [lastSyncedFavorites, setLastSyncedFavorites] = useState(() => JSON.parse(localStorage.getItem('th_favorites_synced')) || []);
const [quality, setQuality] = useState(() => localStorage.getItem('th_quality') || '320k');
const [syncToken, setSyncToken] = useState(() => localStorage.getItem('th_sync_token') || '');
+ const [lastSuccessToken, setLastSuccessToken] = useState(() => localStorage.getItem('th_last_success_token') || '');
// UI State
const [showFullPlayer, setShowFullPlayer] = useState(false);
@@ -902,22 +903,25 @@
useEffect(() => { localStorage.setItem('th_favorites_synced', JSON.stringify(lastSyncedFavorites)); }, [lastSyncedFavorites]);
useEffect(() => { localStorage.setItem('th_quality', quality); }, [quality]);
useEffect(() => { localStorage.setItem('th_sync_token', syncToken); }, [syncToken]);
+ useEffect(() => { localStorage.setItem('th_last_success_token', lastSuccessToken); }, [lastSuccessToken]);
// Auto Sync Logic
useEffect(() => {
- // Only auto-sync if we have a token and there are actual changes compared to last sync
- if (syncToken && JSON.stringify(favorites) !== JSON.stringify(lastSyncedFavorites)) {
+ // Only auto-sync if we have a token, it matches the last successfully synced token,
+ // and there are actual changes compared to last sync.
+ // This prevents auto-syncing current favorites to a new token while typing/switching.
+ if (syncToken && syncToken === lastSuccessToken && JSON.stringify(favorites) !== JSON.stringify(lastSyncedFavorites)) {
// Use a small timeout to avoid rapid-fire syncs if user clicks quickly
const timer = setTimeout(() => {
autoSyncFavorites();
}, 1000);
return () => clearTimeout(timer);
}
- }, [favorites, syncToken, lastSyncedFavorites]);
+ }, [favorites, syncToken, lastSuccessToken, lastSyncedFavorites]);
// 1. Auto Sync: Incremental update based on diff
const autoSyncFavorites = async () => {
- if (!syncToken) return;
+ if (!syncToken || syncToken !== lastSuccessToken) return;
try {
// Get latest remote state
const remoteFavorites = await syncService.get('favorites', syncToken) || [];
@@ -957,36 +961,59 @@
}
};
- // 2. Manual Sync: Merge (Union)
- // Used when user clicks "Sync" button or initial load
+ // 2. Manual Sync
const manualSyncFavorites = async () => {
if (!syncToken) return;
- // Pull Remote
- const cloudFavorites = await syncService.get('favorites', syncToken);
-
- if (cloudFavorites && Array.isArray(cloudFavorites)) {
- // Merge Strategy: Union (Local + Remote)
- // Since this is a manual sync (often initial), we assume user wants to keep everything
- const merged = [...favorites];
- cloudFavorites.forEach(cloudSong => {
- if (!merged.find(s => s.id === cloudSong.id)) {
- merged.push(cloudSong);
- }
- });
-
- // Update Local
- setFavorites(merged);
- setLastSyncedFavorites(merged);
-
- // Push Merged back to server
- await syncService.set('favorites', merged, syncToken);
- } else {
- // If remote is empty/null, push local
- if (favorites.length > 0) {
- await syncService.set('favorites', favorites, syncToken);
- setLastSyncedFavorites(favorites);
+ // Determine Mode: Switch (Overwrite Local) or Sync (Merge)
+ const isSwitchingToken = syncToken !== lastSuccessToken;
+
+ if (isSwitchingToken) {
+ // Mode: Switch Account/Token -> Remote Overwrites Local
+ try {
+ const cloudFavorites = await syncService.get('favorites', syncToken);
+
+ // If cloud has data, use it. If null/empty, we assume new empty account.
+ const newFavorites = Array.isArray(cloudFavorites) ? cloudFavorites : [];
+
+ setFavorites(newFavorites);
+ setLastSyncedFavorites(newFavorites);
+ setLastSuccessToken(syncToken);
+
+ } catch (e) {
+ console.error("Token switch sync failed", e);
+ throw e; // Let UI show error
}
+ } else {
+ // Mode: Regular Sync -> Merge / Union
+
+ // Pull Remote
+ const cloudFavorites = await syncService.get('favorites', syncToken);
+
+ if (cloudFavorites && Array.isArray(cloudFavorites)) {
+ // Merge: Union
+ const merged = [...favorites];
+ cloudFavorites.forEach(cloudSong => {
+ if (!merged.find(s => s.id === cloudSong.id)) {
+ merged.push(cloudSong);
+ }
+ });
+
+ // Update Local
+ setFavorites(merged);
+ setLastSyncedFavorites(merged);
+
+ // Push Merged back to server
+ await syncService.set('favorites', merged, syncToken);
+ } else {
+ // Remote is empty, push local to it
+ if (favorites.length > 0) {
+ await syncService.set('favorites', favorites, syncToken);
+ setLastSyncedFavorites(favorites);
+ }
+ }
+ // Ensure token is marked as success
+ setLastSuccessToken(syncToken);
}
};