diff --git a/packages/app/src/Element/DmWindow.tsx b/packages/app/src/Element/DmWindow.tsx
index 5821845..84e2641 100644
--- a/packages/app/src/Element/DmWindow.tsx
+++ b/packages/app/src/Element/DmWindow.tsx
@@ -8,11 +8,12 @@ import NoteToSelf from "Element/NoteToSelf";
import useLogin from "Hooks/useLogin";
import WriteMessage from "Element/WriteMessage";
import { Chat, ChatType, useChatSystem } from "chat";
+import { Nip4ChatSystem } from "chat/nip4";
export default function DmWindow({ id }: { id: string }) {
const pubKey = useLogin().publicKey;
const dms = useChatSystem();
- const chat = dms.find(a => a.id === id);
+ const chat = dms.find(a => a.id === id) ?? Nip4ChatSystem.createChatObj(id, []);
function sender() {
if (id === pubKey) {
@@ -24,7 +25,7 @@ export default function DmWindow({ id }: { id: string }) {
if (chat?.profile) {
return ;
}
- return ;
+ return ;
}
return (
@@ -34,7 +35,7 @@ export default function DmWindow({ id }: { id: string }) {
{chat && }
-
+
);
diff --git a/packages/app/src/Element/WriteMessage.tsx b/packages/app/src/Element/WriteMessage.tsx
index 490009c..1a3c594 100644
--- a/packages/app/src/Element/WriteMessage.tsx
+++ b/packages/app/src/Element/WriteMessage.tsx
@@ -7,9 +7,9 @@ import useFileUpload from "Upload";
import { openFile } from "SnortUtils";
import Textarea from "./Textarea";
import { System } from "index";
-import { useChatSystem } from "chat";
+import { Chat } from "chat";
-export default function WriteMessage({ chatId }: { chatId: string }) {
+export default function WriteMessage({ chat }: { chat: Chat }) {
const [msg, setMsg] = useState("");
const [sending, setSending] = useState(false);
const [uploading, setUploading] = useState(false);
@@ -17,7 +17,6 @@ export default function WriteMessage({ chatId }: { chatId: string }) {
const [error, setError] = useState("");
const publisher = useEventPublisher();
const uploader = useFileUpload();
- const chat = useChatSystem().find(a => a.id === chatId);
async function attachFile() {
try {
diff --git a/packages/app/src/Pages/ChatPage.css b/packages/app/src/Pages/ChatPage.css
deleted file mode 100644
index f13af8d..0000000
--- a/packages/app/src/Pages/ChatPage.css
+++ /dev/null
@@ -1,3 +0,0 @@
-.chat-page {
- height: calc(100vh - 57px);
-}
diff --git a/packages/app/src/Pages/ChatPage.tsx b/packages/app/src/Pages/ChatPage.tsx
deleted file mode 100644
index d1f3e69..0000000
--- a/packages/app/src/Pages/ChatPage.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import DmWindow from "Element/DmWindow";
-import { useParams } from "react-router-dom";
-import { bech32ToHex } from "SnortUtils";
-
-import "./ChatPage.css";
-
-export default function ChatPage() {
- const { id } = useParams();
-
- return (
-
-
-
- );
-}
diff --git a/packages/app/src/Pages/MessagesPage.tsx b/packages/app/src/Pages/MessagesPage.tsx
index 2c52f3b..a9cdaf4 100644
--- a/packages/app/src/Pages/MessagesPage.tsx
+++ b/packages/app/src/Pages/MessagesPage.tsx
@@ -1,12 +1,12 @@
import React, { useMemo, useState } from "react";
import { FormattedMessage, useIntl } from "react-intl";
-import { useNavigate } from "react-router-dom";
+import { useNavigate, useParams } from "react-router-dom";
import { NostrPrefix } from "@snort/system";
import { useUserProfile } from "@snort/system-react";
import UnreadCount from "Element/UnreadCount";
import ProfileImage, { getDisplayName } from "Element/ProfileImage";
-import { hexToBech32 } from "SnortUtils";
+import { hexToBech32, parseId } from "SnortUtils";
import NoteToSelf from "Element/NoteToSelf";
import useModeration from "Hooks/useModeration";
import useLogin from "Hooks/useLogin";
@@ -29,7 +29,9 @@ export default function MessagesPage() {
const login = useLogin();
const { formatMessage } = useIntl();
const navigate = useNavigate();
- const [chat, setChat] = useState();
+ const { id } = useParams();
+ const parsedId = parseId(id ?? "");
+ const [chat, setChat] = useState(id ? parsedId : undefined);
const pageWidth = usePageWidth();
const chats = useChatSystem();
diff --git a/packages/app/src/SnortUtils/index.ts b/packages/app/src/SnortUtils/index.ts
index b06510c..70be246 100644
--- a/packages/app/src/SnortUtils/index.ts
+++ b/packages/app/src/SnortUtils/index.ts
@@ -107,7 +107,7 @@ export function profileLink(hex: HexKey, relays?: Array | string) {
* Convert hex to bech32
*/
export function hexToBech32(hrp: string, hex?: string) {
- if (typeof hex !== "string" || hex.length === 0 || hex.length % 2 !== 0) {
+ if (typeof hex !== "string" || hex.length === 0 || hex.length % 2 !== 0 || !isHex(hex)) {
return "";
}
@@ -173,6 +173,14 @@ export function deepClone(obj: T) {
}
}
+export function isHex(s: string) {
+ // 48-57 = 0-9
+ // 65-90 = A-Z
+ // 97-122 = a-z
+ return [...s]
+ .map(v => v.charCodeAt(0))
+ .every(v => (v >= 48 && v <= 57) || (v >= 65 && v <= 90) || v >= 97 || v <= 122);
+}
/**
* Simple debounce
*/
diff --git a/packages/app/src/chat/nip4.ts b/packages/app/src/chat/nip4.ts
index 49b033d..48871ed 100644
--- a/packages/app/src/chat/nip4.ts
+++ b/packages/app/src/chat/nip4.ts
@@ -40,25 +40,29 @@ export class Nip4ChatSystem extends ExternalStore> implements ChatSy
listChats(): Chat[] {
const myDms = this.#nip4Events();
- return dedupe(myDms.map(a => a.pubkey)).map(a => {
+ return dedupe(myDms.map(a => chatTo(a))).map(a => {
const messages = myDms.filter(b => chatTo(b) === a || b.pubkey === a);
- const last = lastReadInChat(a);
- return {
- type: ChatType.DirectMessage,
- id: a,
- unread: messages.reduce((acc, v) => (v.created_at > last ? acc++ : acc), 0),
- lastMessage: messages.reduce((acc, v) => (v.created_at > acc ? v.created_at : acc), 0),
- messages,
- createMessage: (msg, pub) => {
- return pub.sendDm(msg, a);
- },
- sendMessage: (ev: NostrEvent, system: SystemInterface) => {
- system.BroadcastEvent(ev);
- },
- } as Chat;
+ return Nip4ChatSystem.createChatObj(a, messages);
});
}
+ static createChatObj(id: string, messages: Array) {
+ const last = lastReadInChat(id);
+ return {
+ type: ChatType.DirectMessage,
+ id,
+ unread: messages.reduce((acc, v) => (v.created_at > last ? acc++ : acc), 0),
+ lastMessage: messages.reduce((acc, v) => (v.created_at > acc ? v.created_at : acc), 0),
+ messages,
+ createMessage: (msg, pub) => {
+ return pub.sendDm(msg, id);
+ },
+ sendMessage: (ev: NostrEvent, system: SystemInterface) => {
+ system.BroadcastEvent(ev);
+ },
+ } as Chat;
+ }
+
#nip4Events() {
return this.#cache.snapshot().filter(a => a.kind === EventKind.DirectMessage);
}
diff --git a/packages/app/src/index.tsx b/packages/app/src/index.tsx
index 348ad87..105766f 100644
--- a/packages/app/src/index.tsx
+++ b/packages/app/src/index.tsx
@@ -21,7 +21,6 @@ import SettingsPage, { SettingsRoutes } from "Pages/SettingsPage";
import ErrorPage from "Pages/ErrorPage";
import VerificationPage from "Pages/Verification";
import MessagesPage from "Pages/MessagesPage";
-import ChatPage from "Pages/ChatPage";
import DonatePage from "Pages/DonatePage";
import HashTagsPage from "Pages/HashTagsPage";
import SearchPage from "Pages/SearchPage";
@@ -123,13 +122,9 @@ export const router = createBrowserRouter([
element: ,
},
{
- path: "/messages",
+ path: "/messages/:id?",
element: ,
},
- {
- path: "/messages/:id",
- element: ,
- },
{
path: "/donate",
element: ,
diff --git a/packages/system/src/nostr-system.ts b/packages/system/src/nostr-system.ts
index b4dc744..38a3e7b 100644
--- a/packages/system/src/nostr-system.ts
+++ b/packages/system/src/nostr-system.ts
@@ -244,7 +244,9 @@ export class NostrSystem extends ExternalStore implements System
*/
BroadcastEvent(ev: NostrEvent) {
for (const [, s] of this.#sockets) {
- s.SendEvent(ev);
+ if (!s.Ephemeral) {
+ s.SendEvent(ev);
+ }
}
}