Browse Source

Merge branch 'purge-button' into 'implement-ui'

Purge button

See merge request ecentrics/apella!7
develop
Apostolos Fanakis 4 years ago
parent
commit
a6c9d51c9b
  1. 2
      packages/concordia-app/package.json
  2. 11
      packages/concordia-app/public/locales/en/translation.json
  3. 151
      packages/concordia-app/src/components/ClearDatabasesModal/index.jsx
  4. 1
      packages/concordia-app/src/index.jsx
  5. 32
      packages/concordia-app/src/layouts/MainLayout/MainLayoutMenu/index.jsx
  6. 1
      packages/concordia-app/src/options/breezeOptions.js
  7. 22
      packages/concordia-app/src/utils/indexedDB/indexedDBUtils.js
  8. 46
      packages/concordia-app/src/utils/indexedDB/patchIndexedDB.js

2
packages/concordia-app/package.json

@ -26,7 +26,7 @@
"dependencies": { "dependencies": {
"@ezerous/breeze": "~0.4.0", "@ezerous/breeze": "~0.4.0",
"@ezerous/drizzle": "~0.4.1", "@ezerous/drizzle": "~0.4.1",
"@ezerous/eth-identity-provider": "^0.1.0", "@ezerous/eth-identity-provider": "~0.1.2",
"@reduxjs/toolkit": "~1.4.0", "@reduxjs/toolkit": "~1.4.0",
"@welldone-software/why-did-you-render": "^6.0.0-rc.1", "@welldone-software/why-did-you-render": "^6.0.0-rc.1",
"concordia-contracts": "~0.1.0", "concordia-contracts": "~0.1.0",

11
packages/concordia-app/public/locales/en/translation.json

@ -2,6 +2,15 @@
"board.header.no.topics.message": "There are no topics yet!", "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.guest": "Sign up and be the first to post.",
"board.sub.header.no.topics.user": "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", "custom.loading.tab.pane.default.generic.message": "Magic in the background",
"edit.information.modal.form.cancel.button": "Cancel", "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.", "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.form.subject.field.placeholder": "Subject",
"post.list.row.post.id": "#{{id}}", "post.list.row.post.id": "#{{id}}",
"profile.general.tab.address.row.title": "Account address:", "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.edit.info.button.title": "Edit information",
"profile.general.tab.location.row.not.set": "Not set", "profile.general.tab.location.row.not.set": "Not set",
"profile.general.tab.location.row.title": "Location:", "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.error.message.header": "Form contains errors",
"register.form.sign.up.step.title": "Sign Up", "register.form.sign.up.step.title": "Sign Up",
"register.p.account.address": "Account address:", "register.p.account.address": "Account address:",
"topbar.button.clear.databases": "Clear databases",
"topbar.button.create.topic": "Create topic", "topbar.button.create.topic": "Create topic",
"topbar.button.profile": "Profile", "topbar.button.profile": "Profile",
"topbar.button.register": "Sign Up", "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;

1
packages/concordia-app/src/index.jsx

@ -1,3 +1,4 @@
import './utils/indexedDB/patchIndexedDB';
import './utils/wdyr'; import './utils/wdyr';
import React, { Suspense } from 'react'; import React, { Suspense } from 'react';
import { render } from 'react-dom'; import { render } from 'react-dom';

32
packages/concordia-app/src/layouts/MainLayout/MainLayoutMenu/index.jsx

@ -1,16 +1,32 @@
import React from 'react'; import React, { useState } from 'react';
import { Menu } from 'semantic-ui-react'; import { Menu } from 'semantic-ui-react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useHistory } from 'react-router'; import { useHistory } from 'react-router';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import AppContext from '../../../components/AppContext'; import AppContext from '../../../components/AppContext';
import appLogo from '../../../assets/images/app_logo.png'; import appLogo from '../../../assets/images/app_logo.png';
import ClearDatabasesModal from '../../../components/ClearDatabasesModal';
const MainLayoutMenu = () => { const MainLayoutMenu = () => {
const hasSignedUp = useSelector((state) => state.user.hasSignedUp); const hasSignedUp = useSelector((state) => state.user.hasSignedUp);
const [isClearDatabasesOpen, setIsClearDatabasesOpen] = useState(false);
const history = useHistory(); const history = useHistory();
const { t } = useTranslation(); const { t } = useTranslation();
const handleClearDatabasesClick = () => {
setIsClearDatabasesOpen(true);
};
const handleDatabasesCleared = () => {
setIsClearDatabasesOpen(false);
history.push('/home');
window.location.reload(false);
};
const handleCancelDatabasesClear = () => {
setIsClearDatabasesOpen(false);
};
return ( return (
<AppContext.Consumer> <AppContext.Consumer>
{() => ( {() => (
@ -23,6 +39,14 @@ const MainLayoutMenu = () => {
> >
<img src={appLogo} alt="app_logo" /> <img src={appLogo} alt="app_logo" />
</Menu.Item> </Menu.Item>
<Menu.Item
link
name="clear-databases"
key="clear-databases"
onClick={handleClearDatabasesClick}
>
{t('topbar.button.clear.databases')}
</Menu.Item>
<Menu.Menu position="right"> <Menu.Menu position="right">
{hasSignedUp && history.location.pathname === '/home' && ( {hasSignedUp && history.location.pathname === '/home' && (
<Menu.Item <Menu.Item
@ -57,6 +81,12 @@ const MainLayoutMenu = () => {
</Menu.Item> </Menu.Item>
)} )}
</Menu.Menu> </Menu.Menu>
<ClearDatabasesModal
open={isClearDatabasesOpen}
onDatabasesCleared={handleDatabasesCleared}
onCancel={handleCancelDatabasesClear}
/>
</Menu> </Menu>
)} )}
</AppContext.Consumer> </AppContext.Consumer>

1
packages/concordia-app/src/options/breezeOptions.js

@ -10,6 +10,7 @@ const REACT_APP_RENDEZVOUS_PORT = process.env.REACT_APP_RENDEZVOUS_PORT || REACT
const breezeOptions = { const breezeOptions = {
ipfs: { ipfs: {
repo: 'concordia',
config: { config: {
Addresses: { Addresses: {
Swarm: [ Swarm: [

22
packages/concordia-app/src/utils/indexedDB/indexedDBUtils.js

@ -0,0 +1,22 @@
import { breeze } from '../../redux/store';
const purgeIndexedDBs = async () => {
const { ipfs, orbit } = breeze;
if (orbit) await orbit.stop();
if (ipfs) await ipfs.stop();
const databases = await indexedDB.databases();
return Promise.all(
databases.map((db) => new Promise(
(resolve, reject) => {
const request = indexedDB.deleteDatabase(db.name);
request.onblocked = resolve;
request.onsuccess = resolve;
request.onerror = reject;
},
)),
);
};
export default purgeIndexedDBs;

46
packages/concordia-app/src/utils/indexedDB/patchIndexedDB.js

@ -0,0 +1,46 @@
/* Patches browsers that do not yet support indexedDB.databases()
(https://developer.mozilla.org/en-US/docs/Web/API/IDBFactory/databases)
See also https://gist.github.com/rmehner/b9a41d9f659c9b1c3340#gistcomment-3449418) */
if (window.indexedDB && typeof window.indexedDB.databases === 'undefined') {
const LOCALSTORAGE_CACHE_KEY = 'indexedDBDatabases';
// Store a key value map of databases
const getFromStorage = () => JSON.parse(window.localStorage[LOCALSTORAGE_CACHE_KEY] || '{}');
// Write the database to local storage
const writeToStorage = (value) => { window.localStorage[LOCALSTORAGE_CACHE_KEY] = JSON.stringify(value); };
IDBFactory.prototype.databases = () => Promise.resolve(
Object.entries(getFromStorage()).reduce((acc, [name, version]) => {
acc.push({ name, version });
return acc;
}, []),
);
// Intercept the existing open handler to write our DBs names
// and versions to localStorage
const { open } = IDBFactory.prototype;
// eslint-disable-next-line func-names
IDBFactory.prototype.open = function (...args) {
const dbName = args[0];
const version = args[1] || 1;
const existing = getFromStorage();
writeToStorage({ ...existing, [dbName]: version });
return open.apply(this, args);
};
// Intercept the existing deleteDatabase handler remove our
// dbNames from localStorage
const { deleteDatabase } = IDBFactory.prototype;
// eslint-disable-next-line func-names
IDBFactory.prototype.deleteDatabase = function (...args) {
const dbName = args[0];
const existing = getFromStorage();
delete existing[dbName];
writeToStorage(existing);
return deleteDatabase.apply(this, args);
};
console.debug('IndexedDB patched successfully!');
}
Loading…
Cancel
Save