From 81fe940918c360a3820aa3382f73af9bed97ae67 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Mon, 18 Nov 2024 12:40:29 +0200 Subject: [PATCH 1/6] Bump yauzl up to 3.2.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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", From 6ad1e647b302d31914bca52fd7c132acc33dbba6 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Mon, 18 Nov 2024 12:40:57 +0200 Subject: [PATCH 2/6] Add ability to process progress handler when unzipping --- src/archiver.js | 129 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 93 insertions(+), 36 deletions(-) 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) From a4303e924ec114b77ecbb2cd9ffa8097a7d1578c Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Mon, 18 Nov 2024 12:41:32 +0200 Subject: [PATCH 3/6] Add ability to show description when pausing app --- src/pause-app.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) 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() From eac8fa7b1e90e080f126f3e913ed3c2cde181924 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Mon, 18 Nov 2024 12:41:56 +0200 Subject: [PATCH 4/6] Add progress perc to loading window for import-db module --- src/import-db.js | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) 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 ) From 1acd10d8e170f8cfe02f3891c4656a67e2c67146 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Mon, 18 Nov 2024 12:42:18 +0200 Subject: [PATCH 5/6] Add en translation for import-db loading window --- build/locales/en/translations.json | 4 ++++ 1 file changed, 4 insertions(+) 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}}" } } }, From d4864b72a21be0695c4733f382fd5c2097f6c624 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Mon, 18 Nov 2024 12:42:40 +0200 Subject: [PATCH 6/6] Add ru translation for import-db loading window --- build/locales/ru/translations.json | 4 ++++ 1 file changed, 4 insertions(+) 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}}" } } },