Skip to content

Commit 93669c6

Browse files
committed
fix(loading): make loading hysteresis customisable
1 parent 9dedf29 commit 93669c6

File tree

8 files changed

+156
-47
lines changed

8 files changed

+156
-47
lines changed

src/components/views/settings/SettingsTabs.vue

+46-23
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import IconClose from '~/components/icons/IconCloseSmall.vue';
1515
import IconPencil from '~/components/icons/IconPencil.vue';
1616
1717
import SettingsFormItem from '~/components/views/settings/SettingsFormItem.vue';
18+
import { loadingHysteresisOptions } from '~/models/loading-hysteresis.model';
1819
import { pageSizeOptions, pageSizeOptionsWithZero } from '~/models/page-size.model';
1920
import { ProgressType } from '~/models/progress-type.model';
2021
import { Route } from '~/models/router.model';
@@ -53,6 +54,7 @@ const {
5354
progressType,
5455
enableRatings,
5556
backgroundColor,
57+
loadingHysteresis,
5658
} = useExtensionSettingsStoreRefs();
5759
5860
const { getIcon, fetchLists } = useListsStore();
@@ -75,6 +77,14 @@ const listOptions = computed<ListOption[]>(() =>
7577
})),
7678
);
7779
80+
const loadingOption = computed(() =>
81+
loadingHysteresisOptions.map(({ label, value }) => ({
82+
label:
83+
typeof label === 'string' ? i18n(label, 'common', 'loading', 'hysteresis') : label,
84+
value,
85+
})),
86+
);
87+
7888
const renderLabel = (option: ListOption) => [
7989
h(NIcon, {
8090
style: {
@@ -139,29 +149,6 @@ const onColor = () => {
139149

140150
<template>
141151
<div ref="container" class="tabs-container">
142-
<!-- Default tab -->
143-
<SettingsFormItem :label="i18n('label_default_tab')">
144-
<div class="form-button">
145-
<input ref="picker" v-model="backgroundColor" type="color" class="color-picker" />
146-
<NButtonGroup
147-
class="color-picker-button-group"
148-
:style="{ '--color-picker': backgroundColor }"
149-
>
150-
<NButton round tertiary @click="onColor">
151-
Picker
152-
<template #icon>
153-
<NIcon><IconPencil /></NIcon>
154-
</template>
155-
</NButton>
156-
<NButton circle tertiary type="error" @click="backgroundColor = 'transparent'">
157-
<template #icon>
158-
<NIcon><IconClose /></NIcon>
159-
</template>
160-
</NButton>
161-
</NButtonGroup>
162-
</div>
163-
</SettingsFormItem>
164-
165152
<!-- Default tab -->
166153
<SettingsFormItem :label="i18n('label_default_tab')">
167154
<NSelect
@@ -187,6 +174,38 @@ const onColor = () => {
187174
multiple
188175
/>
189176
</SettingsFormItem>
177+
<!-- Loading bar -->
178+
<SettingsFormItem :label="i18n('label_loading_bar')">
179+
<NSelect
180+
v-model:value="loadingHysteresis"
181+
class="loading-bar"
182+
:to="container"
183+
:options="loadingOption"
184+
/>
185+
</SettingsFormItem>
186+
187+
<!-- Background color -->
188+
<SettingsFormItem :label="i18n('label_background_color')">
189+
<div class="form-button">
190+
<input ref="picker" v-model="backgroundColor" type="color" class="color-picker" />
191+
<NButtonGroup
192+
class="color-picker-button-group"
193+
:style="{ '--color-picker': backgroundColor }"
194+
>
195+
<NButton round tertiary @click="onColor">
196+
Picker
197+
<template #icon>
198+
<NIcon><IconPencil /></NIcon>
199+
</template>
200+
</NButton>
201+
<NButton circle tertiary type="error" @click="backgroundColor = 'transparent'">
202+
<template #icon>
203+
<NIcon><IconClose /></NIcon>
204+
</template>
205+
</NButton>
206+
</NButtonGroup>
207+
</div>
208+
</SettingsFormItem>
190209

191210
<!-- Restore tab -->
192211
<SettingsFormItem :label="i18n('label_restore_tab')">
@@ -396,6 +415,10 @@ const onColor = () => {
396415
min-width: 5.5rem;
397416
}
398417
418+
.loading-bar {
419+
width: 10rem;
420+
}
421+
399422
.hidden-form-item {
400423
max-height: 0;
401424
overflow: hidden;
+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"common__loading__hysteresis__disabled": {
3+
"message": "Disabled",
4+
"description": "The loading bar is disabled"
5+
},
6+
"common__loading__hysteresis__0": {
7+
"message": "Immediate",
8+
"description": "The loading bar is Immediate"
9+
},
10+
"common__loading__hysteresis__100": {
11+
"message": "100ms",
12+
"description": "The loading bar is delayed by 100ms"
13+
},
14+
"common__loading__hysteresis__200": {
15+
"message": "200ms",
16+
"description": "The loading bar is delayed by 200ms"
17+
},
18+
"common__loading__hysteresis__500": {
19+
"message": "500ms",
20+
"description": "The loading bar is delayed by 300ms"
21+
},
22+
"common__loading__hysteresis__1000": {
23+
"message": "1 second",
24+
"description": "The loading bar is delayed by 1 second"
25+
},
26+
"common__loading__hysteresis__2000": {
27+
"message": "2 seconds",
28+
"description": "The loading bar is delayed by 2 seconds"
29+
},
30+
"common__loading__hysteresis__5000": {
31+
"message": "5 seconds",
32+
"description": "The loading bar is delayed by 5 seconds"
33+
},
34+
"common__loading__hysteresis__10000": {
35+
"message": "10 seconds",
36+
"description": "The loading bar is delayed by 10 seconds"
37+
}
38+
}

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

+8
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
11
{
2+
"settings__tabs__label_background_color": {
3+
"message": "Background Color",
4+
"description": "Label for the 'Background Color' setting"
5+
},
6+
"settings__tabs__label_loading_bar": {
7+
"message": "Loading Bar Hysteresis",
8+
"description": "Label for the 'Loading Bar Hysteresis' setting"
9+
},
210
"settings__tabs__label_default_tab": {
311
"message": "Default tab",
412
"description": "Label for the 'Default tab' setting"
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import type { SelectOption } from 'naive-ui';
2+
3+
export const loadingHysteresisOptions: SelectOption[] = [
4+
{ label: 'disabled', value: -1 } as const,
5+
{ label: '0', value: 0 } as const,
6+
{ label: '100', value: 100 } as const,
7+
{ label: '200', value: 200 } as const,
8+
{ label: '500', value: 500 } as const,
9+
{ label: '1000', value: 1000 } as const,
10+
{ label: '2000', value: 2000 } as const,
11+
{ label: '5000', value: 5000 } as const,
12+
{ label: '10000', value: 10000 } as const,
13+
] as const;

src/models/page-size.model.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ export const PageSize: Record<string, number> = {
1010
} as const;
1111

1212
export const pageSizeOptions: SelectOption[] = [
13-
{ label: '50', value: 50 },
14-
{ label: '100', value: 100 },
15-
{ label: '200', value: 200 },
16-
{ label: '500', value: 500 },
17-
{ label: '1000', value: 1000 },
13+
{ label: '50', value: 50 } as const,
14+
{ label: '100', value: 100 } as const,
15+
{ label: '200', value: 200 } as const,
16+
{ label: '500', value: 500 } as const,
17+
{ label: '1000', value: 1000 } as const,
1818
] as const;
1919

2020
export const pageSizeOptionsWithZero: SelectOption[] = [{ label: '0', value: 0 }, ...pageSizeOptions] as const;

src/services/loading-bar.service.ts

+29-9
Original file line numberDiff line numberDiff line change
@@ -6,33 +6,53 @@ import { Logger } from '~/services/logger.service';
66

77
export class LoadingBarService {
88
private static instance: ReturnType<typeof useLoadingBar>;
9-
private static loading = ref(false);
9+
private static counter = ref(0);
1010

1111
static init(instance: ReturnType<typeof useLoadingBar>) {
1212
this.instance = instance;
1313
}
1414

1515
static get isLoading() {
16-
return this.loading.value;
16+
return this.counter.value > 0;
1717
}
1818

19-
static start() {
20-
if (!this.instance?.start) Logger.warn('LoadingBarService instance is not initialized');
19+
private static increment(value = 1) {
20+
this.counter.value += value;
21+
}
22+
23+
private static decrement(value = 1) {
24+
if (this.counter.value - value < 0) {
25+
this.counter.value = 0;
26+
return Logger.error('LoadingBarService counter is negative, resetting to 0');
27+
}
28+
this.counter.value -= value;
29+
}
30+
31+
private static debounceStart(debounce: number) {
32+
return setTimeout(() => {
33+
if (this.isLoading) this.instance.start();
34+
}, debounce);
35+
}
36+
37+
static start(debounce: number = 0) {
38+
this.increment();
39+
if (!this.instance?.start) return Logger.warn('LoadingBarService instance is not initialized');
40+
if (debounce) return this.debounceStart(debounce);
2141
this.instance.start();
22-
this.loading.value = true;
2342
}
2443

2544
static finish() {
2645
if (!this.isLoading) return;
27-
if (!this.instance?.finish) Logger.warn('LoadingBarService instance is not initialized');
46+
this.decrement();
47+
if (this.isLoading) return;
48+
if (!this.instance?.finish) return Logger.warn('LoadingBarService instance is not initialized');
2849
this.instance.finish();
29-
this.loading.value = false;
3050
}
3151

3252
static error() {
3353
if (!this.isLoading) return;
34-
if (!this.instance?.error) Logger.warn('LoadingBarService instance is not initialized');
54+
this.counter.value = 0;
55+
if (!this.instance?.error) return Logger.warn('LoadingBarService instance is not initialized');
3556
this.instance.error();
36-
this.loading.value = false;
3757
}
3858
}

src/services/trakt.service.ts

+6-10
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ import { traktClientSettings } from '~/settings/trakt.api';
7575
import { useAppStateStore } from '~/stores/app-state.store';
7676
import { useSimklStore } from '~/stores/data/simkl.store';
7777
import { useAuthSettingsStore } from '~/stores/settings/auth.store';
78+
import { useExtensionSettingsStore } from '~/stores/settings/extension.store';
7879
import { useUserSettingsStore } from '~/stores/settings/user.store';
7980
import { CachePrefix, TraktChromeCacheStore } from '~/utils/cache.utils';
8081
import { cancellablePaginatedWriteJson, getCachedProgressEndpoint, getSessionUser } from '~/utils/trakt-service.utils';
@@ -211,15 +212,14 @@ export class TraktService {
211212
}
212213

213214
static async loadingBar<T>(query: Promise<T> | CancellablePromise<T>) {
214-
const timeout = setTimeout(() => LoadingBarService.start(), 1000);
215+
const { loadingHysteresis } = useExtensionSettingsStore();
216+
if (loadingHysteresis >= 0) LoadingBarService.start(loadingHysteresis);
215217
try {
216218
await query;
217-
LoadingBarService.finish();
219+
if (loadingHysteresis >= 0) LoadingBarService.finish();
218220
} catch (error) {
219-
LoadingBarService.error();
221+
if (loadingHysteresis >= 0) LoadingBarService.error();
220222
ErrorService.registerError(error);
221-
} finally {
222-
clearTimeout(timeout);
223223
}
224224
}
225225

@@ -239,11 +239,7 @@ export class TraktService {
239239

240240
this.simklClient.onCall(async call => {
241241
Logger.debug('SimklClient.onCall', call);
242-
try {
243-
await call.query;
244-
} catch (error) {
245-
ErrorService.registerError(error);
246-
}
242+
await this.loadingBar(call.query);
247243
});
248244

249245
this.tmdbClient.onAuthChange(async _auth => {

src/stores/settings/extension.store.ts

+11
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ type ExtensionSettings = {
4747
progressType: ProgressTypes;
4848
enableRatings: boolean;
4949
backgroundColor: string;
50+
loadingHysteresis: number;
5051
};
5152

5253
const ExtensionSettingsConstants = {
@@ -66,6 +67,7 @@ export const useExtensionSettingsStore = defineStore(ExtensionSettingsConstants.
6667
const progressType = ref<ExtensionSettings['progressType']>(ProgressType.Show);
6768
const enableRatings = ref(false);
6869
const backgroundColor = ref('transparent');
70+
const loadingHysteresis = ref(-1);
6971

7072
const clearState = () => {
7173
Object.assign(cacheRetention, DefaultCacheRetention);
@@ -88,6 +90,7 @@ export const useExtensionSettingsStore = defineStore(ExtensionSettingsConstants.
8890
progressType: progressType.value,
8991
enableRatings: enableRatings.value,
9092
backgroundColor: backgroundColor.value,
93+
loadingHysteresis: loadingHysteresis.value,
9194
}),
9295
500,
9396
);
@@ -121,6 +124,7 @@ export const useExtensionSettingsStore = defineStore(ExtensionSettingsConstants.
121124
});
122125
}
123126
if (restored?.backgroundColor !== undefined) backgroundColor.value = restored.backgroundColor;
127+
if (restored?.loadingHysteresis !== undefined) loadingHysteresis.value = restored.loadingHysteresis;
124128

125129
if (!chromeRuntimeId) routeDictionary[Route.Progress] = false;
126130
};
@@ -230,6 +234,13 @@ export const useExtensionSettingsStore = defineStore(ExtensionSettingsConstants.
230234
saveState().catch(err => Logger.error('Failed to save background color extension settings', { value, err }));
231235
},
232236
}),
237+
loadingHysteresis: computed({
238+
get: () => loadingHysteresis.value,
239+
set: (value: number) => {
240+
loadingHysteresis.value = value;
241+
saveState().catch(err => Logger.error('Failed to save loading hysteresis extension settings', { value, err }));
242+
},
243+
}),
233244
};
234245
});
235246

0 commit comments

Comments
 (0)