diff --git a/README.md b/README.md index efee7585..bf2eb9da 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,6 @@ A comprehensive, enterprise-grade multi-signature wallet solution built on Carda - Secure multi-sig staking operations ### Collaboration -- Real-time Nostr-based chat - Discord integration for notifications - Signer verification via message signing - Automated transaction alerts @@ -188,11 +187,11 @@ graph TD ### Database Schema ```prisma model User { - id String @id @default(cuid()) - address String @unique - stakeAddress String @unique - nostrKey String @unique - discordId String @default("") + id String @id @default(cuid()) + address String @unique + stakeAddress String @unique + nostrKey String? @unique + discordId String @default("") } model Wallet { diff --git a/package-lock.json b/package-lock.json index 9ee7a9b0..277b7631 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,6 @@ "hasInstallScript": true, "dependencies": { "@auth/prisma-adapter": "^2.11.1", - "@jinglescode/nostr-chat-plugin": "^0.0.11", "@meshsdk/core": "^1.9.0-beta.87", "@meshsdk/core-csl": "^1.9.0-beta.87", "@meshsdk/core-cst": "^1.9.0-beta.87", @@ -2329,17 +2328,6 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jinglescode/nostr-chat-plugin": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/@jinglescode/nostr-chat-plugin/-/nostr-chat-plugin-0.0.11.tgz", - "integrity": "sha512-teAjUTPqbfI353M+Ip/QcT7LnA4VpUh4jHbbo0xryhajAXlNIYpUDs5bbTa0M4IroU/aMA65Ba8iaqhIta0D0g==", - "dependencies": { - "nostr-tools": "^2.8.0" - }, - "peerDependencies": { - "react": ">=17.0.0" - } - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -16300,52 +16288,6 @@ "node": ">=0.10.0" } }, - "node_modules/nostr-tools": { - "version": "2.23.3", - "resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-2.23.3.tgz", - "integrity": "sha512-AALyt9k8xPdF4UV2mlLJ2mgCn4kpTB0DZ8t2r6wjdUh6anfx2cTVBsHUlo9U0EY/cKC5wcNyiMAmRJV5OVEalA==", - "dependencies": { - "@noble/ciphers": "2.1.1", - "@noble/curves": "2.0.1", - "@noble/hashes": "2.0.1", - "@scure/base": "2.0.0", - "@scure/bip32": "2.0.1", - "@scure/bip39": "2.0.1", - "nostr-wasm": "0.1.0" - }, - "peerDependencies": { - "typescript": ">=5.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/nostr-tools/node_modules/@noble/hashes": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.0.1.tgz", - "integrity": "sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==", - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/nostr-tools/node_modules/@scure/base": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-2.0.0.tgz", - "integrity": "sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w==", - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/nostr-wasm": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/nostr-wasm/-/nostr-wasm-0.1.0.tgz", - "integrity": "sha512-78BTryCLcLYv96ONU8Ws3Q1JzjlAt+43pWQhIl86xZmWeegYCNLPml7yQ+gG3vR6V5h4XGj+TxO+SS5dsThQIA==" - }, "node_modules/npm": { "version": "9.9.4", "resolved": "https://registry.npmjs.org/npm/-/npm-9.9.4.tgz", diff --git a/package.json b/package.json index f72acf6f..61411188 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,6 @@ }, "dependencies": { "@auth/prisma-adapter": "^2.11.1", - "@jinglescode/nostr-chat-plugin": "^0.0.11", "@meshsdk/core": "^1.9.0-beta.87", "@meshsdk/core-csl": "^1.9.0-beta.87", "@meshsdk/core-cst": "^1.9.0-beta.87", diff --git a/prisma/migrations/20260510170000_make_user_nostrkey_optional/migration.sql b/prisma/migrations/20260510170000_make_user_nostrkey_optional/migration.sql new file mode 100644 index 00000000..7d33c422 --- /dev/null +++ b/prisma/migrations/20260510170000_make_user_nostrkey_optional/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "User" ALTER COLUMN "nostrKey" DROP NOT NULL; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 92387e35..537b6f34 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -16,12 +16,12 @@ datasource db { } model User { - id String @id @default(cuid()) - address String @unique - stakeAddress String @unique - drepKeyHash String @default("") - nostrKey String @unique - discordId String @default("") + id String @id @default(cuid()) + address String @unique + stakeAddress String @unique + drepKeyHash String @default("") + nostrKey String? @unique + discordId String @default("") } model Wallet { diff --git a/src/components/common/cardano-objects/connect-wallet.tsx b/src/components/common/cardano-objects/connect-wallet.tsx index a9919c3e..d228fc7d 100644 --- a/src/components/common/cardano-objects/connect-wallet.tsx +++ b/src/components/common/cardano-objects/connect-wallet.tsx @@ -26,7 +26,6 @@ import { } from "@meshsdk/core"; import useUTXOS from "@/hooks/useUTXOS"; import { api } from "@/utils/api"; -import { useNostrChat } from "@jinglescode/nostr-chat-plugin"; import { useWalletContext, WalletState } from "@/hooks/useWalletContext"; import { useToast } from "@/hooks/use-toast"; import { cn } from "@/lib/utils"; @@ -103,7 +102,6 @@ function ConnectWalletContent({ (state) => state.setUserAssetMetadata, ); const { user, isLoading: isUserLoading } = useUser(); - const { generateNsec } = useNostrChat(); const userAddress = useUserStore((state) => state.userAddress); const setUserAddress = useUserStore((state) => state.setUserAddress); const { toast } = useToast(); @@ -472,12 +470,10 @@ function ConnectWalletContent({ // 4) Create or update user (same as normal wallet) if (!isUserLoading) { - const nostrKey = generateNsec(); createUser({ address, stakeAddress, drepKeyHash, - nostrKey: JSON.stringify(nostrKey), }); } @@ -487,7 +483,7 @@ function ConnectWalletContent({ utxosInitializedRef.current = false; } })(); - }, [isUtxosEnabled, utxosWallet, isUserLoading, createUser, generateNsec, setUserAddress, netId]); + }, [isUtxosEnabled, utxosWallet, isUserLoading, createUser, setUserAddress, netId]); // Handle UTXOS wallet assets and network useEffect(() => { diff --git a/src/components/common/overall-layout/layout.tsx b/src/components/common/overall-layout/layout.tsx index 9f6585c3..3efd643d 100644 --- a/src/components/common/overall-layout/layout.tsx +++ b/src/components/common/overall-layout/layout.tsx @@ -1,7 +1,6 @@ import React, { useEffect, Component, ReactNode, useMemo, useCallback, useState, useRef } from "react"; import { useRouter } from "next/router"; import Link from "next/link"; -import { useNostrChat } from "@jinglescode/nostr-chat-plugin"; import { useWallet, useAddress } from "@meshsdk/react"; import { publicRoutes } from "@/data/public-routes"; import { api } from "@/utils/api"; @@ -106,7 +105,6 @@ export default function RootLayout({ const router = useRouter(); const { appWallet } = useAppWallet(); const { multisigWallet } = useMultisigWallet(); - const { generateNsec } = useNostrChat(); const { isEnabled: isUtxosEnabled } = useUTXOS(); const userAddress = useUserStore((state) => state.userAddress); @@ -179,7 +177,6 @@ export default function RootLayout({ address: variables.address, stakeAddress: variables.stakeAddress, drepKeyHash: variables.drepKeyHash ?? "", - nostrKey: variables.nostrKey, } ); } @@ -311,12 +308,10 @@ export default function RootLayout({ } // Create or update user - const nostrKey = generateNsec(); createUser({ address: walletAddress, stakeAddress, drepKeyHash, - nostrKey: JSON.stringify(nostrKey), }); } catch (error) { if (error instanceof Error && error.message.includes("account changed")) { @@ -326,7 +321,7 @@ export default function RootLayout({ } initializeWallet(); - }, [connected, activeWallet, user, userAddress, address, createUser, generateNsec]); + }, [connected, activeWallet, user, userAddress, address, createUser]); // Check wallet session and show authorization modal for first-time connections // Check session as soon as wallet is connected and address is available (don't wait for user) diff --git a/src/components/common/overall-layout/menus/multisig-wallet.tsx b/src/components/common/overall-layout/menus/multisig-wallet.tsx index 4a29fdbc..24148ff5 100644 --- a/src/components/common/overall-layout/menus/multisig-wallet.tsx +++ b/src/components/common/overall-layout/menus/multisig-wallet.tsx @@ -3,7 +3,6 @@ import { useRouter } from "next/router"; import MenuLink from "./menu-link"; import usePendingTransactions from "@/hooks/usePendingTransactions"; import { Badge } from "@/components/ui/badge"; -import { ChatBubbleIcon } from "@radix-ui/react-icons"; import usePendingSignables from "@/hooks/usePendingSignables"; import useMultisigWallet from "@/hooks/useMultisigWallet"; @@ -101,15 +100,6 @@ export default function MenuWallet({ walletId, stakingEnabled }: MenuWalletProps Assets - - - Chat - - {/* Chat and Collaborate */} - -
- Team chat -
-
diff --git a/src/components/pages/wallet/chat/index.tsx b/src/components/pages/wallet/chat/index.tsx deleted file mode 100644 index 37a084be..00000000 --- a/src/components/pages/wallet/chat/index.tsx +++ /dev/null @@ -1,209 +0,0 @@ -import Button from "@/components/common/button"; -import { Label } from "@/components/ui/label"; -import { Textarea } from "@/components/ui/textarea"; -import useAppWallet from "@/hooks/useAppWallet"; -import useUser from "@/hooks/useUser"; -import { useNostrChat } from "@jinglescode/nostr-chat-plugin"; -import { CornerDownLeft } from "lucide-react"; -import { useEffect, useRef, useState } from "react"; -import { api } from "@/utils/api"; -import { AnimatePresence, motion } from "framer-motion"; - -export default function WalletChat() { - const { appWallet } = useAppWallet(); - const { user } = useUser(); - const messagesContainerRef = useRef(null); - const [pubkey, setPubkey] = useState(undefined); - const [connecting, setConnecting] = useState(false); - const [textareaValue, setTextareaValue] = useState(""); - const [usersPubkeyToName, setUsersPubkeyToName] = useState< - { - pubkey: any; - address: string; - name: string | undefined; - }[] - >([]); - - const { data: nostrUsers } = api.user.getNostrKeysByAddresses.useQuery( - { - addresses: appWallet ? appWallet.signersAddresses : [], - }, - { - enabled: appWallet !== undefined, - }, - ); - - const { subscribeRoom, messages, publishMessage, setUser, roomId } = - useNostrChat(); - - useEffect(() => { - if (messagesContainerRef.current) { - messagesContainerRef.current.scrollTop = - messagesContainerRef.current.scrollHeight; - } - }, [messages]); - - useEffect(() => { - async function load() { - if (appWallet && roomId != appWallet.id && !connecting) { - setConnecting(true); - subscribeRoom(appWallet.id); - } - } - load(); - }, [appWallet]); - - useEffect(() => { - if (user && appWallet && nostrUsers && usersPubkeyToName.length === 0) { - const _nostrUsers = nostrUsers.map((user) => { - return { - pubkey: JSON.parse(user.nostrKey).pubkey, - address: user.address, - name: appWallet.signersDescriptions[ - appWallet.signersAddresses.indexOf(user.address) - ], - }; - }); - setUsersPubkeyToName(_nostrUsers); - - const { nsec, pubkey } = JSON.parse(user.nostrKey); - setPubkey(pubkey); - setUser({ nsec, pubkey }); - } - }, [user, appWallet, nostrUsers]); - - function handleSend() { - publishMessage(textareaValue); - setTextareaValue(""); - } - - const handleKeyPress = (event: React.KeyboardEvent) => { - if (event.key === "Enter" && !event.shiftKey) { - event.preventDefault(); - handleSend(); - } - if (event.key === "Enter" && event.shiftKey) { - event.preventDefault(); - setTextareaValue((prev) => prev + "\n"); - } - }; - - if (appWallet === undefined) return <>; - return ( -
-
-
- - {messages && - messages.length > 0 && - [...new Set(messages)] - .sort((a, b) => a.timestamp - b.timestamp) - .map((msg) => ( - -
- {usersPubkeyToName.find((u) => u.pubkey === msg.pubkey) - ?.name && ( -

- { - usersPubkeyToName.find( - (u) => u.pubkey === msg.pubkey, - )?.name - } -

- )} -
-
{msg.message}
-

- {new Date(msg.timestamp * 1000).toLocaleString("en-US", { - weekday: "short", - year: "numeric", - month: "short", - day: "numeric", - hour: "2-digit", - minute: "2-digit", - })} -

-
- ))} -
-
- -
-
- -