From f9dcb00e2d43a76bc5761e035a247c80399a0a5c Mon Sep 17 00:00:00 2001 From: Andrew Gundersen Date: Thu, 26 Aug 2021 12:31:18 -0500 Subject: [PATCH] [WIP] refactor, upgrading yarn --- .gitignore | 9 + .yarnrc.yml | 6 +- package.json | 2 +- src/account.ts | 31 +- src/audio.ts | 115 +- src/auth.ts | 5 - src/composables/useSaveToJSON.ts | 13 - src/composables/useStore.ts | 12 + src/composables/useWebsockets.ts | 28 +- src/init.ts | 52 +- src/io.ts | 34 + src/ipc/listeners.ts | 17 +- src/main.ts | 4 +- src/profile.ts | 22 - src/render/components/controllers/helpers.ts | 26 +- src/render/components/inputItem.vue | 96 +- src/render/components/message.vue | 10 +- src/render/components/messenger.vue | 4 +- src/render/ipc.ts | 8 +- src/render/listeners.ts | 17 +- src/render/shared/audio.ts | 3 +- src/render/shared/messages.ts | 21 +- src/session.ts | 107 +- src/store.ts | 58 - src/types.ts | 20 +- src/{uiState.ts => ui.ts} | 2 +- src/window.ts | 211 +- yarn.lock | 30981 ++++++++++------- 28 files changed, 18227 insertions(+), 13687 deletions(-) delete mode 100644 src/auth.ts delete mode 100644 src/composables/useSaveToJSON.ts create mode 100644 src/composables/useStore.ts create mode 100644 src/io.ts delete mode 100644 src/profile.ts delete mode 100644 src/store.ts rename src/{uiState.ts => ui.ts} (98%) diff --git a/.gitignore b/.gitignore index 20128fc..a628251 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,15 @@ yarn-debug.log* yarn-error.log* pnpm-debug.log* +# Yarn cache +.yarn/* +!.yarn/cache +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/sdks +!.yarn/versions + # Editor directories and files .idea .vscode diff --git a/.yarnrc.yml b/.yarnrc.yml index 03ee333..5c9b97b 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -1,4 +1,8 @@ +nodeLinker: node-modules + npmScopes: crimata: + npmAuthToken: "${NPM_TOKEN}" npmRegistryServer: "https://gitlab.example.com/api/v4/projects/28849281/packages/npm/" - npmAuthToken: ${NPM_TOKEN} \ No newline at end of file + +yarnPath: .yarn/releases/yarn-berry.cjs diff --git a/package.json b/package.json index 2cad1d5..3dd2044 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,6 @@ }, "main": "background.js", "dependencies": { - "@crimata/nodeaudio": "^0.0.0", "@types/animejs": "^3.1.2", "@types/bindings": "^1.3.0", "@types/dom-mediacapture-record": "^1.0.7", @@ -28,6 +27,7 @@ "electron-is-dev": "^2.0.0", "electron-store": "^8.0.0", "electron-updater": "^4.3.8", + "fft.js": "^4.0.4", "mitt": "^2.1.0", "update-electron-app": "^2.0.1", "vue": "^3.0.0-0", diff --git a/src/account.ts b/src/account.ts index 7324e6f..b038e47 100644 --- a/src/account.ts +++ b/src/account.ts @@ -1,20 +1,23 @@ - import { app } from "electron"; -import { parseAuthRes } from "@/auth"; + +import { LaunchSession, EndSession } from "@/session"; +import { store } from "@/composables/useStore"; import { postAuth, postLogin, postLogout } from "@/api/account"; -import { updateAppUI, launchSession, endSession } from "@/session"; -import { getToken, setToken, clearToken } from "./store"; import { backgroundMitt, ipcEmit } from "@/composables/useEmitter"; - /* Either null or a crimataId */ let account: string | null = null; +const parseAuthRes = (authRes: any) => { + const token = authRes.headers['set-cookie'][0].split(";")[0].split("=")[1] as string; + const crimataId = authRes.data as string; + return {token, crimataId} +}; export const accountAuth = async (): Promise => { /* attempt to get a login token from the store */ - const token = getToken(); + const token = store.get("token"); try { @@ -25,17 +28,17 @@ export const accountAuth = async (): Promise => { const parsed = parseAuthRes(res); // save jwt token and profile - setToken(parsed.token); + store.set("token", parsed.token); account = parsed.crimataId // launch session - launchSession(parsed.token); + LaunchSession(parsed.token); } else throw(new Error('Failed to authenticate (no token).')); } catch (e) { console.log(e); - clearToken(); + store.delete("token"); } finally { @@ -57,11 +60,11 @@ export const accountLogin = async (payload: any): Promise => { const parsed = parseAuthRes(res); // save jwt token and profile - setToken(parsed.token); + store.set("token", parsed.token); account = parsed.crimataId; // launch session - launchSession(parsed.token); + LaunchSession(parsed.token); // push state changes to the frontend updateAppState(); @@ -69,7 +72,7 @@ export const accountLogin = async (payload: any): Promise => { return; } catch(e) { - clearToken(); + store.delete("token"); throw e; } @@ -82,14 +85,14 @@ export const accountLogout = async (): Promise => { await postLogout(); // remove key and account - clearToken(); + store.delete("token"); account = null; // push account state to browser ipcEmit("set-account", account); // kill crimata platform session - endSession(); + EndSession(); return; diff --git a/src/audio.ts b/src/audio.ts index 2dcc9cd..76d53c4 100644 --- a/src/audio.ts +++ b/src/audio.ts @@ -1,65 +1,82 @@ +const FFT = require('fft.js'); import { globalShortcut } from "electron"; const nodeAudio = require('@crimata/nodeaudio'); +import { sendMessage } from "@/io"; import { updateTray } from "@/tray"; -import { sendMessage } from "@/session"; import { backgroundMitt, ipcEmit } from '@/composables/useEmitter'; let inputDevice: number; let outputDevice: number; +let setWriteId: ReturnType; let setStreamsId: ReturnType; -let playbackEl; -let writeToOBuffer = false; -let recording = false; +let playbackId: string; + +const streamState = { rec: false, pb: false }; + const chunks: Int16Array[] = []; -backgroundMitt.on("data", (int16Arr: Int16Array) => { - if (recording) { +const fft = new FFT(4); + +function calcFFT(int16Arr: Int16Array) +{ + const out = fft.createComplexArray(); + fft.realTransform(out, int16Arr); + return out; +} + +backgroundMitt.on("read", (int16Arr: Int16Array) => +{ + if (streamState.rec) + { chunks.push(int16Arr); - ipcEmit("recording", int16Arr); + ipcEmit("recording", calcFFT(int16Arr)); /* fft used for animation */ } -}); - -backgroundMitt.on("playback", (uid: string, aplitude: number) => { - ipcEmit("animate-playback", { uid: uid, aplitude: aplitude }); -}); +} +); -backgroundMitt.on("write", (status: boolean) => { - writeToOBuffer = status; -}); +backgroundMitt.on("write", (int16Arr: Int16Array) => +{ + ipcEmit("playback", { uid: playbackId, fft: calcFFT(int16Arr) }); + streamState.pb = true; -const setStreams = () => { + clearTimeout(setWriteId); + setWriteId = setTimeout(() => streamState.pb = false, 1000); +} +); +function setStreams() +{ const defaultInput = nodeAudio.core.GetDefaultInputDevice(); const defaultOutput = nodeAudio.core.GetDefaultOutputDevice(); - if (inputDevice !== defaultInput) { + if (inputDevice !== defaultInput) + { inputDevice = defaultInput; nodeAudio.core.CloseInputStream(inputDevice); nodeAudio.core.OpenInputStream(inputDevice); } - if (outputDevice !== defaultOutput) { + if (outputDevice !== defaultOutput) + { outputDevice = defaultOutput; nodeAudio.core.CloseOutputStream(outputDevice); nodeAudio.core.OpenOutputStream(outputDevice); } } -const startRecording = () => { - - /* If playback is happening, we kill it */ - if (playback) nodeAudio.core.ClearOutputBuffer(); - - recording = true; - updateTray("recording", recording); +function startRecording() +{ + streamState.rec = true; + updateTray("recording", streamState.rec); } -const stopRecording = () => { - recording = false; - updateTray("recording", recording); +function stopRecording() +{ + streamState.rec = false; + updateTray("recording", streamState.rec); /* Send the data to the platform right away */ sendMessage({ @@ -70,40 +87,38 @@ const stopRecording = () => { chunks.length = 0; } -export const initAudio = () => { +function InitAudio() +{ nodeAudio.core.Initialize(backgroundMitt.emit.bind(backgroundMitt)); setStreamsId = setInterval(setStreams, 2000); // sense device /* Toggle recording switch with global shortcut */ - globalShortcut.register('CommandOrControl+R', () => { - if (recording) { + globalShortcut.register('CommandOrControl+R', () => + { + if (streamState.rec) + { stopRecording(); - } else { + } + else + { startRecording(); - }; - }); - + } + } + ); } -export const playback = (audio: Int16Array, source: string) => { - - /* If existing playback is happening, we kill it */ - if (playback) nodeAudio.core.ClearOutputBuffer(); - - const playbackEl = uid; - +/* Will override an existing playback process */ +function playback(audio: Int16Array, uid: string) +{ + playbackId = uid; /* uid of message */ + // nodeAudio.core.CancelPlayback(); /* terminate any current playback */ nodeAudio.core.WriteToOutputStream(audio.buffer); } -export const terminateAudio = () => { +function TerminateAudio() +{ clearInterval(setStreamsId); nodeAudio.core.Terminate(); } -export const getStreamState = () => { - let busy = false; - if (playback || recording) { - busy = true; - } - return busy; -} +export { InitAudio, TerminateAudio, playback, streamState }; diff --git a/src/auth.ts b/src/auth.ts deleted file mode 100644 index 31c66e0..0000000 --- a/src/auth.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const parseAuthRes = (authRes: any) => { - const token = authRes.headers['set-cookie'][0].split(";")[0].split("=")[1] as string; - const crimataId = authRes.data as string; - return {token, crimataId} -}; \ No newline at end of file diff --git a/src/composables/useSaveToJSON.ts b/src/composables/useSaveToJSON.ts deleted file mode 100644 index 06ab4b9..0000000 --- a/src/composables/useSaveToJSON.ts +++ /dev/null @@ -1,13 +0,0 @@ - -import {config} from "@/config"; -import fs from 'fs'; - -export const saveToJson = (fileName: string, data: any) => { - - fs.writeFile(config.configPath + fileName, JSON.stringify(data), (err) => { - if (err) { - console.log("Error when saving to json.") - } - }) - -} diff --git a/src/composables/useStore.ts b/src/composables/useStore.ts new file mode 100644 index 0000000..e876748 --- /dev/null +++ b/src/composables/useStore.ts @@ -0,0 +1,12 @@ +const Store = require('electron-store'); + +const schema = { + token: { + type: 'string', + }, +}; + +export const store = new Store({ + schema, + encryptionKey: "super user test" +}); \ No newline at end of file diff --git a/src/composables/useWebsockets.ts b/src/composables/useWebsockets.ts index 3b9f2c9..8991070 100644 --- a/src/composables/useWebsockets.ts +++ b/src/composables/useWebsockets.ts @@ -8,17 +8,15 @@ const _connectionCheckTimeout = 4000; const _reconnectTimeout = 1000; let _connectionCheckInterval: ReturnType; +/* ws-status key: + * 0 - onMessage (connected) + * 1 - pongMessage (nominal) + * 2 - closeMessage (reconnecting) + * 3 - pingErrorMessage (connection lost) + */ -export default function useWebSockets( - messageCallback: (message: string) => void, - connectionStatusCallback: (status: string) => void, - statusOptions?: { - openMessage: string; - pongMessage: string; - closeMessage: string; - pingErrorMessage: string; - }, -) { +export default function useWebSockets(emit: (eventName: string | symbol, [...args]: any) => boolean) +{ let socket: WebSocket; @@ -47,7 +45,7 @@ export default function useWebSockets( socket.send(JSON.stringify({key: secret})); - connectionStatusCallback(statusOptions ? statusOptions.openMessage : "Connection Opened"); + emit("ws-status", 0); // ping server _connectionCheckInterval = setInterval(() => { @@ -55,7 +53,7 @@ export default function useWebSockets( socket.ping(null, true, (e: Error) => { if (e) { socket.close(); - connectionStatusCallback(statusOptions ? statusOptions.pingErrorMessage : "Connection Lost"); + emit("ws-status", 3); setTimeout(() => connect(socketUrl, secret), _reconnectTimeout); } }); @@ -66,11 +64,11 @@ export default function useWebSockets( socket.on("message", (event: WebSocket.MessageEvent) => { console.log("message received", event); - messageCallback(event.toString()) + emit("ws-message", event.toString()); }); socket.on("close", (code: number, reason: string) => { - connectionStatusCallback(statusOptions ? statusOptions.closeMessage : "Connection Closed"); + emit("ws-status", 2); clearInterval(_connectionCheckInterval); if (code !== 1000 || reason !== 'session-logout') { setTimeout(() => { @@ -80,7 +78,7 @@ export default function useWebSockets( }); - socket.on("pong", () => connectionStatusCallback(statusOptions ? statusOptions.pongMessage : "Pong")); + socket.on("pong", () => emit("ws-status", 1)); socket.on('error', () => {}); diff --git a/src/init.ts b/src/init.ts index 5e3cbad..5d885da 100644 --- a/src/init.ts +++ b/src/init.ts @@ -6,11 +6,11 @@ "use strict"; import { app, protocol, globalShortcut } from "electron"; -import createWindow from "./window"; -import main from "./main"; + +import main from "@/main"; +import { CreateWindow, winState } from "@/window"; +import { TerminateAudio } from '@/audio'; import { backgroundMitt } from '@/composables/useEmitter'; -import { setWindowOpen, getWindowOpen } from './store'; -import { terminateAudio } from './audio'; console.log('Starting Crimata electron app.'); @@ -21,37 +21,33 @@ protocol.registerSchemesAsPrivileged([ const isDev = require('electron-is-dev'); -// Listen for window creation. -backgroundMitt.on('window-active', (state: boolean) => { - setWindowOpen(state) -}); - /* Start main process on ready */ -app.on("ready", async () => { +app.on("ready", async () => +{ await main(); -}); +} +); -app.on("will-quit", () => { - terminateAudio(); +app.on("will-quit", () => +{ + TerminateAudio(); globalShortcut.unregisterAll(); -}); - -// Must keep to ensure app doesn't quit on close. -app.on("before-quit", async () => { -}); - -// 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 (!getWindowOpen()) createWindow(); -}); +app.on("activate", () => +{ + if (!winState.open) CreateWindow(); +} +); // Exit cleanly on request from parent process in development mode. -if (isDev) { - process.on("SIGTERM", () => { +if (isDev) +{ + process.on("SIGTERM", () => + { app.quit(); - }); + } + ); } diff --git a/src/io.ts b/src/io.ts new file mode 100644 index 0000000..75e2c9c --- /dev/null +++ b/src/io.ts @@ -0,0 +1,34 @@ +import { config } from "@/config"; +import { updateTray } from "@/tray"; +import useWebsockets from "@/composables/useWebsockets"; +import { backgroundMitt, ipcEmit } from "@/composables/useEmitter"; + +backgroundMitt.on("ws-status", (status: any) => +{ + let simpleStatus = false; + if (status == 2 || status == 3) simpleStatus = true; + + updateTray("disconnect", simpleStatus); + + ipcEmit('set-connection-status', status); +}); + +const { connect, send, close } = +useWebsockets(backgroundMitt.emit.bind(backgroundMitt)) + +function sendMessage(message: Raw | Request): void +{ + send(message); +} + +function ConnectToPlatform(platformKey: string) +{ + connect(config.PLATFORM_URL, platformKey); +} + +function DisconnectFromPlatform(code: number, reason: string) +{ + close(code, reason); +} + +export { ConnectToPlatform, DisconnectFromPlatform, sendMessage }; \ No newline at end of file diff --git a/src/ipc/listeners.ts b/src/ipc/listeners.ts index 5f63838..c9f45f8 100644 --- a/src/ipc/listeners.ts +++ b/src/ipc/listeners.ts @@ -1,7 +1,6 @@ +import { sendMessage } from '@/io'; import { playback } from "@/audio"; -import { onNavBar } from "@/window"; -import { sendMessage } from '@/session'; -import { setWindowFocus } from '@/store'; +import { winState, onNavBar } from "@/window"; import { updateAppState } from "@/account"; import { IpcListener } from "@/composables/useIpcMain" @@ -26,12 +25,12 @@ export const navBarListener = new IpcListener({ listenerCallback: onNavBar }); -export const windowFocusListener = new IpcListener({ +export const windowFocusListener = new IpcListener({ channel: POST_WINDOW_FOCUS, - listenerCallback: ({ isFocused }) => setWindowFocus(isFocused) + listenerCallback: (state) => winState.focus = state }); -export const playbackListener = new IpcListener({ - channel: PLAYBACK_CHANNEL, - listenerCallback: playback -}); +// export const playbackListener = new IpcListener({ +// channel: PLAYBACK_CHANNEL, +// listenerCallback: playback +// }); \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 8ae759b..ccf0846 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,5 +1,5 @@ import { initTray } from '@/tray'; -import createWindow from "@/window"; +import { CreateWindow } from "@/window"; import initIpcMain from "@/ipc/index"; import { accountAuth } from "@/account"; @@ -12,7 +12,7 @@ export default async function main() { initTray(); /* launch browser window */ - await createWindow(); + await CreateWindow(); /* try to authenticate with token */ accountAuth(); diff --git a/src/profile.ts b/src/profile.ts deleted file mode 100644 index 0c5428d..0000000 --- a/src/profile.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { ipcEmit } from "@/composables/useEmitter"; - - -export default class ProfileHandler { - - profile: Profile; - - constructor(profile: Profile) { - this.profile = profile; - this.emit(); - } - - emit() { - ipcEmit("set-profile", this.profile); - } - - update(update: Update) { - this.profile = update.data as Profile; - this.emit() - } - -} \ No newline at end of file diff --git a/src/render/components/controllers/helpers.ts b/src/render/components/controllers/helpers.ts index 97480f3..7fff75d 100644 --- a/src/render/components/controllers/helpers.ts +++ b/src/render/components/controllers/helpers.ts @@ -85,23 +85,23 @@ export function animateAudioInput () { } -/* TODO: animation function that takes in a message ID, and data about the - * playedback audio. We target the element with the UID and animate it according - * to the data. - still very hypothetical... - */ -export function animatePlayback (uid: string, meta: number) { - console.log("animating playback!") -} - // Given index and length of content, return message child status. -export function calcChild (index: number, len: number) { - if (len === 1) { +export function calcChild (index: number, len: number) +{ + if (len === 1) + { return "none"; - } else if (index === 0) { + } + else if (index === 0) + { return "first-child"; - } else if (index === len - 1) { + } + else if (index === len - 1) + { return "last-child"; - } else { + } + else + { return "middle-child"; } } \ No newline at end of file diff --git a/src/render/components/inputItem.vue b/src/render/components/inputItem.vue index 7efa6a5..c76d2fd 100644 --- a/src/render/components/inputItem.vue +++ b/src/render/components/inputItem.vue @@ -1,17 +1,13 @@