Skip to content

Commit 5852e8c

Browse files
committed
fix(pagination): switch type and fix reactivity
1 parent 4606d02 commit 5852e8c

12 files changed

+79
-52
lines changed

src/components/common/list/ListScroll.vue

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ import { NFlex, NTimeline, NVirtualList } from 'naive-ui';
33
44
import { computed, ref, toRefs } from 'vue';
55
6-
import type { TraktClientPagination } from '@dvcol/trakt-http-client/models';
76
import type { PropType, Ref, Transition } from 'vue';
87
8+
import type { StorePagination } from '~/models/pagination.model';
9+
910
import ListEmpty from '~/components/common/list/ListEmpty.vue';
1011
import ListItem from '~/components/common/list/ListItem.vue';
1112
import ListLoadMore from '~/components/common/list/ListLoadMore.vue';
@@ -29,7 +30,7 @@ const props = defineProps({
2930
required: true,
3031
},
3132
pagination: {
32-
type: Object as PropType<TraktClientPagination>,
33+
type: Object as PropType<StorePagination>,
3334
required: false,
3435
},
3536
pageSize: {

src/components/common/list/use-list-scroll.ts

+26-12
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import { type TraktClientPagination, TraktEpisodeType, type TraktEpisodeTypes } from '@dvcol/trakt-http-client/models';
1+
import { TraktEpisodeType, type TraktEpisodeTypes } from '@dvcol/trakt-http-client/models';
22

33
import { computed, isRef, ref, watch } from 'vue';
44

55
import type { Ref } from 'vue';
66

77
import type { ListScrollItem, ListScrollItemMeta, ListScrollItemTag, ListScrollSourceItem, OnScroll, OnUpdated } from '~/models/list-scroll.model';
88

9+
import type { StorePagination } from '~/models/pagination.model';
910
import type { ImageQuery } from '~/stores/data/image.store';
1011

1112
import { usePanelItem } from '~/components/views/panel/use-panel-item';
@@ -283,7 +284,7 @@ export const useListScrollEvents = (
283284
active,
284285
}: {
285286
data: Ref<ListScrollItem[]>;
286-
pagination: Ref<TraktClientPagination | undefined>;
287+
pagination: Ref<StorePagination>;
287288
loading: Ref<boolean>;
288289
belowThreshold?: Ref<boolean>;
289290
active?: Ref<boolean>;
@@ -325,19 +326,32 @@ export const useListScrollEvents = (
325326
return { onScroll, onUpdated, onLoadMore };
326327
};
327328

328-
export const addLoadMore = (
329-
items: Ref<ListScrollItem[]>,
330-
pagination: Ref<TraktClientPagination | undefined>,
331-
search: Ref<string>,
332-
): Ref<ListScrollItem[]> => {
329+
export const addAllLoaded = (items: Ref<ListScrollItem[]>, pagination: Ref<StorePagination>) => {
330+
return computed(() => {
331+
const array = items.value;
332+
if (!array.length) return array;
333+
const { page, pageCount } = pagination.value ?? {};
334+
if (page && pageCount && page < pageCount) return array;
335+
// If the last item is already an all loaded item we return the array as is
336+
if (array[array.length - 1].type === ListScrollItemType.AllLoaded) return array;
337+
const type = ListScrollItemType.AllLoaded;
338+
const index = items.value.length;
339+
const allLoaded: ListScrollItem = { id: type, type, index, key: `${index}-${type}` };
340+
return [...array, allLoaded];
341+
});
342+
};
343+
344+
export const addLoadMore = (items: Ref<ListScrollItem[]>, pagination: Ref<StorePagination>, search: Ref<string>): Ref<ListScrollItem[]> => {
333345
return computed(() => {
334346
const array = items.value;
335347
if (!array.length) return array;
336-
if (!search.value) return array;
337-
if (!pagination.value?.page) return array;
338-
if (!pagination.value?.pageCount) return array;
339-
if (pagination.value.page === pagination.value.pageCount) return array;
340-
if (array.length && array[array.length - 1].type === ListScrollItemType.LoadMore) return array;
348+
// If there is no search value we add the all loaded item or return the array as is
349+
if (!search.value) return addAllLoaded(items, pagination).value;
350+
const { page, pageCount } = pagination.value ?? {};
351+
// If we are on the last page or an un-paginated view we add the all loaded item or return the array as is
352+
if (!page || !pageCount || page === pageCount) return addAllLoaded(items, pagination).value;
353+
// If the last item is already a load more item we return the array as is
354+
if (array[array.length - 1].type === ListScrollItemType.LoadMore) return array;
341355
const type = ListScrollItemType.LoadMore;
342356
const index = items.value.length;
343357
const loadMore: ListScrollItem = { id: type, type, index, key: `${index}-${type}` };

src/components/views/settings/SettingsExport.vue

+3-7
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
<script lang="ts" setup>
2-
import {
3-
TraktApiExtended,
4-
type TraktClientPagination,
5-
} from '@dvcol/trakt-http-client/models';
2+
import { TraktApiExtended } from '@dvcol/trakt-http-client/models';
63
import { NButton, NIcon, NProgress } from 'naive-ui';
74
85
import { reactive, ref } from 'vue';
96
7+
import type { StorePagination } from '~/models/pagination.model';
108
import type { CancellableWritePromise } from '~/utils/trakt-service.utils';
119
1210
import IconDownload from '~/components/icons/IconDownload.vue';
@@ -36,9 +34,7 @@ const cancelled = reactive<Record<string | number, boolean>>({});
3634
const fetching = reactive<
3735
Record<string | number, CancellableWritePromise<unknown> | undefined>
3836
>({});
39-
const pagination = reactive<
40-
Record<string | number, Partial<TraktClientPagination> | undefined>
41-
>({});
37+
const pagination = reactive<Record<string | number, StorePagination | undefined>>({});
4238
4339
type ExportScope<T extends Promise<unknown> = CancellableWritePromise<unknown>> = {
4440
label: string;

src/components/views/watchlist/WatchlistComponent.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ onMounted(() => {
8787
<ListScroll
8888
ref="listRef"
8989
hide-date
90-
:items="list"
90+
:items="listItems"
9191
:loading="listLoading"
9292
:pagination="pagination"
9393
:page-size="pageSize"

src/models/pagination.model.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import type { TraktClientPagination } from '@dvcol/trakt-http-client/models';
2+
3+
export type StorePagination = Partial<TraktClientPagination>;

src/stores/data/history.store.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ import { TraktApiExtended } from '@dvcol/trakt-http-client/models';
33
import { defineStore, storeToRefs } from 'pinia';
44
import { computed, reactive, ref, watch } from 'vue';
55

6-
import type { TraktClientPagination, TraktHistory, TraktHistoryGetQuery } from '@dvcol/trakt-http-client/models';
6+
import type { TraktHistory, TraktHistoryGetQuery } from '@dvcol/trakt-http-client/models';
77

8+
import type { StorePagination } from '~/models/pagination.model';
89
import type { ErrorDictionary } from '~/utils/retry.utils';
910

1011
import { PageSize } from '~/models/page-size.model';
@@ -43,7 +44,7 @@ export const useHistoryStore = defineStore(HistoryStoreConstants.Store, () => {
4344
const pageSize = ref(PageSize.p100);
4445
const history = ref<TraktHistory[]>([]);
4546
const historyDictionary = reactive<HistoryDictionary>({});
46-
const pagination = ref<TraktClientPagination>();
47+
const pagination = ref<StorePagination>({});
4748
const extended = ref(false);
4849
const init = ref(false);
4950

@@ -78,7 +79,7 @@ export const useHistoryStore = defineStore(HistoryStoreConstants.Store, () => {
7879

7980
const clearState = () => {
8081
history.value = [];
81-
pagination.value = undefined;
82+
pagination.value = {};
8283
searchHistory.value = '';
8384
historyStart.value = undefined;
8485
historyEnd.value = undefined;
@@ -119,7 +120,7 @@ export const useHistoryStore = defineStore(HistoryStoreConstants.Store, () => {
119120
if (extended.value) query.extended = TraktApiExtended.Full;
120121
try {
121122
const response = await TraktService.history(query);
122-
pagination.value = response.pagination;
123+
pagination.value = response.pagination ?? {};
123124
history.value = page ? [...history.value.filter(h => h.id >= 0), ...response.data] : response.data;
124125
delete historyErrors[JSON.stringify(query)];
125126
evicted.history = false;

src/stores/data/list.store.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { computed, reactive, ref, watch } from 'vue';
33

44
import type {
55
TraktApiIds,
6-
TraktClientPagination,
76
TraktCollection,
87
TraktCollectionGetQuery,
98
TraktFavoriteGetQuery,
@@ -15,6 +14,8 @@ import type {
1514
TraktWatchlistGetQuery,
1615
} from '@dvcol/trakt-http-client/models';
1716

17+
import type { StorePagination } from '~/models/pagination.model';
18+
1819
import IconCheckedList from '~/components/icons/IconCheckedList.vue';
1920
import IconGrid from '~/components/icons/IconGrid.vue';
2021
import IconHeart from '~/components/icons/IconHeart.vue';
@@ -236,7 +237,7 @@ export const useListStore = defineStore(ListStoreConstants.Store, () => {
236237
const firstLoad = ref(true);
237238
const loading = ref(true);
238239
const pageSize = ref(PageSize.p100);
239-
const pagination = ref<TraktClientPagination>();
240+
const pagination = ref<StorePagination>({});
240241

241242
const typeLoading = reactive<ListTypeLoading>({});
242243
const typeItemLoading = reactive<ListDictionaryItemLoading>({});
@@ -260,7 +261,7 @@ export const useListStore = defineStore(ListStoreConstants.Store, () => {
260261

261262
const clearState = () => {
262263
listItems.value = [];
263-
pagination.value = undefined;
264+
pagination.value = {};
264265
searchList.value = '';
265266

266267
clearProxy(typeLoading);
@@ -355,7 +356,7 @@ export const useListStore = defineStore(ListStoreConstants.Store, () => {
355356
if ('id' in item) return item;
356357
return { ...item, id: `${page}-${index}` };
357358
});
358-
pagination.value = response.pagination;
359+
pagination.value = response.pagination ?? {};
359360
listItems.value = page ? [...listItems.value.filter(l => l.type !== ListScrollItemType.Loading), ...newData] : newData;
360361
evicted.watchlist = false;
361362
} catch (e) {

src/stores/data/ratings.store.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import {
22
type TraktApiIds,
3-
type TraktClientPagination,
43
type TraktRating,
54
type TraktRatingRequest,
65
TraktRatingType,
@@ -10,6 +9,8 @@ import {
109
import { defineStore, storeToRefs } from 'pinia';
1110
import { computed, reactive, ref } from 'vue';
1211

12+
import type { StorePagination } from '~/models/pagination.model';
13+
1314
import { PageSize } from '~/models/page-size.model';
1415
import { ErrorService } from '~/services/error.service';
1516
import { Logger } from '~/services/logger.service';
@@ -33,14 +34,15 @@ type TraktRatingsReturn<T extends TraktRatingTypes> = T extends 'movies'
3334

3435
type RatingsLoadingDictionary = Partial<Record<TraktRatingTypes, boolean>>;
3536
type RatingsDictionary<T extends TraktRatingTypes = TraktRatingTypes> = Partial<Record<T, Record<string, TraktRatingsReturn<T>>>>;
36-
type RatingsPaginationDictionary = Partial<Record<TraktRatingTypes, TraktClientPagination>>;
37+
type RatingsPaginationDictionary = Partial<Record<TraktRatingTypes, StorePagination>>;
3738

3839
const isMovieRating = (rating: TraktRating): rating is TraktRating<'movie'> => rating.type === 'movie';
3940
const isShowRating = (rating: TraktRating): rating is TraktRating<'show'> => rating.type === 'show';
4041
const isSeasonRating = (rating: TraktRating): rating is TraktRating<'season'> => rating.type === 'season';
4142
const isEpisodeRating = (rating: TraktRating): rating is TraktRating<'episode'> => rating.type === 'episode';
4243

43-
const pageLoaded = (pagination?: TraktClientPagination) => pagination && pagination.page >= pagination.pageCount;
44+
const pageLoaded = (pagination?: StorePagination) =>
45+
pagination && pagination?.page && pagination?.pageCount && pagination.page >= pagination.pageCount;
4446

4547
const updateRatings = (rating: TraktRating, score: TraktSyncRatingValue) => {
4648
rating.rating = score;

src/stores/data/search.store.ts

+6-5
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ import { defineStore, storeToRefs } from 'pinia';
22

33
import { reactive, ref, watch } from 'vue';
44

5-
import type { TraktClientPagination, TraktSearch, TraktSearchResult, TraktSearchType } from '@dvcol/trakt-http-client/models';
5+
import type { TraktSearch, TraktSearchResult, TraktSearchType } from '@dvcol/trakt-http-client/models';
66

7+
import type { StorePagination } from '~/models/pagination.model';
78
import type { ErrorDictionary } from '~/utils/retry.utils';
89

910
import { type ListScrollItem, ListScrollItemType } from '~/models/list-scroll.model';
@@ -44,7 +45,7 @@ export const useSearchStore = defineStore(SearchStoreConstants.Store, () => {
4445
ErrorService.registerDictionary('search', searchErrors);
4546

4647
const pageSize = ref(PageSize.p100);
47-
const pagination = ref<TraktClientPagination>();
48+
const pagination = ref<StorePagination>({});
4849

4950
const loading = ref(false);
5051
const firstLoad = ref(true);
@@ -70,7 +71,7 @@ export const useSearchStore = defineStore(SearchStoreConstants.Store, () => {
7071
const clearState = () => {
7172
types.value = DefaultSearchType;
7273
query.value = false;
73-
pagination.value = undefined;
74+
pagination.value = {};
7475
search.value = '';
7576
history.value = new Set();
7677
clearProxy(searchErrors);
@@ -121,7 +122,7 @@ export const useSearchStore = defineStore(SearchStoreConstants.Store, () => {
121122
return;
122123
}
123124
if (!search.value?.trim()) {
124-
pagination.value = undefined;
125+
pagination.value = {};
125126
searchResults.value = [];
126127
return;
127128
}
@@ -150,7 +151,7 @@ export const useSearchStore = defineStore(SearchStoreConstants.Store, () => {
150151
id: `${page}-${index}`,
151152
}));
152153

153-
pagination.value = response.pagination;
154+
pagination.value = response.pagination ?? {};
154155
searchResults.value = page ? [...searchResults.value.filter(s => s.type !== ListScrollItemType.Loading), ...data] : data;
155156
} catch (e) {
156157
Logger.error('Failed to fetch search query');

src/utils/store.utils.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { computed, onActivated, onDeactivated, onMounted, ref, type Ref, watch } from 'vue';
22

3-
import type { TraktClientPagination } from '@dvcol/trakt-http-client/models';
43
import type { ListScrollSourceItemWithDate } from '~/components/common/list/use-list-scroll';
54

5+
import type { StorePagination } from '~/models/pagination.model';
6+
67
import { ListScrollItemType } from '~/models/list-scroll.model';
78
import { useAuthSettingsStoreRefs } from '~/stores/settings/auth.store';
89
import { debounce } from '~/utils/debounce.utils';
@@ -39,7 +40,7 @@ export const useSearchFilter = <D extends string, T extends ListScrollSourceItem
3940
});
4041
});
4142

42-
export const useBelowThreshold = (threshold: Ref<number>, pagination: Ref<TraktClientPagination | undefined>) =>
43+
export const useBelowThreshold = (threshold: Ref<number>, pagination: Ref<StorePagination>) =>
4344
computed(
4445
() =>
4546
!!(
@@ -50,6 +51,9 @@ export const useBelowThreshold = (threshold: Ref<number>, pagination: Ref<TraktC
5051
),
5152
);
5253

54+
export const useBelowThreshold2 = (threshold: Ref<number>, pagination: StorePagination) =>
55+
computed(() => !!(pagination?.page && pagination?.pageCount && pagination.page !== pagination.pageCount && pagination.page < threshold.value));
56+
5357
export const useLoadingPlaceholder = <T>(pageSize: Ref<number> = ref(50)) =>
5458
computed<T[]>(() =>
5559
Array(pageSize.value)

src/utils/trakt-service.utils.ts

+9-12
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,7 @@ import { getJsonWriter } from '@dvcol/common-utils/common/save';
55

66
import { isResponseOk } from '@dvcol/trakt-http-client';
77

8-
import {
9-
TraktApiExtended,
10-
type TraktApiParamsExtended,
11-
type TraktApiParamsPagination,
12-
type TraktApiResponse,
13-
type TraktClientPagination,
14-
} from '@dvcol/trakt-http-client/models';
8+
import { TraktApiExtended, type TraktApiParamsExtended, type TraktApiParamsPagination, type TraktApiResponse } from '@dvcol/trakt-http-client/models';
159

1610
import { getCookie } from '@dvcol/web-extension-utils/chrome/cookie';
1711

@@ -24,26 +18,29 @@ import type { JsonWriterOptions } from '@dvcol/common-utils/common/save';
2418

2519
import type { CancellablePromise } from '@dvcol/common-utils/http/fetch';
2620

21+
import type { StorePagination } from '~/models/pagination.model';
22+
2723
import type { ProgressItem } from '~/models/progress.model';
2824

2925
import { PageSize } from '~/models/page-size.model';
3026

3127
import { ExternaLinks } from '~/settings/external.links';
3228

3329
import { WebConfig } from '~/settings/web.config';
30+
import { clearAssign } from '~/utils/vue.utils';
3431

3532
type PaginatedQuery = TraktApiParamsExtended & TraktApiParamsPagination;
3633
export const paginatedWriteJson = async <Q extends PaginatedQuery = PaginatedQuery, T extends RecursiveRecord = RecursiveRecord>(
3734
fetch: (query: Q) => Promise<TraktApiResponse<T>>,
3835
query: Q = { extended: TraktApiExtended.Full, pagination: { limit: PageSize.p1000 } } as Q,
3936
writerOptions?: JsonWriterOptions,
4037
cancel?: Ref<boolean>,
41-
pagination?: Partial<TraktClientPagination>,
38+
pagination?: StorePagination,
4239
): Promise<FileSystemFileHandle> => {
4340
let response$ = fetch(query);
4441
const writer = await getJsonWriter(writerOptions);
4542
let response = await response$;
46-
if (pagination) Object.assign(pagination, response.pagination);
43+
if (pagination) clearAssign(pagination, response.pagination);
4744
let data = await response.json();
4845

4946
/* eslint-disable no-await-in-loop */
@@ -52,7 +49,7 @@ export const paginatedWriteJson = async <Q extends PaginatedQuery = PaginatedQue
5249
while (!cancel?.value && response.pagination?.page !== undefined && response.pagination.page < response.pagination.pageCount) {
5350
response$ = fetch({ ...query, pagination: { ...query.pagination, page: response.pagination.page + 1 } });
5451
response = await response$;
55-
if (pagination) Object.assign(pagination, response.pagination);
52+
if (pagination) clearAssign(pagination, response.pagination);
5653
data = await response.json();
5754
await writer.write(data);
5855
}
@@ -63,14 +60,14 @@ export const paginatedWriteJson = async <Q extends PaginatedQuery = PaginatedQue
6360
return writer.handle;
6461
};
6562

66-
export type CancellableWritePromise<T> = Promise<T> & { cancel: () => Promise<T>; pagination: Partial<TraktClientPagination> };
63+
export type CancellableWritePromise<T> = Promise<T> & { cancel: () => Promise<T>; pagination: StorePagination };
6764
export const cancellablePaginatedWriteJson = <Q extends PaginatedQuery = PaginatedQuery, T extends RecursiveRecord = RecursiveRecord>(
6865
fetch: (query: Q) => Promise<TraktApiResponse<T>>,
6966
query: Q = { extended: TraktApiExtended.Full, pagination: { limit: PageSize.p1000 } } as Q,
7067
writerOptions?: JsonWriterOptions & { separator?: string },
7168
): CancellableWritePromise<FileSystemFileHandle> => {
7269
const cancel = ref(false);
73-
const pagination = reactive<Partial<TraktClientPagination>>({});
70+
const pagination = reactive<StorePagination>({});
7471
const promise = paginatedWriteJson(fetch, query, writerOptions, cancel, pagination) as CancellableWritePromise<FileSystemFileHandle>;
7572
promise.cancel = () => {
7673
cancel.value = true;

0 commit comments

Comments
 (0)