6
.gitignore
vendored
@ -33,4 +33,8 @@ yarn-error.log*
|
|||||||
|
|
||||||
lib/
|
lib/
|
||||||
out/
|
out/
|
||||||
build
|
build
|
||||||
|
out-election/
|
||||||
|
dist-ele/
|
||||||
|
icon.icns
|
||||||
|
icons.iconset
|
3
.yarnrc
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
registry "https://registry.npmmirror.com/"
|
||||||
|
|
||||||
|
electron_mirror "https://registry.npmmirror.com/-/binary/electron/"
|
55
apps/web/electron-builder.js
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
module.exports = {
|
||||||
|
productName: "tangsengdaodao", //项目名
|
||||||
|
appId: "com.tsdaodao.im",
|
||||||
|
copyright: "Copyright © tsdaodao", //版权
|
||||||
|
directories: {
|
||||||
|
output: "dist-ele", // 输出文件夹
|
||||||
|
},
|
||||||
|
npmRebuild: false,
|
||||||
|
asar: false,
|
||||||
|
buildDependenciesFromSource: true,
|
||||||
|
electronDownload: {
|
||||||
|
mirror: "https://registry.npmmirror.com/-/binary/electron/",
|
||||||
|
},
|
||||||
|
files: ["resources/**/*","out-election/**/*", "build/**/*"], // 需要打包的文件
|
||||||
|
extraMetadata: {
|
||||||
|
main: "out-election/main/index.js",
|
||||||
|
},
|
||||||
|
mac: {
|
||||||
|
category: "public.app-category.instant-messaging",
|
||||||
|
artifactName: "${productName}-${version}-${arch}.${ext}",
|
||||||
|
icon: "resources/icons/icon.icns",
|
||||||
|
target: ["dmg", "zip"]
|
||||||
|
},
|
||||||
|
dmg: {
|
||||||
|
// background: 'build/appdmg.png', // dmg安装窗口背景图
|
||||||
|
icon: "resources/icons/icon.icns", // 客户端图标
|
||||||
|
iconSize: 100, // 安装图标大小
|
||||||
|
// 安装窗口中包含的项目和配置
|
||||||
|
contents: [
|
||||||
|
{ x: 380, y: 280, type: "link", path: "/Applications" },
|
||||||
|
{ x: 110, y: 280, type: "file" },
|
||||||
|
],
|
||||||
|
window: { width: 500, height: 500 }, // 安装窗口大小
|
||||||
|
},
|
||||||
|
win: {
|
||||||
|
icon: "resources/icons/icon.ico",
|
||||||
|
verifyUpdateCodeSignature: false,
|
||||||
|
target: ["nsis", "zip"],
|
||||||
|
artifactName: "${productName}-Setup-${version}.${ext}"
|
||||||
|
},
|
||||||
|
nsis: {
|
||||||
|
oneClick: false, // 是否一键安装
|
||||||
|
allowElevation: true, // 允许请求提升。 如果为false,则用户必须使用提升的权限重新启动安装程序。
|
||||||
|
allowToChangeInstallationDirectory: true, // 允许修改安装目录
|
||||||
|
// installerIcon: "./build/icon.ico",// 安装图标
|
||||||
|
// uninstallerIcon: "./build/icons/bbb.ico",//卸载图标
|
||||||
|
// installerHeaderIcon: "./build/icon.ico", // 安装时头部图标
|
||||||
|
createDesktopShortcut: true, // 创建桌面图标
|
||||||
|
createStartMenuShortcut: true, // 创建开始菜单图标
|
||||||
|
},
|
||||||
|
linux: {
|
||||||
|
target: ["AppImage", "deb"],
|
||||||
|
icon: "resources/icons/icon.icns",
|
||||||
|
},
|
||||||
|
};
|
@ -2,7 +2,23 @@
|
|||||||
"name": "@tsdaodao/web",
|
"name": "@tsdaodao/web",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "./",
|
"main": "out-election/main/index.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "cross-env BROWSER=none REACT_APP_VERSION=$npm_package_version react-app-rewired start",
|
||||||
|
"dev": "cross-env MODE=dev BROWSER=none REACT_APP_VERSION=$npm_package_version react-app-rewired start",
|
||||||
|
"dev-ele": "kill-port 3000 && concurrently -k -n=web,ele -c=green,blue \"yarn dev\" \"wait-on tcp:3000 && npm-run-all watch\"",
|
||||||
|
"watch": "tsc-watch -p tsconfig.e.json --onSuccess \"npm-run-all start:electron\"",
|
||||||
|
"start:electron": "cross-env NODE_ENV=development electron .",
|
||||||
|
"build": "cross-env REACT_APP_VERSION=$npm_package_version react-app-rewired build",
|
||||||
|
"build:analyzer": "cross-env ANALYZER=true REACT_APP_VERSION=$npm_package_version react-app-rewired build",
|
||||||
|
"build-ele:mac": "tsc -p tsconfig.e.json && electron-builder --mac -c",
|
||||||
|
"build-ele:win": "tsc -p tsconfig.e.json && electron-builder --win -c",
|
||||||
|
"build-ele": "tsc -p tsconfig.e.json && electron-builder -mw -c",
|
||||||
|
"test": "react-app-rewired test",
|
||||||
|
"eject": "react-app-rewired eject",
|
||||||
|
"clean": "rimraf node_modules out-election build dist-ele .turbo",
|
||||||
|
"tauri": "tauri"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tauri-apps/api": "^1.1.0",
|
"@tauri-apps/api": "^1.1.0",
|
||||||
"@tsdaodao/base": "*",
|
"@tsdaodao/base": "*",
|
||||||
@ -13,6 +29,9 @@
|
|||||||
"@types/react-mentions": "^4.1.5",
|
"@types/react-mentions": "^4.1.5",
|
||||||
"@types/react-virtualized": "^9.21.22",
|
"@types/react-virtualized": "^9.21.22",
|
||||||
"classnames": "^2.3.1",
|
"classnames": "^2.3.1",
|
||||||
|
"electron-log": "^5.1.1",
|
||||||
|
"electron-screenshots": "^0.5.26",
|
||||||
|
"howler": "^2.2.4",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-app-rewired": "^2.1.8",
|
"react-app-rewired": "^2.1.8",
|
||||||
"react-lazyload": "^3.2.0",
|
"react-lazyload": "^3.2.0",
|
||||||
@ -26,28 +45,28 @@
|
|||||||
"@testing-library/jest-dom": "^4.2.4",
|
"@testing-library/jest-dom": "^4.2.4",
|
||||||
"@testing-library/react": "^9.3.2",
|
"@testing-library/react": "^9.3.2",
|
||||||
"@testing-library/user-event": "^7.1.2",
|
"@testing-library/user-event": "^7.1.2",
|
||||||
|
"@types/howler": "^2.2.11",
|
||||||
"@types/jest": "^24.0.0",
|
"@types/jest": "^24.0.0",
|
||||||
"@types/node": "^12.0.0",
|
"@types/node": "^20.10.5",
|
||||||
"@types/react": "^16.9.0",
|
"@types/react": "^16.9.0",
|
||||||
|
"concurrently": "^8.2.2",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"customize-cra": "^1.0.0",
|
"customize-cra": "^1.0.0",
|
||||||
|
"electron": "26.0.0",
|
||||||
|
"electron-builder": "^24.9.1",
|
||||||
|
"electron-log": "^5.0.1",
|
||||||
"eslint-config-react-app": "^7.0.1",
|
"eslint-config-react-app": "^7.0.1",
|
||||||
|
"kill-port": "^2.0.1",
|
||||||
|
"npm-run-all": "^4.1.5",
|
||||||
"postcss-normalize": "^10.0.1",
|
"postcss-normalize": "^10.0.1",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-scripts": "5.0.0",
|
"react-scripts": "5.0.0",
|
||||||
"terser-webpack-plugin": "^5.3.9",
|
"terser-webpack-plugin": "^5.3.9",
|
||||||
"typescript": "~3.7.2",
|
"tsc-watch": "^6.0.4",
|
||||||
|
"typescript": "^5.3.3",
|
||||||
|
"wait-on": "^7.2.0",
|
||||||
"webpack-bundle-analyzer": "^4.5.0"
|
"webpack-bundle-analyzer": "^4.5.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
|
||||||
"start": "cross-env BROWSER=none REACT_APP_VERSION=$npm_package_version react-app-rewired start",
|
|
||||||
"dev": "cross-env MODE=dev BROWSER=none REACT_APP_VERSION=$npm_package_version react-app-rewired start",
|
|
||||||
"build": "cross-env REACT_APP_VERSION=$npm_package_version react-app-rewired build",
|
|
||||||
"build:analyzer": "cross-env ANALYZER=true REACT_APP_VERSION=$npm_package_version react-app-rewired build",
|
|
||||||
"test": "react-app-rewired test",
|
|
||||||
"eject": "react-app-rewired eject",
|
|
||||||
"tauri": "tauri"
|
|
||||||
},
|
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"extends": "react-app"
|
"extends": "react-app"
|
||||||
},
|
},
|
||||||
|
BIN
apps/web/resources/icons/128x128.png
Normal file
After Width: | Height: | Size: 8.8 KiB |
BIN
apps/web/resources/icons/128x128@2x.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
apps/web/resources/icons/32x32.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
apps/web/resources/icons/icon.ico
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
apps/web/resources/logo.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
apps/web/resources/tray/128x128.png
Normal file
After Width: | Height: | Size: 8.8 KiB |
BIN
apps/web/resources/tray/30x30.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
apps/web/resources/tray/32x32.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
1
apps/web/src-election/electron-env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
declare module 'tmp';
|
6
apps/web/src-election/main/confing.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
const TSDD_FONFIG = {
|
||||||
|
appId: "com.tsdaodao.im",
|
||||||
|
name: "唐僧叨叨",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TSDD_FONFIG;
|
441
apps/web/src-election/main/index.ts
Normal file
@ -0,0 +1,441 @@
|
|||||||
|
import {
|
||||||
|
app,
|
||||||
|
BrowserWindow,
|
||||||
|
screen,
|
||||||
|
globalShortcut,
|
||||||
|
ipcMain,
|
||||||
|
nativeImage as NativeImage,
|
||||||
|
Menu,
|
||||||
|
Tray,
|
||||||
|
} from "electron";
|
||||||
|
import fs from "fs";
|
||||||
|
import tmp from 'tmp';
|
||||||
|
import Screenshots from "electron-screenshots";
|
||||||
|
import { join } from "path";
|
||||||
|
|
||||||
|
import logo, { getNoMessageTrayIcon } from "./logo";
|
||||||
|
import TSDD_FONFIG from "./confing";
|
||||||
|
|
||||||
|
let forceQuit = false;
|
||||||
|
let mainWindow: any;
|
||||||
|
let isMainWindowFocusedWhenStartScreenshot = false;
|
||||||
|
let screenshots: any;
|
||||||
|
let tray: any;
|
||||||
|
let trayIcon: any;
|
||||||
|
let settings: any = {};
|
||||||
|
let screenShotWindowId = 0;
|
||||||
|
let isFullScreen = false;
|
||||||
|
|
||||||
|
let isOsx = process.platform === "darwin";
|
||||||
|
let isWin = !isOsx;
|
||||||
|
|
||||||
|
const isDevelopment = process.env.NODE_ENV === "development";
|
||||||
|
|
||||||
|
let mainMenu: (Electron.MenuItemConstructorOptions | Electron.MenuItem)[] = [
|
||||||
|
{
|
||||||
|
label: "唐僧叨叨",
|
||||||
|
submenu: [
|
||||||
|
{
|
||||||
|
label: `关于唐僧叨叨`,
|
||||||
|
},
|
||||||
|
{ label: "服务", role: "services" },
|
||||||
|
{ type: "separator" },
|
||||||
|
{
|
||||||
|
label: "退出",
|
||||||
|
accelerator: "Command+Q",
|
||||||
|
click() {
|
||||||
|
forceQuit = true;
|
||||||
|
mainWindow = null;
|
||||||
|
setTimeout(() => {
|
||||||
|
app.exit(0);
|
||||||
|
}, 1000);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "编辑",
|
||||||
|
submenu: [
|
||||||
|
{
|
||||||
|
role: "undo",
|
||||||
|
label: "撤销",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
role: "redo",
|
||||||
|
label: "重做",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "separator",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
role: "cut",
|
||||||
|
label: "剪切",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
role: "copy",
|
||||||
|
label: "复制",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
role: "paste",
|
||||||
|
label: "粘贴",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
role: "pasteAndMatchStyle",
|
||||||
|
label: "粘贴并匹配样式",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
role: "delete",
|
||||||
|
label: "删除",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
role: "selectAll",
|
||||||
|
label: "全选",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "显示",
|
||||||
|
submenu: [
|
||||||
|
{
|
||||||
|
label: isFullScreen ? "全屏" : "退出全屏",
|
||||||
|
accelerator: "Shift+Cmd+F",
|
||||||
|
click() {
|
||||||
|
isFullScreen = !isFullScreen;
|
||||||
|
|
||||||
|
mainWindow.show();
|
||||||
|
mainWindow.setFullScreen(isFullScreen);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "切换会话",
|
||||||
|
accelerator: "Shift+Cmd+M",
|
||||||
|
click() {
|
||||||
|
mainWindow.show();
|
||||||
|
mainWindow.webContents.send("show-conversations");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "separator",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "separator",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
role: "toggleDevTools",
|
||||||
|
label: "切换开发者工具",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
role: "togglefullscreen",
|
||||||
|
label: "切换全屏",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "窗口",
|
||||||
|
role: "window",
|
||||||
|
submenu: [
|
||||||
|
{
|
||||||
|
label: "最小化",
|
||||||
|
role: "minimize",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "关闭窗口",
|
||||||
|
role: "close",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "帮助",
|
||||||
|
role: "help",
|
||||||
|
submenu: [
|
||||||
|
{
|
||||||
|
type: "separator",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
role: "reload",
|
||||||
|
label: "刷新",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
role: "forceReload",
|
||||||
|
label: "强制刷新",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
let trayMenu: Electron.MenuItemConstructorOptions[] = [
|
||||||
|
{
|
||||||
|
label: "显示窗口",
|
||||||
|
click() {
|
||||||
|
let isVisible = mainWindow.isVisible();
|
||||||
|
isVisible ? mainWindow.hide() : mainWindow.show();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "separator",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "退出",
|
||||||
|
accelerator: "Command+Q",
|
||||||
|
click() {
|
||||||
|
forceQuit = true;
|
||||||
|
mainWindow = null;
|
||||||
|
setTimeout(() => {
|
||||||
|
app.exit(0);
|
||||||
|
}, 1000);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
function updateTray(unread = 0): any {
|
||||||
|
settings.showOnTray = true;
|
||||||
|
|
||||||
|
// linux 系统不支持 tray
|
||||||
|
if (process.platform === "linux") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.showOnTray) {
|
||||||
|
let contextmenu = Menu.buildFromTemplate(trayMenu);
|
||||||
|
|
||||||
|
if (!trayIcon) {
|
||||||
|
trayIcon = getNoMessageTrayIcon();
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!tray) {
|
||||||
|
// Init tray icon
|
||||||
|
tray = new Tray(trayIcon);
|
||||||
|
if (process.platform === "linux") {
|
||||||
|
tray.setContextMenu(contextmenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
tray.on("right-click", () => {
|
||||||
|
tray.popUpContextMenu(contextmenu);
|
||||||
|
});
|
||||||
|
|
||||||
|
tray.on("click", () => {
|
||||||
|
mainWindow.show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isOsx) {
|
||||||
|
tray.setTitle(unread > 0 ? " " + unread : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
tray.setImage(trayIcon);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (!tray) return;
|
||||||
|
tray.destroy();
|
||||||
|
tray = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createMenu() {
|
||||||
|
var menu = Menu.buildFromTemplate(mainMenu);
|
||||||
|
|
||||||
|
if (isOsx) {
|
||||||
|
Menu.setApplicationMenu(menu);
|
||||||
|
} else {
|
||||||
|
mainWindow.setMenu(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function regShortcut() {
|
||||||
|
globalShortcut.register("CommandOrControl+shift+a", () => {
|
||||||
|
isMainWindowFocusedWhenStartScreenshot = mainWindow.isFocused();
|
||||||
|
console.log(
|
||||||
|
"isMainWindowFocusedWhenStartScreenshot",
|
||||||
|
mainWindow.isFocused()
|
||||||
|
);
|
||||||
|
screenshots.startCapture();
|
||||||
|
});
|
||||||
|
|
||||||
|
globalShortcut.register("esc", () => {
|
||||||
|
if (screenshots.$win?.isFocused()) {
|
||||||
|
screenshots.endCapture();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 打开所有窗口控制台
|
||||||
|
globalShortcut.register("ctrl+shift+i", () => {
|
||||||
|
let windows = BrowserWindow.getAllWindows();
|
||||||
|
windows.forEach((win: any) => win.openDevTools());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const createMainWindow = async () => {
|
||||||
|
const NODE_ENV = process.env.NODE_ENV;
|
||||||
|
const { width, height } = screen.getPrimaryDisplay().workAreaSize;
|
||||||
|
mainWindow = new BrowserWindow({
|
||||||
|
width: 960,
|
||||||
|
height: 600,
|
||||||
|
minWidth: 960,
|
||||||
|
minHeight: 600,
|
||||||
|
// frame: true, // * app边框(包括关闭,全屏,最小化按钮的导航栏) @false: 隐藏
|
||||||
|
// titleBarStyle: "hidden",
|
||||||
|
// transparent: true, // * app 背景透明
|
||||||
|
hasShadow: false, // * app 边框阴影
|
||||||
|
show: false, // 启动窗口时隐藏,直到渲染进程加载完成「ready-to-show 监听事件」 再显示窗口,防止加载时闪烁
|
||||||
|
resizable: true, // 禁止手动修改窗口尺寸
|
||||||
|
webPreferences: {
|
||||||
|
// 加载脚本
|
||||||
|
preload: join(__dirname, "..", "preload/index"),
|
||||||
|
nodeIntegration: true,
|
||||||
|
},
|
||||||
|
// frame: !isWin,
|
||||||
|
});
|
||||||
|
mainWindow.center();
|
||||||
|
mainWindow.once("ready-to-show", () => {
|
||||||
|
mainWindow.show(); // 显示窗口
|
||||||
|
mainWindow.focus();
|
||||||
|
});
|
||||||
|
|
||||||
|
mainWindow.on("close", (e: any) => {
|
||||||
|
if (forceQuit || !tray) {
|
||||||
|
mainWindow = null;
|
||||||
|
} else {
|
||||||
|
e.preventDefault();
|
||||||
|
if (mainWindow.isFullScreen()) {
|
||||||
|
mainWindow.setFullScreen(false);
|
||||||
|
mainWindow.once("leave-full-screen", () => mainWindow.hide());
|
||||||
|
} else {
|
||||||
|
mainWindow.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (NODE_ENV === "development") mainWindow.loadURL("http://localhost:3000");
|
||||||
|
if (NODE_ENV !== "development") {
|
||||||
|
process.env.DIST_ELECTRON = join(__dirname, "../");
|
||||||
|
const WEB_URL = join(process.env.DIST_ELECTRON, "../build/index.html");
|
||||||
|
mainWindow.loadFile(WEB_URL);
|
||||||
|
}
|
||||||
|
|
||||||
|
ipcMain.on("screenshots-start", (event, args) => {
|
||||||
|
console.log("main voip-message event", args);
|
||||||
|
screenShotWindowId = event.sender.id;
|
||||||
|
screenshots.startCapture();
|
||||||
|
});
|
||||||
|
|
||||||
|
createMenu();
|
||||||
|
};
|
||||||
|
|
||||||
|
function onDeepLink(url: string) {
|
||||||
|
console.log("onOpenDeepLink", url);
|
||||||
|
mainWindow.webContents.send("deep-link", url);
|
||||||
|
}
|
||||||
|
|
||||||
|
app.setName(TSDD_FONFIG.name);
|
||||||
|
isDevelopment && app.dock && app.dock.setIcon(logo);
|
||||||
|
app.on("open-url", (event, url) => {
|
||||||
|
onDeepLink(url);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 单例模式启动
|
||||||
|
const gotTheLock = app.requestSingleInstanceLock();
|
||||||
|
if (!gotTheLock) {
|
||||||
|
app.quit();
|
||||||
|
} else {
|
||||||
|
app.on("second-instance", (event, argv) => {
|
||||||
|
if (mainWindow) {
|
||||||
|
mainWindow.show();
|
||||||
|
if (mainWindow.isMinimized()) {
|
||||||
|
mainWindow.restore();
|
||||||
|
}
|
||||||
|
mainWindow.focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
app.on("ready", () => {
|
||||||
|
regShortcut();
|
||||||
|
createMainWindow(); // 创建窗口
|
||||||
|
|
||||||
|
screenshots = new Screenshots({
|
||||||
|
singleWindow: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const onScreenShotEnd = (result?: any) => {
|
||||||
|
console.log(
|
||||||
|
"onScreenShotEnd",
|
||||||
|
isMainWindowFocusedWhenStartScreenshot,
|
||||||
|
screenShotWindowId
|
||||||
|
);
|
||||||
|
if (isMainWindowFocusedWhenStartScreenshot) {
|
||||||
|
if (result) {
|
||||||
|
mainWindow.webContents.send("screenshots-ok", result);
|
||||||
|
}
|
||||||
|
mainWindow.show();
|
||||||
|
isMainWindowFocusedWhenStartScreenshot = false;
|
||||||
|
} else if (screenShotWindowId) {
|
||||||
|
let windows = BrowserWindow.getAllWindows();
|
||||||
|
let tms = windows.filter(
|
||||||
|
(win) => win.webContents.id === screenShotWindowId
|
||||||
|
);
|
||||||
|
if (tms.length > 0) {
|
||||||
|
if (result) {
|
||||||
|
tms[0].webContents.send("screenshots-ok", result);
|
||||||
|
}
|
||||||
|
tms[0].show();
|
||||||
|
}
|
||||||
|
screenShotWindowId = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 点击确定按钮回调事件
|
||||||
|
screenshots.on("ok", (e, buffer, bounds) => {
|
||||||
|
let filename = tmp.tmpNameSync() + '.png';
|
||||||
|
let image = NativeImage.createFromBuffer(buffer);
|
||||||
|
fs.writeFileSync(filename, image.toPNG());
|
||||||
|
|
||||||
|
console.log("screenshots ok", e);
|
||||||
|
onScreenShotEnd({ filePath: filename });
|
||||||
|
});
|
||||||
|
|
||||||
|
// 点击取消按钮回调事件
|
||||||
|
screenshots.on("cancel", (e: any) => {
|
||||||
|
// 执行了preventDefault
|
||||||
|
// 点击取消不会关闭截图窗口
|
||||||
|
// e.preventDefault()
|
||||||
|
// console.log('capture', 'cancel2')
|
||||||
|
console.log("screenshots cancel", e);
|
||||||
|
onScreenShotEnd();
|
||||||
|
});
|
||||||
|
// 点击保存按钮回调事件
|
||||||
|
screenshots.on("save", (e, { viewer }) => {
|
||||||
|
console.log("screenshots save", e);
|
||||||
|
onScreenShotEnd();
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
updateTray();
|
||||||
|
} catch (e) {
|
||||||
|
// do nothing
|
||||||
|
console.log("==updateTray==", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.on("activate", () => {
|
||||||
|
if (!mainWindow) {
|
||||||
|
return createMainWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mainWindow.isVisible()) {
|
||||||
|
mainWindow.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.on("before-quit", () => {
|
||||||
|
forceQuit = true;
|
||||||
|
|
||||||
|
if (!tray) return;
|
||||||
|
|
||||||
|
tray.destroy();
|
||||||
|
tray = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 除了 macOS 外,当所有窗口都被关闭的时候退出程序。 macOS窗口全部关闭时,dock中程序不会退出
|
||||||
|
app.on("window-all-closed", () => {
|
||||||
|
process.platform !== "darwin" && app.quit();
|
||||||
|
});
|
16
apps/web/src-election/main/logo.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import path from "path";
|
||||||
|
import { app, screen } from "electron";
|
||||||
|
|
||||||
|
export default path.join(app.getAppPath(), "./resources/logo.png");
|
||||||
|
|
||||||
|
export function getNoMessageTrayIcon () {
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
return path.join(app.getAppPath(), './resources/tray/30x30.png')
|
||||||
|
} else if (process.platform === 'win32') {
|
||||||
|
return path.join(app.getAppPath(), './resources/tray/128x128.png')
|
||||||
|
} else if (screen.getPrimaryDisplay().scaleFactor > 1) {
|
||||||
|
return path.join(app.getAppPath(), './resources/tray/128x128.png')
|
||||||
|
} else {
|
||||||
|
return path.join(app.getAppPath(), './resources/tray/128x128.png')
|
||||||
|
}
|
||||||
|
}
|
35
apps/web/src-election/main/utils/createWindow.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { BrowserWindow, screen } from "electron";
|
||||||
|
import { join } from "path";
|
||||||
|
|
||||||
|
export function createWindow() {
|
||||||
|
const NODE_ENV = process.env.NODE_ENV;
|
||||||
|
const { width, height } = screen.getPrimaryDisplay().workAreaSize;
|
||||||
|
// 生成窗口实例
|
||||||
|
const mainWin = new BrowserWindow({
|
||||||
|
width, // * 指定启动app时的默认窗口尺寸
|
||||||
|
height, // * 指定启动app时的默认窗口尺寸
|
||||||
|
frame: true, // * app边框(包括关闭,全屏,最小化按钮的导航栏) @false: 隐藏
|
||||||
|
titleBarStyle: "hidden",
|
||||||
|
transparent: true, // * app 背景透明
|
||||||
|
hasShadow: false, // * app 边框阴影
|
||||||
|
show: false, // 启动窗口时隐藏,直到渲染进程加载完成「ready-to-show 监听事件」 再显示窗口,防止加载时闪烁
|
||||||
|
resizable: true, // 禁止手动修改窗口尺寸
|
||||||
|
webPreferences: {
|
||||||
|
// 加载脚本
|
||||||
|
preload: join(__dirname, "../..", "preload/index"),
|
||||||
|
nodeIntegration: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 启动窗口时隐藏,直到渲染进程加载完成「ready-to-show 监听事件」 再显示窗口,防止加载时闪烁
|
||||||
|
mainWin.once("ready-to-show", () => {
|
||||||
|
mainWin.show(); // 显示窗口
|
||||||
|
});
|
||||||
|
|
||||||
|
if (NODE_ENV === "development") mainWin.loadURL("http://localhost:3000");
|
||||||
|
if (NODE_ENV !== "development") {
|
||||||
|
process.env.DIST_ELECTRON = join(__dirname, '../');
|
||||||
|
const WEB_URL = join(process.env.DIST_ELECTRON, "../../build/index.html");
|
||||||
|
mainWin.loadFile(WEB_URL);
|
||||||
|
}
|
||||||
|
}
|
21
apps/web/src-election/preload/index.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { contextBridge, ipcRenderer } from "electron";
|
||||||
|
|
||||||
|
contextBridge.exposeInMainWorld("__POWERED_ELECTRON__", true);
|
||||||
|
|
||||||
|
contextBridge.exposeInMainWorld("ipc", {
|
||||||
|
send: (channel: string, ...args: any[]) => ipcRenderer.send(channel, ...args),
|
||||||
|
invoke: (channel: string, ...args: any[]): Promise<any> =>
|
||||||
|
ipcRenderer.invoke(channel, ...args),
|
||||||
|
on: (
|
||||||
|
channel: string,
|
||||||
|
listener: (event: Electron.IpcRendererEvent, ...args: any[]) => void
|
||||||
|
) => {
|
||||||
|
ipcRenderer.on(channel, listener);
|
||||||
|
},
|
||||||
|
once: (
|
||||||
|
channel: string,
|
||||||
|
listener: (event: Electron.IpcRendererEvent, ...args: any[]) => void
|
||||||
|
) => {
|
||||||
|
ipcRenderer.once(channel, listener);
|
||||||
|
},
|
||||||
|
});
|
@ -1,60 +1,100 @@
|
|||||||
import { ChatPage, EndpointCategory, WKApp, Menus } from '@tsdaodao/base';
|
import { ChatPage, EndpointCategory, WKApp, Menus } from "@tsdaodao/base";
|
||||||
import { ContactsList } from '@tsdaodao/contacts';
|
import { ContactsList } from "@tsdaodao/contacts";
|
||||||
import React from 'react';
|
import React from "react";
|
||||||
import './index.css';
|
import "./index.css";
|
||||||
import AppLayout from '../Layout';
|
import AppLayout from "../Layout";
|
||||||
import { WKSDK } from 'wukongimjssdk';
|
import { WKSDK } from "wukongimjssdk";
|
||||||
function App() {
|
function App() {
|
||||||
registerMenus()
|
registerMenus();
|
||||||
return (
|
return <AppLayout />;
|
||||||
<AppLayout />
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function registerMenus() {
|
function registerMenus() {
|
||||||
|
|
||||||
WKSDK.shared().conversationManager.addConversationListener(() => {
|
WKSDK.shared().conversationManager.addConversationListener(() => {
|
||||||
WKApp.menus.refresh()
|
WKApp.menus.refresh();
|
||||||
})
|
});
|
||||||
|
|
||||||
WKApp.endpointManager.setMethod("menus.friendapply.change", () => {
|
WKApp.endpointManager.setMethod(
|
||||||
WKApp.menus.refresh()
|
"menus.friendapply.change",
|
||||||
}, {
|
() => {
|
||||||
category: EndpointCategory.friendApplyDataChange,
|
WKApp.menus.refresh();
|
||||||
})
|
},
|
||||||
|
{
|
||||||
WKApp.menus.register("chat", (context) => {
|
category: EndpointCategory.friendApplyDataChange,
|
||||||
const m = new Menus("chat", "/", "会话",
|
|
||||||
<img alt='会话' src={require("./assets/HomeTab.svg").default}></img>,
|
|
||||||
<img alt='会话' src={require("./assets/HomeTabSelected.svg").default}></img>)
|
|
||||||
let badge = 0
|
|
||||||
for (const conversation of WKSDK.shared().conversationManager.conversations) {
|
|
||||||
if (!conversation.channelInfo?.mute) {
|
|
||||||
badge += conversation.unread
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
m.badge = badge
|
);
|
||||||
return m
|
|
||||||
},1000)
|
|
||||||
|
|
||||||
WKApp.menus.register("contacts", (param) => {
|
WKApp.menus.register(
|
||||||
const m = new Menus("contacts", "/contacts", "通讯录",
|
"chat",
|
||||||
<img alt='通讯录' src={require("./assets/ContactsTab.svg").default}></img>,
|
(context) => {
|
||||||
<img alt='通讯录' src={require("./assets/ContactsTabSelected.svg").default}></img>)
|
const m = new Menus(
|
||||||
|
"chat",
|
||||||
|
"/",
|
||||||
|
"会话",
|
||||||
|
<img alt="会话" src={require("./assets/HomeTab.svg").default}></img>,
|
||||||
|
(
|
||||||
|
<img
|
||||||
|
alt="会话"
|
||||||
|
src={require("./assets/HomeTabSelected.svg").default}
|
||||||
|
></img>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
let badge = 0;
|
||||||
|
for (const conversation of WKSDK.shared().conversationManager
|
||||||
|
.conversations) {
|
||||||
|
if (!conversation.channelInfo?.mute) {
|
||||||
|
badge += conversation.unread;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m.badge = badge;
|
||||||
|
return m;
|
||||||
|
},
|
||||||
|
1000
|
||||||
|
);
|
||||||
|
|
||||||
m.badge = WKApp.shared.getFriendApplysUnreadCount()
|
// 获取好友未申请添加数量
|
||||||
return m
|
let unreadCount = 0;
|
||||||
},2000)
|
if (WKApp.loginInfo.isLogined()) {
|
||||||
|
WKApp.apiClient.get(`/user/reddot/friendApply`).then((res) => {
|
||||||
|
unreadCount = res.count;
|
||||||
|
WKApp.menus.refresh();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
WKApp.menus.register(
|
||||||
|
"contacts",
|
||||||
|
(param) => {
|
||||||
|
const m = new Menus(
|
||||||
|
"contacts",
|
||||||
|
"/contacts",
|
||||||
|
"通讯录",
|
||||||
|
(
|
||||||
|
<img
|
||||||
|
alt="通讯录"
|
||||||
|
src={require("./assets/ContactsTab.svg").default}
|
||||||
|
></img>
|
||||||
|
),
|
||||||
|
(
|
||||||
|
<img
|
||||||
|
alt="通讯录"
|
||||||
|
src={require("./assets/ContactsTabSelected.svg").default}
|
||||||
|
></img>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
m.badge = unreadCount;
|
||||||
|
return m;
|
||||||
|
},
|
||||||
|
2000
|
||||||
|
);
|
||||||
|
|
||||||
WKApp.route.register("/", () => {
|
WKApp.route.register("/", () => {
|
||||||
return <ChatPage></ChatPage>
|
return <ChatPage></ChatPage>;
|
||||||
})
|
});
|
||||||
|
|
||||||
WKApp.route.register("/contacts", () => {
|
WKApp.route.register("/contacts", () => {
|
||||||
return <ContactsList></ContactsList>
|
return <ContactsList></ContactsList>;
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
|
||||||
|
@ -1,49 +1,48 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from "react-dom";
|
||||||
import './index.css';
|
import "./index.css";
|
||||||
import App from './App';
|
import App from "./App";
|
||||||
import reportWebVitals from './reportWebVitals';
|
import reportWebVitals from "./reportWebVitals";
|
||||||
import { BaseModule, WKApp } from '@tsdaodao/base';
|
import { BaseModule, WKApp } from "@tsdaodao/base";
|
||||||
import { LoginModule } from '@tsdaodao/login';
|
import { LoginModule } from "@tsdaodao/login";
|
||||||
import { DataSourceModule } from '@tsdaodao/datasource';
|
import { DataSourceModule } from "@tsdaodao/datasource";
|
||||||
import {ContactsModule} from '@tsdaodao/contacts';
|
import { ContactsModule } from "@tsdaodao/contacts";
|
||||||
|
|
||||||
const apiURL = "https://api.botgate.cn/v1/"
|
const apiURL = "https://api.botgate.cn/v1/";
|
||||||
|
|
||||||
if(!(window as any).__TAURI_IPC__) { // tauri环境
|
if ((window as any).__TAURI_IPC__) {
|
||||||
if(process.env.NODE_ENV === "development") {
|
// tauri环境
|
||||||
WKApp.apiClient.config.apiURL = apiURL
|
console.log("tauri环境");
|
||||||
}else {
|
WKApp.apiClient.config.apiURL = apiURL;
|
||||||
WKApp.apiClient.config.apiURL = "/api/v1/" // 正式环境地址 (通用打包镜像,用此相对地址),打包出来的镜像可以通过API_URL环境变量来修改API地址
|
} else if ((window as any)?.__POWERED_ELECTRON__) {
|
||||||
|
console.log("__POWERED_ELECTRON__环境");
|
||||||
|
WKApp.apiClient.config.apiURL = apiURL;
|
||||||
|
} else {
|
||||||
|
if (process.env.NODE_ENV === "development") {
|
||||||
|
WKApp.apiClient.config.apiURL = apiURL;
|
||||||
|
} else {
|
||||||
|
WKApp.apiClient.config.apiURL = "/api/v1/"; // 正式环境地址 (通用打包镜像,用此相对地址),打包出来的镜像可以通过API_URL环境变量来修改API地址
|
||||||
}
|
}
|
||||||
}else {
|
|
||||||
console.log("tauri环境")
|
|
||||||
WKApp.apiClient.config.apiURL = apiURL
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WKApp.apiClient.config.tokenCallback = () => {
|
||||||
|
return WKApp.loginInfo.token;
|
||||||
|
};
|
||||||
|
WKApp.config.appVersion = `${process.env.REACT_APP_VERSION || "0.0.0"}`;
|
||||||
|
|
||||||
|
WKApp.loginInfo.load(); // 加载登录信息
|
||||||
|
|
||||||
WKApp.apiClient.config.tokenCallback = ()=> {
|
|
||||||
return WKApp.loginInfo.token
|
|
||||||
}
|
|
||||||
WKApp.config.appVersion = `${process.env.REACT_APP_VERSION || "0.0.0"}`
|
|
||||||
|
|
||||||
WKApp.loginInfo.load() // 加载登录信息
|
|
||||||
|
|
||||||
WKApp.shared.registerModule(new BaseModule()); // 基础模块
|
WKApp.shared.registerModule(new BaseModule()); // 基础模块
|
||||||
WKApp.shared.registerModule(new DataSourceModule()) // 数据源模块
|
WKApp.shared.registerModule(new DataSourceModule()); // 数据源模块
|
||||||
WKApp.shared.registerModule(new LoginModule()); // 登录模块
|
WKApp.shared.registerModule(new LoginModule()); // 登录模块
|
||||||
WKApp.shared.registerModule(new ContactsModule()); // 联系模块
|
WKApp.shared.registerModule(new ContactsModule()); // 联系模块
|
||||||
|
|
||||||
WKApp.shared.startup() // app启动
|
WKApp.shared.startup(); // app启动
|
||||||
|
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<App />
|
<App />
|
||||||
</React.StrictMode>,
|
</React.StrictMode>,
|
||||||
document.getElementById('root')
|
document.getElementById("root")
|
||||||
);
|
);
|
||||||
reportWebVitals();
|
reportWebVitals();
|
||||||
|
|
||||||
|
15
apps/web/tsconfig.e.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"exclude": ["node_modules", "src"],
|
||||||
|
"include": ["src-election/**/*"],
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "./out-election",
|
||||||
|
"module": "commonjs",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"noEmit": false,
|
||||||
|
"baseUrl": "./",
|
||||||
|
"skipLibCheck":true,
|
||||||
|
"sourceMap": false
|
||||||
|
}
|
||||||
|
}
|
10
package.json
@ -6,11 +6,15 @@
|
|||||||
],
|
],
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"//": "See https://github.com/facebook/create-react-app/issues/11773",
|
"//": "See https://github.com/facebook/create-react-app/issues/11773",
|
||||||
"react-error-overlay": "6.0.9"
|
"react-error-overlay": "6.0.11"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "turbo run build",
|
"bootstrap": "yarn install",
|
||||||
"dev": "turbo run dev --parallel",
|
"dev": "turbo run dev --parallel",
|
||||||
|
"dev-ele": "turbo run dev-ele --parallel",
|
||||||
|
"build": "turbo run build",
|
||||||
|
"build-ele": "turbo run build-ele",
|
||||||
|
"clean": "turbo run clean && rimraf node_modules",
|
||||||
"lint": "turbo run lint",
|
"lint": "turbo run lint",
|
||||||
"format": "prettier --write \"**/*.{ts,tsx,md}\""
|
"format": "prettier --write \"**/*.{ts,tsx,md}\""
|
||||||
},
|
},
|
||||||
@ -18,8 +22,8 @@
|
|||||||
"eslint": "^7.32.0",
|
"eslint": "^7.32.0",
|
||||||
"eslint-config-custom": "*",
|
"eslint-config-custom": "*",
|
||||||
"prettier": "^2.5.1",
|
"prettier": "^2.5.1",
|
||||||
|
"rimraf": "^5.0.5",
|
||||||
"turbo": "latest"
|
"turbo": "latest"
|
||||||
},
|
},
|
||||||
"dependencies": {},
|
|
||||||
"version": "0.0.0"
|
"version": "0.0.0"
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
extends: ["next", "turbo", "prettier"],
|
extends: ["next", "turbo", "prettier", "@typescript-eslint/parser"],
|
||||||
rules: {
|
rules: {
|
||||||
"@next/next/no-html-link-for-pages": "off",
|
"@next/next/no-html-link-for-pages": "off",
|
||||||
"react/jsx-key": "off",
|
"react/jsx-key": "off",
|
||||||
|
"@next/next/no-img-element": "off",
|
||||||
|
"no-template-curly-in-string": "off"
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -3,7 +3,11 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"scripts": {
|
||||||
|
"clean": "rimraf node_modules out-election"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@typescript-eslint/parser": "^6.16.0",
|
||||||
"eslint-config-next": "^12.0.8",
|
"eslint-config-next": "^12.0.8",
|
||||||
"eslint-config-prettier": "^8.3.0",
|
"eslint-config-prettier": "^8.3.0",
|
||||||
"eslint-plugin-react": "7.28.0",
|
"eslint-plugin-react": "7.28.0",
|
||||||
|
@ -13,8 +13,9 @@
|
|||||||
"axios": "^0.25.0",
|
"axios": "^0.25.0",
|
||||||
"benz-amr-recorder": "^1.1.3",
|
"benz-amr-recorder": "^1.1.3",
|
||||||
"classnames": "^2.3.1",
|
"classnames": "^2.3.1",
|
||||||
|
"electron-log": "^5.1.1",
|
||||||
|
"electron-screenshots": "^0.5.26",
|
||||||
"hotkeys-js": "^3.8.7",
|
"hotkeys-js": "^3.8.7",
|
||||||
"wukongimjssdk": "^1.1.2",
|
|
||||||
"moment": "^2.29.3",
|
"moment": "^2.29.3",
|
||||||
"qrcode.react": "^1.0.1",
|
"qrcode.react": "^1.0.1",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
@ -22,7 +23,8 @@
|
|||||||
"react-mentions": "^4.3.1",
|
"react-mentions": "^4.3.1",
|
||||||
"react-scroll": "^1.8.4",
|
"react-scroll": "^1.8.4",
|
||||||
"react-spinners": "^0.11.0",
|
"react-spinners": "^0.11.0",
|
||||||
"react-viewer": "^3.2.2"
|
"react-viewer": "^3.2.2",
|
||||||
|
"wukongimjssdk": "^1.2.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/react-avatar-editor": "^13.0.0",
|
"@types/react-avatar-editor": "^13.0.0",
|
||||||
|
@ -457,9 +457,9 @@ export default class WKApp extends ProviderListener {
|
|||||||
for (const friendApplyObj of friendApplyObjs) {
|
for (const friendApplyObj of friendApplyObjs) {
|
||||||
const f = new FriendApply()
|
const f = new FriendApply()
|
||||||
f.uid = friendApplyObj.uid
|
f.uid = friendApplyObj.uid
|
||||||
f.name = friendApplyObj.name
|
f.to_name = friendApplyObj.to_name
|
||||||
f.remark = friendApplyObj.remark
|
f.remark = friendApplyObj.remark
|
||||||
f.state = friendApplyObj.state
|
f.status = friendApplyObj.status
|
||||||
f.token = friendApplyObj.token
|
f.token = friendApplyObj.token
|
||||||
f.unread = friendApplyObj.unread
|
f.unread = friendApplyObj.unread
|
||||||
f.createdAt = friendApplyObj.createdAt
|
f.createdAt = friendApplyObj.createdAt
|
||||||
@ -472,35 +472,41 @@ export default class WKApp extends ProviderListener {
|
|||||||
return friendApplys
|
return friendApplys
|
||||||
}
|
}
|
||||||
|
|
||||||
public getFriendApplysUnreadCount() {
|
public async getFriendApplysUnreadCount() {
|
||||||
const friendApplys = this.getFriendApplys()
|
// const friendApplys = this.getFriendApplys()
|
||||||
let unreadCount = 0
|
let unreadCount = 0
|
||||||
if (friendApplys && friendApplys.length > 0) {
|
// if (friendApplys && friendApplys.length > 0) {
|
||||||
for (const friendApply of friendApplys) {
|
// for (const friendApply of friendApplys) {
|
||||||
if (friendApply.unread) {
|
// if (friendApply.unread) {
|
||||||
unreadCount++
|
// unreadCount++
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
if (!WKApp.loginInfo.isLogined()) {
|
||||||
|
return unreadCount;
|
||||||
|
}
|
||||||
|
const res = await WKApp.apiClient.get(`/user/reddot/friendApply`);
|
||||||
|
unreadCount = res.count;
|
||||||
return unreadCount
|
return unreadCount
|
||||||
}
|
}
|
||||||
|
|
||||||
public friendApplyMarkAllReaded() {
|
public async friendApplyMarkAllReaded(): Promise<void> {
|
||||||
let friendApplys = this.getFriendApplys()
|
// let friendApplys = this.getFriendApplys()
|
||||||
if (!friendApplys) {
|
// if (!friendApplys) {
|
||||||
friendApplys = new Array<FriendApply>()
|
// friendApplys = new Array<FriendApply>()
|
||||||
}
|
// }
|
||||||
var change = false
|
// var change = false
|
||||||
for (const friendApply of friendApplys) {
|
// for (const friendApply of friendApplys) {
|
||||||
if (friendApply.unread) {
|
// if (friendApply.unread) {
|
||||||
friendApply.unread = false
|
// friendApply.unread = false
|
||||||
change = true
|
// change = true
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
if (change) {
|
// if (change) {
|
||||||
WKApp.loginInfo.setStorageItem(this.getFriendApplysKey(), JSON.stringify(friendApplys))
|
// WKApp.loginInfo.setStorageItem(this.getFriendApplysKey(), JSON.stringify(friendApplys))
|
||||||
WKApp.endpointManager.invokes(EndpointCategory.friendApplyDataChange)
|
// WKApp.endpointManager.invokes(EndpointCategory.friendApplyDataChange)
|
||||||
}
|
// }
|
||||||
|
await WKApp.apiClient.delete(`/user/reddot/friendApply`);
|
||||||
}
|
}
|
||||||
|
|
||||||
public addFriendApply(friendApply: FriendApply) {
|
public addFriendApply(friendApply: FriendApply) {
|
||||||
@ -572,13 +578,14 @@ export enum FriendApplyState {
|
|||||||
}
|
}
|
||||||
// 好友申请
|
// 好友申请
|
||||||
export class FriendApply {
|
export class FriendApply {
|
||||||
uid!: string
|
uid!: string;
|
||||||
name!: string
|
to_uid!: string;
|
||||||
remark?: string
|
to_name!: string;
|
||||||
token?: string
|
remark?: string;
|
||||||
state!: FriendApplyState
|
token?: string;
|
||||||
unread: boolean = false // 是否未读
|
status!: FriendApplyState;
|
||||||
createdAt!: number // 创建时间
|
unread: boolean = false; // 是否未读
|
||||||
|
createdAt!: number; // 创建时间
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BIN
packages/tsdaodaobase/src/assets/msg-tip.mp3
Normal file
@ -1,58 +1,92 @@
|
|||||||
import { FriendApplyState, WKApp, WKViewQueueHeader, Provider } from "@tsdaodao/base";
|
import {
|
||||||
|
FriendApplyState,
|
||||||
|
WKApp,
|
||||||
|
WKViewQueueHeader,
|
||||||
|
Provider,
|
||||||
|
} from "@tsdaodao/base";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Component, ReactNode } from "react";
|
import { Component, ReactNode } from "react";
|
||||||
import { Button } from '@douyinfe/semi-ui';
|
import { Button } from "@douyinfe/semi-ui";
|
||||||
import "./index.css"
|
import "./index.css";
|
||||||
import { NewFriendVM } from "./vm";
|
import { NewFriendVM } from "./vm";
|
||||||
import "./index.css"
|
import "./index.css";
|
||||||
import { FriendAdd } from "../FriendAdd";
|
import { FriendAdd } from "../FriendAdd";
|
||||||
|
|
||||||
export class NewFriend extends Component {
|
export class NewFriend extends Component {
|
||||||
|
render(): ReactNode {
|
||||||
render(): ReactNode {
|
return (
|
||||||
return <Provider create={() => {
|
<Provider
|
||||||
return new NewFriendVM()
|
create={() => {
|
||||||
}} render={(vm: NewFriendVM) => {
|
return new NewFriendVM();
|
||||||
|
}}
|
||||||
return <div className="wk-newfriend">
|
render={(vm: NewFriendVM) => {
|
||||||
<WKViewQueueHeader title="新朋友" onBack={() => {
|
return (
|
||||||
WKApp.routeLeft.pop()
|
<div className="wk-newfriend">
|
||||||
}} action={<div className="wk-viewqueueheader-content-action">
|
<WKViewQueueHeader
|
||||||
<Button size="small" onClick={()=>{
|
title="新朋友"
|
||||||
WKApp.routeLeft.push(<FriendAdd onBack={()=>{
|
onBack={() => {
|
||||||
WKApp.routeLeft.pop()
|
WKApp.routeLeft.pop();
|
||||||
}}></FriendAdd>)
|
}}
|
||||||
}} >添加好友</Button>
|
action={
|
||||||
</div>}></WKViewQueueHeader>
|
<div className="wk-viewqueueheader-content-action">
|
||||||
<div className="wk-newfriend-content">
|
<Button
|
||||||
<ul>
|
size="small"
|
||||||
{
|
onClick={() => {
|
||||||
vm.friendApplys.map((f) => {
|
WKApp.routeLeft.push(
|
||||||
return <li key={f.uid} >
|
<FriendAdd
|
||||||
<div className="wk-newfriend-content-avatar">
|
onBack={() => {
|
||||||
<img src={WKApp.shared.avatarUser(f.uid)}></img>
|
WKApp.routeLeft.pop();
|
||||||
</div>
|
}}
|
||||||
<div className="wk-newfriend-content-title">
|
></FriendAdd>
|
||||||
<div className="wk-newfriend-content-title-name">
|
);
|
||||||
{f.name}
|
}}
|
||||||
</div>
|
>
|
||||||
<div className="wk-newfriend-content-title-remark">
|
添加好友
|
||||||
{f.remark}
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
}
|
||||||
<div className="wk-newfriend-content-action">
|
></WKViewQueueHeader>
|
||||||
<Button loading={vm.currentFriendApply?.uid === f.uid && vm.sureLoading } disabled={f.state==FriendApplyState.accepted} onClick={()=>{
|
<div className="wk-newfriend-content">
|
||||||
vm.friendSure(f)
|
<ul>
|
||||||
}}>{f.state==FriendApplyState.accepted?"已添加":"确认"}</Button>
|
{vm.friendApplys.map((f) => {
|
||||||
</div>
|
return (
|
||||||
</li>
|
<li key={f.to_uid}>
|
||||||
})
|
<div className="wk-newfriend-content-avatar">
|
||||||
}
|
<img src={WKApp.shared.avatarUser(f.to_uid)}></img>
|
||||||
</ul>
|
</div>
|
||||||
</div>
|
<div className="wk-newfriend-content-title">
|
||||||
|
<div className="wk-newfriend-content-title-name">
|
||||||
|
{f.to_name}
|
||||||
|
</div>
|
||||||
|
<div className="wk-newfriend-content-title-remark">
|
||||||
|
{f.remark}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="wk-newfriend-content-action">
|
||||||
|
<Button
|
||||||
|
loading={
|
||||||
|
vm.currentFriendApply?.to_uid === f.to_uid &&
|
||||||
|
vm.sureLoading
|
||||||
|
}
|
||||||
|
disabled={f.status == FriendApplyState.accepted}
|
||||||
|
onClick={() => {
|
||||||
|
vm.friendSure(f);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{f.status == FriendApplyState.accepted
|
||||||
|
? "已添加"
|
||||||
|
: "确认"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}}>
|
);
|
||||||
|
}}
|
||||||
</Provider>
|
></Provider>
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -1,33 +1,83 @@
|
|||||||
|
import { WKSDK, Message, CMDContent } from "wukongimjssdk";
|
||||||
import { FriendApplyState, WKApp, ProviderListener } from "@tsdaodao/base";
|
import { FriendApplyState, WKApp, ProviderListener } from "@tsdaodao/base";
|
||||||
import { FriendApply } from "@tsdaodao/base";
|
import { FriendApply } from "@tsdaodao/base";
|
||||||
|
|
||||||
|
|
||||||
export class NewFriendVM extends ProviderListener {
|
export class NewFriendVM extends ProviderListener {
|
||||||
friendApplys: FriendApply[] = []
|
friendApplys: FriendApply[] = [];
|
||||||
sureLoading:boolean = false
|
sureLoading: boolean = false;
|
||||||
currentFriendApply?:FriendApply
|
currentFriendApply?: FriendApply;
|
||||||
|
|
||||||
didMount(): void {
|
async didMount(): Promise<void> {
|
||||||
|
WKApp.shared.friendApplyMarkAllReaded();
|
||||||
|
|
||||||
WKApp.shared.friendApplyMarkAllReaded()
|
this.friendApplys = await this.getFriendApply();
|
||||||
|
if (this.friendApplys.length === 0) {
|
||||||
this.friendApplys = WKApp.shared.getFriendApplys()
|
this.clearFriendApply();
|
||||||
this.notifyListener()
|
|
||||||
}
|
}
|
||||||
|
this.notifyListener();
|
||||||
|
// 监听好友申请
|
||||||
|
WKSDK.shared().chatManager.addCMDListener(this.friendRequestCMDListener);
|
||||||
|
}
|
||||||
|
|
||||||
friendSure(apply: FriendApply) {
|
didUnMount(): void {
|
||||||
this.sureLoading = true
|
// 监听好友申请
|
||||||
this.currentFriendApply = apply
|
WKSDK.shared().chatManager.removeCMDListener(this.friendRequestCMDListener);
|
||||||
this.notifyListener()
|
}
|
||||||
|
|
||||||
WKApp.dataSource.commonDataSource.friendSure(apply.token || "").then(() => {
|
friendSure(apply: FriendApply) {
|
||||||
apply.state = FriendApplyState.accepted
|
this.sureLoading = true;
|
||||||
WKApp.shared.updateFriendApply(apply)
|
this.currentFriendApply = apply;
|
||||||
this.sureLoading = false
|
this.notifyListener();
|
||||||
this.notifyListener()
|
|
||||||
}).catch(() => {
|
WKApp.dataSource.commonDataSource
|
||||||
this.sureLoading = false
|
.friendSure(apply.token || "")
|
||||||
this.notifyListener()
|
.then(() => {
|
||||||
})
|
apply.status = FriendApplyState.accepted;
|
||||||
|
WKApp.shared.updateFriendApply(apply);
|
||||||
|
this.sureLoading = false;
|
||||||
|
this.notifyListener();
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.sureLoading = false;
|
||||||
|
this.notifyListener();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getFriendApply(): Promise<FriendApply[]> {
|
||||||
|
const fromData = {
|
||||||
|
page_index: 1,
|
||||||
|
page_size: 999,
|
||||||
|
};
|
||||||
|
const res = await WKApp.apiClient.get("/friend/apply", {
|
||||||
|
param: fromData,
|
||||||
|
});
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
async delFriendApply(apply: FriendApply): Promise<void> {
|
||||||
|
WKApp.apiClient
|
||||||
|
.delete(`/friend/apply/${apply.to_uid}`)
|
||||||
|
.then(async () => {
|
||||||
|
this.friendApplys = await this.getFriendApply();
|
||||||
|
this.sureLoading = false;
|
||||||
|
this.notifyListener();
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.sureLoading = false;
|
||||||
|
this.notifyListener();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async clearFriendApply(): Promise<void> {
|
||||||
|
await WKApp.apiClient.delete(`/user/reddot/friendApply`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async friendRequestCMDListener(message: Message) {
|
||||||
|
console.log("收到CMD->", message);
|
||||||
|
const cmdContent = message.content as CMDContent;
|
||||||
|
if (cmdContent.cmd === "friendRequest") {
|
||||||
|
this.friendApplys = await this.getFriendApply();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -1,57 +1,105 @@
|
|||||||
import { EndpointCategory, IconListItem, IModule, WKApp, ThemeMode } from "@tsdaodao/base"
|
import {
|
||||||
import React from "react"
|
EndpointCategory,
|
||||||
import Blacklist from "./Blacklist"
|
IconListItem,
|
||||||
import { FriendAdd } from "./FriendAdd"
|
IModule,
|
||||||
import GroupSave from "./GroupSave"
|
WKApp,
|
||||||
import { NewFriend } from "./NewFriend"
|
ThemeMode,
|
||||||
import { ContactsListManager } from "./Service/ContactsListManager"
|
} from "@tsdaodao/base";
|
||||||
|
import React from "react";
|
||||||
|
import Blacklist from "./Blacklist";
|
||||||
|
import { FriendAdd } from "./FriendAdd";
|
||||||
|
import GroupSave from "./GroupSave";
|
||||||
|
import { NewFriend } from "./NewFriend";
|
||||||
|
import { ContactsListManager } from "./Service/ContactsListManager";
|
||||||
|
|
||||||
export default class ContactsModule implements IModule {
|
export default class ContactsModule implements IModule {
|
||||||
|
id(): string {
|
||||||
|
return "ContactsModule";
|
||||||
|
}
|
||||||
|
init(): void {
|
||||||
|
console.log("【ContactsModule】初始化");
|
||||||
|
|
||||||
id(): string {
|
WKApp.endpointManager.setMethod(
|
||||||
return "ContactsModule"
|
"contacts.friendapply.change",
|
||||||
|
() => {
|
||||||
|
ContactsListManager.shared.refreshList();
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: EndpointCategory.friendApplyDataChange,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// 获取好友未申请添加数量
|
||||||
|
let unreadCount = 0;
|
||||||
|
if (WKApp.loginInfo.isLogined()) {
|
||||||
|
WKApp.apiClient.get(`/user/reddot/friendApply`).then((res) => {
|
||||||
|
unreadCount = res.count;
|
||||||
|
WKApp.menus.refresh();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
init(): void {
|
|
||||||
console.log("【ContactsModule】初始化")
|
|
||||||
|
|
||||||
|
WKApp.endpoints.registerContactsHeader("friends.new", (param: any) => {
|
||||||
|
return (
|
||||||
|
<IconListItem
|
||||||
|
badge={unreadCount}
|
||||||
|
title="新朋友"
|
||||||
|
icon={require("./assets/friend_new.png")}
|
||||||
|
backgroudColor={"var(--wk-color-secondary)"}
|
||||||
|
onClick={() => {
|
||||||
|
WKApp.routeLeft.push(<NewFriend></NewFriend>);
|
||||||
|
}}
|
||||||
|
></IconListItem>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
WKApp.endpointManager.setMethod("contacts.friendapply.change", () => {
|
WKApp.endpoints.registerContactsHeader("groups.save", (param: any) => {
|
||||||
ContactsListManager.shared.refreshList()
|
return (
|
||||||
}, {
|
<IconListItem
|
||||||
category: EndpointCategory.friendApplyDataChange,
|
title="保存的群"
|
||||||
})
|
icon={require("./assets/icon_group_save.png")}
|
||||||
|
backgroudColor={"var(--wk-color-secondary)"}
|
||||||
|
onClick={() => {
|
||||||
|
WKApp.routeLeft.push(<GroupSave></GroupSave>);
|
||||||
|
}}
|
||||||
|
></IconListItem>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
WKApp.endpoints.registerContactsHeader("friends.new", (param: any) => {
|
WKApp.endpoints.registerContactsHeader(
|
||||||
return <IconListItem badge={WKApp.shared.getFriendApplysUnreadCount()} title="新朋友" icon={require("./assets/friend_new.png")} backgroudColor={"var(--wk-color-secondary)"} onClick={() => {
|
"contacts.blacklist",
|
||||||
WKApp.routeLeft.push(<NewFriend></NewFriend>)
|
(param: any) => {
|
||||||
}} ></IconListItem>
|
return (
|
||||||
})
|
<IconListItem
|
||||||
|
title="黑名单"
|
||||||
|
icon={require("./assets/blacklist.png")}
|
||||||
|
backgroudColor={"var(--wk-color-secondary)"}
|
||||||
|
onClick={() => {
|
||||||
|
WKApp.routeLeft.push(<Blacklist></Blacklist>);
|
||||||
|
}}
|
||||||
|
></IconListItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
WKApp.endpoints.registerContactsHeader("groups.save", (param: any) => {
|
WKApp.shared.chatMenusRegister("chatmenus.addfriend", (param) => {
|
||||||
return <IconListItem title="保存的群" icon={require("./assets/icon_group_save.png")} backgroudColor={"var(--wk-color-secondary)"} onClick={() => {
|
const isDark = WKApp.config.themeMode === ThemeMode.dark;
|
||||||
WKApp.routeLeft.push(<GroupSave></GroupSave>)
|
return {
|
||||||
}}></IconListItem>
|
title: "添加朋友",
|
||||||
})
|
icon: require(`${
|
||||||
|
isDark
|
||||||
WKApp.endpoints.registerContactsHeader("contacts.blacklist", (param: any) => {
|
? "./assets/popmenus_friendadd_dark.png"
|
||||||
return <IconListItem title="黑名单" icon={require("./assets/blacklist.png")} backgroudColor={"var(--wk-color-secondary)"} onClick={() => {
|
: "./assets/popmenus_friendadd.png"
|
||||||
WKApp.routeLeft.push(<Blacklist></Blacklist>)
|
}`),
|
||||||
}}></IconListItem>
|
onClick: () => {
|
||||||
})
|
WKApp.routeLeft.push(
|
||||||
|
<FriendAdd
|
||||||
WKApp.shared.chatMenusRegister("chatmenus.addfriend",(param)=>{
|
onBack={() => {
|
||||||
const isDark = WKApp.config.themeMode === ThemeMode.dark
|
WKApp.routeLeft.pop();
|
||||||
return {
|
}}
|
||||||
title: "添加朋友",
|
></FriendAdd>
|
||||||
icon: require(`${isDark?"./assets/popmenus_friendadd_dark.png":"./assets/popmenus_friendadd.png"}`),
|
);
|
||||||
onClick:()=>{
|
},
|
||||||
WKApp.routeLeft.push(<FriendAdd onBack={()=>{
|
};
|
||||||
WKApp.routeLeft.pop()
|
});
|
||||||
}}></FriendAdd>)
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
15
turbo.json
@ -1,14 +1,25 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://turborepo.org/schema.json",
|
"$schema": "https://turborepo.org/schema.json",
|
||||||
"pipeline": {
|
"pipeline": {
|
||||||
|
"dev": {
|
||||||
|
"cache": false
|
||||||
|
},
|
||||||
|
"dev-ele": {
|
||||||
|
"dependsOn": ["^dev"],
|
||||||
|
"cache": false
|
||||||
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"outputs": [".next/**"]
|
"outputs": [".next/**"]
|
||||||
},
|
},
|
||||||
|
"build-ele": {
|
||||||
|
"outputs": [".next/**"],
|
||||||
|
"dependsOn": ["^build"]
|
||||||
|
},
|
||||||
"lint": {
|
"lint": {
|
||||||
"outputs": []
|
"outputs": []
|
||||||
},
|
},
|
||||||
"dev": {
|
"clean": {
|
||||||
"cache": false
|
"dependsOn": ["^clean"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|