Skip to content

Commit b37f9a4

Browse files
committed
feat(release): setup release fetching
1 parent 8cdcaac commit b37f9a4

15 files changed

+200
-163
lines changed

src/components/common/debug/DebugProvider.vue

-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import { TraktService } from '~/services/trakt.service';
77
const debugProvider = ref<HTMLDivElement>();
88
99
onMounted(() => {
10-
console.info('DebugProvider mounted', debugProvider.value);
1110
Object.defineProperty(debugProvider.value, 'trakt', {
1211
value: TraktService,
1312
});

src/components/common/list/ListItem.vue

+11
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,15 @@ const props = defineProps({
5858
type: Boolean,
5959
required: false,
6060
},
61+
hideTime: {
62+
type: Boolean,
63+
required: false,
64+
},
65+
contentHeight: {
66+
type: Number,
67+
required: false,
68+
default: 1,
69+
},
6170
hover: {
6271
type: Boolean,
6372
required: false,
@@ -204,6 +213,8 @@ const onClick = () => emit('onItemClick', { item: item?.value });
204213
:item="item"
205214
:loading="loading"
206215
:hide-date="hideDate"
216+
:hide-time="hideTime"
217+
:content-height="contentHeight"
207218
:show-progress="showProgress"
208219
>
209220
<slot :item="item" :loading="loading" />

src/components/common/list/ListItemPanel.vue

+12-3
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,15 @@ const props = defineProps({
4343
type: Boolean,
4444
required: false,
4545
},
46+
hideTime: {
47+
type: Boolean,
48+
required: false,
49+
},
50+
contentHeight: {
51+
type: Number,
52+
required: false,
53+
default: 1,
54+
},
4655
showProgress: {
4756
type: Boolean,
4857
required: false,
@@ -131,16 +140,16 @@ const onTagClick = (url?: string) => {
131140
</div>
132141
<div class="content">
133142
<NSkeleton v-if="loading" text style="width: 60%" round />
134-
<NEllipsis v-else :line-clamp="1" :tooltip="tooltipOptions">{{
143+
<NEllipsis v-else :line-clamp="contentHeight" :tooltip="tooltipOptions">{{
135144
content
136145
}}</NEllipsis>
137146
</div>
138-
<NFlex v-if="date || tags?.length" size="medium" class="tags">
147+
<NFlex v-if="(!hideTime && date) || tags?.length" size="medium" class="tags">
139148
<template v-for="(tag, i) of tags" :key="`${i}-${tag.label}`">
140149
<NSkeleton v-if="loading" text style="width: 6%" />
141150
<TagLink :tag="tag" @on-click="onTagClick" />
142151
</template>
143-
<template v-if="date">
152+
<template v-if="!hideTime && date">
144153
<NSkeleton v-if="loading" text style="width: 6%" />
145154
<NTag v-else class="tag" size="small" type="default" :bordered="false">
146155
{{ date }}

src/components/common/list/ListScroll.vue

+11
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,15 @@ const props = defineProps({
4747
type: Boolean,
4848
required: false,
4949
},
50+
hideTime: {
51+
type: Boolean,
52+
required: false,
53+
},
54+
contentHeight: {
55+
type: Number,
56+
required: false,
57+
default: 1,
58+
},
5059
scrollIntoView: {
5160
type: Array as PropType<ListScrollItem['id'][]>,
5261
required: false,
@@ -173,6 +182,8 @@ const onLoadMore = (payload: { page: number; pageCount: number; pageSize: number
173182
:height="listOptions?.itemSize ?? 145"
174183
:size="items.length"
175184
:hide-date="hideDate"
185+
:hide-time="hideTime"
186+
:content-height="contentHeight"
176187
:backdrop="backdrop"
177188
:hover="hoverDate === item.date?.current?.toDateString()"
178189
:scroll-into-view="scrollIntoView?.includes(item.id)"

src/components/views/calendar/CalendarComponent.vue

+9-68
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,32 @@
11
<script lang="ts" setup>
2-
import { computed, ref, watch } from 'vue';
3-
4-
import type {
5-
VirtualListRef,
6-
VirtualListScrollToOptions,
7-
} from '~/models/list-scroll.model';
2+
import { watch } from 'vue';
83
94
import FloatingButton from '~/components/common/buttons/FloatingButton.vue';
105
import ListScroll from '~/components/common/list/ListScroll.vue';
116
import { useListScroll } from '~/components/common/list/use-list-scroll';
12-
import IconChevronDown from '~/components/icons/IconChevronDown.vue';
13-
import IconChevronUp from '~/components/icons/IconChevronUp.vue';
147
158
import { usePanelItem } from '~/components/views/panel/use-panel-item';
169
import { useCalendarStore, useCalendarStoreRefs } from '~/stores/data/calendar.store';
10+
import { useCalendar, useCenterButton } from '~/utils/calendar.utils';
1711
import { useI18n } from '~/utils/i18n.utils';
1812
import { watchUserChange } from '~/utils/store.utils';
1913
2014
const i18n = useI18n('calendar');
2115
22-
const { calendar, loading, center, filteredCalendar } = useCalendarStoreRefs();
16+
const { loading, center, filteredCalendar } = useCalendarStoreRefs();
2317
const { fetchCalendar, clearState } = useCalendarStore();
2418
2519
const list = useListScroll(filteredCalendar, 'date');
2620
27-
const centerItem = computed(() => {
28-
return list.value.find(
29-
item => item.date?.current.toLocaleDateString() === center.value.toLocaleDateString(),
30-
);
31-
});
21+
const { centerItem, centerIsToday, scrolledOut, recenterIcon, onScrollIntoOutOfView } =
22+
useCenterButton({ list, center });
3223
33-
const centerIsToday = computed(() => {
34-
return (
35-
centerItem.value?.date?.current.toLocaleDateString() ===
36-
new Date().toLocaleDateString()
37-
);
24+
const { listRef, onClick, onScrollTop, onScrollBottom, reload } = useCalendar({
25+
list,
26+
centerItem,
27+
fetchData: fetchCalendar,
3828
});
3929
40-
const listRef = ref<{ list: VirtualListRef }>();
41-
42-
const scrollTo = (
43-
options?: VirtualListScrollToOptions,
44-
index = centerItem.value?.index,
45-
) => {
46-
if (index === undefined) return;
47-
if (!listRef.value?.list) return;
48-
49-
listRef.value?.list.scrollTo({
50-
top: index * 145,
51-
...options,
52-
});
53-
};
54-
55-
const reload = async () => {
56-
const promise = fetchCalendar();
57-
// watch for loading changes and recenter
58-
const unsub = watch(list, async () => scrollTo());
59-
await promise;
60-
scrollTo();
61-
unsub();
62-
};
63-
6430
watch(center, () => reload());
6531
6632
watchUserChange({
@@ -74,31 +40,6 @@ watchUserChange({
7440
},
7541
});
7642
77-
const scrolledOut = ref(false);
78-
const scrolledDown = ref(true);
79-
const onClick = () => scrollTo({ behavior: 'smooth' });
80-
const onScrollIntoOutOfView = (_scrolled: boolean, _itemRef?: HTMLDivElement) => {
81-
scrolledOut.value = _scrolled;
82-
if (!_scrolled || !_itemRef) return;
83-
scrolledDown.value = _itemRef.getBoundingClientRect().top > 0;
84-
};
85-
const recenterIcon = computed(() =>
86-
scrolledDown.value ? IconChevronDown : IconChevronUp,
87-
);
88-
89-
const onScrollTop = async () => {
90-
const first = list.value[0];
91-
await fetchCalendar('start');
92-
93-
listRef.value?.list.scrollTo({
94-
top: (list.value.findIndex(item => item.id === first.id) - 1) * 145,
95-
});
96-
};
97-
98-
const onScrollBottom = async () => {
99-
await fetchCalendar('end');
100-
};
101-
10243
const { onItemClick } = usePanelItem();
10344
</script>
10445

src/components/views/releases/ReleasesComponent.vue

+31-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
<script lang="ts" setup>
22
import { watch } from 'vue';
33
4+
import type { TraktSearchResult } from '@dvcol/trakt-http-client/models';
5+
import type { ListScrollItem } from '~/models/list-scroll.model';
6+
47
import FloatingButton from '~/components/common/buttons/FloatingButton.vue';
58
import ListScroll from '~/components/common/list/ListScroll.vue';
69
import { useListScroll } from '~/components/common/list/use-list-scroll';
710
811
import { usePanelItem } from '~/components/views/panel/use-panel-item';
12+
import { TraktService } from '~/services/trakt.service';
913
import { useReleasesStore, useReleasesStoreRefs } from '~/stores/data/releases.store';
1014
import { useCalendar, useCenterButton } from '~/utils/calendar.utils';
1115
import { useI18n } from '~/utils/i18n.utils';
@@ -21,7 +25,7 @@ const list = useListScroll(releases, 'date');
2125
const { centerItem, centerIsToday, scrolledOut, recenterIcon, onScrollIntoOutOfView } =
2226
useCenterButton({ list, center });
2327
24-
const { onClick, onScrollTop, onScrollBottom, reload } = useCalendar({
28+
const { listRef, onClick, onScrollTop, onScrollBottom, reload } = useCalendar({
2529
list,
2630
centerItem,
2731
fetchData: fetchReleases,
@@ -41,6 +45,29 @@ watchUserChange({
4145
});
4246
4347
const { onItemClick } = usePanelItem();
48+
49+
const onMovieClick = async ({ item }: { item: ListScrollItem }) => {
50+
const lookup: TraktSearchResult[] = await TraktService.lookup({
51+
id: item.id.toString(),
52+
id_type: 'tmdb',
53+
type: 'movie',
54+
});
55+
onItemClick({
56+
item: {
57+
...item,
58+
meta: {
59+
...item.meta,
60+
ids: {
61+
...item.meta?.ids,
62+
movie: {
63+
...item.meta?.ids?.movie,
64+
trakt: lookup.pop()?.movie?.ids?.trakt,
65+
},
66+
},
67+
},
68+
},
69+
});
70+
};
4471
</script>
4572

4673
<template>
@@ -49,9 +76,10 @@ const { onItemClick } = usePanelItem();
4976
ref="listRef"
5077
:items="list"
5178
:loading="loading"
52-
backdrop
79+
:content-height="3"
80+
hide-time
5381
:scroll-into-view="centerItem?.id ? [centerItem?.id] : []"
54-
@on-item-click="onItemClick"
82+
@on-item-click="onMovieClick"
5583
@on-scroll-into-view="e => onScrollIntoOutOfView(false, e.ref)"
5684
@on-scroll-out-of-view="e => onScrollIntoOutOfView(true, e.ref)"
5785
@on-scroll-top="onScrollTop"

src/components/views/releases/ReleasesNavbar.vue

+6-12
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ defineProps({
1818
1919
const i18n = useI18n('navbar', 'releases');
2020
21-
const { center, loading, region, regions, regionLoading, types } = useReleasesStoreRefs();
21+
const { center, loading, region, regions, regionLoading, releaseType } =
22+
useReleasesStoreRefs();
2223
const { clearState, fetchRegions } = useReleasesStore();
2324
2425
const pickerValue = computed({
@@ -52,13 +53,10 @@ onBeforeMount(() => fetchRegions());
5253
<template>
5354
<NFlex class="row" align="center" justify="space-evenly" :vertical="false">
5455
<NSelect
55-
v-model:value="types"
56+
v-model:value="releaseType"
5657
class="type-select"
5758
:options="typesOptions"
5859
:to="parentElement"
59-
:max-tag-count="1"
60-
:ellipsis-tag-popover-props="{ disabled: true }"
61-
multiple
6260
/>
6361
<NDatePicker
6462
v-model:show="open"
@@ -95,19 +93,15 @@ onBeforeMount(() => fetchRegions());
9593
padding: 0 0.5rem;
9694
9795
.type-select {
98-
flex: 0 0 12rem;
96+
flex: 0 0 10rem;
9997
}
10098
10199
.region-select {
102-
flex: 0 0 12rem;
100+
flex: 1 0 10rem;
103101
}
104102
105103
.date-picker {
106-
flex: 1 1 33%;
107-
}
108-
109-
.search-input {
110-
flex: 1 1 33%;
104+
flex: 2 1 48%;
111105
}
112106
}
113107
</style>

src/components/views/settings/SettingsTabs.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ const container = ref();
145145
<SettingsFormItem
146146
:label="i18n(`label_route_${ route }`)"
147147
:warning="
148-
state && route === Route.Progress
148+
state && [Route.Progress, Route.Releases].includes(route)
149149
? i18n(`label_route_${ route }_warning`)
150150
: undefined
151151
"

src/i18n/en/settings/settings-tabs.json

+4
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@
2727
"message": "Enable the releases tab",
2828
"description": "Label for the 'Enable the releases tab' setting"
2929
},
30+
"settings__tabs__label_route_releases_warning": {
31+
"message": "Regional data is sourced from TMDb and may be incomplete or inaccurate.",
32+
"description": "Warning message for the 'Enable the releases tab' setting"
33+
},
3034
"settings__tabs__label_route_history": {
3135
"message": "Enable the history tab",
3236
"description": "Label for the 'Enable the history tab' setting"

src/services/trakt.service.ts

+15-15
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,21 @@ import { CacheRetention } from '@dvcol/common-utils/common/cache';
44
import { DateUtils } from '@dvcol/common-utils/common/date';
55
import { TmdbClient } from '@dvcol/tmdb-http-client';
66

7+
import {
8+
type TmdbApiResponse,
9+
type TmdbConfigurationCounty,
10+
type TmdbDiscoverMovieQuery,
11+
type TmdbMovieReleaseTypes,
12+
type TmdbMovieShort,
13+
type TmdbPaginatedData,
14+
type TmdbParamPagination,
15+
} from '@dvcol/tmdb-http-client/models';
716
import { isResponseOk, TraktClient } from '@dvcol/trakt-http-client';
817

918
import { TvdbClient } from '@dvcol/tvdb-http-client';
1019

1120
import type { JsonWriterOptions } from '@dvcol/common-utils/common/save';
1221
import type { CancellablePromise } from '@dvcol/common-utils/http/fetch';
13-
import type {
14-
TmdbApiResponse,
15-
TmdbConfigurationCounty,
16-
TmdbDiscoverMovieQuery,
17-
TmdbMovieReleaseTypes,
18-
TmdbMovieShort,
19-
TmdbPaginatedData,
20-
TmdbParamPagination,
21-
} from '@dvcol/tmdb-http-client/models';
2222

2323
import type {
2424
TraktApiResponse,
@@ -563,12 +563,12 @@ export class TraktService {
563563
query: TmdbDiscoverMovieQuery & TmdbParamPagination = {},
564564
): Promise<TmdbPaginatedData<TmdbMovieShort>> => {
565565
query.region = options?.region ?? query.region;
566-
query['release_date.gte'] = options?.from?.toISOString().split('T').at(0) ?? query['release_date.gte'];
567-
if (!query['release_date.gte']) query['release_date.gte'] = DateUtils.previous(7).toISOString().split('T').at(0);
568-
if (!query['release_date.gte']) throw new Error('From date is required for movie releases');
569-
query['release_date.lte'] = options?.to?.toISOString().split('T').at(0) ?? query['release_date.lte'];
570-
if (!query['release_date.lte']) query['release_date.lte'] = DateUtils.next(7).toISOString().split('T').at(0);
571-
if (!query['release_date.lte']) throw new Error('To date is required for movie releases');
566+
query[`release_date.gte`] = options?.from?.toISOString().split('T').at(0) ?? query[`release_date.gte`];
567+
if (!query[`release_date.gte`]) query[`release_date.gte`] = DateUtils.previous(7).toISOString().split('T').at(0);
568+
if (!query[`release_date.gte`]) throw new Error('From date is required for movie releases');
569+
query[`release_date.lte`] = options?.to?.toISOString().split('T').at(0) ?? query[`release_date.lte`];
570+
if (!query[`release_date.lte`]) query[`release_date.lte`] = DateUtils.next(7).toISOString().split('T').at(0);
571+
if (!query[`release_date.lte`]) throw new Error('To date is required for movie releases');
572572
query.with_release_type = options?.release ?? query.with_release_type;
573573
if (!query.with_release_type) throw new Error('Release type is required for movie releases');
574574
const response = await TraktService.tmdbClient.v3.discover.movie.cached(query, undefined, { retention: CacheRetention.Week });

0 commit comments

Comments
 (0)