- 基于 openim-electron-demo 改造:工号+密码登录(自研账号服务)、会话列表/聊天窗/通讯录界面重做
- 文字/语音/文件/图片消息、文件拖拽发送、表情面板、一对一语音通话(LiveKit)
- RTC 令牌对接账号服务带鉴权版 /api/rtc_token(Bearer imToken + {room,identity})
- account-service: CORS Allow-Headers 补 authorization(浏览器/渲染进程跨域调 rtc_token 需要)
- 附本地 mock 账号服务(scripts/mock-account-server.js)与截图联调脚本
119 lines
3.2 KiB
TypeScript
119 lines
3.2 KiB
TypeScript
import fs from "fs";
|
|
import path from "path";
|
|
import { DataPath, IElectronAPI } from "./../../src/types/globalExpose.d";
|
|
import { contextBridge, ipcRenderer } from "electron";
|
|
import { isProd } from "../utils";
|
|
import "@openim/electron-client-sdk/lib/preload";
|
|
import { Platform } from "@openim/wasm-client-sdk";
|
|
|
|
const getPlatform = () => {
|
|
if (process.platform === "darwin") {
|
|
return Platform.MacOSX;
|
|
}
|
|
if (process.platform === "win32") {
|
|
return Platform.Windows;
|
|
}
|
|
return Platform.Linux;
|
|
};
|
|
|
|
const getDataPath = (key: DataPath) => {
|
|
switch (key) {
|
|
case "public":
|
|
return isProd ? ipcRenderer.sendSync("getDataPath", "public") : "";
|
|
case "sdkResources":
|
|
return isProd ? ipcRenderer.sendSync("getDataPath", "sdkResources") : "";
|
|
case "logsPath":
|
|
return isProd ? ipcRenderer.sendSync("getDataPath", "logsPath") : "";
|
|
default:
|
|
return "";
|
|
}
|
|
};
|
|
|
|
const subscribe = (channel: string, callback: (...args: any[]) => void) => {
|
|
const subscription = (_, ...args) => callback(...args);
|
|
ipcRenderer.on(channel, subscription);
|
|
return () => ipcRenderer.removeListener(channel, subscription);
|
|
};
|
|
|
|
const subscribeOnce = (channel: string, callback: (...args: any[]) => void) => {
|
|
ipcRenderer.once(channel, (_, ...args) => callback(...args));
|
|
};
|
|
|
|
const unsubscribeAll = (channel: string) => {
|
|
ipcRenderer.removeAllListeners(channel);
|
|
};
|
|
|
|
const ipcInvoke = (channel: string, ...arg: any) => {
|
|
return ipcRenderer.invoke(channel, ...arg);
|
|
};
|
|
|
|
const ipcSendSync = (channel: string, ...arg: any) => {
|
|
return ipcRenderer.sendSync(channel, ...arg);
|
|
};
|
|
|
|
const getUniqueSavePath = (originalPath: string) => {
|
|
let counter = 0;
|
|
let savePath = originalPath;
|
|
let fileDir = path.dirname(originalPath);
|
|
let fileName = path.basename(originalPath);
|
|
let fileExt = path.extname(originalPath);
|
|
let baseName = path.basename(fileName, fileExt);
|
|
|
|
while (fs.existsSync(savePath)) {
|
|
counter++;
|
|
fileName = `${baseName}(${counter})${fileExt}`;
|
|
savePath = path.join(fileDir, fileName);
|
|
}
|
|
|
|
return savePath;
|
|
};
|
|
|
|
const getFileByPath = async (filePath: string) => {
|
|
try {
|
|
const filename = path.basename(filePath);
|
|
const data = await fs.promises.readFile(filePath);
|
|
return new File([data], filename);
|
|
} catch (error) {
|
|
console.log(error);
|
|
return null;
|
|
}
|
|
};
|
|
|
|
const saveFileToDisk = async ({
|
|
file,
|
|
sync,
|
|
}: {
|
|
file: File;
|
|
sync?: boolean;
|
|
}): Promise<string> => {
|
|
const arrayBuffer = await file.arrayBuffer();
|
|
const saveDir = ipcRenderer.sendSync("getDataPath", "sdkResources");
|
|
const savePath = path.join(saveDir, file.name);
|
|
const uniqueSavePath = getUniqueSavePath(savePath);
|
|
if (!fs.existsSync(saveDir)) {
|
|
fs.mkdirSync(saveDir, { recursive: true });
|
|
}
|
|
if (sync) {
|
|
await fs.promises.writeFile(uniqueSavePath, Buffer.from(arrayBuffer));
|
|
} else {
|
|
fs.promises.writeFile(uniqueSavePath, Buffer.from(arrayBuffer));
|
|
}
|
|
return uniqueSavePath;
|
|
};
|
|
|
|
const Api: IElectronAPI = {
|
|
getDataPath,
|
|
getVersion: () => process.version,
|
|
getPlatform,
|
|
getSystemVersion: process.getSystemVersion,
|
|
subscribe,
|
|
subscribeOnce,
|
|
unsubscribeAll,
|
|
ipcInvoke,
|
|
ipcSendSync,
|
|
getFileByPath,
|
|
saveFileToDisk,
|
|
};
|
|
|
|
contextBridge.exposeInMainWorld("electronAPI", Api);
|