WGShare.Client.Electron/main.js

789 lines
28 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const {
app,
BrowserWindow,
screen,
Tray,
nativeImage,
Menu,
ipcMain,
clipboard,
dialog,
crashReporter,
desktopCapturer,
} = require('electron');
const path = require('node:path')
const updateJs = require('./src/utils/package/update')
const fs = require('fs');
const Registry = require('winreg');
const { autoUpdater, CancellationToken } = require('electron-updater');
const signalR = require('@microsoft/signalr');
const { setTimeout, setInterval, clearTimeout, clearInterval } = require('timers');
const cancellationToken = new CancellationToken()
app.allowRendererProcessReuse = false;
let mainWindow = null;
let childWindow = {}
let isMaximized = false;
let env;
let regKey;
let connection = null;
let envStr;
let startNumber = 0;
class AppWindow extends BrowserWindow {
constructor(config) {
const basicConfig = {
webPreferences: {
contextIsolation: false,
nodeIntegration: true,
enableRemoteModule: true,
nodeIntegrationInWorker: true,
allowMediaDevices: true,
preload: path.join(__dirname, 'preload.js')
},
show: false,
frame: false,
backgroundColor: '#00000000',
transparent: true,
};
const finalConfig = { ...basicConfig, ...config };
super(finalConfig);
if (env === 'development') {
// 开发
this.loadURL('http://localhost:3000');
} else {
// 测试 | 生产
this.loadFile(path.resolve(__dirname, './dist/index.html'));
}
this.once('ready-to-show', () => {
this.show();
});
}
}
function quit() {
app.quit()
}
function createTray() {
const iconPath = `${__dirname}/src/assets/${updateJs.getIcon(envStr)}.png`;
const trayIcon = nativeImage.createFromPath(iconPath);
const tray = new Tray(trayIcon);
const contextMenu = Menu.buildFromTemplate([
{
label: '打开', click: () => {
mainWindow.webContents.send('isOpenWindows');
},
// icon: iconPath,
},
{
label: '最小化到系统托盘', click: () => {
mainWindow.hide();
},
// icon: iconPath,
},
{
label: '退出', click: async () => {
quit()
},
// icon: iconPath,
},
]);
tray.setToolTip(updateJs.getTitle(envStr));
tray.setContextMenu(contextMenu);
tray.on('click', () => {
mainWindow.webContents.send('isOpenWindows');
});
}
function createWindow() {
mainWindow = new AppWindow();
mainWindow.focus();
}
const additionalData = { myKey: 'myValue' }
app.on('ready', () => {
const gotTheLock = app.requestSingleInstanceLock(additionalData)
if (gotTheLock) {
app.getPath('crashDumps')
crashReporter.start({
uploadToServer: false,
ignoreSystemCrashHandler: false
})
env = process.argv.find((arg) => arg.startsWith('--env='))?.split('=')[1];
if (env === 'development') {
Object.defineProperty(app, 'isPackaged', {
get() {
return true
}
})
autoUpdater.updateConfigPath = path.join('latest.yml')
}
createWindow()
regKey = new Registry({
hive: Registry.HKCU,
key: '\\Software\\ZhiHuiXiang'
});
// 获取当前脚本所在目录的绝对路径
const currentDirectory = __dirname;
// 获取安装父目录
const parentDirectory = path.resolve(currentDirectory, '..');
const customFolderPath = path.join(parentDirectory, 'Downloads');
if (!fs.existsSync(customFolderPath)) {
// 如果不存在,则创建文件夹
fs.mkdirSync(customFolderPath);
}
// 监听f12打开控制台
mainWindow.webContents.on('before-input-event', (event, input) => {
if (input.key === 'F12') {
mainWindow.webContents.openDevTools()
}
});
// 监听移动
mainWindow.on('move', () => {
// 如果是全屏自动恢复到上次窗口大小
if (isMaximized) {
mainWindow.unmaximize()
isMaximized = false;
}
if (mainWindow.isMaximized()) {
isMaximized = true;
}
});
ipcMain.handle('setEnv', (event, str) => {
envStr = str;
if (startNumber === 0) {
updateHandle() // 检查更新
setInterval(() => {
updateHandle() // 每一小时检查更新
}, 1000 * 60 * 60)
createTray()
startNumber++
}
});
// socket
ipcMain.handle('startSignalr', (event, user) => {
startSignalr(user)
});
ipcMain.handle('offSignalr', (event) => {
if (connection) {
connection.off('ReceiveMessage');
connection.off('Operation');
connection.off('ForceExitRoom');
connection.off('AllLeave');
connection.off('ShowUser');
connection.off('RefreshView');
connection.off('UserJoined');
connection.off('UserLeave');
connection.off('OperAllMicr');
connection.off('OperMicr');
connection.off('OperCamera');
connection.off('ManagerRefresh');
connection.off('ApplyToSpeak');
connection.off('Watch');
connection.off('DriverList');
connection.off('SetDriver');
connection.off('ShowDriverList');
connection.off('ModifyNickName');
}
});
ipcMain.handle('onStop', (event) => {
if (connection) {
mainWindow.webContents.send('changeLocalStorage', {
isSignalr: false,
});
connection.off('Invitation');
connection.off('ForceLogout');
connection.stop()
connection = ""
}
});
ipcMain.handle('onInvoke', async (event, str, data) => {
switch (str) {
case 'sendChannelMsg':
await connection.invoke(str, data.roomNum, data.msg)
break;
case 'sendOper':
// 4:屏幕共享
await connection.invoke(str, data.roomNum, data.type)
break;
case 'getDrivers':
// 获取某个人的设备列表
await connection.invoke(str, data.uid)
break;
case 'sendDrivers':
// 发送设备列表给某个人
await connection.invoke(str, data.uid, data.driversJsonString)
break;
case 'setDrivers':
// 设置某个人的设备列表
await connection.invoke(str, data.uid, data.driversJsonString)
break;
case 'joinChannel':
// 设置某个人的设备列表
await connection.invoke(str, data.roomNum, data.enableMicr, data.enableCamera, data.isRoomManager || false)
break;
case 'levelChannel':
// 设置某个人的设备列表
await connection.invoke(str, data.roomNum)
break;
}
});
ipcMain.handle('onOtherSignalr', (event) => {
if (connection) {
// 邀请
connection.on("Invitation", (roomNum, roomName, InviterName) => {
mainWindow.webContents.send('onOtherSignalr', {
key: 'Invitation',
roomNum, roomName, InviterName
});
});
// 退出
connection.on("ForceLogout", (msg) => {
mainWindow.webContents.send('onOtherSignalr', {
key: 'ForceLogout',
msg
});
});
}
});
ipcMain.handle('onSignalr', (event) => {
if (connection) {
// 聊天
connection.on("ReceiveMessage", (uid, userName, message, timestamp) => {
mainWindow.webContents.send('onSignalr', {
key: 'ReceiveMessage',
uid, message, userName, timestamp
})
});
// 扩展操作
connection.on("Operation", (type) => {
mainWindow.webContents.send('onSignalr', {
key: 'Operation',
type
})
});
// 移出会议
connection.on("ForceExitRoom", () => {
mainWindow.webContents.send('onSignalr', {
key: 'ForceExitRoom',
})
});
// 全员离开房间
connection.on("AllLeave", () => {
mainWindow.webContents.send('onSignalr', {
key: 'AllLeave',
})
});
// 全员看他
connection.on("ShowUser", (uid, uname, operUid, operUserName) => {
mainWindow.webContents.send('onSignalr', {
key: 'ShowUser',
uid,
uname,
operUid,
operUserName,
})
});
// 更新视图模式
connection.on("RefreshView", (type) => {
mainWindow.webContents.send('onSignalr', {
key: 'RefreshView',
type
})
});
// 用户加入频道回调
connection.on("UserJoined", (user) => {
mainWindow.webContents.send('onSignalr', {
key: 'UserJoined',
user,
})
});
// 用户退出频道回调
connection.on("UserLeave", (uid) => {
mainWindow.webContents.send('onSignalr', {
key: 'UserLeave',
uid,
})
});
// 所有用户开闭麦
connection.on("OperAllMicr", (enableMicr, uid) => {
mainWindow.webContents.send('onSignalr', {
key: 'OperAllMicr',
enableMicr,
uid
})
});
// 用户关闭开启麦克风
connection.on("OperMicr", (user, operUid) => {
mainWindow.webContents.send('onSignalr', {
key: 'OperMicr',
user,
operUid
})
});
// 用户开启关闭摄像头
connection.on("OperCamera", (user, operUid) => {
mainWindow.webContents.send('onSignalr', {
key: 'OperCamera',
user,
operUid
})
});
// 发言人用户信息刷新
connection.on("ManagerRefresh", (user, uid) => {
mainWindow.webContents.send('onSignalr', {
key: 'ManagerRefresh',
user,
uid
})
});
// 申请发言
connection.on("ApplyToSpeak", (uid, uname) => {
mainWindow.webContents.send('onSignalr', {
key: 'ApplyToSpeak',
uid,
uname
})
});
// 管理员查看随机用户
connection.on("Watch", (watchUids) => {
mainWindow.webContents.send('onSignalr', {
key: 'Watch',
watchUids
})
});
// 设备列表
connection.on("DriverList", (callerUid) => {
mainWindow.webContents.send('onSignalr', {
key: 'DriverList',
callerUid
})
});
// 设置设备
connection.on("SaveDriver", (driver) => {
mainWindow.webContents.send('onSignalr', {
key: 'SaveDriver',
driver
})
});
// 显示设备列表
connection.on("ShowDriverList", (driversJsonString) => {
mainWindow.webContents.send('onSignalr', {
key: 'ShowDriverList',
driversJsonString
})
});
// 修改用户名称
connection.on("ModifyNickName", (uid, nickName) => {
mainWindow.webContents.send('onSignalr', {
key: 'ModifyNickName',
uid,
nickName
})
});
}
});
// 放大缩小退出窗口
ipcMain.handle('setViewStatus', async (event, status) => {
switch (status) {
case 'quit':
await mainWindow.webContents.send('onQuit');
break;
case 'maximize':
mainWindow.maximize()
break;
case 'unmaximize':
mainWindow.unmaximize()
break;
case 'minimize':
mainWindow.minimize()
break;
case 'hide':
mainWindow.hide()
break;
case 'show':
mainWindow.show()
mainWindow.focus();
mainWindow.setSkipTaskbar(false)
mainWindow.setResizable(true)
mainWindow.setAlwaysOnTop(false)
break;
}
});
// 导出是否全屏
ipcMain.handle('getIsMaximized', () => {
return mainWindow.isMaximized();
});
// 获取app路径
ipcMain.handle('getAppPath', () => {
return app.getAppPath();
});
// 获取版本号
ipcMain.handle('getVersion', () => {
return app.getVersion();
});
// 获取窗口是否显示
ipcMain.handle('isVisible', () => {
return mainWindow.isVisible();
});
// 获取共享屏幕列表
ipcMain.handle('getSources', async () => {
return await desktopCapturer.getSources({
types: ['screen']
});
});
// 复制文字
ipcMain.handle('setWriteText', (event, text) => {
clipboard.writeText(text)
});
// 退出
ipcMain.handle('quit', async (event, bool) => {
if (bool) {
quit()
} else {
await mainWindow.webContents.send('quitAndInstall');
}
});
// 加入房间通知
ipcMain.handle('joinNotification', (event, user) => {
mainWindow.show()
mainWindow.focus();
});
// 通知下载包
ipcMain.handle('updateDownload', (event, data) => {
if (data === '0') { // 取消下载
cancleDownloadUpdate()
} else if (data === '1') { // 开始下载
downloadUpdate()
} else if (data === '2') { // 下载完成 点击安装
quitAndInstall()
}
});
// 选择文件夹
ipcMain.handle('selectFilePath', async (event, data) => {
const result = await dialog.showOpenDialog({
properties: ['openDirectory']
});
if (result.canceled) {
} else {
switch (data.key) {
case 'shareFilesPath':
case 'recordingFilesPath':
mainWindow.webContents.send('onFilePath', result.filePaths[0] + '\\', data.key);
break;
default:
mainWindow.webContents.send('downFile', {
downFilePaths: result.filePaths[0] + '\\',
fileName: data.fileName,
filePath: data.filePath,
});
break;
}
}
});
// 获取桌面大小
ipcMain.handle('getWindowSize', (event, config) => {
const primaryDisplay = screen.getPrimaryDisplay()
const { width, height } = primaryDisplay.workAreaSize
return { width, height }
});
// 设置桌面应用基础属性
ipcMain.handle('setMainWindowSize', (event, config) => {
if (config.width === 250) {
mainWindow.setSkipTaskbar(true)
mainWindow.setResizable(false)
mainWindow.setAlwaysOnTop(true, 'screen-saver')
}
// 设置最小窗口尺寸
mainWindow.setMinimumSize(config.width, config.height);
// 设置最大尺寸
const primaryDisplay = screen.getPrimaryDisplay()
const { width, height } = primaryDisplay.workAreaSize
if (config.key === 'login') {
mainWindow.setMaximumSize(config.width, config.height);
} else {
mainWindow.setMaximumSize(width, height);
}
// 设置窗口尺寸
mainWindow.setSize(config.width, config.height)
// 设置窗口位置使其居中于当前屏幕
mainWindowCenter()
});
// 写入注册表
ipcMain.handle('setRegistry', (event, uuid) => {
regKey.create((err) => {
if (err) {
return;
}
// 设置键和值
regKey.set('uuid', Registry.REG_SZ, uuid, (err) => {
if (err) {
return;
}
});
});
});
// 读取注册表
ipcMain.handle('getRegistry', async () => {
return new Promise((resolve, reject) => {
regKey.get('uuid', (err, item) => {
resolve(item)
})
});
});
// 创建子窗口
ipcMain.handle('createChildWindow', (event, config) => {
const child = new BrowserWindow({
parent: mainWindow,
webPreferences: {
contextIsolation: false,
nodeIntegration: true,
enableRemoteModule: true,
nodeIntegrationInWorker: true,
allowMediaDevices: true,
preload: path.join(__dirname, 'preload.js')
},
show: false,
frame: false,
backgroundColor: '#00000000',
transparent: true,
width: config.width,
height: config.height,
})
if (envStr === 'development') {
// 开发
child.loadURL(config.url)
} else {
// 测试 | 生产
child.loadURL(`file://${path.join(__dirname, './dist/index.html')}#/${config.key}`);
}
childWindow[config.key] = child
child.once('ready-to-show', () => {
if (config.show) {
childWindow[config.key].show()
}
childWindow[config.key].setAlwaysOnTop(true, 'screen-saver')
childWindow[config.key].setSkipTaskbar(true)
windowOperation(config)
})
child.webContents.on('before-input-event', (event, input) => {
if (input.key === 'F12') {
child.webContents.openDevTools()
}
});
});
// 关闭子窗口
ipcMain.handle('closeChildWindow', (event, key) => {
if (key === 'shareScreenWindow') {
for (const k in childWindow) {
if (childWindow[k]) {
childWindow[k].close()
childWindow[k] = ""
}
}
} else {
childWindow[key].close()
childWindow[key] = ""
}
});
// 设置子窗口
ipcMain.handle('setChildWindow', (event, config) => {
switch (config.key) {
case 'shareScreenWindow':
childWindow[config.key].setBounds({ width: config.width })
break;
case 'chatSmallWindow':
childWindow[config.key].setBounds({ height: config.height })
break;
case 'noticeWindow':
childWindow[config.key].setBounds({ width: config.width, height: config.height })
break;
default:
mainWindow.setMinimumSize(250, config.height);
mainWindow.setMaximumSize(250, config.height);
mainWindow.setSize(250, config.height)
break;
}
});
// 隐藏显示子窗口
ipcMain.handle('setChildWindowShow', (event, config) => {
if (config.key === 'noticeWindow') {
if (config.bool) {
childWindow[config.key].show()
} else {
if (childWindow[config.key].isVisible()) {
childWindow[config.key].hide()
}
}
} else {
if (config.bool) {
childWindow[config.key].hide()
} else {
if (childWindow[config.key].isVisible()) {
childWindow[config.key].hide()
} else {
childWindow[config.key].show()
}
}
}
});
// 定位主窗口
ipcMain.handle('setPosition', (event, data) => {
const display = screen.getDisplayMatching({ ...mainWindow.getBounds() });
const { width, height } = display.size
switch (data) {
case 'right':
x = width - mainWindow.getSize()[0];
mainWindow.setPosition(x - 40, 40);
break;
default:
break;
}
});
// 窗口通信
ipcMain.handle('windowHandleMessage', (event, data) => {
if (childWindow[data.key]) {
childWindow[data.key].webContents.send('windowHandleMessageCallBack', data)
}
});
}
});
// 检测更新在你想要检查更新的时候执行renderer事件触发后的操作自行编写
function updateHandle() {
autoUpdater.checkForUpdates()
// autoUpdater.checkForUpdatesAndNotify().catch();
const message = {
error: '检查更新出错',
checking: '正在检查更新……',
updateAva: '检测到新版本,正在下载……',
updateNotAva: '已经是最新版本,不用更新'
}
autoUpdater.setFeedURL(updateJs.getUpdateUrl(envStr))
autoUpdater.autoDownload = false // 不自动下载安装包
autoUpdater.autoInstallOnAppQuit = false // 不自动安装
autoUpdater.on('error', function (error) {
sendUpdateMessage(message.error)
})
autoUpdater.on('checking-for-update', function () {
sendUpdateMessage(message.checking)
})
autoUpdater.on('update-available', function (info) {
let messageStr = JSON.stringify({ type: '0' })
setTimeout(() => {
sendUpdateMessage(messageStr)
}, 5000)
})
autoUpdater.on('update-not-available', function (info) {
})
// 更新下载进度事件
autoUpdater.on('download-progress', function (progressObj) {
let message = JSON.stringify({
type: '1',
value: progressObj.percent
})
sendUpdateMessage(message)
})
autoUpdater.on('update-downloaded', function (event, releaseNotes, releaseName, releaseDate, updateUrl, quitAndUpdate) {
let message = JSON.stringify({
type: '2',
})
sendUpdateMessage(message)
})
}
// 通过main进程发送事件给renderer进程提示更新信息
// type: 0 检测到需要更新(打开窗口) 1 正在下载更新包
function sendUpdateMessage(text) {
mainWindow.webContents.send('update', text)
}
// 下载最新的包
function downloadUpdate() {
autoUpdater.downloadUpdate(cancellationToken)
}
// 取消下载
function cancleDownloadUpdate() {
autoUpdater.downloadUpdate(cancellationToken)
// stop download
cancellationToken.cancel()
}
// 完成下载立即安装
function quitAndInstall() {
autoUpdater.quitAndInstall();
}
function windowOperation(config) {
const child = childWindow[config.key];
const display = screen.getDisplayMatching({ ...child.getBounds() });
const { width, height } = display.size
let x, y;
child.setResizable(false)
switch (config.key) {
case 'shareScreenWindow':
x = Math.round((display.workArea.width - child.getSize()[0]) / 2);
child.setPosition(x, 0);
break;
case 'chatSmallWindow':
y = height - child.getSize()[1];
child.setPosition(40, y - 200);
break;
case 'noticeWindow':
x = width - child.getSize()[0];
y = height - child.getSize()[1];
child.setPosition(x, y - 80);
break;
}
}
// 主窗口居中
function mainWindowCenter() {
const display = screen.getDisplayMatching({ ...mainWindow.getBounds() });
const x = Math.round((display.workArea.width - mainWindow.getSize()[0]) / 2);
const y = Math.round((display.workArea.height - mainWindow.getSize()[1]) / 2);
mainWindow.setPosition(x, y);
}
const startSignalr = async (user) => {
connection = new signalR.HubConnectionBuilder()
.withUrl(`${envStr === 'development' ? 'http://192.168.2.9:5192' : 'https://meeting-api.23544.com/pc'}/session-manage`, {
skipNegotiation: true,
transport: signalR.HttpTransportType.WebSockets,
accessTokenFactory: () => user.token
})
.withAutomaticReconnect([0, 3000, 3000, 3000])
.build();
mainWindow.webContents.send('changeLocalStorage', {
isSignalr: true,
reconnect: true,
});
connection.onreconnected(async () => {
mainWindow.webContents.send('changeLocalStorage', {
reconnect: true,
});
});
connection.onreconnecting(async () => {
onStart()
mainWindow.webContents.send('changeLocalStorage', {
reconnect: false,
});
});
connection.start();
}
const onStart = async () => {
if (connection) {
if (connection.state === signalR.HubConnectionState.Disconnected) {
connection.start().then(() => {
mainWindow.webContents.send('changeLocalStorage', {
reconnect: true,
});
}).catch((err) => {
});
}
if (connection.state !== signalR.HubConnectionState.Connected) {
setTimeout(onStart, 3000);
}
}
}