Skip to content

Commit b711fb2

Browse files
committed
feat(router): adds default tab selector and fix restore toggles
1 parent e2a2e9d commit b711fb2

File tree

7 files changed

+133
-26
lines changed

7 files changed

+133
-26
lines changed

src/components/views/settings/SettingsTabs.vue

+43-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script lang="ts" setup>
22
import { NSelect, NSwitch } from 'naive-ui';
33
4-
import { ref } from 'vue';
4+
import { computed, ref } from 'vue';
55
66
import SettingsFormItem from '~/components/views/settings/SettingsFormItem.vue';
77
import { pageSizeOptions } from '~/models/page-size.model';
@@ -17,18 +17,41 @@ import { useI18n } from '~/utils';
1717
1818
const i18n = useI18n('settings', 'tabs');
1919
20-
const { enabledTabs, restoreRoute, routeDictionary } = useExtensionSettingsStoreRefs();
20+
const { enabledTabs, restoreRoute, routeDictionary, restorePanel, defaultTab } =
21+
useExtensionSettingsStoreRefs();
2122
const { toggleTab } = useExtensionSettingsStore();
2223
2324
const { pageSize: historyPageSize } = useHistoryStoreRefs();
2425
const { pageSize: listPageSize } = useListStoreRefs();
2526
const { pageSize: searchPageSize } = useSearchStoreRefs();
2627
28+
const tabsOptions = computed(() =>
29+
enabledTabs.value.map(([route, state]) => ({
30+
label: i18n(route, 'route'),
31+
value: route,
32+
disabled: !state,
33+
})),
34+
);
35+
36+
const disabled = computed(
37+
() => enabledTabs.value.filter(([_, state]) => state).length <= 1,
38+
);
39+
2740
const container = ref();
2841
</script>
2942

3043
<template>
3144
<div ref="container" class="tabs-container">
45+
<!-- Default tab -->
46+
<SettingsFormItem :label="i18n('label_default_tab')">
47+
<NSelect
48+
v-model:value="defaultTab"
49+
class="default-tab"
50+
:to="container"
51+
:options="tabsOptions"
52+
/>
53+
</SettingsFormItem>
54+
3255
<!-- Restore tab -->
3356
<SettingsFormItem :label="i18n('label_restore_tab')">
3457
<NSwitch v-model:value="restoreRoute" class="form-switch">
@@ -37,6 +60,14 @@ const container = ref();
3760
</NSwitch>
3861
</SettingsFormItem>
3962

63+
<!-- Restore panel -->
64+
<SettingsFormItem :label="i18n('label_restore_panel')">
65+
<NSwitch v-model:value="restorePanel" class="form-switch">
66+
<template #checked>{{ i18n('on', 'common', 'button') }}</template>
67+
<template #unchecked>{{ i18n('off', 'common', 'button') }}</template>
68+
</NSwitch>
69+
</SettingsFormItem>
70+
4071
<!-- Enable tabs -->
4172
<SettingsFormItem
4273
v-for="[route, state] of enabledTabs"
@@ -48,7 +79,12 @@ const container = ref();
4879
: undefined
4980
"
5081
>
51-
<NSwitch :value="state" class="form-switch" @click="toggleTab(route)">
82+
<NSwitch
83+
:value="state"
84+
class="form-switch"
85+
:disabled="state && disabled"
86+
@update:value="toggleTab(route)"
87+
>
5288
<template #checked>{{ i18n('on', 'common', 'button') }}</template>
5389
<template #unchecked>{{ i18n('off', 'common', 'button') }}</template>
5490
</NSwitch>
@@ -106,4 +142,8 @@ const container = ref();
106142
.form-select {
107143
min-width: 5.5rem;
108144
}
145+
146+
.default-tab {
147+
min-width: 7rem;
148+
}
109149
</style>

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

