243 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
			
		
		
	
	
			243 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
/* eslint-disable @typescript-eslint/no-unused-vars */
 | 
						|
/* eslint-disable @typescript-eslint/no-explicit-any */
 | 
						|
import {
 | 
						|
  app,
 | 
						|
  BrowserWindow,
 | 
						|
  ipcMain,
 | 
						|
  Menu,
 | 
						|
  Notification,
 | 
						|
  screen,
 | 
						|
  Tray,
 | 
						|
} from "electron";
 | 
						|
import path from "node:path";
 | 
						|
import { fileURLToPath } from "node:url";
 | 
						|
import { io } from "socket.io-client";
 | 
						|
import {
 | 
						|
  addEmail,
 | 
						|
  deleteEmail,
 | 
						|
  deleteMessage,
 | 
						|
  fetchEmails,
 | 
						|
  fetchMessages,
 | 
						|
} from "../src/apis";
 | 
						|
import { createMailWindow } from "./windows/mails.window";
 | 
						|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
 | 
						|
 | 
						|
// The built directory structure
 | 
						|
//
 | 
						|
// ├─┬─┬ dist
 | 
						|
// │ │ └── index.html
 | 
						|
// │ │
 | 
						|
// │ ├─┬ dist-electron
 | 
						|
// │ │ ├── main.js
 | 
						|
// │ │ └── preload.mjs
 | 
						|
// │
 | 
						|
process.env.APP_ROOT = path.join(__dirname, "..");
 | 
						|
 | 
						|
// 🚧 Use ['ENV_NAME'] avoid vite:define plugin - Vite@2.x
 | 
						|
export const VITE_DEV_SERVER_URL = process.env["VITE_DEV_SERVER_URL"];
 | 
						|
export const MAIN_DIST = path.join(process.env.APP_ROOT, "dist-electron");
 | 
						|
export const RENDERER_DIST = path.join(process.env.APP_ROOT, "dist");
 | 
						|
 | 
						|
process.env.VITE_PUBLIC = VITE_DEV_SERVER_URL
 | 
						|
  ? path.join(process.env.APP_ROOT, "public")
 | 
						|
  : RENDERER_DIST;
 | 
						|
 | 
						|
let win: BrowserWindow | null;
 | 
						|
let tray;
 | 
						|
let isQuiting = false;
 | 
						|
 | 
						|
Menu.setApplicationMenu(null);
 | 
						|
 | 
						|
function createWindow() {
 | 
						|
  // Lấy thông tin tất cả các màn hình
 | 
						|
  const displays = screen.getAllDisplays();
 | 
						|
 | 
						|
  // Lấy vị trí con trỏ chuột
 | 
						|
  const cursorPoint = screen.getCursorScreenPoint();
 | 
						|
 | 
						|
  // Tìm màn hình có chứa con trỏ chuột
 | 
						|
  let display = displays.find((display) => {
 | 
						|
    const { x, y, width, height } = display.bounds;
 | 
						|
    return (
 | 
						|
      cursorPoint.x >= x &&
 | 
						|
      cursorPoint.x <= x + width &&
 | 
						|
      cursorPoint.y >= y &&
 | 
						|
      cursorPoint.y <= y + height
 | 
						|
    );
 | 
						|
  });
 | 
						|
 | 
						|
  // Nếu không tìm thấy màn hình chứa con trỏ, sử dụng màn hình chính
 | 
						|
  if (!display) {
 | 
						|
    display = screen.getPrimaryDisplay();
 | 
						|
  }
 | 
						|
 | 
						|
  const { width, height } = display.workAreaSize;
 | 
						|
 | 
						|
  const browserWidth = 400;
 | 
						|
  const browserHeight = 400;
 | 
						|
 | 
						|
  // Vị trí cửa sổ ở góc phải dưới của màn hình đã chọn
 | 
						|
 | 
						|
  win = new BrowserWindow({
 | 
						|
    width: browserWidth,
 | 
						|
    height: browserHeight,
 | 
						|
    x: 0, // Đặt cửa sổ ở góc phải
 | 
						|
    y: 0, // Đặt cửa sổ ở góc dưới
 | 
						|
    alwaysOnTop: true, // Cửa sổ luôn nằm trên các cửa sổ khác
 | 
						|
    resizable: true, // Không cho phép thay đổi kích thước
 | 
						|
    icon: path.join(
 | 
						|
      app.isPackaged ? process.resourcesPath : ".",
 | 
						|
      "build/icons/icon24x24.png"
 | 
						|
    ),
 | 
						|
    // icon: path.join(process.env.VITE_PUBLIC ,'assets', 'icon.png'),
 | 
						|
    webPreferences: {
 | 
						|
      preload: path.join(__dirname, "preload.mjs"),
 | 
						|
    },
 | 
						|
  });
 | 
						|
 | 
						|
  // Test active push message to Renderer-process.
 | 
						|
  win.webContents.on("did-finish-load", () => {
 | 
						|
    win?.webContents.send("main-process-message", new Date().toLocaleString());
 | 
						|
  });
 | 
						|
 | 
						|
  win.setPosition(width - browserWidth, height - browserHeight);
 | 
						|
 | 
						|
  if (VITE_DEV_SERVER_URL) {
 | 
						|
    win.loadURL(VITE_DEV_SERVER_URL);
 | 
						|
  } else {
 | 
						|
    // win.loadFile('dist/index.html')
 | 
						|
    win.loadFile(path.join(RENDERER_DIST, "index.html"));
 | 
						|
  }
 | 
						|
 | 
						|
  // Khi bấm dấu X
 | 
						|
  win.on("close", (event) => {
 | 
						|
    if (!isQuiting) {
 | 
						|
      event.preventDefault();
 | 
						|
      win?.hide();
 | 
						|
    }
 | 
						|
  });
 | 
						|
}
 | 
						|
 | 
						|
