diff --git a/entrypoints/options/layouts/StorageSection.tsx b/entrypoints/options/layouts/StorageSection.tsx index d212be4..7e97037 100644 --- a/entrypoints/options/layouts/StorageSection.tsx +++ b/entrypoints/options/layouts/StorageSection.tsx @@ -1,4 +1,6 @@ import { useDialog } from "@/contexts/DialogProvider"; +import { cloudDisabled, setCloudStorage } from "@/features/collectionStorage"; +import { useDangerStyles } from "@/hooks/useDangerStyles"; import useStorageInfo from "@/hooks/useStorageInfo"; import { Button, Field, MessageBar, MessageBarBody, MessageBarTitle, ProgressBar } from "@fluentui/react-components"; import { ArrowDownload20Regular, ArrowUpload20Regular } from "@fluentui/react-icons"; @@ -10,9 +12,17 @@ export default function StorageSection(): React.ReactElement { const { bytesInUse, storageQuota, usedStorageRatio } = useStorageInfo(); const [importResult, setImportResult] = useState(null); + const [isCloudDisabled, setCloudDisabled] = useState(null!); const dialog = useDialog(); const cls = useOptionsStyles(); + const dangerCls = useDangerStyles(); + + useEffect(() => + { + cloudDisabled.getValue().then(setCloudDisabled); + return cloudDisabled.watch(setCloudDisabled); + }, []); const handleImport = (): void => dialog.pushPrompt({ @@ -30,15 +40,32 @@ export default function StorageSection(): React.ReactElement ) }); + const handleDisableCloud = (): void => + dialog.pushPrompt({ + title: i18n.t("options_page.storage.disable"), + content: i18n.t("options_page.storage.disable_prompt.text"), + confirmText: i18n.t("options_page.storage.disable_prompt.action"), + destructive: true, + onConfirm: () => setCloudStorage(false) + }); + return ( <> - = 0.8 ? "error" : undefined } - > - - + { isCloudDisabled === false && + = 0.8 ? "error" : undefined } + > + + + } + + { isCloudDisabled === true && + + }
+
+ } ); } diff --git a/features/collectionStorage/index.ts b/features/collectionStorage/index.ts index 2e5983d..2045680 100644 --- a/features/collectionStorage/index.ts +++ b/features/collectionStorage/index.ts @@ -4,6 +4,9 @@ export * from "./utils/getCollections"; export { default as getCollections } from "./utils/getCollections"; export { default as resoveConflict } from "./utils/resolveConflict"; export { default as saveCollections } from "./utils/saveCollections"; +export { default as setCloudStorage } from "./utils/setCloudStorage"; export const collectionCount = collectionStorage.count; export const graphics = collectionStorage.graphics; + +export const cloudDisabled = collectionStorage.disableCloud; diff --git a/features/collectionStorage/utils/collectionStorage.ts b/features/collectionStorage/utils/collectionStorage.ts index e6443e4..41c4c17 100644 --- a/features/collectionStorage/utils/collectionStorage.ts +++ b/features/collectionStorage/utils/collectionStorage.ts @@ -8,5 +8,6 @@ export const collectionStorage = localCollections: storage.defineItem("local:collections", { fallback: [] }), count: storage.defineItem("local:count", { fallback: 0 }), graphics: storage.defineItem("local:graphics", { fallback: {} }), + disableCloud: storage.defineItem("sync:disableCloud", { fallback: false }), maxChunkCount: 12 }; diff --git a/features/collectionStorage/utils/getCollections.ts b/features/collectionStorage/utils/getCollections.ts index 3e81897..a428378 100644 --- a/features/collectionStorage/utils/getCollections.ts +++ b/features/collectionStorage/utils/getCollections.ts @@ -1,14 +1,17 @@ import { CollectionItem } from "@/models/CollectionModels"; +import getLogger from "@/utils/getLogger"; import { collectionStorage } from "./collectionStorage"; import getCollectionsFromCloud from "./getCollectionsFromCloud"; import getCollectionsFromLocal from "./getCollectionsFromLocal"; import saveCollectionsToLocal from "./saveCollectionsToLocal"; -import getLogger from "@/utils/getLogger"; const logger = getLogger("getCollections"); export default async function getCollections(): Promise<[CollectionItem[], CloudStorageIssueType | null]> { + if (await collectionStorage.disableCloud.getValue() === true) + return [await getCollectionsFromLocal(), null]; + const lastUpdatedLocal: number = await collectionStorage.localLastUpdated.getValue(); const lastUpdatedSync: number = await collectionStorage.syncLastUpdated.getValue(); diff --git a/features/collectionStorage/utils/saveCollections.ts b/features/collectionStorage/utils/saveCollections.ts index e266b84..4b312c6 100644 --- a/features/collectionStorage/utils/saveCollections.ts +++ b/features/collectionStorage/utils/saveCollections.ts @@ -1,6 +1,7 @@ import { CollectionItem, GraphicsStorage } from "@/models/CollectionModels"; import getLogger from "@/utils/getLogger"; import sendNotification from "@/utils/sendNotification"; +import { collectionStorage } from "./collectionStorage"; import saveCollectionsToCloud from "./saveCollectionsToCloud"; import saveCollectionsToLocal from "./saveCollectionsToLocal"; import updateGraphics from "./updateGraphics"; @@ -16,7 +17,7 @@ export default async function saveCollections( const timestamp: number = Date.now(); await saveCollectionsToLocal(collections, timestamp); - if (updateCloud) + if (updateCloud && await collectionStorage.disableCloud.getValue() !== true) try { await saveCollectionsToCloud(collections, timestamp); diff --git a/features/collectionStorage/utils/setCloudStorage.ts b/features/collectionStorage/utils/setCloudStorage.ts new file mode 100644 index 0000000..bd392c8 --- /dev/null +++ b/features/collectionStorage/utils/setCloudStorage.ts @@ -0,0 +1,19 @@ +import { collectionStorage } from "./collectionStorage"; +import saveCollectionsToCloud from "./saveCollectionsToCloud"; + +export default async function setCloudStorage(enable: boolean): Promise +{ + if (enable) + { + await collectionStorage.disableCloud.setValue(false); + const collections = await collectionStorage.localCollections.getValue(); + const lastUpdated = await collectionStorage.localLastUpdated.getValue(); + await saveCollectionsToCloud(collections, lastUpdated); + } + else + { + await collectionStorage.disableCloud.setValue(true); + await saveCollectionsToCloud([], 0); + browser.runtime.reload(); + } +} diff --git a/hooks/useDangerStyles.ts b/hooks/useDangerStyles.ts index 787d9ae..929e3b5 100644 --- a/hooks/useDangerStyles.ts +++ b/hooks/useDangerStyles.ts @@ -24,5 +24,19 @@ export const useDangerStyles = makeStyles({ backgroundColor: tokens.colorStatusDangerBackground3Pressed } } + }, + buttonSubtle: + { + color: tokens.colorStatusDangerForeground1, + + "&:hover": + { + color: tokens.colorStatusDangerForeground2, + + "&:active": + { + color: tokens.colorStatusDangerForeground3 + } + } } }); diff --git a/locales/en.yml b/locales/en.yml index 50d1d00..6f0fb64 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -116,6 +116,11 @@ options_page: warning_title: "This is an irreversible action" warning_text: "This will overwrite all your data. Make sure you picked a correct file, otherwise data corruption or loss may occur. It is recommended to export data first." proceed: "Pick a file" + enable: "Enable cloud storage" + disable: "Disable cloud storage" + disable_prompt: + text: "This action will disable collection synchronization between your devices. Extension's settings will still be synchronized." + action: "Disable and reload the extension" about: title: "About" developed_by: "Developed by Eugene Fox" diff --git a/locales/ru.yml b/locales/ru.yml index 5c471a2..207285e 100644 --- a/locales/ru.yml +++ b/locales/ru.yml @@ -116,6 +116,11 @@ options_page: warning_title: "Это необратимое действие!" warning_text: "Оно перезапишет все ваши данные. Убедитесь, что вы выбрали правильный файл, иначе это может привести к повреждению или потере данных. Рекомендуется сначала экспортировать данные." proceed: "Выбрать файл" + enable: "Включить облачное хранилище" + disable: "Отключить облачное хранилище" + disable_prompt: + text: "Это действие отключит синхронизацию коллекций между вашими устройствами. Настройки расширения продолжат храниться в облаке." + action: "Отключить и перезагрузить расширение" about: title: "О расширении" developed_by: "Разработчик: Евгений Лис" diff --git a/locales/uk.yml b/locales/uk.yml index 939ecdd..d34a571 100644 --- a/locales/uk.yml +++ b/locales/uk.yml @@ -116,6 +116,11 @@ options_page: warning_title: "Це незворотна дія!" warning_text: "Вона перезапише всі ваші дані. Переконайтеся, що ви вибрали правильний файл, інакше це може призвести до пошкодження або втрати даних. Рекомендується спочатку експортувати дані." proceed: "Вибрати файл" + disable: "Вимкнути хмарне сховище" + enable: "Увімкнути хмарне сховище" + disable_prompt: + text: "Ця дія вимкне синхронізацію колекцій між вашими пристроями. Налаштування продовжать зберігатися у хмарі." + action: "Вимкнути та перезавантажити розширення" about: title: "О розширенні" developed_by: "Розробник: Євген Лис"