import { browser } from '$app/environment'; import root from '$lib/Utility/root'; import { get, writable } from 'svelte/store'; import settingsSyncPulled from './settingsSyncPulled'; import settingsSyncTimes from './settingsSyncTimes'; import identity from './identity'; const VERSION = '1.0.1'; export interface Settings { cacheMangaMinutes: number; cacheMinutes: number; displayUpcomingAnimeCollapsed: boolean; displayAnimeCollapsed: boolean; displayMangaCollapsed: boolean; displayNotStarted: boolean; displayUnresolved: boolean; calculateChaptersRoundedDown: boolean; displayOutboundLinksTo: 'anilist' | 'livechartme' | 'animeschedule' | 'myanimelist'; displayPausedMedia: boolean; displayLimitListHeight: boolean; displaySocialButton: boolean; calculateGuessingDisabled: boolean; displayHoverNavigation: boolean; displayTitleFormat: 'english' | 'romaji' | 'native'; calculateGuessMethod: 'median' | 'iqr_median' | 'iqr_mode' | 'mode'; calculateDisableOutOfDateVolumeWarning: boolean; calculatePreferNativeChapterCount: boolean; displayPlannedAnime: boolean; displayFurigana: boolean; displayAoButa: | 'kaede' | 'mai' | 'mai_2' | 'nodoka' | 'rio' | 'sakuta' | 'shouko' | 'tomoe' | 'random' | 'none'; disableManga: boolean; disableAnime: boolean; disableUpcomingAnime: boolean; display24HourTime: boolean; displayCountdownRightAligned: boolean; displayNativeCountdown: boolean; displayHoverCover: boolean; displayDisableAnimations: boolean; displayDisableNotifications: boolean; displayCoverModeAnime: boolean; displayCoverModeManga: boolean; displayCoverWidth: number; displayShortCountdown: boolean; displayScheduleListMode: boolean; displayLanguage: 'en' | 'ja'; displayDisableLastActivityWarning: boolean; settingsSync: boolean; settingsVersion?: string; displayBlurAdultContent: boolean; displayCopyMediaTitleNotLink: boolean; displayTotalDueEpisodes: boolean; displayTotalEpisodes: boolean; displayAniListNotifications: boolean; displayFiltersIncludeCompleted: boolean; displayDataSaver: boolean; debugDummyLists: boolean; displayScheduleFilterList: boolean; displayReverseSort: boolean; displayAnimeSort: 'difference' | 'start_date' | 'end_date' | 'time_remaining'; displayMediaListFilter: boolean; displayCustomCSS: string; displayMediaRoulette: boolean; } const defaultSettings: Settings = { // Display displayOutboundLinksTo: 'anilist', displayPausedMedia: true, displayPlannedAnime: true, displayLimitListHeight: false, displaySocialButton: false, displayUnresolved: false, displayTitleFormat: 'english', displayFurigana: false, displayHoverNavigation: false, displayNotStarted: false, displayUpcomingAnimeCollapsed: false, displayAnimeCollapsed: false, displayMangaCollapsed: false, displayAoButa: 'none', disableManga: false, disableAnime: false, disableUpcomingAnime: false, display24HourTime: false, displayCountdownRightAligned: false, displayNativeCountdown: false, displayHoverCover: false, displayDisableAnimations: false, displayDisableNotifications: false, displayCoverModeAnime: true, displayCoverModeManga: true, displayCoverWidth: 100, // 116.609 displayShortCountdown: false, displayScheduleListMode: false, displayLanguage: 'en', displayDisableLastActivityWarning: false, displayBlurAdultContent: true, displayCopyMediaTitleNotLink: false, displayTotalDueEpisodes: false, displayTotalEpisodes: false, displayAniListNotifications: false, displayFiltersIncludeCompleted: false, displayDataSaver: false, displayScheduleFilterList: false, displayReverseSort: false, displayAnimeSort: 'time_remaining', displayMediaListFilter: false, displayCustomCSS: '', displayMediaRoulette: false, // Debug debugDummyLists: false, // Calculation calculateChaptersRoundedDown: true, calculateDisableOutOfDateVolumeWarning: false, calculateGuessingDisabled: true, calculateGuessMethod: 'iqr_mode', calculatePreferNativeChapterCount: false, // Cache cacheMangaMinutes: 120, cacheMinutes: 30, // Sync settingsSync: false, settingsVersion: VERSION }; const createStore = () => { const initialValue = browser ? JSON.parse(localStorage.getItem('settings') || JSON.stringify(defaultSettings)) : defaultSettings; const store = writable(initialValue); let state: Settings = initialValue; store.subscribe((value) => { state = value; if (browser) localStorage.setItem('settings', JSON.stringify(value)); }); return { subscribe: store.subscribe, set: store.set, update: store.update, reset: () => store.set(defaultSettings), get: () => { const keys = Object.keys(defaultSettings); const settingsKeys = Object.keys(state); const updatedSettings = { ...state }; for (const key of keys) if (!settingsKeys.includes(key)) (updatedSettings as unknown as Record)[key] = ( defaultSettings as unknown as Record )[key]; if (browser) localStorage.setItem('settings', JSON.stringify(updatedSettings)); return updatedSettings; }, setKey: (key: keyof Settings, value: unknown) => store.update((settings) => ({ ...settings, [key]: value })) }; }; const settings = createStore(); settings.subscribe((value) => { if (!browser) return; if (value.settingsSync && get(settingsSyncPulled) === true) { fetch(root(`/api/configuration?id=${get(identity).id}`)).then((response) => { if (response.ok) response.json().then((data) => { const isEqualsJson = (firstObject: Settings, secondObject: Settings) => { type AnyObject = { [key: string]: unknown }; return ( Object.keys(firstObject).length === Object.keys(secondObject).length && Object.keys(firstObject).every( (key) => (firstObject as unknown as AnyObject)[key] === (secondObject as unknown as AnyObject)[key] ) ); }; if (data?.configuration && !isEqualsJson(data.configuration, value)) fetch(root(`/api/configuration`), { method: 'PUT', body: JSON.stringify(value) }).then((response) => { if (response.ok) console.log('Pushed local configuration'); settingsSyncTimes.update((times) => ({ ...times, lastPush: new Date() })); }); }); }); } }); export default settings;