From 9e8641b4def1a49c8e607475b1dfe1d8a2176b4f Mon Sep 17 00:00:00 2001 From: Andrew Gundersen Date: Sat, 12 Jun 2021 09:49:41 -0500 Subject: [PATCH] more changes --- src/api/account.ts | 27 +++- src/composables/canvas.ts | 36 +++++ src/composables/store.ts | 16 -- src/composables/websockets.ts | 50 +++---- src/main.ts | 54 +++---- src/render/App.vue | 12 +- .../controllers/messenger.control.ts | 139 +++--------------- src/render/components/messenger.vue | 20 ++- src/session.ts | 62 ++++---- src/state.ts | 29 ---- src/types.ts | 7 - 11 files changed, 167 insertions(+), 285 deletions(-) create mode 100644 src/composables/canvas.ts delete mode 100644 src/composables/store.ts delete mode 100644 src/state.ts diff --git a/src/api/account.ts b/src/api/account.ts index de1335b..98d3c69 100644 --- a/src/api/account.ts +++ b/src/api/account.ts @@ -4,20 +4,33 @@ import axios from "axios"; const { post } = useHttp(); -export const submit = async (email: string, password: string) => ( - await post('/account/login', { email, password }) -) +export const usrPwdAuth = async (email: string, password: string) => { -export const fetchAccount = async (email: string, token: string) => ( - await axios({ + try { + return await post('/account/login', { email, password }) + + } catch (e) { + return null; + } + +} + +export const tokenAuth = async (cid: string, token: string) => { + + try { + return await axios({ url: "http://127.0.0.1:3000/api/account/profile", headers: { Cookie: `jwt=${token}` }, method: 'GET', data: { - email, + cid, } }) -) + } catch (e) { + return null; + } + +} \ No newline at end of file diff --git a/src/composables/canvas.ts b/src/composables/canvas.ts new file mode 100644 index 0000000..c85f7ed --- /dev/null +++ b/src/composables/canvas.ts @@ -0,0 +1,36 @@ + + + +export default class Canvas { + + messages: Message[]; + + /* seed canvas with messages on init */ + constructor(messages: Message[]) { + this.messages = messages; + ipcEmit("seed-view", this.messages); + } + + /* add a new message to the canvas */ + add(message: Message) { + this.messages.push(message); + ipcEmit("update-view", message); + } + + /* update an existing message */ + update(message: Message) { + + /* get the target message */ + let target_message = this.messages.filter((m: Message) => { + return m.uid = message.uid; + })[0]; + + /* replace the target message */ + if (target_message) { + target_message = message; + ipcEmit("update-view", message); + } + + } + +} diff --git a/src/composables/store.ts b/src/composables/store.ts deleted file mode 100644 index 16f8409..0000000 --- a/src/composables/store.ts +++ /dev/null @@ -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" -}); diff --git a/src/composables/websockets.ts b/src/composables/websockets.ts index 779f00b..d034027 100644 --- a/src/composables/websockets.ts +++ b/src/composables/websockets.ts @@ -3,7 +3,7 @@ import WebSocket from 'ws'; -export default function useWebSockets(receiveCallback: (s: string) => void, openCallback?: () => void) { +export default function useWebSockets(onMessageCallback: (s: string) => void) { let socket: WebSocket | null = null; @@ -19,36 +19,25 @@ export default function useWebSockets(receiveCallback: (s: string) => void, open }); } - 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 connect = (socketUrl: string, secret: string) => { - const onClose = (event: WebSocket.CloseEvent) => { - console.log("WS:Socket closed normally.") - } + /* create a new socket */ + socket = new WebSocket(socketUrl) - // 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); - } + /* add event listeners */ - const createSocket = (socketUrl: string) => { + socket.on("open", () => { + if (socket) + socket.send(secret); + }); - socket = new WebSocket(socketUrl) + socket.on("message", (event: WebSocket.MessageEvent) => { + onMessageCallback(event.data.toString()) + }); - // Add listeners. - socket.addEventListener("open", onOpen); - socket.addEventListener("message", onServerMessage); - socket.addEventListener("close", onClose); - socket.addEventListener("error", onError); + socket.on("close", () => { + return + }); } @@ -59,15 +48,10 @@ export default function useWebSockets(receiveCallback: (s: string) => void, open } } - const checkConnection = () => { - return true; - } - return { - createSocket, + connect, send, - close, - checkConnection + close }; } diff --git a/src/main.ts b/src/main.ts index 744643d..9d6414c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -9,37 +9,29 @@ * */ -import { fetchAccount, submit } from "@/api/account"; +import { tokenAuth, usrPwdAuth } from "@/api/account"; import { launchSession, endSession } from "@/session"; import useIpc from "@/ipc/index"; import store from "@/composables/store"; -/* user profile, signals whether user is logged in */ -let auth: Profile | null = null; - /* authenticate the user */ export async function authenticate(email: string, password: string) { /* attempt normal login */ - try { - auth = await submit(email, password); - } catch (e) { - console.log(e); - } + const platformKey, token, crimataId = await usrPwdAuth(email, password); + + /* launch if successful */ + if (platformKey) + launchSession(platformKey, crimataId); - /* launch if profile */ - if (auth) { - launchSession(auth); - } + /* save the token */ + store.set("token", token); } /* logout the user, end the session */ export function deauthenticate() { - /* set profile back to null */ - auth = null; - /* terminate the session */ endSession(); @@ -47,26 +39,24 @@ export function deauthenticate() { export default async function main() { + /* initiate controls for frontend to use when needed */ + useIpc(); + /* launch browser window */ - // await createWindow(); + await createWindow(); - /* attempt key-based authentication with business api */ - const token = store.get('key', null); - const crimataId = store.get('crimataId', null); + /* attempt to get a login token from the store */ + const token = store.get("token"); - try { - const res = await fetchAccount(crimataId, token); - auth = parseAuthRes(res); - } catch (e) { - console.log('[MAIN]', e); - } + /* try to login with it, returns platform secret and new token on success */ + if (token) + const newToken, profile = await tokenAuth(token); - /* connect to Crimata, or listen for manual login req */ - if (auth) { - launchSession(auth); - } + /* if secret, we launch a session */ + if (newToken) + launchSession(newToken, profile); - /* initiate controls for frontend to use when needed */ - useIpc(); + /* finally, save the most recent token */ + store.set("token", newToken); } \ No newline at end of file diff --git a/src/render/App.vue b/src/render/App.vue index e5c477d..d90bcc0 100644 --- a/src/render/App.vue +++ b/src/render/App.vue @@ -6,8 +6,9 @@ @@ -39,13 +40,13 @@ export default defineComponent({ setup() { - const state: Ref; + const crimataId = ref(false); onMounted(async () => { console.log("[APP]:mounted."); /* listen for auth related messages */ - window.addEventListener("update-state", (event: any) => { + window.addEventListener("update-auth", (event: any) => { state.value = event.data; }); @@ -56,7 +57,7 @@ export default defineComponent({ }); return { - profile + crimataId } } }) @@ -68,7 +69,6 @@ export default defineComponent({ html, body { margin: 0; padding: 0; - // Background color set in window.ts } #app { diff --git a/src/render/components/controllers/messenger.control.ts b/src/render/components/controllers/messenger.control.ts index 63f15ee..9ae7d47 100644 --- a/src/render/components/controllers/messenger.control.ts +++ b/src/render/components/controllers/messenger.control.ts @@ -1,143 +1,38 @@ import { ref } from 'vue'; -import invokeSavedMessages from "@/render/ipc"; import useScroll from "@/render/composables/scroll"; -const messages = ref(new Map()); +const canvas = ref(); -function getTimeStamp(): number { - const currentdate = new Date(); - return currentdate.getTime(); +/* seed the canvas with messages */ +const seedCanvas = (messages: Message[]) => { + canvas.value = messages; } -const newViewMessage = (message: Message): ViewMessage => { - return { - text: message.text, - context: message.context, - audio: message.audio, - from: message.from, - uid: message.uid, - time: getTimeStamp(), - isChild: "none", - seen: false, - newMessage: false - }; -} - -const addMessage = (message: Message, newMessage=false) => { - const viewMessage = newViewMessage(message); - if (newMessage) viewMessage.newMessage = true; - messages.value.set(viewMessage.uid, viewMessage); +const addMessage = (message: Message) => { + canvas.value.push(message); } const updateMessage = (message: Message) => { - const viewMessage = messages.value.get(message.uid); - viewMessage.context = message.context; - viewMessage.text = message.text; -} - -const loadSavedMessages = async () => { - const messageData = await invokeSavedMessages(); - messages.value = new Map(Object.entries(messageData)); -} - -const saveMessages = () => { - const messageData = Object.fromEntries(messages.value); - // must save to json. -} -const pruneMessages = (limit=200) => { - if (messages.value.size >= limit) { - const oldest = Array.from(messages.value.keys()).shift(); - messages.value.delete(oldest); - } -} - -const updateGrouping = () => { + let target_message = canvas.value.filter((m: Message) => { + return m.uid = message.uid; + })[0]; - const isSimmilar = (messageA: ViewMessage, messageB: ViewMessage) => { - if ((Math.abs(messageA.time - messageB.time) < 20000) && (messageA.from == messageB.from) && (messageA.context == messageB.context)) { - return true - } - return false + if (target_message) { + target_message = message; } - const refs = Array.from(messages.value.keys()) - - // Get the last three messages. - const first = messages.value.get(refs[refs.length - 1]) - const second = messages.value.get(refs[refs.length - 2]) - const third = messages.value.get(refs[refs.length - 3]) - - // If messages are simmilar, update the classes. - if ((first) && (second)) { - if (isSimmilar(first, second)) { - first.isChild = "last" // i.e. last in group. - second.isChild = "first" - - if (third) { - if ((third.isChild == "first") || (third.isChild == "middle")) { - second.isChild = "middle" - } - } - } - } } export default function useMessages() { const { updateScrollRef, adjustScroll } = useScroll("messenger"); - /* Given new message object, update the view accordingly */ - const updateMessageView = (newMessages: Message[]) => { - - const bottom = updateScrollRef(); // see if the user is scrolled down - - // take each message and apply view - newMessages.forEach((message: Message) => { - - // add or update message depending - if (messages.value.has(message.uid)) { - updateMessage(message); - } else addMessage(message); - - pruneMessages(); // pop off old messages from view - updateGrouping(); // group like message together - - if (bottom) adjustScroll(); // only scroll if user was at bottom - - saveMessages(); - - }); - - } - return { - messages, - updateMessageView, - loadSavedMessages - } - -} - - - // // Seed message view with message history. - // const prepMessageView = async (newMessages: Message[]) => { - // console.log("MSGR:Prepping messenger view.") - - // // Load and render saved messages and immediately scroll to bottom. - // await loadSavedMessages(); - // setTimeout(setScroll.bind(false), 10); - - // // Render new messages, then wait 1s to scroll. - // if (newMessages.length) { - - // console.log("MSGR:Adding new messages") - - // newMessages.forEach(message => { - // addMessage(message, true); - // }) - - // setTimeout(setScroll.bind(true), 1000); + canvas, + seedCanvas, + addMessage, + updateMessage + }; - // } - // } \ No newline at end of file +} \ No newline at end of file diff --git a/src/render/components/messenger.vue b/src/render/components/messenger.vue index 77d54f3..4ea2d5b 100644 --- a/src/render/components/messenger.vue +++ b/src/render/components/messenger.vue @@ -11,6 +11,7 @@ v-for="message in messages" :text="message.text" :context="message.context" + :child :key="message[0]" /> @@ -28,7 +29,7 @@ import useMessages from "@/render/composables/messages"; export default defineComponent({ name: "Messenger", - props: ["state"], + props: ["profile", "messages"], components: { Message, @@ -43,12 +44,19 @@ export default defineComponent({ onMounted(() => { - /* populate the message view with existing messages */ - updateMessageView(state.savedMessages, state.newMessages); + /* seed messages */ + window.ipcRenderer.on("init-messages", (e_: any, payload: any) => { + seedMessages(payload.messages); + }); + + /* add a new message */ + window.ipcRenderer.on("add-message", (_e: any, payload: any) => { + addMessage(payload.message); + }); - /* wait and listen for new messages to come in */ - window.ipcRenderer.on("new-message", (_e: any, payload: any) => { - updateMessageView(payload.message); + /* update an existing message */ + window.ipcRenderer.on("update-message", (_e: any, payload: any) => { + updateMessage(payload.message); }); }); diff --git a/src/session.ts b/src/session.ts index 0a893d8..afebe5b 100644 --- a/src/session.ts +++ b/src/session.ts @@ -3,7 +3,11 @@ import useAudio from "@/audio"; -import { loadState, saveState, emitState } from "@/state"; +import Canvas from "@/composables/convas"; +import ipcEmit from "@/composables/emitter"; + +/* data structure of messages that's tied to the UI */ +let msgrState: UIState | null = null; /* start and stop audio functionality */ const { initAudio, closeAudio } = useAudio(); @@ -12,53 +16,57 @@ const { initAudio, closeAudio } = useAudio(); * Controls for interfacing with the platform. * Takes an onMessage callback which we define below. */ -const { connect, send, close } = usePlatform((message: Message) => { - - /* add the message to the state */ - addMessage(message); - - /* push the message to the browser */ - if (win) emit("new-message", message); +const { connect, send, close } = useWebsockets((content: any) => { + + /* if the platform fails to authenticate, we must back down */ + if (content === "auth_error") { + deauthenticate(); + return; + } + + /* on init, platform sends state, used to init canvas */ + if (isInitMessage(content)) { + uiState.set(content); + } + + + else if (isAddMessage(content)) { + uiState.add(content); + } + + else { + uiState.update(content); + } }); /* send a message to the platform */ export function sendMessage(message: Message) { - /* add the message to the state */ - addMessage(message); - - /* push the message to the browser */ - if (win) emit("new-message", message); - /* socket send */ send(message); } /* launch a new session (the main process for authenticated users) */ -export function launchSession(profile: Profile) { - - /* load any previously saved state for that user */ - loadState(profile); +export function launchSession(platformKey: string, crimata_id: string) { /* connect to the platform */ - connect(profile); + connect(PLATFORM_URL, platformKey); /* initialize the audio streams */ - // initAudio(); + initAudio(); - /* finally we can push state to browser */ - if (win) emitState(); + /* push profile to window */ + if (win) + ipcEmit("update-auth", crimata_id); } export function endSession() { - closeAudioStreams(); + closeAudio(); - closeSocket(); + close(); - state.clear(); - -} \ No newline at end of file +} diff --git a/src/state.ts b/src/state.ts deleted file mode 100644 index 1d6ce02..0000000 --- a/src/state.ts +++ /dev/null @@ -1,29 +0,0 @@ -const Store = require('electron-store'); - -/* simple data persistance */ -const store = new Store; - -/* state of the session (e.g. profile and messages for now) */ -let state: State | null = null; - -/* load saved state in electron store for given user */ -export function loadState(profile: Profile) { - state = store.get("state", null); -} - -/* add a message to state.messages */ -export function addMessage(message: Message) { - if (state) { - state.messages.push(message); - saveState(); - } -} - -export function saveState() { - store.set("state", state); -} - -export function emitState() { - emit("update-state", state); -} - diff --git a/src/types.ts b/src/types.ts index b437ffa..e8682b8 100644 --- a/src/types.ts +++ b/src/types.ts @@ -9,8 +9,6 @@ interface Message { interface ViewMessage extends Message { child: string; - seen: boolean; - newMessage: boolean; } interface WindowState { @@ -26,11 +24,6 @@ interface Profile { initials: string; } -interface State { - profile: Profile | null; - messages -} - interface LoginPayload { email: string; password: string; -- 2.43.0