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 (