Skip to content

Commit dc8ac91

Browse files
committed
feat(progress): adds mismatch warning and improve stale data detection
1 parent 5314c05 commit dc8ac91

14 files changed

+157
-62
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
"@dvcol/tmdb-http-client": "^1.2.3",
5757
"@dvcol/trakt-http-client": "^1.4.5",
5858
"@dvcol/tvdb-http-client": "^1.1.3",
59-
"@dvcol/web-extension-utils": "^3.1.0",
59+
"@dvcol/web-extension-utils": "^3.2.0",
6060
"@vue/devtools": "^7.0.15",
6161
"iso-3166-2": "^1.0.0",
6262
"naive-ui": "^2.38.1",

pnpm-lock.yaml

+5-5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

scripts/manifest.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export const manifest: Manifest.WebExtensionManifest = {
4444
service_worker: 'scripts/background.js',
4545
type: 'module',
4646
},
47-
permissions: ['storage', 'tabs', 'contextMenus'],
47+
permissions: ['storage', 'tabs', 'contextMenus', 'cookies'],
4848
web_accessible_resources: [
4949
{
5050
resources: ['/views/options/index.html'],

src/components/common/navbar/NavbarSettingsDopdown.vue

+3-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import IconLogOut from '~/components/icons/IconLogOut.vue';
2222
2323
import { Route } from '~/models/router.model';
2424
import { Logger } from '~/services/logger.service';
25+
import { NavbarService } from '~/services/navbar.service';
2526
import { TraktService } from '~/services/trakt.service';
2627
import { ExternaLinks } from '~/settings/external.links';
2728
import { useExtensionSettingsStoreRefs } from '~/stores/settings/extension.store';
@@ -33,7 +34,7 @@ import { useI18n } from '~/utils/i18n.utils';
3334
const i18n = useI18n('navbar', 'settings');
3435
const router = useRouter();
3536
36-
const { user, userSetting, userSettings } = useUserSettingsStoreRefs();
37+
const { userSetting, userSettings } = useUserSettingsStoreRefs();
3738
const { enabledRoutes } = useExtensionSettingsStoreRefs();
3839
3940
const avatar = computed(() => userSetting.value?.user?.images?.avatar?.full);
@@ -137,6 +138,7 @@ const onSelect: DropdownProps['onSelect'] = async (key: string, { label }) => {
137138
size="small"
138139
class="settings-dropdown"
139140
:style="{ '--tab-count': enabledRoutes.length + 1 }"
141+
:on-update:show="(visible: boolean) => (NavbarService.dropdown.value = visible)"
140142
@select="onSelect"
141143
>
142144
<NFlex justify="space-around" align="center" :wrap="false">

src/components/container/ContainerComponent.ce.vue

+35-11
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import LoadingBarProvider from '~/components/container/LoadingBarProvider.vue';
1515
import MessageProvider from '~/components/container/MessageProvider.vue';
1616
import NotificationProvider from '~/components/container/NotificationProvider.vue';
1717
import { NavbarService } from '~/services/navbar.service';
18+
import { useExtensionSettingsStoreRefs } from '~/stores/settings/extension.store';
1819
import { lazyComponent } from '~/utils/lazy.utils';
1920
import { addCustomProgressProperty } from '~/utils/style.utils';
2021
@@ -27,16 +28,20 @@ const override: GlobalThemeOverrides = {
2728
// TODO red palette
2829
};
2930
30-
const { drawer } = NavbarService;
31-
const drawerOpen = NavbarService.open;
31+
const { drawer, open, dropdown } = NavbarService;
32+
const { enabledRoutes } = useExtensionSettingsStoreRefs();
3233
3334
const root = ref<HTMLElement>();
3435
3536
onBeforeMount(() => addCustomProgressProperty());
3637
</script>
3738

3839
<template>
39-
<div id="trakt-extension-root" ref="root">
40+
<div
41+
id="trakt-extension-root"
42+
ref="root"
43+
:style="{ '--tab-count': enabledRoutes.length + 1 }"
44+
>
4045
<NConfigProvider :theme="theme" :theme-overrides="override" abstract>
4146
<AppComponent />
4247

@@ -51,11 +56,7 @@ onBeforeMount(() => addCustomProgressProperty());
5156
:to="root"
5257
:max="2"
5358
:container-class="
54-
[
55-
'message-container',
56-
drawer ? 'has-drawer' : '',
57-
drawerOpen ? 'drawer-visible' : '',
58-
]
59+
['message-container', drawer ? 'has-drawer' : '', open ? 'drawer-visible' : '']
5960
.filter(Boolean)
6061
.join(' ')
6162
"
@@ -70,7 +71,8 @@ onBeforeMount(() => addCustomProgressProperty());
7071
[
7172
'notification-container',
7273
drawer ? 'has-drawer' : '',
73-
drawerOpen ? 'drawer-visible' : '',
74+
open ? 'drawer-visible' : '',
75+
dropdown ? 'dropdown-visible' : '',
7476
]
7577
.filter(Boolean)
7678
.join(' ')
@@ -124,23 +126,45 @@ onBeforeMount(() => addCustomProgressProperty());
124126
}
125127
126128
.notification-container.n-notification-container.n-notification-container {
127-
@include layout.navbar-transition($transition: top);
129+
@include layout.navbar-transition(
130+
#{top layout.$navbar-transition,
131+
right layout.$navbar-transition},
132+
#{top layout.$navbar-transition-visible,
133+
right layout.$navbar-transition-visible},
134+
#{layout.$navbar-transition-delay,
135+
layout.$navbar-transition-delay-visible}
136+
);
128137
129138
top: layout.$header-navbar-height;
130139
131140
&.drawer-visible {
132141
top: layout.$header-open-drawer-height;
133142
}
143+
144+
&.dropdown-visible {
145+
right: max(calc(100vw / var(--tab-count)), 8rem);
146+
}
134147
}
135148
136149
.message-container.n-message-container.n-message-container {
137-
@include layout.navbar-transition($transition: top);
150+
@include layout.navbar-transition(
151+
#{top layout.$navbar-transition,
152+
right layout.$navbar-transition},
153+
#{top layout.$navbar-transition-visible,
154+
right layout.$navbar-transition-visible},
155+
#{layout.$navbar-transition-delay,
156+
layout.$navbar-transition-delay-visible}
157+
);
138158
139159
top: calc(#{layout.$header-navbar-height} + 12px);
140160
141161
&.drawer-visible {
142162
top: calc(#{layout.$header-open-drawer-height} + 12px);
143163
}
164+
165+
&.dropdown-visible {
166+
right: max(calc(100vw / var(--tab-count)), 8rem);
167+
}
144168
}
145169
146170
.n-popover.n-popconfirm {

src/components/views/progress/ProgressComponent.vue

+33-7
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
<script lang="ts" setup>
2-
import { NFlex } from 'naive-ui';
3-
import { onMounted, Transition, watch } from 'vue';
2+
import { NFlex, type NotificationReactive } from 'naive-ui';
3+
import { onMounted, ref, Transition, watch } from 'vue';
44
55
import FloatingButton from '~/components/common/buttons/FloatingButton.vue';
66
import { useBackToTop } from '~/components/common/buttons/use-back-to-top';
77
import ListScroll from '~/components/common/list/ListScroll.vue';
88
99
import LoginCard from '~/components/views/login/LoginCard.vue';
1010
import { usePanelItem } from '~/components/views/panel/use-panel-item';
11+
import { NotificationService } from '~/services/notification.service';
1112
import { ExternaLinks } from '~/settings/external.links';
1213
import { useAppStateStoreRefs } from '~/stores/app-state.store';
1314
import { useProgressStore, useProgressStoreRefs } from '~/stores/data/progress.store';
1415
import { useWatchingStoreRefs } from '~/stores/data/watching.store';
16+
import { useUserSettingsStoreRefs } from '~/stores/settings/user.store';
1517
import { useI18n } from '~/utils/i18n.utils';
1618
import { watchUserChange } from '~/utils/store.utils';
19+
import { getSessionUser } from '~/utils/trakt-service.utils';
1720
1821
const i18n = useI18n('progress');
1922
@@ -23,11 +26,6 @@ const { progress, loading, loggedOut } = useProgressStoreRefs();
2326
const { fetchProgress, clearState } = useProgressStore();
2427
const { isWatching } = useWatchingStoreRefs();
2528
26-
watchUserChange({
27-
fetch: fetchProgress,
28-
clear: clearState,
29-
});
30-
3129
onMounted(() => {
3230
watch(panelOpen, async value => {
3331
if (!value && panelDirty.value) await fetchProgress();
@@ -38,6 +36,34 @@ onMounted(() => {
3836
});
3937
});
4038
39+
const { user } = useUserSettingsStoreRefs();
40+
const unSub = ref<() => void>();
41+
const notification = ref<NotificationReactive>();
42+
43+
watchUserChange({
44+
fetch: fetchProgress,
45+
clear: clearState,
46+
activated: async () => {
47+
await fetchProgress();
48+
unSub.value = watch(
49+
user,
50+
async _user => {
51+
const session = await getSessionUser();
52+
if (notification.value) notification.value.destroy();
53+
if (!session || _user === session) return;
54+
notification.value = NotificationService.userMismatch({ user: _user, session });
55+
},
56+
{
57+
immediate: true,
58+
},
59+
);
60+
},
61+
deactivated: () => {
62+
unSub.value?.();
63+
notification.value?.destroy();
64+
},
65+
});
66+
4167
const { scrolled, listRef, onClick } = useBackToTop();
4268
const { onItemClick } = usePanelItem();
4369
</script>

src/i18n/en/notification/notification.json

+4
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,9 @@
1010
"notification__release_notes": {
1111
"message": "Release notes",
1212
"description": "The text of the link to the release notes."
13+
},
14+
"notification__sign_out": {
15+
"message": "Sign Out",
16+
"description": "The text of the link to the login page."
1317
}
1418
}

src/services/navbar.service.ts

+1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ import { ref } from 'vue';
33
export class NavbarService {
44
static readonly open = ref(false);
55
static readonly drawer = ref(false);
6+
static readonly dropdown = ref(false);
67
}

src/services/notification.service.ts

+27-16
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { NButton } from 'naive-ui';
55
import { h } from 'vue';
66

77
import type { Mutable } from '@dvcol/common-utils/common';
8-
import type { NotificationOptions, useMessage, useNotification } from 'naive-ui';
8+
import type { ButtonProps, NotificationOptions, useMessage, useNotification } from 'naive-ui';
99

1010
import type { MessagePayload, MessageType } from '~/models/message/message-type.model';
1111

@@ -15,6 +15,8 @@ import { useI18n } from '~/utils/i18n.utils';
1515
type NotificationApi = ReturnType<typeof useNotification>;
1616
type MessageApi = ReturnType<typeof useMessage>;
1717

18+
const renderButton = (label: string, props: ButtonProps) => () => h(NButton, { tertiary: true, type: 'info', ...props }, { default: () => label });
19+
1820
export class NotificationService {
1921
static notification: NotificationApi;
2022
static message: MessageApi;
@@ -44,21 +46,30 @@ export class NotificationService {
4446
content: i18n('release_content'),
4547
meta: new Date(payload.date).toLocaleDateString(),
4648
duration: 10 * 1000,
47-
action: () =>
48-
h(
49-
NButton,
50-
{
51-
tertiary: true,
52-
type: 'info',
53-
onClick: () => {
54-
createTab({ url: ExternaLinks.release });
55-
notification.destroy();
56-
},
57-
},
58-
{
59-
default: () => i18n('release_notes'),
60-
},
61-
),
49+
action: renderButton(i18n('release_notes'), {
50+
onClick: () => {
51+
createTab({ url: ExternaLinks.release });
52+
notification.destroy();
53+
},
54+
}),
55+
});
56+
return notification;
57+
}
58+
59+
static userMismatch({ user, session }: { user: string; session: string }) {
60+
const i18n = useI18n('notification');
61+
const notification = NotificationService.notification.warning({
62+
title: 'Warning: user mismatch',
63+
description: `Displaying ${session} instead of ${user}`,
64+
content: 'Current user does not match the session. \nPlease log out and log in from trakt.tv.',
65+
meta: new Date().toLocaleDateString(),
66+
action: renderButton(i18n('sign_out'), {
67+
type: 'warning',
68+
onClick: () => {
69+
createTab({ url: ExternaLinks.trakt.signOut });
70+
notification.destroy();
71+
},
72+
}),
6273
});
6374
return notification;
6475
}

0 commit comments

Comments
 (0)