feat : add electron auto update template
feat : add some components url : https://github.com/RSS1101/electron-vite-react
This commit is contained in:
		
							parent
							
								
									29d5fa95a8
								
							
						
					
					
						commit
						564c48861d
					
				| 
						 | 
					@ -33,5 +33,10 @@
 | 
				
			||||||
    "perMachine": false,
 | 
					    "perMachine": false,
 | 
				
			||||||
    "allowToChangeInstallationDirectory": true,
 | 
					    "allowToChangeInstallationDirectory": true,
 | 
				
			||||||
    "deleteAppDataOnUninstall": false
 | 
					    "deleteAppDataOnUninstall": false
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  publish:{
 | 
				
			||||||
 | 
					    provider: 'generic', 
 | 
				
			||||||
 | 
					    channel: 'latest',
 | 
				
			||||||
 | 
					    url: 'https://github.com/RSS1101/electron-vite-react/releases/download/v9.9.9/',
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,7 @@
 | 
				
			||||||
import { app, BrowserWindow, shell, ipcMain } from 'electron'
 | 
					import { app, BrowserWindow, shell, ipcMain } from 'electron'
 | 
				
			||||||
import { release } from 'node:os'
 | 
					import { release } from 'node:os'
 | 
				
			||||||
import { join } from 'node:path'
 | 
					import { join } from 'node:path'
 | 
				
			||||||
 | 
					import { update } from '../preload/update'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// The built directory structure
 | 
					// The built directory structure
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
| 
						 | 
					@ -72,6 +73,7 @@ async function createWindow() {
 | 
				
			||||||
    if (url.startsWith('https:')) shell.openExternal(url)
 | 
					    if (url.startsWith('https:')) shell.openExternal(url)
 | 
				
			||||||
    return { action: 'deny' }
 | 
					    return { action: 'deny' }
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
 | 
					  update(win)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
app.whenReady().then(createWindow)
 | 
					app.whenReady().then(createWindow)
 | 
				
			||||||
| 
						 | 
					@ -113,4 +115,5 @@ ipcMain.handle('open-win', (_, arg) => {
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    childWindow.loadFile(indexHtml, { hash: arg })
 | 
					    childWindow.loadFile(indexHtml, { hash: arg })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,113 @@
 | 
				
			||||||
 | 
					import { autoUpdater } from "electron-updater"
 | 
				
			||||||
 | 
					import { app, ipcMain } from "electron";
 | 
				
			||||||
 | 
					export const update = (win: Electron.CrossProcessExports.BrowserWindow) => {
 | 
				
			||||||
 | 
					  // 设置日志打印
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 是否自动下载更新,设置为 false 时将通过 API 触发更新下载
 | 
				
			||||||
 | 
					  autoUpdater.autoDownload = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  autoUpdater.disableWebInstaller = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 是否允许版本降级,也就是服务器版本低于本地版本时,依旧以服务器版本为主
 | 
				
			||||||
 | 
					  autoUpdater.allowDowngrade = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 设置服务器版本最新版本查询接口配置
 | 
				
			||||||
 | 
					  autoUpdater.setFeedURL({
 | 
				
			||||||
 | 
					    provider: 'generic', 
 | 
				
			||||||
 | 
					    channel: 'latest',
 | 
				
			||||||
 | 
					    url: 'https://github.com/RSS1101/electron-vite-react/releases/download/v9.9.9/',
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 保存是否需要安装更新的版本状态,因为需要提供用户在下载完成更新之后立即更新和稍后更新的操作
 | 
				
			||||||
 | 
					  let NEED_INSTALL = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Object.defineProperty(app, 'isPackaged', {
 | 
				
			||||||
 | 
					    get() {
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 调用 API 检查是否用更新
 | 
				
			||||||
 | 
					  ipcMain.on('check-update',()=>{
 | 
				
			||||||
 | 
					    autoUpdater.checkForUpdatesAndNotify()
 | 
				
			||||||
 | 
					      .then((res) => {
 | 
				
			||||||
 | 
					        win.webContents.send('check-update-type',{ checkUpdate: true})
 | 
				
			||||||
 | 
					      }).catch(err => {
 | 
				
			||||||
 | 
					        // 网络错误 network error
 | 
				
			||||||
 | 
					        win.webContents.send('check-update-type', { checkUpdate: false})
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 检测开始
 | 
				
			||||||
 | 
					  autoUpdater.on('checking-for-update', function () {
 | 
				
			||||||
 | 
					    console.log('checking-for-update')
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  // 更新可用
 | 
				
			||||||
 | 
					  autoUpdater.on('update-available', (arg) => {
 | 
				
			||||||
 | 
					    console.log('update-available')
 | 
				
			||||||
 | 
					    win.webContents.send('is-update-available', { isUpdate: true, oldVersion: app.getVersion(), newVersion: arg?.version })
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  // 更新不可用
 | 
				
			||||||
 | 
					  autoUpdater.on('update-not-available', (arg) => {
 | 
				
			||||||
 | 
					    console.log('update-not-available')
 | 
				
			||||||
 | 
					    win.webContents.send('is-update-available', { isUpdate: false, oldVersion: app.getVersion(), newVersion: arg?.version })
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  // API 触发更新下载
 | 
				
			||||||
 | 
					  const startDownload = (callback: any, successCallback: any) => {
 | 
				
			||||||
 | 
					    // 监听下载进度并推送到更新窗口
 | 
				
			||||||
 | 
					    autoUpdater.on('download-progress', (data) => {
 | 
				
			||||||
 | 
					      console.log("progress", data)
 | 
				
			||||||
 | 
					      win.webContents.send('download-progress-data', data)
 | 
				
			||||||
 | 
					      callback && callback instanceof Function && callback(null, data);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    // 监听下载错误并推送到更新窗口
 | 
				
			||||||
 | 
					    autoUpdater.on('error', (err) => {
 | 
				
			||||||
 | 
					      console.log("error")
 | 
				
			||||||
 | 
					      callback && callback instanceof Function && callback(err);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    // 监听下载完成并推送到更新窗口
 | 
				
			||||||
 | 
					    autoUpdater.on('update-downloaded', () => {
 | 
				
			||||||
 | 
					      console.log("update-downloaded")
 | 
				
			||||||
 | 
					      NEED_INSTALL = true;
 | 
				
			||||||
 | 
					      successCallback && successCallback instanceof Function && successCallback();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    // 下载更新
 | 
				
			||||||
 | 
					    autoUpdater.downloadUpdate();
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 监听应用层发送来的进程消息,开始下载更新
 | 
				
			||||||
 | 
					  ipcMain.on('start-download', (event) => {
 | 
				
			||||||
 | 
					    console.log("start")
 | 
				
			||||||
 | 
					    startDownload(
 | 
				
			||||||
 | 
					      (err: any, progressInfo: { percent: any; }) => {
 | 
				
			||||||
 | 
					        if (err) {
 | 
				
			||||||
 | 
					          //回推下载错误消息
 | 
				
			||||||
 | 
					          console.log("update-error")
 | 
				
			||||||
 | 
					          event.sender.send('update-error', { updateError:true});
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          //回推下载进度消息
 | 
				
			||||||
 | 
					          console.log("pdate-progress-percent")
 | 
				
			||||||
 | 
					          event.sender.send('update-progress', { progressInfo: progressInfo.percent });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      () => {
 | 
				
			||||||
 | 
					        //回推下载完成消息
 | 
				
			||||||
 | 
					        console.log("update-downed")
 | 
				
			||||||
 | 
					        event.sender.send('update-downed');
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ipcMain.on('quit-and-install', () => {
 | 
				
			||||||
 | 
					    autoUpdater.quitAndInstall(false, true);
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 用户点击稍后安装后程序退出时执行立即安装更新
 | 
				
			||||||
 | 
					  app.on('will-quit', () => {
 | 
				
			||||||
 | 
					    console.log("NEED_INSTALL=true")
 | 
				
			||||||
 | 
					    if (NEED_INSTALL) {
 | 
				
			||||||
 | 
					      autoUpdater.quitAndInstall(true, false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -36,5 +36,8 @@
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "engines": {
 | 
					  "engines": {
 | 
				
			||||||
    "node": "^14.18.0 || >=16.0.0"
 | 
					    "node": "^14.18.0 || >=16.0.0"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "dependencies": {
 | 
				
			||||||
 | 
					    "electron-updater": "^5.3.0"
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,6 +32,10 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.card {
 | 
					.card {
 | 
				
			||||||
  padding: 2em;
 | 
					  padding: 2em;
 | 
				
			||||||
 | 
					  button{
 | 
				
			||||||
 | 
					    margin: 0 20px;
 | 
				
			||||||
 | 
					    background-color: #646cffaa;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.read-the-docs {
 | 
					.read-the-docs {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										43
									
								
								src/App.tsx
								
								
								
								
							
							
						
						
									
										43
									
								
								src/App.tsx
								
								
								
								
							| 
						 | 
					@ -1,11 +1,48 @@
 | 
				
			||||||
import nodeLogo from "./assets/node.svg"
 | 
					import nodeLogo from "./assets/node.svg"
 | 
				
			||||||
import { useState } from 'react'
 | 
					import { useEffect, useRef, useState } from 'react'
 | 
				
			||||||
import './App.scss'
 | 
					import './App.scss'
 | 
				
			||||||
 | 
					import Update from "./pages/update"
 | 
				
			||||||
 | 
					import { ipcRenderer } from "electron"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
console.log('[App.tsx]', `Hello world from Electron ${process.versions.electron}!`)
 | 
					console.log('[App.tsx]', `Hello world from Electron ${process.versions.electron}!`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface checkUpdateType {
 | 
				
			||||||
 | 
					  checkUpdate: boolean
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function App() {
 | 
					function App() {
 | 
				
			||||||
  const [count, setCount] = useState(0)
 | 
					  const [count, setCount] = useState(0)
 | 
				
			||||||
 | 
					  const ref =useRef<any>(null)
 | 
				
			||||||
 | 
					  const [checkBtnText, setCheckBtnText] = useState('check update')
 | 
				
			||||||
 | 
					  const [checkLoading, setCheckLoading] = useState(false)
 | 
				
			||||||
 | 
					  const [checkType, setCheckType] = useState(false)
 | 
				
			||||||
 | 
					  const [openModalType, setopenModalType] = useState(false)
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * @description 检查是否有新的更新 Listen for new updates
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  const checkUpdate = () => {
 | 
				
			||||||
 | 
					    setCheckLoading(true)
 | 
				
			||||||
 | 
					    setCheckBtnText('checking Update ...')
 | 
				
			||||||
 | 
					    ipcRenderer.send('check-update')
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * @description 获取到检查结果 Get the check results
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  ipcRenderer.on('check-update-type', (_event, ...args: checkUpdateType[]) => {
 | 
				
			||||||
 | 
					    setCheckLoading(false)
 | 
				
			||||||
 | 
					    setCheckBtnText('check update')
 | 
				
			||||||
 | 
					    setCheckType(args[0].checkUpdate)
 | 
				
			||||||
 | 
					    setopenModalType(true)
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * @description 在通信监听的过程中获取不到ref.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  useEffect(()=>{
 | 
				
			||||||
 | 
					    if (openModalType){
 | 
				
			||||||
 | 
					    ref.current!.openModal()
 | 
				
			||||||
 | 
					    setopenModalType(false)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }, [openModalType])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div className="App">
 | 
					    <div className="App">
 | 
				
			||||||
| 
						 | 
					@ -19,6 +56,9 @@ function App() {
 | 
				
			||||||
        <button onClick={() => setCount((count) => count + 1)}>
 | 
					        <button onClick={() => setCount((count) => count + 1)}>
 | 
				
			||||||
          count is {count}
 | 
					          count is {count}
 | 
				
			||||||
        </button>
 | 
					        </button>
 | 
				
			||||||
 | 
					        <button disabled={checkLoading} onClick={checkUpdate}>
 | 
				
			||||||
 | 
					          {checkBtnText}
 | 
				
			||||||
 | 
					        </button>
 | 
				
			||||||
        <p>
 | 
					        <p>
 | 
				
			||||||
          Edit <code>src/App.tsx</code> and save to test HMR
 | 
					          Edit <code>src/App.tsx</code> and save to test HMR
 | 
				
			||||||
        </p>
 | 
					        </p>
 | 
				
			||||||
| 
						 | 
					@ -29,6 +69,7 @@ function App() {
 | 
				
			||||||
      <div className="flex-center">
 | 
					      <div className="flex-center">
 | 
				
			||||||
        Place static files into the<code>/public</code> folder <img style={{ width: "5em" }} src={nodeLogo} alt="Node logo" />
 | 
					        Place static files into the<code>/public</code> folder <img style={{ width: "5em" }} src={nodeLogo} alt="Node logo" />
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
 | 
					      <Update ref={ref} checkType={checkType}/>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,50 @@
 | 
				
			||||||
 | 
					.rs-modal-bg{
 | 
				
			||||||
 | 
					  width: 100vw;
 | 
				
			||||||
 | 
					  height: 100vh;
 | 
				
			||||||
 | 
					  position: fixed;
 | 
				
			||||||
 | 
					  left: 0;
 | 
				
			||||||
 | 
					  top: 0;
 | 
				
			||||||
 | 
					  z-index: 9999;
 | 
				
			||||||
 | 
					  background: rgba(0, 0, 0, 0.3);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.rs-modal {
 | 
				
			||||||
 | 
					  position: absolute;
 | 
				
			||||||
 | 
					  top: 20vh;
 | 
				
			||||||
 | 
					  left: 30vw;
 | 
				
			||||||
 | 
					  z-index: 10000;
 | 
				
			||||||
 | 
					  .rs-modal-panel {
 | 
				
			||||||
 | 
					    border: 1px solid #000000;
 | 
				
			||||||
 | 
					    border-radius: 5px;
 | 
				
			||||||
 | 
					    .rs-modal-header {
 | 
				
			||||||
 | 
					      $titleheight: 38px;
 | 
				
			||||||
 | 
					      width: 530px;
 | 
				
			||||||
 | 
					      height: $titleheight;
 | 
				
			||||||
 | 
					      line-height: $titleheight;
 | 
				
			||||||
 | 
					      background-color: rgb(99, 153, 255);
 | 
				
			||||||
 | 
					      display: flex;
 | 
				
			||||||
 | 
					      .rs-modal-header-text {
 | 
				
			||||||
 | 
					        text-align: center;
 | 
				
			||||||
 | 
					        width: 480px;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .rs-modal-body{
 | 
				
			||||||
 | 
					      background-color: #ffffff;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .rs-modal-footer {
 | 
				
			||||||
 | 
					      background-color: #ffffff;
 | 
				
			||||||
 | 
					      display: flex;
 | 
				
			||||||
 | 
					      justify-content: end;
 | 
				
			||||||
 | 
					      button{
 | 
				
			||||||
 | 
					        margin: 10px;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  .icon {
 | 
				
			||||||
 | 
					    padding: 0 15px;
 | 
				
			||||||
 | 
					    width: 20px;
 | 
				
			||||||
 | 
					    fill: currentColor;
 | 
				
			||||||
 | 
					    &:hover {
 | 
				
			||||||
 | 
					      color: rgba(0, 0, 0, 0.4);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,60 @@
 | 
				
			||||||
 | 
					import { createPortal } from 'react-dom';
 | 
				
			||||||
 | 
					import { ModalChildType, ModalPropsType } from './type';
 | 
				
			||||||
 | 
					import './index.scss';
 | 
				
			||||||
 | 
					const ModalTemplate = (child: ModalChildType) => {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <>
 | 
				
			||||||
 | 
					      <div className="rs-modal-bg" onClick={child.onCanCel} />
 | 
				
			||||||
 | 
					      <div className="rs-modal">
 | 
				
			||||||
 | 
					        <div className="rs-modal-panel">
 | 
				
			||||||
 | 
					          {child.isHeaderShow ? (
 | 
				
			||||||
 | 
					            <div className="rs-modal-header">
 | 
				
			||||||
 | 
					              <div className="rs-modal-header-text">{child.titleText}</div>
 | 
				
			||||||
 | 
					              <svg
 | 
				
			||||||
 | 
					                onClick={child.onCanCel}
 | 
				
			||||||
 | 
					                className="icon"
 | 
				
			||||||
 | 
					                viewBox="0 0 1026 1024"
 | 
				
			||||||
 | 
					                version="1.1"
 | 
				
			||||||
 | 
					                xmlns="http://www.w3.org/2000/svg"
 | 
				
			||||||
 | 
					              >
 | 
				
			||||||
 | 
					                <path
 | 
				
			||||||
 | 
					                  d="M585.781589 510.748226l423.38657-423.38657A51.071963
 | 
				
			||||||
 | 
					            51.071963 0 0 0 937.156692 14.839469L513.770122 438.736759
 | 
				
			||||||
 | 
					            90.383552 14.839469A51.071963 51.071963 0 0 0 17.861365 87.361656L441.758655 
 | 
				
			||||||
 | 
					            510.748226l-423.89729 423.38657A51.071963 51.071963 0 1 0 89.872832 1006.146263l423.89729-423.38657 
 | 
				
			||||||
 | 
					            423.38657 423.38657a51.071963 51.071963 0 0 0 72.011467-72.011467z"
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					              </svg>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          ) : null}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          <div className="rs-modal-body">{child.body}</div>
 | 
				
			||||||
 | 
					          {child.isFooterShow ? (
 | 
				
			||||||
 | 
					            <div className="rs-modal-footer">
 | 
				
			||||||
 | 
					              <button onClick={child.onSubmit}>{child.submitText ?? '确认'}</button>
 | 
				
			||||||
 | 
					              <button onClick={child.onCanCel}>{child.canCelText ?? '取消'}</button>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          ) : null}
 | 
				
			||||||
 | 
					        </div> 
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const RsModal = (props: ModalPropsType) => {
 | 
				
			||||||
 | 
					  return  createPortal(
 | 
				
			||||||
 | 
					    props.isOpenModal?
 | 
				
			||||||
 | 
					      ModalTemplate({
 | 
				
			||||||
 | 
					        titleText: props.titleText,
 | 
				
			||||||
 | 
					        isHeaderShow: props.isHeaderShow ?? true,
 | 
				
			||||||
 | 
					        isFooterShow: props.isFooterShow ?? true,
 | 
				
			||||||
 | 
					        body: props.children,
 | 
				
			||||||
 | 
					        submitText: props.submitText,
 | 
				
			||||||
 | 
					        canCelText: props.canCelText,
 | 
				
			||||||
 | 
					        onCanCel: props.onCanCel,
 | 
				
			||||||
 | 
					        onSubmit: props.onSubmit,
 | 
				
			||||||
 | 
					      }): <div></div>,
 | 
				
			||||||
 | 
					      document.body,
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					export default RsModal;
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,18 @@
 | 
				
			||||||
 | 
					import { ReactNode } from 'react';
 | 
				
			||||||
 | 
					interface childrens {
 | 
				
			||||||
 | 
					  titleText?: string;
 | 
				
			||||||
 | 
					  isHeaderShow?: boolean;
 | 
				
			||||||
 | 
					  isFooterShow?: boolean;
 | 
				
			||||||
 | 
					  canCelText?: string;
 | 
				
			||||||
 | 
					  submitText?: string;
 | 
				
			||||||
 | 
					  onSubmit?: () => void;
 | 
				
			||||||
 | 
					  onCanCel?: () => void;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					export interface ModalChildType extends childrens {
 | 
				
			||||||
 | 
					  body: ReactNode | null;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface ModalPropsType extends childrens {
 | 
				
			||||||
 | 
					  isOpenModal: boolean;
 | 
				
			||||||
 | 
					  children: ReactNode | null;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,16 @@
 | 
				
			||||||
 | 
					.rs-progress{
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  .rs-progress-pr{
 | 
				
			||||||
 | 
					     border: 1px solid #000000;
 | 
				
			||||||
 | 
					     border-radius: 3px;
 | 
				
			||||||
 | 
					     height: 6px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  .rs-progress-rate{
 | 
				
			||||||
 | 
					     height: 6px;
 | 
				
			||||||
 | 
					     border-radius: 3px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  .rs-progress-num{
 | 
				
			||||||
 | 
					    margin: 0 10px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,22 @@
 | 
				
			||||||
 | 
					import { RsProgressType } from './type';
 | 
				
			||||||
 | 
					import './index.scss';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const RsProgress = (props: RsProgressType) => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div className="rs-progress">
 | 
				
			||||||
 | 
					      <div className="rs-progress-pr"  style={{ width: props.rateWidth ?? 250 }}>
 | 
				
			||||||
 | 
					      <div
 | 
				
			||||||
 | 
					        className="rs-progress-rate"
 | 
				
			||||||
 | 
					        style={{
 | 
				
			||||||
 | 
					          width: (props.percent ?? 0) * ((props.rateWidth ?? 250) / 100),
 | 
				
			||||||
 | 
					          backgroundColor: props.rateColor ?? 'blue',
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					      <span className='rs-progress-num'>{props.percent > 100 ? 100 :(props.percent.toString().substring(0,2) ?? 0) }%</span>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default RsProgress;
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,5 @@
 | 
				
			||||||
 | 
					export interface RsProgressType {
 | 
				
			||||||
 | 
					  rateColor?: string;
 | 
				
			||||||
 | 
					  rateWidth?: number;
 | 
				
			||||||
 | 
					  percent: number;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,13 @@
 | 
				
			||||||
 | 
					.modal-body{
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  justify-content: center;
 | 
				
			||||||
 | 
					  height: 100px;
 | 
				
			||||||
 | 
					  .progress-title{
 | 
				
			||||||
 | 
					    width: 150px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .update-progress{
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,123 @@
 | 
				
			||||||
 | 
					import RsModal from "@/components/RsModal"
 | 
				
			||||||
 | 
					import RsProgress from "@/components/RsProgress"
 | 
				
			||||||
 | 
					import { ipcRenderer } from "electron"
 | 
				
			||||||
 | 
					import { forwardRef, useEffect, useImperativeHandle, useState } from "react"
 | 
				
			||||||
 | 
					import "./index.scss"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface VersionInfo {
 | 
				
			||||||
 | 
					  oldVersion: string,
 | 
				
			||||||
 | 
					  newVersion: string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface isUpdateAvailable extends VersionInfo {
 | 
				
			||||||
 | 
					  isUpdate: boolean,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					interface ModalBtnText {
 | 
				
			||||||
 | 
					  canCelText: string,
 | 
				
			||||||
 | 
					  submitText: string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let onModalSubmit = () => { }
 | 
				
			||||||
 | 
					let onModalCanCel = () => { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Update = forwardRef((props: { checkType: boolean }, ref) => {
 | 
				
			||||||
 | 
					  const [isOpenModal, setIsOpenModal] = useState<boolean>(false)
 | 
				
			||||||
 | 
					  const [percentNum, setPercentNum] = useState<number>(0)
 | 
				
			||||||
 | 
					  const [isNeedUpdate, setIsNeedUpdate] = useState<boolean>(false)
 | 
				
			||||||
 | 
					  const [updateError, setUpdateError] = useState<boolean>(false)
 | 
				
			||||||
 | 
					  const [versionInfo, setVersionInfo] = useState<VersionInfo>({
 | 
				
			||||||
 | 
					    oldVersion: '',
 | 
				
			||||||
 | 
					    newVersion: ''
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  const [modalBtnText, setModalBtnText] = useState<ModalBtnText>({
 | 
				
			||||||
 | 
					    canCelText: '',
 | 
				
			||||||
 | 
					    submitText: ''
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  useImperativeHandle(ref, () => ({
 | 
				
			||||||
 | 
					    openModal: () => setIsOpenModal(true)
 | 
				
			||||||
 | 
					  }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    onModalCanCel = () => setIsOpenModal(false)
 | 
				
			||||||
 | 
					  }, [])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					  * @description 获取版本信息和是否需要更新 Get version information and whether to update
 | 
				
			||||||
 | 
					  */
 | 
				
			||||||
 | 
					  ipcRenderer.on('is-update-available', (_event, ...args: isUpdateAvailable[]) => {
 | 
				
			||||||
 | 
					    setVersionInfo({
 | 
				
			||||||
 | 
					      oldVersion: args[0].oldVersion,
 | 
				
			||||||
 | 
					      newVersion: args[0].newVersion,
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    setIsNeedUpdate(args[0].isUpdate)
 | 
				
			||||||
 | 
					    if (args[0].isUpdate) {
 | 
				
			||||||
 | 
					      setModalBtnText({
 | 
				
			||||||
 | 
					        canCelText: 'cancel',
 | 
				
			||||||
 | 
					        submitText: 'update'
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      onModalSubmit = () => ipcRenderer.send('start-download')
 | 
				
			||||||
 | 
					      onModalCanCel = () => setIsOpenModal(false)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * @description 如果更新失败了抛出更新失败信息 Throw the update failure message if the update fails
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  ipcRenderer.on('update-error', (_event, ...args: { updateError: boolean }[]) => {
 | 
				
			||||||
 | 
					    setUpdateError(args[0].updateError)
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * @description 监听更新进度 update progress 
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  ipcRenderer.on('update-progress', (_event, ...args: { progressInfo: number }[]) => {
 | 
				
			||||||
 | 
					    setPercentNum(args[0].progressInfo)
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * @description 监听是否更新完成 is update been completed
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  ipcRenderer.on('update-downed', (_event, ...args) => {
 | 
				
			||||||
 | 
					    setPercentNum(100)
 | 
				
			||||||
 | 
					    setModalBtnText({
 | 
				
			||||||
 | 
					      canCelText: 'install later',
 | 
				
			||||||
 | 
					      submitText: 'install now'
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    onModalSubmit = () => ipcRenderer.send('quit-and-install')
 | 
				
			||||||
 | 
					    onModalCanCel = () => {
 | 
				
			||||||
 | 
					      ipcRenderer.send('will-quit')
 | 
				
			||||||
 | 
					      setIsOpenModal(false)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <>
 | 
				
			||||||
 | 
					      <RsModal isOpenModal={isOpenModal} onCanCel={onModalCanCel} onSubmit={onModalSubmit}
 | 
				
			||||||
 | 
					        canCelText={modalBtnText.canCelText} submitText={modalBtnText.submitText}
 | 
				
			||||||
 | 
					        isFooterShow={props.checkType && isNeedUpdate}>
 | 
				
			||||||
 | 
					        <div className="modal-body">
 | 
				
			||||||
 | 
					          {updateError ?
 | 
				
			||||||
 | 
					            <div className="update-error">Error downloading the latest version, please contact the developer</div> :
 | 
				
			||||||
 | 
					            props.checkType ? (
 | 
				
			||||||
 | 
					              isNeedUpdate ? (
 | 
				
			||||||
 | 
					                <div>
 | 
				
			||||||
 | 
					                  <div>
 | 
				
			||||||
 | 
					                    <span> oldVersion : v.{versionInfo.oldVersion} </span>
 | 
				
			||||||
 | 
					                    <span> newVersion : v.{versionInfo.newVersion} </span>
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                  <div className="update-progress">
 | 
				
			||||||
 | 
					                    <span className="progress-title"> update progress : </span>
 | 
				
			||||||
 | 
					                    <RsProgress percent={percentNum} ></RsProgress>
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                </div>)
 | 
				
			||||||
 | 
					                : <span>This is last version : v.{versionInfo.oldVersion} !</span>
 | 
				
			||||||
 | 
					            ) : <span>Check update is Error,Please check your network!</span>
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </RsModal>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    </>
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Update
 | 
				
			||||||
		Loading…
	
		Reference in New Issue