Skip to content

Commit a24775b

Browse files
committed
feat(list): create lists store and connect navbar
1 parent 2bc08fd commit a24775b

File tree

14 files changed

+282
-126
lines changed

14 files changed

+282
-126
lines changed

src/components/common/list/ListScroll.model.ts

+9-5
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,20 @@ export type VirtualListProps = {
1717
export type OnScroll = (listRef: Ref<VirtualListRef | undefined>) => void;
1818
export type OnUpdated = (listRef: Ref<VirtualListRef | undefined>) => void;
1919

20-
export type ListScrollItem = {
21-
id: string | number | 'load-more';
22-
index: number;
23-
24-
type?: 'movie' | 'show' | 'season' | 'episode';
20+
export type ListScrollSourceItem = {
21+
id: string | number;
2522

2623
movie?: TraktMovie<'short'>;
2724
show?: TraktShow<'short'>;
2825
season?: TraktSeason<'short'>;
2926
episode?: TraktEpisode<'short'>;
27+
};
28+
29+
export type ListScrollItem = ListScrollSourceItem & {
30+
id: string | number | 'load-more';
31+
index: number;
32+
33+
type?: 'movie' | 'show' | 'season' | 'episode';
3034

3135
poster?: Ref<string | undefined>;
3236

src/components/common/list/useListScroll.ts

+4-15
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,17 @@ import { computed } from 'vue';
22

33
import type { Ref } from 'vue';
44

5-
import type { ListScrollItem } from '~/components/common/list/ListScroll.model';
5+
import type { ListScrollItem, ListScrollSourceItem } from '~/components/common/list/ListScroll.model';
66
import type { TraktClientPagination } from '~/models/trakt/trakt-client.model';
7-
import type { TraktEpisode } from '~/models/trakt/trakt-episode.model';
8-
import type { TraktMovie } from '~/models/trakt/trakt-movie.model';
9-
import type { TraktSeason } from '~/models/trakt/trakt-season.model';
10-
import type { TraktShow } from '~/models/trakt/trakt-show.model';
117

12-
export type ListScrollSourceItems<T extends string> = {
13-
id: number;
8+
export type ListScrollSourceItemWithDate<T extends string> = ListScrollSourceItem & Record<T, string | number | Date>;
149

15-
movie?: TraktMovie<'short'>;
16-
show?: TraktShow<'short'>;
17-
season?: TraktSeason<'short'>;
18-
episode?: TraktEpisode<'short'>;
19-
} & Record<T, string | number | Date>;
20-
21-
export const useListScroll = <T extends string>(items: Ref<ListScrollSourceItems<T>[]>, dateProp?: T) =>
10+
export const useListScroll = <T extends string>(items: Ref<ListScrollSourceItemWithDate<T>[]>, dateProp?: T) =>
2211
computed<ListScrollItem[]>(() => {
2312
const array = items.value;
2413
if (!array.length) return [];
2514
return array.map((item, index) => {
26-
const _item: ListScrollItem = { ...item, index, loading: item.id < 0 };
15+
const _item: ListScrollItem = { ...item, index, loading: typeof item.id === 'number' && item.id < 0 };
2716

2817
if ('movie' in _item) _item.type = 'movie';
2918
else if ('episode' in _item) _item.type = 'episode';

src/components/views/history/HistoryComponent.vue

+3-21
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script lang="ts" setup>
2-
import { onActivated, onDeactivated, onMounted, ref, watch } from 'vue';
2+
import { ref } from 'vue';
33
44
import type {
55
OnScroll,
@@ -11,34 +11,16 @@ import FloatingButton from '~/components/common/buttons/FloatingButton.vue';
1111
import ListScroll from '~/components/common/list/ListScroll.vue';
1212
import { addLoadMore, useListScroll } from '~/components/common/list/useListScroll';
1313
import { useHistoryStore, useHistoryStoreRefs } from '~/stores/data/history.store';
14-
import { useUserSettingsStoreRefs } from '~/stores/settings/user.store';
1514
import { useI18n } from '~/utils';
15+
import { watchUserChange } from '~/utils/store.utils';
1616
1717
const { filteredHistory, pagination, loading, pageSize, belowThreshold, searchHistory } =
1818
useHistoryStoreRefs();
1919
const { fetchHistory, clearState } = useHistoryStore();
2020
21-
const { user } = useUserSettingsStoreRefs();
22-
2321
const i18n = useI18n('history');
2422
25-
const active = ref(false);
26-
27-
onActivated(() => {
28-
active.value = true;
29-
fetchHistory();
30-
});
31-
32-
onDeactivated(() => {
33-
active.value = false;
34-
});
35-
36-
onMounted(() => {
37-
watch(user, () => {
38-
if (active.value) fetchHistory();
39-
else clearState();
40-
});
41-
});
23+
watchUserChange(fetchHistory, clearState);
4224
4325
const list = useListScroll(filteredHistory, 'watched_at');
4426

src/components/views/history/HistoryNavbar.vue

+3-15
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
11
<script setup lang="ts">
22
import { NDatePicker, NFlex, NIcon, NInput } from 'naive-ui';
33
4-
import { computed, defineProps, ref, watch } from 'vue';
4+
import { computed, defineProps } from 'vue';
55
66
import NavbarPageSizeSelect from '~/components/common/navbar/NavbarPageSizeSelect.vue';
77
import IconLoop from '~/components/icons/IconLoop.vue';
88
99
import { useHistoryStore, useHistoryStoreRefs } from '~/stores/data/history.store';
1010
import { useI18n } from '~/utils';
1111
import { debounce } from '~/utils/debounce.utils';
12+
import { useDebouncedSearch } from '~/utils/store.utils';
1213
1314
const i18n = useI18n('navbar');
1415
1516
const { searchHistory, historyEnd, historyStart, pageSize } = useHistoryStoreRefs();
1617
const { setHistoryRange } = useHistoryStore();
1718
18-
const debouncedSearch = ref(searchHistory.value);
19+
const debouncedSearch = useDebouncedSearch(searchHistory);
1920
2021
defineProps({
2122
parentElement: {
@@ -24,19 +25,6 @@ defineProps({
2425
},
2526
});
2627
27-
watch(searchHistory, () => {
28-
if (searchHistory.value !== debouncedSearch.value) {
29-
debouncedSearch.value = searchHistory.value;
30-
}
31-
});
32-
33-
watch(
34-
debouncedSearch,
35-
debounce(() => {
36-
searchHistory.value = debouncedSearch.value;
37-
}, 350),
38-
);
39-
4028
const pickerValues = computed<[number, number] | null>(() => {
4129
if (!historyStart.value || !historyEnd.value) return null;
4230
return [historyStart.value.getTime(), historyEnd.value.getTime()];

src/components/views/watchlist/WatchlistNavbar.vue

+32-14
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,65 @@
11
<script setup lang="ts">
22
import { NFlex, NIcon, NInput, NSelect } from 'naive-ui';
33
4-
import { defineProps, ref } from 'vue';
4+
import { computed, defineProps } from 'vue';
55
66
import NavbarPageSizeSelect from '~/components/common/navbar/NavbarPageSizeSelect.vue';
77
import IconLoop from '~/components/icons/IconLoop.vue';
8+
import {
9+
useListsStore,
10+
useListsStoreRefs,
11+
useListStoreRefs,
12+
} from '~/stores/data/list.store';
813
import { useI18n } from '~/utils';
14+
import { useDebouncedSearch, watchUserChange } from '~/utils/store.utils';
915
10-
const i18n = useI18n('navbar');
16+
const i18n = useI18n('navbar_list');
1117
12-
const activeList = ref('list-watchlist');
13-
const listOptions = [
14-
{ label: 'Movie collection', value: 'collection-movie' },
15-
{ label: 'TV collection', value: 'collection-tv' },
16-
{ label: 'Watchlist', value: 'list-watchlist' },
17-
{ label: 'Personal', value: 'list-personal' },
18-
{ label: 'Collaboration', value: 'list-collaboration' },
19-
];
18+
const { pageSize, searchList } = useListStoreRefs();
2019
21-
const pageSize = ref(100);
20+
const { loading, lists, activeList } = useListsStoreRefs();
21+
const { fetchLists, clearState } = useListsStore();
2222
23-
const debouncedSearch = ref('');
23+
const listOptions = computed(() =>
24+
lists.value.map((list, i) => ({
25+
label: ['collection', 'watchlist'].includes(list.type) ? i18n(list.name) : list.name,
26+
value: JSON.stringify(list),
27+
})),
28+
);
29+
30+
const selectValue = computed({
31+
get: () => JSON.stringify(activeList.value),
32+
set: selected => {
33+
activeList.value = JSON.parse(selected);
34+
},
35+
});
36+
37+
const debouncedSearch = useDebouncedSearch(searchList);
2438
2539
defineProps({
2640
parentElement: {
2741
type: HTMLElement,
2842
required: false,
2943
},
3044
});
45+
46+
watchUserChange(fetchLists, clearState);
3147
</script>
3248

3349
<template>
3450
<NFlex class="row" align="center" justify="center" :vertical="false">
3551
<NSelect
36-
v-model:value="activeList"
52+
v-model:value="selectValue"
3753
class="list-select"
3854
:options="listOptions"
3955
:to="parentElement"
56+
:loading="loading"
57+
filterable
4058
/>
4159
<NInput
4260
v-model:value="debouncedSearch"
4361
class="search-input"
44-
:placeholder="i18n('search')"
62+
:placeholder="i18n('search', 'navbar')"
4563
autosize
4664
clearable
4765
>

src/i18n/en/navbar/navbar-list.json

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"navbar_list__list_type__watchlist": {
3+
"message": "Watchlist",
4+
"description": "Label for the watchlist list type"
5+
},
6+
"navbar_list__list_type__collection_movie": {
7+
"message": "Movie collection",
8+
"description": "Label for the movie collection list type"
9+
},
10+
"navbar_list__list_type__collection_show": {
11+
"message": "Show collection",
12+
"description": "Label for the show collection list type"
13+
}
14+
}

src/models/trakt/trakt-collection.model.ts

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { TraktApiExtended, TraktApiParamsExtended } from '~/models/trakt/trakt-client.model';
12
import type { Any, EntityTypes, Short } from '~/models/trakt/trakt-entity.model';
23
import type { TraktEpisode } from '~/models/trakt/trakt-episode.model';
34
import type { TraktMovie } from '~/models/trakt/trakt-movie.model';
@@ -132,3 +133,7 @@ export type TraktCollectionRemoved = {
132133
episodes: Pick<TraktEpisode, 'ids'>[];
133134
};
134135
};
136+
137+
export type TraktCollectionGetQuery = {
138+
type: 'movies' | 'shows';
139+
} & TraktApiParamsExtended<typeof TraktApiExtended.Full | typeof TraktApiExtended.Metadata>;

src/models/trakt/trakt-watchlist.model.ts

+9
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { TraktApiExtended, TraktApiParamsExtended, TraktApiParamsPagination } from '~/models/trakt/trakt-client.model';
12
import type { Any, EntityTypes } from '~/models/trakt/trakt-entity.model';
23
import type { TraktEpisode } from '~/models/trakt/trakt-episode.model';
34
import type { TraktList } from '~/models/trakt/trakt-list.model';
@@ -83,3 +84,11 @@ export type TraktWatchlistRemoved = {
8384
item_count: number;
8485
};
8586
};
87+
88+
export type TraktWatchlistGetQuery = {
89+
/** Filter for a specific item type */
90+
type?: 'movies' | 'shows' | 'seasons' | 'episodes';
91+
/** How to sort (only if type is also sent) */
92+
sort?: 'rank' | 'added' | 'released' | 'title';
93+
} & TraktApiParamsExtended<typeof TraktApiExtended.Full> &
94+
TraktApiParamsPagination;

src/services/trakt-client/api/endpoints/lists.endpoint.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ export const lists = {
170170
/** Trakt ID (i.e. 15) */
171171
id: number;
172172
/** Filter for a specific item type */
173-
type?: 'movie' | 'show' | 'season' | 'episode' | 'perso';
173+
type?: ('movie' | 'show' | 'season' | 'episode' | 'person') | ('movie' | 'show' | 'season' | 'episode' | 'person')[];
174174
} & TraktApiParamsPagination &
175175
TraktApiParamsExtended<typeof TraktApiExtended.Full>,
176176
TraktListItem[]
@@ -187,6 +187,7 @@ export const lists = {
187187
},
188188
},
189189
},
190+
transform: params => (Array.isArray(params.type) ? { ...params, type: params.type.join(',') as never } : params),
190191
}),
191192
/**
192193
* Returns all top level comments for a list.

src/services/trakt-client/api/endpoints/sync.endpoint.ts

+18-18
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
import type { TraktCollection, TraktCollectionAdded, TraktCollectionRemoved, TraktCollectionRequest } from '~/models/trakt/trakt-collection.model';
1+
import type {
2+
TraktCollection,
3+
TraktCollectionAdded,
4+
TraktCollectionGetQuery,
5+
TraktCollectionRemoved,
6+
TraktCollectionRequest,
7+
} from '~/models/trakt/trakt-collection.model';
28
import type {
39
TraktFavoriteAdded,
410
TraktFavoriteItem,
@@ -18,7 +24,13 @@ import type { TraktListReordered } from '~/models/trakt/trakt-list.model';
1824
import type { TraktRating, TraktRatingAdded, TraktRatingRemoved, TraktRatingRequest } from '~/models/trakt/trakt-rating.model';
1925
import type { TraktSyncActivities, TraktSyncProgress, TraktSyncRequest, TraktSyncUpdateRequest } from '~/models/trakt/trakt-sync.model';
2026
import type { TraktWatched } from '~/models/trakt/trakt-watched.model';
21-
import type { TraktWatchlist, TraktWatchlistAdded, TraktWatchlistList, TraktWatchlistRemoved } from '~/models/trakt/trakt-watchlist.model';
27+
import type {
28+
TraktWatchlist,
29+
TraktWatchlistAdded,
30+
TraktWatchlistGetQuery,
31+
TraktWatchlistList,
32+
TraktWatchlistRemoved,
33+
} from '~/models/trakt/trakt-watchlist.model';
2234

2335
import { TraktApiExtended, type TraktApiParamsExtended, type TraktApiParamsPagination, TraktClientEndpoint } from '~/models/trakt/trakt-client.model';
2436
import { HttpMethod } from '~/utils/http.utils';
@@ -181,13 +193,10 @@ export const sync = {
181193
*
182194
* @extended true - {@link TraktApiExtended.Full}, {@link TraktApiExtended.Metadata}
183195
* @auth required
196+
*
197+
* @see [get-collection]{@link https://trakt.docs.apiary.io/#reference/sync/get-collection/get-collection}
184198
*/
185-
get: new TraktClientEndpoint<
186-
{
187-
type: 'movies' | 'shows';
188-
} & TraktApiParamsExtended<typeof TraktApiExtended.Full | typeof TraktApiExtended.Metadata>,
189-
TraktCollection[]
190-
>({
199+
get: new TraktClientEndpoint<TraktCollectionGetQuery, TraktCollection[]>({
191200
method: HttpMethod.GET,
192201
url: '/sync/collection/:type',
193202
opts: {
@@ -493,16 +502,7 @@ export const sync = {
493502
*
494503
* @see [get-watchlist]{@link https://trakt.docs.apiary.io/#reference/sync/get-watchlist/get-watchlist}
495504
*/
496-
get: new TraktClientEndpoint<
497-
{
498-
/** Filter for a specific item type */
499-
type?: 'movies' | 'shows' | 'seasons' | 'episodes';
500-
/** How to sort (only if type is also sent) */
501-
sort?: 'rank' | 'added' | 'released' | 'title';
502-
} & TraktApiParamsExtended<typeof TraktApiExtended.Full> &
503-
TraktApiParamsPagination,
504-
TraktWatchlist[]
505-
>({
505+
get: new TraktClientEndpoint<TraktWatchlistGetQuery, TraktWatchlist[]>({
506506
method: HttpMethod.GET,
507507
url: '/sync/watchlist/:type/:sort',
508508
opts: {

src/services/trakt-client/api/endpoints/users.endpoint.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -894,7 +894,7 @@ export const users = {
894894
/** List Trakt ID or Trakt slug */
895895
list_id: string;
896896
/** Type of list items */
897-
type?: 'movies' | 'shows' | 'seasons' | 'episodes' | 'person';
897+
type?: ('movies' | 'shows' | 'seasons' | 'episodes' | 'person') | ('movies' | 'shows' | 'seasons' | 'episodes' | 'person')[];
898898
} & TraktApiParamsExtended<typeof TraktApiExtended.Full> &
899899
TraktApiParamsPagination,
900900
TraktListItem[]
@@ -916,6 +916,7 @@ export const users = {
916916
},
917917
},
918918
},
919+
transform: params => (Array.isArray(params.type) ? { ...params, type: params.type.join(',') as never } : params),
919920
}),
920921
/**
921922
* Add one or more items to a personal list. Items can be movies, shows, seasons, episodes, or people.

0 commit comments

Comments
 (0)