import "./MessagesPage.css"; import React, { useEffect, useMemo, useState } from "react"; import { FormattedMessage, useIntl } from "react-intl"; import { useNavigate, useParams } from "react-router-dom"; import { NostrLink, NostrPrefix, TLVEntryType, UserMetadata, decodeTLV } from "@snort/system"; import { useUserProfile, useUserSearch } from "@snort/system-react"; import UnreadCount from "Element/UnreadCount"; import ProfileImage, { getDisplayName } from "Element/ProfileImage"; import { appendDedupe, debounce, parseId } from "SnortUtils"; import NoteToSelf from "Element/NoteToSelf"; import useModeration from "Hooks/useModeration"; import useLogin from "Hooks/useLogin"; import usePageWidth from "Hooks/usePageWidth"; import NoteTime from "Element/NoteTime"; import DmWindow from "Element/DmWindow"; import Avatar from "Element/Avatar"; import Icon from "Icons/Icon"; import Text from "Element/Text"; import { Chat, ChatType, createChatLink, useChatSystem } from "chat"; import Modal from "Element/Modal"; import ProfilePreview from "Element/ProfilePreview"; import { useEventFeed } from "Feed/EventFeed"; import { LoginSession, LoginStore } from "Login"; import { Nip28ChatSystem } from "chat/nip28"; import { ChatParticipantProfile } from "Element/ChatParticipant"; const TwoCol = 768; const ThreeCol = 1500; export default function MessagesPage() { const login = useLogin(); const { formatMessage } = useIntl(); const navigate = useNavigate(); const { id } = useParams(); const [chat, setChat] = useState(); const pageWidth = usePageWidth(); useEffect(() => { const parsedId = parseId(id ?? ""); setChat(id ? parsedId : undefined); }, [id]); const chats = useChatSystem(); const unreadCount = useMemo(() => chats.reduce((p, c) => p + c.unread, 0), [chats]); function openChat(e: React.MouseEvent, type: ChatType, id: string) { e.stopPropagation(); e.preventDefault(); navigate(`/messages/${encodeURIComponent(id)}`); } function noteToSelf(chat: Chat) { return (
openChat(e, chat.type, chat.id)}>
); } function conversationIdent(cx: Chat) { if (cx.participants.length === 1) { return ; } else { return (
{cx.participants.map(v => ( ))} {cx.title ?? }
); } } function conversation(cx: Chat) { if (!login.publicKey) return null; const participants = cx.participants.map(a => a.id); if (participants.length === 1 && participants[0] === login.publicKey) return noteToSelf(cx); const isActive = cx.id === chat; return (
openChat(e, cx.type, cx.id)}> {conversationIdent(cx)}
{cx.unread > 0 && }
); } return (
{(pageWidth >= TwoCol || !chat) && (
{chats .sort((a, b) => { const aSelf = a.participants.length === 1 && a.participants[0].id === login.publicKey; const bSelf = b.participants.length === 1 && b.participants[0].id === login.publicKey; if (aSelf || bSelf) { return aSelf ? -1 : 1; } return b.lastMessage > a.lastMessage ? 1 : -1; }) .map(conversation)}
)} {chat ? : pageWidth >= TwoCol &&
} {pageWidth >= ThreeCol && chat && (
)}
); } function ProfileDmActions({ id }: { id: string }) { const authors = decodeTLV(id) .filter(a => a.type === TLVEntryType.Author) .map(a => a.value as string); const pubkey = authors[0]; const profile = useUserProfile(pubkey); const { block, unblock, isBlocked } = useModeration(); function truncAbout(s?: string) { if ((s?.length ?? 0) > 200) { return `${s?.slice(0, 200)}...`; } return s; } const blocked = isBlocked(pubkey); return ( <>

{getDisplayName(profile, pubkey)}

(blocked ? unblock(pubkey) : block(pubkey))}> {blocked ? : }
); } function NewChatWindow() { const [show, setShow] = useState(false); const [newChat, setNewChat] = useState>([]); const [results, setResults] = useState>([]); const [term, setSearchTerm] = useState(""); const navigate = useNavigate(); const search = useUserSearch(); const login = useLogin(); useEffect(() => { setNewChat([]); setSearchTerm(""); setResults(login.follows.item); }, [show]); useEffect(() => { return debounce(500, () => { if (term) { search(term).then(setResults); } else { setResults(login.follows.item); } }); }, [term]); function togglePubkey(a: string) { setNewChat(c => (c.includes(a) ? c.filter(v => v !== a) : appendDedupe(c, [a]))); } function startChat() { setShow(false); if (newChat.length === 1) { navigate(createChatLink(ChatType.DirectMessage, newChat[0])); } else { navigate(createChatLink(ChatType.PrivateGroupChat, ...newChat)); } } return ( <> {show && ( setShow(false)} className="new-chat-modal">

setSearchTerm(e.target.value)} />
{newChat.map(a => ( togglePubkey(a)} /> ))}

{results.map(a => { return ( } onClick={() => togglePubkey(a)} className={newChat.includes(a) ? "active" : undefined} /> ); })} {results.length === 1 && ( { setShow(false); LoginStore.updateSession({ ...login, extraChats: appendDedupe(login.extraChats, [Nip28ChatSystem.chatId(id)]), } as LoginSession); navigate(createChatLink(ChatType.PublicGroupChat, id)); }} /> )}
)} ); } export function Nip28ChatProfile({ id, onClick }: { id: string; onClick: (id: string) => void }) { const channel = useEventFeed(new NostrLink(NostrPrefix.Event, id, 40)); if (channel?.data) { const meta = JSON.parse(channel.data.content) as UserMetadata; return ( } onClick={() => onClick(id)} /> ); } }