Skip to content

Commit 8e49fd1

Browse files
committed
feat(restore): restore service state on init
1 parent 729436c commit 8e49fd1

File tree

7 files changed

+156
-34
lines changed

7 files changed

+156
-34
lines changed

src/components/views/history/HistoryComponent.vue

+4-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ const { fetchHistory, clearState } = useHistoryStore();
1717
1818
const i18n = useI18n('history');
1919
20-
watchUserChange(fetchHistory, clearState);
20+
watchUserChange({
21+
fetch: fetchHistory,
22+
clear: clearState,
23+
});
2124
2225
const list = useListScroll(filteredHistory, 'watched_at');
2326

src/components/views/watchlist/WatchlistComponent.vue

+4-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ const { filteredListItems, pagination, loading, pageSize, belowThreshold, search
2323
useListStoreRefs();
2424
const { fetchListItems, clearState } = useListStore();
2525
26-
watchUserChange(fetchListItems, clearState);
26+
watchUserChange({
27+
fetch: fetchListItems,
28+
clear: clearState,
29+
});
2730
2831
const list = useListScroll<AnyListDateTypes, AnyList>(
2932
filteredListItems,

src/components/views/watchlist/WatchlistNavbar.vue

+10-4
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,14 @@ const { fetchLists, clearState } = useListsStore();
2525
const listOptions = computed(() =>
2626
lists.value.map((list, i) => ({
2727
label: ['collection', 'watchlist'].includes(list.type) ? i18n(list.name) : list.name,
28-
value: JSON.stringify(list),
28+
value: list.id,
2929
})),
3030
);
3131
3232
const selectValue = computed({
33-
get: () => JSON.stringify(activeList.value),
33+
get: () => activeList.value.id,
3434
set: selected => {
35-
activeList.value = JSON.parse(selected);
35+
activeList.value = lists.value.find(l => l.id === selected)!;
3636
},
3737
});
3838
@@ -45,7 +45,13 @@ defineProps({
4545
},
4646
});
4747
48-
watchUserChange(fetchLists, clearState);
48+
watchUserChange({
49+
fetch: fetchLists,
50+
userChange: active => {
51+
clearState();
52+
if (active) fetchLists();
53+
},
54+
});
4955
5056
const open = ref(false);
5157
</script>

src/stores/data/history.store.ts

+34-6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { TraktClientPagination } from '~/models/trakt/trakt-client.model';
55
import type { TraktHistory } from '~/models/trakt/trakt-history.model';
66

77
import { TraktService } from '~/services/trakt.service';
8+
import { storage } from '~/utils/browser/browser-storage.utils';
89
import { debounceLoading, useBelowThreshold, useLoadingPlaceholder, useSearchFilter } from '~/utils/store.utils';
910

1011
export const useHistoryStore = defineStore('data.history', () => {
@@ -20,6 +21,25 @@ export const useHistoryStore = defineStore('data.history', () => {
2021

2122
const threshold = ref(10);
2223

24+
const saveState = async () =>
25+
storage.local.set('data.history', {
26+
pageSize: pageSize.value,
27+
historyStart: historyStart.value?.getTime(),
28+
historyEnd: historyEnd.value?.getTime(),
29+
});
30+
31+
const restoreState = async () => {
32+
const restored = await storage.local.get<{
33+
pageSize: number;
34+
historyStart: Date | undefined;
35+
historyEnd: Date | undefined;
36+
}>('data.history');
37+
38+
if (restored?.pageSize) pageSize.value = restored.pageSize;
39+
if (restored?.historyStart) historyStart.value = new Date(restored.historyStart);
40+
if (restored?.historyEnd) historyEnd.value = new Date(restored.historyEnd);
41+
};
42+
2343
const clearState = () => {
2444
history.value = [];
2545
pagination.value = undefined;
@@ -62,16 +82,23 @@ export const useHistoryStore = defineStore('data.history', () => {
6282
}
6383
};
6484

65-
const setHistoryRange = ({ start, end }: { start?: Date; end?: Date } = {}) => {
85+
const setHistoryRange = async ({ start, end }: { start?: Date; end?: Date } = {}) => {
6686
historyStart.value = start;
6787
historyEnd.value = end;
68-
return fetchHistory({ start, end });
88+
const result = fetchHistory({ start, end });
89+
await saveState();
90+
return result;
6991
};
7092

71-
watch(pageSize, async () => {
72-
await fetchHistory();
73-
searchHistory.value = '';
74-
});
93+
const initHistoryStore = async () => {
94+
await restoreState();
95+
96+
watch(pageSize, async () => {
97+
await fetchHistory();
98+
searchHistory.value = '';
99+
await saveState();
100+
});
101+
};
75102

76103
return {
77104
history,
@@ -88,6 +115,7 @@ export const useHistoryStore = defineStore('data.history', () => {
88115
historyEnd,
89116
setHistoryRange,
90117
clearState,
118+
initHistoryStore,
91119
};
92120
});
93121

src/stores/data/list.store.ts

+68-15
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@ import type { TraktWatchlist } from '~/models/trakt/trakt-watchlist.model';
1010

1111
import { TraktService } from '~/services/trakt.service';
1212
import { useUserSettingsStoreRefs } from '~/stores/settings/user.store';
13+
import { storage } from '~/utils/browser/browser-storage.utils';
1314
import { debounceLoading, useBelowThreshold, useLoadingPlaceholder, useSearchFilter } from '~/utils/store.utils';
1415

1516
export type AnyList = TraktListItem | TraktWatchlist | (TraktCollection & { id: number });
1617
export type ListType = {
1718
type: 'list' | 'collaboration' | 'collection' | 'watchlist';
1819
name: string;
19-
id?: number | string;
20+
id: number | string;
2021
scope?: 'movies' | 'shows';
2122
};
2223

@@ -29,9 +30,9 @@ export const anyListDateGetter = (item: AnyList) => {
2930
};
3031

3132
export const DefaultLists: Record<string, ListType> = {
32-
Watchlist: { type: 'watchlist', name: 'list_type__watchlist' },
33-
MovieCollection: { type: 'collection', scope: 'movies', name: 'list_type__collection_movie' },
34-
ShowCollection: { type: 'collection', scope: 'shows', name: 'list_type__collection_show' },
33+
Watchlist: { type: 'watchlist', id: 'watchlist', name: 'list_type__watchlist' },
34+
MovieCollection: { type: 'collection', id: 'collection-movies', scope: 'movies', name: 'list_type__collection_movie' },
35+
ShowCollection: { type: 'collection', id: 'collection-shows', scope: 'shows', name: 'list_type__collection_show' },
3536
} as const;
3637

3738
const DefaultList: ListType[] = [DefaultLists.Watchlist, DefaultLists.MovieCollection, DefaultLists.ShowCollection];
@@ -42,6 +43,22 @@ export const useListsStore = defineStore('data.lists', () => {
4243
const lists = ref<ListType[]>(DefaultList);
4344
const activeList = ref<ListType>(DefaultLists.Watchlist);
4445

46+
const saveState = async () =>
47+
storage.local.set('data.lists', {
48+
lists: [...lists.value],
49+
activeList: activeList.value,
50+
});
51+
52+
const restoreState = async () => {
53+
const restored = await storage.local.get<{
54+
lists: ListType[];
55+
activeList: ListType;
56+
}>('data.lists');
57+
if (restored?.lists) lists.value = restored.lists;
58+
if (restored?.activeList === activeList.value) return;
59+
if (restored?.activeList) activeList.value = restored.activeList;
60+
};
61+
4562
const clearState = () => {
4663
lists.value = DefaultList;
4764
activeList.value = DefaultLists.Watchlist;
@@ -73,15 +90,28 @@ export const useListsStore = defineStore('data.lists', () => {
7390
}) satisfies ListType,
7491
),
7592
];
93+
if (activeList.value?.id && !lists.value.some(l => activeList.value.id === l?.id)) {
94+
console.warn('Active List not found, falling back to default', activeList.value);
95+
activeList.value = DefaultLists.Watchlist;
96+
}
7697
} catch (e) {
7798
console.error('Failed to fetch lists');
7899
throw e;
79100
} finally {
80101
loading.value = false;
102+
console.info('Fetched Lists', loading.value);
81103
}
82104
};
83105

84-
return { loading, lists, activeList, fetchLists, clearState };
106+
const initListsStore = async () => {
107+
await restoreState();
108+
109+
watch(activeList, async _value => {
110+
await saveState();
111+
});
112+
};
113+
114+
return { loading, lists, activeList, fetchLists, clearState, initListsStore };
85115
});
86116

87117
export const useListsStoreRefs = () => storeToRefs(useListsStore());
@@ -95,6 +125,13 @@ export const useListStore = defineStore('data.list', () => {
95125
const searchList = ref('');
96126
const threshold = ref(10);
97127

128+
const saveState = async () => storage.local.set('data.list.page-size', pageSize.value);
129+
const restoreState = async () => {
130+
const restored = await storage.local.get<number>('data.list.page-size');
131+
if (restored === pageSize.value) return;
132+
if (restored) pageSize.value = restored;
133+
};
134+
98135
const clearState = () => {
99136
listItems.value = [];
100137
pagination.value = undefined;
@@ -139,7 +176,6 @@ export const useListStore = defineStore('data.list', () => {
139176
} else {
140177
throw new Error('Invalid list type');
141178
}
142-
console.info('Fetched List', list, response);
143179
const newData = response.data.map((item, index) => {
144180
if ('id' in item) return item;
145181
return { ...item, id: index };
@@ -156,17 +192,34 @@ export const useListStore = defineStore('data.list', () => {
156192
}
157193
};
158194

159-
watch(pageSize, async () => {
160-
await fetchListItems();
161-
searchList.value = '';
162-
});
195+
const initListStore = async () => {
196+
await restoreState();
163197

164-
watch(activeList, async () => {
165-
await fetchListItems();
166-
searchList.value = '';
167-
});
198+
watch(pageSize, async () => {
199+
await fetchListItems();
200+
searchList.value = '';
201+
await saveState();
202+
});
168203

169-
return { loading, listItems, pagination, pageSize, searchList, clearState, belowThreshold, loadingPlaceholder, fetchListItems, filteredListItems };
204+
watch(activeList, async () => {
205+
await fetchListItems();
206+
searchList.value = '';
207+
});
208+
};
209+
210+
return {
211+
loading,
212+
listItems,
213+
pagination,
214+
pageSize,
215+
searchList,
216+
clearState,
217+
belowThreshold,
218+
loadingPlaceholder,
219+
fetchListItems,
220+
filteredListItems,
221+
initListStore,
222+
};
170223
});
171224

172225
export const useListStoreRefs = () => storeToRefs(useListStore());

src/utils/store.utils.ts

+26-6
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export const useSearchFilter = <D extends string, T extends ListScrollSourceItem
3030
if (!_search) return false;
3131
if (item.show?.title?.toLowerCase().includes(_search)) return true;
3232
if (item.movie?.title?.toLowerCase().includes(_search)) return true;
33+
if (item.person?.name?.toLowerCase().includes(_search)) return true;
3334
if (!date) return false;
3435
const _date = typeof date === 'function' ? date(item) : item[date];
3536
if (!_date) return false;
@@ -55,25 +56,44 @@ export const useLoadingPlaceholder = <T>(pageSize: Ref<number>) =>
5556
.map((_, i) => ({ id: -1 * (i + 1) }) as T),
5657
);
5758

58-
export const watchUserChange = (fetch: () => Promise<void>, clear: () => void) => {
59+
export const watchUserChange = ({
60+
fetch,
61+
clear,
62+
activated,
63+
deactivated,
64+
userChange,
65+
}: {
66+
fetch?: () => Promise<void>;
67+
clear?: () => void | Promise<void>;
68+
activated?: () => void | Promise<void>;
69+
deactivated?: () => void | Promise<void>;
70+
userChange?: (active: boolean) => void | Promise<void>;
71+
}) => {
5972
const { user } = useUserSettingsStoreRefs();
6073

6174
const active = ref(false);
6275

6376
onActivated(async () => {
6477
active.value = true;
65-
await fetch();
78+
if (activated) return activated();
79+
await fetch?.();
6680
});
6781

6882
onDeactivated(() => {
6983
active.value = false;
84+
deactivated?.();
7085
});
7186

7287
onMounted(() => {
73-
watch(user, async () => {
74-
if (active.value) await fetch();
75-
else clear();
76-
});
88+
watch(
89+
user,
90+
91+
async () => {
92+
if (userChange) return userChange(active.value);
93+
if (active.value) await fetch?.();
94+
else clear?.();
95+
},
96+
);
7797
});
7898

7999
return { active, user };

src/web/init-services.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { TraktService } from '~/services/trakt.service';
22
import { useAppStateStore } from '~/stores/app-state.store';
3+
import { useHistoryStore } from '~/stores/data/history.store';
34
import { useImageStore } from '~/stores/data/image.store';
5+
import { useListsStore, useListStore } from '~/stores/data/list.store';
46
import { useAuthSettingsStore } from '~/stores/settings/auth.store';
57
import { useUserSettingsStore } from '~/stores/settings/user.store';
68
import { initLocalI18n } from '~/utils';
@@ -17,7 +19,14 @@ export const initServices = async () => {
1719

1820
TraktService.listen();
1921

20-
await Promise.all([syncRestoreAllUsers(), useImageStore().initImageStore(), initLocalI18n().promise]);
22+
await Promise.all([
23+
initLocalI18n().promise,
24+
syncRestoreAllUsers(),
25+
useImageStore().initImageStore(),
26+
useListsStore().initListsStore(),
27+
useListStore().initListStore(),
28+
useHistoryStore().initHistoryStore(),
29+
]);
2130

2231
setAppReady(true);
2332
};

0 commit comments

Comments
 (0)