pstream is dead; long live pstream
taciturnaxolotl.github.io/pstream-ng/
1import {
2 Dispatch,
3 SetStateAction,
4 useCallback,
5 useMemo,
6 useState,
7} from "react";
8
9import { SubtitleStyling } from "@/stores/subtitles";
10import { usePreviewThemeStore, useThemeStore } from "@/stores/theme";
11
12export function useDerived<T>(
13 initial: T,
14): [T, Dispatch<SetStateAction<T>>, () => void, boolean] {
15 const [overwrite, setOverwrite] = useState<T | undefined>(undefined);
16 const changed = useMemo(
17 () =>
18 JSON.stringify(overwrite) !== JSON.stringify(initial) &&
19 overwrite !== undefined,
20 [overwrite, initial],
21 );
22 const setter = useCallback<Dispatch<SetStateAction<T>>>(
23 (inp) => {
24 if (!(inp instanceof Function)) setOverwrite(inp);
25 else setOverwrite((s) => inp(s !== undefined ? s : initial));
26 },
27 [initial, setOverwrite],
28 );
29 const data = overwrite === undefined ? initial : overwrite;
30
31 const reset = useCallback(() => setOverwrite(undefined), [setOverwrite]);
32
33 return [data, setter, reset, changed];
34}
35
36export function useSettingsState(
37 theme: string | null,
38 appLanguage: string,
39 subtitleStyling: SubtitleStyling,
40 deviceName: string,
41 nickname: string,
42 proxyUrls: string[] | null,
43 backendUrl: string | null,
44 febboxKey: string | null,
45 debridToken: string | null,
46 debridService: string,
47 tidbKey: string | null,
48 profile:
49 | {
50 colorA: string;
51 colorB: string;
52 icon: string;
53 }
54 | undefined,
55 enableThumbnails: boolean,
56 enableAutoplay: boolean,
57 enableSkipCredits: boolean,
58 enableAutoSkipSegments: boolean,
59 enableDiscover: boolean,
60 enableFeatured: boolean,
61 enableDetailsModal: boolean,
62 sourceOrder: string[],
63 enableSourceOrder: boolean,
64 lastSuccessfulSource: string | null,
65 enableLastSuccessfulSource: boolean,
66 embedOrder: string[],
67 enableEmbedOrder: boolean,
68 proxyTmdb: boolean,
69 enableImageLogos: boolean,
70 enableCarouselView: boolean,
71 enableMinimalCards: boolean,
72 forceCompactEpisodeView: boolean,
73 enableLowPerformanceMode: boolean,
74 enableNativeSubtitles: boolean,
75 enableHoldToBoost: boolean,
76 homeSectionOrder: string[],
77 manualSourceSelection: boolean,
78 enableDoubleClickToSeek: boolean,
79 enableAutoResumeOnPlaybackError: boolean,
80 enablePauseOverlay: boolean,
81 customTheme: {
82 primary: string;
83 secondary: string;
84 tertiary: string;
85 },
86) {
87 const [proxyUrlsState, setProxyUrls, resetProxyUrls, proxyUrlsChanged] =
88 useDerived(proxyUrls);
89 const [backendUrlState, setBackendUrl, resetBackendUrl, backendUrlChanged] =
90 useDerived(backendUrl);
91 const [febboxKeyState, setFebboxKey, resetFebboxKey, febboxKeyChanged] =
92 useDerived(febboxKey);
93 const [
94 debridTokenState,
95 setdebridToken,
96 resetdebridToken,
97 debridTokenChanged,
98 ] = useDerived(debridToken);
99 const [
100 debridServiceState,
101 setdebridService,
102 _resetdebridService,
103 debridServiceChanged,
104 ] = useDerived(debridService);
105 const [tidbKeyState, setTIDBKey, resetTIDBKey, tidbKeyChanged] =
106 useDerived(tidbKey);
107 const [themeState, setTheme, resetTheme, themeChanged] = useDerived(theme);
108 const setPreviewTheme = usePreviewThemeStore((s) => s.setPreviewTheme);
109 const resetPreviewTheme = useCallback(
110 () => setPreviewTheme(theme),
111 [setPreviewTheme, theme],
112 );
113 const [
114 appLanguageState,
115 setAppLanguage,
116 resetAppLanguage,
117 appLanguageChanged,
118 ] = useDerived(appLanguage);
119 const [subStylingState, setSubStyling, resetSubStyling, subStylingChanged] =
120 useDerived(subtitleStyling);
121 const [
122 deviceNameState,
123 setDeviceNameState,
124 resetDeviceName,
125 deviceNameChanged,
126 ] = useDerived(deviceName);
127 const [nicknameState, setNicknameState, resetNickname, nicknameChanged] =
128 useDerived(nickname);
129 const [profileState, setProfileState, resetProfile, profileChanged] =
130 useDerived(profile);
131 const [
132 enableThumbnailsState,
133 setEnableThumbnailsState,
134 resetEnableThumbnails,
135 enableThumbnailsChanged,
136 ] = useDerived(enableThumbnails);
137 const [
138 enableAutoplayState,
139 setEnableAutoplayState,
140 resetEnableAutoplay,
141 enableAutoplayChanged,
142 ] = useDerived(enableAutoplay);
143 const [
144 enableSkipCreditsState,
145 setEnableSkipCreditsState,
146 resetEnableSkipCredits,
147 enableSkipCreditsChanged,
148 ] = useDerived(enableSkipCredits);
149 const [
150 enableAutoSkipSegmentsState,
151 setEnableAutoSkipSegmentsState,
152 resetEnableAutoSkipSegments,
153 enableAutoSkipSegmentsChanged,
154 ] = useDerived(enableAutoSkipSegments);
155 const [
156 enableDiscoverState,
157 setEnableDiscoverState,
158 resetEnableDiscover,
159 enableDiscoverChanged,
160 ] = useDerived(enableDiscover);
161 const [
162 enableFeaturedState,
163 setEnableFeaturedState,
164 resetEnableFeatured,
165 enableFeaturedChanged,
166 ] = useDerived(enableFeatured);
167 const [
168 enableDetailsModalState,
169 setEnableDetailsModalState,
170 resetEnableDetailsModal,
171 enableDetailsModalChanged,
172 ] = useDerived(enableDetailsModal);
173 const [
174 enableImageLogosState,
175 setEnableImageLogosState,
176 resetEnableImageLogos,
177 enableImageLogosChanged,
178 ] = useDerived(enableImageLogos);
179 const [
180 sourceOrderState,
181 setSourceOrderState,
182 resetSourceOrder,
183 sourceOrderChanged,
184 ] = useDerived(sourceOrder);
185 const [
186 enableSourceOrderState,
187 setEnableSourceOrderState,
188 resetEnableSourceOrder,
189 enableSourceOrderChanged,
190 ] = useDerived(enableSourceOrder);
191 const [
192 lastSuccessfulSourceState,
193 setLastSuccessfulSourceState,
194 resetLastSuccessfulSource,
195 lastSuccessfulSourceChanged,
196 ] = useDerived(lastSuccessfulSource);
197 const [
198 enableLastSuccessfulSourceState,
199 setEnableLastSuccessfulSourceState,
200 resetEnableLastSuccessfulSource,
201 enableLastSuccessfulSourceChanged,
202 ] = useDerived(enableLastSuccessfulSource);
203 const [
204 embedOrderState,
205 setEmbedOrderState,
206 resetEmbedOrder,
207 embedOrderChanged,
208 ] = useDerived(embedOrder);
209 const [
210 enableEmbedOrderState,
211 setEnableEmbedOrderState,
212 resetEnableEmbedOrder,
213 enableEmbedOrderChanged,
214 ] = useDerived(enableEmbedOrder);
215 const [proxyTmdbState, setProxyTmdbState, resetProxyTmdb, proxyTmdbChanged] =
216 useDerived(proxyTmdb);
217 const [
218 enableCarouselViewState,
219 setEnableCarouselViewState,
220 resetEnableCarouselView,
221 enableCarouselViewChanged,
222 ] = useDerived(enableCarouselView);
223 const [
224 enableMinimalCardsState,
225 setEnableMinimalCardsState,
226 resetEnableMinimalCards,
227 enableMinimalCardsChanged,
228 ] = useDerived(enableMinimalCards);
229 const [
230 forceCompactEpisodeViewState,
231 setForceCompactEpisodeViewState,
232 resetForceCompactEpisodeView,
233 forceCompactEpisodeViewChanged,
234 ] = useDerived(forceCompactEpisodeView);
235 const [
236 enableLowPerformanceModeState,
237 setEnableLowPerformanceModeState,
238 resetEnableLowPerformanceMode,
239 enableLowPerformanceModeChanged,
240 ] = useDerived(enableLowPerformanceMode);
241 const [
242 enableNativeSubtitlesState,
243 setEnableNativeSubtitlesState,
244 resetEnableNativeSubtitles,
245 enableNativeSubtitlesChanged,
246 ] = useDerived(enableNativeSubtitles);
247 const [
248 enableHoldToBoostState,
249 setEnableHoldToBoostState,
250 resetEnableHoldToBoost,
251 enableHoldToBoostChanged,
252 ] = useDerived(enableHoldToBoost);
253 const [
254 homeSectionOrderState,
255 setHomeSectionOrderState,
256 resetHomeSectionOrder,
257 homeSectionOrderChanged,
258 ] = useDerived(homeSectionOrder);
259 const [
260 manualSourceSelectionState,
261 setManualSourceSelectionState,
262 resetManualSourceSelection,
263 manualSourceSelectionChanged,
264 ] = useDerived(manualSourceSelection);
265 const [
266 enableDoubleClickToSeekState,
267 setEnableDoubleClickToSeekState,
268 resetEnableDoubleClickToSeek,
269 enableDoubleClickToSeekChanged,
270 ] = useDerived(enableDoubleClickToSeek);
271 const [
272 enableAutoResumeOnPlaybackErrorState,
273 setEnableAutoResumeOnPlaybackErrorState,
274 resetEnableAutoResumeOnPlaybackError,
275 enableAutoResumeOnPlaybackErrorChanged,
276 ] = useDerived(enableAutoResumeOnPlaybackError);
277 const [
278 enablePauseOverlayState,
279 setEnablePauseOverlayState,
280 resetEnablePauseOverlay,
281 enablePauseOverlayChanged,
282 ] = useDerived(enablePauseOverlay);
283 const [
284 customThemeState,
285 setCustomThemeState,
286 resetCustomTheme,
287 customThemeChanged,
288 ] = useDerived(customTheme);
289 const setCustomThemeStore = useThemeStore((s) => s.setCustomTheme);
290
291 function reset() {
292 resetTheme();
293 resetPreviewTheme();
294 resetAppLanguage();
295 resetSubStyling();
296 resetProxyUrls();
297 resetBackendUrl();
298 resetFebboxKey();
299 resetdebridToken();
300 resetTIDBKey();
301 resetDeviceName();
302 resetNickname();
303 resetProfile();
304 resetEnableThumbnails();
305 resetEnableAutoplay();
306 resetEnableSkipCredits();
307 resetEnableAutoSkipSegments();
308 resetEnableDiscover();
309 resetEnableFeatured();
310 resetEnableDetailsModal();
311 resetEnableImageLogos();
312 resetSourceOrder();
313 resetEnableSourceOrder();
314 resetLastSuccessfulSource();
315 resetEnableLastSuccessfulSource();
316 resetEmbedOrder();
317 resetEnableEmbedOrder();
318 resetProxyTmdb();
319 resetEnableCarouselView();
320 resetEnableMinimalCards();
321 resetForceCompactEpisodeView();
322 resetEnableLowPerformanceMode();
323 resetEnableNativeSubtitles();
324 resetEnableHoldToBoost();
325 resetHomeSectionOrder();
326 resetManualSourceSelection();
327 resetEnableDoubleClickToSeek();
328 resetEnableAutoResumeOnPlaybackError();
329 resetEnablePauseOverlay();
330 resetCustomTheme();
331 }
332
333 const changed =
334 themeChanged ||
335 appLanguageChanged ||
336 subStylingChanged ||
337 deviceNameChanged ||
338 nicknameChanged ||
339 backendUrlChanged ||
340 proxyUrlsChanged ||
341 febboxKeyChanged ||
342 debridTokenChanged ||
343 debridServiceChanged ||
344 tidbKeyChanged ||
345 profileChanged ||
346 enableThumbnailsChanged ||
347 enableAutoplayChanged ||
348 enableSkipCreditsChanged ||
349 enableAutoSkipSegmentsChanged ||
350 enableDiscoverChanged ||
351 enableFeaturedChanged ||
352 enableDetailsModalChanged ||
353 enableImageLogosChanged ||
354 sourceOrderChanged ||
355 enableSourceOrderChanged ||
356 lastSuccessfulSourceChanged ||
357 enableLastSuccessfulSourceChanged ||
358 embedOrderChanged ||
359 enableEmbedOrderChanged ||
360 proxyTmdbChanged ||
361 enableCarouselViewChanged ||
362 enableMinimalCardsChanged ||
363 forceCompactEpisodeViewChanged ||
364 enableLowPerformanceModeChanged ||
365 enableNativeSubtitlesChanged ||
366 enableHoldToBoostChanged ||
367 homeSectionOrderChanged ||
368 manualSourceSelectionChanged ||
369 enableDoubleClickToSeekChanged ||
370 enableAutoResumeOnPlaybackErrorChanged ||
371 enablePauseOverlayChanged ||
372 customThemeChanged;
373
374 return {
375 reset,
376 changed,
377 theme: {
378 state: themeState,
379 set: setTheme,
380 changed: themeChanged,
381 },
382 appLanguage: {
383 state: appLanguageState,
384 set: setAppLanguage,
385 changed: appLanguageChanged,
386 },
387 subtitleStyling: {
388 state: subStylingState,
389 set: setSubStyling,
390 changed: subStylingChanged,
391 },
392 deviceName: {
393 state: deviceNameState,
394 set: setDeviceNameState,
395 changed: deviceNameChanged,
396 },
397 nickname: {
398 state: nicknameState,
399 set: setNicknameState,
400 changed: nicknameChanged,
401 },
402 proxyUrls: {
403 state: proxyUrlsState,
404 set: setProxyUrls,
405 changed: proxyUrlsChanged,
406 },
407 backendUrl: {
408 state: backendUrlState,
409 set: setBackendUrl,
410 changed: backendUrlChanged,
411 },
412 febboxKey: {
413 state: febboxKeyState,
414 set: setFebboxKey,
415 changed: febboxKeyChanged,
416 },
417 debridToken: {
418 state: debridTokenState,
419 set: setdebridToken,
420 changed: debridTokenChanged,
421 },
422 debridService: {
423 state: debridServiceState,
424 set: setdebridService,
425 changed: debridServiceChanged,
426 },
427 tidbKey: {
428 state: tidbKeyState,
429 set: setTIDBKey,
430 changed: tidbKeyChanged,
431 },
432 profile: {
433 state: profileState,
434 set: setProfileState,
435 changed: profileChanged,
436 },
437 enableThumbnails: {
438 state: enableThumbnailsState,
439 set: setEnableThumbnailsState,
440 changed: enableThumbnailsChanged,
441 },
442 enableAutoplay: {
443 state: enableAutoplayState,
444 set: setEnableAutoplayState,
445 changed: enableAutoplayChanged,
446 },
447 enableSkipCredits: {
448 state: enableSkipCreditsState,
449 set: setEnableSkipCreditsState,
450 changed: enableSkipCreditsChanged,
451 },
452 enableAutoSkipSegments: {
453 state: enableAutoSkipSegmentsState,
454 set: setEnableAutoSkipSegmentsState,
455 changed: enableAutoSkipSegmentsChanged,
456 },
457 enableDiscover: {
458 state: enableDiscoverState,
459 set: setEnableDiscoverState,
460 changed: enableDiscoverChanged,
461 },
462 enableFeatured: {
463 state: enableFeaturedState,
464 set: setEnableFeaturedState,
465 changed: enableFeaturedChanged,
466 },
467 enableDetailsModal: {
468 state: enableDetailsModalState,
469 set: setEnableDetailsModalState,
470 changed: enableDetailsModalChanged,
471 },
472 enableImageLogos: {
473 state: enableImageLogosState,
474 set: setEnableImageLogosState,
475 changed: enableImageLogosChanged,
476 },
477 sourceOrder: {
478 state: sourceOrderState,
479 set: setSourceOrderState,
480 changed: sourceOrderChanged,
481 },
482 enableSourceOrder: {
483 state: enableSourceOrderState,
484 set: setEnableSourceOrderState,
485 changed: enableSourceOrderChanged,
486 },
487 lastSuccessfulSource: {
488 state: lastSuccessfulSourceState,
489 set: setLastSuccessfulSourceState,
490 changed: lastSuccessfulSourceChanged,
491 },
492 enableLastSuccessfulSource: {
493 state: enableLastSuccessfulSourceState,
494 set: setEnableLastSuccessfulSourceState,
495 changed: enableLastSuccessfulSourceChanged,
496 },
497 proxyTmdb: {
498 state: proxyTmdbState,
499 set: setProxyTmdbState,
500 changed: proxyTmdbChanged,
501 },
502 embedOrder: {
503 state: embedOrderState,
504 set: setEmbedOrderState,
505 changed: embedOrderChanged,
506 },
507 enableEmbedOrder: {
508 state: enableEmbedOrderState,
509 set: setEnableEmbedOrderState,
510 changed: enableEmbedOrderChanged,
511 },
512 enableCarouselView: {
513 state: enableCarouselViewState,
514 set: setEnableCarouselViewState,
515 changed: enableCarouselViewChanged,
516 },
517 enableMinimalCards: {
518 state: enableMinimalCardsState,
519 set: setEnableMinimalCardsState,
520 changed: enableMinimalCardsChanged,
521 },
522 forceCompactEpisodeView: {
523 state: forceCompactEpisodeViewState,
524 set: setForceCompactEpisodeViewState,
525 changed: forceCompactEpisodeViewChanged,
526 },
527 enableLowPerformanceMode: {
528 state: enableLowPerformanceModeState,
529 set: setEnableLowPerformanceModeState,
530 changed: enableLowPerformanceModeChanged,
531 },
532 enableNativeSubtitles: {
533 state: enableNativeSubtitlesState,
534 set: setEnableNativeSubtitlesState,
535 changed: enableNativeSubtitlesChanged,
536 },
537 enableHoldToBoost: {
538 state: enableHoldToBoostState,
539 set: setEnableHoldToBoostState,
540 changed: enableHoldToBoostChanged,
541 },
542 homeSectionOrder: {
543 state: homeSectionOrderState,
544 set: setHomeSectionOrderState,
545 changed: homeSectionOrderChanged,
546 },
547 manualSourceSelection: {
548 state: manualSourceSelectionState,
549 set: setManualSourceSelectionState,
550 changed: manualSourceSelectionChanged,
551 },
552 enableDoubleClickToSeek: {
553 state: enableDoubleClickToSeekState,
554 set: setEnableDoubleClickToSeekState,
555 changed: enableDoubleClickToSeekChanged,
556 },
557 enableAutoResumeOnPlaybackError: {
558 state: enableAutoResumeOnPlaybackErrorState,
559 set: setEnableAutoResumeOnPlaybackErrorState,
560 changed: enableAutoResumeOnPlaybackErrorChanged,
561 },
562 enablePauseOverlay: {
563 state: enablePauseOverlayState,
564 set: setEnablePauseOverlayState,
565 changed: enablePauseOverlayChanged,
566 },
567 customTheme: {
568 state: customThemeState,
569 set: (v: { primary: string; secondary: string; tertiary: string }) => {
570 setCustomThemeState(v);
571 setCustomThemeStore(v);
572 },
573 changed: customThemeChanged,
574 },
575 };
576}