Skip to content

Commit f44e113

Browse files
committed
feat(panel): adds show details to panel
1 parent a922574 commit f44e113

File tree

6 files changed

+272
-57
lines changed

6 files changed

+272
-57
lines changed

src/components/views/panel/ShowPanel.vue

+20-12
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import type {
99
import type { TraktShowExtended } from '~/models/trakt/trakt-show.model';
1010
1111
import TitleLink from '~/components/common/buttons/TitleLink.vue';
12+
import ShowPanelDetails from '~/components/views/panel/ShowPanelDetails.vue';
1213
import ShowPanelOverview from '~/components/views/panel/ShowPanelOverview.vue';
1314
import ShowPanelPicker from '~/components/views/panel/ShowPanelPicker.vue';
1415
import ShowPanelPoster from '~/components/views/panel/ShowPanelPoster.vue';
@@ -69,6 +70,15 @@ const showTitle = computed(() => {
6970
return deCapitalise(show.value.title);
7071
});
7172
73+
const titleUrl = computed(() => {
74+
if (!show.value?.ids?.trakt) return;
75+
return ResolveExternalLinks.search({
76+
type: 'show',
77+
source: 'trakt',
78+
id: show.value.ids.trakt,
79+
});
80+
});
81+
7282
const subscriptions = new Set<() => void>();
7383
7484
const { getShowRef, getShowSeasonsRef, getShowSeasonEpisodesRef, getShowEpisodeRef } =
@@ -132,15 +142,6 @@ onUnmounted(() => {
132142
});
133143
134144
const { openTab } = useExtensionSettingsStore();
135-
136-
const titleUrl = computed(() => {
137-
if (!show.value?.ids?.trakt) return;
138-
return ResolveExternalLinks.search({
139-
type: 'show',
140-
source: 'trakt',
141-
id: show.value.ids.trakt,
142-
});
143-
});
144145
</script>
145146

146147
<template>
@@ -150,14 +151,21 @@ const titleUrl = computed(() => {
150151
</TitleLink>
151152
<NSkeleton v-else class="show-title-skeleton" style="width: 50dvh" round />
152153

153-
<ShowPanelPicker :seasons="seasons" :episodes="episodes" :mode="panelType" />
154-
155154
<ShowPanelPoster
156155
:show-id="show?.ids.tmdb"
157156
:season-number="seasonNb"
158157
:episode-number="episodeNb"
159158
/>
160159

160+
<ShowPanelDetails
161+
:show="show"
162+
:season="season"
163+
:episode="episode"
164+
:mode="panelType"
165+
/>
166+
167+
<ShowPanelPicker :seasons="seasons" :episodes="episodes" :mode="panelType" />
168+
161169
<ShowPanelOverview
162170
:episode="episode"
163171
:season="season"
@@ -188,7 +196,7 @@ const titleUrl = computed(() => {
188196
189197
.show-title:deep(h2),
190198
.show-title-skeleton {
191-
margin-bottom: 0.5rem;
199+
margin-bottom: 1rem;
192200
}
193201
194202
.show-title-skeleton {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
<script lang="ts" setup>
2+
import { NFlex, NSkeleton, NTag } from 'naive-ui';
3+
import { computed, type PropType, toRefs } from 'vue';
4+
5+
import type { TraktEpisodeExtended } from '~/models/trakt/trakt-episode.model';
6+
import type { TraktSeasonExtended } from '~/models/trakt/trakt-season.model';
7+
import type { TraktShowExtended } from '~/models/trakt/trakt-show.model';
8+
9+
import { capitalizeEachWord } from '~/utils/string.utils';
10+
11+
const props = defineProps({
12+
episode: {
13+
type: Object as PropType<TraktEpisodeExtended>,
14+
required: false,
15+
},
16+
season: {
17+
type: Object as PropType<TraktSeasonExtended>,
18+
required: false,
19+
},
20+
show: {
21+
type: Object as PropType<TraktShowExtended>,
22+
required: false,
23+
},
24+
mode: {
25+
type: String as PropType<'show' | 'season' | 'episode'>,
26+
required: false,
27+
default: 'episode',
28+
},
29+
});
30+
31+
const { mode, episode, season, show } = toRefs(props);
32+
33+
const aired = computed(() => {
34+
if (mode.value === 'episode') {
35+
if (!episode?.value) return;
36+
if (!episode.value?.first_aired) return '-';
37+
return new Date(episode.value?.first_aired).toLocaleDateString();
38+
}
39+
if (mode.value === 'season') {
40+
if (!season?.value) return;
41+
if (!season.value?.first_aired) return '-';
42+
return new Date(season.value?.first_aired).toLocaleDateString();
43+
}
44+
if (!show?.value) return;
45+
if (!show.value?.first_aired) return '-';
46+
return new Date(show.value?.first_aired).toLocaleDateString();
47+
});
48+
49+
const runtime = computed(() => {
50+
if (mode.value === 'episode') {
51+
if (!episode?.value) return;
52+
if (!episode.value?.runtime) return '-';
53+
return `${episode.value.runtime} min`;
54+
}
55+
if (!show?.value) return;
56+
if (!show.value?.runtime) return '-';
57+
return `${show.value.runtime} min`;
58+
});
59+
60+
const genres = computed(() => {
61+
if (!show?.value) return;
62+
return show.value?.genres?.map(capitalizeEachWord) ?? [];
63+
});
64+
65+
const year = computed(() => {
66+
if (!show?.value) return;
67+
return show.value?.year ?? '-';
68+
});
69+
70+
const status = computed(() => {
71+
if (!show?.value) return;
72+
return capitalizeEachWord(show.value?.status) ?? '-';
73+
});
74+
75+
const airedEpisodes = computed(() => {
76+
if (!season?.value) return;
77+
return `${season.value?.aired_episodes ?? '-'} / ${season.value?.episode_count ?? '-'}`;
78+
});
79+
80+
const episodeType = computed(() => {
81+
if (!episode?.value) return;
82+
return episode.value?.episode_type ?? '-';
83+
});
84+
85+
const network = computed(() => {
86+
if (!show?.value && !season?.value) return;
87+
return show?.value?.network ?? season?.value?.network ?? '-';
88+
});
89+
90+
const country = computed(() => {
91+
if (!show?.value) return;
92+
return show.value?.country ?? '-';
93+
});
94+
</script>
95+
96+
<template>
97+
<NFlex size="large" class="container" vertical>
98+
<NFlex class="row" size="large">
99+
<!-- Show Year -->
100+
<NFlex class="detail" align="center">
101+
<span class="prefix">Year</span>
102+
<span v-if="year">{{ year }}</span>
103+
<NSkeleton v-else style="width: 2.25rem" round />
104+
</NFlex>
105+
106+
<!-- Show Country -->
107+
<NFlex class="detail" align="center">
108+
<span class="prefix">Country</span>
109+
<span v-if="country">{{ country }}</span>
110+
<NSkeleton v-else style="width: 2ch" round />
111+
</NFlex>
112+
113+
<!-- Show Network -->
114+
<NFlex class="detail" align="center">
115+
<span class="prefix">Network</span>
116+
<span v-if="network">{{ network }}</span>
117+
<NSkeleton v-else style="width: 5.5rem" round />
118+
</NFlex>
119+
</NFlex>
120+
121+
<NFlex class="row" size="large">
122+
<!-- Air date -->
123+
<NFlex class="detail" align="center">
124+
<span class="prefix">Aired</span>
125+
<span v-if="aired">{{ aired }}</span>
126+
<NSkeleton v-else style="width: 5.125rem" round />
127+
</NFlex>
128+
129+
<!-- Show Status -->
130+
<NFlex class="detail" align="center">
131+
<span class="prefix">Status</span>
132+
<span v-if="status">{{ status }}</span>
133+
<NSkeleton v-else style="width: 7.5rem" round />
134+
</NFlex>
135+
136+
<!-- Runtime -->
137+
<NFlex class="detail" align="center">
138+
<span class="prefix">Runtime</span>
139+
<span v-if="runtime">{{ runtime }}</span>
140+
<NSkeleton v-else style="width: 3.75rem" round />
141+
</NFlex>
142+
</NFlex>
143+
144+
<NFlex class="row" size="large">
145+
<!-- Season aired episodes -->
146+
<NFlex v-if="mode !== 'show'" class="detail" align="center">
147+
<span class="prefix">Aired episodes</span>
148+
<span v-if="airedEpisodes">{{ airedEpisodes }}</span>
149+
<NSkeleton v-else style="width: 3rem" round />
150+
</NFlex>
151+
152+
<!-- Type -->
153+
<NFlex v-if="mode === 'episode'" class="detail" align="center">
154+
<span class="prefix">Type</span>
155+
<span v-if="episodeType">{{ episodeType }}</span>
156+
<NSkeleton v-else style="width: 6.25rem" round />
157+
</NFlex>
158+
</NFlex>
159+
160+
<!-- Genres -->
161+
<NFlex class="detail genres" align="center" justify="flex-start">
162+
<span class="prefix">Genres</span>
163+
<template v-if="genres">
164+
<NTag v-for="(genre, i) of genres" :key="i" size="small" round>{{ genre }}</NTag>
165+
</template>
166+
<template v-else>
167+
<NSkeleton style="width: 3rem" round />
168+
<NSkeleton style="width: 3rem" round />
169+
<NSkeleton style="width: 3rem" round />
170+
</template>
171+
</NFlex>
172+
</NFlex>
173+
</template>
174+
175+
<style lang="scss" scoped>
176+
.container,
177+
.row {
178+
flex: 1 1 auto;
179+
width: 100%;
180+
}
181+
182+
.prefix {
183+
color: var(--white-50);
184+
font-weight: 600;
185+
transition: color 0.3s var(--n-bezier);
186+
}
187+
188+
.detail {
189+
flex: 1 1 30%;
190+
191+
&:hover .prefix {
192+
color: var(--white-70);
193+
}
194+
195+
&.genres {
196+
flex: 1 1 auto;
197+
width: 100%;
198+
margin: 0.5rem 0;
199+
}
200+
}
201+
</style>

src/components/views/panel/ShowPanelOverview.vue

+21-37
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<script setup lang="ts">
22
import { NFlex, NH4, NSkeleton } from 'naive-ui';
3-
import { computed, onMounted, type PropType, ref, toRefs, Transition } from 'vue';
3+
import { computed, type PropType, toRefs } from 'vue';
44
55
import type { TraktEpisodeExtended } from '~/models/trakt/trakt-episode.model';
66
@@ -86,47 +86,33 @@ const overview = computed(() => {
8686
return episode?.value?.overview ?? '-';
8787
});
8888
89-
const key = computed(() => `episode-${episode?.value?.ids.trakt}`);
90-
9189
const { openTab } = useExtensionSettingsStore();
92-
93-
const transition = ref('none');
94-
95-
onMounted(() => {
96-
setTimeout(() => {
97-
transition.value = 'scale';
98-
}, 100);
99-
});
10090
</script>
10191

10292
<template>
103-
<Transition :name="transition" mode="out-in">
104-
<NFlex :key="key" justify="center" align="center" vertical class="overview container">
105-
<TitleLink
106-
v-if="title"
107-
class="title"
108-
:href="url"
109-
:component="NH4"
110-
@on-click="openTab"
111-
>
112-
{{ title }}
113-
</TitleLink>
114-
<NSkeleton v-else class="title-skeleton" style="width: 40dvh" round />
115-
116-
<div v-if="overview">{{ overview }}</div>
117-
<template v-else>
118-
<NSkeleton style="width: 100%" />
119-
<NSkeleton style="width: 100%" />
120-
<NSkeleton style="width: 100%" />
121-
</template>
122-
</NFlex>
123-
</Transition>
93+
<NFlex justify="center" align="center" vertical class="overview">
94+
<TitleLink
95+
v-if="title"
96+
class="title"
97+
:href="url"
98+
:component="NH4"
99+
@on-click="openTab"
100+
>
101+
{{ title }}
102+
</TitleLink>
103+
<NSkeleton v-else class="title-skeleton" style="width: 40dvh" round />
104+
105+
<div v-if="overview">{{ overview }}</div>
106+
<template v-else>
107+
<NSkeleton style="width: 100%" />
108+
<NSkeleton style="width: 100%" />
109+
<NSkeleton style="width: 100%" />
110+
</template>
111+
</NFlex>
124112
</template>
125113

126114
<style lang="scss" scoped>
127115
@use '~/styles/z-index' as layers;
128-
@use '~/styles/transition' as transition;
129-
@include transition.scale;
130116
131117
.anchor-link {
132118
z-index: layers.$in-front;
@@ -144,9 +130,7 @@ onMounted(() => {
144130
}
145131
146132
.overview {
147-
.container {
148-
width: 100%;
149-
}
133+
width: 100%;
150134
151135
.title:deep(h4),
152136
.title-skeleton {

0 commit comments

Comments
 (0)