diff --git a/packages/react-chat-example/src/index.tsx b/packages/react-chat-example/src/index.tsx index a06392b8..695fbcbc 100644 --- a/packages/react-chat-example/src/index.tsx +++ b/packages/react-chat-example/src/index.tsx @@ -76,7 +76,7 @@ function DragDiv() { diff --git a/packages/react-chat/src/components/Chat.tsx b/packages/react-chat/src/components/Chat.tsx index 1885fae9..d4f2fe06 100644 --- a/packages/react-chat/src/components/Chat.tsx +++ b/packages/react-chat/src/components/Chat.tsx @@ -10,6 +10,7 @@ import { Community } from "./Community"; import { Members } from "./Members/Members"; import { CommunityModal } from "./Modals/CommunityModal"; import { EditModal } from "./Modals/EditModal"; +import { ProfileModal } from "./Modals/ProfileModal"; export function Chat() { const [showMembers, setShowMembers] = useState(true); @@ -55,6 +56,7 @@ export function Chat() { )} + ); } diff --git a/packages/react-chat/src/components/Chat/ChatCreation.tsx b/packages/react-chat/src/components/Chat/ChatCreation.tsx index 4ea53ae4..51d041b1 100644 --- a/packages/react-chat/src/components/Chat/ChatCreation.tsx +++ b/packages/react-chat/src/components/Chat/ChatCreation.tsx @@ -123,7 +123,7 @@ export function ChatCreation({ Contacts - {contacts + {Object.values(contacts) .filter( (e) => e.id != bufToHex(identity.publicKey) && @@ -132,7 +132,7 @@ export function ChatCreation({ .map((contact) => ( addMember(contact.id)} /> diff --git a/packages/react-chat/src/components/Chat/ChatMessageContent.tsx b/packages/react-chat/src/components/Chat/ChatMessageContent.tsx index f9361cfe..d5b2258e 100644 --- a/packages/react-chat/src/components/Chat/ChatMessageContent.tsx +++ b/packages/react-chat/src/components/Chat/ChatMessageContent.tsx @@ -3,10 +3,29 @@ import React, { useEffect, useMemo, useState } from "react"; import styled from "styled-components"; import { useFetchMetadata } from "../../contexts/fetchMetadataProvider"; +import { useMessengerContext } from "../../contexts/messengerProvider"; import { ChatMessage } from "../../models/ChatMessage"; import { Metadata } from "../../models/Metadata"; +import { ContactMenu } from "../Form/ContactMenu"; import { ImageMenu } from "../Form/ImageMenu"; +interface MentionProps { + id: string; +} + +function Mention({ id }: MentionProps) { + const { contacts } = useMessengerContext(); + const contact = useMemo(() => contacts[id.slice(1)], [id, contacts]); + const [showMenu, setShowMenu] = useState(false); + if (!contact) return <>{id}; + return ( + setShowMenu(!showMenu)}> + {`@${contact.customName ?? contact.id}`} + {showMenu && } + + ); +} + type ChatMessageContentProps = { message: ChatMessage; setImage: (image: string) => void; @@ -40,7 +59,7 @@ export function ChatMessageContent({ ]; } if (element.startsWith("@")) { - return [{element}, " "]; + return [, " "]; } return [element, " "]; }); @@ -158,9 +177,10 @@ const ContentWrapper = styled.div` flex-direction: column; `; -const Mention = styled.span` +const MentionSpan = styled.span` color: blue; font-weight: 500; + position: relative; `; const Link = styled.a` diff --git a/packages/react-chat/src/components/Chat/ChatMessages.tsx b/packages/react-chat/src/components/Chat/ChatMessages.tsx index 44a319a1..cf508a92 100644 --- a/packages/react-chat/src/components/Chat/ChatMessages.tsx +++ b/packages/react-chat/src/components/Chat/ChatMessages.tsx @@ -1,7 +1,6 @@ import React, { useEffect, useMemo, useRef, useState } from "react"; import styled from "styled-components"; -import { useBlockedUsers } from "../../contexts/blockedUsersProvider"; import { useMessengerContext } from "../../contexts/messengerProvider"; import { useModal } from "../../contexts/modalProvider"; import { useChatScrollHandle } from "../../hooks/useChatScrollHandle"; @@ -14,7 +13,6 @@ import { UntrustworthIcon } from "../Icons/UntrustworthIcon"; import { UserIcon } from "../Icons/UserIcon"; import { LinkModal, LinkModalName } from "../Modals/LinkModal"; import { PictureModal, PictureModalName } from "../Modals/PictureModal"; -import { ProfileModal } from "../Modals/ProfileModal"; import { textMediumStyles, textSmallStyles } from "../Text"; import { ChatMessageContent } from "./ChatMessageContent"; @@ -27,10 +25,6 @@ type ChatUiMessageProps = { prevMessage: ChatMessage; setImage: (img: string) => void; setLink: (link: string) => void; - setUser: (user: string) => void; - customName?: string; - trueName?: string; - setRenaming: (val: boolean) => void; }; function ChatUiMessage({ @@ -39,13 +33,13 @@ function ChatUiMessage({ prevMessage, setImage, setLink, - setUser, - customName, - trueName, - setRenaming, }: ChatUiMessageProps) { + const { contacts } = useMessengerContext(); + const contact = useMemo( + () => contacts[message.sender], + [message.sender, contacts] + ); const [showMenu, setShowMenu] = useState(false); - const [isUntrustworthy, setIsUntrustworthy] = useState(false); return ( @@ -60,19 +54,10 @@ function ChatUiMessage({ { setShowMenu((e) => !e); - setUser(message.sender); }} > {showMenu && ( - + )} @@ -82,14 +67,14 @@ function ChatUiMessage({ {" "} - {customName ? customName : message.sender.slice(0, 10)} + {contact.customName ?? message.sender.slice(0, 10)} - {customName && ( + {contact.customName && ( {message.sender.slice(0, 5)}...{message.sender.slice(-3)} )} - {isUntrustworthy && } + {contact.isUntrustworthy && } {message.date.toLocaleString()} @@ -107,20 +92,20 @@ function ChatUiMessage({ } export function ChatMessages() { - const { messages, activeChannel } = useMessengerContext(); + const { messages, activeChannel, contacts } = useMessengerContext(); const ref = useRef(null); const loadingMessages = useChatScrollHandle(messages, ref, activeChannel.id); - const { blockedUsers } = useBlockedUsers(); - const shownMessages = useMemo( - () => messages.filter((message) => !blockedUsers.includes(message.sender)), - [blockedUsers, messages, messages.length] + () => + messages.filter( + (message) => !contacts?.[message.sender]?.blocked ?? true + ), + [contacts, messages, messages.length] ); const [image, setImage] = useState(""); const [link, setLink] = useState(""); - const [user, setUser] = useState(""); const { setModal: setPictureModal, isVisible: showPictureModal } = useModal(PictureModalName); @@ -136,23 +121,10 @@ export function ChatMessages() { ); useEffect(() => (!showLinkModal ? setLink("") : undefined), [showLinkModal]); - const [renaming, setRenaming] = useState(false); - const [customName, setCustomName] = useState(""); - const [trueName, setTrueName] = useState(""); - return ( - {loadingMessages && ( @@ -167,10 +139,6 @@ export function ChatMessages() { prevMessage={shownMessages[idx - 1]} setLink={setLink} setImage={setImage} - setUser={setUser} - customName={customName} - trueName={trueName} - setRenaming={setRenaming} /> ))} diff --git a/packages/react-chat/src/components/Form/ContactMenu.tsx b/packages/react-chat/src/components/Form/ContactMenu.tsx index a9c73a87..58fedc89 100644 --- a/packages/react-chat/src/components/Form/ContactMenu.tsx +++ b/packages/react-chat/src/components/Form/ContactMenu.tsx @@ -1,11 +1,12 @@ import React, { useMemo } from "react"; +import { bufToHex } from "status-communities/dist/cjs/utils"; import styled from "styled-components"; -import { useBlockedUsers } from "../../contexts/blockedUsersProvider"; import { useFriends } from "../../contexts/friendsProvider"; +import { useIdentity } from "../../contexts/identityProvider"; import { useModal } from "../../contexts/modalProvider"; -import { ChatMessage } from "../../models/ChatMessage"; -import { Icon, UserAddress } from "../Chat/ChatMessages"; +import { useManageContact } from "../../hooks/useManageContact"; +import { UserAddress } from "../Chat/ChatMessages"; import { AddContactSvg } from "../Icons/AddContactIcon"; import { BlockSvg } from "../Icons/BlockIcon"; import { ChatSvg } from "../Icons/ChatIcon"; @@ -20,63 +21,45 @@ import { textMediumStyles } from "../Text"; import { DropdownMenu, MenuItem, MenuText } from "./DropdownMenu"; type ContactMenuProps = { - message: ChatMessage; + id: string; setShowMenu: (val: boolean) => void; - isUntrustworthy: boolean; - setIsUntrustworthy: (val: boolean) => void; - customName?: string; - trueName?: string; - setRenaming: (val: boolean) => void; }; -export function ContactMenu({ - message, - setShowMenu, - isUntrustworthy, - setIsUntrustworthy, - customName, - trueName, - setRenaming, -}: ContactMenuProps) { - const id = message.sender; - const { blockedUsers, setBlockedUsers } = useBlockedUsers(); - - const userInBlocked = useMemo( - () => blockedUsers.includes(id), - [blockedUsers, id] +export function ContactMenu({ id, setShowMenu }: ContactMenuProps) { + const identity = useIdentity(); + const isUser = useMemo( + () => id === bufToHex(identity.publicKey), + [id, identity] ); + const { setModal } = useModal(ProfileModalName); const { friends, setFriends } = useFriends(); const userIsFriend = useMemo(() => friends.includes(id), [friends, id]); + const { contact, setBlocked, setIsUntrustworthy } = useManageContact(id); - const { setModal } = useModal(ProfileModalName); - + if (!contact) return null; return ( - {message.image ? ( - - ) : ( - - )} + - - {customName ? customName : message.sender.slice(0, 10)} - - {isUntrustworthy && } + {contact.customName ?? id.slice(0, 10)} + {contact.isUntrustworthy && } - {trueName && ({trueName})} + {contact.customName && ( + ({contact.trueName}) + )} - {message.sender.slice(0, 10)}...{message.sender.slice(-3)} + {id.slice(0, 10)}...{id.slice(-3)} - setModal(true)}> + { + setModal({ id, renamingState: false }); + }} + > View Profile @@ -94,8 +77,7 @@ export function ContactMenu({ )} { - setModal(true); - setRenaming(true); + setModal({ id, renamingState: true }); }} > @@ -103,30 +85,30 @@ export function ContactMenu({ - setIsUntrustworthy(!isUntrustworthy)}> + setIsUntrustworthy(!contact.isUntrustworthy)}> - - {isUntrustworthy + + {contact.isUntrustworthy ? "Remove Untrustworthy Mark" : "Mark as Untrustworthy"} - {!userIsFriend && ( + {!userIsFriend && !isUser && ( { - userInBlocked - ? setBlockedUsers((prev) => prev.filter((e) => e != id)) - : setBlockedUsers((prev) => [...prev, id]); + setBlocked(!contact.blocked); setShowMenu(false); }} > - Block User + + {contact.blocked ? "Unblock User" : "Block User"} + )} diff --git a/packages/react-chat/src/components/Members/Member.tsx b/packages/react-chat/src/components/Members/Member.tsx index 56fe65f9..d3b0dbe9 100644 --- a/packages/react-chat/src/components/Members/Member.tsx +++ b/packages/react-chat/src/components/Members/Member.tsx @@ -1,12 +1,13 @@ -import React from "react"; +import React, { useState } from "react"; import styled from "styled-components"; +import { Contact } from "../../models/Contact"; import { Icon } from "../Chat/ChatMessages"; -// import { ContactMenu } from '../Form/ContactMenu'; +import { ContactMenu } from "../Form/ContactMenu"; import { UserIcon } from "../Icons/UserIcon"; interface MemberProps { - member: string; + contact: Contact; isOnline?: boolean; switchShowMembers?: () => void; setMembersList?: any; @@ -14,7 +15,7 @@ interface MemberProps { } export function Member({ - member, + contact, isOnline, switchShowMembers, setMembersList, @@ -30,11 +31,11 @@ export function Member({ }); }; - // const [showMenu, setShowMenu] = useState(false); + const [showMenu, setShowMenu] = useState(false); const onMemberClick = () => { switchShowMembers?.(); - startDialog(member); + startDialog(contact.id); }; return ( @@ -44,12 +45,12 @@ export function Member({ backgroundImage: "unset", }} className={isOnline ? "online" : "offline"} - // onClick={() => setShowMenu(e => !e)} + onClick={() => setShowMenu((e) => !e)} > - {/* {showMenu && } */} + {showMenu && } - {member} + {contact.customName ?? contact.id} ); } diff --git a/packages/react-chat/src/components/Members/MembersList.tsx b/packages/react-chat/src/components/Members/MembersList.tsx index 7e5209b1..9b708838 100644 --- a/packages/react-chat/src/components/Members/MembersList.tsx +++ b/packages/react-chat/src/components/Members/MembersList.tsx @@ -34,13 +34,13 @@ export function MembersList({ Online - {contacts + {Object.values(contacts) .filter((e) => e.id != bufToHex(identity.publicKey)) .filter((e) => e.online) .map((contact) => ( Offline - {contacts + {Object.values(contacts) .filter((e) => e.id != bufToHex(identity.publicKey)) .filter((e) => !e.online) .map((contact) => ( void; - setTrueName: (val: string) => void; - setCustomName: (val: string) => void; -} + renamingState?: boolean; +}; -export const ProfileModal = ({ - user, - image, - renaming, - customName, - trueName, - setRenaming, - setTrueName, - setCustomName, -}: ProfileModalProps) => { - const [isUntrustworthy, setIsUntrustworthy] = useState(false); +export const ProfileModal = () => { + const { props } = useModal(ProfileModalName); + const { id, image, renamingState } = useMemo( + () => (props ? props : { id: "" }), + [props] + ); - const { blockedUsers, setBlockedUsers } = useBlockedUsers(); - - const userInBlocked = useMemo( - () => blockedUsers.includes(user), - [blockedUsers, user] + const identity = useIdentity(); + const isUser = useMemo( + () => id === bufToHex(identity.publicKey), + [id, identity] ); const { friends, setFriends } = useFriends(); - const userIsFriend = useMemo(() => friends.includes(user), [friends, user]); + const userIsFriend = useMemo(() => friends.includes(id), [friends, id]); + const [renaming, setRenaming] = useState(renamingState ?? false); + useEffect(() => { + setRenaming(renamingState ?? false); + }, [renamingState]); + + const { contact, setBlocked, setCustomName, setIsUntrustworthy } = + useManageContact(id); + const [customNameInput, setCustomNameInput] = useState(""); + + if (!contact) return null; return (
- {user.slice(0, 10)}’s Profile + {id.slice(0, 10)}’s Profile
@@ -70,8 +72,8 @@ export const ProfileModal = ({ )} - {customName ? customName : user.slice(0, 10)} - {isUntrustworthy && } + {contact.customName ?? id.slice(0, 10)} + {contact.isUntrustworthy && } {!renaming && ( )} - {trueName && {trueName}} - {trueName && } + {contact.customName && ( + {contact.trueName} + )} {renaming ? ( setCustomName(e.currentTarget.value)} + value={contact.customName} + onChange={(e) => setCustomNameInput(e.currentTarget.value)} /> - {customName && ( + {contact.customName && ( { - setCustomName(""); - setTrueName(""); + setCustomName(undefined); + setCustomNameInput(""); }} > @@ -103,9 +106,9 @@ export const ProfileModal = ({ ) : ( <> - Chatkey: {user.slice(0, 30)} + Chatkey: {id.slice(0, 30)} - copy(user)}> + copy(id)}> @@ -120,9 +123,9 @@ export const ProfileModal = ({ { - setTrueName(user.slice(0, 10)); + setCustomName(customNameInput); setRenaming(false); }} > @@ -131,38 +134,36 @@ export const ProfileModal = ({ ) : ( <> - {!userIsFriend && ( + {!userIsFriend && !isUser && ( { - userInBlocked - ? setBlockedUsers((prev) => prev.filter((e) => e != user)) - : setBlockedUsers((prev) => [...prev, user]); + setBlocked(!contact.blocked); }} > - {userInBlocked ? "Unblock" : "Block"} + {contact.blocked ? "Unblock" : "Block"} )} {userIsFriend && ( - setFriends((prev) => prev.filter((e) => e != user)) + setFriends((prev) => prev.filter((e) => e != id)) } > Remove Contact )} setIsUntrustworthy(!isUntrustworthy)} + className={contact.isUntrustworthy ? "" : "red"} + onClick={() => setIsUntrustworthy(!contact.isUntrustworthy)} > - {isUntrustworthy + {contact.isUntrustworthy ? "Remove Untrustworthy Mark" : "Mark as Untrustworthy"} {!userIsFriend && ( - setFriends((prev) => [...prev, user])}> + setFriends((prev) => [...prev, id])}> Send Contact Request )} diff --git a/packages/react-chat/src/components/ReactChat.tsx b/packages/react-chat/src/components/ReactChat.tsx index 6af8359d..4a66e938 100644 --- a/packages/react-chat/src/components/ReactChat.tsx +++ b/packages/react-chat/src/components/ReactChat.tsx @@ -2,7 +2,6 @@ import React, { useRef } from "react"; import { ThemeProvider } from "styled-components"; import styled from "styled-components"; -import { BlockedUsersProvider } from "../contexts/blockedUsersProvider"; import { FetchMetadataProvider } from "../contexts/fetchMetadataProvider"; import { FriendsProvider } from "../contexts/friendsProvider"; import { ModalProvider } from "../contexts/modalProvider"; @@ -29,17 +28,15 @@ export function ReactChat({ - - - - - - -