diff --git a/packages/chat/src/components/input.tsx b/packages/chat/src/components/input.tsx index ae27596..e1356ee 100644 --- a/packages/chat/src/components/input.tsx +++ b/packages/chat/src/components/input.tsx @@ -69,6 +69,8 @@ export default function InputComponent({ return; } + log(3, "chat", "purple", "Message send init, adding live message ..."); + const reference = addLiveMessage({ height: 0, not_encrypted: true, @@ -78,7 +80,19 @@ export default function InputComponent({ message_state: "awaiting", }); - const encryptedContext = await encryptText(sharedSecret, currentValue); + log(3, "chat", "purple", "Live message added, encrypting..."); + + const encryptedContext = await encryptText( + sharedSecret, + currentValue, + ).catch((err) => { + toast("error", "Failed to encrypt message", String(err)); + reference.setFailed(true); + }); + + if (!encryptedContext) return; + + log(3, "chat", "purple", "Content encrypted, sending message..."); send("message_send", { height: 0, @@ -96,6 +110,8 @@ export default function InputComponent({ toast("error", "Failed to send message"); }); + log(3, "chat", "purple", "Message sent"); + moveUserIdToTop(userId); if (!preserveContent) { diff --git a/packages/chat/src/components/message.tsx b/packages/chat/src/components/message.tsx index 51705ff..25090c0 100644 --- a/packages/chat/src/components/message.tsx +++ b/packages/chat/src/components/message.tsx @@ -1,8 +1,8 @@ import * as React from "react"; import type { RawMessage } from "../values"; import Text from "@tensamin/markdown/text"; -import { AlertTriangle } from "lucide-react"; -import { useEffect, useState } from "react"; +import { AlertTriangle, Check, CheckCheck } from "lucide-react"; +import { useCallback, useEffect, useState } from "react"; import type { User } from "@tensamin/user/context"; import { @@ -16,6 +16,8 @@ import { } from "@tensamin/ui"; import MessageContextMenu from "./messageContextMenu"; import Media from "./media"; +import { useStorage } from "@tensamin/storage/context"; +import { useTTP } from "@tensamin/ttp"; function MessageComponent({ grouped, @@ -29,14 +31,14 @@ function MessageComponent({ user: User | null; }) { const actuallyFailed = message.failed && message.message_state === "awaiting"; + + // Fade-in const [hasFadedIn, setHasFadedIn] = useState(false); const opacityClass = !hasFadedIn ? "opacity-0" : actuallyFailed || message.message_state === "awaiting" ? "opacity-50" : "opacity-100"; - - const [isValidURL, setIsValidURL] = useState(false); useEffect(() => { const timeout = window.setTimeout(() => { setHasFadedIn(true); @@ -47,6 +49,8 @@ function MessageComponent({ }; }, []); + // Check if message is a url + const [isValidURL, setIsValidURL] = useState(false); useEffect(() => { try { if (message.content.split(" ").length > 1) throw new Error(); @@ -58,6 +62,58 @@ function MessageComponent({ } }, [message.content]); + // Message states + const { load } = useStorage(); + const { send } = useTTP(); + const messageStateReadUpdate = useCallback(async () => { + const readConfirmations = await load("settings.read_confirmations"); + + if (readConfirmations) { + if (!user?.user_id) return; + + await send("message_state", { + chat_partner_id: user?.user_id, + send_time: message.send_time, + message_state: "read", + }); + } else { + await send("message_state", { + chat_partner_id: user?.user_id, + send_time: message.send_time, + message_state: "received", + }); + } + }, [load, message.send_time, user?.user_id, send]); + useEffect(() => { + let cancelled = false; + + async function run() { + const receiveConfirmations = await load("settings.receive_confirmations"); + if (cancelled) return; + + switch (message.message_state) { + case "sent": + if (receiveConfirmations) { + messageStateReadUpdate(); + } + break; + + case "received": + messageStateReadUpdate(); + break; + + default: + break; + } + } + + run(); + + return () => { + cancelled = true; + }; + }, [message.message_state, load, messageStateReadUpdate]); + return (
+
+ {message.message_state === "read" ? ( + + ) : message.message_state === "received" ? ( + + ) : message.message_state === "sent" ? ( + + ) : message.message_state === "sending" ? ( +
IDK
+ ) : null} +
)} {isValidURL ? ( diff --git a/packages/notifications/src/context.tsx b/packages/notifications/src/context.tsx index 8da45f4..306788e 100644 --- a/packages/notifications/src/context.tsx +++ b/packages/notifications/src/context.tsx @@ -52,20 +52,20 @@ export default function Provider(props: { children: React.ReactNode }) { ); // Update message state - if ( - (await load("settings.read_confirmations")) && - userId === sender_id - ) { - void send( - "message_state", - { - message_state: "read", - }, - { - id: ttpMessage.id, - }, - ); - } else { + if (userId === sender_id) { + addLiveMessage({ + ...message, + content: decryptedContent, + sent_by_self: false, + }); + return; + } + + // todo: add notification symbol to conversation cards (incl. message start) + + moveUserIdToTop(sender_id); + + if (await load("settings.receive_confirmations")) { void send( "message_state", { @@ -77,20 +77,6 @@ export default function Provider(props: { children: React.ReactNode }) { ); } - if (userId === sender_id) { - addLiveMessage({ - ...message, - content: decryptedContent, - sent_by_self: false, - }); - - return; - } - - // todo: add notification symbol to conversation cards (incl. message start) - - moveUserIdToTop(sender_id); - if (isTauri()) { console.log("weewoo"); } else { diff --git a/packages/shared/src/data.ts b/packages/shared/src/data.ts index ee79dd9..86a4da9 100644 --- a/packages/shared/src/data.ts +++ b/packages/shared/src/data.ts @@ -23,8 +23,8 @@ export const message = z.object({ avatar: z.boolean().optional(), display: z.boolean().optional(), message_state: z - .enum(["read", "received", "sent", "sending", "awaiting"]) - .default("received"), // awaiting for 'internal' use + .enum(["read", "received", "sent", "sending", "awaiting"]) // awaiting for 'internal' use + .default("received"), }); export const failedUser = { diff --git a/packages/shared/src/log.tsx b/packages/shared/src/log.tsx index ebea635..0cba6d3 100644 --- a/packages/shared/src/log.tsx +++ b/packages/shared/src/log.tsx @@ -67,26 +67,31 @@ const size = 20; export function toast( type: "error" | "info" | "warn" | "success", message: string, + description?: string, ) { switch (type) { case "error": sonnerToast(message, { icon: , + description, }); break; case "info": sonnerToast(message, { icon: , + description, }); break; case "warn": sonnerToast(message, { icon: , + description, }); break; case "success": sonnerToast(message, { icon: , + description, }); break; } diff --git a/packages/ttp/todo.md b/packages/ttp/todo.md new file mode 100644 index 0000000..a62aa4d --- /dev/null +++ b/packages/ttp/todo.md @@ -0,0 +1,3 @@ +- Replace with MTP + - Remove queue + - Use Date.now() for message id