diff --git a/src/background/helper.js b/src/background/helper.js index 865b417..6df4ecb 100644 --- a/src/background/helper.js +++ b/src/background/helper.js @@ -8,8 +8,10 @@ import { fetchLatestConfigOnLogin } from '@utils/config'; import { logger } from '@utils/logger'; import { isObjectEmpty } from '@utils/misc'; -// Since content-script cannot access the Browser.tabs API, use background to notify (redirect to) other tabs -// Note that background cannot use axios: Adapter 'http' is not available in the build. Axios is based on XMLHttpRequest which is not available in service worker +/* + * Since content-script cannot access the Browser.tabs API, use background to notify (redirect to) other tabs + * Note that background cannot use axios: Adapter 'http' is not available in the build. Axios is based on XMLHttpRequest which is not available in service worker + */ export const onCollectedWordsUpdate = async message => { sendMessageToOtherContentScripts(message); }; diff --git a/src/popup/PopupManager/index.jsx b/src/popup/PopupManager/index.jsx index f6eda94..f1ab282 100644 --- a/src/popup/PopupManager/index.jsx +++ b/src/popup/PopupManager/index.jsx @@ -40,7 +40,7 @@ const PopupManager = ({ children }) => { /* * Since this function is registered to the listener in the beginning, the setExtMessageValue becomes a "stale" version - * Even if using the callback function syntax cannot let us get the latest value + * Even using the callback function syntax cannot let us get the latest value */ const onMessageListener = (message, sender) => { const sdr = sender.tab ? `from a content script :${sender.tab.url}` : 'from the extension'; diff --git a/src/shared/hooks/useAsync.js b/src/shared/hooks/useAsync.js new file mode 100644 index 0000000..29a21cc --- /dev/null +++ b/src/shared/hooks/useAsync.js @@ -0,0 +1,24 @@ +import { useCallback, useEffect, useState } from 'react'; + +export default useAsync = (callback, deps = []) => { + const [loading, setLoading] = useState(true); + const [error, setError] = useState(); + const [value, setValue] = useState(); + + const callbackMemoized = useCallback(() => { + setLoading(true); + setError(null); + setValue(null); + + callback() + .then(setValue) + .catch(setError) + .finally(() => setLoading(false)); + }, deps); + + useEffect(() => { + callbackMemoized(); + }, [callbackMemoized]); + + return { loading, error, value }; +}; diff --git a/src/shared/hooks/useStorage.js b/src/shared/hooks/useStorage.js new file mode 100644 index 0000000..0374538 --- /dev/null +++ b/src/shared/hooks/useStorage.js @@ -0,0 +1,31 @@ +import { useCallback, useState, useEffect } from 'react'; + +export const useLocalStorage = (key, defaultValue) => useStorage(key, defaultValue, window.localStorage); + +export const useSessionStorage = (key, defaultValue) => useStorage(key, defaultValue, window.sessionStorage); + +export default useStorage = (key, defaultValue, storageObject) => { + const [value, setValue] = useState(() => { + const jsonValue = storageObject.getItem(key); + if (jsonValue !== null) { + return JSON.parse(jsonValue); + } + + if (typeof defaultValue === 'function') { + return defaultValue(); + } else { + return defaultValue; + } + }); + + useEffect(() => { + if (value === undefined) return storageObject.removeItem(key); + storageObject.setItem(key, JSON.stringify(value)); + }, [key, value, storageObject]); + + const remove = useCallback(() => { + setValue(undefined); + }, []); + + return [value, setValue, remove]; +}; diff --git a/src/shared/utils/config.js b/src/shared/utils/config.js index 2e53a51..0319702 100644 --- a/src/shared/utils/config.js +++ b/src/shared/utils/config.js @@ -95,6 +95,7 @@ export const isConfigEqual = (config1 = {}, config2 = {}) => { if (Object.keys(c1).length !== Object.keys(c2).length) { return false; } + const misMatches = Object.entries(c1).filter(([key, val]) => { if (isArray(val)) { return val.length !== c2[key].length; // Not accurate but acceptable