diff --git a/apps/web/src/index.tsx b/apps/web/src/index.tsx index 16eac67..01fd1af 100644 --- a/apps/web/src/index.tsx +++ b/apps/web/src/index.tsx @@ -146,9 +146,11 @@ function AppShell() { - - - + + + + + @@ -162,14 +164,6 @@ function CallInit() { return null; } -function Chat() { - return ( - - - - ); -} - const rootRoute = createRootRoute({ component: RootShell, }); @@ -234,7 +228,7 @@ const homeRoute = createRoute({ const chatRoute = createRoute({ getParentRoute: () => appRoute, path: "chat", - component: Chat, + component: ChatScreen, validateSearch: z.object({ id: z.number().optional(), }), diff --git a/bun.lock b/bun.lock index c93e836..70f4ecb 100644 --- a/bun.lock +++ b/bun.lock @@ -168,6 +168,8 @@ "name": "@tensamin/notifications", "version": "0.0.0", "dependencies": { + "@tauri-apps/api": "^2.11.0", + "@tensamin/chat": "workspace:*", "@tensamin/crypto": "workspace:*", "@tensamin/shared": "workspace:*", "@tensamin/storage": "workspace:*", @@ -1670,6 +1672,8 @@ "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + "@tensamin/notifications/@tauri-apps/api": ["@tauri-apps/api@2.11.0", "", {}, "sha512-7CinYODhky9lmO23xHnUFv0Xt43fbtWMyxZcLcRBlFkcgXKuEirBvHpmtJ89YMhyeGcq20Wuc47Fa4XjyniywA=="], + "@tensamin/shared/sonner": ["sonner@1.7.4", "", { "peerDependencies": { "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-DIS8z4PfJRbIyfVFDVnK9rO3eYDtse4Omcm6bt0oEr5/jtLgysmjuBl1frJ9E/EQZrFmKx2A8m/s5s9CRXIzhw=="], "@tensamin/tauri/lucide-react": ["lucide-react@1.8.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-WuvlsjngSk7TnTBJ1hsCy3ql9V9VOdcPkd3PKcSmM34vJD8KG6molxz7m7zbYFgICwsanQWmJ13JlYs4Zp7Arw=="], diff --git a/packages/call/todo.md b/packages/call/todo.md index cbe5aa7..23ec7d7 100644 --- a/packages/call/todo.md +++ b/packages/call/todo.md @@ -5,4 +5,6 @@ - Avatar in the center - Mobile - Call invite popup + - Save call invite in `calls` array - Sounds +- Fullscreen button for screenshares diff --git a/packages/chat/src/components/message.tsx b/packages/chat/src/components/message.tsx index 419c6fe..db4fc20 100644 --- a/packages/chat/src/components/message.tsx +++ b/packages/chat/src/components/message.tsx @@ -3,7 +3,7 @@ import type { RawMessage } from "../values"; import Text from "@tensamin/markdown/text"; import { AlertTriangle, Clock } from "lucide-react"; -import { Tooltip, TooltipContent, TooltipTrigger } from "@tensamin/ui"; +import { cn, Tooltip, TooltipContent, TooltipTrigger } from "@tensamin/ui"; import { useIsMobile } from "@tensamin/ui"; import { ContextMenu, @@ -20,22 +20,27 @@ function MessageComponent({ }; }) { const isMobile = useIsMobile(); + const actuallyFailed = message.failed && message.message_state === "awaiting"; return (
{message.failed && message.message_state === "awaiting" && ( diff --git a/packages/notifications/package.json b/packages/notifications/package.json index 71c4fb2..af90c45 100644 --- a/packages/notifications/package.json +++ b/packages/notifications/package.json @@ -12,10 +12,12 @@ "build": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@tensamin/ttp": "workspace:*", - "@tensamin/shared": "workspace:*", + "@tauri-apps/api": "^2.11.0", "@tensamin/crypto": "workspace:*", + "@tensamin/shared": "workspace:*", + "@tensamin/chat": "workspace:*", "@tensamin/storage": "workspace:*", + "@tensamin/ttp": "workspace:*", "@tensamin/user": "workspace:*", "react": "^19.2.0", "react-dom": "^19.2.0", diff --git a/packages/notifications/src/context.tsx b/packages/notifications/src/context.tsx index 1367eb5..7ba0d11 100644 --- a/packages/notifications/src/context.tsx +++ b/packages/notifications/src/context.tsx @@ -1,43 +1,92 @@ import { useCrypto } from "@tensamin/crypto/context"; import { useStorage } from "@tensamin/storage/context"; import { useUser } from "@tensamin/user/context"; +import { useChat } from "@tensamin/chat/context"; import { useTTP } from "@tensamin/ttp"; import { createContext, useEffect, useContext } from "react"; import z from "zod"; import { toast as sonnerToast } from "sonner"; import { message as messageSchema } from "@tensamin/shared/data"; +import { Avatar, AvatarFallback, AvatarImage } from "@tensamin/ui"; +import { isTauri } from "@tauri-apps/api/core"; export const context = createContext(undefined); +function reduceDisplay(display: string) { + const words = display.split(" "); + if (words.length === 1) { + return display.slice(0, 2).toUpperCase(); + } else { + return words[0].charAt(0).toUpperCase() + words[1].charAt(0).toUpperCase(); + } +} + export default function Provider(props: { children: React.ReactNode }) { const { subscribePush } = useTTP(); const { load } = useStorage(); const { get } = useUser(); const { decryptText, getSharedSecret } = useCrypto(); + const { addLiveMessage, userId } = useChat(); useEffect(() => { - subscribePush(async (ttpMessage) => { + return subscribePush(async (ttpMessage) => { if (ttpMessage.type === "message_live") { const { message, sender_id } = ttpMessage.data as { message: z.infer; sender_id: number; }; + const user = await get(sender_id); + const decryptedContent = await decryptText( await getSharedSecret( await load("private_key"), await get(await load("user_id")).then((data) => data.public_key), - await get(sender_id).then((data) => data.public_key), + user.public_key, ), message.content, ); - sonnerToast(decryptedContent, { - icon:
A
, - }); + console.log(userId, sender_id, userId === sender_id); + if (userId === sender_id) { + addLiveMessage({ + ...message, + content: decryptedContent, + sent_by_self: false, + }); + + return; + } + + // add notification symbol to conversation cards + + if (isTauri()) { + console.log("weewoo"); + } else { + sonnerToast(user.display, { + classNames: { + content: "pl-4", + }, + description: decryptedContent, + icon: ( + + + {reduceDisplay(user.display)} + + ), + }); + } } }); - }, [subscribePush, decryptText, getSharedSecret, load, get]); + }, [ + subscribePush, + decryptText, + getSharedSecret, + load, + get, + addLiveMessage, + userId, + ]); return ( {} }}> diff --git a/packages/notifications/todo.md b/packages/notifications/todo.md new file mode 100644 index 0000000..599b984 --- /dev/null +++ b/packages/notifications/todo.md @@ -0,0 +1,3 @@ +- Tauri notifications +- Mobile notifications channel setup +- Add notification symbol / outline to user modal diff --git a/packages/shared/src/data.ts b/packages/shared/src/data.ts index 3a73f95..3302875 100644 --- a/packages/shared/src/data.ts +++ b/packages/shared/src/data.ts @@ -22,7 +22,9 @@ export const message = z.object({ tint: z.string().length(7).startsWith("#").optional(), avatar: z.boolean().optional(), display: z.boolean().optional(), - message_state: z.enum(["read", "received", "sent", "sending", "awaiting"]), // awaiting for 'internal' use + message_state: z + .enum(["read", "received", "sent", "sending", "awaiting"]) + .default("received"), // awaiting for 'internal' use }); export const failedUser = {