From f4088691c5dc7d407d19f6681801d62b047c1fd9 Mon Sep 17 00:00:00 2001 From: Alois Date: Wed, 3 Jun 2026 16:14:39 +0200 Subject: [PATCH] (feat): improve profile popup in the navbar, add shared code to that popup --- apps/web/src/components/modals/profile.tsx | 103 ++++++++++++++++++--- apps/web/src/components/navbar.tsx | 4 +- packages/shared/package.json | 1 + packages/shared/src/code.ts | 20 ++++ 4 files changed, 114 insertions(+), 14 deletions(-) create mode 100644 packages/shared/src/code.ts diff --git a/apps/web/src/components/modals/profile.tsx b/apps/web/src/components/modals/profile.tsx index 6a31e36..c46e4ef 100644 --- a/apps/web/src/components/modals/profile.tsx +++ b/apps/web/src/components/modals/profile.tsx @@ -1,8 +1,62 @@ import type { User } from "@tensamin/user/context"; -import { Avatar, AvatarFallback, AvatarImage } from "@tensamin/ui"; +import { + Avatar, + AvatarFallback, + AvatarImage, + Button, + Tooltip, + TooltipContent, + TooltipTrigger, +} from "@tensamin/ui"; +import { toLossySixDigitCode } from "@tensamin/shared/code"; import Text from "@tensamin/markdown/text"; +import { ChevronDown, ChevronUp, Info } from "lucide-react"; +import { useEffect, useState } from "react"; +import { useCrypto } from "@tensamin/crypto/context"; +import { useStorage } from "@tensamin/storage/context"; +import { useUser } from "@tensamin/user/context"; export default function Profile({ user }: { user: User }) { + const [showAdvancedInformation, setShowAdvancedInformation] = useState(false); + const [sharedSecret, setSharedSecret] = useState(""); + + const { getSharedSecret } = useCrypto(); + const { load } = useStorage(); + const { get } = useUser(); + + useEffect(() => { + let active = true; + + void (async () => { + try { + const ownId = await load("user_id"); + const privateKey = await load("private_key"); + const ownData = await get(ownId); + const secret = await getSharedSecret( + privateKey, + ownData.public_key, + user.public_key, + ); + + if (active) { + setSharedSecret(secret); + } + } catch { + if (active) { + setSharedSecret(""); + } + } + })(); + + return () => { + active = false; + }; + }, [get, getSharedSecret, load, user.public_key]); + + const sharedSecretCode = sharedSecret + ? toLossySixDigitCode(sharedSecret) + : "------"; + return (
@@ -18,17 +72,42 @@ export default function Profile({ user }: { user: User }) {
-
-

- iota: {user.iota_id} -

-

- user: {user.user_id} -

-

- pub key: {user.public_key} -

-
+ + {showAdvancedInformation && ( +
+

+ Shared Code: {sharedSecretCode}{" "} + + } /> + + You can compare this code with the conversation partner to + validate that this chat is E2EE + + +

+

+ Iota ID: {user.iota_id} +

+

+ User ID: {user.user_id} +

+

+ Public Key: {user.public_key} +

+
+ )} ); } diff --git a/apps/web/src/components/navbar.tsx b/apps/web/src/components/navbar.tsx index 1c48c6c..cb83cb6 100644 --- a/apps/web/src/components/navbar.tsx +++ b/apps/web/src/components/navbar.tsx @@ -74,7 +74,7 @@ export default function Navbar({ forMobile }: { forMobile: boolean }) { userId={id} component={(user) => isMobile ? ( -

{user?.display}

+

{user?.display}

) : ( -

{user?.display}

+

{user?.display}

} /> diff --git a/packages/shared/package.json b/packages/shared/package.json index 3d06116..26c0d9f 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -4,6 +4,7 @@ "version": "0.0.0", "type": "module", "exports": { + "./code": "./src/code.ts", "./data": "./src/data.ts", "./desktopMedia": "./src/desktopMedia.tsx", "./log": "./src/log.tsx", diff --git a/packages/shared/src/code.ts b/packages/shared/src/code.ts new file mode 100644 index 0000000..1f1d10e --- /dev/null +++ b/packages/shared/src/code.ts @@ -0,0 +1,20 @@ +export function toLossySixDigitCode(input: string): string { + let firstHash = 0xdeadbeef; + let secondHash = 0x41c6ce57; + + for (let index = 0; index < input.length; index += 1) { + const character = input.charCodeAt(index); + + firstHash = Math.imul(firstHash ^ character, 2654435761); + secondHash = Math.imul(secondHash ^ character, 1597334677); + } + + firstHash = Math.imul(firstHash ^ (firstHash >>> 16), 2246822507); + firstHash ^= Math.imul(secondHash ^ (secondHash >>> 13), 3266489909); + secondHash = Math.imul(secondHash ^ (secondHash >>> 16), 2246822507); + secondHash ^= Math.imul(firstHash ^ (firstHash >>> 13), 3266489909); + + const hash = 4294967296 * (2097151 & secondHash) + (firstHash >>> 0); + + return String(hash % 1_000_000).padStart(6, "0"); +}