+8
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
{
2+
"settings__tabs__label_default_tab": {
3+
"message": "Default tab",
4+
"description": "Label for the 'Default tab' setting"
5+
},
26
"settings__tabs__label_restore_tab": {
37
"message": "Restore active tab on startup",
48
"description": "Label for the 'Restore active tab on startup' setting"
59
},
10+
"settings__tabs__label_restore_panel": {
11+
"message": "Restore active panel on startup",
12+
"description": "Label for the 'Restore active panel on startup' setting"
13+
},
614
"settings__tabs__label_route_progress": {
715
"message": "Enable the progress tab",
816
"description": "Label for the 'Enable the progress tab' setting"

src/router/create-router.ts

+32-7
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { watch } from 'vue';
12
import { createRouter as createVueRouter, createWebHashHistory, type LocationQueryRaw } from 'vue-router';
23

34
import { isLoginAuthResponseSuccess } from '~/models/login/login-auth-response';
@@ -8,28 +9,47 @@ import { useAppStateStoreRefs } from '~/stores/app-state.store';
89
import { useRouterStore, useRouterStoreRefs } from '~/stores/router.store';
910

1011
import { useAuthSettingsStoreRefs } from '~/stores/settings/auth.store';
12+
import { useExtensionSettingsStore, useExtensionSettingsStoreRefs } from '~/stores/settings/extension.store';
1113
import { logger } from '~/stores/settings/log.store';
1214

1315
export type RouterOptions = { baseName?: string; baseUrl?: string };
1416
export const createRouter = ({ baseName = '', baseUrl = import.meta.env.BASE_URL }: RouterOptions) => {
17+
const { restoreRoute, restorePanel, defaultTab } = useExtensionSettingsStoreRefs();
18+
const { initExtensionSettingsStore } = useExtensionSettingsStore();
19+
1520
const { setBaseName, setBaseUrl, setRouteParam } = useRouterStore();
1621
const { routeParam } = useRouterStoreRefs();
1722

1823
setBaseName(baseName);
1924
setBaseUrl(baseUrl);
2025

2126
const _routes = routes.map(r => ({ ...r, path: `${baseName}${r.path}` }));
22-
const _routesWithBase = [
27+
const _redirects = [
28+
{
29+
name: 'root',
30+
path: `${baseName}/`,
31+
redirect: { name: defaultTab.value },
32+
},
2333
{
34+
name: 'root-eager',
2435
path: `${baseName}/:pathMatch(.*)`,
25-
redirect: { name: Route.Calendar },
36+
redirect: { name: defaultTab.value },
2637
},
27-
..._routes,
2838
];
2939

3040
const router = createVueRouter({
3141
history: createWebHashHistory(baseUrl),
32-
routes: _routesWithBase,
42+
routes: [..._redirects, ..._routes],
43+
});
44+
45+
watch(defaultTab, _default => {
46+
_redirects.forEach(r => {
47+
router.removeRoute(r.name);
48+
router.addRoute({
49+
...r,
50+
redirect: { name: _default },
51+
});
52+
});
3353
});
3454

3555
const { waitAppReady } = useAppStateStoreRefs();
@@ -66,9 +86,14 @@ export const createRouter = ({ baseName = '', baseUrl = import.meta.env.BASE_URL
6686

6787
restoreLastRoute().then(async _route => {
6888
const isNotLogin = _route?.name && _route?.name !== Route.Login;
69-
if (!isNotLogin) await router.push({ name: Route.Calendar });
70-
else {
71-
if (_route.meta.base) await router.push(_route.meta.base);
89+
await initExtensionSettingsStore();
90+
if (!isNotLogin || !restoreRoute.value) {
91+
await router.push({ name: defaultTab.value });
92+
} else {
93+
if (_route.meta.base) {
94+
await router.push(_route.meta.base);
95+
if (!restorePanel.value) return;
96+
}
7297
await router.push(_route);
7398
}
7499
});

src/router/routes.ts

-4
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,6 @@ const panelRoutes = (base: Route): RouteRecordRaw[] => [
4040
];
4141

4242
export const routes: RouteRecordRaw[] = [
43-
{
44-
path: '/',
45-
redirect: { name: Route.Calendar },
46-
},
4743
{
4844
path: `/${Route.Login}`,
4945
name: Route.Login,

src/stores/settings/extension.store.ts

+46-8
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,16 @@ type ExtensionSettings = {
3434
cacheRetention: CacheRetentionState;
3535
enabledRoutes: RouteDictionary;
3636
restoreRoute: boolean;
37-
progressTab: boolean;
38-
logLevel: string;
37+
restorePanel: boolean;
3938
};
4039

4140
export const useExtensionSettingsStore = defineStore('settings.extension', () => {
4241
const cacheRetention = reactive<CacheRetentionState>(DefaultCacheRetention);
4342
const routeDictionary = reactive<RouteDictionary>(DefaultRoutes);
4443
const restoreRoute = ref(true);
45-
// todo: use each store value instead in settings page
44+
const restorePanel = ref(false);
45+
const defaultTab = ref(Route.Calendar);
46+
const initialized = ref<Promise<boolean>>();
4647

4748
const clearState = () => {
4849
Object.assign(cacheRetention, DefaultCacheRetention);
@@ -56,8 +57,9 @@ export const useExtensionSettingsStore = defineStore('settings.extension', () =>
5657
cacheRetention: toRaw(cacheRetention),
5758
enabledRoutes: toRaw(routeDictionary),
5859
restoreRoute: restoreRoute.value,
60+
restorePanel: restorePanel.value,
5961
}),
60-
1000,
62+
500,
6163
);
6264

6365
const setRetention = (retention: Partial<CacheRetentionState>, persist = true) => {
@@ -74,25 +76,61 @@ export const useExtensionSettingsStore = defineStore('settings.extension', () =>
7476
if (restored?.cacheRetention !== undefined) setRetention(restored.cacheRetention, false);
7577
if (restored?.enabledRoutes !== undefined) Object.assign(routeDictionary, restored.enabledRoutes);
7678
if (restored?.restoreRoute !== undefined) restoreRoute.value = restored.restoreRoute;
79+
if (restored?.restorePanel !== undefined) restorePanel.value = restored.restorePanel;
80+
};
81+
82+
const saveDefaultTab = debounce(() => storage.sync.set('settings.extension.default-tab', defaultTab.value), 500);
83+
84+
const restoreDefaultTab = async () => {
85+
const restored = await storage.sync.get<Route>('settings.extension.default-tab');
86+
if (restored) defaultTab.value = restored;
7787
};
7888

7989
const initExtensionSettingsStore = async () => {
80-
await restoreState();
90+
if (!initialized.value) initialized.value = Promise.all([restoreState(), restoreDefaultTab()]).then(() => true);
91+
return initialized.value;
92+
};
93+
94+
const setDefaultTab = (value?: Route) => {
95+
if (!value) return;
96+
defaultTab.value = value;
97+
saveDefaultTab().catch(err => logger.error('Failed to save default tab in extension settings', { value, err }));
8198
};
8299

83100
const toggleTab = (tab: Route) => {
84101
routeDictionary[tab] = !routeDictionary[tab];
85-
saveState().catch(err => logger.error('Failed to save extension settings', { tab, err }));
102+
if (defaultTab.value === tab && !routeDictionary[tab]) {
103+
setDefaultTab((Object.keys(routeDictionary) as Route[]).find(key => routeDictionary[key]));
104+
}
105+
saveState().catch(err => logger.error('Failed to save enabled tab extension settings', { tab, err }));
86106
};
87107

88108
return {
89109
initExtensionSettingsStore,
110+
restoreDefaultTab,
90111
saveState,
91112
clearState,
92-
restoreRoute,
113+
restoreRoute: computed({
114+
get: () => restoreRoute.value,
115+
set: (value: boolean) => {
116+
restoreRoute.value = value;
117+
saveState().catch(err => logger.error('Failed to save restore route extension settings', { value, err }));
118+
},
119+
}),
120+
restorePanel: computed({
121+
get: () => restorePanel.value,
122+
set: (value: boolean) => {
123+
restorePanel.value = value;
124+
saveState().catch(err => logger.error('Failed to save restore panel extension settings', { value, err }));
125+
},
126+
}),
93127
toggleTab,
94128
routeDictionary,
95-
enabledTabs: computed(() => Object.entries(routeDictionary).filter(([r]) => r !== Route.Calendar) as [Route, boolean][]),
129+
defaultTab: computed({
130+
get: () => defaultTab.value,
131+
set: setDefaultTab,
132+
}),
133+
enabledTabs: computed(() => Object.entries(routeDictionary) as [Route, boolean][]),
96134
enabledRoutes: computed(() =>
97135
Object.entries(routeDictionary)
98136
.filter(([, value]) => value)

src/stores/settings/links.store.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,9 @@ export const useLinksStore = defineStore('settings.links', () => {
7474
clearProxy(linkScopeDictionary);
7575
};
7676

77-
const saveState = debounce(() => storage.sync.set('settings.links', { enabled: enabled.value, backgroundLink: backgroundLink.value }), 1000);
78-
const saveAlias = debounce(() => storage.sync.set('settings.links.aliases', aliasDictionary), 1000);
79-
const saveLinks = debounce(() => storage.sync.set('settings.links.links', linkDictionary), 1000);
77+
const saveState = debounce(() => storage.sync.set('settings.links', { enabled: enabled.value, backgroundLink: backgroundLink.value }), 500);
78+
const saveAlias = debounce(() => storage.sync.set('settings.links.aliases', aliasDictionary), 500);
79+
const saveLinks = debounce(() => storage.sync.set('settings.links.links', linkDictionary), 500);
8080

8181
const addToScope = (scope: CustomLinkScopes, link: CustomLink) => {
8282
if (!linkScopeDictionary[scope]) linkScopeDictionary[scope] = {};

src/stores/settings/log.store.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export const useLogStore = defineStore('settings.logs', () => {
2222
logLevel.value = LogLevel.Warn;
2323
};
2424

25-
const saveState = debounce(() => storage.sync.set('settings.logs', { logLevel: logLevel.value }), 1000);
25+
const saveState = debounce(() => storage.sync.set('settings.logs', { logLevel: logLevel.value }), 500);
2626

2727
const restoreState = async () => {
2828
const restored = await storage.sync.get<LogSettings>('settings.logs');

0 commit comments

Comments
 (0)