From 201c0a79c88c2cd413b901a30cc36eb387113009 Mon Sep 17 00:00:00 2001 From: Apostolof Date: Thu, 10 Dec 2020 22:02:29 +0200 Subject: [PATCH] Add clear databases confirmation modal --- .../public/locales/en/translation.json | 11 ++ .../components/ClearDatabasesModal/index.jsx | 151 ++++++++++++++++++ .../MainLayout/MainLayoutMenu/index.jsx | 43 +++-- .../src/utils/indexedDB/indexedDBUtils.js | 28 ++-- 4 files changed, 211 insertions(+), 22 deletions(-) create mode 100644 packages/concordia-app/src/components/ClearDatabasesModal/index.jsx diff --git a/packages/concordia-app/public/locales/en/translation.json b/packages/concordia-app/public/locales/en/translation.json index 61d79a1..74f1e18 100644 --- a/packages/concordia-app/public/locales/en/translation.json +++ b/packages/concordia-app/public/locales/en/translation.json @@ -2,6 +2,15 @@ "board.header.no.topics.message": "There are no topics yet!", "board.sub.header.no.topics.guest": "Sign up and be the first to post.", "board.sub.header.no.topics.user": "Be the first to post.", + "clear.databases.modal.cancel.button": "Cancel, keep databases", + "clear.databases.modal.clear.button": "Yes, delete databases", + "clear.databases.modal.clearing.progress.message": "This might take a minute...", + "clear.databases.modal.clearing.progress.title": "Clearing all Concordia databases", + "clear.databases.modal.description.body.user": "Although this action is generally recoverable some of your topics and posts may be permanently lost.", + "clear.databases.modal.description.pre": "You are about to clear the Concordia databases stored locally in your browser.", + "clear.databases.modal.form.username.label.guest": "Please type concordia to confirm.", + "clear.databases.modal.form.username.label.user": "Please type your username to confirm.", + "clear.databases.modal.title": "Clear all Concordia databases. Are you sure?", "custom.loading.tab.pane.default.generic.message": "Magic in the background", "edit.information.modal.form.cancel.button": "Cancel", "edit.information.modal.form.error.invalid.profile.picture.url.message": "The profile picture URL provided is not valid.", @@ -17,6 +26,7 @@ "post.form.subject.field.placeholder": "Subject", "post.list.row.post.id": "#{{id}}", "profile.general.tab.address.row.title": "Account address:", + "profile.general.tab.clear.databases.button.title": "Clear databases", "profile.general.tab.edit.info.button.title": "Edit information", "profile.general.tab.location.row.not.set": "Not set", "profile.general.tab.location.row.title": "Location:", @@ -52,6 +62,7 @@ "register.form.sign.up.step.error.message.header": "Form contains errors", "register.form.sign.up.step.title": "Sign Up", "register.p.account.address": "Account address:", + "topbar.button.clear.databases": "Clear databases", "topbar.button.create.topic": "Create topic", "topbar.button.profile": "Profile", "topbar.button.register": "Sign Up", diff --git a/packages/concordia-app/src/components/ClearDatabasesModal/index.jsx b/packages/concordia-app/src/components/ClearDatabasesModal/index.jsx new file mode 100644 index 0000000..26e47fe --- /dev/null +++ b/packages/concordia-app/src/components/ClearDatabasesModal/index.jsx @@ -0,0 +1,151 @@ +import React, { + useCallback, useMemo, useState, + useEffect, +} from 'react'; +import { + Button, Form, Input, Modal, +} from 'semantic-ui-react'; +import PropTypes from 'prop-types'; +import { useTranslation } from 'react-i18next'; +import { useSelector } from 'react-redux'; +import purgeIndexedDBs from '../../utils/indexedDB/indexedDBUtils'; + +const ClearDatabasesModal = (props) => { + const { + open, onDatabasesCleared, onCancel, + } = props; + const [confirmationInput, setConfirmationInput] = useState(''); + const [userConfirmed, setUserConfirmed] = useState(false); + const [isClearing, setIsClearing] = useState(false); + const user = useSelector((state) => state.user); + const { t } = useTranslation(); + + useEffect(() => { + if (user.hasSignedUp && confirmationInput === user.username) { + setUserConfirmed(true); + } else if (!user.hasSignedUp && confirmationInput === 'concordia') { + setUserConfirmed(true); + } else { + setUserConfirmed(false); + } + }, [confirmationInput, user.hasSignedUp, user.username]); + + const handleSubmit = useCallback(() => { + setIsClearing(true); + + purgeIndexedDBs() + .then(() => { + onDatabasesCleared(); + }).catch((reason) => console.log(reason)); + }, [onDatabasesCleared]); + + const onCancelTry = useCallback(() => { + if (!isClearing) { + setConfirmationInput(''); + onCancel(); + } + }, [isClearing, onCancel]); + + const handleInputChange = (event, { value }) => { setConfirmationInput(value); }; + + const modalContent = useMemo(() => { + if (isClearing) { + return ( + <> +

+ {t('clear.databases.modal.clearing.progress.message')} +

+ + ); + } + + if (user.hasSignedUp) { + return ( + <> +

+ {t('clear.databases.modal.description.pre')} +

+

+ {t('clear.databases.modal.description.body.user')} +

+
+ + + + +
+ + ); + } + + return ( + <> +

+ {t('clear.databases.modal.description.pre')} +

+
+ + + + +
+ + ); + }, [confirmationInput, isClearing, t, user.hasSignedUp]); + + return useMemo(() => ( + + + {isClearing + ? t('clear.databases.modal.clearing.progress.title') + : t('clear.databases.modal.title')} + + + + {modalContent} + + + + {!isClearing && ( + + + + + )} + + ), [handleSubmit, isClearing, modalContent, onCancelTry, open, t, userConfirmed]); +}; + +ClearDatabasesModal.defaultProps = { + open: false, +}; + +ClearDatabasesModal.propTypes = { + open: PropTypes.bool, + onDatabasesCleared: PropTypes.func.isRequired, + onCancel: PropTypes.func.isRequired, +}; + +export default ClearDatabasesModal; diff --git a/packages/concordia-app/src/layouts/MainLayout/MainLayoutMenu/index.jsx b/packages/concordia-app/src/layouts/MainLayout/MainLayoutMenu/index.jsx index 975c40c..3c51293 100644 --- a/packages/concordia-app/src/layouts/MainLayout/MainLayoutMenu/index.jsx +++ b/packages/concordia-app/src/layouts/MainLayout/MainLayoutMenu/index.jsx @@ -1,17 +1,32 @@ -import React from 'react'; +import React, { useState } from 'react'; import { Menu } from 'semantic-ui-react'; import { useTranslation } from 'react-i18next'; import { useHistory } from 'react-router'; import { useSelector } from 'react-redux'; import AppContext from '../../../components/AppContext'; import appLogo from '../../../assets/images/app_logo.png'; -import purgeIndexedDBs from '../../../utils/indexedDB/indexedDBUtils'; +import ClearDatabasesModal from '../../../components/ClearDatabasesModal'; const MainLayoutMenu = () => { const hasSignedUp = useSelector((state) => state.user.hasSignedUp); + const [isClearDatabasesOpen, setIsClearDatabasesOpen] = useState(false); const history = useHistory(); const { t } = useTranslation(); + const handleClearDatabasesClick = () => { + setIsClearDatabasesOpen(true); + }; + + const handleDatabasesCleared = () => { + setIsClearDatabasesOpen(false); + history.push('/home'); + window.location.reload(false); + }; + + const handleCancelDatabasesClear = () => { + setIsClearDatabasesOpen(false); + }; + return ( {() => ( @@ -24,6 +39,14 @@ const MainLayoutMenu = () => { > app_logo + + {t('topbar.button.clear.databases')} + {hasSignedUp && history.location.pathname === '/home' && ( { {t('topbar.button.register')} )} - { - await purgeIndexedDBs(); - }} - > - Purge - + + )} diff --git a/packages/concordia-app/src/utils/indexedDB/indexedDBUtils.js b/packages/concordia-app/src/utils/indexedDB/indexedDBUtils.js index a44fdfa..95374a6 100644 --- a/packages/concordia-app/src/utils/indexedDB/indexedDBUtils.js +++ b/packages/concordia-app/src/utils/indexedDB/indexedDBUtils.js @@ -1,20 +1,28 @@ import { breeze } from '../../redux/store'; -async function purgeIndexedDBs() { +const purgeIndexedDBs = async () => { const { ipfs, orbit } = breeze; + if (orbit) await orbit.stop(); if (ipfs) await ipfs.stop(); + const databases = await indexedDB.databases(); - const dbs = await indexedDB.databases(); - await Promise.all( - dbs.map((db) => new Promise( - (resolve, reject) => { - const request = indexedDB.deleteDatabase(db.name); + return Promise + .all(databases + .filter((database) => database.name !== 'level-js-ethprovider/identities') + .map((database) => new Promise((resolve, reject) => { + const request = indexedDB.deleteDatabase(database.name); + request.onblocked = () => { + Promise.all([ + orbit && orbit.stop ? orbit.stop() : Promise.resolve(), + ipfs && ipfs.stop ? ipfs.stop() : Promise.resolve(), + ]).catch((reason) => { + console.log(reason); + }); + }; request.onsuccess = resolve; request.onerror = reject; - }, - )), - ); -} + }))); +}; export default purgeIndexedDBs;