From 5d238516c820c8428c940b6011f343e103b76da7 Mon Sep 17 00:00:00 2001 From: ni00 Date: Mon, 5 Jun 2023 17:14:11 +0800 Subject: [PATCH] refactor(updateChecker):optimize automatic updates and add a progress bar --- src/background.ts | 26 ++- src/lang/index.ts | 2 +- src/lang/update.ts | 65 +++++++ src/main/getMenuTemplate.ts | 3 +- src/main/updateChecker.ts | 121 ++++-------- src/main/updateDownloader.ts | 54 ++++++ src/types/locale.d.ts | 2 +- src/utils/element.ts | 4 +- src/utils/i18n.ts | 1 + src/views/Home.vue | 3 + src/views/about/index.vue | 4 +- src/views/update/index.vue | 359 +++++++++++++++++++++++++++++++++++ 12 files changed, 541 insertions(+), 103 deletions(-) create mode 100644 src/lang/update.ts create mode 100644 src/main/updateDownloader.ts create mode 100644 src/views/update/index.vue diff --git a/src/background.ts b/src/background.ts index 65a14b26cc..0f26b0abe2 100644 --- a/src/background.ts +++ b/src/background.ts @@ -4,7 +4,8 @@ import { app, protocol, BrowserWindow, ipcMain, shell, Menu } from 'electron' import { createProtocol } from 'vue-cli-plugin-electron-builder/lib' import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer' import { quitAndRenameLogger } from './utils/logger' -import updateChecker, { createUpdateWindow } from './main/updateChecker' +import { createUpdateWindow, autoDownload } from './main/updateDownloader' +import { getCurrentLang } from './main/updateChecker' import getMenuTemplate from './main/getMenuTemplate' import saveFile from './main/saveFile' import saveExcel from './main/saveExcel' @@ -12,6 +13,7 @@ import newWindow from './main/newWindow' import { onSystemThemeChanged } from './main/systemTheme' import useConnection, { initOptionModel } from '@/database/useConnection' import useServices from '@/database/useServices' +import { dialog } from 'electron' declare const __static: string @@ -49,8 +51,8 @@ function handleIpcMessages() { Menu.setApplicationMenu(menu) } }) - ipcMain.on('checkUpdate', () => { - updateChecker(false) + ipcMain.on('clickUpdate', (event: Electron.IpcMainEvent) => { + event.sender.send('clickUpdate') }) ipcMain.on('exportData', (event: Electron.IpcMainEvent, ...args: any[]) => { const [filename, content, type] = args @@ -73,6 +75,20 @@ function handleIpcMessages() { event.sender.send('getWindowSize', win.getBounds()) } }) + ipcMain.on('startDownloadProgress', (event, updateDetail) => { + getCurrentLang().then((lang) => { + electronStore.set('isShow', false) + autoDownload(event, updateDetail, lang) + }) + }) + ipcMain.on('showMsg', (event) => { + dialog.showMessageBox({ + type: 'info', + title: '', + buttons: ['OK'], + message: 'There are currently no updates available.', + }) + }) } // handle event when APP quit @@ -154,10 +170,6 @@ async function createWindow() { beforeAppQuit() }) handleIpcMessages() - if (autoCheckUpdate) { - updateChecker() - } - // updateWindow electronStore.get('isShow') && createUpdateWindow() } diff --git a/src/lang/index.ts b/src/lang/index.ts index 60199128bf..59c8362b92 100644 --- a/src/lang/index.ts +++ b/src/lang/index.ts @@ -9,7 +9,7 @@ import huLocale from 'element-ui/lib/locale/lang/hu' import { formati18n } from '@/utils/i18n' const supportLang: SupportLangModel = ['zh', 'en', 'ja', 'tr', 'hu'] -const i18nModules: i18nLocaleModel = ['connections', 'settings', 'common', 'about', 'script', 'log', 'help'] +const i18nModules: i18nLocaleModel = ['connections', 'settings', 'common', 'about', 'script', 'log', 'help', 'update'] const { en, zh, ja, tr, hu }: VueI18n.LocaleMessages = formati18n(i18nModules, supportLang) diff --git a/src/lang/update.ts b/src/lang/update.ts new file mode 100644 index 0000000000..f6cb5fb4c3 --- /dev/null +++ b/src/lang/update.ts @@ -0,0 +1,65 @@ +export default { + updateTitle: { + zh: '有可用更新', + en: 'Update Available', + ja: '利用可能な更新', + tr: 'Güncelleme Mevcut', + hu: 'Frissítés elérhető', + }, + ignoreVersion: { + zh: '忽略', + en: 'Dismiss', + ja: '却下', + tr: 'Reddet', + hu: 'Elutasít', + }, + nextRemind: { + zh: '稍后提醒', + en: 'Remind me Later', + ja: '後でリマインドしてください', + tr: 'Bana daha sonra hatırlat', + hu: 'Emlékeztessen később', + }, + update: { + zh: '更新', + en: 'Update', + ja: '更新', + tr: 'Güncelle', + hu: 'Frissítés', + }, + downloadProgress: { + zh: '下载进度', + en: 'Download Progress', + ja: 'ダウンロード進行状況', + tr: 'İndirme İlerlemesi', + hu: 'Letöltési Előrehaladás', + }, + downloading: { + zh: '下载更新中...', + en: 'Downloading Update...', + ja: '更新をダウンロード中...', + tr: 'Güncelleme indiriliyor...', + hu: 'Frissítés letöltése...', + }, + downloaded: { + zh: '安装并重启', + en: 'Restart and Install Update', + ja: '再起動してアップデートをインストール', + tr: 'Yeniden Başlat ve Güncellemeyi Yükle', + hu: 'Újraindítás és Frissítés Telepítése', + }, + cancel: { + zh: '取消更新', + en: 'Cancel Update', + ja: '更新をキャンセル', + tr: 'Güncellemeyi iptal et', + hu: 'Frissítés megszakítása', + }, + install: { + zh: '安装并重启', + en: 'Install and Relunch', + ja: 'インストールして再起動', + tr: 'Kur ve Tekrar Başlat', + hu: 'Telepítés és újraindítás', + }, +} diff --git a/src/main/getMenuTemplate.ts b/src/main/getMenuTemplate.ts index 0855243ddb..af8d334690 100644 --- a/src/main/getMenuTemplate.ts +++ b/src/main/getMenuTemplate.ts @@ -1,5 +1,4 @@ import { app, shell, BrowserWindow } from 'electron' -import updateChecker from './updateChecker' import translations from '../lang/menu' const isMac = process.platform === 'darwin' @@ -35,7 +34,7 @@ const getMenuTemplate = (win: BrowserWindow, lang?: Language): $TSFixed => { { label: labels.checkForUpdate, click: () => { - updateChecker(false) + win.webContents.send('clickUpdate') }, }, { type: 'separator' }, diff --git a/src/main/updateChecker.ts b/src/main/updateChecker.ts index f64b2efe92..2f8dd4193b 100644 --- a/src/main/updateChecker.ts +++ b/src/main/updateChecker.ts @@ -1,109 +1,54 @@ -import { dialog, BrowserWindow } from 'electron' import axios from 'axios' import version from '@/version' import useServices from '@/database/useServices' -const { autoUpdater } = require('electron-updater') const Store = require('electron-store') const electronStore = new Store() -const release = 'https://api.github.com/repos/emqx/MQTTX/releases/latest' -let language: string = 'en' - -const isUpdate = (latest: string, current: string): boolean => { - const latestVersion: number[] = latest.split('.').map((item) => parseInt(item, 10)) - const currentVersion: number[] = current.split('.').map((item) => parseInt(item, 10)) - let update: boolean = false - - for (let i: number = 0; i < 3; i++) { - if (currentVersion[i] < latestVersion[i]) { - update = true - } - } - - return update +export interface versionDetail { + version: string + detail: string } -const autoDownload = (latest: string, language: string): void => { - const urlLang = language === 'zh' ? 'zh' : 'en' - const downloadUrl = `https://www.emqx.com/${urlLang}/downloads/MQTTX/${latest}` - autoUpdater.setFeedURL(downloadUrl) - autoUpdater.checkForUpdatesAndNotify() - autoUpdater.on('checking-for-update', () => {}) - autoUpdater.on('update-available', () => {}) - autoUpdater.on('update-not-available', () => {}) - autoUpdater.on('error', () => {}) - autoUpdater.on('download-progress', () => {}) - autoUpdater.on('update-downloaded', () => { - electronStore.set('isShow', true) - dialog - .showMessageBox({ - type: 'info', - title: 'New Version', - buttons: ['Install', 'No'], - message: `Update available: ${latest}`, - }) - .then((res) => { - if (res.response === 0) { - // if selected yes - autoUpdater.quitAndInstall() - } else { - dialog.showMessageBox({ - type: 'info', - message: 'Automatic update on do not shut down the computer immediately', - }) - } - }) - }) -} - -const updateChecker = async (isAuto: boolean = true): Promise => { - const response = await axios.get(release) +export const getCurrentLang = async (): Promise => { + let language: string = 'en' const { settingService } = useServices() await settingService.set() const setting = await settingService.get() if (setting) { language = setting.currentLang } - if (response.status === 200) { - const latest: string = response.data.name - const isPrerelease: boolean = response.data.prerelease - if (latest && isUpdate(latest.slice(1, 6), version) && !isPrerelease) { - autoDownload(latest, language) - } else { - if (!isAuto) { - dialog.showMessageBox({ - type: 'info', - title: '', - buttons: ['OK'], - message: 'There are currently no updates available.', - }) + return language === 'zh' ? 'zh' : 'en' +} + +const getUpdateDtail = async (current: string): Promise => { + const tagsUrl = 'https://api.github.com/repos/emqx/MQTTX/tags' + const tagsRes = await axios.get(tagsUrl) + if (tagsRes.status === 200) { + const tagsList: string[] = tagsRes.data.map((item: any) => item.name) + const latestTagsList: string[] = tagsList.slice(0, tagsList.indexOf(current)) + while (latestTagsList.length > 0) { + const latestVersion = latestTagsList.shift() + const versionRes = await axios.get( + `https://community-sites.emqx.com/api/v1/changelogs?product=MQTTX&version=${latestVersion}`, + ) + if (latestVersion && versionRes.status === 200) { + return { + version: latestVersion, + detail: versionRes.data.data.changelog, + } } } - } else { - return false } + return null } -//what's new window -export async function createUpdateWindow() { - const updateWindow = new BrowserWindow({ - width: 600, - height: 500, - webPreferences: { - nodeIntegration: true, - contextIsolation: false, - enableRemoteModule: true, - }, - }) - const { settingService } = useServices() - await settingService.set() - const setting = await settingService.get() - if (setting) { - language = setting.currentLang + +export const updateChecker = async (isAuto: boolean = true): Promise => { + const currentVersion = `v${version}` + const updateDetail: versionDetail | null = await getUpdateDtail(currentVersion) + const language: string = await getCurrentLang() + if (updateDetail && (!isAuto || electronStore.get('isIgnore') !== updateDetail.version)) { + return updateDetail } - let link: string = 'https://mqttx.app' - link = language === 'zh' ? `${link}/zh` : link - updateWindow.loadURL(`${link}/changelogs/v${version}`) - electronStore.set('isShow', false) + return false } -export default updateChecker diff --git a/src/main/updateDownloader.ts b/src/main/updateDownloader.ts new file mode 100644 index 0000000000..7f3ca272a4 --- /dev/null +++ b/src/main/updateDownloader.ts @@ -0,0 +1,54 @@ +import { BrowserWindow, IpcMainEvent, ipcMain } from 'electron' +import version from '@/version' +import { getCurrentLang, versionDetail } from './updateChecker' + +const { autoUpdater } = require('electron-updater') +const Store = require('electron-store') +const electronStore = new Store() + +export const autoDownload = (event: IpcMainEvent, updateDetail: versionDetail, language: string) => { + const downloadUrl = `https://www.emqx.com/${language}/downloads/MQTTX/${updateDetail.version}` + autoUpdater.setFeedURL(downloadUrl) + autoUpdater.autoDownload = false + autoUpdater.autoInstallOnAppQuit = false + autoUpdater.checkForUpdatesAndNotify() + autoUpdater.on('update-available', () => { + autoUpdater.downloadUpdate() + }) + autoUpdater.on('error', (e: any) => { + console.log(e) + }) + autoUpdater.on('download-progress', (progressObj: any) => { + event.sender.send('downloadProgressPercent', progressObj.percent) + }) + autoUpdater.on('update-downloaded', () => { + event.sender.send('downloadProgressPercent', 100) + }) + ipcMain.on('toQuitAndInstall', () => { + electronStore.set('isShow', true) + autoUpdater.quitAndInstall() + }) + ipcMain.on('cancelDownload', () => { + autoUpdater.removeAllListeners() + autoUpdater.autoDownload = false + autoUpdater.autoInstallOnAppQuit = false + autoUpdater.autoCheckForUpdates = false + }) +} + +export async function createUpdateWindow() { + const updateWindow = new BrowserWindow({ + width: 600, + height: 500, + webPreferences: { + nodeIntegration: true, + contextIsolation: false, + enableRemoteModule: true, + }, + }) + const language: string = await getCurrentLang() + let link: string = 'https://mqttx.app' + link = language === 'zh' ? `${link}/zh` : link + updateWindow.loadURL(`${link}/changelogs/v${version}`) + electronStore.set('isShow', false) +} diff --git a/src/types/locale.d.ts b/src/types/locale.d.ts index 6f850d4555..7c5675ab67 100644 --- a/src/types/locale.d.ts +++ b/src/types/locale.d.ts @@ -6,7 +6,7 @@ declare module 'element-ui/lib/locale/lang/ja' {} declare module 'element-ui/lib/locale/lang/tr-TR' {} declare module 'element-ui/lib/locale/lang/hu' {} -type i18nLocaleModel = ['connections', 'settings', 'common', 'about', 'script', 'log', 'help'] +type i18nLocaleModel = ['connections', 'settings', 'common', 'about', 'script', 'log', 'help', 'update'] type SupportLangModel = ['zh', 'en', 'ja', 'tr', 'hu'] declare module '*.json' { diff --git a/src/utils/element.ts b/src/utils/element.ts index 6ee0c8b4ee..53b39f8328 100644 --- a/src/utils/element.ts +++ b/src/utils/element.ts @@ -51,7 +51,7 @@ import { Row, Col, // Upload, - // Progress, + Progress, Badge, Card, // Rate, @@ -124,7 +124,7 @@ export default (Vue: typeof _Vue) => { Vue.use(Row) Vue.use(Col) // Vue.use(Upload) - // Vue.use(Progress) + Vue.use(Progress) Vue.use(Badge) Vue.use(Card) // Vue.use(Rate) diff --git a/src/utils/i18n.ts b/src/utils/i18n.ts index 3adb25fc29..b684e40082 100644 --- a/src/utils/i18n.ts +++ b/src/utils/i18n.ts @@ -15,6 +15,7 @@ export const formati18n = (transItems: i18nLocaleModel, langs: SupportLangModel) script: {}, log: {}, help: {}, + update: {}, } }) transItems.forEach((item) => { diff --git a/src/views/Home.vue b/src/views/Home.vue index 12d3907e5b..047e20c4cb 100644 --- a/src/views/Home.vue +++ b/src/views/Home.vue @@ -3,6 +3,7 @@ + @@ -12,11 +13,13 @@ import { Getter, Action } from 'vuex-class' import { remote } from 'electron' import Ipc from '@/components/Ipc.vue' import Leftbar from '@/components/Leftbar.vue' +import Update from '@/views/update/index.vue' @Component({ components: { Ipc, Leftbar, + Update, }, }) export default class Home extends Vue { diff --git a/src/views/about/index.vue b/src/views/about/index.vue index 8716f179b0..aa953ffa27 100644 --- a/src/views/about/index.vue +++ b/src/views/about/index.vue @@ -9,7 +9,7 @@

{{ $t('common.version') }} v{{ version }}

- {{ $t('about.update') }} + {{ $t('about.update') }} {{ $t('about.releases') }} @@ -128,7 +128,7 @@ export default class About extends Vue { } private checkUpdate(): void { - ipcRenderer.send('checkUpdate') + ipcRenderer.send('clickUpdate') } private goToLink(url: string) { diff --git a/src/views/update/index.vue b/src/views/update/index.vue new file mode 100644 index 0000000000..7e828574ff --- /dev/null +++ b/src/views/update/index.vue @@ -0,0 +1,359 @@ + + + + +