1
0
mirror of https://github.com/XFox111/TabsAsideExtension.git synced 2026-04-22 07:58:01 +03:00

feat: ability to disable cloud collection storage

This commit is contained in:
2025-05-04 14:21:10 +03:00
parent 70ed16c286
commit e59782973b
10 changed files with 103 additions and 9 deletions
+45 -7
View File
@@ -1,4 +1,6 @@
import { useDialog } from "@/contexts/DialogProvider"; import { useDialog } from "@/contexts/DialogProvider";
import { cloudDisabled, setCloudStorage } from "@/features/collectionStorage";
import { useDangerStyles } from "@/hooks/useDangerStyles";
import useStorageInfo from "@/hooks/useStorageInfo"; import useStorageInfo from "@/hooks/useStorageInfo";
import { Button, Field, MessageBar, MessageBarBody, MessageBarTitle, ProgressBar } from "@fluentui/react-components"; import { Button, Field, MessageBar, MessageBarBody, MessageBarTitle, ProgressBar } from "@fluentui/react-components";
import { ArrowDownload20Regular, ArrowUpload20Regular } from "@fluentui/react-icons"; import { ArrowDownload20Regular, ArrowUpload20Regular } from "@fluentui/react-icons";
@@ -10,9 +12,17 @@ export default function StorageSection(): React.ReactElement
{ {
const { bytesInUse, storageQuota, usedStorageRatio } = useStorageInfo(); const { bytesInUse, storageQuota, usedStorageRatio } = useStorageInfo();
const [importResult, setImportResult] = useState<boolean | null>(null); const [importResult, setImportResult] = useState<boolean | null>(null);
const [isCloudDisabled, setCloudDisabled] = useState<boolean>(null!);
const dialog = useDialog(); const dialog = useDialog();
const cls = useOptionsStyles(); const cls = useOptionsStyles();
const dangerCls = useDangerStyles();
useEffect(() =>
{
cloudDisabled.getValue().then(setCloudDisabled);
return cloudDisabled.watch(setCloudDisabled);
}, []);
const handleImport = (): void => const handleImport = (): void =>
dialog.pushPrompt({ 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 ( return (
<> <>
<Field { isCloudDisabled === false &&
label={ i18n.t("options_page.storage.capacity.title") } <Field
hint={ i18n.t("options_page.storage.capacity.description", [(bytesInUse / 1024).toFixed(1), storageQuota / 1024]) } label={ i18n.t("options_page.storage.capacity.title") }
validationState={ usedStorageRatio >= 0.8 ? "error" : undefined } hint={ i18n.t("options_page.storage.capacity.description", [(bytesInUse / 1024).toFixed(1), storageQuota / 1024]) }
> validationState={ usedStorageRatio >= 0.8 ? "error" : undefined }
<ProgressBar value={ usedStorageRatio } thickness="large" /> >
</Field> <ProgressBar value={ usedStorageRatio } thickness="large" />
</Field>
}
{ isCloudDisabled === true &&
<Button appearance="primary" onClick={ () => setCloudStorage(true) }>
{ i18n.t("options_page.storage.enable") }
</Button>
}
<div className={ cls.horizontalButtons }> <div className={ cls.horizontalButtons }>
<Button icon={ <ArrowDownload20Regular /> } onClick={ exportData }> <Button icon={ <ArrowDownload20Regular /> } onClick={ exportData }>
@@ -59,6 +86,17 @@ export default function StorageSection(): React.ReactElement
</MessageBarBody> </MessageBarBody>
</MessageBar> </MessageBar>
} }
{ isCloudDisabled === false &&
<div className={ cls.horizontalButtons }>
<Button
appearance="subtle" className={ dangerCls.buttonSubtle }
onClick={ handleDisableCloud }
>
{ i18n.t("options_page.storage.disable") }
</Button>
</div>
}
</> </>
); );
} }
+3
View File
@@ -4,6 +4,9 @@ export * from "./utils/getCollections";
export { default as getCollections } from "./utils/getCollections"; export { default as getCollections } from "./utils/getCollections";
export { default as resoveConflict } from "./utils/resolveConflict"; export { default as resoveConflict } from "./utils/resolveConflict";
export { default as saveCollections } from "./utils/saveCollections"; export { default as saveCollections } from "./utils/saveCollections";
export { default as setCloudStorage } from "./utils/setCloudStorage";
export const collectionCount = collectionStorage.count; export const collectionCount = collectionStorage.count;
export const graphics = collectionStorage.graphics; export const graphics = collectionStorage.graphics;
export const cloudDisabled = collectionStorage.disableCloud;
@@ -8,5 +8,6 @@ export const collectionStorage =
localCollections: storage.defineItem<CollectionItem[]>("local:collections", { fallback: [] }), localCollections: storage.defineItem<CollectionItem[]>("local:collections", { fallback: [] }),
count: storage.defineItem<number>("local:count", { fallback: 0 }), count: storage.defineItem<number>("local:count", { fallback: 0 }),
graphics: storage.defineItem<GraphicsStorage>("local:graphics", { fallback: {} }), graphics: storage.defineItem<GraphicsStorage>("local:graphics", { fallback: {} }),
disableCloud: storage.defineItem<boolean>("sync:disableCloud", { fallback: false }),
maxChunkCount: 12 maxChunkCount: 12
}; };
@@ -1,14 +1,17 @@
import { CollectionItem } from "@/models/CollectionModels"; import { CollectionItem } from "@/models/CollectionModels";
import getLogger from "@/utils/getLogger";
import { collectionStorage } from "./collectionStorage"; import { collectionStorage } from "./collectionStorage";
import getCollectionsFromCloud from "./getCollectionsFromCloud"; import getCollectionsFromCloud from "./getCollectionsFromCloud";
import getCollectionsFromLocal from "./getCollectionsFromLocal"; import getCollectionsFromLocal from "./getCollectionsFromLocal";
import saveCollectionsToLocal from "./saveCollectionsToLocal"; import saveCollectionsToLocal from "./saveCollectionsToLocal";
import getLogger from "@/utils/getLogger";
const logger = getLogger("getCollections"); const logger = getLogger("getCollections");
export default async function getCollections(): Promise<[CollectionItem[], CloudStorageIssueType | null]> 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 lastUpdatedLocal: number = await collectionStorage.localLastUpdated.getValue();
const lastUpdatedSync: number = await collectionStorage.syncLastUpdated.getValue(); const lastUpdatedSync: number = await collectionStorage.syncLastUpdated.getValue();
@@ -1,6 +1,7 @@
import { CollectionItem, GraphicsStorage } from "@/models/CollectionModels"; import { CollectionItem, GraphicsStorage } from "@/models/CollectionModels";
import getLogger from "@/utils/getLogger"; import getLogger from "@/utils/getLogger";
import sendNotification from "@/utils/sendNotification"; import sendNotification from "@/utils/sendNotification";
import { collectionStorage } from "./collectionStorage";
import saveCollectionsToCloud from "./saveCollectionsToCloud"; import saveCollectionsToCloud from "./saveCollectionsToCloud";
import saveCollectionsToLocal from "./saveCollectionsToLocal"; import saveCollectionsToLocal from "./saveCollectionsToLocal";
import updateGraphics from "./updateGraphics"; import updateGraphics from "./updateGraphics";
@@ -16,7 +17,7 @@ export default async function saveCollections(
const timestamp: number = Date.now(); const timestamp: number = Date.now();
await saveCollectionsToLocal(collections, timestamp); await saveCollectionsToLocal(collections, timestamp);
if (updateCloud) if (updateCloud && await collectionStorage.disableCloud.getValue() !== true)
try try
{ {
await saveCollectionsToCloud(collections, timestamp); await saveCollectionsToCloud(collections, timestamp);
@@ -0,0 +1,19 @@
import { collectionStorage } from "./collectionStorage";
import saveCollectionsToCloud from "./saveCollectionsToCloud";
export default async function setCloudStorage(enable: boolean): Promise<void>
{
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();
}
}
+14
View File
@@ -24,5 +24,19 @@ export const useDangerStyles = makeStyles({
backgroundColor: tokens.colorStatusDangerBackground3Pressed backgroundColor: tokens.colorStatusDangerBackground3Pressed
} }
} }
},
buttonSubtle:
{
color: tokens.colorStatusDangerForeground1,
"&:hover":
{
color: tokens.colorStatusDangerForeground2,
"&:active":
{
color: tokens.colorStatusDangerForeground3
}
}
} }
}); });
+5
View File
@@ -116,6 +116,11 @@ options_page:
warning_title: "This is an irreversible action" 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." 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" 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: about:
title: "About" title: "About"
developed_by: "Developed by Eugene Fox" developed_by: "Developed by Eugene Fox"
+5
View File
@@ -116,6 +116,11 @@ options_page:
warning_title: "Это необратимое действие!" warning_title: "Это необратимое действие!"
warning_text: "Оно перезапишет все ваши данные. Убедитесь, что вы выбрали правильный файл, иначе это может привести к повреждению или потере данных. Рекомендуется сначала экспортировать данные." warning_text: "Оно перезапишет все ваши данные. Убедитесь, что вы выбрали правильный файл, иначе это может привести к повреждению или потере данных. Рекомендуется сначала экспортировать данные."
proceed: "Выбрать файл" proceed: "Выбрать файл"
enable: "Включить облачное хранилище"
disable: "Отключить облачное хранилище"
disable_prompt:
text: "Это действие отключит синхронизацию коллекций между вашими устройствами. Настройки расширения продолжат храниться в облаке."
action: "Отключить и перезагрузить расширение"
about: about:
title: "О расширении" title: "О расширении"
developed_by: "Разработчик: Евгений Лис" developed_by: "Разработчик: Евгений Лис"
+5
View File
@@ -116,6 +116,11 @@ options_page:
warning_title: "Це незворотна дія!" warning_title: "Це незворотна дія!"
warning_text: "Вона перезапише всі ваші дані. Переконайтеся, що ви вибрали правильний файл, інакше це може призвести до пошкодження або втрати даних. Рекомендується спочатку експортувати дані." warning_text: "Вона перезапише всі ваші дані. Переконайтеся, що ви вибрали правильний файл, інакше це може призвести до пошкодження або втрати даних. Рекомендується спочатку експортувати дані."
proceed: "Вибрати файл" proceed: "Вибрати файл"
disable: "Вимкнути хмарне сховище"
enable: "Увімкнути хмарне сховище"
disable_prompt:
text: "Ця дія вимкне синхронізацію колекцій між вашими пристроями. Налаштування продовжать зберігатися у хмарі."
action: "Вимкнути та перезавантажити розширення"
about: about:
title: "О розширенні" title: "О розширенні"
developed_by: "Розробник: Євген Лис" developed_by: "Розробник: Євген Лис"