From a214014ee82022f4e8bde410825644b6712a57dc Mon Sep 17 00:00:00 2001 From: Ezerous Date: Wed, 9 Dec 2020 18:50:11 +0200 Subject: [PATCH 1/4] Add ability to purge IndexedDB --- packages/concordia-app/src/index.jsx | 1 + .../MainLayout/MainLayoutMenu/index.jsx | 11 +++++ .../src/options/breezeOptions.js | 1 + .../src/utils/indexedDB/indexedDBUtils.js | 20 ++++++++ .../src/utils/indexedDB/patchIndexedDB.js | 46 +++++++++++++++++++ 5 files changed, 79 insertions(+) create mode 100644 packages/concordia-app/src/utils/indexedDB/indexedDBUtils.js create mode 100644 packages/concordia-app/src/utils/indexedDB/patchIndexedDB.js diff --git a/packages/concordia-app/src/index.jsx b/packages/concordia-app/src/index.jsx index 4f21227..a29f530 100644 --- a/packages/concordia-app/src/index.jsx +++ b/packages/concordia-app/src/index.jsx @@ -1,3 +1,4 @@ +import './utils/indexedDB/patchIndexedDB'; import './utils/wdyr'; import React, { Suspense } from 'react'; import { render } from 'react-dom'; diff --git a/packages/concordia-app/src/layouts/MainLayout/MainLayoutMenu/index.jsx b/packages/concordia-app/src/layouts/MainLayout/MainLayoutMenu/index.jsx index 11524eb..975c40c 100644 --- a/packages/concordia-app/src/layouts/MainLayout/MainLayoutMenu/index.jsx +++ b/packages/concordia-app/src/layouts/MainLayout/MainLayoutMenu/index.jsx @@ -5,6 +5,7 @@ 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'; const MainLayoutMenu = () => { const hasSignedUp = useSelector((state) => state.user.hasSignedUp); @@ -56,6 +57,16 @@ const MainLayoutMenu = () => { {t('topbar.button.register')} )} + { + await purgeIndexedDBs(); + }} + > + Purge + )} diff --git a/packages/concordia-app/src/options/breezeOptions.js b/packages/concordia-app/src/options/breezeOptions.js index 41984c0..3f3a15f 100644 --- a/packages/concordia-app/src/options/breezeOptions.js +++ b/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 = { ipfs: { + repo: 'concordia', config: { Addresses: { Swarm: [ diff --git a/packages/concordia-app/src/utils/indexedDB/indexedDBUtils.js b/packages/concordia-app/src/utils/indexedDB/indexedDBUtils.js new file mode 100644 index 0000000..a44fdfa --- /dev/null +++ b/packages/concordia-app/src/utils/indexedDB/indexedDBUtils.js @@ -0,0 +1,20 @@ +import { breeze } from '../../redux/store'; + +async function purgeIndexedDBs() { + const { ipfs, orbit } = breeze; + if (orbit) await orbit.stop(); + if (ipfs) await ipfs.stop(); + + const dbs = await indexedDB.databases(); + await Promise.all( + dbs.map((db) => new Promise( + (resolve, reject) => { + const request = indexedDB.deleteDatabase(db.name); + request.onsuccess = resolve; + request.onerror = reject; + }, + )), + ); +} + +export default purgeIndexedDBs; diff --git a/packages/concordia-app/src/utils/indexedDB/patchIndexedDB.js b/packages/concordia-app/src/utils/indexedDB/patchIndexedDB.js new file mode 100644 index 0000000..185ecda --- /dev/null +++ b/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!'); +} From 201c0a79c88c2cd413b901a30cc36eb387113009 Mon Sep 17 00:00:00 2001 From: Apostolof Date: Thu, 10 Dec 2020 22:02:29 +0200 Subject: [PATCH 2/4] 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; From d2c056ce2c65409a172114d0010c993364eae69c Mon Sep 17 00:00:00 2001 From: Ezerous Date: Fri, 11 Dec 2020 15:28:29 +0200 Subject: [PATCH 3/4] Reverted indexedDButils --- .../src/utils/indexedDB/indexedDBUtils.js | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/packages/concordia-app/src/utils/indexedDB/indexedDBUtils.js b/packages/concordia-app/src/utils/indexedDB/indexedDBUtils.js index 95374a6..e348dc3 100644 --- a/packages/concordia-app/src/utils/indexedDB/indexedDBUtils.js +++ b/packages/concordia-app/src/utils/indexedDB/indexedDBUtils.js @@ -5,24 +5,17 @@ const purgeIndexedDBs = async () => { if (orbit) await orbit.stop(); if (ipfs) await ipfs.stop(); - const databases = await indexedDB.databases(); - 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); - }); - }; + const databases = await indexedDB.databases(); + return Promise.all( + databases.map((db) => new Promise( + (resolve, reject) => { + const request = indexedDB.deleteDatabase(db.name); request.onsuccess = resolve; request.onerror = reject; - }))); + }, + )), + ); }; export default purgeIndexedDBs; From 942dc522b48e685b60a5d712fbdf144c2eaa6659 Mon Sep 17 00:00:00 2001 From: Ezerous Date: Fri, 11 Dec 2020 20:47:58 +0200 Subject: [PATCH 4/4] Readd resolving on blocked dbs, add fixed eth provider --- packages/concordia-app/package.json | 2 +- packages/concordia-app/src/utils/indexedDB/indexedDBUtils.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/concordia-app/package.json b/packages/concordia-app/package.json index 9b842e9..6645ee2 100644 --- a/packages/concordia-app/package.json +++ b/packages/concordia-app/package.json @@ -26,7 +26,7 @@ "dependencies": { "@ezerous/breeze": "~0.4.0", "@ezerous/drizzle": "~0.4.1", - "@ezerous/eth-identity-provider": "^0.1.0", + "@ezerous/eth-identity-provider": "~0.1.2", "@reduxjs/toolkit": "~1.4.0", "@welldone-software/why-did-you-render": "^6.0.0-rc.1", "concordia-contracts": "~0.1.0", diff --git a/packages/concordia-app/src/utils/indexedDB/indexedDBUtils.js b/packages/concordia-app/src/utils/indexedDB/indexedDBUtils.js index e348dc3..bc4b3be 100644 --- a/packages/concordia-app/src/utils/indexedDB/indexedDBUtils.js +++ b/packages/concordia-app/src/utils/indexedDB/indexedDBUtils.js @@ -11,6 +11,7 @@ const purgeIndexedDBs = async () => { databases.map((db) => new Promise( (resolve, reject) => { const request = indexedDB.deleteDatabase(db.name); + request.onblocked = resolve; request.onsuccess = resolve; request.onerror = reject; },