| 1 | import { saveSettingsDebounced } from '../../script.js'; |
| 2 | |
| 3 | const MIGRATED_MARKER = '__migrated'; |
| 4 | const 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 | */ |
| 40 | class 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 | */ |
| 145 | export const accountStorage = new AccountStorage(); |