diff --git a/build/locales/en/translations.json b/build/locales/en/translations.json
index 41ccc6c3..62603f97 100644
--- a/build/locales/en/translations.json
+++ b/build/locales/en/translations.json
@@ -165,6 +165,10 @@
},
"modalDialog": {
"title": "Database import"
+ },
+ "loadingWindow": {
+ "description": "Importing DB ...",
+ "unzippedBytes": "DB size {{prettyUnzippedBytes}}"
}
}
},
diff --git a/build/locales/ru/translations.json b/build/locales/ru/translations.json
index 8c808737..687ba602 100644
--- a/build/locales/ru/translations.json
+++ b/build/locales/ru/translations.json
@@ -165,6 +165,10 @@
},
"modalDialog": {
"title": "Импорт базы данных"
+ },
+ "loadingWindow": {
+ "description": "Импортирование БД ...",
+ "unzippedBytes": "размер БД {{prettyUnzippedBytes}}"
}
}
},
diff --git a/package.json b/package.json
index 264e498b..a07ec864 100644
--- a/package.json
+++ b/package.json
@@ -32,7 +32,7 @@
"new-github-issue-url": "0.2.1",
"showdown": "2.0.3",
"truncate-utf8-bytes": "1.0.2",
- "yauzl": "2.10.0"
+ "yauzl": "3.2.0"
},
"devDependencies": {
"@mapbox/node-pre-gyp": "1.0.11",
diff --git a/src/archiver.js b/src/archiver.js
index 6a4c86b0..eccbffc2 100644
--- a/src/archiver.js
+++ b/src/archiver.js
@@ -140,15 +140,72 @@ const zip = async (
const unzip = (
zipPath,
folderPath,
- params = {}
+ params
) => {
- const { extractFiles } = { ...params }
- const extractedfileNames = []
- let isClosedByError = false
+ const {
+ extractFiles,
+ progressHandler
+ } = params ?? {}
+ return new Promise((_resolve, _reject) => {
+ const entryStates = []
+ let totalUncompressedSize = 0
+ let unzippedBytes = 0
+ let lastProgressEventMts = Date.now()
+
+ const asyncProgressHandler = async () => {
+ try {
+ if (typeof progressHandler !== 'function') {
+ return
+ }
+
+ if (
+ !Number.isFinite(totalUncompressedSize) ||
+ totalUncompressedSize === 0 ||
+ !Number.isFinite(unzippedBytes)
+ ) {
+ return
+ }
+
+ const progress = unzippedBytes / totalUncompressedSize
+ const prettyUnzippedBytes = bytesToSize(unzippedBytes)
+
+ await progressHandler({
+ progress,
+ unzippedBytes,
+ prettyUnzippedBytes
+ })
+ } catch (err) {
+ console.debug(err)
+ }
+ }
+ const resolve = (entryState) => {
+ if (entryState) {
+ entryState.isClosedSuccessfully = true
+ }
+ if (
+ entryStates.some((state) => state?.isClosedWithError) ||
+ entryStates.some((state) => !state?.isClosedSuccessfully)
+ ) {
+ return
+ }
+
+ asyncProgressHandler()
+
+ return _resolve(entryStates.map((state) => state?.entry?.fileName))
+ }
+ const reject = (err, zipfile, entryState) => {
+ if (entryState) {
+ entryState.isClosedWithError = true
+ }
+ if (zipfile) {
+ zipfile.close()
+ }
+
+ return _reject(err)
+ }
- return new Promise((resolve, reject) => {
try {
- yauzl.open(zipPath, { lazyEntries: true }, (err, zipfile) => {
+ yauzl.open(zipPath, { lazyEntries: false }, (err, zipfile) => {
if (err) {
reject(err)
@@ -156,67 +213,67 @@ const unzip = (
}
zipfile.on('error', reject)
- zipfile.on('end', () => {
- if (isClosedByError) {
- return
- }
-
- resolve(extractedfileNames)
- })
- zipfile.readEntry()
-
+ zipfile.on('end', () => resolve())
zipfile.on('entry', (entry) => {
const { fileName } = entry
const filePath = path.join(folderPath, fileName)
const errorMessage = yauzl.validateFileName(fileName)
if (/\/$/.test(fileName)) {
- zipfile.readEntry()
-
return
}
if (
Array.isArray(extractFiles) &&
extractFiles.every(file => file !== fileName)
) {
- zipfile.readEntry()
-
return
}
+
+ const entryState = {
+ isClosedWithError: false,
+ isClosedSuccessfully: false,
+ entry
+ }
+ totalUncompressedSize += entry?.uncompressedSize ?? 0
+ entryStates.push(entryState)
+
if (errorMessage) {
- isClosedByError = true
- zipfile.close()
- reject(new InvalidFileNameInArchiveError(errorMessage))
+ reject(
+ new InvalidFileNameInArchiveError(errorMessage),
+ zipfile,
+ entryState
+ )
return
}
zipfile.openReadStream(entry, (err, readStream) => {
if (err) {
- isClosedByError = true
- zipfile.close()
- reject(err)
+ reject(err, zipfile, entryState)
return
}
const output = fs.createWriteStream(filePath)
- output.on('close', () => {
- extractedfileNames.push(fileName)
-
- zipfile.readEntry()
- })
+ output.on('close', () => resolve(entryState))
output.on('error', (err) => {
- isClosedByError = true
- zipfile.close()
- reject(err)
+ reject(err, zipfile, entryState)
})
readStream.on('error', (err) => {
- isClosedByError = true
- zipfile.close()
- reject(err)
+ reject(err, zipfile, entryState)
+ })
+ readStream.on('data', (chunk) => {
+ unzippedBytes += chunk.length
+ const currMts = Date.now()
+
+ if (currMts - lastProgressEventMts < 500) {
+ return
+ }
+
+ lastProgressEventMts = currMts
+ asyncProgressHandler()
})
readStream.pipe(output)
diff --git a/src/import-db.js b/src/import-db.js
index 58e3e646..a940a2f0 100644
--- a/src/import-db.js
+++ b/src/import-db.js
@@ -13,6 +13,9 @@ const pauseApp = require('./pause-app')
const relaunch = require('./relaunch')
const { rm, isMainWinAvailable } = require('./helpers')
const wins = require('./window-creators/windows')
+const {
+ setLoadingDescription
+} = require('./window-creators/change-loading-win-visibility-state')
const {
DB_FILE_NAME,
DB_SHM_FILE_NAME,
@@ -78,7 +81,33 @@ module.exports = ({
throw new InvalidFilePathError()
}
- await pauseApp()
+ const progressHandler = async (args) => {
+ const {
+ progress,
+ prettyUnzippedBytes
+ } = args ?? {}
+
+ const _description = i18next
+ .t('common.importDB.loadingWindow.description')
+ const _unzipped = i18next.t(
+ 'common.importDB.loadingWindow.unzippedBytes',
+ { prettyUnzippedBytes }
+ )
+
+ const unzipped = prettyUnzippedBytes
+ ? `
${_unzipped}`
+ : ''
+ const description = `${_description}${unzipped}`
+
+ await setLoadingDescription({ progress, description })
+ }
+
+ await pauseApp({
+ loadingWinParams: {
+ description: i18next
+ .t('common.importDB.loadingWindow.description')
+ }
+ })
await _rmDbExcludeMain(pathToUserData, DB_FILE_NAME)
const extractedfileNames = await unzip(
filePaths[0],
@@ -89,7 +118,8 @@ module.exports = ({
DB_SHM_FILE_NAME,
DB_WAL_FILE_NAME,
SECRET_KEY_FILE_NAME
- ]
+ ],
+ progressHandler
}
)
@@ -100,8 +130,11 @@ module.exports = ({
relaunch()
} catch (err) {
try {
+ const _win = isMainWinAvailable(wins.loadingWindow)
+ ? wins.loadingWindow
+ : win
await showErrorModalDialog(
- win,
+ _win,
i18next.t('common.importDB.modalDialog.title'),
err
)
diff --git a/src/pause-app.js b/src/pause-app.js
index 3883dd78..96d513b2 100644
--- a/src/pause-app.js
+++ b/src/pause-app.js
@@ -28,12 +28,16 @@ const _closeServer = () => {
})
}
-module.exports = async (opts = {}) => {
+module.exports = async (opts) => {
const {
- beforeClosingServHook = () => {}
- } = opts
+ beforeClosingServHook = () => {},
+ loadingWinParams
+ } = opts ?? {}
- await showLoadingWindow({ isRequiredToCloseAllWins: true })
+ await showLoadingWindow({
+ isRequiredToCloseAllWins: true,
+ ...loadingWinParams
+ })
await beforeClosingServHook()