Browse Source

Merge pull request #1585 from meriadec/fix-1554-reset

Ensure sqlite folder is removed after clear cache / hard reset
gre-patch-1
Gaëtan Renaudeau 6 years ago
committed by GitHub
parent
commit
8926b50a74
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 14
      src/components/SettingsPage/CleanButton.js
  2. 13
      src/components/SettingsPage/ResetButton.js
  3. 43
      src/components/SettingsPage/ResetFallbackModal.js
  4. 1
      src/config/errors.js
  5. 15
      src/helpers/reset.js
  6. 8
      static/i18n/en/app.json

14
src/components/SettingsPage/CleanButton.js

@ -3,11 +3,13 @@
import React, { Fragment, PureComponent } from 'react' import React, { Fragment, PureComponent } from 'react'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import { translate } from 'react-i18next' import { translate } from 'react-i18next'
import logger from 'logger'
import type { T } from 'types/common' import type { T } from 'types/common'
import { cleanAccountsCache } from 'actions/accounts' import { cleanAccountsCache } from 'actions/accounts'
import Button from 'components/base/Button' import Button from 'components/base/Button'
import { ConfirmModal } from 'components/base/Modal' import { ConfirmModal } from 'components/base/Modal'
import { softReset } from 'helpers/reset' import { softReset } from 'helpers/reset'
import ResetFallbackModal from './ResetFallbackModal'
const mapDispatchToProps = { const mapDispatchToProps = {
cleanAccountsCache, cleanAccountsCache,
@ -20,32 +22,36 @@ type Props = {
type State = { type State = {
opened: boolean, opened: boolean,
fallbackOpened: boolean,
isLoading: boolean, isLoading: boolean,
} }
class CleanButton extends PureComponent<Props, State> { class CleanButton extends PureComponent<Props, State> {
state = { state = {
opened: false, opened: false,
fallbackOpened: false,
isLoading: false, isLoading: false,
} }
open = () => this.setState({ opened: true }) open = () => this.setState({ opened: true })
close = () => this.setState({ opened: false }) close = () => this.setState({ opened: false })
closeFallback = () => this.setState({ fallbackOpened: false })
action = async () => { action = async () => {
if (this.state.isLoading) return if (this.state.isLoading) return
try { try {
this.setState({ isLoading: true }) this.setState({ isLoading: true })
await softReset({ cleanAccountsCache: this.props.cleanAccountsCache }) await softReset({ cleanAccountsCache: this.props.cleanAccountsCache })
} finally { } catch (err) {
this.setState({ isLoading: false }) logger.error(err)
this.setState({ isLoading: false, fallbackOpened: true })
} }
} }
render() { render() {
const { t } = this.props const { t } = this.props
const { opened, isLoading } = this.state const { opened, isLoading, fallbackOpened } = this.state
return ( return (
<Fragment> <Fragment>
<Button small primary onClick={this.open} event="ClearCacheIntent"> <Button small primary onClick={this.open} event="ClearCacheIntent">
@ -63,6 +69,8 @@ class CleanButton extends PureComponent<Props, State> {
subTitle={t('common.areYouSure')} subTitle={t('common.areYouSure')}
desc={t('settings.softResetModal.desc')} desc={t('settings.softResetModal.desc')}
/> />
<ResetFallbackModal isOpened={fallbackOpened} onClose={this.closeFallback} />
</Fragment> </Fragment>
) )
} }

13
src/components/SettingsPage/ResetButton.js

@ -4,12 +4,14 @@ import React, { Fragment, PureComponent } from 'react'
import styled from 'styled-components' import styled from 'styled-components'
import { remote } from 'electron' import { remote } from 'electron'
import { translate } from 'react-i18next' import { translate } from 'react-i18next'
import logger from 'logger'
import type { T } from 'types/common' import type { T } from 'types/common'
import { hardReset } from 'helpers/reset' import { hardReset } from 'helpers/reset'
import Box from 'components/base/Box' import Box from 'components/base/Box'
import Button from 'components/base/Button' import Button from 'components/base/Button'
import { ConfirmModal } from 'components/base/Modal' import { ConfirmModal } from 'components/base/Modal'
import IconTriangleWarning from 'icons/TriangleWarning' import IconTriangleWarning from 'icons/TriangleWarning'
import ResetFallbackModal from './ResetFallbackModal'
type Props = { type Props = {
t: T, t: T,
@ -18,16 +20,19 @@ type Props = {
type State = { type State = {
opened: boolean, opened: boolean,
pending: boolean, pending: boolean,
fallbackOpened: boolean,
} }
class ResetButton extends PureComponent<Props, State> { class ResetButton extends PureComponent<Props, State> {
state = { state = {
opened: false, opened: false,
pending: false, pending: false,
fallbackOpened: false,
} }
open = () => this.setState({ opened: true }) open = () => this.setState({ opened: true })
close = () => this.setState({ opened: false }) close = () => this.setState({ opened: false })
closeFallback = () => this.setState({ fallbackOpened: false })
action = async () => { action = async () => {
this.setState({ pending: true }) this.setState({ pending: true })
@ -35,13 +40,15 @@ class ResetButton extends PureComponent<Props, State> {
await hardReset() await hardReset()
remote.getCurrentWindow().webContents.reloadIgnoringCache() remote.getCurrentWindow().webContents.reloadIgnoringCache()
} catch (err) { } catch (err) {
this.setState({ pending: false }) logger.error(err)
this.setState({ pending: false, fallbackOpened: true })
} }
} }
render() { render() {
const { t } = this.props const { t } = this.props
const { opened, pending } = this.state const { opened, pending, fallbackOpened } = this.state
return ( return (
<Fragment> <Fragment>
<Button small danger onClick={this.open} event="HardResetIntent"> <Button small danger onClick={this.open} event="HardResetIntent">
@ -66,6 +73,8 @@ class ResetButton extends PureComponent<Props, State> {
</IconWrapperCircle> </IconWrapperCircle>
)} )}
/> />
<ResetFallbackModal isOpened={fallbackOpened} onClose={this.closeFallback} />
</Fragment> </Fragment>
) )
} }

43
src/components/SettingsPage/ResetFallbackModal.js

@ -0,0 +1,43 @@
// @flow
import React, { PureComponent } from 'react'
import { translate } from 'react-i18next'
import { ConfirmModal } from 'components/base/Modal'
import { openUserDataFolderAndQuit } from 'helpers/reset'
type Props = {
t: *,
isOpened: boolean,
onClose: () => *,
}
class ResetFallbackModal extends PureComponent<Props> {
render() {
const { t, isOpened, onClose } = this.props
return (
<ConfirmModal
centered
isOpened={isOpened}
onConfirm={openUserDataFolderAndQuit}
onClose={onClose}
onReject={onClose}
confirmText={'Open folder'}
title={t('settings.resetFallbackModal.title')}
desc={
<div>
<p>{t('settings.resetFallbackModal.part1')}</p>
<p style={{ fontWeight: 'bold' }}>
{t('settings.resetFallbackModal.part2')}
{t('settings.resetFallbackModal.part3')}
{t('settings.resetFallbackModal.part4')}
</p>
<p style={{ marginTop: 20 }}>{t('settings.resetFallbackModal.part5')}</p>
</div>
}
/>
)
}
}
export default translate()(ResetFallbackModal)

1
src/config/errors.js

@ -50,3 +50,4 @@ export const FeeNotLoaded = createCustomErrorClass('FeeNotLoaded')
// db stuff, no need to translate // db stuff, no need to translate
export const NoDBPathGiven = createCustomErrorClass('NoDBPathGiven') export const NoDBPathGiven = createCustomErrorClass('NoDBPathGiven')
export const DBWrongPassword = createCustomErrorClass('DBWrongPassword') export const DBWrongPassword = createCustomErrorClass('DBWrongPassword')
export const DBNotReset = createCustomErrorClass('DBNotReset')

15
src/helpers/reset.js

@ -1,5 +1,7 @@
// @flow // @flow
import fs from 'fs'
import { shell, remote } from 'electron'
import path from 'path' import path from 'path'
import rimraf from 'rimraf' import rimraf from 'rimraf'
import resolveUserDataDirectory from 'helpers/resolveUserDataDirectory' import resolveUserDataDirectory from 'helpers/resolveUserDataDirectory'
@ -7,11 +9,15 @@ import { disable as disableDBMiddleware } from 'middlewares/db'
import db from 'helpers/db' import db from 'helpers/db'
import { delay } from 'helpers/promise' import { delay } from 'helpers/promise'
import killInternalProcess from 'commands/killInternalProcess' import killInternalProcess from 'commands/killInternalProcess'
import { DBNotReset } from 'config/errors'
async function resetLibcoreDatabase() { async function resetLibcoreDatabase() {
await killInternalProcess.send().toPromise() await killInternalProcess.send().toPromise()
const dbpath = path.resolve(resolveUserDataDirectory(), 'sqlite/') const dbpath = path.resolve(resolveUserDataDirectory(), 'sqlite/')
rimraf.sync(dbpath, { glob: false }) rimraf.sync(dbpath, { glob: false })
if (fs.existsSync(dbpath)) {
throw new DBNotReset()
}
} }
function reload() { function reload() {
@ -24,7 +30,7 @@ export async function hardReset() {
disableDBMiddleware() disableDBMiddleware()
db.resetAll() db.resetAll()
await delay(500) await delay(500)
resetLibcoreDatabase() await resetLibcoreDatabase()
reload() reload()
} }
@ -32,6 +38,11 @@ export async function softReset({ cleanAccountsCache }: *) {
cleanAccountsCache() cleanAccountsCache()
await delay(500) await delay(500)
await db.cleanCache() await db.cleanCache()
resetLibcoreDatabase() await resetLibcoreDatabase()
reload() reload()
} }
export async function openUserDataFolderAndQuit() {
shell.openItem(resolveUserDataDirectory())
remote.app.quit()
}

8
static/i18n/en/app.json

@ -406,6 +406,14 @@
"title": "Clear cache", "title": "Clear cache",
"desc": "Clearing the Ledger Live cache forces network resynchronization. Your settings and accounts are not affected. The private keys to access your crypto assets in the blockchain remain secure on your Ledger device and on your Recovery sheet." "desc": "Clearing the Ledger Live cache forces network resynchronization. Your settings and accounts are not affected. The private keys to access your crypto assets in the blockchain remain secure on your Ledger device and on your Recovery sheet."
}, },
"resetFallbackModal": {
"title": "User action required",
"part1": "Could not delete cache folder. Please delete the folder manually:",
"part2": "Click the Open folder button, the ",
"part3": "app will close",
"part4": ", and manually delete the \"sqlite\" folder.",
"part5": "Then you can restart the app normally."
},
"removeAccountModal": { "removeAccountModal": {
"title": "Remove account", "title": "Remove account",
"desc": "The account will no longer be included in your portfolio. This operation does not affect your assets. Accounts can always be re-added." "desc": "The account will no longer be included in your portfolio. This operation does not affect your assets. Accounts can always be re-added."

Loading…
Cancel
Save