Merge branch 'perf/ui-blocked' into feature/ui

This commit is contained in:
geekgeekrun
2024-03-10 21:44:14 +08:00
25 changed files with 576 additions and 510 deletions
@@ -1,103 +0,0 @@
import * as path from 'node:path'
import * as os from 'node:os'
import * as fs from 'node:fs'
import type { InstalledBrowser } from '@puppeteer/browsers'
import { is } from '@electron-toolkit/utils'
import electron from 'electron'
import { saveLastUsedAndAvailableBrowserInfo, BrowserInfo } from './history-utils'
export const EXPECT_CHROMIUM_BUILD_ID = '113.0.5672.63'
const cacheDir = path.join(
os.homedir(),
'.geekgeekrun',
'cache'
)
const getPuppeteerManagerModule = async () => {
let puppeteerManager
if (is.dev) {
puppeteerManager = await import('@puppeteer/browsers')
} else {
puppeteerManager = (
await import(
'file://' +
path.resolve(
electron.app.getAppPath(),
'..',
'external-node-runtime-dependencies/index.mjs'
)
)
).puppeteerManager
}
return puppeteerManager
}
export const getExpectCachedPuppeteerExecutable = async (): Promise<BrowserInfo> => {
const puppeteerManager = await getPuppeteerManagerModule()
const executablePath = puppeteerManager.computeExecutablePath({
browser: puppeteerManager.Browser.CHROME,
cacheDir,
buildId: EXPECT_CHROMIUM_BUILD_ID
})
const browser = puppeteerManager.Browser.CHROME[0].toUpperCase() + puppeteerManager.Browser.CHROME.slice(1) + ' ' + EXPECT_CHROMIUM_BUILD_ID
return {
executablePath,
browser
}
}
export const checkCachedPuppeteerExecutable = async () => {
try {
const executablePath = (await getExpectCachedPuppeteerExecutable()).executablePath
return fs.existsSync(executablePath)
} catch {
// should limit [ERR_MODULE_NOT_FOUND]
return false
}
}
const checkAndDownloadPuppeteerExecutable = async (
options: {
downloadProgressCallback?: (downloadedBytes: number, totalBytes: number) => void
confirmContinuePromise?: Promise<void>
} = {}
) => {
const puppeteerManager = await getPuppeteerManagerModule()
let installedBrowser: InstalledBrowser
if (!(await checkCachedPuppeteerExecutable())) {
try {
await options.confirmContinuePromise
} catch {
throw new Error('USER_CANCEL_DOWNLOAD_PUPPETEER')
}
// maybe the exist installation is broken.
await puppeteerManager.uninstall({
cacheDir,
buildId: EXPECT_CHROMIUM_BUILD_ID,
browser: puppeteerManager.Browser.CHROME
})
installedBrowser = await puppeteerManager.install({
browser: puppeteerManager.Browser.CHROME,
cacheDir,
buildId: EXPECT_CHROMIUM_BUILD_ID,
downloadProgressCallback: options.downloadProgressCallback
})
} else {
installedBrowser = (
await puppeteerManager.getInstalledBrowsers({
cacheDir
})
).find((it) => it.buildId === EXPECT_CHROMIUM_BUILD_ID)!
}
await saveLastUsedAndAvailableBrowserInfo({
executablePath: installedBrowser.executablePath,
browser: installedBrowser.browser[0].toUpperCase() + installedBrowser.browser.slice(1) + ' ' + EXPECT_CHROMIUM_BUILD_ID
})
return installedBrowser
}
export default checkAndDownloadPuppeteerExecutable
@@ -1,66 +0,0 @@
import { is } from '@electron-toolkit/utils'
import electron from 'electron'
import * as os from 'node:os'
import * as fs from 'node:fs'
import path from 'node:path'
import { EXPECT_CHROMIUM_BUILD_ID } from './check-and-download-puppeteer-executable'
import {
getExecutableFileVersion
} from '@geekgeekrun/utils/windows-only/file.mjs'
import { BrowserInfo } from './history-utils'
export default async function findAndLocateExistedChromiumExecutable(): Promise<BrowserInfo> {
const exceptChromiumMainVersion = Number(EXPECT_CHROMIUM_BUILD_ID.split('.')[0])
// For windows, try to find Edge(chromium)
if (os.platform() === 'win32') {
// TODO: handle windows
const edgeExecutableLocation = path.join(
process.env['ProgramFiles(x86)']!,
'Microsoft/Edge/Application',
'msedge.exe'
)
if (
fs.existsSync(edgeExecutableLocation)
) {
try {
const version = await getExecutableFileVersion(edgeExecutableLocation)
const mainVersion = Number(version.split('.')[0])
if ( mainVersion >= exceptChromiumMainVersion) {
return {
executablePath: edgeExecutableLocation,
browser: `Edge ${version}`
}
}
} catch(err) {
console.log(err)
}
}
}
// For other, use findChrome
let findChrome: typeof import('find-chrome-bin').findChrome
if (is.dev) {
findChrome = (await import('find-chrome-bin')).findChrome
} else {
findChrome = (
await import(
'file://' +
path.resolve(
electron.app.getAppPath(),
'..',
'external-node-runtime-dependencies/index.mjs'
)
)
).findChromeBin.findChrome
}
const targetBrowser = await findChrome({
min: exceptChromiumMainVersion
})
if (!targetBrowser?.executablePath) {
throw new Error('NO_EXPECT_CHROMIUM_FOUND')
}
return {
executablePath: targetBrowser.executablePath,
browser: targetBrowser.browser
}
}
@@ -1,53 +1,14 @@
import { app } from 'electron' import { app } from 'electron'
import checkAndDownloadPuppeteerExecutable, { import { checkAndDownloadPuppeteerExecutable } from './utils/puppeteer-executable/index'
checkCachedPuppeteerExecutable,
getExpectCachedPuppeteerExecutable
} from './check-and-download-puppeteer-executable'
import * as fs from 'fs' import * as fs from 'fs'
import { pipeWriteRegardlessError } from '../utils/pipe' import { pipeWriteRegardlessError } from '../utils/pipe'
import { import { sleep } from '@geekgeekrun/utils/sleep.mjs'
removeLastUsedAndAvailableBrowserPath,
getLastUsedAndAvailableBrowser,
saveLastUsedAndAvailableBrowserInfo,
BrowserInfo
} from './history-utils'
import findAndLocateExistedChromiumExecutable from './check-and-locate-existed-chromium-executable'
import {
sleep
} from '@geekgeekrun/utils/sleep.mjs'
export enum DOWNLOAD_ERROR_EXIT_CODE { export enum DOWNLOAD_ERROR_EXIT_CODE {
NO_ERROR = 0, NO_ERROR = 0,
DOWNLOAD_ERROR = 1 DOWNLOAD_ERROR = 1
} }
export const getAnyAvailablePuppeteerExecutable = async (): Promise<BrowserInfo | null> => {
const lastUsedOne = await getLastUsedAndAvailableBrowser()
if (lastUsedOne) {
return lastUsedOne
}
// find existed browser - the one maybe actively installed by user or ship with os like Edge on windows
try {
const existedOne = (await findAndLocateExistedChromiumExecutable())
await saveLastUsedAndAvailableBrowserInfo(existedOne)
// save its path
return existedOne
} catch {
console.log('no existed browser path found')
}
// find existed browser - the fallback one
if (await checkCachedPuppeteerExecutable()) {
const cachedOne = await getExpectCachedPuppeteerExecutable()
await saveLastUsedAndAvailableBrowserInfo(cachedOne)
return cachedOne
}
// if no one available, then return null and remove last used browser
await removeLastUsedAndAvailableBrowserPath()
return null
}
export const checkAndDownloadDependenciesForInit = async () => { export const checkAndDownloadDependenciesForInit = async () => {
process.on('disconnect', () => app.exit()) process.on('disconnect', () => app.exit())
app.dock?.hide() app.dock?.hide()
@@ -4,8 +4,8 @@ import * as fs from 'fs'
import * as fsPromise from 'fs/promises' import * as fsPromise from 'fs/promises'
export interface BrowserInfo { export interface BrowserInfo {
browser: string; browser: string
executablePath: string; executablePath: string
} }
const runtimeFolderPath = path.join(os.homedir(), '.geekgeekrun') const runtimeFolderPath = path.join(os.homedir(), '.geekgeekrun')
@@ -27,7 +27,7 @@ export const getLastUsedAndAvailableBrowser = async (): Promise<BrowserInfo | nu
} }
try { try {
const fileContent = (await fsPromise.readFile(lastUsedBrowserRecordFilePath)).toString() const fileContent = (await fsPromise.readFile(lastUsedBrowserRecordFilePath)).toString()
const [path, browser] = fileContent.split('\n').map(it => it.trim()) const [path, browser] = fileContent.split('\n').map((it) => it.trim())
if (!path || !fs.existsSync(path)) { if (!path || !fs.existsSync(path)) {
await removeLastUsedAndAvailableBrowserPath() await removeLastUsedAndAvailableBrowserPath()
return null return null
@@ -48,10 +48,8 @@ export const saveLastUsedAndAvailableBrowserInfo = async (browserInfo: BrowserIn
await fsPromise.mkdir(runtimeFolderPath) await fsPromise.mkdir(runtimeFolderPath)
} }
await fsPromise.writeFile( await fsPromise.writeFile(
lastUsedBrowserRecordFilePath, [ lastUsedBrowserRecordFilePath,
browserInfo.executablePath, [browserInfo.executablePath, browserInfo.browser].join('\n')
browserInfo.browser
].join('\n')
) )
} catch { } catch {
console.warn('lastUsedBrowserRecordFile write error') console.warn('lastUsedBrowserRecordFile write error')
@@ -0,0 +1,213 @@
import * as path from 'node:path'
import * as os from 'node:os'
import * as fs from 'node:fs'
import type { InstalledBrowser } from '@puppeteer/browsers'
import {
saveLastUsedAndAvailableBrowserInfo,
BrowserInfo,
getLastUsedAndAvailableBrowser,
removeLastUsedAndAvailableBrowserPath
} from '../browser-history'
import { getExecutableFileVersion } from '@geekgeekrun/utils/windows-only/file.mjs'
import CheckAndLocateExistedChromiumExecutableWorker from './worker/find-and-locate-existed-chromium-executable?nodeWorker&url'
import { type Worker } from 'worker_threads'
const getPuppeteerManagerModule = async () => {
const electron = await import('electron')
let puppeteerManager
if (process.env.NODE_ENV === 'development') {
puppeteerManager = await import('@puppeteer/browsers')
} else {
puppeteerManager = (
await import(
'file://' +
path.resolve(
electron.app.getAppPath(),
'..',
'external-node-runtime-dependencies/index.mjs'
)
)
).puppeteerManager
}
return puppeteerManager
}
const EXPECT_CHROMIUM_BUILD_ID = '113.0.5672.63'
const cacheDir = path.join(os.homedir(), '.geekgeekrun', 'cache')
const getExpectCachedPuppeteerExecutable = async (): Promise<BrowserInfo> => {
const puppeteerManager = await getPuppeteerManagerModule()
const executablePath = puppeteerManager.computeExecutablePath({
browser: puppeteerManager.Browser.CHROME,
cacheDir,
buildId: EXPECT_CHROMIUM_BUILD_ID
})
const browser =
puppeteerManager.Browser.CHROME[0].toUpperCase() +
puppeteerManager.Browser.CHROME.slice(1) +
' ' +
EXPECT_CHROMIUM_BUILD_ID
return {
executablePath,
browser
}
}
const checkCachedPuppeteerExecutable = async () => {
try {
const executablePath = (await getExpectCachedPuppeteerExecutable()).executablePath
return fs.existsSync(executablePath)
} catch {
// should limit [ERR_MODULE_NOT_FOUND]
return false
}
}
export const checkAndDownloadPuppeteerExecutable = async (
options: {
downloadProgressCallback?: (downloadedBytes: number, totalBytes: number) => void
confirmContinuePromise?: Promise<void>
} = {}
) => {
const puppeteerManager = await getPuppeteerManagerModule()
let installedBrowser: InstalledBrowser
if (!(await checkCachedPuppeteerExecutable())) {
try {
await options.confirmContinuePromise
} catch {
throw new Error('USER_CANCEL_DOWNLOAD_PUPPETEER')
}
// maybe the exist installation is broken.
await puppeteerManager.uninstall({
cacheDir,
buildId: EXPECT_CHROMIUM_BUILD_ID,
browser: puppeteerManager.Browser.CHROME
})
installedBrowser = await puppeteerManager.install({
browser: puppeteerManager.Browser.CHROME,
cacheDir,
buildId: EXPECT_CHROMIUM_BUILD_ID,
downloadProgressCallback: options.downloadProgressCallback
})
} else {
installedBrowser = (
await puppeteerManager.getInstalledBrowsers({
cacheDir
})
).find((it) => it.buildId === EXPECT_CHROMIUM_BUILD_ID)!
}
await saveLastUsedAndAvailableBrowserInfo({
executablePath: installedBrowser.executablePath,
browser:
installedBrowser.browser[0].toUpperCase() +
installedBrowser.browser.slice(1) +
' ' +
EXPECT_CHROMIUM_BUILD_ID
})
return installedBrowser
}
export const getAnyAvailablePuppeteerExecutable = async (): Promise<BrowserInfo | null> => {
const lastUsedOne = await getLastUsedAndAvailableBrowser()
if (lastUsedOne) {
return lastUsedOne
}
// find existed browser - the one maybe actively installed by user or ship with os like Edge on windows
try {
const existedOne = await findAndLocateUserInstalledChromiumExecutable()
await saveLastUsedAndAvailableBrowserInfo(existedOne)
// save its path
return existedOne
} catch (err) {
console.error(err)
console.log('no existed browser path found')
}
// find existed browser - the fallback one
if (await checkCachedPuppeteerExecutable()) {
const cachedOne = await getExpectCachedPuppeteerExecutable()
await saveLastUsedAndAvailableBrowserInfo(cachedOne)
return cachedOne
}
// if no one available, then return null and remove last used browser
await removeLastUsedAndAvailableBrowserPath()
return null
}
export async function findAndLocateUserInstalledChromiumExecutableSync(): Promise<BrowserInfo> {
const electron = await import('electron')
const exceptChromiumMainVersion = Number(EXPECT_CHROMIUM_BUILD_ID.split('.')[0])
// For windows, try to find Edge(chromium)
if (os.platform() === 'win32') {
// TODO: handle windows
const edgeExecutableLocation = path.join(
process.env['ProgramFiles(x86)']!,
'Microsoft/Edge/Application',
'msedge.exe'
)
if (fs.existsSync(edgeExecutableLocation)) {
try {
const version = await getExecutableFileVersion(edgeExecutableLocation)
const mainVersion = Number(version.split('.')[0])
if (mainVersion >= exceptChromiumMainVersion) {
return {
executablePath: edgeExecutableLocation,
browser: `Edge ${version}`
}
}
} catch (err) {
console.log(err)
}
}
}
// For other, use findChrome
let findChrome: typeof import('find-chrome-bin').findChrome
if (process.env.NODE_ENV === 'development') {
findChrome = (await import('find-chrome-bin')).findChrome
} else {
findChrome = (
await import(
'file://' +
path.resolve(
electron.app.getAppPath(),
'..',
'external-node-runtime-dependencies/index.mjs'
)
)
).findChromeBin.findChrome
}
const targetBrowser = await findChrome({
min: exceptChromiumMainVersion
})
if (!targetBrowser?.executablePath) {
throw new Error('NO_EXPECT_CHROMIUM_FOUND')
}
return {
executablePath: targetBrowser.executablePath,
browser: targetBrowser.browser
}
}
export async function findAndLocateUserInstalledChromiumExecutable(): Promise<BrowserInfo> {
return new Promise((resolve, reject) => {
const worker: Worker = new CheckAndLocateExistedChromiumExecutableWorker()
worker.once('message', (data) => {
if (data.type === 'RESULT') {
resolve(data.data)
}
})
worker.once('message', (data) => {
if (data.type === 'ERROR') {
reject(data.error)
}
})
})
}
@@ -0,0 +1,18 @@
import { parentPort } from 'node:worker_threads'
import { findAndLocateUserInstalledChromiumExecutableSync } from '../index'
;(async () => {
try {
const result = await findAndLocateUserInstalledChromiumExecutableSync()
parentPort?.postMessage({
type: 'RESULT',
data: result
})
} catch (error) {
parentPort?.postMessage({
type: 'ERROR',
error
})
}
process.exit(0)
})()
@@ -3,8 +3,8 @@ import { app } from 'electron'
import { SyncHook, AsyncSeriesHook } from 'tapable' import { SyncHook, AsyncSeriesHook } from 'tapable'
import { readConfigFile } from '@geekgeekrun/geek-auto-start-chat-with-boss/runtime-file-utils.mjs' import { readConfigFile } from '@geekgeekrun/geek-auto-start-chat-with-boss/runtime-file-utils.mjs'
import * as fs from 'fs' import * as fs from 'fs'
import { pipeWriteRegardlessError } from './utils/pipe' import { pipeWriteRegardlessError } from '../utils/pipe'
import { getAnyAvailablePuppeteerExecutable } from './CHECK_AND_DOWNLOAD_DEPENDENCIES' import { getAnyAvailablePuppeteerExecutable } from '../CHECK_AND_DOWNLOAD_DEPENDENCIES/utils/puppeteer-executable'
const { groupRobotAccessToken: dingTalkAccessToken } = readConfigFile('dingtalk.json') const { groupRobotAccessToken: dingTalkAccessToken } = readConfigFile('dingtalk.json')
@@ -3,11 +3,6 @@ import { main, loginEventBus } from '@geekgeekrun/launch-bosszhipin-login-page-w
import { pipeWriteRegardlessError } from './utils/pipe' import { pipeWriteRegardlessError } from './utils/pipe'
import fs from "node:fs"; import fs from "node:fs";
export enum DOWNLOAD_ERROR_EXIT_CODE {
NO_ERROR = 0,
DOWNLOAD_ERROR = 1
}
export const launchBossZhipinLoginPageWithPreloadExtension = async () => { export const launchBossZhipinLoginPageWithPreloadExtension = async () => {
process.on('disconnect', () => app.exit()) process.on('disconnect', () => app.exit())
app.dock?.hide() app.dock?.hide()
+1 -1
View File
@@ -1,4 +1,4 @@
import { runAutoChat } from './flow/GEEK_AUTO_START_CHAT_WITH_BOSS' import { runAutoChat } from './flow/GEEK_AUTO_START_CHAT_WITH_BOSS/index'
import { openSettingWindow } from './flow/OPEN_SETTING_WINDOW' import { openSettingWindow } from './flow/OPEN_SETTING_WINDOW'
import { checkAndDownloadDependenciesForInit } from './flow/CHECK_AND_DOWNLOAD_DEPENDENCIES/index' import { checkAndDownloadDependenciesForInit } from './flow/CHECK_AND_DOWNLOAD_DEPENDENCIES/index'
import { launchBossZhipinLoginPageWithPreloadExtension } from './flow/LAUNCH_BOSS_ZHIPIN_LOGIN_PAGE_WITH_PRELOAD_EXTENSION' import { launchBossZhipinLoginPageWithPreloadExtension } from './flow/LAUNCH_BOSS_ZHIPIN_LOGIN_PAGE_WITH_PRELOAD_EXTENSION'
+22 -15
View File
@@ -1,11 +1,9 @@
import { BrowserWindow, ipcMain, shell } from 'electron' import { BrowserWindow, ipcMain, shell } from 'electron'
import path from 'path' import path from 'path'
import * as childProcess from 'node:child_process' import * as childProcess from 'node:child_process'
import { is } from '@electron-toolkit/utils'
import { import {
ensureConfigFileExist, ensureConfigFileExist,
ensureStorageFileExist, ensureStorageFileExist,
configFileNameList, configFileNameList,
readConfigFile, readConfigFile,
writeConfigFile, writeConfigFile,
@@ -15,10 +13,8 @@ import {
import { ChildProcess } from 'child_process' import { ChildProcess } from 'child_process'
import * as JSONStream from 'JSONStream' import * as JSONStream from 'JSONStream'
import { checkCookieListFormat } from '../../common/utils/cookie' import { checkCookieListFormat } from '../../common/utils/cookie'
import { import { getAnyAvailablePuppeteerExecutable } from '../flow/CHECK_AND_DOWNLOAD_DEPENDENCIES/utils/puppeteer-executable/index'
DOWNLOAD_ERROR_EXIT_CODE, import { DOWNLOAD_ERROR_EXIT_CODE } from '../flow/CHECK_AND_DOWNLOAD_DEPENDENCIES/index'
getAnyAvailablePuppeteerExecutable
} from '../flow/CHECK_AND_DOWNLOAD_DEPENDENCIES'
import { sleep } from '@geekgeekrun/utils/sleep.mjs' import { sleep } from '@geekgeekrun/utils/sleep.mjs'
let mainWindow: BrowserWindow | null = null let mainWindow: BrowserWindow | null = null
@@ -31,8 +27,8 @@ export function createMainWindow(): void {
autoHideMenuBar: true, autoHideMenuBar: true,
...(process.platform === 'linux' ...(process.platform === 'linux'
? { ? {
/* icon */ /* icon */
} }
: {}), : {}),
webPreferences: { webPreferences: {
preload: path.join(__dirname, '../preload/index.js'), preload: path.join(__dirname, '../preload/index.js'),
@@ -40,7 +36,7 @@ export function createMainWindow(): void {
} }
}) })
is.dev && mainWindow.webContents.openDevTools() process.env.NODE_ENV === 'development' && mainWindow.webContents.openDevTools()
mainWindow.on('ready-to-show', () => { mainWindow.on('ready-to-show', () => {
mainWindow.show() mainWindow.show()
@@ -53,7 +49,7 @@ export function createMainWindow(): void {
// HMR for renderer base on electron-vite cli. // HMR for renderer base on electron-vite cli.
// Load the remote URL for development or the local html file for production. // Load the remote URL for development or the local html file for production.
if (is.dev && process.env['ELECTRON_RENDERER_URL']) { if (process.env.NODE_ENV === 'development' && process.env['ELECTRON_RENDERER_URL']) {
mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL']) mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'])
} else { } else {
mainWindow.loadFile(path.join(__dirname, '../renderer/index.html')) mainWindow.loadFile(path.join(__dirname, '../renderer/index.html'))
@@ -70,7 +66,7 @@ export function createMainWindow(): void {
return readConfigFile(fileName) return readConfigFile(fileName)
}) })
const result = { const result = {
config: {}, config: {}
} }
configFileNameList.forEach((fileName, index) => { configFileNameList.forEach((fileName, index) => {
@@ -106,17 +102,28 @@ export function createMainWindow(): void {
// const currentExecutablePath = app.getPath('exe') // const currentExecutablePath = app.getPath('exe')
// console.log(currentExecutablePath) // console.log(currentExecutablePath)
ipcMain.handle('prepare-run-geek-auto-start-chat-with-boss', async () => {
mainWindow?.webContents.send('locating-puppeteer-executable')
const puppeteerExecutable = await getAnyAvailablePuppeteerExecutable()
if (!puppeteerExecutable) {
return Promise.reject('NEED_TO_CHECK_RUNTIME_DEPENDENCIES')
}
mainWindow?.webContents.send('puppeteer-executable-is-located')
})
let subProcessOfPuppeteer: ChildProcess | null = null let subProcessOfPuppeteer: ChildProcess | null = null
ipcMain.handle('run-geek-auto-start-chat-with-boss', async () => { ipcMain.handle('run-geek-auto-start-chat-with-boss', async () => {
if (subProcessOfPuppeteer) { if (subProcessOfPuppeteer) {
return return
} }
const puppeteerExecutable = await getAnyAvailablePuppeteerExecutable()
if (!puppeteerExecutable) {
return Promise.reject('NEED_TO_CHECK_RUNTIME_DEPENDENCIES')
}
const subProcessEnv = { const subProcessEnv = {
...process.env, ...process.env,
MAIN_BOSSGEEKGO_UI_RUN_MODE: 'geekAutoStartWithBoss', MAIN_BOSSGEEKGO_UI_RUN_MODE: 'geekAutoStartWithBoss',
PUPPETEER_EXECUTABLE_PATH: (await getAnyAvailablePuppeteerExecutable())!.executablePath PUPPETEER_EXECUTABLE_PATH: puppeteerExecutable.executablePath
} }
subProcessOfPuppeteer = childProcess.spawn(process.argv[0], process.argv.slice(1), { subProcessOfPuppeteer = childProcess.spawn(process.argv[0], process.argv.slice(1), {
env: subProcessEnv, env: subProcessEnv,
@@ -171,7 +178,7 @@ export function createMainWindow(): void {
} }
const subProcessEnv = { const subProcessEnv = {
...process.env, ...process.env,
MAIN_BOSSGEEKGO_UI_RUN_MODE: 'checkAndDownloadDependenciesForInit', MAIN_BOSSGEEKGO_UI_RUN_MODE: 'checkAndDownloadDependenciesForInit'
} }
subProcessOfCheckAndDownloadDependencies = childProcess.spawn( subProcessOfCheckAndDownloadDependencies = childProcess.spawn(
process.argv[0], process.argv[0],
@@ -194,7 +201,7 @@ export function createMainWindow(): void {
} }
case 'PUPPETEER_DOWNLOAD_ENCOUNTER_ERROR': { case 'PUPPETEER_DOWNLOAD_ENCOUNTER_ERROR': {
console.error(data) console.error(data)
break; break
} }
default: { default: {
return return
+3 -1
View File
@@ -1,6 +1,8 @@
<template> <template>
<el-config-provider :locale="zhCn"> <el-config-provider :locale="zhCn">
<RouterView /> <Suspense>
<RouterView />
</Suspense>
</el-config-provider> </el-config-provider>
</template> </template>
@@ -1,39 +0,0 @@
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import DependenciesSetupProgressIndicatorDialog from './index.vue'
export const mountGlobalDialog = (o: { dependenciesStatus: Record<string, boolean>, processWaitee? }) => {
const containerElId = `elForDependenciesSetupProgressIndicatorDialog`
if (document.getElementById(containerElId)) {
return
}
let containerEl: null | HTMLElement = (() => {
const el = document.createElement('div')
el.id = containerElId
return el
})()
document.body.append(containerEl)
const dispose = () => {
app?.unmount()
containerEl?.remove()
app = null
containerEl = null
}
let app: null | ReturnType<typeof createApp> = createApp(DependenciesSetupProgressIndicatorDialog, {
modelValue: true,
onClosed() {
dispose()
},
dispose,
dependenciesStatus: o?.dependenciesStatus,
processWaitee: o?.processWaitee
}).use(ElementPlus)
app.mount(containerEl)
return {
dispose
}
}
@@ -1,106 +1,116 @@
<template> <template>
<div <div
class="container" class="flying-company-logo-list-container"
:style="{ :style="{
gridTemplateColumns: `repeat(${colCount}, 1fr)`, gridTemplateColumns: `repeat(${colCount}, 1fr)`,
gridTemplateRows: `repeat(${rowCount}, 1fr)` gridTemplateRows: `repeat(${rowCount}, 1fr)`
}" }"
> ref="imageElContainer"
<img />
v-for="n in rowCount * colCount"
:key="n"
class="dot"
:src="currentIndexToSrcMap[n - 1]"
@animationiteration="
(ev) => {
handleAnimationiteration(ev, n - 1)
}
"
/>
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { onMounted, ref } from 'vue'
import { logoQueueInjectKey } from './types'
const logoQueue = ref<string[]>([])
const rowCount = 4 const rowCount = 4
const colCount = 6 const colCount = 6
const currentIndexToSrcMap: string[] = ref([]) const logoQueue: HTMLImageElement[] = []
const imageElContainer = ref<HTMLElement>()
onMounted(async () => {
const res = (
await Promise.all(
[...Object.values(import.meta.glob('./resources/*.png', { as: 'url' }))].map((it) => it())
)
).map((url) => {
const img = new Image()
img.src = url
img.onanimationiteration = () => {
const newImg = logoQueue.shift()!
newImg.classList.add('dot')
Object.assign(newImg.style, {
position: 'relative',
left: (Math.random() > 0.5 ? -1 : 1) * (100 * Math.random()) + 'px',
bottom: (Math.random() > 0.5 ? -1 : 1) * (100 * Math.random()) + 'px',
transform: `translateZ(${-5000}px)`
})
img.replaceWith(newImg)
img.classList.remove('dot')
Object.assign(img.style, {
position: '',
left: '',
bottom: '',
transform: ``
})
logoQueue.push(img as HTMLImageElement)
}
Promise.all( return img
[...Object.values(import.meta.glob('./resources/*.png', { as: 'url' }))].map((it) => it()) })
).then((res) => { logoQueue.push(...res)
logoQueue.value = logoQueue.value.concat(res)
currentIndexToSrcMap.value = logoQueue.value.splice(0, rowCount * colCount) for (let i = 0; i < rowCount * colCount; i++) {
const img = logoQueue.shift()!
Object.assign(img.style, {
position: 'relative',
left: -40 * Math.random() + 'px',
bottom: -40 * Math.random() + 'px',
transform: `translateZ(${-5000}px)`
})
img.classList.add('dot')
imageElContainer.value?.append(img)
}
}) })
const handleAnimationiteration = (ev, indexInSrcMap) => {
logoQueue.value.push(currentIndexToSrcMap.value[indexInSrcMap])
currentIndexToSrcMap.value[indexInSrcMap] = logoQueue.value.shift()
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@keyframes fly-in { .flying-company-logo-list-container {
0% { perspective: 600px;
transform: translateZ(-2500px);
opacity: 0;
}
25% {
opacity: 1;
}
75% {
opacity: 0.5;
}
100% {
transform: translateZ(0);
opacity: 0;
}
}
.container {
height: 600px;
perspective: 1200px;
display: grid; display: grid;
align-items: center; align-items: center;
justify-items: center; justify-items: center;
justify-content: center; justify-content: center;
gap: 30px; gap: 30px;
} }
</style>
.dot { <style lang="scss">
display: block; @keyframes fly-in {
--dot-run-duration: 1s; 0% {
animation: fly-in var(--dot-run-duration) linear infinite; opacity: 0;
transform-origin: center; }
mix-blend-mode: darken; 70% {
width: 200px; opacity: 0.8;
}
100% {
transform: translateZ(0);
opacity: 0;
}
} }
.dot:nth-child(2n) { .flying-company-logo-list-container {
animation-delay: calc(-1 * var(--dot-run-duration)); .dot {
} display: block;
.dot:nth-child(2n + 1) { --dot-run-duration: 2.5s;
animation-delay: calc(-0.1 * var(--dot-run-duration)); animation: fly-in var(--dot-run-duration) ease-in-out infinite;
} transform-origin: center;
.dot:nth-child(3n + 1) { mix-blend-mode: darken;
animation-delay: calc(-0.2 * var(--dot-run-duration)); width: 200px;
} }
.dot:nth-child(5n + 1) { .dot:nth-child(2n) {
animation-delay: calc(-0.1 * var(--dot-run-duration)); animation-delay: calc(-0.35 * var(--dot-run-duration));
} }
.dot:nth-child(7n + 1) { .dot:nth-child(2n + 1) {
animation-delay: calc(-0.4 * var(--dot-run-duration)); animation-delay: calc(-0.15 * var(--dot-run-duration));
} }
.dot:nth-child(11n + 1) { .dot:nth-child(3n + 1) {
animation-delay: calc(-0.5 * var(--dot-run-duration)); animation-delay: calc(-0.22 * var(--dot-run-duration));
} }
.dot:nth-child(13n + 1) { .dot:nth-child(5n + 1) {
animation-delay: calc(0 * var(--dot-run-duration)); animation-delay: calc(-0.05 * var(--dot-run-duration));
} }
.dot:nth-child(17n + 1) { .dot:nth-child(7n + 1) {
animation-delay: calc(-0.1 * var(--dot-run-duration)); animation-delay: calc(-0.1 * var(--dot-run-duration));
}
} }
</style> </style>
@@ -1,38 +0,0 @@
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import WaitForLogin from './index.vue'
export const mountGlobalDialog = (o: { processWaitee? }) => {
const containerElId = `elForWaitForLogin`
if (document.getElementById(containerElId)) {
return
}
let containerEl: null | HTMLElement = (() => {
const el = document.createElement('div')
el.id = containerElId
return el
})()
document.body.append(containerEl)
const dispose = () => {
app?.unmount()
containerEl?.remove()
app = null
containerEl = null
}
let app: null | ReturnType<typeof createApp> = createApp(WaitForLogin, {
modelValue: true,
onClosed() {
dispose()
},
dispose,
processWaitee: o?.processWaitee
}).use(ElementPlus)
app.mount(containerEl)
return {
dispose
}
}
@@ -0,0 +1,41 @@
<template>
<div>
<div>愿你心想事成</div>
<RouterView
:dependencies-status="checkDependenciesResult"
:process-waitee="downloadProcessWaitee"
></RouterView>
</div>
</template>
<script lang="ts" setup>
import { useRouter } from 'vue-router'
import { onMounted, ref } from 'vue'
import { sleep } from '@geekgeekrun/utils/sleep.mjs'
const router = useRouter()
const checkDependenciesResult = ref({})
const downloadProcessWaitee = ref(null)
onMounted(async () => {
checkDependenciesResult.value = await electron.ipcRenderer.invoke('check-dependencies')
downloadProcessWaitee.value = Promise.withResolvers()
if (Object.values(checkDependenciesResult.value).includes(false)) {
router.replace('/downloadingDependencies')
} else {
downloadProcessWaitee.value!.resolve()
}
downloadProcessWaitee.value!.promise.then(async () => {
const isCookieFileValid = await electron.ipcRenderer.invoke('check-boss-zhipin-cookie-file')
if (!isCookieFileValid) {
router.replace('/cookieAssistant')
} else {
await sleep(1000)
router.replace('/configuration')
}
})
})
</script>
@@ -1,20 +1,12 @@
<template> <template>
<el-dialog <div v-if="!dependenciesStatus.puppeteerExecutableAvailable">
v-bind="$attrs" <div mb14px>正在下载核心组件</div>
:close-on-click-modal="false" <el-progress
:close-on-press-escape="false" :percentage="browserDownloadPercentage"
:show-close="false" :format="(n) => `${n.toFixed(1)}%`"
@open="handleDialogOpen" :stroke-width="10"
> />
<template v-if="!copiedDependenciesStatus.puppeteerExecutableAvailable"> </div>
<div mb14px>正在下载核心组件</div>
<el-progress
:percentage="browserDownloadPercentage"
:format="(n) => `${n.toFixed(1)}%`"
:stroke-width="10"
/>
</template>
</el-dialog>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@@ -22,7 +14,6 @@ import { ref, onUnmounted, PropType } from 'vue'
import { ElMessageBox } from 'element-plus' import { ElMessageBox } from 'element-plus'
const props = defineProps({ const props = defineProps({
dispose: Function,
dependenciesStatus: { dependenciesStatus: {
type: Object as PropType<Record<string, boolean>>, type: Object as PropType<Record<string, boolean>>,
default: () => ({}) default: () => ({})
@@ -30,15 +21,6 @@ const props = defineProps({
processWaitee: Object processWaitee: Object
}) })
// shallow copy
const copiedDependenciesStatus = {
...props.dependenciesStatus
}
const handleDialogOpen = () => {
browserDownloadPercentage.value = 0
}
const browserDownloadPercentage = ref(0) const browserDownloadPercentage = ref(0)
const handleBrowserDownloadProgress = (ev, { downloadedBytes, totalBytes }) => { const handleBrowserDownloadProgress = (ev, { downloadedBytes, totalBytes }) => {
browserDownloadPercentage.value = (downloadedBytes / totalBytes) * 100 browserDownloadPercentage.value = (downloadedBytes / totalBytes) * 100
@@ -63,11 +45,11 @@ const processDownloadBrowser = async () => {
const promiseList: Array<Promise<void>> = [] const promiseList: Array<Promise<void>> = []
const processTasks = async () => { const processTasks = async () => {
if (!copiedDependenciesStatus.puppeteerExecutableAvailable) { if (!props.dependenciesStatus.puppeteerExecutableAvailable) {
const p = processDownloadBrowser() const p = processDownloadBrowser()
promiseList.push(p) promiseList.push(p)
p.then(() => { p.then(() => {
copiedDependenciesStatus.puppeteerExecutableAvailable = true props.dependenciesStatus.puppeteerExecutableAvailable = true
}) })
} }
@@ -77,7 +59,6 @@ const processTasks = async () => {
p.then(() => { p.then(() => {
if (!promiseList.length) { if (!promiseList.length) {
props.processWaitee?.resolve?.() props.processWaitee?.resolve?.()
props.dispose?.()
} }
}) })
await p await p
@@ -95,7 +76,6 @@ const processTasks = async () => {
.catch(() => { .catch(() => {
// FIXME: should exit app here // FIXME: should exit app here
promiseList.length = 0 promiseList.length = 0
props.dispose?.()
}) })
} }
} }
@@ -28,9 +28,8 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref } from 'vue'
import { ElForm, ElMessage } from 'element-plus' import { ElForm, ElMessage } from 'element-plus'
import router from '../../router/index' import { useRouter } from 'vue-router'
import { mountGlobalDialog as mountDependenciesSetupProgressIndicatorDialog } from '@renderer/features/DependenciesSetupProgressIndicatorDialog/operations' const router = useRouter()
import { mountGlobalDialog as mountWaitForLoginDialog } from '@renderer/features/WaitForLoginDialog/operations'
const formContent = ref({ const formContent = ref({
dingtalkRobotAccessToken: '', dingtalkRobotAccessToken: '',
@@ -51,28 +50,7 @@ const handleSubmit = async () => {
await formRef.value!.validate() await formRef.value!.validate()
await electron.ipcRenderer.invoke('save-config-file-from-ui', JSON.stringify(formContent.value)) await electron.ipcRenderer.invoke('save-config-file-from-ui', JSON.stringify(formContent.value))
try { router.replace('/geekAutoStartChatWithBoss/prepareRun')
const res = await electron.ipcRenderer.invoke(
'run-geek-auto-start-chat-with-boss',
JSON.stringify(formContent.value)
)
if (res.type === 'GEEK_AUTO_START_CHAT_WITH_BOSS_STARTED') {
router.replace('/geekAutoStartChatWithBoss/runningStatus')
}
} catch (err) {
if (err instanceof Error && err.message.includes('NEED_TO_CHECK_RUNTIME_DEPENDENCIES')) {
ElMessage.error({
message: `核心组件损坏,正在尝试修复`
})
const checkDependenciesResult = await electron.ipcRenderer.invoke('check-dependencies')
if (Object.values(checkDependenciesResult).includes(false)) {
mountDependenciesSetupProgressIndicatorDialog(checkDependenciesResult)
// TODO: should continue interrupted task
}
}
console.error(err)
}
} }
const handleSave = async () => { const handleSave = async () => {
await formRef.value!.validate() await formRef.value!.validate()
@@ -89,7 +67,7 @@ const handleExpectCompaniesInputBlur = (event) => {
} }
const handleClickLaunchLogin = () => { const handleClickLaunchLogin = () => {
mountWaitForLoginDialog() router.replace('/cookieAssistant')
} }
</script> </script>
@@ -2,9 +2,8 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, onUnmounted } from 'vue' import { onMounted, onUnmounted } from 'vue'
import { mountGlobalDialog as mountDependenciesSetupProgressIndicatorDialog } from '@renderer/features/DependenciesSetupProgressIndicatorDialog/operations' import { useRouter } from 'vue-router'
import { mountGlobalDialog as mountWaitForLoginDialog } from '@renderer/features/WaitForLoginDialog/operations' const router = useRouter()
const unmountedCbs: Array<InstanceType<typeof Function>> = [] const unmountedCbs: Array<InstanceType<typeof Function>> = []
onUnmounted(() => { onUnmounted(() => {
while (unmountedCbs.length) { while (unmountedCbs.length) {
@@ -14,31 +13,27 @@ onUnmounted(() => {
} catch {} } catch {}
} }
}) })
const goToCheckBossZhipinCookieFile = () => router.replace('/cookieAssistant')
onMounted(() => { onMounted(() => {
electron.ipcRenderer.on('check-boss-zhipin-cookie-file', mountWaitForLoginDialog) electron.ipcRenderer.on('check-boss-zhipin-cookie-file', goToCheckBossZhipinCookieFile)
}) })
onUnmounted(() => { onUnmounted(() => {
electron.ipcRenderer.removeListener('check-boss-zhipin-cookie-file', mountWaitForLoginDialog) electron.ipcRenderer.removeListener(
'check-boss-zhipin-cookie-file',
goToCheckBossZhipinCookieFile
)
}) })
;(async () => { ;(async () => {
const checkDependenciesResult = await electron.ipcRenderer.invoke('check-dependencies') const checkDependenciesResult = await electron.ipcRenderer.invoke('check-dependencies')
if (Object.values(checkDependenciesResult).includes(false)) { if (Object.values(checkDependenciesResult).includes(false)) {
const processWaitee = Promise.withResolvers() router.replace('/')
mountDependenciesSetupProgressIndicatorDialog({ return
checkDependenciesResult, processWaitee
})
await processWaitee.promise
} }
const isCookieFileValid = await electron.ipcRenderer.invoke('check-boss-zhipin-cookie-file') const isCookieFileValid = await electron.ipcRenderer.invoke('check-boss-zhipin-cookie-file')
if (!isCookieFileValid) { if (!isCookieFileValid) {
const processWaitee = Promise.withResolvers() router.replace('/cookieAssistant')
mountWaitForLoginDialog({ return
processWaitee
})
await processWaitee.promise
} }
})() })()
</script> </script>
@@ -1,14 +1,6 @@
<template> <template>
<el-dialog <div class="cookie-assistant-page">
v-bind="$attrs" <div ml1em mt1em mb1em >Cookie 助手</div>
:close-on-click-modal="false"
:close-on-press-escape="!cookieInvalid"
title="Boss直聘 Cookie助手"
:width="720"
top="20px"
lock-scroll
:show-close="!cookieInvalid"
>
<el-alert <el-alert
v-if="cookieInvalid" v-if="cookieInvalid"
type="warning" type="warning"
@@ -18,21 +10,17 @@
由于您是首次使用本程序或者您之前使用的Boss直聘账号登录状态失效因此您需要重新获取登录凭证 由于您是首次使用本程序或者您之前使用的Boss直聘账号登录状态失效因此您需要重新获取登录凭证
</el-alert> </el-alert>
<div ml1em mt1em line-height-normal> <div ml1em mt1em line-height-normal>
如果您了解如何获取Cookie了解有效的Cookie格式可以直接在下方输入框中进行编辑<br /> 如果您了解如何获取Cookie了解有效的Cookie格式可以直接在下方输入框中进行编辑由于手动编辑较为麻烦建议您打开已登录过Boss直聘的浏览器使用<a
手动编辑较为麻烦建议您打开已登录过Boss直聘的浏览器使用
<a
color-blue color-blue
decoration-none decoration-none
href="javascript:void(0)" href="javascript:void(0)"
@click.prevent="handleEditThisCookieExtensionStoreLinkClick" @click.prevent="handleEditThisCookieExtensionStoreLinkClick"
>EditThisCookie 扩展程序</a >EditThisCookie 扩展程序</a
> >复制Cookie然后粘贴在下方输入框中文本格式为被序列化为JSON的数组不含两侧引号
复制Cookie然后粘贴在下方输入框中<br />
格式为被序列化为JSON的数组不含两侧引号
</div> </div>
<br /> <br />
<div ml1em line-height-normal> <div ml1em line-height-normal>
如果您不了解Cookie相关概念或者不能访问Chrome扩展程序商店下载EditThisCookie来获取Cookie请按照以下步骤进行操作 如果您不了解Cookie相关概念或者期望操作简单一些请按照以下步骤进行操作
</div> </div>
<ol lh-2em mt-0> <ol lh-2em mt-0>
<li> <li>
@@ -104,23 +92,20 @@
> >
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <footer flex mt20px pb20px flex-justify-end>
<el-button v-if="!cookieInvalid" @click="dispose">关闭</el-button> <el-button v-if="!cookieInvalid" @click="handleCancel">取消</el-button>
<el-button type="primary" @click="handleSubmit">保存Cookie</el-button> <el-button type="primary" @click="handleSubmit">确定</el-button>
</template> </footer>
</el-dialog> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ElForm, ElMessage } from 'element-plus' import { ElForm, ElMessage } from 'element-plus'
import { ref, onUnmounted, onMounted } from 'vue' import { ref, onUnmounted, onMounted } from 'vue'
import { checkCookieListFormat } from '../../../../common/utils/cookie' import { checkCookieListFormat } from '../../../../common/utils/cookie'
import { useRouter } from 'vue-router';
const props = defineProps({ const router = useRouter()
dispose: Function,
processWaitee: Object
})
const cookieInvalid = ref(false) const cookieInvalid = ref(false)
enum LOGIN_COOKIE_WAITING_STATUS { enum LOGIN_COOKIE_WAITING_STATUS {
@@ -195,6 +180,9 @@ const handleEditThisCookieExtensionStoreLinkClick = () => {
) )
} }
const handleCancel = () => {
router.replace('/configuration')
}
const handleSubmit = async () => { const handleSubmit = async () => {
await formRef.value!.validate() await formRef.value!.validate()
await electron.ipcRenderer.invoke('write-storage-file', { await electron.ipcRenderer.invoke('write-storage-file', {
@@ -202,8 +190,7 @@ const handleSubmit = async () => {
data: formContent.value.collectedCookies data: formContent.value.collectedCookies
}) })
ElMessage.success('Boss直聘 Cookie 保存成功') ElMessage.success('Boss直聘 Cookie 保存成功')
props.processWaitee?.resolve?.() router.replace('/configuration')
props.dispose()
} }
const handleBossZhipinLoginPageClosed = () => { const handleBossZhipinLoginPageClosed = () => {
@@ -235,6 +222,13 @@ onUnmounted(() => {
}) })
</script> </script>
<style lang="scss" scoped>
.cookie-assistant-page {
max-width: 640px;
margin: 0 auto;
}
</style>
<style lang="scss"> <style lang="scss">
.cookie-form.el-form { .cookie-form.el-form {
.el-form-item__error--inline { .el-form-item__error--inline {
@@ -0,0 +1,29 @@
<template>
<div class="geek-auto-start-chat-with-boss__prepare-run">
<div>{{ statusText }}</div>
</div>
</template>
<script lang="ts" setup>
import { hasOwn } from '@vue/shared'
import { PropType, computed } from 'vue'
const statusTextMap = {
_: '请稍后,正在进行一些处理',
'locating-puppeteer-executable': '正在寻找可用的浏览器'
} as const
const props = defineProps({
status: {
type: String as PropType<keyof typeof statusText>,
default: '_'
}
})
const statusText = computed(() => {
if (hasOwn(statusTextMap, props.status)) {
return statusTextMap[props.status]
} else {
return statusTextMap._
}
})
</script>
@@ -1,5 +1,6 @@
<template> <template>
<div class="geek-auto-start-chat-with-boss__running-status"> <div class="geek-auto-start-chat-with-boss__running-status">
<FlyingCompanyLogoList class="flying-company-logo-list" />
<div class="tip"> <div class="tip">
<article> <article>
<h1>👋 BOSS炸弹正在运行</h1> <h1>👋 BOSS炸弹正在运行</h1>
@@ -9,14 +10,14 @@
</article> </article>
<el-button :disabled="isStopping" @click="handleStopButtonClick">停止开聊</el-button> <el-button :disabled="isStopping" @click="handleStopButtonClick">停止开聊</el-button>
</div> </div>
<FlyingCompanyLogoList class="flying-company-logo-list" />
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onUnmounted } from 'vue'; import { ref, onUnmounted, onMounted } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import FlyingCompanyLogoList from '../../features/FlyingCompanyLogoList/index.vue' import FlyingCompanyLogoList from '../../features/FlyingCompanyLogoList/index.vue'
import { ElMessage } from 'element-plus';
const { ipcRenderer } = electron const { ipcRenderer } = electron
const router = useRouter() const router = useRouter()
@@ -40,16 +41,46 @@ onUnmounted(() => {
ipcRenderer.removeListener('geek-auto-start-chat-with-boss-stopped', handleStopped) ipcRenderer.removeListener('geek-auto-start-chat-with-boss-stopped', handleStopped)
ipcRenderer.removeListener('geek-auto-start-chat-with-boss-stopping', handleStopping) ipcRenderer.removeListener('geek-auto-start-chat-with-boss-stopping', handleStopping)
}) })
onMounted(async () => {
try {
await electron.ipcRenderer.invoke('run-geek-auto-start-chat-with-boss')
} catch (err) {
if (err instanceof Error && err.message.includes('NEED_TO_CHECK_RUNTIME_DEPENDENCIES')) {
ElMessage.error({
message: `核心组件损坏,正在尝试修复`
})
const checkDependenciesResult = await electron.ipcRenderer.invoke('check-dependencies')
if (Object.values(checkDependenciesResult).includes(false)) {
router.replace('/')
// TODO: should continue interrupted task
}
}
console.error(err)
}
})
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.geek-auto-start-chat-with-boss__running-status { .geek-auto-start-chat-with-boss__running-status {
max-width: 100%; width: 100%;
max-height: 100%; height: 100%;
overflow: hidden; overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.tip { .tip {
margin: 0 auto; margin: 0 auto;
margin-top: -15vh;
max-width: 640px; max-width: 640px;
} }
.flying-company-logo-list {
position: absolute;
inset: 0;
z-index: -1;
opacity: 0.25;
}
} }
</style> </style>
@@ -1 +1,37 @@
<template><RouterView /></template> <template><RouterView :status="currentStatus" /></template>
<script lang="ts" setup>
import { ElMessage } from 'element-plus'
import { ref, onMounted, onUnmounted } from 'vue'
import { useRouter } from 'vue-router'
const router = useRouter()
const currentStatus = ref('')
onMounted(() => {
const promise = electron.ipcRenderer.invoke('prepare-run-geek-auto-start-chat-with-boss')
const handleLocatingPuppeteerExecutable = () => {
currentStatus.value = 'locating-puppeteer-executable'
}
electron.ipcRenderer.once('locating-puppeteer-executable', handleLocatingPuppeteerExecutable)
onUnmounted(() => {
electron.ipcRenderer.removeListener(
'locating-puppeteer-executable',
handleLocatingPuppeteerExecutable
)
})
promise
.then(() => {
router.replace('/geekAutoStartChatWithBoss/runningStatus')
})
.catch(async (err) => {
if (err instanceof Error && err.message.includes('NEED_TO_CHECK_RUNTIME_DEPENDENCIES')) {
ElMessage.error({
message: `核心组件损坏,正在尝试修复`
})
router.replace('/')
}
console.error(err)
})
})
</script>
+30 -6
View File
@@ -1,12 +1,13 @@
import { defineComponent, h } from 'vue'
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router' import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
import BootstrapSplash from '@renderer/page/BootstrapSplash/index.vue'
const routes: Array<RouteRecordRaw> = [ const routes: Array<RouteRecordRaw> = [
{ {
path: '/', path: '/cookieAssistant',
component: defineComponent({ setup: () => h('div') }), component: () => import('@renderer/page/CookieAssistant/index.vue'),
redirect: '/configuration', meta: {
children: [] title: 'Cookie 助手'
}
}, },
{ {
path: '/configuration', path: '/configuration',
@@ -26,6 +27,13 @@ const routes: Array<RouteRecordRaw> = [
path: '/geekAutoStartChatWithBoss', path: '/geekAutoStartChatWithBoss',
component: () => import('@renderer/page/GeekAutoStartChatWithBoss/index.vue'), component: () => import('@renderer/page/GeekAutoStartChatWithBoss/index.vue'),
children: [ children: [
{
path: 'prepareRun',
component: () => import('@renderer/page/GeekAutoStartChatWithBoss/PrepareRun.vue'),
meta: {
title: 'BOSS炸弹 正在预热'
}
},
{ {
path: 'runningStatus', path: 'runningStatus',
component: () => import('@renderer/page/GeekAutoStartChatWithBoss/RunningStatus.vue'), component: () => import('@renderer/page/GeekAutoStartChatWithBoss/RunningStatus.vue'),
@@ -34,7 +42,23 @@ const routes: Array<RouteRecordRaw> = [
} }
} }
] ]
} },
{
path: '/',
component: BootstrapSplash,
meta: {
title: '薪想事成'
},
children: [
{
path: '/downloadingDependencies',
component: () => import('@renderer/page/BootstrapSplash/page/DownloadingDependencies.vue'),
meta: {
title: '正在下载浏览器'
},
}
]
},
] ]
const router = createRouter({ const router = createRouter({