refactor: cleanup
This commit is contained in:
		
							parent
							
								
									1f8b17810a
								
							
						
					
					
						commit
						07f3854019
					
				| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
import { app, BrowserWindow, shell, ipcMain } from 'electron'
 | 
			
		||||
import { release } from 'node:os'
 | 
			
		||||
import { join } from 'node:path'
 | 
			
		||||
import { update } from '../preload/update'
 | 
			
		||||
import { update } from './update'
 | 
			
		||||
 | 
			
		||||
// The built directory structure
 | 
			
		||||
//
 | 
			
		||||
| 
						 | 
				
			
			@ -73,6 +73,8 @@ async function createWindow() {
 | 
			
		|||
    if (url.startsWith('https:')) shell.openExternal(url)
 | 
			
		||||
    return { action: 'deny' }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  // Apply electron-updater
 | 
			
		||||
  update(win)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,70 @@
 | 
			
		|||
import { app, ipcMain } from 'electron'
 | 
			
		||||
import {
 | 
			
		||||
  type ProgressInfo,
 | 
			
		||||
  type UpdateDownloadedEvent,
 | 
			
		||||
  autoUpdater
 | 
			
		||||
} from 'electron-updater'
 | 
			
		||||
 | 
			
		||||
export function update(win: Electron.BrowserWindow) {
 | 
			
		||||
 | 
			
		||||
  // When set to false, the update download will be triggered through the API
 | 
			
		||||
  autoUpdater.autoDownload = false
 | 
			
		||||
 | 
			
		||||
  autoUpdater.disableWebInstaller = false
 | 
			
		||||
 | 
			
		||||
  autoUpdater.allowDowngrade = false
 | 
			
		||||
 | 
			
		||||
  // start check
 | 
			
		||||
  autoUpdater.on('checking-for-update', function () { })
 | 
			
		||||
  // update available
 | 
			
		||||
  autoUpdater.on('update-available', (arg) => {
 | 
			
		||||
    win.webContents.send('update-can-available', { update: true, version: app.getVersion(), newVersion: arg?.version })
 | 
			
		||||
  })
 | 
			
		||||
  // update not available
 | 
			
		||||
  autoUpdater.on('update-not-available', (arg) => {
 | 
			
		||||
    win.webContents.send('update-can-available', { update: false, version: app.getVersion(), newVersion: arg?.version })
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  // Checking for updates
 | 
			
		||||
  ipcMain.handle('check-update', async () => {
 | 
			
		||||
    try {
 | 
			
		||||
      return await autoUpdater.checkForUpdatesAndNotify()
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
      return { message: 'Network error', error }
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  // Start downloading and feedback on progress
 | 
			
		||||
  ipcMain.handle('start-download', (event) => {
 | 
			
		||||
    startDownload(
 | 
			
		||||
      (error, progressInfo) => {
 | 
			
		||||
        if (error) {
 | 
			
		||||
          // feedback download error message
 | 
			
		||||
          event.sender.send('update-error', { message: error.message, error })
 | 
			
		||||
        } else {
 | 
			
		||||
          // feedback update progress message
 | 
			
		||||
          event.sender.send('download-progress', progressInfo)
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      () => {
 | 
			
		||||
        // feedback update downloaded message
 | 
			
		||||
        event.sender.send('update-downloaded')
 | 
			
		||||
      }
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  // Install now
 | 
			
		||||
  ipcMain.handle('quit-and-install', () => {
 | 
			
		||||
    autoUpdater.quitAndInstall(false, true)
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function startDownload(
 | 
			
		||||
  callback: (error: Error | null, info: ProgressInfo) => void,
 | 
			
		||||
  complete: (evnet: UpdateDownloadedEvent) => void,
 | 
			
		||||
) {
 | 
			
		||||
  autoUpdater.on('download-progress', info => callback(null, info))
 | 
			
		||||
  autoUpdater.on('error', error => callback(error, null))
 | 
			
		||||
  autoUpdater.on('update-downloaded', complete)
 | 
			
		||||
  autoUpdater.downloadUpdate()
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,95 +0,0 @@
 | 
			
		|||
import { autoUpdater } from "electron-updater"
 | 
			
		||||
import { app, ipcMain } from "electron";
 | 
			
		||||
export const update = (win: Electron.CrossProcessExports.BrowserWindow) => {
 | 
			
		||||
 | 
			
		||||
  // When set to false, the update download will be triggered through the API
 | 
			
		||||
  autoUpdater.autoDownload = false;
 | 
			
		||||
 | 
			
		||||
  autoUpdater.disableWebInstaller = false
 | 
			
		||||
 | 
			
		||||
  autoUpdater.allowDowngrade = false;
 | 
			
		||||
 | 
			
		||||
  // Save the version status of whether the update needs to be installed,
 | 
			
		||||
  // Because the user needs to update immediately and later after the update is downloaded
 | 
			
		||||
  let NEED_INSTALL = false;
 | 
			
		||||
 | 
			
		||||
  // Check whether update is used
 | 
			
		||||
  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})
 | 
			
		||||
      });
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  // start check
 | 
			
		||||
  autoUpdater.on('checking-for-update', function () {
 | 
			
		||||
    console.log('checking-for-update')
 | 
			
		||||
  })
 | 
			
		||||
  // update available
 | 
			
		||||
  autoUpdater.on('update-available', (arg) => {
 | 
			
		||||
    console.log('update-available')
 | 
			
		||||
    win.webContents.send('is-update-available', { isUpdate: true, oldVersion: app.getVersion(), newVersion: arg?.version })
 | 
			
		||||
  })
 | 
			
		||||
  // update not available
 | 
			
		||||
  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 })
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  const startDownload = (callback: any, successCallback: any) => {
 | 
			
		||||
    // Monitor the download progress and push it to the update window
 | 
			
		||||
    autoUpdater.on('download-progress', (data) => {
 | 
			
		||||
      console.log("progress", data)
 | 
			
		||||
      win.webContents.send('download-progress-data', data)
 | 
			
		||||
      callback && callback instanceof Function && callback(null, data);
 | 
			
		||||
    });
 | 
			
		||||
    // Listen for download errors and push to the update window
 | 
			
		||||
    autoUpdater.on('error', (err) => {
 | 
			
		||||
      callback && callback instanceof Function && callback(err);
 | 
			
		||||
    });
 | 
			
		||||
    // Listen to the download completion and push it to the update window
 | 
			
		||||
    autoUpdater.on('update-downloaded', () => {
 | 
			
		||||
      NEED_INSTALL = true;
 | 
			
		||||
      successCallback && successCallback instanceof Function && successCallback();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    autoUpdater.downloadUpdate();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // Listen to the process message sent by the application layer and start downloading updates
 | 
			
		||||
  ipcMain.on('start-download', (event) => {
 | 
			
		||||
    console.log("start")
 | 
			
		||||
    startDownload(
 | 
			
		||||
      (err: any, progressInfo: { percent: any; }) => {
 | 
			
		||||
        if (err) {
 | 
			
		||||
          // callback download error message
 | 
			
		||||
          event.sender.send('update-error', { updateError:true});
 | 
			
		||||
        } else {
 | 
			
		||||
           // callback update progress message
 | 
			
		||||
          event.sender.send('update-progress', { progressInfo: progressInfo.percent });
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      () => {
 | 
			
		||||
        // callback update downed message
 | 
			
		||||
        event.sender.send('update-downed');
 | 
			
		||||
      }
 | 
			
		||||
    );
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  // install now
 | 
			
		||||
  ipcMain.on('quit-and-install', () => {
 | 
			
		||||
    autoUpdater.quitAndInstall(false, true);
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  // install later
 | 
			
		||||
  app.on('will-quit', () => {
 | 
			
		||||
    console.log("NEED_INSTALL=true")
 | 
			
		||||
    if (NEED_INSTALL) {
 | 
			
		||||
      autoUpdater.quitAndInstall(true, false);
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -18,6 +18,9 @@
 | 
			
		|||
    "pree2e": "vite build --mode=test",
 | 
			
		||||
    "e2e": "playwright test"
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "electron-updater": "^5.3.0"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@playwright/test": "^1.31.0",
 | 
			
		||||
    "@types/react": "^18.0.28",
 | 
			
		||||
| 
						 | 
				
			
			@ -36,8 +39,5 @@
 | 
			
		|||
  },
 | 
			
		||||
  "engines": {
 | 
			
		||||
    "node": "^14.18.0 || >=16.0.0"
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "electron-updater": "^5.3.0"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,11 +1,10 @@
 | 
			
		|||
import nodeLogo from './assets/node.svg'
 | 
			
		||||
import { useState } from 'react'
 | 
			
		||||
import './App.scss'
 | 
			
		||||
import Update from '@/components/update'
 | 
			
		||||
import './App.scss'
 | 
			
		||||
 | 
			
		||||
console.log('[App.tsx]', `Hello world from Electron ${process.versions.electron}!`)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
function App() {
 | 
			
		||||
  const [count, setCount] = useState(0)
 | 
			
		||||
  return (
 | 
			
		||||
| 
						 | 
				
			
			@ -30,6 +29,7 @@ function App() {
 | 
			
		|||
      <div className='flex-center'>
 | 
			
		||||
        Place static files into the<code>/public</code> folder <img style={{ width: '5em' }} src={nodeLogo} alt='Node logo' />
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <Update />
 | 
			
		||||
    </div>
 | 
			
		||||
  )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,62 +1,67 @@
 | 
			
		|||
import { createPortal } from 'react-dom';
 | 
			
		||||
import { ModalChildType, ModalPropsType } from './type';
 | 
			
		||||
import modalScss from './modal.module.scss'
 | 
			
		||||
const ModalTemplate = (child: ModalChildType) => {
 | 
			
		||||
  return (
 | 
			
		||||
    <div className={modalScss.modal}>
 | 
			
		||||
      <div className='modal-bg' onClick={child.onCanCel} />
 | 
			
		||||
      <div className='modal-outboard'>
 | 
			
		||||
        <div className='modal-panel'>
 | 
			
		||||
          {child.isHeaderShow ? (
 | 
			
		||||
            <div className='modal-header'>
 | 
			
		||||
              <div className='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}
 | 
			
		||||
import React, { ReactNode } from 'react'
 | 
			
		||||
import { createPortal } from 'react-dom'
 | 
			
		||||
import styles from './modal.module.scss'
 | 
			
		||||
 | 
			
		||||
          <div className='modal-body'>{child.body}</div>
 | 
			
		||||
          {child.isFooterShow ? (
 | 
			
		||||
const ModalTemplate: React.FC<React.PropsWithChildren<{
 | 
			
		||||
  title?: ReactNode
 | 
			
		||||
  footer?: ReactNode
 | 
			
		||||
  cancelText?: string
 | 
			
		||||
  okText?: string
 | 
			
		||||
  onCancel?: () => void
 | 
			
		||||
  onOk?: () => void
 | 
			
		||||
  width?: number
 | 
			
		||||
}>> = props => {
 | 
			
		||||
  const {
 | 
			
		||||
    title,
 | 
			
		||||
    children,
 | 
			
		||||
    footer,
 | 
			
		||||
    cancelText = 'Cancel',
 | 
			
		||||
    okText = 'OK',
 | 
			
		||||
    onCancel,
 | 
			
		||||
    onOk,
 | 
			
		||||
    width = 530,
 | 
			
		||||
  } = props
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div className={styles.modal}>
 | 
			
		||||
      <div className='modal-mask' />
 | 
			
		||||
      <div className='modal-warp'>
 | 
			
		||||
        <div className='modal-content' style={{ width }}>
 | 
			
		||||
          <div className='modal-header'>
 | 
			
		||||
            <div className='modal-header-text'>{title}</div>
 | 
			
		||||
            <span
 | 
			
		||||
              className='modal-close'
 | 
			
		||||
              onClick={onCancel}
 | 
			
		||||
            >
 | 
			
		||||
              <svg
 | 
			
		||||
                viewBox="0 0 1024 1024"
 | 
			
		||||
                version="1.1" xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
              >
 | 
			
		||||
                <path d="M557.312 513.248l265.28-263.904c12.544-12.48 12.608-32.704 0.128-45.248-12.512-12.576-32.704-12.608-45.248-0.128l-265.344 263.936-263.04-263.84C236.64 191.584 216.384 191.52 203.84 204 191.328 216.48 191.296 236.736 203.776 249.28l262.976 263.776L201.6 776.8c-12.544 12.48-12.608 32.704-0.128 45.248 6.24 6.272 14.464 9.44 22.688 9.44 8.16 0 16.32-3.104 22.56-9.312l265.216-263.808 265.44 266.24c6.24 6.272 14.432 9.408 22.656 9.408 8.192 0 16.352-3.136 22.592-9.344 12.512-12.48 12.544-32.704 0.064-45.248L557.312 513.248z" p-id="2764" fill="currentColor">
 | 
			
		||||
                </path>
 | 
			
		||||
              </svg>
 | 
			
		||||
            </span>
 | 
			
		||||
          </div>
 | 
			
		||||
          <div className='modal-body'>{children}</div>
 | 
			
		||||
          {typeof footer !== 'undefined' ? (
 | 
			
		||||
            <div className='modal-footer'>
 | 
			
		||||
              {(child.isSubmitShow ?? true) ?<button onClick={child.onSubmit}>{child.submitText ?? '确认'}</button> : null}
 | 
			
		||||
              {(child.isCanCelShow ?? true) ? <button onClick={child.onCanCel}>{child.canCelText ?? '取消'}</button> : null}
 | 
			
		||||
              <button onClick={onCancel}>{cancelText}</button>
 | 
			
		||||
              <button onClick={onOk}>{okText}</button>
 | 
			
		||||
            </div>
 | 
			
		||||
          ) : null}
 | 
			
		||||
          ) : footer}
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Modal = (props: ModalPropsType) => {
 | 
			
		||||
  return  createPortal(
 | 
			
		||||
    props.isOpenModal?
 | 
			
		||||
      ModalTemplate({
 | 
			
		||||
        titleText: props.titleText,
 | 
			
		||||
        isHeaderShow: props.isHeaderShow ?? true,
 | 
			
		||||
        isFooterShow: props.isFooterShow ?? true,
 | 
			
		||||
        isCanCelShow: props.isCanCelShow ?? true,
 | 
			
		||||
        isSubmitShow: props.isSubmitShow ?? true,
 | 
			
		||||
        body: props.children,
 | 
			
		||||
        submitText: props.submitText,
 | 
			
		||||
        canCelText: props.canCelText,
 | 
			
		||||
        onCanCel: props.onCanCel,
 | 
			
		||||
        onSubmit: props.onSubmit,
 | 
			
		||||
      }): <div></div>,
 | 
			
		||||
      document.body,
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
export default Modal;
 | 
			
		||||
const Modal = (props: Parameters<typeof ModalTemplate>[0] & { open: boolean }) => {
 | 
			
		||||
  const { open, ...omit } = props
 | 
			
		||||
 | 
			
		||||
  return createPortal(
 | 
			
		||||
    open ? ModalTemplate(omit) : null,
 | 
			
		||||
    document.body,
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default Modal
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,63 +1,89 @@
 | 
			
		|||
.modal{
 | 
			
		||||
  :global{
 | 
			
		||||
    .modal-bg {
 | 
			
		||||
        width: 100vw;
 | 
			
		||||
        height: 100vh;
 | 
			
		||||
        position: fixed;
 | 
			
		||||
        left: 0;
 | 
			
		||||
        top: 0;
 | 
			
		||||
        z-index: 9999;
 | 
			
		||||
        background: rgba(0, 0, 0, 0.3);
 | 
			
		||||
.modal {
 | 
			
		||||
  --primary-color: rgb(224, 30, 90);
 | 
			
		||||
 | 
			
		||||
  :global {
 | 
			
		||||
    .modal-mask {
 | 
			
		||||
      width: 100vw;
 | 
			
		||||
      height: 100vh;
 | 
			
		||||
      position: fixed;
 | 
			
		||||
      left: 0;
 | 
			
		||||
      top: 0;
 | 
			
		||||
      z-index: 9;
 | 
			
		||||
      background: rgba(0, 0, 0, 0.45);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .modal-warp {
 | 
			
		||||
      position: fixed;
 | 
			
		||||
      top: 50%;
 | 
			
		||||
      left: 50%;
 | 
			
		||||
      transform: translate(-50%, -50%);
 | 
			
		||||
      z-index: 19;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .modal-content {
 | 
			
		||||
      box-shadow: 0 0 10px -4px rgb(130, 86, 208);
 | 
			
		||||
      overflow: hidden;
 | 
			
		||||
      border-radius: 4px;
 | 
			
		||||
 | 
			
		||||
      .modal-header {
 | 
			
		||||
        display: flex;
 | 
			
		||||
        line-height: 38px;
 | 
			
		||||
        background-color: var(--primary-color);
 | 
			
		||||
 | 
			
		||||
        .modal-header-text {
 | 
			
		||||
          font-weight: bold;
 | 
			
		||||
          width: 0;
 | 
			
		||||
          flex-grow: 1;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .modal-outboard {
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        top: 20vh;
 | 
			
		||||
        left: 30vw;
 | 
			
		||||
        z-index: 10000;
 | 
			
		||||
      .modal-close {
 | 
			
		||||
        width: 30px;
 | 
			
		||||
        height: 30px;
 | 
			
		||||
        margin: 4px;
 | 
			
		||||
        line-height: 34px;
 | 
			
		||||
        text-align: center;
 | 
			
		||||
        cursor: pointer;
 | 
			
		||||
 | 
			
		||||
        svg {
 | 
			
		||||
          width: 17px;
 | 
			
		||||
          height: 17px;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .modal-panel {
 | 
			
		||||
        border: 1px solid #000000;
 | 
			
		||||
        border-radius: 5px;
 | 
			
		||||
      .modal-body {
 | 
			
		||||
        padding: 10px;
 | 
			
		||||
        background-color: #fff;
 | 
			
		||||
        color: #333;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
        .modal-header {
 | 
			
		||||
          $titleheight: 38px;
 | 
			
		||||
          width: 530px;
 | 
			
		||||
          height: $titleheight;
 | 
			
		||||
          line-height: $titleheight;
 | 
			
		||||
          background-color: rgb(99, 153, 255);
 | 
			
		||||
          display: flex;
 | 
			
		||||
      .modal-footer {
 | 
			
		||||
        padding: 10px;
 | 
			
		||||
        background-color: #fff;
 | 
			
		||||
        display: flex;
 | 
			
		||||
        justify-content: end;
 | 
			
		||||
 | 
			
		||||
          .modal-header-text {
 | 
			
		||||
            text-align: center;
 | 
			
		||||
            width: 480px;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        button {
 | 
			
		||||
          padding: 7px 11px;
 | 
			
		||||
          background-color: var(--primary-color);
 | 
			
		||||
          font-size: 14px;
 | 
			
		||||
          margin-left: 10px;
 | 
			
		||||
 | 
			
		||||
        .modal-body {
 | 
			
		||||
          background-color: #ffffff;
 | 
			
		||||
        }
 | 
			
		||||
    
 | 
			
		||||
        .modal-footer {
 | 
			
		||||
          background-color: #ffffff;
 | 
			
		||||
          display: flex;
 | 
			
		||||
          justify-content: end;
 | 
			
		||||
    
 | 
			
		||||
          button {
 | 
			
		||||
            margin: 10px;
 | 
			
		||||
          &:first-child {
 | 
			
		||||
            margin-left: 0;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
      .icon {
 | 
			
		||||
        padding: 0 15px;
 | 
			
		||||
        width: 20px;
 | 
			
		||||
        fill: currentColor;
 | 
			
		||||
    .icon {
 | 
			
		||||
      padding: 0 15px;
 | 
			
		||||
      width: 20px;
 | 
			
		||||
      fill: currentColor;
 | 
			
		||||
 | 
			
		||||
        &:hover {
 | 
			
		||||
          color: rgba(0, 0, 0, 0.4);
 | 
			
		||||
        }
 | 
			
		||||
      &:hover {
 | 
			
		||||
        color: rgba(0, 0, 0, 0.4);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,20 +0,0 @@
 | 
			
		|||
import { ReactNode } from 'react'
 | 
			
		||||
interface childrens {
 | 
			
		||||
  titleText?: string
 | 
			
		||||
  isHeaderShow?: boolean
 | 
			
		||||
  isFooterShow?: boolean
 | 
			
		||||
  isCanCelShow?: boolean
 | 
			
		||||
  isSubmitShow?: 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
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,22 +1,22 @@
 | 
			
		|||
import { RsProgressType } from './type'
 | 
			
		||||
import progressScss from './progress.module.scss'
 | 
			
		||||
import React from 'react'
 | 
			
		||||
import styles from './progress.module.scss'
 | 
			
		||||
 | 
			
		||||
const Progress = (props: RsProgressType) => {
 | 
			
		||||
const Progress: React.FC<React.PropsWithChildren<{
 | 
			
		||||
  percent?: number
 | 
			
		||||
}>> = props => {
 | 
			
		||||
  const { percent = 0 } = props
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div className={progressScss.progress}>
 | 
			
		||||
      <div className='progress-pr'  style={{ width: props.rateWidth ?? 250 }}>
 | 
			
		||||
      <div
 | 
			
		||||
        className='progress-rate'
 | 
			
		||||
        style={{
 | 
			
		||||
          width: (props.percent ?? 0) * ((props.rateWidth ?? 250) / 100),
 | 
			
		||||
          backgroundColor: props.rateColor ?? 'blue',
 | 
			
		||||
        }}
 | 
			
		||||
      />
 | 
			
		||||
    <div className={styles.progress}>
 | 
			
		||||
      <div className='progress-pr'>
 | 
			
		||||
        <div
 | 
			
		||||
          className='progress-rate'
 | 
			
		||||
          style={{ width: `${percent / 100}%` }}
 | 
			
		||||
        />
 | 
			
		||||
      </div>
 | 
			
		||||
      <span className='progress-num'>{percent}%</span>
 | 
			
		||||
    </div>
 | 
			
		||||
      <span className='progress-num'>{props.percent > 100 ? 100 :(props.percent.toString().substring(0,4) ?? 0) }%</span>
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default Progress;
 | 
			
		||||
export default Progress
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,7 @@
 | 
			
		|||
 | 
			
		||||
  :global {
 | 
			
		||||
    .progress-pr {
 | 
			
		||||
      border: 1px solid #000000;
 | 
			
		||||
      border: 1px solid #000;
 | 
			
		||||
      border-radius: 3px;
 | 
			
		||||
      height: 6px;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -12,6 +12,7 @@
 | 
			
		|||
    .progress-rate {
 | 
			
		||||
      height: 6px;
 | 
			
		||||
      border-radius: 3px;
 | 
			
		||||
      background-image: linear-gradient(to right, rgb(130, 86, 208) 0%, var(--primary-color) 100%)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .progress-num {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +0,0 @@
 | 
			
		|||
export interface RsProgressType {
 | 
			
		||||
  rateColor?: string
 | 
			
		||||
  rateWidth?: number
 | 
			
		||||
  percent: number
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
interface VersionInfo {
 | 
			
		||||
  update: boolean
 | 
			
		||||
  version: string
 | 
			
		||||
  newVersion?: string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface ErrorType {
 | 
			
		||||
  message: string
 | 
			
		||||
  error: Error
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,120 +1,132 @@
 | 
			
		|||
import { ipcRenderer } from 'electron'
 | 
			
		||||
import type { ProgressInfo } from 'electron-updater'
 | 
			
		||||
import { useCallback, useEffect, useState } from 'react'
 | 
			
		||||
import Modal from '@/components/update/Modal'
 | 
			
		||||
import Progress from '@/components/update/Progress'
 | 
			
		||||
import { ipcRenderer } from 'electron'
 | 
			
		||||
import { useEffect, useState } from 'react'
 | 
			
		||||
import updateScss from './update.module.scss'
 | 
			
		||||
import { checkUpdateType, isUpdateAvailable, ModalBtnText, VersionInfo } from './type'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
let onModalSubmit = () => { }
 | 
			
		||||
let onModalCanCel = () => { }
 | 
			
		||||
import styles from './update.module.scss'
 | 
			
		||||
 | 
			
		||||
const Update = () => {
 | 
			
		||||
  const [checkBtnText, setCheckBtnText] = useState('check update')
 | 
			
		||||
  const [checkType, setCheckType] = useState(false)
 | 
			
		||||
  const [checkLoading, setCheckLoading] = useState(false)
 | 
			
		||||
  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: ''
 | 
			
		||||
  const [checking, setChecking] = useState(false)
 | 
			
		||||
  const [updateAvailable, setUpdateAvailable] = useState(false)
 | 
			
		||||
  const [versionInfo, setVersionInfo] = useState<VersionInfo>()
 | 
			
		||||
  const [updateError, setUpdateError] = useState<ErrorType>()
 | 
			
		||||
  const [progressInfo, setProgressInfo] = useState<ProgressInfo>()
 | 
			
		||||
  const [modalOpen, setModalOpen] = useState<boolean>(false)
 | 
			
		||||
  const [modalBtn, setModalBtn] = useState<{
 | 
			
		||||
    cancelText?: string
 | 
			
		||||
    okText?: string
 | 
			
		||||
    onCancel?: () => void
 | 
			
		||||
    onOk?: () => void
 | 
			
		||||
  }>({
 | 
			
		||||
    onCancel: () => setModalOpen(false),
 | 
			
		||||
    onOk: () => ipcRenderer.invoke('start-download'),
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    onModalCanCel = () => setIsOpenModal(false)
 | 
			
		||||
  }, [])
 | 
			
		||||
  const checkUpdate = async () => {
 | 
			
		||||
    setChecking(true)
 | 
			
		||||
    /**
 | 
			
		||||
     * @type {import('electron-updater').UpdateCheckResult | null | { message: string, error: Error }}
 | 
			
		||||
     */
 | 
			
		||||
    const result = await ipcRenderer.invoke('check-update')
 | 
			
		||||
    setChecking(false)
 | 
			
		||||
 | 
			
		||||
  // Check for updates
 | 
			
		||||
  const checkUpdate = () => {
 | 
			
		||||
    setCheckLoading(true)
 | 
			
		||||
    setCheckBtnText('checking Update ...')
 | 
			
		||||
    ipcRenderer.send('check-update')
 | 
			
		||||
    if (result?.error) {
 | 
			
		||||
      console.error(result.error)
 | 
			
		||||
      setUpdateAvailable(false)
 | 
			
		||||
    } else {
 | 
			
		||||
      setUpdateAvailable(true)
 | 
			
		||||
      setModalOpen(true)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Listen to get the check result
 | 
			
		||||
  ipcRenderer.on('check-update-type', (_event, ...args: checkUpdateType[]) => {
 | 
			
		||||
    setCheckLoading(false)
 | 
			
		||||
    setCheckBtnText('check update')
 | 
			
		||||
    setCheckType(args[0].checkUpdate)
 | 
			
		||||
    setIsOpenModal(true)
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  // 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)
 | 
			
		||||
    // Update required
 | 
			
		||||
    if (args[0].isUpdate) {
 | 
			
		||||
      setModalBtnText({
 | 
			
		||||
        canCelText: 'cancel',
 | 
			
		||||
        submitText: 'update'
 | 
			
		||||
      })
 | 
			
		||||
      onModalSubmit = () => ipcRenderer.send('start-download')
 | 
			
		||||
      onModalCanCel = () => setIsOpenModal(false)
 | 
			
		||||
  const onUpdateCanAvailable = useCallback((_event: Electron.IpcRendererEvent, arg1: VersionInfo) => {
 | 
			
		||||
    setVersionInfo(arg1)
 | 
			
		||||
    // Can be update
 | 
			
		||||
    if (arg1.update) {
 | 
			
		||||
      setModalBtn(state => ({
 | 
			
		||||
        ...state,
 | 
			
		||||
        cancelText: 'Cancel',
 | 
			
		||||
        okText: 'Update',
 | 
			
		||||
        onCancel: () => setModalOpen(false),
 | 
			
		||||
        onOk: () => ipcRenderer.invoke('start-download'),
 | 
			
		||||
      }))
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
  }, [])
 | 
			
		||||
 | 
			
		||||
  // Throw the update failure message when the update fails
 | 
			
		||||
  ipcRenderer.on('update-error', (_event, ...args: { updateError: boolean }[]) => {
 | 
			
		||||
    setUpdateError(args[0].updateError)
 | 
			
		||||
    setCheckType(false)
 | 
			
		||||
  })
 | 
			
		||||
  const onUpdateError = useCallback((_event: Electron.IpcRendererEvent, arg1: ErrorType) => {
 | 
			
		||||
    console.error(arg1.error)
 | 
			
		||||
    setUpdateError(arg1)
 | 
			
		||||
  }, [])
 | 
			
		||||
 | 
			
		||||
  // Get update progress 
 | 
			
		||||
  ipcRenderer.on('update-progress', (_event, ...args: { progressInfo: number }[]) => {
 | 
			
		||||
    setPercentNum(args[0].progressInfo)
 | 
			
		||||
  })
 | 
			
		||||
  const onDownloadProgress = useCallback((_event: Electron.IpcRendererEvent, arg1: ProgressInfo) => {
 | 
			
		||||
    console.log(arg1)
 | 
			
		||||
    setProgressInfo(arg1)
 | 
			
		||||
  }, [])
 | 
			
		||||
 | 
			
		||||
  // 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)
 | 
			
		||||
  const onUpdateDownloaded = useCallback((_event: Electron.IpcRendererEvent, ...args: any[]) => {
 | 
			
		||||
    setModalBtn(state => ({
 | 
			
		||||
      ...state,
 | 
			
		||||
      cancelText: 'Later',
 | 
			
		||||
      okText: 'Install now',
 | 
			
		||||
      onOk: () => ipcRenderer.invoke('quit-and-install'),
 | 
			
		||||
    }))
 | 
			
		||||
  }, [])
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    // Get version information and whether to update
 | 
			
		||||
    ipcRenderer.on('update-can-available', onUpdateCanAvailable)
 | 
			
		||||
    ipcRenderer.on('update-error', onUpdateError)
 | 
			
		||||
    ipcRenderer.on('download-progress', onDownloadProgress)
 | 
			
		||||
    ipcRenderer.on('update-downloaded', onUpdateDownloaded)
 | 
			
		||||
 | 
			
		||||
    return () => {
 | 
			
		||||
      ipcRenderer.off('update-can-available', onUpdateCanAvailable)
 | 
			
		||||
      ipcRenderer.off('update-error', onUpdateError)
 | 
			
		||||
      ipcRenderer.off('download-progress', onDownloadProgress)
 | 
			
		||||
      ipcRenderer.off('update-downloaded', onUpdateDownloaded)
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
  }, [])
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      <Modal isOpenModal={isOpenModal} onCanCel={onModalCanCel} onSubmit={onModalSubmit}
 | 
			
		||||
        canCelText={modalBtnText.canCelText} submitText={modalBtnText.submitText}
 | 
			
		||||
        isFooterShow={checkType && isNeedUpdate}>
 | 
			
		||||
        <div className={updateScss.modalslot}>
 | 
			
		||||
          {updateError ?
 | 
			
		||||
            <div className='update-error'>Error downloading the latest version, please contact the developer</div> :
 | 
			
		||||
            checkType ? (
 | 
			
		||||
              isNeedUpdate ? (
 | 
			
		||||
                <div>
 | 
			
		||||
                  <div>
 | 
			
		||||
                    <span> oldVersion : v.{versionInfo.oldVersion} </span>
 | 
			
		||||
                    <span> newVersion : v.{versionInfo.newVersion} </span>
 | 
			
		||||
      <Modal
 | 
			
		||||
        open={modalOpen}
 | 
			
		||||
        cancelText={modalBtn?.cancelText}
 | 
			
		||||
        okText={modalBtn?.okText}
 | 
			
		||||
        onCancel={modalBtn?.onCancel}
 | 
			
		||||
        onOk={modalBtn?.onOk}
 | 
			
		||||
        footer={!versionInfo?.update ? /* hide footer */null : undefined}
 | 
			
		||||
      >
 | 
			
		||||
        <div className={styles.modalslot}>
 | 
			
		||||
          {updateError ? (
 | 
			
		||||
            <div className='update-error'>
 | 
			
		||||
              <p>Error downloading the latest version.</p>
 | 
			
		||||
              <p>{updateError.message}</p>
 | 
			
		||||
            </div>
 | 
			
		||||
          ) : null}
 | 
			
		||||
          {!updateAvailable ? (
 | 
			
		||||
            <div className='update-not-available'>
 | 
			
		||||
              <span>Check update is Error, Please check your Network!</span>
 | 
			
		||||
            </div>
 | 
			
		||||
          ) : null}
 | 
			
		||||
          {versionInfo
 | 
			
		||||
            ? (
 | 
			
		||||
              <div>
 | 
			
		||||
                <div>The last version is: v{versionInfo.newVersion}</div>
 | 
			
		||||
                <div>v{versionInfo.version} -> v{versionInfo.newVersion}</div>
 | 
			
		||||
                <div className='update-progress'>
 | 
			
		||||
                  <div className='progress-title'>Update progress:</div>
 | 
			
		||||
                  <div className='progress-bar'>
 | 
			
		||||
                    <Progress percent={progressInfo?.percent} ></Progress>
 | 
			
		||||
                  </div>
 | 
			
		||||
                  <div className='update-progress'>
 | 
			
		||||
                    <span className='progress-title'> update progress : </span>
 | 
			
		||||
                    <Progress percent={percentNum} ></Progress>
 | 
			
		||||
                  </div>
 | 
			
		||||
                </div>)
 | 
			
		||||
                : <span>This is last version : v.{versionInfo.oldVersion} !</span>
 | 
			
		||||
            ) : <span>Check update is Error,Please check your network!</span>
 | 
			
		||||
          }
 | 
			
		||||
                </div>
 | 
			
		||||
              </div>
 | 
			
		||||
            )
 | 
			
		||||
            : <span>Checking...</span>}
 | 
			
		||||
        </div>
 | 
			
		||||
      </Modal>
 | 
			
		||||
      <button disabled={checkLoading} onClick={checkUpdate}>
 | 
			
		||||
        {checkBtnText}
 | 
			
		||||
      <button disabled={checking} onClick={checkUpdate}>
 | 
			
		||||
        {checking ? 'Checking...' : 'Check update'}
 | 
			
		||||
      </button>
 | 
			
		||||
    </>
 | 
			
		||||
  )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,18 +0,0 @@
 | 
			
		|||
 | 
			
		||||
export interface checkUpdateType {
 | 
			
		||||
  checkUpdate: boolean
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface VersionInfo {
 | 
			
		||||
  oldVersion: string
 | 
			
		||||
  newVersion: string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface isUpdateAvailable extends VersionInfo {
 | 
			
		||||
  isUpdate: boolean
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
export interface ModalBtnText {
 | 
			
		||||
  canCelText: string
 | 
			
		||||
  submitText: string
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,19 +1,20 @@
 | 
			
		|||
.modalslot{
 | 
			
		||||
  display: flex;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  justify-content: center;
 | 
			
		||||
  height: 100px;
 | 
			
		||||
.modalslot {
 | 
			
		||||
  // display: flex;
 | 
			
		||||
  // align-items: center;
 | 
			
		||||
  // justify-content: center;
 | 
			
		||||
 | 
			
		||||
  :global {
 | 
			
		||||
    .progress-title {
 | 
			
		||||
      width: 150px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .update-progress {
 | 
			
		||||
      display: flex;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .progress-title {
 | 
			
		||||
      margin-right: 10px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .progress-bar {
 | 
			
		||||
      width: 0;
 | 
			
		||||
      flex-grow: 1;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
.a{
 | 
			
		||||
  color: red;
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue