Skip to content

Commit b29ddb4

Browse files
committed
feat(navbar): adds external links to trakt.tv
1 parent df788d3 commit b29ddb4

File tree

7 files changed

+178
-21
lines changed

7 files changed

+178
-21
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<script lang="ts" setup>
2+
import { NButton, NIcon, NTooltip } from 'naive-ui';
3+
import { type Component, type PropType, ref } from 'vue';
4+
5+
import IconExternalLinkRounded from '~/components/icons/IconExternalLinkRounded.vue';
6+
7+
defineOptions({
8+
inheritAttrs: false,
9+
});
10+
11+
defineProps({
12+
href: {
13+
type: String,
14+
required: false,
15+
},
16+
label: {
17+
type: String,
18+
required: false,
19+
},
20+
icon: {
21+
type: Object as PropType<Component>,
22+
required: false,
23+
default: IconExternalLinkRounded,
24+
},
25+
});
26+
27+
const anchor = ref();
28+
</script>
29+
30+
<template>
31+
<NTooltip
32+
class="button-link-external-tooltip"
33+
:disabled="!label"
34+
:show-arrow="false"
35+
placement="bottom"
36+
:delay="300"
37+
trigger="hover"
38+
:to="anchor"
39+
>
40+
<span>{{ label }}</span>
41+
<template #trigger>
42+
<a ref="anchor" class="anchor-link" :href="href">
43+
<NButton tertiary class="external-link" v-bind="$attrs">
44+
<template #icon>
45+
<NIcon :component="icon" />
46+
</template>
47+
</NButton>
48+
</a>
49+
</template>
50+
</NTooltip>
51+
</template>
52+
53+
<style lang="scss" scoped>
54+
@use '~/styles/z-index' as layers;
55+
56+
.anchor-link {
57+
z-index: layers.$in-front;
58+
color: inherit;
59+
text-decoration: none;
60+
61+
.external-link {
62+
width: 2.25rem;
63+
}
64+
}
65+
</style>
66+
67+
<style lang="scss">
68+
.button-link-external-tooltip.button-link-external-tooltip {
69+
margin-top: 12px;
70+
}
71+
</style>

src/components/views/calendar/CalendarNavbar.vue

+14-3
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ import { NDatePicker, NFlex, NIcon, NInput, NTooltip } from 'naive-ui';
33
44
import { computed, defineProps, ref } from 'vue';
55
6+
import ButtonLinkExternal from '~/components/common/buttons/ButtonLinkExternal.vue';
67
import IconCalendar from '~/components/icons/IconCalendar.vue';
78
import IconChevron from '~/components/icons/IconChevronDownSmall.vue';
89
910
import IconLoop from '~/components/icons/IconLoop.vue';
1011
12+
import { ResolveExternalLinks } from '~/settings/external.links';
1113
import { useCalendarStore, useCalendarStoreRefs } from '~/stores/data/calendar.store';
1214
import { useI18n } from '~/utils';
1315
import { useDebouncedSearch } from '~/utils/store.utils';
@@ -31,11 +33,19 @@ const pickerValue = computed({
3133
set: (value: number) => {
3234
const newDate = value ? new Date(value) : new Date();
3335
if (newDate.toLocaleDateString() === center.value.toLocaleDateString()) return;
34-
console.info('pickerValue', new Date(value));
3536
clearState(value ? new Date(value) : undefined);
3637
},
3738
});
3839
40+
const external = computed(() => {
41+
if (pickerValue.value) {
42+
return ResolveExternalLinks.trakt.calendar(
43+
new Date(pickerValue.value).toISOString().split('T')[0],
44+
);
45+
}
46+
return ResolveExternalLinks.trakt.calendar();
47+
});
48+
3949
const open = ref(false);
4050
</script>
4151

@@ -79,6 +89,7 @@ const open = ref(false);
7989
</NInput>
8090
</template>
8191
</NTooltip>
92+
<ButtonLinkExternal :href="external" :label="i18n('calendar', 'common', 'link')" />
8293
</NFlex>
8394
</template>
8495

@@ -88,11 +99,11 @@ const open = ref(false);
8899
padding: 0 0.5rem;
89100
90101
.date-picker {
91-
flex: 0 1 48%;
102+
flex: 0 1 33%;
92103
}
93104
94105
.search-input {
95-
flex: 1 1 50%;
106+
flex: 1 1 33%;
96107
}
97108
}
98109
</style>

src/components/views/history/HistoryNavbar.vue

