messenger v69

This commit is contained in:
riqo 2021-03-08 16:49:14 -06:00
commit aeb212b5c9
10 changed files with 172 additions and 121 deletions

View file

@ -32,6 +32,7 @@
"mitt": "^2.1.0",
"naudiodon": "^2.3.2",
"node-record-lpcm16": "^1.0.1",
"uuid": "^8.3.2",
"vue": "^3.0.0-0",
"vue-router": "^4.0.0-0",
"vuex": "^4.0.0-0",

View file

@ -12,7 +12,7 @@ let record = false;
const audioContainer = {
input: '',
}
const encoding = "base64";
const encoding = "hex";
const audioOptions = {
channelCount: 1,
sampleFormat: 16,
@ -22,10 +22,13 @@ const audioOptions = {
}
// callback run on space bar key up and down.
const updateRecorder = (_event, payload: boolean): void => {
record = payload;
const updateRecorder = (_event, payload: {
isRecording: boolean;
uid: string | null;
}): void => {
record = payload.isRecording;
if (!record) {
sendAudio(Buffer.from(audioContainer.input, 'base64'));
if (payload.uid) sendAudio(audioContainer.input, payload.uid);
}
};

View file

@ -4,26 +4,9 @@ import WebSocket from 'ws';
import { backgroundMitt } from './emitter';
import { ipcMain } from "electron";
import { play } from './audio';
interface RenderMessage {
content: {
text: string;
audio: string;
};
context: string;
modifier: string;
id: string;
}
interface UserCreds {
email: string;
password: string;
}
interface TextMessage {
audio: 0;
content: string;
}
import { UserCreds, ClientMessage } from "@/types/message/index";
import { clientMessage } from '@/modules/message';
import { v4 as uuidv4 } from 'uuid';
const ip = 'ws://127.0.0.1';
const port = 8760;
@ -32,8 +15,7 @@ let socket: WebSocket;
let success = false;
let auth = false;
const authSession = async (_event, payload: string | UserCreds | null): Promise<string> => {
const onAuthSession = async (_event, payload: string | UserCreds | null): Promise<string> => {
return new Promise((resolve, reject) => {
console.log('authenticating...');
@ -61,18 +43,7 @@ const authSession = async (_event, payload: string | UserCreds | null): Promise<
});
};
// function parseHexString(str: string) {
// var result = [];
// while (str.length >= 8) {
// result.push(parseInt(str.substring(0, 8), 16));
// str = str.substring(8, str.length);
// }
// return result;
// }
const receiveMessage = (message: string): void => {
const onMessage = (message: string): void => {
while (!auth) {
backgroundMitt.emit('auth-res', message);
@ -80,34 +51,34 @@ const receiveMessage = (message: string): void => {
}
// Parse the message.
const parsed: RenderMessage = JSON.parse(message);
console.log('message received', parsed.content.text);
const m = JSON.parse(message);
// If there is audio, play it.
if (parsed.content.audio) {
const audioBytes = Buffer.from(parsed.content.audio, 'hex');
play(audioBytes);
// check to see if this is a Render Message.
if (m.content) {
// If there is audio, play it.
if (m.content.audio) {
const audioBytes = Buffer.from(m.content.audio as string, 'hex');
play(audioBytes);
}
// if there is no unique id, add it.
if(!m.uid) m.uid = uuidv4();
}
backgroundMitt.emit('ipc-renderer', {
endpoint: 'render-message',
message: parsed
message: m
});
};
const sendMessage = (_event, payload: TextMessage): void => {
console.log('sending message')
const onClientMessage = (_event, payload: ClientMessage): void => {
console.log('sending message');
socket.send(JSON.stringify(payload));
}
export const sendAudio = (content: Buffer) => {
console.log('sending audio')
socket.send(content);
}
// Run every time we want to connect to backend.
export const initSession = () => {
export function initSession(): void {
// Close existing sockets.
if (socket) {
@ -123,11 +94,11 @@ export const initSession = () => {
// handle renderer auth-token event
ipcMain.removeHandler('auth-session'); // avoid setting duplicate handlers
ipcMain.handle('auth-session', authSession);
ipcMain.handle('auth-session', onAuthSession);
// handle user message event
ipcMain.removeAllListeners('message');
ipcMain.on('message', sendMessage);
ipcMain.removeAllListeners('client-message');
ipcMain.on('client-message', onClientMessage);
// Connect to the backend.
socket.on('open', () => {
@ -162,6 +133,10 @@ export const initSession = () => {
initSession();
});
socket.on("message", receiveMessage);
};
socket.on("message", onMessage);
}
export function sendAudio(content: string, uid: string): void {
const m = clientMessage("", content, uid);
socket.send(JSON.stringify(m));
}

View file

@ -1,8 +1,8 @@
/*
* Controls which input to visualize
* Will visualize text input by default as user types or
* if user holds space bar down, input audio stream
* will be visualized & recorded
* Main logic used by inputItem component.
* Will visualize text input by default as user types.
* If user holds space bar down, input audio stream will
* be recorded and sent to the backend for analysis.
*/
import { ref, watch, onUnmounted, onMounted } from "vue";
@ -10,8 +10,13 @@ import useMitt from "@/modules/mitt";
import { useIpc } from '@/modules/ipc';
import keyboardNameMap from "./keyBoardMaps/keyboardNameMap";
import keyboardCharMap from "./keyBoardMaps/keyboardCharMap";
import { RenderMessage, ClientMessage } from "@/types/message/index";
import { v4 as uuidv4 } from 'uuid';
import {clientMessage, renderMessage} from '@/modules/message';
let textInput: HTMLInputElement | null;
let m: RenderMessage | ClientMessage;
let uid: string;
export default function useInputController() {
@ -21,19 +26,28 @@ export default function useInputController() {
const isTyping = ref(false);
const input = ref("");
// send message on ENTER
// send message on ENTER.
const onEnter = () => {
if (input.value) {
// remove first character of input.
const text = input.value.substring(1);
const message = {
audio: 0,
text: input.value
};
uid = uuidv4();
// trigger send animation
emitter.emit("animate-send");
m = renderMessage(text, false, "sf", uid);
// send message to backend
if (input.value) post('message', message);
// render user message.
emitter.emit("self-message", m);
m = clientMessage(m.content.text, m.content.audio, uid);
// send message to backend.
post('client-message', m);
// reset text input.
if (textInput) textInput.value = '';
input.value = " ";
}
}
// update input on keydown event & listen for special commands.
@ -59,8 +73,6 @@ export default function useInputController() {
switch (cmd) {
case "ENTER":
onEnter();
if (textInput) textInput.value = ''
input.value = " ";
break;
case "BACK_SPACE":
input.value = input.value.slice(0, -1);
@ -74,12 +86,24 @@ export default function useInputController() {
}
}
// stops stream recording on space key up event.
// stop stream recording and render audio message on space key up event.
const onKeyUp = (e: KeyboardEvent) => {
if (e.key === " " && isRecording.value) {
isRecording.value = false;
post('update-recorder', isRecording.value);
uid = uuidv4();
// render audio message.
m = renderMessage("", false, "sf", uid);
emitter.emit("self-message", m);
// stop stream recording.
post('update-recorder', {
isRecording: isRecording.value,
uid
});
input.value = ''
}
}
@ -107,7 +131,10 @@ export default function useInputController() {
if (input === " " && isRecording.value === false) {
isTyping.value = false;
isRecording.value = true;
post('update-recorder', isRecording.value);
post('update-recorder', {
isRecording: isRecording.value,
uid: null
});
return;
}
isTyping.value = true;

21
src/modules/message.ts Normal file
View file

@ -0,0 +1,21 @@
import { RenderMessage, ClientMessage } from "@/types/message/index";
export const renderMessage = (text: string, audio: string | boolean, modifier: string, uid: string): RenderMessage => (
{
content: {
audio,
text
},
context: "",
modifier,
uid
}
)
export const clientMessage = (text: string, audio: string | boolean, uid: string): ClientMessage => (
{
audio,
text,
uid
}
)

View file

@ -25,8 +25,8 @@ process.once("loaded", () => {
ipcRenderer.send("update-recorder", message.content);
}
if (message.endpoint === "message") {
ipcRenderer.send("message", message.content);
if (message.endpoint === "client-message") {
ipcRenderer.send("client-message", message.content);
}
});

View file

@ -35,21 +35,21 @@ const router = createRouter({
});
// route auth check
// router.beforeEach((to, from, next) => {
// const { accessToken } = useAuth();
//
// // Not logged into a guarded route?
// if (to.meta.requiresAuth && !accessToken.value) {
// next({ name: 'login' })
// }
//
// // Logged in for an auth route
// else if ((to.name == 'login' || to.name == 'register') && accessToken.value){
// next({ name: 'home' });
// }
//
// // Carry On...
// else next();
// })
router.beforeEach((to, from, next) => {
const { accessToken } = useAuth();
// Not logged into a guarded route?
if (to.meta.requiresAuth && !accessToken.value) {
next({ name: 'login' })
}
// Logged in for an auth route
else if ((to.name == 'login' || to.name == 'register') && accessToken.value){
next({ name: 'home' });
}
// Carry On...
else next();
})
export default router;

View file

@ -1,18 +1,26 @@
export interface Message {
export interface UserCreds {
email: string;
password: string;
}
export interface RenderMessage {
content: {
text: string;
audio: boolean | Buffer;
audio: boolean | string;
};
context: string;
modifier: string;
id: string;
uid: string;
}
interface MessageTransform {
initialTranslate: string;
finalTranslate: string;
export interface ClientMessage {
text: string;
audio: boolean | string;
uid: string;
}
export interface MessageAnimeFunc {
(message: SVGElement, transform: MessageTransform, scrollHeight?: number | undefined, targetList?: NodeList | undefined): void;
}
export interface Annotation {
text: string;
context: string;
uid: string;
}

View file

@ -3,15 +3,16 @@
<!-- List of message bubbles. -->
<div id="messenger">
<MessageItem v-for="message in messages" :message="message" :key="message.id" />
<MessageItem v-for="message in messages" :message="message[1]" :key="message[0]" />
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, onMounted, onUnmounted } from "vue";
import { Message } from "@/types/message/index";
import { RenderMessage , Annotation } from "@/types/message/index";
import MessageItem from "@/components/messageItem.vue";
import InputItem from "@/components/inputItem/inputItem.vue";
import useMitt from "@/modules/mitt";
export default defineComponent({
name: "Messenger",
@ -22,32 +23,42 @@ export default defineComponent({
},
setup() {
const { emitter } = useMitt();
// List of messages in the view.
// Oldest message first.
const messages: Message[] = reactive([]);
const messages_ref: any = {};
const messages = reactive(new Map());
// Render a message.
// Called on "enter" instead of on "recv message"
const render = (message: Message) => {
messages.push(message);
messages_ref[message.id] = message;
};
let m: RenderMessage | Annotation;
// Annotate/update an existing message.
const annotate = (message: Message) => {
1 + 1;
};
const onRenderMessage = (event: any, payload: any) => {
// render message if there is content.
if (payload.message.content) {
m = payload.message as RenderMessage;
messages.set(m.uid, m);
} else {
// must be an annotation, update messages.
m = payload.message as Annotation;
if (messages.has(m.uid)) {
messages.get(m.uid).context = m.context;
messages.get(m.uid).content.text = m.text;
}
}
}
onMounted(() => {
emitter.on('self-message', (message) => messages.set(message.uid, message));
// Listen for new messages to render.
window.ipcRenderer.on("render-message", (event, payload) => {
if (payload.message.content) render(payload.message);
});
window.ipcRenderer.on("render-message", onRenderMessage);
});
onUnmounted(() => {
window.ipcRenderer.removeAllListeners("render-message");
emitter.all.clear();
});
return {

View file

@ -12838,6 +12838,11 @@ uuid@^8.0.0:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.1.tgz#2ba2e6ca000da60fce5a196954ab241131e05a31"
integrity sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==
uuid@^8.3.2:
version "8.3.2"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
v8-compile-cache@^2.0.3:
version "2.2.0"
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz#9471efa3ef9128d2f7c6a7ca39c4dd6b5055b132"