fixes #586
This commit is contained in:
2023-06-26 12:29:12 +01:00
parent 7c1d5273fc
commit 379bce6f9e
9 changed files with 43 additions and 50 deletions

View File

@ -8,11 +8,12 @@ import NoteToSelf from "Element/NoteToSelf";
import useLogin from "Hooks/useLogin"; import useLogin from "Hooks/useLogin";
import WriteMessage from "Element/WriteMessage"; import WriteMessage from "Element/WriteMessage";
import { Chat, ChatType, useChatSystem } from "chat"; import { Chat, ChatType, useChatSystem } from "chat";
import { Nip4ChatSystem } from "chat/nip4";
export default function DmWindow({ id }: { id: string }) { export default function DmWindow({ id }: { id: string }) {
const pubKey = useLogin().publicKey; const pubKey = useLogin().publicKey;
const dms = useChatSystem(); const dms = useChatSystem();
const chat = dms.find(a => a.id === id); const chat = dms.find(a => a.id === id) ?? Nip4ChatSystem.createChatObj(id, []);
function sender() { function sender() {
if (id === pubKey) { if (id === pubKey) {
@ -24,7 +25,7 @@ export default function DmWindow({ id }: { id: string }) {
if (chat?.profile) { if (chat?.profile) {
return <ProfileImage pubkey={id} className="f-grow mb10" profile={chat.profile} />; return <ProfileImage pubkey={id} className="f-grow mb10" profile={chat.profile} />;
} }
return <ProfileImage pubkey={""} className="f-grow mb10" overrideUsername={chat?.id} />; return <ProfileImage pubkey={id ?? ""} className="f-grow mb10" overrideUsername={chat?.id} />;
} }
return ( return (
@ -34,7 +35,7 @@ export default function DmWindow({ id }: { id: string }) {
<div className="flex f-col">{chat && <DmChatSelected chat={chat} />}</div> <div className="flex f-col">{chat && <DmChatSelected chat={chat} />}</div>
</div> </div>
<div> <div>
<WriteMessage chatId={id} /> <WriteMessage chat={chat} />
</div> </div>
</div> </div>
); );

View File

@ -7,9 +7,9 @@ import useFileUpload from "Upload";
import { openFile } from "SnortUtils"; import { openFile } from "SnortUtils";
import Textarea from "./Textarea"; import Textarea from "./Textarea";
import { System } from "index"; 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 [msg, setMsg] = useState("");
const [sending, setSending] = useState(false); const [sending, setSending] = useState(false);
const [uploading, setUploading] = useState(false); const [uploading, setUploading] = useState(false);
@ -17,7 +17,6 @@ export default function WriteMessage({ chatId }: { chatId: string }) {
const [error, setError] = useState(""); const [error, setError] = useState("");
const publisher = useEventPublisher(); const publisher = useEventPublisher();
const uploader = useFileUpload(); const uploader = useFileUpload();
const chat = useChatSystem().find(a => a.id === chatId);
async function attachFile() { async function attachFile() {
try { try {

View File

@ -1,3 +0,0 @@
.chat-page {
height: calc(100vh - 57px);
}

View File

@ -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 (
<div className="chat-page">
<DmWindow id={bech32ToHex(id ?? "")} />
</div>
);
}

View File

@ -1,12 +1,12 @@
import React, { useMemo, useState } from "react"; import React, { useMemo, useState } from "react";
import { FormattedMessage, useIntl } from "react-intl"; 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 { NostrPrefix } from "@snort/system";
import { useUserProfile } from "@snort/system-react"; import { useUserProfile } from "@snort/system-react";
import UnreadCount from "Element/UnreadCount"; import UnreadCount from "Element/UnreadCount";
import ProfileImage, { getDisplayName } from "Element/ProfileImage"; import ProfileImage, { getDisplayName } from "Element/ProfileImage";
import { hexToBech32 } from "SnortUtils"; import { hexToBech32, parseId } from "SnortUtils";
import NoteToSelf from "Element/NoteToSelf"; import NoteToSelf from "Element/NoteToSelf";
import useModeration from "Hooks/useModeration"; import useModeration from "Hooks/useModeration";
import useLogin from "Hooks/useLogin"; import useLogin from "Hooks/useLogin";
@ -29,7 +29,9 @@ export default function MessagesPage() {
const login = useLogin(); const login = useLogin();
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
const navigate = useNavigate(); const navigate = useNavigate();
const [chat, setChat] = useState<string>(); const { id } = useParams();
const parsedId = parseId(id ?? "");
const [chat, setChat] = useState(id ? parsedId : undefined);
const pageWidth = usePageWidth(); const pageWidth = usePageWidth();
const chats = useChatSystem(); const chats = useChatSystem();

View File

@ -107,7 +107,7 @@ export function profileLink(hex: HexKey, relays?: Array<string> | string) {
* Convert hex to bech32 * Convert hex to bech32
*/ */
export function hexToBech32(hrp: string, hex?: string) { 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 ""; return "";
} }
@ -173,6 +173,14 @@ export function deepClone<T>(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 * Simple debounce
*/ */

View File

@ -40,25 +40,29 @@ export class Nip4ChatSystem extends ExternalStore<Array<Chat>> implements ChatSy
listChats(): Chat[] { listChats(): Chat[] {
const myDms = this.#nip4Events(); 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 messages = myDms.filter(b => chatTo(b) === a || b.pubkey === a);
const last = lastReadInChat(a); return Nip4ChatSystem.createChatObj(a, messages);
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;
}); });
} }
static createChatObj(id: string, messages: Array<NostrEvent>) {
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() { #nip4Events() {
return this.#cache.snapshot().filter(a => a.kind === EventKind.DirectMessage); return this.#cache.snapshot().filter(a => a.kind === EventKind.DirectMessage);
} }

View File

@ -21,7 +21,6 @@ import SettingsPage, { SettingsRoutes } from "Pages/SettingsPage";
import ErrorPage from "Pages/ErrorPage"; import ErrorPage from "Pages/ErrorPage";
import VerificationPage from "Pages/Verification"; import VerificationPage from "Pages/Verification";
import MessagesPage from "Pages/MessagesPage"; import MessagesPage from "Pages/MessagesPage";
import ChatPage from "Pages/ChatPage";
import DonatePage from "Pages/DonatePage"; import DonatePage from "Pages/DonatePage";
import HashTagsPage from "Pages/HashTagsPage"; import HashTagsPage from "Pages/HashTagsPage";
import SearchPage from "Pages/SearchPage"; import SearchPage from "Pages/SearchPage";
@ -123,13 +122,9 @@ export const router = createBrowserRouter([
element: <VerificationPage />, element: <VerificationPage />,
}, },
{ {
path: "/messages", path: "/messages/:id?",
element: <MessagesPage />, element: <MessagesPage />,
}, },
{
path: "/messages/:id",
element: <ChatPage />,
},
{ {
path: "/donate", path: "/donate",
element: <DonatePage />, element: <DonatePage />,

View File

@ -244,7 +244,9 @@ export class NostrSystem extends ExternalStore<SystemSnapshot> implements System
*/ */
BroadcastEvent(ev: NostrEvent) { BroadcastEvent(ev: NostrEvent) {
for (const [, s] of this.#sockets) { for (const [, s] of this.#sockets) {
s.SendEvent(ev); if (!s.Ephemeral) {
s.SendEvent(ev);
}
} }
} }