WGShare.Client.Electron/main.js

898 lines
32 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,
powerSaveBlocker,
net
} = 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 } = require('timers');
const cancellationToken = new CancellationToken()
app.allowRendererProcessReuse = false;
let mainWindow = null;
let childWindow = {}
let isMaximized = false;
let env = 'development'; //development production xy
let regKey;
let connection = null;
let startNumber = 0;
let buildStatus = false; //true 打包开发版本 false 本地开发
powerSaveBlocker.start('prevent-display-sleep')
const id = powerSaveBlocker.start('prevent-display-sleep')
powerSaveBlocker.stop(id)
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') {
if (buildStatus) {
this.loadURL('http://192.168.2.9:8827/');
} else {
this.loadURL('http://localhost:3000');
}
} else {
this.loadURL('https://meeting-api.23544.com/')
}
// this.loadFile(path.resolve(__dirname, './dist/index.html'))
this.once('ready-to-show', () => {
this.show();
});
}
}
function quit() {
app.quit()
}
let tray;
// 检查网络状态
function checkNetworkStatus() {
if (!net.isOnline()) {
dialog.showErrorBox(`${env === 'xy' ? '湖北襄阳四中教研平台' : '智汇享'}-网络连接错误', '当前无网络连接,请检查您的网络设置。`);
app.quit();
return false;
}
return true;
}
function createTray() {
const iconPath = `${__dirname}/src/assets/${updateJs.getIcon(env)}.png`;
const trayIcon = nativeImage.createFromPath(iconPath);
tray = new Tray(trayIcon);
tray.setToolTip(updateJs.getTitle(env));
tray.on('click', () => {
mainWindow.webContents.send('isOpenWindows');
});
}
function createWindow() {
mainWindow = new AppWindow();
mainWindow.focus();
mainWindow.hookWindowMessage(278, function (e) {
mainWindow.setEnabled(false);//窗口禁用
setTimeout(() => {
mainWindow.setEnabled(true);//窗口启用
}, 100);
return true;
})
}
const additionalData = { myKey: 'myValue' }
app.on('ready', () => {
// 检查网络状态
if (!checkNetworkStatus()) {
return;
}
// const gotTheLock = true
const gotTheLock = app.requestSingleInstanceLock(additionalData)
if (gotTheLock) {
app.getPath('crashDumps')
crashReporter.start({
uploadToServer: false,
ignoreSystemCrashHandler: false
})
// if (!buildStatus) {
// 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('focus', () => {
mainWindow.show()
});
mainWindow.on('maximize', () => {
mainWindow.show()
});
// 监听移动
mainWindow.on('move', () => {
// 如果是全屏自动恢复到上次窗口大小
if (isMaximized) {
mainWindow.unmaximize()
isMaximized = false;
}
if (mainWindow.isMaximized()) {
isMaximized = true;
}
});
ipcMain.handle('startLoad', (event) => {
if (startNumber === 0) {
updateHandle() // 检查更新
setInterval(() => {
autoUpdater.checkForUpdates()
}, 1000 * 60 * 60)
createTray()
startNumber++
}
});
// 更新
ipcMain.handle('updateHandle', () => {
autoUpdater.checkForUpdates()
});
// 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');
connection.off('JoinChannelCallback');
connection.off('ExitSharedScreen');
connection.off('SetSpeaker');
connection.off('ReceivedOperation');
connection.off('StopedSharedScreen');
}
});
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':
await connection.invoke(str, data.roomNum, data.contentString)
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;
case 'SetSpeakerCallback':
// 发言人设置成功
await connection.invoke(str, data)
break;
case 'sendOper2User':
// 扩展参数
await connection.invoke(str, data.uid, data.contentString)
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", (contentString) => {
mainWindow.webContents.send('onSignalr', {
key: 'Operation',
contentString
})
});
// 移出会议
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
})
});
// 加入房间回调
connection.on("JoinChannelCallback", (isSuccess) => {
mainWindow.webContents.send('onSignalr', {
key: 'JoinChannelCallback',
isSuccess,
})
});
// 退出共享
connection.on("ExitSharedScreen", () => {
mainWindow.webContents.send('onSignalr', {
key: 'ExitSharedScreen'
})
});
// 设置发言人
connection.on("SetSpeaker", (RoomManagerInputDTO) => {
mainWindow.webContents.send('onSignalr', {
key: 'SetSpeaker',
RoomManagerInputDTO
})
});
// 扩展参数
connection.on("ReceivedOperation", (contentString) => {
mainWindow.webContents.send('onSignalr', {
key: 'ReceivedOperation',
contentString
})
});
// 共享人取消共享屏幕
connection.on("StopedSharedScreen", (ScreenShareId) => {
mainWindow.webContents.send('onSignalr', {
key: 'StopedSharedScreen',
ScreenShareId
})
});
}
});
// 放大缩小退出窗口
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('getEnv', () => {
return env;
});
// 获取窗口是否显示
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()
} else if (data === '3') { // 打开弹窗
let message = JSON.stringify({
type: '3',
})
sendUpdateMessage(message)
}
});
// 选择文件夹
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) {
const contextMenu = Menu.buildFromTemplate([
{
label: '退出',
click: () => quit(),
},
]);
tray.setContextMenu(contextMenu);
mainWindow.setSkipTaskbar(true)
mainWindow.setResizable(false)
mainWindow.setAlwaysOnTop(true, 'screen-saver')
} else {
const contextMenu = Menu.buildFromTemplate([
{
label: '打开',
click: () => mainWindow.webContents.send('isOpenWindows'),
},
{
label: '最小化到系统托盘',
click: () => mainWindow.hide(),
},
{
label: '退出',
click: () => quit(),
},
]);
tray.setContextMenu(contextMenu);
}
// 设置最小窗口尺寸
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 (env === 'development') {
// 开发
if (buildStatus) {
child.loadURL(`http://192.168.2.9:8827/#/${config.key}`);
// child.loadURL(`file://${path.join(__dirname, './dist/index.html')}#/${config.key}`);
} else {
child.loadURL(config.url)
}
} else {
// 测试 | 生产
child.loadURL(`https://meeting-api.23544.com/#/${config.key}`);
// child.loadURL(`file://${path.join(__dirname, './dist/index.html')}#/${config.key}`);
}
child.hookWindowMessage(278, function (e) {
child.setEnabled(false);//窗口禁用
setTimeout(() => {
child.setEnabled(true);//窗口启用
}, 100);
return true;
})
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.checkForUpdatesAndNotify().catch();
const message = {
error: '检查更新出错',
checking: '正在检查更新……',
updateAva: '检测到新版本,正在下载……',
updateNotAva: '已经是最新版本,不用更新'
}
autoUpdater.setFeedURL(updateJs.getUpdateUrl(env))
autoUpdater.autoDownload = false // 不自动下载安装包
autoUpdater.autoInstallOnAppQuit = false // 不自动安装
autoUpdater.on('error', function (error) {
sendUpdateMessage(error)
})
autoUpdater.on('checking-for-update', function () {
sendUpdateMessage(message.checking)
})
autoUpdater.on('update-available', function (info) {
let messageStr = JSON.stringify({ type: '0' })
sendUpdateMessage(messageStr)
mainWindow.webContents.send('changeLocalStorage', {
isUpdate: true,
});
})
autoUpdater.on('update-not-available', function (info) {
mainWindow.webContents.send('changeLocalStorage', {
isUpdate: false,
});
})
// 更新下载进度事件
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(`${env === '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);
}
}
}