function createTray() {
 | 
						|
  tray = new Tray(
 | 
						|
    path.join(process.env.VITE_PUBLIC, "assets", "icon16x16.png")
 | 
						|
  );
 | 
						|
 | 
						|
  const contextMenu = Menu.buildFromTemplate([
 | 
						|
    {
 | 
						|
      label: "Show",
 | 
						|
      click: () => {
 | 
						|
        win?.show();
 | 
						|
      },
 | 
						|
    },
 | 
						|
    {
 | 
						|
      label: "Quit",
 | 
						|
      click: () => {
 | 
						|
        isQuiting = true;
 | 
						|
        app.quit();
 | 
						|
      },
 | 
						|
    },
 | 
						|
  ]);
 | 
						|
 | 
						|
  tray.setToolTip("Zulip notes");
 | 
						|
  tray.setContextMenu(contextMenu);
 | 
						|
 | 
						|
  tray.on("double-click", () => {
 | 
						|
    win?.show();
 | 
						|
  });
 | 
						|
}
 | 
						|
 | 
						|
// Quit when all windows are closed, except on macOS. There, it's common
 | 
						|
// for applications and their menu bar to stay active until the user quits
 | 
						|
// explicitly with Cmd + Q.
 | 
						|
app.on("window-all-closed", () => {
 | 
						|
  if (process.platform !== "darwin") {
 | 
						|
    app.quit();
 | 
						|
    win = null;
 | 
						|
  }
 | 
						|
});
 | 
						|
 | 
						|
app.on("activate", () => {
 | 
						|
  // On OS X it's common to re-create a window in the app when the
 | 
						|
  // dock icon is clicked and there are no other windows open.
 | 
						|
  if (BrowserWindow.getAllWindows().length === 0) {
 | 
						|
    createWindow();
 | 
						|
  }
 | 
						|
});
 | 
						|
 | 
						|
app.whenReady().then(() => {
 | 
						|
  // tạo cửa sổ chính
 | 
						|
  createWindow();
 | 
						|
  // tạo cửa sổ chạy nền
 | 
						|
  createTray();
 | 
						|
});
 | 
						|
 | 
						|
// IPC Main Events
 | 
						|
ipcMain.on("open-devtools", (event) => {
 | 
						|
  const webContents = event.sender;
 | 
						|
  webContents.openDevTools({ mode: "detach" });
 | 
						|
});
 | 
						|
 | 
						|
// Xử lý connect socket
 | 
						|
ipcMain.handle("connect-socket", async (_) => {
 | 
						|
  const socket = io("https://zulip.ipsupply.com.au", {
 | 
						|
    path: "/apac-custom/socket.io",
 | 
						|
    secure: true,
 | 
						|
    query: { token: import.meta.env.VITE_API_KEY },
 | 
						|
    rejectUnauthorized: false,
 | 
						|
  });
 | 
						|
 | 
						|
  if (!socket.connected) {
 | 
						|
    socket.connect();
 | 
						|
  }
 | 
						|
 | 
						|
  socket.on("connect", () => {
 | 
						|
    console.log(socket.connected); // true
 | 
						|
  });
 | 
						|
 | 
						|
  socket.on("newNote", (data) => {
 | 
						|
    win?.webContents.send("newNote", data);
 | 
						|
  });
 | 
						|
});
 | 
						|
 | 
						|
ipcMain.handle("fetchMessages", async () => {
 | 
						|
  return await fetchMessages();
 | 
						|
});
 | 
						|
 | 
						|
ipcMain.handle("fetchEmails", async () => {
 | 
						|
  return await fetchEmails();
 | 
						|
});
 | 
						|
 | 
						|
ipcMain.handle("open-new-window", async () => {
 | 
						|
  createMailWindow({ RENDERER_DIST });
 | 
						|
});
 | 
						|
 | 
						|
ipcMain.handle("add-email", async (_, email: string) => {
 | 
						|
  return addEmail(email);
 | 
						|
});
 | 
						|
 | 
						|
ipcMain.handle("del-email", async (_, id: number) => {
 | 
						|
  return deleteEmail(id);
 | 
						|
});
 | 
						|
 | 
						|
ipcMain.handle("del-message", async (_, id: number) => {
 | 
						|
  return deleteMessage(id);
 | 
						|
});
 | 
						|
 | 
						|
ipcMain.handle("show-notification", async (_, { title, body }) => {
 | 
						|
  const notification = new Notification({
 | 
						|
    title,
 | 
						|
    body,
 | 
						|
    silent: false,
 | 
						|
  });
 | 
						|
 | 
						|
  notification.on("click", () => {
 | 
						|
    if (win && !isQuiting) {
 | 
						|
      win.show();
 | 
						|
      win.focus();
 | 
						|
    }
 | 
						|
  });
 | 
						|
 | 
						|
  notification.show();
 | 
						|
});
 |