Browse Source

Add clear databases confirmation modal

develop
Apostolos Fanakis 4 years ago
parent
commit
201c0a79c8
  1. 11
      packages/concordia-app/public/locales/en/translation.json
  2. 151
      packages/concordia-app/src/components/ClearDatabasesModal/index.jsx
  3. 43
      packages/concordia-app/src/layouts/MainLayout/MainLayoutMenu/index.jsx
  4. 28
      packages/concordia-app/src/utils/indexedDB/indexedDBUtils.js

11
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",

151
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 (
<>
<p>
{t('clear.databases.modal.clearing.progress.message')}
</p>
</>
);
}
if (user.hasSignedUp) {
return (
<>
<p>
{t('clear.databases.modal.description.pre')}
</p>
<p>
{t('clear.databases.modal.description.body.user')}
</p>
<Form>
<Form.Field>
<label htmlFor="form-clear-databases-field-confirm">
{t('clear.databases.modal.form.username.label.user')}
</label>
<Input
id="form-clear-databases-field-confirm"
name="confirmationInput"
value={confirmationInput}
onChange={handleInputChange}
/>
</Form.Field>
</Form>
</>
);
}
return (
<>
<p>
{t('clear.databases.modal.description.pre')}
</p>
<Form>
<Form.Field>
<label htmlFor="form-clear-databases-field-confirm">
{t('clear.databases.modal.form.username.label.guest')}
</label>
<Input
id="form-clear-databases-field-confirm"
name="confirmationInput"
value={confirmationInput}
onChange={handleInputChange}
/>
</Form.Field>
</Form>
</>
);
}, [confirmationInput, isClearing, t, user.hasSignedUp]);
return useMemo(() => (
<Modal
onClose={onCancelTry}
open={open}
size="small"
>
<Modal.Header>
{isClearing
? t('clear.databases.modal.clearing.progress.title')
: t('clear.databases.modal.title')}
</Modal.Header>
<Modal.Content>
<Modal.Description>
{modalContent}
</Modal.Description>
</Modal.Content>
{!isClearing && (
<Modal.Actions>
<Button color="black" basic onClick={onCancelTry} disabled={isClearing}>
{t('clear.databases.modal.cancel.button')}
</Button>
<Button onClick={handleSubmit} negative disabled={!userConfirmed}>
{t('clear.databases.modal.clear.button')}
</Button>
</Modal.Actions>
)}
</Modal>
), [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;

43
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 (
<AppContext.Consumer>
{() => (
@ -24,6 +39,14 @@ const MainLayoutMenu = () => {
>
<img src={appLogo} alt="app_logo" />
</Menu.Item>
<Menu.Item
link
name="clear-databases"
key="clear-databases"
onClick={handleClearDatabasesClick}
>
{t('topbar.button.clear.databases')}
</Menu.Item>
<Menu.Menu position="right">
{hasSignedUp && history.location.pathname === '/home' && (
<Menu.Item
@ -57,17 +80,13 @@ const MainLayoutMenu = () => {
{t('topbar.button.register')}
</Menu.Item>
)}
<Menu.Item
link
name="purge"
key="purge"
onClick={async () => {
await purgeIndexedDBs();
}}
>
Purge
</Menu.Item>
</Menu.Menu>
<ClearDatabasesModal
open={isClearDatabasesOpen}
onDatabasesCleared={handleDatabasesCleared}
onCancel={handleCancelDatabasesClear}
/>
</Menu>
)}
</AppContext.Consumer>

28
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;

Loading…
Cancel
Save