beginnings of refactor
This commit is contained in:
parent
ac8f338d75
commit
3b5da6124f
63 changed files with 1697 additions and 2656 deletions
|
|
@ -1,172 +0,0 @@
|
|||
/* eslint @typescript-eslint/no-var-requires: "off" */
|
||||
|
||||
"use strict";
|
||||
|
||||
import { backgroundMitt } from '@/modules/emitter';
|
||||
const portAudio = require('naudiodon');
|
||||
|
||||
// Audio in and out stream objects.
|
||||
let ai: typeof portAudio.AudioIO | boolean = false;
|
||||
let ao: typeof portAudio.AudioIO | boolean = false;
|
||||
|
||||
// Whether activly recording.
|
||||
let record = false;
|
||||
|
||||
const audioContainer = {
|
||||
input: '',
|
||||
}
|
||||
|
||||
const audioOptions = {
|
||||
channelCount: 1,
|
||||
sampleFormat: 16,
|
||||
sampleRate: 16000,
|
||||
deviceId: -1,
|
||||
closeOnError: false,
|
||||
}
|
||||
|
||||
export const toggleRecord = (): void => { record = !record };
|
||||
|
||||
|
||||
export const fetchAudioInput = (): Promise<Error | string> => (
|
||||
|
||||
new Promise((resolve, reject) => {
|
||||
|
||||
try {
|
||||
resolve(audioContainer.input);
|
||||
toggleRecord();
|
||||
} catch (e) {
|
||||
reject(new Error('Failed to fetch the audio.'))
|
||||
}
|
||||
|
||||
})
|
||||
)
|
||||
|
||||
|
||||
// Main audio function run by run.ts module.
|
||||
export function initAudioIO(): void {
|
||||
console.log("AUDIO:Starting io streams.")
|
||||
|
||||
if (!ai) {
|
||||
|
||||
// Initialize and start input stream.
|
||||
ai = new portAudio.AudioIO({ inOptions: audioOptions });
|
||||
ai.setEncoding("hex");
|
||||
ai.start();
|
||||
|
||||
// On each data chunk...
|
||||
ai.on('data', (chunk: string) => {
|
||||
|
||||
// If recording, we capture the data.
|
||||
if (record) {
|
||||
console.log('AUDIO:Recording...')
|
||||
audioContainer.input += chunk;
|
||||
}
|
||||
|
||||
// Else, we don't capture and also clear audioContainer.
|
||||
else {
|
||||
if (audioContainer.input.length) {
|
||||
audioContainer.input = "";
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
if (!ao) {
|
||||
|
||||
// Initialize and start input stream.
|
||||
ao = new portAudio.AudioIO({ outOptions: audioOptions });
|
||||
ao.start();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ---Audio playback--------------------------------------------
|
||||
|
||||
// Split Buffer into an array of len-sized Buffers.
|
||||
function bufSplit(buf: Buffer, len: number): Array<Buffer> {
|
||||
const chunks = [];
|
||||
let i = 0;
|
||||
let L = len;
|
||||
|
||||
while(i < buf.byteLength) {
|
||||
chunks.push(buf.slice(i, L));
|
||||
i = L;
|
||||
L += len;
|
||||
}
|
||||
|
||||
return chunks;
|
||||
}
|
||||
|
||||
// Audio playback.
|
||||
export function play(input: string): void {
|
||||
|
||||
// Format the audio.
|
||||
const audio = bufSplit(
|
||||
Buffer.from(input as string, 'hex'),
|
||||
8192
|
||||
);
|
||||
|
||||
// Called on end of write.
|
||||
const callback = () => {
|
||||
|
||||
// We stop audio playback anim.
|
||||
backgroundMitt.emit('ipc-renderer', {
|
||||
endpoint: 'stop-playback-anim'
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
write();
|
||||
|
||||
// Iterate through audio array and write buffers to portAudio writable.
|
||||
function write() {
|
||||
let chunk: Buffer;
|
||||
let ok = true;
|
||||
let i = 0;
|
||||
|
||||
do {
|
||||
chunk = audio[i];
|
||||
if (i === audio.length - 1) {
|
||||
// write last chunk.
|
||||
ao.write(chunk, null, callback);
|
||||
} else {
|
||||
// check for backpreassure.
|
||||
ok = ao.write(chunk, null);
|
||||
}
|
||||
i++;
|
||||
} while (i < audio.length && ok);
|
||||
|
||||
if (i < audio.length) {
|
||||
// Had to stop early!
|
||||
// Write some more once it drains.
|
||||
ao.once('drain', write);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
|
||||
// Get's called on window close.
|
||||
export async function stopStream() {
|
||||
console.log("AUDIO:Stopping audio stream.")
|
||||
if (ai) {
|
||||
try {
|
||||
await ai.quit()
|
||||
} catch(e){
|
||||
console.log('AUDIO: Failed to shutdown audio input.');
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
if (ao) {
|
||||
try {
|
||||
await ao.quit()
|
||||
} catch(e){
|
||||
console.log('AUDIO: Failed to shutdown audio output.');
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
import fs from 'fs';
|
||||
import { backgroundMitt } from "@/modules/emitter";
|
||||
import { SessionState, WindowState } from "@/types";
|
||||
import { app } from "electron";
|
||||
|
||||
const configPath = app.getPath("userData");
|
||||
|
||||
export const ipcEmit = (channel: string, payload: any) => {
|
||||
backgroundMitt.emit('ipc-renderer', {
|
||||
endpoint: channel,
|
||||
message: payload
|
||||
});
|
||||
}
|
||||
|
||||
export const loadState = (fileName: string): SessionState => {
|
||||
let state: SessionState;
|
||||
|
||||
try {
|
||||
state = JSON.parse(fs.readFileSync(configPath + fileName).toString());
|
||||
}
|
||||
|
||||
catch (error) {
|
||||
state = {
|
||||
key: false,
|
||||
newMessages: []
|
||||
}
|
||||
}
|
||||
|
||||
return state
|
||||
|
||||
}
|
||||
|
||||
export const loadWinState = (fileName: string): WindowState => {
|
||||
let state: WindowState;
|
||||
|
||||
try {
|
||||
state = JSON.parse(fs.readFileSync(configPath + fileName).toString());
|
||||
}
|
||||
|
||||
catch (error) {
|
||||
state = {
|
||||
width: 600,
|
||||
height: 500,
|
||||
x: null,
|
||||
y: null,
|
||||
}
|
||||
}
|
||||
|
||||
return state
|
||||
|
||||
}
|
||||
|
||||
// Save session or window state.
|
||||
export const saveToJson = (fileName: string, data: any) => {
|
||||
|
||||
fs.writeFile(configPath + fileName, JSON.stringify(data), (err) => {
|
||||
if (err) {
|
||||
console.log("Error when saving to json.")
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
import { app, dialog } from "electron";
|
||||
import { createWindow } from './window';
|
||||
import { stopStream } from './audio';
|
||||
import { backgroundMitt } from '@/modules/emitter';
|
||||
import useIpc from "@/background/ipc/index";
|
||||
const { autoUpdater } = require('electron-updater');
|
||||
|
||||
let win: boolean;
|
||||
|
||||
// Listen for window creation.
|
||||
backgroundMitt.on('window-active', (state: boolean) => {
|
||||
win = state;
|
||||
});
|
||||
|
||||
// Auto updating.
|
||||
autoUpdater.requestHeaders = { 'PRIVATE-TOKEN': 'mvvgWYwWnot4bisiQMh_' }
|
||||
|
||||
autoUpdater.on('update-available', (info: any) => {
|
||||
console.log(`Update available: ${info.version}`)
|
||||
})
|
||||
|
||||
autoUpdater.on('update-downloaded', (info: any) => {
|
||||
|
||||
const updateDialog = {
|
||||
type: 'info',
|
||||
buttons: ['Restart', 'Later'],
|
||||
title: 'Application Update',
|
||||
message: info.version,
|
||||
detail: 'A new version has been downloaded. Restart the application to apply the updates.'
|
||||
}
|
||||
|
||||
dialog.showMessageBox(updateDialog).then((returnValue) => {
|
||||
if (returnValue.response === 0) autoUpdater.quitAndInstall()
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
|
||||
|
||||
// Run when electron app is initialized.
|
||||
async function main(): Promise<void> {
|
||||
|
||||
console.log("MAIN:Initializing Electron App.");
|
||||
|
||||
useIpc();
|
||||
|
||||
// Must wait til window is created.
|
||||
await createWindow();
|
||||
|
||||
}
|
||||
|
||||
// Root function of app.
|
||||
export function initApp(dev: boolean): void {
|
||||
|
||||
// On initial startup.
|
||||
app.on("ready", () => {
|
||||
// autoUpdater.checkForUpdates()
|
||||
main();
|
||||
});
|
||||
|
||||
// Must keep to ensure app doesn't quit on close.
|
||||
app.on("before-quit", async () => {
|
||||
await stopStream();
|
||||
});
|
||||
|
||||
// Must keep to ensure app doesn't quit on close.
|
||||
app.on("window-all-closed", () => {
|
||||
});
|
||||
|
||||
// When user clicks app icon (re-open)
|
||||
app.on("activate", () => {
|
||||
|
||||
if (!win) {
|
||||
createWindow();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Exit cleanly on request from parent process in development mode.
|
||||
if (dev) {
|
||||
process.on("SIGTERM", () => {
|
||||
app.quit();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,119 +0,0 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
import { Profile } from "@/types";
|
||||
import { submit, fetchProfile, logout } from "@/api/account";
|
||||
import { ipcMain, IpcMainInvokeEvent } from "electron";
|
||||
import { store } from "@/background/store";
|
||||
import { endSession } from "@/background/session";
|
||||
|
||||
|
||||
const parseAuthRes = (authRes: any) => {
|
||||
const token = authRes.headers['set-cookie'][0].split(";")[0].split("=")[1] as string;
|
||||
const profile = authRes.data as Profile;
|
||||
return {
|
||||
token,
|
||||
profile
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const onProfile = async (
|
||||
_event: IpcMainInvokeEvent,
|
||||
_payload: null
|
||||
): Promise<Profile | Error> => (
|
||||
|
||||
new Promise(async (resolve, reject) => {
|
||||
console.log('[IPC]: user-profile');
|
||||
|
||||
// get jwt token and crimataId from store
|
||||
const token = store.get('key');
|
||||
const crimataId = store.get('crimataId');
|
||||
|
||||
// authenticate and fetch profile
|
||||
try {
|
||||
|
||||
const res = await fetchProfile(
|
||||
crimataId,
|
||||
token
|
||||
);
|
||||
|
||||
const parsed = parseAuthRes(res);
|
||||
resolve(parsed.profile);
|
||||
|
||||
} catch(e) {
|
||||
reject(new Error('Failed to fetch profile.'));
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
|
||||
const onLogin = async (
|
||||
_event: IpcMainInvokeEvent,
|
||||
payload: string
|
||||
): Promise<Profile | Error> => (
|
||||
|
||||
new Promise(async (resolve, reject) => {
|
||||
console.log('[IPC]: user-login');
|
||||
|
||||
const account = JSON.parse(payload);
|
||||
|
||||
if ( account.password && account.email ) {
|
||||
try {
|
||||
const res = await submit(account.email, account.password);
|
||||
const parsed = parseAuthRes(res);
|
||||
|
||||
// save jwt token and profile
|
||||
store.set('key', parsed.token);
|
||||
store.set('crimataId', parsed.profile.crimataId);
|
||||
|
||||
// return profile to renderer
|
||||
resolve(parsed.profile);
|
||||
|
||||
} catch(e) {
|
||||
console.log('[API]', e);
|
||||
reject(new Error('Failed to authenticate'));
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
const onLogout = async (
|
||||
_event: IpcMainInvokeEvent,
|
||||
_payload: null
|
||||
): Promise<void> => (
|
||||
|
||||
new Promise(async (resolve, reject) => {
|
||||
console.log('[IPC]: user-logout');
|
||||
|
||||
try {
|
||||
// post logout to backend
|
||||
await logout();
|
||||
|
||||
// remove key and crimataId
|
||||
store.delete('key');
|
||||
store.delete('crimataId');
|
||||
|
||||
// TODO: kill crimata platform session
|
||||
endSession();
|
||||
|
||||
resolve();
|
||||
} catch(e) {
|
||||
reject(new Error('Failed to logout. Please try again.'));
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
|
||||
export default function useAccountListeners(): void {
|
||||
|
||||
ipcMain.removeHandler("user-profile");
|
||||
ipcMain.handle("user-profile", onProfile);
|
||||
|
||||
ipcMain.removeHandler("user-login");
|
||||
ipcMain.handle("user-login", onLogin);
|
||||
|
||||
ipcMain.removeHandler("user-logout");
|
||||
ipcMain.handle("user-logout", onLogout);
|
||||
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
import { ipcMain, IpcMainInvokeEvent, IpcMainEvent } from "electron";
|
||||
import { fetchAudioInput, toggleRecord } from "@/background/audio";
|
||||
|
||||
|
||||
// Toggles record to true to begin capturing chunks.
|
||||
const onRecordingStart = (
|
||||
_event: IpcMainEvent,
|
||||
_payload: null
|
||||
): void => {
|
||||
|
||||
console.log('[IPC]: start-recording');
|
||||
|
||||
toggleRecord();
|
||||
};
|
||||
|
||||
|
||||
// Returns recorded audio to frontend and sets record to false.
|
||||
const onRecordingStop = async (
|
||||
_event: IpcMainInvokeEvent,
|
||||
_payload: null
|
||||
): Promise<Error | string> => {
|
||||
|
||||
console.log('[IPC]: stop-recording');
|
||||
|
||||
return await fetchAudioInput()
|
||||
};
|
||||
|
||||
|
||||
export default function useAudioListeners(): void {
|
||||
|
||||
ipcMain.removeAllListeners("start-recording");
|
||||
ipcMain.on("start-recording", onRecordingStart);
|
||||
|
||||
ipcMain.removeHandler("stop-recording");
|
||||
ipcMain.handle("stop-recording", onRecordingStop);
|
||||
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
import useAccountListeners from "./account";
|
||||
import useSessionListeners from "./session";
|
||||
import useAudioListeners from "./audio";
|
||||
|
||||
|
||||
export default function useIpc(): void {
|
||||
|
||||
useAccountListeners();
|
||||
|
||||
useSessionListeners();
|
||||
|
||||
useAudioListeners();
|
||||
|
||||
}
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
import { initSession, emitNewMessages, sendMessage } from '@/background/session';
|
||||
import { ipcMain, IpcMainInvokeEvent, IpcMainEvent } from "electron";
|
||||
import { initAudioIO } from "@/background/audio";
|
||||
import { ClientMessage } from "@/types";
|
||||
import { store } from "@/background/store";
|
||||
|
||||
|
||||
// Instantiate socket session with crimata-platorm.
|
||||
const onSessionInit = (
|
||||
_event: IpcMainInvokeEvent,
|
||||
cid: string
|
||||
): void => {
|
||||
|
||||
console.log('[IPC]: init-session');
|
||||
|
||||
const token = store.get('key');
|
||||
const crimataId = store.get('crimataId');
|
||||
|
||||
|
||||
initSession({
|
||||
token,
|
||||
crimataId
|
||||
});
|
||||
|
||||
initAudioIO();
|
||||
}
|
||||
|
||||
|
||||
const onAppMounted = (
|
||||
_event: IpcMainInvokeEvent,
|
||||
_payload: null
|
||||
): void => {
|
||||
|
||||
console.log('[IPC]: app-mounted');
|
||||
|
||||
emitNewMessages()
|
||||
};
|
||||
|
||||
|
||||
// Handle messages from window/client.
|
||||
const onClientMessage = (
|
||||
_event: IpcMainEvent,
|
||||
payload: ClientMessage
|
||||
): void => {
|
||||
|
||||
console.log('[IPC]: client-message');
|
||||
|
||||
sendMessage(payload);
|
||||
}
|
||||
|
||||
|
||||
export default function useSessionListeners(): void {
|
||||
|
||||
console.log('[IPC]: Init session listeners.');
|
||||
|
||||
// Attach listeners for frontend.
|
||||
ipcMain.removeAllListeners("client-message");
|
||||
ipcMain.on("client-message", onClientMessage);
|
||||
|
||||
// Attack browser window init listener.
|
||||
ipcMain.removeAllListeners("app-mounted");
|
||||
ipcMain.on("app-mounted", onAppMounted);
|
||||
|
||||
ipcMain.removeAllListeners("init-session");
|
||||
ipcMain.on("init-session", onSessionInit);
|
||||
|
||||
}
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
/*
|
||||
* Creates a websocket session with Crimata Servers.
|
||||
*
|
||||
* Connects to Servers and attempts key authentication. Server will respond
|
||||
* with key and user profile. We send the profile to the browser. We also
|
||||
* resend this information on new broser window. We then serve as a
|
||||
* communication interface between the window and the servers. It will
|
||||
* automatically try to reconnect on websocket disconnect.
|
||||
*/
|
||||
|
||||
import { backgroundMitt } from "@/modules/emitter";
|
||||
import { ipcEmit, loadState } from './helpers';
|
||||
import WebSocket from 'ws';
|
||||
|
||||
import useWebSockets from "./websockets";
|
||||
|
||||
import { play } from "./audio";
|
||||
import { renderMessage } from "@/modules/message";
|
||||
import { SessionState } from "@/types";
|
||||
|
||||
let win = true;
|
||||
|
||||
// Info saved to json on quit (key, newMessages).
|
||||
let state: SessionState;
|
||||
|
||||
let socket: WebSocket | null = null;
|
||||
|
||||
|
||||
// Calls appropriate endpoint for a server message.
|
||||
const onMessage = (data: string): void => {
|
||||
let message = JSON.parse(data);
|
||||
if (message === "CLOSE_AUTH_FAIL") {
|
||||
ipcEmit("session-auth-fail", null)
|
||||
return;
|
||||
}
|
||||
|
||||
// Standard message.
|
||||
if (message.content) {
|
||||
|
||||
// Convert to render message
|
||||
message = renderMessage(
|
||||
message.content.text,
|
||||
message.content.audio,
|
||||
message.context,
|
||||
message.modifier
|
||||
);
|
||||
|
||||
if (win) {
|
||||
console.log("SESS:Emitting standard message.")
|
||||
if (message.audio) {
|
||||
play(message.audio)
|
||||
}
|
||||
ipcEmit("render-message", message)
|
||||
}
|
||||
|
||||
else {
|
||||
console.log("SESS:No window: saving message.")
|
||||
state.newMessages.push(message);
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
if (win) {
|
||||
ipcEmit("render-message", message)
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
// Websockets module.
|
||||
const { createSocket, send } = useWebSockets(onMessage);
|
||||
|
||||
|
||||
export const emitNewMessages = (): void => {
|
||||
if (state) {
|
||||
ipcEmit("update-state", {
|
||||
newMessages: state.newMessages,
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export const sendMessage = (payload: Record<string, any>): void => {
|
||||
try {
|
||||
send(payload)
|
||||
} catch(e) {
|
||||
console.log("Unable to send message: ", payload);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const endSession = (): void => {
|
||||
if (socket) {
|
||||
socket.close();
|
||||
socket = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Call this to initialize session with Crimata servers.
|
||||
export const initSession = (authPayload: {
|
||||
token: string;
|
||||
crimataId: string;
|
||||
}): void => {
|
||||
console.log("SESS:Creating new session.")
|
||||
|
||||
// Load Json or createState.
|
||||
state = loadState("session.json");
|
||||
|
||||
// Open socket connection.
|
||||
if (!socket)
|
||||
socket = createSocket(authPayload);
|
||||
|
||||
// Keep win up-to-date.
|
||||
backgroundMitt.on('window-active', (state: boolean) => {
|
||||
win = state;
|
||||
});
|
||||
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
|
||||
const Store = require('electron-store');
|
||||
|
||||
const schema = {
|
||||
key: {
|
||||
type: 'string',
|
||||
},
|
||||
crimataId: {
|
||||
type: 'string'
|
||||
}
|
||||
};
|
||||
|
||||
export const store = new Store({
|
||||
schema,
|
||||
encryptionKey: "super user test"
|
||||
});
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
import WebSocket from 'ws';
|
||||
|
||||
let socket: WebSocket;
|
||||
|
||||
const socketUrl = "ws://127.0.0.1:8760"
|
||||
|
||||
// Run every time we want to connect to backend.
|
||||
export default function useWebSockets(
|
||||
receiveCallback: (s: string) => void,
|
||||
openCallback?: () => void
|
||||
) {
|
||||
|
||||
// Returns bool (sucess or fail).
|
||||
const sendMessage = (data: any) => {
|
||||
console.log("WS:Sending message: ", data)
|
||||
|
||||
if (socket.readyState !== 1) {
|
||||
return false
|
||||
}
|
||||
|
||||
else {
|
||||
socket.send(JSON.stringify(data))
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const send = async (data: Record<string, any>): Promise<boolean> => (
|
||||
new Promise((resolve, reject) => {
|
||||
if (socket.readyState !== 1) {
|
||||
reject(false);
|
||||
}
|
||||
socket.send(JSON.stringify(data))
|
||||
resolve(true);
|
||||
|
||||
}))
|
||||
|
||||
|
||||
const onOpen = (_event: WebSocket.OpenEvent) => {
|
||||
|
||||
console.log("WS:Connected to WS Server!");
|
||||
if (openCallback) openCallback();
|
||||
|
||||
}
|
||||
|
||||
const onServerMessage = (event: WebSocket.MessageEvent) => {
|
||||
|
||||
console.log("WS:Message received: ", event.data);
|
||||
|
||||
receiveCallback(event.data.toString())
|
||||
|
||||
}
|
||||
|
||||
const onClose = (event: WebSocket.CloseEvent) => {
|
||||
console.log("WS:Socket closed normally.")
|
||||
}
|
||||
|
||||
// Reconnect automatically on error.
|
||||
const onError = (event: WebSocket.ErrorEvent) => {
|
||||
console.log("WS:WebSocket error: ", event.message);
|
||||
|
||||
console.log("Attempting reconnect in 1s.")
|
||||
setTimeout(createSocket, 1000)
|
||||
}
|
||||
|
||||
const createSocket = (authPayload: {
|
||||
token: string;
|
||||
crimataId: string;
|
||||
}):WebSocket => {
|
||||
socket = new WebSocket(socketUrl)
|
||||
|
||||
// Add listeners.
|
||||
socket.addEventListener("open", (_event: WebSocket.OpenEvent) => {
|
||||
socket.send(JSON.stringify({
|
||||
key: authPayload.token,
|
||||
crimata_id: authPayload.crimataId
|
||||
}));
|
||||
})
|
||||
socket.addEventListener("message", onServerMessage)
|
||||
socket.addEventListener("close", onClose)
|
||||
socket.addEventListener("error", onError)
|
||||
|
||||
return socket;
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
createSocket,
|
||||
sendMessage,
|
||||
send
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,147 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
import { BrowserWindow, ipcMain } from "electron";
|
||||
import { createProtocol } from "vue-cli-plugin-electron-builder/lib";
|
||||
import { backgroundMitt } from '@/modules/emitter';
|
||||
import { RenderMessage, WindowState } from "@/types";
|
||||
import { loadWinState, saveToJson } from "./helpers";
|
||||
import * as path from "path";
|
||||
const { autoUpdater } = require('electron-updater');
|
||||
|
||||
interface IpcRendererPayload {
|
||||
endpoint: string;
|
||||
message: RenderMessage | null;
|
||||
}
|
||||
|
||||
let win: BrowserWindow | null;
|
||||
let winState: WindowState;
|
||||
|
||||
// Called when a NavBar button is pressed.
|
||||
const onNavBar = (_event: any, action: string): void => {
|
||||
if (win) {
|
||||
if (action === "close") {
|
||||
win.close()
|
||||
} else {
|
||||
win.minimize()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Util function to render message on ipc-renderer event.
|
||||
const renderMessage = (payload: IpcRendererPayload): void => {
|
||||
if (win) {
|
||||
win.webContents.send(payload.endpoint, {
|
||||
message: payload.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Write a json with position and size of window.
|
||||
const saveWindowState = () => {
|
||||
if (win) {
|
||||
const bounds = win.getBounds();
|
||||
const position = win.getPosition();
|
||||
|
||||
if (winState) {
|
||||
|
||||
winState.width = bounds.width;
|
||||
winState.height = bounds.height;
|
||||
winState.x = position[0];
|
||||
winState.y = position[1]
|
||||
|
||||
saveToJson("window.json", winState)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do this on window mount.
|
||||
const onWindowMount = (): void => {
|
||||
|
||||
// Must tell initApp that window exists.
|
||||
backgroundMitt.emit('window-active', true);
|
||||
|
||||
// Handle win nav-bar event.
|
||||
ipcMain.removeAllListeners("nav-bar") // avoid setting duplicate handlers
|
||||
ipcMain.on("nav-bar", onNavBar);
|
||||
|
||||
// Gateway for messages to the frontend.
|
||||
backgroundMitt.removeAllListeners("ipc-renderer")
|
||||
backgroundMitt.on("ipc-renderer", renderMessage);
|
||||
|
||||
}
|
||||
|
||||
// Do this on window dismount (close).
|
||||
const onWindowDismount = (): void => {
|
||||
win = null;
|
||||
backgroundMitt.emit('window-active', false);
|
||||
}
|
||||
|
||||
// function used by run.ts to create the main window.
|
||||
export async function createWindow(): Promise<void> {
|
||||
return new Promise((resolve, _reject) => {
|
||||
|
||||
// avoid creating duplicate windows.
|
||||
if (win) resolve();
|
||||
|
||||
// Load the saved window state.
|
||||
winState = loadWinState("window.json")
|
||||
|
||||
// Define the browser window.
|
||||
win = new BrowserWindow({
|
||||
width: winState.width,
|
||||
height: winState.height,
|
||||
x: winState.x,
|
||||
y: winState.y,
|
||||
resizable: true,
|
||||
backgroundColor: '#EBEBEB',
|
||||
frame: false,
|
||||
minWidth: 350,
|
||||
minHeight: 500,
|
||||
webPreferences: {
|
||||
nodeIntegration: (process.env.ELECTRON_NODE_INTEGRATION as unknown) as boolean, preload: path.join(__dirname, "preload.js")
|
||||
}
|
||||
});
|
||||
|
||||
// Load the URL.
|
||||
if (process.env.WEBPACK_DEV_SERVER_URL) {
|
||||
win.loadURL(process.env.WEBPACK_DEV_SERVER_URL as string); // dev
|
||||
}
|
||||
|
||||
else {
|
||||
createProtocol("app");
|
||||
win.loadURL("app://./index.html"); // prod
|
||||
}
|
||||
|
||||
// Handle window close.
|
||||
win.on("closed", onWindowDismount);
|
||||
|
||||
// Save window state on resize and move.
|
||||
win.on("moved", saveWindowState);
|
||||
win.on("resize", saveWindowState);
|
||||
|
||||
win.once('ready-to-show', () => {
|
||||
autoUpdater.checkForUpdatesAndNotify();
|
||||
if (win) win.show()
|
||||
})
|
||||
|
||||
// For showing/hiding the splash screen.
|
||||
win.webContents.on('did-finish-load', () => {
|
||||
if (win) win.webContents.send('window-ready', {
|
||||
message: true
|
||||
});
|
||||
|
||||
onWindowMount();
|
||||
resolve();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
autoUpdater.on('update-available', () => {
|
||||
if (win) win.webContents.send('update_available');
|
||||
});
|
||||
|
||||
autoUpdater.on('update-downloaded', () => {
|
||||
if (win) win.webContents.send('update_downloaded');
|
||||
});
|
||||
Loading…
Reference in a new issue