Skip to content

Commit 55dfa9f

Browse files
committed
feat(panel): adds multi rating support & rework linking
1 parent 8bbf577 commit 55dfa9f

21 files changed

+609
-311
lines changed

src/components/common/buttons/TitleLink.vue

+5-5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { NH2 } from 'naive-ui';
33
44
import { type Component, type PropType, useAttrs } from 'vue';
55
6+
import { useLinksStore } from '~/stores/settings/links.store';
7+
68
defineProps({
79
component: {
810
type: Object as PropType<Component>,
@@ -15,16 +17,14 @@ defineProps({
1517
},
1618
});
1719
18-
const emit = defineEmits<{
19-
(e: 'onClick', href?: string): void;
20-
}>();
21-
2220
const attrs = useAttrs() as Record<keyof HTMLAnchorElement, string> | undefined;
2321
22+
const { openTab } = useLinksStore();
23+
2424
const onTitleClick = (e: MouseEvent) => {
2525
e.preventDefault();
2626
e.stopPropagation();
27-
emit('onClick', attrs?.href);
27+
openTab(attrs?.href);
2828
};
2929
</script>
3030

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<template>
2+
<svg
3+
width="800px"
4+
height="800px"
5+
viewBox="0 0 1024 1024"
6+
xmlns="http://www.w3.org/2000/svg"
7+
>
8+
<circle cx="512" cy="512" r="512" style="fill: #2e51a2" />
9+
<path
10+
d="M432.49 410.61V590.3l-44.86-.06V479l-43.31 51.29-42.43-52.44-.43 112.75H256V410.65h47l39.79 54.29 43-54.31zm184.06 44.14.53 135.15h-50.45l-.17-61.25h-59.73c1.49 10.65 4.48 27 8.9 38 3.31 8.13 6.36 16 12.44 24.06l-36.37 24c-7.45-13.57-13.27-28.52-18.73-44.42a198.31 198.31 0 0 1-10.82-46.49c-1.81-16-2.07-31.38 2.28-47.19a83.37 83.37 0 0 1 24.77-39.81c6.68-6.25 16-10.67 23.47-14.66s15.85-5.63 23.62-7.66a158 158 0 0 1 25.41-3.9c8.49-.73 23.62-1.41 51-.6l11.63 37.31h-58.78c-12.65.17-18.73 0-28.61 4.46a47.7 47.7 0 0 0-27.26 41l56.81.7.81-38.61h49.26zM701.72 410v141.35L768 552l-9.17 37.87H656.28V409.33z"
11+
style="fill: #fff"
12+
/>
13+
</svg>
14+
</template>

src/components/views/panel/MoviePanel.vue

+6-62
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,14 @@
11
<script setup lang="ts">
22
import { deCapitalise } from '@dvcol/common-utils/common/string';
3-
4-
import {
5-
TraktRatingType,
6-
type TraktSyncRatingValue,
7-
} from '@dvcol/trakt-http-client/models';
83
import { NFlex, NSkeleton } from 'naive-ui';
94
import { computed, onMounted, toRefs, watch } from 'vue';
105
116
import TitleLink from '~/components/common/buttons/TitleLink.vue';
127
import MoviePanelButtons from '~/components/views/panel/MoviePanelButtons.vue';
138
import MoviePanelDetails from '~/components/views/panel/MoviePanelDetails.vue';
149
import MoviePanelOverview from '~/components/views/panel/MoviePanelOverview.vue';
10+
import PanelMovieStatistics from '~/components/views/panel/PanelMovieStatistics.vue';
1511
import PanelPoster from '~/components/views/panel/PanelPoster.vue';
16-
import PanelStatistics from '~/components/views/panel/PanelStatistics.vue';
1712
import {
1813
PanelButtonsOption,
1914
type PanelButtonsOptions,
@@ -30,10 +25,8 @@ import {
3025
useListStore,
3126
} from '~/stores/data/list.store';
3227
import { useMovieStore, useMovieStoreRefs } from '~/stores/data/movie.store';
33-
import { useRatingsStore } from '~/stores/data/ratings.store';
3428
import { useWatchingStore, useWatchingStoreRefs } from '~/stores/data/watching.store';
3529
import { useExtensionSettingsStoreRefs } from '~/stores/settings/extension.store';
36-
import { useLinksStore } from '~/stores/settings/links.store';
3730
import { useI18n } from '~/utils/i18n.utils';
3831
import {
3932
isWatchingMovie,
@@ -103,7 +96,7 @@ const {
10396
fetchAll,
10497
} = useListStore();
10598
106-
const { loadLists, loadListsPageSize, enableRatings } = useExtensionSettingsStoreRefs();
99+
const { loadLists, loadListsPageSize } = useExtensionSettingsStoreRefs();
107100
108101
const shouldFetchLists = computed(() => {
109102
if (!myLists.value?.length) return false;
@@ -256,48 +249,8 @@ const onCheckin = async (cancel: boolean) => {
256249
await fetchMovieWatched(true);
257250
};
258251
259-
const { openTab } = useLinksStore();
260-
261-
const { loadRatings, getRatings, getLoading, addRating, removeRating } =
262-
useRatingsStore();
263-
264-
const scoreLoading = computed(() => getLoading(TraktRatingType.Movies));
265-
266-
const score = computed(() => {
267-
if (!movieId.value) return;
268-
return getRatings(TraktRatingType.Movies, movieId.value);
269-
});
270-
271-
const ratingUrl = computed(() => {
272-
if (!movie.value?.ids?.slug) return;
273-
return ResolveExternalLinks.trakt.item({
274-
type: 'movies',
275-
slug: movie.value.ids.slug,
276-
suffix: '/stats',
277-
});
278-
});
279-
280-
const onScoreEdit = async (_score: TraktSyncRatingValue) => {
281-
if (!movie.value?.ids?.trakt) return;
282-
return (_score ? addRating : removeRating)(TraktRatingType.Movies, {
283-
movies: [
284-
{
285-
ids: movie.value.ids,
286-
rating: _score,
287-
},
288-
],
289-
});
290-
};
291-
292252
onMounted(() => {
293-
watch(
294-
movieId,
295-
id => {
296-
fetchMovie(id);
297-
if (enableRatings.value) loadRatings(TraktRatingType.Movies, id);
298-
},
299-
{ immediate: true },
300-
);
253+
watch(movieId, id => fetchMovie(id), { immediate: true });
301254
fetchMovieWatched();
302255
fetchMovieCollected();
303256
@@ -321,7 +274,6 @@ onMounted(() => {
321274
class="show-title"
322275
:href="titleUrl"
323276
:title="i18n('open_in_trakt', 'common', 'tooltip')"
324-
@on-click="openTab"
325277
>
326278
{{ title }}
327279
</TitleLink>
@@ -332,17 +284,9 @@ onMounted(() => {
332284
round
333285
/>
334286

335-
<PanelStatistics
336-
:rating="movie?.rating"
337-
:votes="movie?.votes"
338-
:score="score?.rating"
339-
:loading-score="scoreLoading"
340-
:loading-rating="movieLoading"
341-
:url="ratingUrl"
342-
@on-score-edit="onScoreEdit"
343-
>
344-
<PanelPoster :tmdb="movie?.ids.tmdb" mode="movie" />
345-
</PanelStatistics>
287+
<PanelMovieStatistics :movie="movie" :movie-loading="movieLoading">
288+
<PanelPoster :tmdb="movie?.ids.tmdb" mode="movie" :link="titleUrl" />
289+
</PanelMovieStatistics>
346290

347291
<MoviePanelDetails
348292
:movie="movie"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
<script setup lang="ts">
2+
import {
3+
type TraktMovieExtended,
4+
TraktRatingType,
5+
type TraktSyncRatingValue,
6+
} from '@dvcol/trakt-http-client/models';
7+
8+
import { computed, onMounted, type PropType, toRefs, watch } from 'vue';
9+
10+
import type { RatingItem } from '~/models/rating.model';
11+
12+
import IconTrakt from '~/components/icons/IconTrakt.vue';
13+
import PanelStatistics from '~/components/views/panel/PanelStatistics.vue';
14+
import { ResolveExternalLinks } from '~/settings/external.links';
15+
import { useRatingsStore } from '~/stores/data/ratings.store';
16+
import { useExtensionSettingsStoreRefs } from '~/stores/settings/extension.store';
17+
import { useI18n } from '~/utils/i18n.utils';
18+
19+
const i18n = useI18n('panel', 'statistics');
20+
21+
const props = defineProps({
22+
movie: {
23+
type: Object as PropType<TraktMovieExtended>,
24+
required: false,
25+
},
26+
movieLoading: {
27+
type: Boolean,
28+
required: false,
29+
default: false,
30+
},
31+
});
32+
33+
const { movie, movieLoading } = toRefs(props);
34+
35+
const { enableRatings } = useExtensionSettingsStoreRefs();
36+
const { loadRatings, getRatings, getLoading, addRating, removeRating } =
37+
useRatingsStore();
38+
39+
const movieId = computed(() => movie?.value?.ids.trakt);
40+
const scoreLoading = computed(() => getLoading(TraktRatingType.Movies));
41+
42+
const score = computed(() => {
43+
if (!movieId.value) return;
44+
return getRatings(TraktRatingType.Movies, movieId.value.toString());
45+
});
46+
47+
const ratingUrl = computed(() => {
48+
if (!movie?.value?.ids?.slug) return;
49+
return ResolveExternalLinks.trakt.item({
50+
type: 'movies',
51+
slug: movie?.value.ids.slug,
52+
suffix: '/stats',
53+
});
54+
});
55+
56+
const ratings = computed<RatingItem[]>(
57+
() =>
58+
[
59+
{
60+
name: i18n('trakt', 'common', 'source', 'name'),
61+
icon: IconTrakt,
62+
rating: {
63+
votes: movie?.value?.votes,
64+
rating: movie?.value?.rating,
65+
url: ratingUrl.value,
66+
loading: movieLoading.value,
67+
},
68+
},
69+
] satisfies RatingItem[],
70+
);
71+
72+
const onScoreEdit = async (_score: TraktSyncRatingValue) => {
73+
if (!movie?.value?.ids?.trakt) return;
74+
return (_score ? addRating : removeRating)(TraktRatingType.Movies, {
75+
movies: [
76+
{
77+
ids: movie.value.ids,
78+
rating: _score,
79+
},
80+
],
81+
});
82+
};
83+
84+
onMounted(() => {
85+
watch(movieId, () => {
86+
if (!movieId.value) return;
87+
if (!enableRatings.value) return;
88+
loadRatings(TraktRatingType.Movies, movieId.value.toString());
89+
});
90+
});
91+
</script>
92+
93+
<template>
94+
<PanelStatistics
95+
:ratings="ratings"
96+
:score="score?.rating"
97+
:loading-score="scoreLoading"
98+
@on-score-edit="onScoreEdit"
99+
>
100+
<slot />
101+
</PanelStatistics>
102+
</template>

src/components/views/panel/PanelOverview.vue

+1-11
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import { NFlex, NH4, NSkeleton } from 'naive-ui';
33
44
import TitleLink from '~/components/common/buttons/TitleLink.vue';
5-
import { useLinksStore } from '~/stores/settings/links.store';
65
76
defineProps({
87
title: {
@@ -22,20 +21,11 @@ defineProps({
2221
required: false,
2322
},
2423
});
25-
26-
const { openTab } = useLinksStore();
2724
</script>
2825

2926
<template>
3027
<NFlex justify="center" align="center" vertical class="overview">
31-
<TitleLink
32-
v-if="title"
33-
class="title"
34-
:href="url"
35-
:title="label"
36-
:component="NH4"
37-
@on-click="openTab"
38-
>
28+
<TitleLink v-if="title" class="title" :href="url" :title="label" :component="NH4">
3929
{{ title }}
4030
</TitleLink>
4131
<NSkeleton v-else class="title-skeleton" style="width: var(--height-40-dvh)" round />

src/components/views/panel/PanelPoster.vue

+30-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import type { PosterItem } from '~/models/poster.model';
66
import type { ImageQuery, ImageStoreTypes } from '~/stores/data/image.store';
77
88
import PosterComponent from '~/components/common/poster/PosterComponent.vue';
9+
import { useLinksStore } from '~/stores/settings/links.store';
10+
import { useI18n } from '~/utils/i18n.utils';
11+
12+
const i18n = useI18n('common', 'tooltip');
913
1014
const props = defineProps({
1115
tmdb: {
@@ -28,9 +32,13 @@ const props = defineProps({
2832
type: Number,
2933
required: false,
3034
},
35+
link: {
36+
type: String,
37+
required: false,
38+
},
3139
});
3240
33-
const { tmdb, mode, seasonNumber, episodeNumber } = toRefs(props);
41+
const { tmdb, mode, seasonNumber, episodeNumber, link } = toRefs(props);
3442
3543
const size = computed(() => window?.innerWidth ?? 800 / 2);
3644
@@ -61,11 +69,24 @@ const key = computed(() => {
6169
if (tmdb?.value !== undefined) return `tmdb-${tmdb?.value}`;
6270
return `placeholder`;
6371
});
72+
73+
const label = computed(() => {
74+
if (!link?.value?.length) return undefined;
75+
return i18n(`open_${mode.value}_in_trakt`, 'common', 'tooltip');
76+
});
77+
78+
const { openTab } = useLinksStore();
6479
</script>
6580

6681
<template>
6782
<Transition v-if="posterItem" name="scale" mode="out-in">
68-
<NFlex :key="key" class="poster-container" :class="{ landscape: !portait }">
83+
<NFlex
84+
:key="key"
85+
class="poster-container"
86+
:class="{ landscape: !portait, link }"
87+
:title="label"
88+
@click="openTab(link)"
89+
>
6990
<PosterComponent :item="posterItem" :size="size" :backdrop="!portait" />
7091
</NFlex>
7192
</Transition>
@@ -77,13 +98,20 @@ const key = computed(() => {
7798
</template>
7899

79100
<style lang="scss" scoped>
101+
@use '~/styles/mixin' as mixin;
80102
@use '~/styles/transition' as transition;
81103
@include transition.scale;
82104
83105
.poster-container {
84106
--poster-height: calc(min(var(--half-width), var(--height-70-dvh)) * (9 / 16));
85107
--poster-width: calc(var(--poster-height) * (2 / 3));
86108
109+
&.link {
110+
@include mixin.hover-box-shadow;
111+
112+
cursor: pointer;
113+
}
114+
87115
&.landscape {
88116
--poster-width: min(var(--half-width), var(--height-70-dvh));
89117
--poster-height: calc(var(--poster-width) * (9 / 16));

0 commit comments

Comments
 (0)