+14-1
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@ import { NDatePicker, NFlex, NIcon, NInput } from 'naive-ui';
33
44
import { computed, defineProps, ref } from 'vue';
55
6+
import ButtonLinkExternal from '~/components/common/buttons/ButtonLinkExternal.vue';
67
import NavbarPageSizeSelect from '~/components/common/navbar/NavbarPageSizeSelect.vue';
78
import IconCalendar from '~/components/icons/IconCalendar.vue';
89
import IconChevron from '~/components/icons/IconChevronDownSmall.vue';
910
import IconLoop from '~/components/icons/IconLoop.vue';
1011
12+
import { ResolveExternalLinks } from '~/settings/external.links';
1113
import { useHistoryStore, useHistoryStoreRefs } from '~/stores/data/history.store';
14+
import { useUserSettingsStoreRefs } from '~/stores/settings/user.store';
1215
import { useI18n } from '~/utils';
1316
import { debounce } from '~/utils/debounce.utils';
1417
import { useDebouncedSearch } from '~/utils/store.utils';
@@ -20,6 +23,15 @@ const { setHistoryRange } = useHistoryStore();
2023
2124
const debouncedSearch = useDebouncedSearch(searchHistory);
2225
26+
const { user } = useUserSettingsStoreRefs();
27+
const external = computed(() =>
28+
ResolveExternalLinks.trakt.history({
29+
user: user.value,
30+
start: historyStart.value?.toISOString(),
31+
end: historyEnd.value?.toISOString(),
32+
}),
33+
);
34+
2335
defineProps({
2436
parentElement: {
2537
type: HTMLElement,
@@ -73,6 +85,7 @@ const open = ref(false);
7385
</template>
7486
</NInput>
7587
<NavbarPageSizeSelect v-model:page-size="pageSize" :parent-element="parentElement" />
88+
<ButtonLinkExternal :href="external" :label="i18n('history', 'common', 'link')" />
7689
</NFlex>
7790
</template>
7891

@@ -82,7 +95,7 @@ const open = ref(false);
8295
padding: 0 0.5rem;
8396
8497
.date-picker {
85-
flex: 0 1 50%;
98+
flex: 0 1 33%;
8699
}
87100
88101
.search-input {

src/components/views/search/SearchNavbar.vue

+5
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { type Component, computed, defineProps, h, onActivated, ref } from 'vue'
1313
1414
import type { TraktSearchType } from '~/models/trakt/trakt-search.model';
1515
16+
import ButtonLinkExternal from '~/components/common/buttons/ButtonLinkExternal.vue';
1617
import NavbarPageSizeSelect from '~/components/common/navbar/NavbarPageSizeSelect.vue';
1718
import IconAccount from '~/components/icons/IconAccount.vue';
1819
import IconChevronDown from '~/components/icons/IconChevronDownSmall.vue';
@@ -23,6 +24,7 @@ import IconMovie from '~/components/icons/IconMovie.vue';
2324
import IconScreen from '~/components/icons/IconScreen.vue';
2425
import IconYoutube from '~/components/icons/IconYoutube.vue';
2526
27+
import { ResolveExternalLinks } from '~/settings/external.links';
2628
import { SupportedSearchType, useSearchStoreRefs } from '~/stores/data/search.store';
2729
import { useI18n } from '~/utils';
2830
import { debounce } from '~/utils/debounce.utils';
@@ -40,6 +42,7 @@ const toggleFocus = (focus: boolean) => {
4042
};
4143
4244
const debouncedSearch = useDebouncedSearch(search, 1000);
45+
const external = computed(() => ResolveExternalLinks.trakt.query(debouncedSearch.value));
4346
4447
const filteredHistory = computed(() =>
4548
[...history.value]
@@ -217,6 +220,8 @@ onActivated(() => {
217220
</NTooltip>
218221

219222
<NavbarPageSizeSelect v-model:page-size="pageSize" :parent-element="parentElement" />
223+
<ButtonLinkExternal :href="external" :label="i18n('search', 'common', 'link')" />
224+
220225
<NSwitch
221226
v-if="false"
222227
v-model:value="query"

src/components/views/watchlist/WatchlistNavbar.vue

+22-1
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,19 @@ import { NFlex, NIcon, NInput, NSelect, type SelectOption } from 'naive-ui';
33
44
import { type Component, computed, defineProps, h, ref } from 'vue';
55
6+
import ButtonLinkExternal from '~/components/common/buttons/ButtonLinkExternal.vue';
67
import NavbarPageSizeSelect from '~/components/common/navbar/NavbarPageSizeSelect.vue';
78
import IconLoop from '~/components/icons/IconLoop.vue';
89
10+
import { ResolveExternalLinks } from '~/settings/external.links';
911
import {
1012
type ListEntity,
1113
ListType,
1214
useListsStore,
1315
useListsStoreRefs,
1416
useListStoreRefs,
1517
} from '~/stores/data/list.store';
18+
import { useUserSettingsStoreRefs } from '~/stores/settings/user.store';
1619
import { useI18n } from '~/utils';
1720
import { useDebouncedSearch, watchUserChange } from '~/utils/store.utils';
1821
@@ -23,6 +26,23 @@ const { pageSize, searchList } = useListStoreRefs();
2326
const { listsLoading, lists, activeList } = useListsStoreRefs();
2427
const { fetchLists, clearState, getIcon } = useListsStore();
2528
29+
const { user } = useUserSettingsStoreRefs();
30+
31+
const external = computed(() => {
32+
switch (activeList.value.type) {
33+
case ListType.Watchlist:
34+
return ResolveExternalLinks.trakt.watchlist(user.value);
35+
case ListType.Favorites:
36+
return ResolveExternalLinks.trakt.favorites(user.value);
37+
case ListType.Collection:
38+
return ResolveExternalLinks.trakt.collection(user.value, activeList.value.scope);
39+
case ListType.List:
40+
return ResolveExternalLinks.trakt.list(user.value, activeList.value.name);
41+
default:
42+
return '';
43+
}
44+
});
45+
2646
type ListOption = SelectOption & { source: ListEntity; icon: Component };
2747
const listOptions = computed<ListOption[]>(() =>
2848
lists.value.map((list, i) => ({
@@ -109,6 +129,7 @@ const renderTag = ({ option }: { option: SelectOption }) => option.label?.toStri
109129
</template>
110130
</NInput>
111131
<NavbarPageSizeSelect v-model:page-size="pageSize" :parent-element="parentElement" />
132+
<ButtonLinkExternal :href="external" :label="i18n('list', 'common', 'link')" />
112133
</NFlex>
113134
</template>
114135

@@ -118,7 +139,7 @@ const renderTag = ({ option }: { option: SelectOption }) => option.label?.toStri
118139
padding: 0 0.5rem;
119140
120141
.list-select {
121-
flex: 0 1 40%;
142+
flex: 0 1 33%;
122143
}
123144
124145
.search-input {

src/i18n/en/common/link.json

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"common__link__calendar": {
3+
"message": "Open calendar in Trakt.tv",
4+
"description": "Link to open calendar in Trakt.tv"
5+
},
6+
"common__link__history": {
7+
"message": "Open history in Trakt.tv",
8+
"description": "Link to open history in Trakt.tv"
9+
},
10+
"common__link__list": {
11+
"message": "Open list in Trakt.tv",
12+
"description": "Link to open list in Trakt.tv"
13+
},
14+
"common__link__search": {
15+
"message": "Open search in Trakt.tv",
16+
"description": "Link to open search in Trakt.tv"
17+
}
18+
}

src/settings/external.links.ts

+34-16
Original file line numberDiff line numberDiff line change
@@ -21,22 +21,40 @@ export const ExternaLinks = {
2121
} as const;
2222

2323
export const ResolveExternalLinks = {
24-
trakt: ({
25-
type,
26-
slug,
27-
season,
28-
episode,
29-
base = ExternaLinks.trakt.production,
30-
}: {
31-
type: 'movies' | 'shows' | 'season' | 'episode' | 'person' | 'comment' | 'list';
32-
slug: string;
33-
season?: number;
34-
episode?: number;
35-
base?: string;
36-
}) => {
37-
if (type === 'episode') return `${base}shows/${slug}/seasons/${season}/episodes/${episode}`;
38-
if (type === 'season') return `${base}shows/${slug}/seasons/${season}`;
39-
return `${base}${type}/${slug}`;
24+
trakt: {
25+
item: ({
26+
type,
27+
slug,
28+
season,
29+
episode,
30+
base = ExternaLinks.trakt.production,
31+
}: {
32+
type: 'movies' | 'shows' | 'season' | 'episode' | 'person' | 'comment' | 'list';
33+
slug: string;
34+
season?: number;
35+
episode?: number;
36+
base?: string;
37+
}) => {
38+
if (type === 'episode') return `${base}shows/${slug}/seasons/${season}/episodes/${episode}`;
39+
if (type === 'season') return `${base}shows/${slug}/seasons/${season}`;
40+
return `${base}${type}/${slug}`;
41+
},
42+
query: (query: string) => `${ExternaLinks.trakt.production}search?query=${query}`,
43+
watchlist: (user: string) => `${ExternaLinks.trakt.production}users/${user}/watchlist`,
44+
favorites: (user: string) => `${ExternaLinks.trakt.production}users/${user}/favorites`,
45+
list: (user: string, list: string) => `${ExternaLinks.trakt.production}users/${user}/lists/${list}`,
46+
collection: (user: string, type?: 'movies' | 'shows' | 'episodes') => {
47+
const url = `${ExternaLinks.trakt.production}users/${user}/collection`;
48+
if (type) return `${url}/${type}`;
49+
return url;
50+
},
51+
history: ({ user, start, end }: { user: string; start?: string; end?: string }) => {
52+
let url = `${ExternaLinks.trakt.production}users/${user}/history`;
53+
if (start) url += `?start_at=${start}`;
54+
if (end) url += `${start ? '&' : '?'}end_at=${end}`;
55+
return url;
56+
},
57+
calendar: (date?: string) => `${ExternaLinks.trakt.production}calendars/my/shows-movies/${date ?? ''}`,
4058
},
4159
search: ({
4260
id,

0 commit comments

Comments
 (0)