redux-devtools/extension/src/options/syncOptions.ts
renovate[bot] 922985f9ea
chore(deps): update dependency prettier to v3 (#1434)
* chore(deps): update dependency prettier to v3

* Format

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
2023-07-12 18:03:20 +00:00

165 lines
4.5 KiB
TypeScript

import { FilterState, FilterStateValue } from '../pageScript/api/filters';
export interface Options {
readonly useEditor: number;
readonly editor: string;
readonly projectPath: string;
readonly maxAge: number;
readonly filter: FilterStateValue;
readonly allowlist: string;
readonly denylist: string;
readonly shouldCatchErrors: boolean;
readonly inject: boolean;
readonly urls: string;
readonly showContextMenus: boolean;
}
interface OldOrNewOptions {
readonly useEditor: number;
readonly editor: string;
readonly projectPath: string;
readonly maxAge: number;
readonly filter:
| FilterStateValue
| 'WHITELIST_SPECIFIC'
| 'BLACKLIST_SPECIFIC'
| boolean;
readonly allowlist: string;
readonly denylist: string;
readonly whitelist: string;
readonly blacklist: string;
readonly shouldCatchErrors: boolean;
readonly inject: boolean;
readonly urls: string;
readonly showContextMenus: boolean;
}
let options: Options | undefined;
let subscribers: ((options: Options) => void)[] = [];
export interface OptionsMessage {
readonly options: Options;
}
type ToAllTabs = (msg: OptionsMessage) => void;
const save =
(toAllTabs: ToAllTabs | undefined) =>
<K extends keyof Options>(key: K, value: Options[K]) => {
let obj: { [K1 in keyof Options]?: Options[K1] } = {};
obj[key] = value;
chrome.storage.sync.set(obj);
options![key] = value;
toAllTabs!({ options: options! });
subscribers.forEach((s) => s(options!));
};
const migrateOldOptions = (oldOptions: OldOrNewOptions): Options => ({
...oldOptions,
filter:
// Migrate the old `filter` option from 2.2.1
typeof oldOptions.filter === 'boolean'
? oldOptions.filter && oldOptions.whitelist.length > 0
? FilterState.ALLOWLIST_SPECIFIC
: oldOptions.filter
? FilterState.DENYLIST_SPECIFIC
: FilterState.DO_NOT_FILTER
: oldOptions.filter === 'WHITELIST_SPECIFIC'
? FilterState.ALLOWLIST_SPECIFIC
: oldOptions.filter === 'BLACKLIST_SPECIFIC'
? FilterState.DENYLIST_SPECIFIC
: oldOptions.filter,
});
const get = (callback: (options: Options) => void) => {
if (options) callback(options);
else {
chrome.storage.sync.get(
{
useEditor: 0,
editor: '',
projectPath: '',
maxAge: 50,
filter: FilterState.DO_NOT_FILTER,
whitelist: '',
blacklist: '',
allowlist: '',
denylist: '',
shouldCatchErrors: false,
inject: true,
urls: '^https?://localhost|0\\.0\\.0\\.0:\\d+\n^https?://.+\\.github\\.io',
showContextMenus: true,
},
function (items) {
options = migrateOldOptions(items as OldOrNewOptions);
callback(options);
},
);
}
};
const subscribe = (callback: (options: Options) => void) => {
subscribers = subscribers.concat(callback);
};
const toReg = (str: string) =>
str !== '' ? str.split('\n').filter(Boolean).join('|') : null;
export const injectOptions = (newOptions: Options) => {
if (!newOptions) return;
options = {
...newOptions,
allowlist:
newOptions.filter !== FilterState.DO_NOT_FILTER
? toReg(newOptions.allowlist)!
: newOptions.allowlist,
denylist:
newOptions.filter !== FilterState.DO_NOT_FILTER
? toReg(newOptions.denylist)!
: newOptions.denylist,
};
let s = document.createElement('script');
s.type = 'text/javascript';
s.appendChild(
document.createTextNode(
'window.devToolsOptions = Object.assign(window.devToolsOptions||{},' +
JSON.stringify(options) +
');',
),
);
(document.head || document.documentElement).appendChild(s);
s.parentNode!.removeChild(s);
};
export const getOptionsFromBg = () => {
/* chrome.runtime.sendMessage({ type: 'GET_OPTIONS' }, response => {
if (response && response.options) injectOptions(response.options);
});
*/
get((newOptions) => {
injectOptions(newOptions);
}); // Legacy
};
export const isAllowed = (localOptions = options) =>
!localOptions ||
localOptions.inject ||
!localOptions.urls ||
location.href.match(toReg(localOptions.urls)!);
export interface SyncOptions {
readonly save: <K extends keyof Options>(key: K, value: Options[K]) => void;
readonly get: (callback: (options: Options) => void) => void;
readonly subscribe: (callback: (options: Options) => void) => void;
}
export default function syncOptions(toAllTabs?: ToAllTabs): SyncOptions {
if (toAllTabs && !options) get(() => {}); // Initialize
return {
save: save(toAllTabs),
get: get,
subscribe: subscribe,
};
}