Skip to content

Commit 4b8eabb

Browse files
committed
feat(progress): allows display of progress for season instead of shows
1 parent f3ba370 commit 4b8eabb

File tree

9 files changed

+142
-26
lines changed

9 files changed

+142
-26
lines changed

src/components/common/list/ListItemPanel.vue

+14-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ import TagLink from '~/components/common/buttons/TagLink.vue';
1616
import ProgressTooltip from '~/components/common/tooltip/ProgressTooltip.vue';
1717
import { type ListScrollItem, type ShowProgress } from '~/models/list-scroll.model';
1818
19+
import { ProgressType } from '~/models/progress-type.model';
1920
import { useShowStore } from '~/stores/data/show.store';
21+
import { useExtensionSettingsStoreRefs } from '~/stores/settings/extension.store';
2022
import { useLinksStore } from '~/stores/settings/links.store';
2123
import { shortTime } from '~/utils/date.utils';
2224
import { useI18n } from '~/utils/i18n.utils';
@@ -84,6 +86,16 @@ const progress = computed<ShowProgress | undefined>(() => {
8486
return getShowWatchedProgress(id, cacheOptions).value;
8587
});
8688
89+
const { progressType } = useExtensionSettingsStoreRefs();
90+
91+
const percentage = computed(() => {
92+
if (!progress.value) return;
93+
if (progressType.value === ProgressType.Season) {
94+
if (progress.value?.lastAired) return progress.value?.lastAired?.percentage;
95+
}
96+
return progress.value?.percentage;
97+
});
98+
8799
const innerContainer = ref();
88100
const tooltipOptions = computed<PopoverProps>(() => ({
89101
to: innerContainer.value,
@@ -140,11 +152,11 @@ const onTagClick = (url?: string) => {
140152
<NProgress
141153
class="line"
142154
:data-show-id="progress?.id"
143-
:data-percentage="progress?.percentage"
155+
:data-percentage="percentage"
144156
:theme-overrides="{
145157
railHeight: 'var(--rail-height)',
146158
}"
147-
:percentage="progress?.percentage ?? 0"
159+
:percentage="percentage ?? 0"
148160
:show-indicator="false"
149161
color="var(--trakt-red-dark)"
150162
/>

src/components/common/tooltip/ProgressTooltip.vue

+47-3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import {
1111
type ShowProgressTypes,
1212
} from '~/models/list-scroll.model';
1313
14+
import { ProgressType } from '~/models/progress-type.model';
15+
import { useExtensionSettingsStoreRefs } from '~/stores/settings/extension.store';
1416
import { useI18n } from '~/utils/i18n.utils';
1517
1618
const props = defineProps({
@@ -31,16 +33,32 @@ const props = defineProps({
3133
3234
const { progress, type, disabled } = toRefs(props);
3335
36+
const { progressType } = useExtensionSettingsStoreRefs();
37+
3438
const percentage = computed(() => {
3539
if (!progress?.value) return;
40+
if (
41+
progressType.value === ProgressType.Season &&
42+
'lastAired' in progress.value &&
43+
progress.value?.lastAired
44+
) {
45+
return Math.round(progress.value.lastAired.percentage);
46+
}
3647
if (!('percentage' in progress.value)) return;
3748
return Math.round(progress.value?.percentage);
3849
});
3950
4051
const remaining = computed(() => {
4152
if (!progress?.value) return;
42-
if (!('aired' in progress.value)) return;
4353
if (!progress.value.completed) return;
54+
if (
55+
progressType.value === ProgressType.Season &&
56+
'lastAired' in progress.value &&
57+
progress.value?.lastAired
58+
) {
59+
return progress.value.lastAired.aired - progress.value.lastAired.completed;
60+
}
61+
if (!('aired' in progress.value)) return;
4462
return progress.value.aired - progress.value.completed;
4563
});
4664
@@ -51,6 +69,32 @@ const isCount = computed(() => {
5169
return typeof progress.value?.aired === 'number';
5270
});
5371
72+
const completed = computed(() => {
73+
if (!progress?.value) return '-';
74+
if (
75+
progressType.value === ProgressType.Season &&
76+
'lastAired' in progress.value &&
77+
progress.value?.lastAired
78+
) {
79+
return progress.value.lastAired.completed;
80+
}
81+
if (!('completed' in progress.value)) return '-';
82+
return progress.value.completed;
83+
});
84+
85+
const aired = computed(() => {
86+
if (!progress?.value) return '-';
87+
if (
88+
progressType.value === ProgressType.Season &&
89+
'lastAired' in progress.value &&
90+
progress.value?.lastAired
91+
) {
92+
return progress.value.lastAired.aired;
93+
}
94+
if (!('aired' in progress.value)) return '-';
95+
return progress.value.aired;
96+
});
97+
5498
const i18n = useI18n('common', 'tooltip', 'progress');
5599
</script>
56100

@@ -63,9 +107,9 @@ const i18n = useI18n('common', 'tooltip', 'progress');
63107
<slot name="label">
64108
<NFlex v-if="progress && isCount" vertical align="flex-end">
65109
<div v-if="'aired' in progress">
66-
<span class="metric">{{ progress?.completed ?? '-' }}</span>
110+
<span class="metric">{{ completed }}</span>
67111
<span> / </span>
68-
<span class="metric">{{ progress?.aired ?? '-' }}</span>
112+
<span class="metric">{{ aired }}</span>
69113
<span>&nbsp;</span>
70114
<span>{{ i18n('episodes') }}</span>
71115
</div>

src/components/views/settings/SettingsTabs.vue

+43-19
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { type Component, computed, h, onBeforeMount, ref } from 'vue';
55
66
import SettingsFormItem from '~/components/views/settings/SettingsFormItem.vue';
77
import { pageSizeOptions } from '~/models/page-size.model';
8+
import { ProgressType } from '~/models/progress-type.model';
89
import { Route } from '~/models/router.model';
910
import { useHistoryStoreRefs } from '~/stores/data/history.store';
1011
import {
@@ -33,6 +34,7 @@ const {
3334
defaultTab,
3435
loadLists,
3536
loadListsPageSize,
37+
progressType,
3638
} = useExtensionSettingsStoreRefs();
3739
3840
const { getIcon, fetchLists } = useListsStore();
@@ -82,6 +84,11 @@ const tabsOptions = computed(() =>
8284
})),
8385
);
8486
87+
const progressTypeOptions = Object.values(ProgressType).map(pt => ({
88+
label: i18n(`type__${pt}`, 'progress'),
89+
value: pt,
90+
}));
91+
8592
const disabled = computed(
8693
() => enabledTabs.value.filter(([_, state]) => state).length <= 1,
8794
);
@@ -134,26 +141,39 @@ const container = ref();
134141
</SettingsFormItem>
135142

136143
<!-- Enable tabs -->
137-
<SettingsFormItem
138-
v-for="[route, state] of enabledTabs"
139-
:key="route"
140-
:label="i18n(`label_route_${ route }`)"
141-
:warning="
142-
state && route === Route.Progress
143-
? i18n(`label_route_${ route }_warning`)
144-
: undefined
145-
"
146-
>
147-
<NSwitch
148-
:value="state"
149-
class="form-switch"
150-
:disabled="state && disabled"
151-
@update:value="toggleTab(route)"
144+
<template v-for="[route, state] of enabledTabs" :key="route">
145+
<SettingsFormItem
146+
:label="i18n(`label_route_${ route }`)"
147+
:warning="
148+
state && route === Route.Progress
149+
? i18n(`label_route_${ route }_warning`)
150+
: undefined
151+
"
152152
>
153-
<template #checked>{{ i18n('on', 'common', 'button') }}</template>
154-
<template #unchecked>{{ i18n('off', 'common', 'button') }}</template>
155-
</NSwitch>
156-
</SettingsFormItem>
153+
<NSwitch
154+
:value="state"
155+
class="form-switch"
156+
:disabled="state && disabled"
157+
@update:value="toggleTab(route)"
158+
>
159+
<template #checked>{{ i18n('on', 'common', 'button') }}</template>
160+
<template #unchecked>{{ i18n('off', 'common', 'button') }}</template>
161+
</NSwitch>
162+
</SettingsFormItem>
163+
164+
<!-- Progress Type -->
165+
<SettingsFormItem
166+
v-if="state && route === Route.Progress"
167+
:label="i18n('label_progress_type')"
168+
>
169+
<NSelect
170+
v-model:value="progressType"
171+
class="progress-type"
172+
:to="container"
173+
:options="progressTypeOptions"
174+
/>
175+
</SettingsFormItem>
176+
</template>
157177

158178
<!-- Page Size -->
159179
<SettingsFormItem :label="i18n('label_load_lists_page_size')">
@@ -222,4 +242,8 @@ const container = ref();
222242
.list-select {
223243
width: 12.5rem;
224244
}
245+
246+
.progress-type {
247+
width: 6rem;
248+
}
225249
</style>

src/i18n/en/progress/progress.json

+8
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,13 @@
66
"progress__logout__trakt": {
77
"message": "Please sign into trakt.tv directly.",
88
"description": "Login message for trakt.tv auth sessions."
9+
},
10+
"progress__type__show": {
11+
"message": "Show",
12+
"description": "Type label for a show."
13+
},
14+
"progress__type__season": {
15+
"message": "Season",
16+
"description": "Type label for a movie."
917
}
1018
}

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

+4
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,9 @@
5454
"settings__tabs__label_load_lists_page_size": {
5555
"message": "Page size for lists",
5656
"description": "Label for the 'Page size for lists' setting"
57+
},
58+
"settings__tabs__label_progress_type": {
59+
"message": "Progress type",
60+
"description": "Label for the 'Progress type' setting"
5761
}
5862
}

src/models/list-scroll.model.ts

+1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ export type ShowProgress = BaseTraktProgress & {
7171
seasons: SeasonProgress[];
7272
percentage: number;
7373
finished: boolean;
74+
lastAired?: SeasonProgress;
7475
};
7576

7677
export const ListScrollItemType = {

src/models/progress-type.model.ts

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export const ProgressType = {
2+
Show: 'show',
3+
Season: 'season',
4+
} as const;
5+
6+
export type ProgressTypes = (typeof ProgressType)[keyof typeof ProgressType];

src/stores/data/show.store.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,9 @@ const parseProgressDate = (
4949
};
5050

5151
const watchProgressToListProgress = (progress: TraktWatchedProgress | TraktCollectionProgress, id: string | number): ShowProgress => {
52-
let completed = 0;
53-
let aired = 0;
52+
let completed: ShowProgress['completed'] = 0;
53+
let aired: ShowProgress['aired'] = 0;
54+
let lastAired: ShowProgress['lastAired'];
5455

5556
const result = {
5657
id,
@@ -75,6 +76,8 @@ const watchProgressToListProgress = (progress: TraktWatchedProgress | TraktColle
7576
return { ...episode, date };
7677
});
7778

79+
if (_season.aired && (!lastAired || _season.number > lastAired.number)) lastAired = _season;
80+
7881
return _season;
7982
}),
8083
};
@@ -84,6 +87,7 @@ const watchProgressToListProgress = (progress: TraktWatchedProgress | TraktColle
8487
aired,
8588
percentage: aired ? ((completed ?? 0) / aired) * 100 : 0,
8689
finished: !!aired && completed === aired,
90+
lastAired,
8791
};
8892
};
8993

src/stores/settings/extension.store.ts

+13
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { CacheRetention } from '@dvcol/common-utils/common/cache';
22
import { defineStore, storeToRefs } from 'pinia';
33
import { computed, reactive, ref, toRaw } from 'vue';
44

5+
import { ProgressType, type ProgressTypes } from '~/models/progress-type.model';
56
import { Route } from '~/models/router.model';
67
import { TraktService } from '~/services/trakt.service';
78
import { type ListEntity } from '~/stores/data/list.store';
@@ -38,6 +39,7 @@ type ExtensionSettings = {
3839
restorePanel: boolean;
3940
loadLists: ListEntity[];
4041
loadListsPageSize: number;
42+
progressType: ProgressTypes;
4143
};
4244

4345
const ExtensionSettingsConstants = {
@@ -54,13 +56,15 @@ export const useExtensionSettingsStore = defineStore(ExtensionSettingsConstants.
5456
const loadListsPageSize = ref<ExtensionSettings['loadListsPageSize']>(500);
5557
const defaultTab = ref<Route>(Route.Calendar);
5658
const initialized = ref<Promise<boolean>>();
59+
const progressType = ref<ExtensionSettings['progressType']>(ProgressType.Show);
5760

5861
const clearState = () => {
5962
Object.assign(cacheRetention, DefaultCacheRetention);
6063
Object.assign(routeDictionary, DefaultRoutes);
6164
restoreRoute.value = true;
6265
restorePanel.value = false;
6366
loadLists.value = [];
67+
progressType.value = ProgressType.Show;
6468
};
6569

6670
const saveState = debounce(
@@ -72,6 +76,7 @@ export const useExtensionSettingsStore = defineStore(ExtensionSettingsConstants.
7276
restorePanel: restorePanel.value,
7377
loadLists: loadLists.value,
7478
loadListsPageSize: loadListsPageSize.value,
79+
progressType: progressType.value,
7580
}),
7681
500,
7782
);
@@ -93,6 +98,7 @@ export const useExtensionSettingsStore = defineStore(ExtensionSettingsConstants.
9398
if (restored?.restorePanel !== undefined) restorePanel.value = restored.restorePanel;
9499
if (restored?.loadLists !== undefined) loadLists.value = Object.values(restored.loadLists);
95100
if (restored?.loadListsPageSize !== undefined) loadListsPageSize.value = restored.loadListsPageSize;
101+
if (restored?.progressType !== undefined) progressType.value = restored.progressType;
96102
};
97103

98104
const saveDefaultTab = debounce(() => storage.sync.set(ExtensionSettingsConstants.LocalDefaultTab, defaultTab.value), 500);
@@ -168,6 +174,13 @@ export const useExtensionSettingsStore = defineStore(ExtensionSettingsConstants.
168174
get: () => cacheRetention.tmdb,
169175
set: (value: number) => setRetention({ tmdb: value }),
170176
}),
177+
progressType: computed({
178+
get: () => progressType.value,
179+
set: (value: ExtensionSettings['progressType']) => {
180+
progressType.value = value;
181+
saveState().catch(err => logger.error('Failed to save progress type extension settings', { value, err }));
182+
},
183+
}),
171184
};
172185
});
173186

0 commit comments

Comments
 (0)