From c60c516ddd3640aba79f3484620352e395cabd62 Mon Sep 17 00:00:00 2001 From: Claas Date: Sun, 2 Jun 2024 14:26:01 +0200 Subject: [PATCH] Finish proof of concept --- src-tauri/src/lib.rs | 25 ++++++++++++++++++++----- src/AppContext.tsx | 44 +++++++++++++++++++++++++++++++++++++------- src/routes/Group.tsx | 9 ++++++--- 3 files changed, 63 insertions(+), 15 deletions(-) diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 4b82a0a..2cacfa6 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -11,6 +11,7 @@ use std::{ io::Read, sync::{Arc, PoisonError}, }; +use std::string::FromUtf8Error; use tauri::{AppHandle, Manager, State}; use thiserror::Error; use tokio::sync::Mutex; @@ -290,15 +291,28 @@ enum ReceiveMessageError { #[serde(skip)] ProcessMessageError, ), + #[error("Error deserializing message")] + DeserializeMessageError( + #[from] + #[serde(skip)] + FromUtf8Error, + ), } const JOIN_GROUP_EVENT: &str = "join_group"; +const NEW_MESSAGE_EVENT: &str = "new_message"; #[derive(Serialize, Clone)] struct JoinGroupEvent { group_id: String, } +#[derive(Serialize, Clone)] +struct NewMessageEvent { + group_id: String, + message: String, +} + #[tauri::command] async fn process_message( data: Vec, @@ -333,22 +347,23 @@ async fn process_message( let protocol_message : ProtocolMessage = message.into(); let id = protocol_message.group_id(); - let id = BASE64_URL_SAFE_NO_PAD.encode(id.as_slice()); + let group_id = BASE64_URL_SAFE_NO_PAD.encode(id.as_slice()); let mut groups = state.groups.lock().await; - let Some(group) = groups.get_mut(&id) else { + let Some(group) = groups.get_mut(&group_id) else { return Err(ReceiveMessageError::GroupNotFound); }; let processed_message = group.process_message(state.backend.as_ref(), protocol_message)?; match processed_message.into_content() { ProcessedMessageContent::ApplicationMessage(application_message) => { - //TODO send message to frontend - println!("Application message: {:?}", application_message); + let message = String::from_utf8(application_message.into_bytes())?; + app.emit(NEW_MESSAGE_EVENT, NewMessageEvent { group_id, message })?; + + return Ok(()); }, _ => unimplemented!("Message processed but that type is not implemented yet"), } - Ok(()) }, _ => unimplemented!("Processing messages is not implemented yet"), } diff --git a/src/AppContext.tsx b/src/AppContext.tsx index f05ac9f..1a4fc77 100644 --- a/src/AppContext.tsx +++ b/src/AppContext.tsx @@ -9,9 +9,9 @@ import { createEffect, createResource, createSignal, - onCleanup, useContext, } from "solid-js"; +import { createStore } from "solid-js/store"; const [groups, { mutate: setGroups }] = createResource( async () => (await invoke("get_groups")) as string[] @@ -28,6 +28,8 @@ const [identity, { mutate: setIdentity }] = createResource( })) as string ); +const [messages, setMessages] = createStore>({}); + async function handleMessage(event: MessageEvent) { // Might remove redundant check later if (typeof event.data === "string") @@ -63,6 +65,7 @@ type AppState = { socket: Accessor; groups: Resource; setGroups: Setter; + messages: Record; }; const state = { @@ -71,6 +74,7 @@ const state = { socket, groups, setGroups, + messages, } satisfies AppState; const AppContext = createContext(state); @@ -100,15 +104,41 @@ export function useWebSocket(onmessage?: (event: MessageEvent) => any) { export const useAppState = () => useContext(AppContext); -listen("join_group", (event) => { +function getGroupId(payload: unknown): string { if ( - typeof event.payload !== "object" || - event.payload === null || - !("group_id" in event.payload) || - typeof event.payload.group_id !== "string" + typeof payload !== "object" || + payload === null || + !("group_id" in payload) || + typeof payload.group_id !== "string" ) throw new Error("Unexpected join group event payload"); - const group = event.payload.group_id; + return payload.group_id; +} + +function getMessage(payload: unknown): string { + if ( + typeof payload !== "object" || + payload === null || + !("message" in payload) || + typeof payload.message !== "string" + ) + throw new Error("Unexpected new message event payload"); + + return payload.message; +} + +listen("join_group", (event) => { + const group = getGroupId(event.payload); setGroups((groups) => (groups === undefined ? [group] : [...groups, group])); }); + +listen("new_message", (event) => { + const groupId = getGroupId(event.payload); + const message = getMessage(event.payload); + + //TODO check if using an object (/record) has perfomance impact compared to a map + setMessages(groupId, (messages) => + messages ? [...messages, message] : [message] + ); +}); diff --git a/src/routes/Group.tsx b/src/routes/Group.tsx index 3168d81..cae2dd4 100644 --- a/src/routes/Group.tsx +++ b/src/routes/Group.tsx @@ -13,11 +13,12 @@ async function getPackagesIndex() { export default function Group() { const parameters = useParams(); + const sendMessage = useWebSocket(); const groupId = () => parameters.id; const [packages] = createResource(getPackagesIndex); - const { identity } = useAppState(); + const { identity, messages } = useAppState(); async function invitePackage(id: string) { if (!groupId()) return; @@ -27,8 +28,6 @@ export default function Group() { packageId: id, })) as number[]; - console.debug("invite", { message }); - const data = Uint8Array.from(message); sendMessage(data); } @@ -38,6 +37,7 @@ export default function Group() { // @ts-ignore const message = event.target.message.value; + (event.target as HTMLFormElement).reset(); const data = (await invoke("create_message", { groupId: groupId(), @@ -67,11 +67,14 @@ export default function Group() {

Messages

+
+ + {(message) =>

{message}

}
); } -- 2.51.2