Blame Raw
· · · 145 lines (3.7 KB)
0 contributors
1import { saveSettingsDebounced } from '../../script.js';
2
3const MIGRATED_MARKER = '__migrated';
4const MIGRATABLE_KEYS = [
5 /^AlertRegex_/,
6 /^AlertWI_/,
7 /^Assets_SkipConfirm_/,
8 /^Characters_PerPage$/,
9 /^DataBank_sortField$/,
10 /^DataBank_sortOrder$/,
11 /^extension_update_nag$/,
12 /^extensions_sortByName$/,
13 /^FeatherlessModels_PerPage$/,
14 /^GroupMembers_PerPage$/,
15 /^GroupCandidates_PerPage$/,
16 /^LNavLockOn$/,
17 /^LNavOpened$/,
18 /^mediaWarningShown:/,
19 /^NavLockOn$/,
20 /^NavOpened$/,
21 /^Personas_PerPage$/,
22 /^Personas_GridView$/,
23 /^Proxy_SkipConfirm_/,
24 /^qr--executeShortcut$/,
25 /^qr--syntax$/,
26 /^qr--tabSize$/,
27 /^qr--wrap$/,
28 /^RegenerateWithCtrlEnter$/,
29 /^SelectedNavTab$/,
30 /^sendAsNamelessWarningShown$/,
31 /^StoryStringValidationCache$/,
32 /^WINavOpened$/,
33 /^WI_PerPage$/,
34 /^world_info_sort_order$/,
35];
36
37/**
38 * Provides access to account storage of arbitrary key-value pairs.
39 */
40class AccountStorage {
41 /**
42 * @type {Record<string, string>} Storage state
43 */
44 #state = {};
45
46 /**
47 * @type {boolean} If the storage was initialized
48 */
49 #ready = false;
50
51 #migrateLocalStorage() {
52 const localStorageKeys = [];
53 for (let i = 0; i < globalThis.localStorage.length; i++) {
54 localStorageKeys.push(globalThis.localStorage.key(i));
55 }
56 for (const key of localStorageKeys) {
57 if (MIGRATABLE_KEYS.some(k => k.test(key))) {
58 const value = globalThis.localStorage.getItem(key);
59 this.#state[key] = value;
60 globalThis.localStorage.removeItem(key);
61 }
62 }
63 }
64
65 /**
66 * Initialize the account storage.
67 * @param {Object} state Initial state
68 */
69 init(state) {
70 if (state && typeof state === 'object') {
71 this.#state = Object.assign(this.#state, state);
72 }
73
74 if (!Object.hasOwn(this.#state, MIGRATED_MARKER)) {
75 this.#migrateLocalStorage();
76 this.#state[MIGRATED_MARKER] = '1';
77 saveSettingsDebounced();
78 }
79
80 this.#ready = true;
81 }
82
83 /**
84 * Get the value of a key in account storage.
85 * @param {string} key Key to get
86 * @returns {string|null} Value of the key
87 */
88 getItem(key) {
89 if (!this.#ready) {
90 console.warn(`AccountStorage not ready (trying to read from ${key})`);
91 }
92
93 return Object.hasOwn(this.#state, key) ? String(this.#state[key]) : null;
94 }
95
96 /**
97 * Set a key in account storage.
98 * @param {string} key Key to set
99 * @param {string} value Value to set
100 */
101 setItem(key, value) {
102 if (!this.#ready) {
103 console.warn(`AccountStorage not ready (trying to write to ${key})`);
104 }
105
106 const hasPropertySet = Object.hasOwn(this.#state, key) && this.#state[key] === String(value);
107
108 if (hasPropertySet) {
109 return;
110 }
111
112 this.#state[key] = String(value);
113 saveSettingsDebounced();
114 }
115
116 /**
117 * Remove a key from account storage.
118 * @param {string} key Key to remove
119 */
120 removeItem(key) {
121 if (!this.#ready) {
122 console.warn(`AccountStorage not ready (trying to remove ${key})`);
123 }
124
125 if (!Object.hasOwn(this.#state, key)) {
126 return;
127 }
128
129 delete this.#state[key];
130 saveSettingsDebounced();
131 }
132
133 /**
134 * Gets a snapshot of the storage state.
135 * @returns {Record<string, string>} A deep clone of the storage state
136 */
137 getState() {
138 return structuredClone(this.#state);
139 }
140}
141
142/**
143 * Account storage instance.
144 */
145export const accountStorage = new AccountStorage();