Improve profile modal (#132)
This commit is contained in:
parent
7fb0bfbdc7
commit
81a14fcb9a
|
@ -76,7 +76,7 @@ function DragDiv() {
|
||||||
<ReactChat
|
<ReactChat
|
||||||
theme={theme ? lightTheme : darkTheme}
|
theme={theme ? lightTheme : darkTheme}
|
||||||
communityKey={
|
communityKey={
|
||||||
"0x02f04996fd22b82e7b02a2d3bf07fa986c5181cd23190a992187d9c887011d4a8e"
|
"0x0229d71fb62c14ec0370d2507ef7e13f1d53ddf1170db3babb5710fd113d0d94f9"
|
||||||
}
|
}
|
||||||
fetchMetadata={fetchMetadata}
|
fetchMetadata={fetchMetadata}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { Community } from "./Community";
|
||||||
import { Members } from "./Members/Members";
|
import { Members } from "./Members/Members";
|
||||||
import { CommunityModal } from "./Modals/CommunityModal";
|
import { CommunityModal } from "./Modals/CommunityModal";
|
||||||
import { EditModal } from "./Modals/EditModal";
|
import { EditModal } from "./Modals/EditModal";
|
||||||
|
import { ProfileModal } from "./Modals/ProfileModal";
|
||||||
|
|
||||||
export function Chat() {
|
export function Chat() {
|
||||||
const [showMembers, setShowMembers] = useState(true);
|
const [showMembers, setShowMembers] = useState(true);
|
||||||
|
@ -55,6 +56,7 @@ export function Chat() {
|
||||||
)}
|
)}
|
||||||
<CommunityModal subtitle="Public Community" />
|
<CommunityModal subtitle="Public Community" />
|
||||||
<EditModal />
|
<EditModal />
|
||||||
|
<ProfileModal />
|
||||||
</ChatWrapper>
|
</ChatWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,7 +123,7 @@ export function ChatCreation({
|
||||||
<Contacts>
|
<Contacts>
|
||||||
<ContactsHeading>Contacts</ContactsHeading>
|
<ContactsHeading>Contacts</ContactsHeading>
|
||||||
<ContactsList>
|
<ContactsList>
|
||||||
{contacts
|
{Object.values(contacts)
|
||||||
.filter(
|
.filter(
|
||||||
(e) =>
|
(e) =>
|
||||||
e.id != bufToHex(identity.publicKey) &&
|
e.id != bufToHex(identity.publicKey) &&
|
||||||
|
@ -132,7 +132,7 @@ export function ChatCreation({
|
||||||
.map((contact) => (
|
.map((contact) => (
|
||||||
<Member
|
<Member
|
||||||
key={contact.id}
|
key={contact.id}
|
||||||
member={contact.id}
|
contact={contact}
|
||||||
isOnline={contact.online}
|
isOnline={contact.online}
|
||||||
onClick={() => addMember(contact.id)}
|
onClick={() => addMember(contact.id)}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -3,10 +3,29 @@ import React, { useEffect, useMemo, useState } from "react";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
|
||||||
import { useFetchMetadata } from "../../contexts/fetchMetadataProvider";
|
import { useFetchMetadata } from "../../contexts/fetchMetadataProvider";
|
||||||
|
import { useMessengerContext } from "../../contexts/messengerProvider";
|
||||||
import { ChatMessage } from "../../models/ChatMessage";
|
import { ChatMessage } from "../../models/ChatMessage";
|
||||||
import { Metadata } from "../../models/Metadata";
|
import { Metadata } from "../../models/Metadata";
|
||||||
|
import { ContactMenu } from "../Form/ContactMenu";
|
||||||
import { ImageMenu } from "../Form/ImageMenu";
|
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 (
|
||||||
|
<MentionSpan onClick={() => setShowMenu(!showMenu)}>
|
||||||
|
{`@${contact.customName ?? contact.id}`}
|
||||||
|
{showMenu && <ContactMenu id={id.slice(1)} setShowMenu={setShowMenu} />}
|
||||||
|
</MentionSpan>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
type ChatMessageContentProps = {
|
type ChatMessageContentProps = {
|
||||||
message: ChatMessage;
|
message: ChatMessage;
|
||||||
setImage: (image: string) => void;
|
setImage: (image: string) => void;
|
||||||
|
@ -40,7 +59,7 @@ export function ChatMessageContent({
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
if (element.startsWith("@")) {
|
if (element.startsWith("@")) {
|
||||||
return [<Mention key={idx}>{element}</Mention>, " "];
|
return [<Mention key={idx} id={element} />, " "];
|
||||||
}
|
}
|
||||||
return [element, " "];
|
return [element, " "];
|
||||||
});
|
});
|
||||||
|
@ -158,9 +177,10 @@ const ContentWrapper = styled.div`
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Mention = styled.span`
|
const MentionSpan = styled.span`
|
||||||
color: blue;
|
color: blue;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
position: relative;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Link = styled.a`
|
const Link = styled.a`
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
|
||||||
import { useBlockedUsers } from "../../contexts/blockedUsersProvider";
|
|
||||||
import { useMessengerContext } from "../../contexts/messengerProvider";
|
import { useMessengerContext } from "../../contexts/messengerProvider";
|
||||||
import { useModal } from "../../contexts/modalProvider";
|
import { useModal } from "../../contexts/modalProvider";
|
||||||
import { useChatScrollHandle } from "../../hooks/useChatScrollHandle";
|
import { useChatScrollHandle } from "../../hooks/useChatScrollHandle";
|
||||||
|
@ -14,7 +13,6 @@ import { UntrustworthIcon } from "../Icons/UntrustworthIcon";
|
||||||
import { UserIcon } from "../Icons/UserIcon";
|
import { UserIcon } from "../Icons/UserIcon";
|
||||||
import { LinkModal, LinkModalName } from "../Modals/LinkModal";
|
import { LinkModal, LinkModalName } from "../Modals/LinkModal";
|
||||||
import { PictureModal, PictureModalName } from "../Modals/PictureModal";
|
import { PictureModal, PictureModalName } from "../Modals/PictureModal";
|
||||||
import { ProfileModal } from "../Modals/ProfileModal";
|
|
||||||
import { textMediumStyles, textSmallStyles } from "../Text";
|
import { textMediumStyles, textSmallStyles } from "../Text";
|
||||||
|
|
||||||
import { ChatMessageContent } from "./ChatMessageContent";
|
import { ChatMessageContent } from "./ChatMessageContent";
|
||||||
|
@ -27,10 +25,6 @@ type ChatUiMessageProps = {
|
||||||
prevMessage: ChatMessage;
|
prevMessage: ChatMessage;
|
||||||
setImage: (img: string) => void;
|
setImage: (img: string) => void;
|
||||||
setLink: (link: string) => void;
|
setLink: (link: string) => void;
|
||||||
setUser: (user: string) => void;
|
|
||||||
customName?: string;
|
|
||||||
trueName?: string;
|
|
||||||
setRenaming: (val: boolean) => void;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function ChatUiMessage({
|
function ChatUiMessage({
|
||||||
|
@ -39,13 +33,13 @@ function ChatUiMessage({
|
||||||
prevMessage,
|
prevMessage,
|
||||||
setImage,
|
setImage,
|
||||||
setLink,
|
setLink,
|
||||||
setUser,
|
|
||||||
customName,
|
|
||||||
trueName,
|
|
||||||
setRenaming,
|
|
||||||
}: ChatUiMessageProps) {
|
}: ChatUiMessageProps) {
|
||||||
|
const { contacts } = useMessengerContext();
|
||||||
|
const contact = useMemo(
|
||||||
|
() => contacts[message.sender],
|
||||||
|
[message.sender, contacts]
|
||||||
|
);
|
||||||
const [showMenu, setShowMenu] = useState(false);
|
const [showMenu, setShowMenu] = useState(false);
|
||||||
const [isUntrustworthy, setIsUntrustworthy] = useState(false);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MessageOuterWrapper>
|
<MessageOuterWrapper>
|
||||||
|
@ -60,19 +54,10 @@ function ChatUiMessage({
|
||||||
<Icon
|
<Icon
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setShowMenu((e) => !e);
|
setShowMenu((e) => !e);
|
||||||
setUser(message.sender);
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{showMenu && (
|
{showMenu && (
|
||||||
<ContactMenu
|
<ContactMenu id={message.sender} setShowMenu={setShowMenu} />
|
||||||
message={message}
|
|
||||||
setShowMenu={setShowMenu}
|
|
||||||
isUntrustworthy={isUntrustworthy}
|
|
||||||
setIsUntrustworthy={setIsUntrustworthy}
|
|
||||||
customName={customName}
|
|
||||||
trueName={trueName}
|
|
||||||
setRenaming={setRenaming}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
<UserIcon />
|
<UserIcon />
|
||||||
</Icon>
|
</Icon>
|
||||||
|
@ -82,14 +67,14 @@ function ChatUiMessage({
|
||||||
<UserNameWrapper>
|
<UserNameWrapper>
|
||||||
<UserName>
|
<UserName>
|
||||||
{" "}
|
{" "}
|
||||||
{customName ? customName : message.sender.slice(0, 10)}
|
{contact.customName ?? message.sender.slice(0, 10)}
|
||||||
</UserName>
|
</UserName>
|
||||||
{customName && (
|
{contact.customName && (
|
||||||
<UserAddress>
|
<UserAddress>
|
||||||
{message.sender.slice(0, 5)}...{message.sender.slice(-3)}
|
{message.sender.slice(0, 5)}...{message.sender.slice(-3)}
|
||||||
</UserAddress>
|
</UserAddress>
|
||||||
)}
|
)}
|
||||||
{isUntrustworthy && <UntrustworthIcon />}
|
{contact.isUntrustworthy && <UntrustworthIcon />}
|
||||||
</UserNameWrapper>
|
</UserNameWrapper>
|
||||||
<TimeWrapper>{message.date.toLocaleString()}</TimeWrapper>
|
<TimeWrapper>{message.date.toLocaleString()}</TimeWrapper>
|
||||||
</MessageHeaderWrapper>
|
</MessageHeaderWrapper>
|
||||||
|
@ -107,20 +92,20 @@ function ChatUiMessage({
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ChatMessages() {
|
export function ChatMessages() {
|
||||||
const { messages, activeChannel } = useMessengerContext();
|
const { messages, activeChannel, contacts } = useMessengerContext();
|
||||||
const ref = useRef<HTMLHeadingElement>(null);
|
const ref = useRef<HTMLHeadingElement>(null);
|
||||||
const loadingMessages = useChatScrollHandle(messages, ref, activeChannel.id);
|
const loadingMessages = useChatScrollHandle(messages, ref, activeChannel.id);
|
||||||
|
|
||||||
const { blockedUsers } = useBlockedUsers();
|
|
||||||
|
|
||||||
const shownMessages = useMemo(
|
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 [image, setImage] = useState("");
|
||||||
const [link, setLink] = useState("");
|
const [link, setLink] = useState("");
|
||||||
const [user, setUser] = useState("");
|
|
||||||
|
|
||||||
const { setModal: setPictureModal, isVisible: showPictureModal } =
|
const { setModal: setPictureModal, isVisible: showPictureModal } =
|
||||||
useModal(PictureModalName);
|
useModal(PictureModalName);
|
||||||
|
@ -136,23 +121,10 @@ export function ChatMessages() {
|
||||||
);
|
);
|
||||||
useEffect(() => (!showLinkModal ? setLink("") : undefined), [showLinkModal]);
|
useEffect(() => (!showLinkModal ? setLink("") : undefined), [showLinkModal]);
|
||||||
|
|
||||||
const [renaming, setRenaming] = useState(false);
|
|
||||||
const [customName, setCustomName] = useState("");
|
|
||||||
const [trueName, setTrueName] = useState("");
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MessagesWrapper ref={ref}>
|
<MessagesWrapper ref={ref}>
|
||||||
<PictureModal image={image} />
|
<PictureModal image={image} />
|
||||||
<LinkModal link={link} />
|
<LinkModal link={link} />
|
||||||
<ProfileModal
|
|
||||||
user={user}
|
|
||||||
renaming={renaming}
|
|
||||||
setRenaming={setRenaming}
|
|
||||||
customName={customName}
|
|
||||||
setCustomName={setCustomName}
|
|
||||||
trueName={trueName}
|
|
||||||
setTrueName={setTrueName}
|
|
||||||
/>
|
|
||||||
<EmptyChannel channel={activeChannel} />
|
<EmptyChannel channel={activeChannel} />
|
||||||
{loadingMessages && (
|
{loadingMessages && (
|
||||||
<LoadingWrapper>
|
<LoadingWrapper>
|
||||||
|
@ -167,10 +139,6 @@ export function ChatMessages() {
|
||||||
prevMessage={shownMessages[idx - 1]}
|
prevMessage={shownMessages[idx - 1]}
|
||||||
setLink={setLink}
|
setLink={setLink}
|
||||||
setImage={setImage}
|
setImage={setImage}
|
||||||
setUser={setUser}
|
|
||||||
customName={customName}
|
|
||||||
trueName={trueName}
|
|
||||||
setRenaming={setRenaming}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</MessagesWrapper>
|
</MessagesWrapper>
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import React, { useMemo } from "react";
|
import React, { useMemo } from "react";
|
||||||
|
import { bufToHex } from "status-communities/dist/cjs/utils";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
|
||||||
import { useBlockedUsers } from "../../contexts/blockedUsersProvider";
|
|
||||||
import { useFriends } from "../../contexts/friendsProvider";
|
import { useFriends } from "../../contexts/friendsProvider";
|
||||||
|
import { useIdentity } from "../../contexts/identityProvider";
|
||||||
import { useModal } from "../../contexts/modalProvider";
|
import { useModal } from "../../contexts/modalProvider";
|
||||||
import { ChatMessage } from "../../models/ChatMessage";
|
import { useManageContact } from "../../hooks/useManageContact";
|
||||||
import { Icon, UserAddress } from "../Chat/ChatMessages";
|
import { UserAddress } from "../Chat/ChatMessages";
|
||||||
import { AddContactSvg } from "../Icons/AddContactIcon";
|
import { AddContactSvg } from "../Icons/AddContactIcon";
|
||||||
import { BlockSvg } from "../Icons/BlockIcon";
|
import { BlockSvg } from "../Icons/BlockIcon";
|
||||||
import { ChatSvg } from "../Icons/ChatIcon";
|
import { ChatSvg } from "../Icons/ChatIcon";
|
||||||
|
@ -20,63 +21,45 @@ import { textMediumStyles } from "../Text";
|
||||||
import { DropdownMenu, MenuItem, MenuText } from "./DropdownMenu";
|
import { DropdownMenu, MenuItem, MenuText } from "./DropdownMenu";
|
||||||
|
|
||||||
type ContactMenuProps = {
|
type ContactMenuProps = {
|
||||||
message: ChatMessage;
|
id: string;
|
||||||
setShowMenu: (val: boolean) => void;
|
setShowMenu: (val: boolean) => void;
|
||||||
isUntrustworthy: boolean;
|
|
||||||
setIsUntrustworthy: (val: boolean) => void;
|
|
||||||
customName?: string;
|
|
||||||
trueName?: string;
|
|
||||||
setRenaming: (val: boolean) => void;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function ContactMenu({
|
export function ContactMenu({ id, setShowMenu }: ContactMenuProps) {
|
||||||
message,
|
const identity = useIdentity();
|
||||||
setShowMenu,
|
const isUser = useMemo(
|
||||||
isUntrustworthy,
|
() => id === bufToHex(identity.publicKey),
|
||||||
setIsUntrustworthy,
|
[id, identity]
|
||||||
customName,
|
|
||||||
trueName,
|
|
||||||
setRenaming,
|
|
||||||
}: ContactMenuProps) {
|
|
||||||
const id = message.sender;
|
|
||||||
const { blockedUsers, setBlockedUsers } = useBlockedUsers();
|
|
||||||
|
|
||||||
const userInBlocked = useMemo(
|
|
||||||
() => blockedUsers.includes(id),
|
|
||||||
[blockedUsers, id]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { setModal } = useModal(ProfileModalName);
|
||||||
const { friends, setFriends } = useFriends();
|
const { friends, setFriends } = useFriends();
|
||||||
|
|
||||||
const userIsFriend = useMemo(() => friends.includes(id), [friends, id]);
|
const userIsFriend = useMemo(() => friends.includes(id), [friends, id]);
|
||||||
|
const { contact, setBlocked, setIsUntrustworthy } = useManageContact(id);
|
||||||
|
|
||||||
const { setModal } = useModal(ProfileModalName);
|
if (!contact) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ContactDropdown>
|
<ContactDropdown>
|
||||||
<ContactInfo>
|
<ContactInfo>
|
||||||
{message.image ? (
|
<UserIcon />
|
||||||
<Icon
|
|
||||||
style={{
|
|
||||||
backgroundImage: `url(${message.image}`,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<UserIcon />
|
|
||||||
)}
|
|
||||||
<UserNameWrapper>
|
<UserNameWrapper>
|
||||||
<UserName>
|
<UserName>{contact.customName ?? id.slice(0, 10)}</UserName>
|
||||||
{customName ? customName : message.sender.slice(0, 10)}
|
{contact.isUntrustworthy && <UntrustworthIcon />}
|
||||||
</UserName>
|
|
||||||
{isUntrustworthy && <UntrustworthIcon />}
|
|
||||||
</UserNameWrapper>
|
</UserNameWrapper>
|
||||||
{trueName && <UserTrueName>({trueName})</UserTrueName>}
|
{contact.customName && (
|
||||||
|
<UserTrueName>({contact.trueName})</UserTrueName>
|
||||||
|
)}
|
||||||
<UserAddress>
|
<UserAddress>
|
||||||
{message.sender.slice(0, 10)}...{message.sender.slice(-3)}
|
{id.slice(0, 10)}...{id.slice(-3)}
|
||||||
</UserAddress>
|
</UserAddress>
|
||||||
</ContactInfo>
|
</ContactInfo>
|
||||||
<MenuSection>
|
<MenuSection>
|
||||||
<MenuItem onClick={() => setModal(true)}>
|
<MenuItem
|
||||||
|
onClick={() => {
|
||||||
|
setModal({ id, renamingState: false });
|
||||||
|
}}
|
||||||
|
>
|
||||||
<ProfileSvg width={16} height={16} />
|
<ProfileSvg width={16} height={16} />
|
||||||
<MenuText>View Profile</MenuText>
|
<MenuText>View Profile</MenuText>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
@ -94,8 +77,7 @@ export function ContactMenu({
|
||||||
)}
|
)}
|
||||||
<MenuItem
|
<MenuItem
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setModal(true);
|
setModal({ id, renamingState: true });
|
||||||
setRenaming(true);
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<EditSvg width={16} height={16} />
|
<EditSvg width={16} height={16} />
|
||||||
|
@ -103,30 +85,30 @@ export function ContactMenu({
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</MenuSection>
|
</MenuSection>
|
||||||
<MenuSection>
|
<MenuSection>
|
||||||
<MenuItem onClick={() => setIsUntrustworthy(!isUntrustworthy)}>
|
<MenuItem onClick={() => setIsUntrustworthy(!contact.isUntrustworthy)}>
|
||||||
<WarningSvg
|
<WarningSvg
|
||||||
width={16}
|
width={16}
|
||||||
height={16}
|
height={16}
|
||||||
className={isUntrustworthy ? "" : "red"}
|
className={contact.isUntrustworthy ? "" : "red"}
|
||||||
/>
|
/>
|
||||||
<MenuText className={isUntrustworthy ? "" : "red"}>
|
<MenuText className={contact.isUntrustworthy ? "" : "red"}>
|
||||||
{isUntrustworthy
|
{contact.isUntrustworthy
|
||||||
? "Remove Untrustworthy Mark"
|
? "Remove Untrustworthy Mark"
|
||||||
: "Mark as Untrustworthy"}
|
: "Mark as Untrustworthy"}
|
||||||
</MenuText>
|
</MenuText>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|
||||||
{!userIsFriend && (
|
{!userIsFriend && !isUser && (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
userInBlocked
|
setBlocked(!contact.blocked);
|
||||||
? setBlockedUsers((prev) => prev.filter((e) => e != id))
|
|
||||||
: setBlockedUsers((prev) => [...prev, id]);
|
|
||||||
setShowMenu(false);
|
setShowMenu(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<BlockSvg width={16} height={16} className="red" />
|
<BlockSvg width={16} height={16} className="red" />
|
||||||
<MenuText className="red">Block User</MenuText>
|
<MenuText className="red">
|
||||||
|
{contact.blocked ? "Unblock User" : "Block User"}
|
||||||
|
</MenuText>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
</MenuSection>
|
</MenuSection>
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
|
||||||
|
import { Contact } from "../../models/Contact";
|
||||||
import { Icon } from "../Chat/ChatMessages";
|
import { Icon } from "../Chat/ChatMessages";
|
||||||
// import { ContactMenu } from '../Form/ContactMenu';
|
import { ContactMenu } from "../Form/ContactMenu";
|
||||||
import { UserIcon } from "../Icons/UserIcon";
|
import { UserIcon } from "../Icons/UserIcon";
|
||||||
|
|
||||||
interface MemberProps {
|
interface MemberProps {
|
||||||
member: string;
|
contact: Contact;
|
||||||
isOnline?: boolean;
|
isOnline?: boolean;
|
||||||
switchShowMembers?: () => void;
|
switchShowMembers?: () => void;
|
||||||
setMembersList?: any;
|
setMembersList?: any;
|
||||||
|
@ -14,7 +15,7 @@ interface MemberProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Member({
|
export function Member({
|
||||||
member,
|
contact,
|
||||||
isOnline,
|
isOnline,
|
||||||
switchShowMembers,
|
switchShowMembers,
|
||||||
setMembersList,
|
setMembersList,
|
||||||
|
@ -30,11 +31,11 @@ export function Member({
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// const [showMenu, setShowMenu] = useState(false);
|
const [showMenu, setShowMenu] = useState(false);
|
||||||
|
|
||||||
const onMemberClick = () => {
|
const onMemberClick = () => {
|
||||||
switchShowMembers?.();
|
switchShowMembers?.();
|
||||||
startDialog(member);
|
startDialog(contact.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -44,12 +45,12 @@ export function Member({
|
||||||
backgroundImage: "unset",
|
backgroundImage: "unset",
|
||||||
}}
|
}}
|
||||||
className={isOnline ? "online" : "offline"}
|
className={isOnline ? "online" : "offline"}
|
||||||
// onClick={() => setShowMenu(e => !e)}
|
onClick={() => setShowMenu((e) => !e)}
|
||||||
>
|
>
|
||||||
{/* {showMenu && <ContactMenu id={member} setShowMenu={setShowMenu} />} */}
|
{showMenu && <ContactMenu id={contact.id} setShowMenu={setShowMenu} />}
|
||||||
<UserIcon memberView={true} />
|
<UserIcon memberView={true} />
|
||||||
</MemberIcon>
|
</MemberIcon>
|
||||||
<MemberName>{member}</MemberName>
|
<MemberName>{contact.customName ?? contact.id}</MemberName>
|
||||||
</MemberData>
|
</MemberData>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,13 +34,13 @@ export function MembersList({
|
||||||
</MemberCategory>
|
</MemberCategory>
|
||||||
<MemberCategory>
|
<MemberCategory>
|
||||||
<MemberCategoryName>Online</MemberCategoryName>
|
<MemberCategoryName>Online</MemberCategoryName>
|
||||||
{contacts
|
{Object.values(contacts)
|
||||||
.filter((e) => e.id != bufToHex(identity.publicKey))
|
.filter((e) => e.id != bufToHex(identity.publicKey))
|
||||||
.filter((e) => e.online)
|
.filter((e) => e.online)
|
||||||
.map((contact) => (
|
.map((contact) => (
|
||||||
<Member
|
<Member
|
||||||
key={contact.id}
|
key={contact.id}
|
||||||
member={contact.id}
|
contact={contact}
|
||||||
isOnline={contact.online}
|
isOnline={contact.online}
|
||||||
switchShowMembers={switchShowMembers}
|
switchShowMembers={switchShowMembers}
|
||||||
setMembersList={setMembersList}
|
setMembersList={setMembersList}
|
||||||
|
@ -49,13 +49,13 @@ export function MembersList({
|
||||||
</MemberCategory>
|
</MemberCategory>
|
||||||
<MemberCategory>
|
<MemberCategory>
|
||||||
<MemberCategoryName>Offline</MemberCategoryName>
|
<MemberCategoryName>Offline</MemberCategoryName>
|
||||||
{contacts
|
{Object.values(contacts)
|
||||||
.filter((e) => e.id != bufToHex(identity.publicKey))
|
.filter((e) => e.id != bufToHex(identity.publicKey))
|
||||||
.filter((e) => !e.online)
|
.filter((e) => !e.online)
|
||||||
.map((contact) => (
|
.map((contact) => (
|
||||||
<Member
|
<Member
|
||||||
key={contact.id}
|
key={contact.id}
|
||||||
member={contact.id}
|
contact={contact}
|
||||||
isOnline={contact.online}
|
isOnline={contact.online}
|
||||||
switchShowMembers={switchShowMembers}
|
switchShowMembers={switchShowMembers}
|
||||||
setMembersList={setMembersList}
|
setMembersList={setMembersList}
|
||||||
|
|
|
@ -3,9 +3,9 @@ import styled from "styled-components";
|
||||||
|
|
||||||
import { Modal } from "./Modal";
|
import { Modal } from "./Modal";
|
||||||
|
|
||||||
export const PictureModalName = "PictureModal";
|
export const PictureModalName = "PictureModal" as const;
|
||||||
|
|
||||||
interface PictureModalProps {
|
export interface PictureModalProps {
|
||||||
image: string;
|
image: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
import React, { useMemo, useState } from "react";
|
import React, { useEffect, useMemo, useState } from "react";
|
||||||
|
import { bufToHex } from "status-communities/dist/cjs/utils";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
|
||||||
import { useBlockedUsers } from "../../contexts/blockedUsersProvider";
|
|
||||||
import { useFriends } from "../../contexts/friendsProvider";
|
import { useFriends } from "../../contexts/friendsProvider";
|
||||||
|
import { useIdentity } from "../../contexts/identityProvider";
|
||||||
|
import { useModal } from "../../contexts/modalProvider";
|
||||||
|
import { useManageContact } from "../../hooks/useManageContact";
|
||||||
import { copy } from "../../utils";
|
import { copy } from "../../utils";
|
||||||
import { buttonStyles } from "../Buttons/buttonStyle";
|
import { buttonStyles } from "../Buttons/buttonStyle";
|
||||||
import { ClearSvg } from "../Icons/ClearIcon";
|
import { ClearSvg } from "../Icons/ClearIcon";
|
||||||
|
@ -16,46 +19,45 @@ import { textMediumStyles } from "../Text";
|
||||||
import { Modal } from "./Modal";
|
import { Modal } from "./Modal";
|
||||||
import { ButtonSection, Heading, Section } from "./ModalStyle";
|
import { ButtonSection, Heading, Section } from "./ModalStyle";
|
||||||
|
|
||||||
export const ProfileModalName = "profileModal";
|
export const ProfileModalName = "profileModal" as const;
|
||||||
|
|
||||||
interface ProfileModalProps {
|
export type ProfileModalProps = {
|
||||||
user: string;
|
id: string;
|
||||||
image?: string;
|
image?: string;
|
||||||
renaming: boolean;
|
renamingState?: boolean;
|
||||||
customName?: string;
|
};
|
||||||
trueName?: string;
|
|
||||||
setRenaming: (val: boolean) => void;
|
|
||||||
setTrueName: (val: string) => void;
|
|
||||||
setCustomName: (val: string) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ProfileModal = ({
|
export const ProfileModal = () => {
|
||||||
user,
|
const { props } = useModal(ProfileModalName);
|
||||||
image,
|
const { id, image, renamingState } = useMemo(
|
||||||
renaming,
|
() => (props ? props : { id: "" }),
|
||||||
customName,
|
[props]
|
||||||
trueName,
|
);
|
||||||
setRenaming,
|
|
||||||
setTrueName,
|
|
||||||
setCustomName,
|
|
||||||
}: ProfileModalProps) => {
|
|
||||||
const [isUntrustworthy, setIsUntrustworthy] = useState(false);
|
|
||||||
|
|
||||||
const { blockedUsers, setBlockedUsers } = useBlockedUsers();
|
const identity = useIdentity();
|
||||||
|
const isUser = useMemo(
|
||||||
const userInBlocked = useMemo(
|
() => id === bufToHex(identity.publicKey),
|
||||||
() => blockedUsers.includes(user),
|
[id, identity]
|
||||||
[blockedUsers, user]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const { friends, setFriends } = useFriends();
|
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 (
|
return (
|
||||||
<Modal name={ProfileModalName} className="profile">
|
<Modal name={ProfileModalName} className="profile">
|
||||||
<Section>
|
<Section>
|
||||||
<Heading>{user.slice(0, 10)}’s Profile</Heading>
|
<Heading>{id.slice(0, 10)}’s Profile</Heading>
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
<ProfileSection>
|
<ProfileSection>
|
||||||
|
@ -70,8 +72,8 @@ export const ProfileModal = ({
|
||||||
<UserIcon />
|
<UserIcon />
|
||||||
)}
|
)}
|
||||||
<UserNameWrapper>
|
<UserNameWrapper>
|
||||||
<UserName>{customName ? customName : user.slice(0, 10)}</UserName>
|
<UserName>{contact.customName ?? id.slice(0, 10)}</UserName>
|
||||||
{isUntrustworthy && <UntrustworthIcon />}
|
{contact.isUntrustworthy && <UntrustworthIcon />}
|
||||||
{!renaming && (
|
{!renaming && (
|
||||||
<button onClick={() => setRenaming(true)}>
|
<button onClick={() => setRenaming(true)}>
|
||||||
{" "}
|
{" "}
|
||||||
|
@ -79,21 +81,22 @@ export const ProfileModal = ({
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</UserNameWrapper>
|
</UserNameWrapper>
|
||||||
{trueName && <UserTrueName>{trueName}</UserTrueName>}
|
{contact.customName && (
|
||||||
{trueName && <button onClick={() => setTrueName("")}></button>}
|
<UserTrueName>{contact.trueName}</UserTrueName>
|
||||||
|
)}
|
||||||
</NameSection>
|
</NameSection>
|
||||||
{renaming ? (
|
{renaming ? (
|
||||||
<NameInputWrapper>
|
<NameInputWrapper>
|
||||||
<NameInput
|
<NameInput
|
||||||
placeholder="Only you will see this nickname"
|
placeholder="Only you will see this nickname"
|
||||||
value={customName}
|
value={contact.customName}
|
||||||
onChange={(e) => setCustomName(e.currentTarget.value)}
|
onChange={(e) => setCustomNameInput(e.currentTarget.value)}
|
||||||
/>
|
/>
|
||||||
{customName && (
|
{contact.customName && (
|
||||||
<ClearBtn
|
<ClearBtn
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setCustomName("");
|
setCustomName(undefined);
|
||||||
setTrueName("");
|
setCustomNameInput("");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ClearSvg width={16} height={16} className="profile" />
|
<ClearSvg width={16} height={16} className="profile" />
|
||||||
|
@ -103,9 +106,9 @@ export const ProfileModal = ({
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<UserAddressWrapper>
|
<UserAddressWrapper>
|
||||||
<UserAddress>Chatkey: {user.slice(0, 30)}</UserAddress>
|
<UserAddress>Chatkey: {id.slice(0, 30)}</UserAddress>
|
||||||
|
|
||||||
<CopyButton onClick={() => copy(user)}>
|
<CopyButton onClick={() => copy(id)}>
|
||||||
<CopySvg width={24} height={24} />
|
<CopySvg width={24} height={24} />
|
||||||
</CopyButton>
|
</CopyButton>
|
||||||
</UserAddressWrapper>
|
</UserAddressWrapper>
|
||||||
|
@ -120,9 +123,9 @@ export const ProfileModal = ({
|
||||||
<LeftIconSvg width={28} height={28} />
|
<LeftIconSvg width={28} height={28} />
|
||||||
</BackBtn>
|
</BackBtn>
|
||||||
<Btn
|
<Btn
|
||||||
disabled={!customName}
|
disabled={!customNameInput}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setTrueName(user.slice(0, 10));
|
setCustomName(customNameInput);
|
||||||
setRenaming(false);
|
setRenaming(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -131,38 +134,36 @@ export const ProfileModal = ({
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{!userIsFriend && (
|
{!userIsFriend && !isUser && (
|
||||||
<ProfileBtn
|
<ProfileBtn
|
||||||
className={userInBlocked ? "" : "red"}
|
className={contact.blocked ? "" : "red"}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
userInBlocked
|
setBlocked(!contact.blocked);
|
||||||
? setBlockedUsers((prev) => prev.filter((e) => e != user))
|
|
||||||
: setBlockedUsers((prev) => [...prev, user]);
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{userInBlocked ? "Unblock" : "Block"}
|
{contact.blocked ? "Unblock" : "Block"}
|
||||||
</ProfileBtn>
|
</ProfileBtn>
|
||||||
)}
|
)}
|
||||||
{userIsFriend && (
|
{userIsFriend && (
|
||||||
<ProfileBtn
|
<ProfileBtn
|
||||||
className="red"
|
className="red"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
setFriends((prev) => prev.filter((e) => e != user))
|
setFriends((prev) => prev.filter((e) => e != id))
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Remove Contact
|
Remove Contact
|
||||||
</ProfileBtn>
|
</ProfileBtn>
|
||||||
)}
|
)}
|
||||||
<ProfileBtn
|
<ProfileBtn
|
||||||
className={isUntrustworthy ? "" : "red"}
|
className={contact.isUntrustworthy ? "" : "red"}
|
||||||
onClick={() => setIsUntrustworthy(!isUntrustworthy)}
|
onClick={() => setIsUntrustworthy(!contact.isUntrustworthy)}
|
||||||
>
|
>
|
||||||
{isUntrustworthy
|
{contact.isUntrustworthy
|
||||||
? "Remove Untrustworthy Mark"
|
? "Remove Untrustworthy Mark"
|
||||||
: "Mark as Untrustworthy"}
|
: "Mark as Untrustworthy"}
|
||||||
</ProfileBtn>
|
</ProfileBtn>
|
||||||
{!userIsFriend && (
|
{!userIsFriend && (
|
||||||
<Btn onClick={() => setFriends((prev) => [...prev, user])}>
|
<Btn onClick={() => setFriends((prev) => [...prev, id])}>
|
||||||
Send Contact Request
|
Send Contact Request
|
||||||
</Btn>
|
</Btn>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -2,7 +2,6 @@ import React, { useRef } from "react";
|
||||||
import { ThemeProvider } from "styled-components";
|
import { ThemeProvider } from "styled-components";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
|
||||||
import { BlockedUsersProvider } from "../contexts/blockedUsersProvider";
|
|
||||||
import { FetchMetadataProvider } from "../contexts/fetchMetadataProvider";
|
import { FetchMetadataProvider } from "../contexts/fetchMetadataProvider";
|
||||||
import { FriendsProvider } from "../contexts/friendsProvider";
|
import { FriendsProvider } from "../contexts/friendsProvider";
|
||||||
import { ModalProvider } from "../contexts/modalProvider";
|
import { ModalProvider } from "../contexts/modalProvider";
|
||||||
|
@ -29,17 +28,15 @@ export function ReactChat({
|
||||||
<ThemeProvider theme={theme}>
|
<ThemeProvider theme={theme}>
|
||||||
<NarrowProvider myRef={ref}>
|
<NarrowProvider myRef={ref}>
|
||||||
<FetchMetadataProvider fetchMetadata={fetchMetadata}>
|
<FetchMetadataProvider fetchMetadata={fetchMetadata}>
|
||||||
<BlockedUsersProvider>
|
<FriendsProvider>
|
||||||
<FriendsProvider>
|
<ModalProvider>
|
||||||
<ModalProvider>
|
<Wrapper ref={ref}>
|
||||||
<Wrapper ref={ref}>
|
<GlobalStyle />
|
||||||
<GlobalStyle />
|
<ChatLoader communityKey={communityKey} />
|
||||||
<ChatLoader communityKey={communityKey} />
|
<div id="modal-root" />
|
||||||
<div id="modal-root" />
|
</Wrapper>
|
||||||
</Wrapper>
|
</ModalProvider>
|
||||||
</ModalProvider>
|
</FriendsProvider>
|
||||||
</FriendsProvider>
|
|
||||||
</BlockedUsersProvider>
|
|
||||||
</FetchMetadataProvider>
|
</FetchMetadataProvider>
|
||||||
</NarrowProvider>
|
</NarrowProvider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
|
|
|
@ -22,7 +22,7 @@ export const SearchBlock = ({
|
||||||
const { contacts } = useMessengerContext();
|
const { contacts } = useMessengerContext();
|
||||||
|
|
||||||
const searchList = useMemo(() => {
|
const searchList = useMemo(() => {
|
||||||
return contacts
|
return Object.values(contacts)
|
||||||
.filter((member) => member.id.includes(query))
|
.filter((member) => member.id.includes(query))
|
||||||
.filter((member) => !dsicludeList.includes(member.id));
|
.filter((member) => !dsicludeList.includes(member.id));
|
||||||
}, [query, dsicludeList, contacts]);
|
}, [query, dsicludeList, contacts]);
|
||||||
|
@ -35,7 +35,7 @@ export const SearchBlock = ({
|
||||||
style={{ [onBotttom ? "bottom" : "top"]: "calc(100% + 24px)" }}
|
style={{ [onBotttom ? "bottom" : "top"]: "calc(100% + 24px)" }}
|
||||||
>
|
>
|
||||||
<ContactsList>
|
<ContactsList>
|
||||||
{contacts
|
{Object.values(contacts)
|
||||||
.filter((member) => member.id.includes(query))
|
.filter((member) => member.id.includes(query))
|
||||||
.filter((member) => !dsicludeList.includes(member.id))
|
.filter((member) => !dsicludeList.includes(member.id))
|
||||||
.map((member) => (
|
.map((member) => (
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
import React, { createContext, useContext, useState } from "react";
|
|
||||||
|
|
||||||
const BlockedUsersContext = createContext<{
|
|
||||||
blockedUsers: string[];
|
|
||||||
setBlockedUsers: React.Dispatch<React.SetStateAction<string[]>>;
|
|
||||||
}>({
|
|
||||||
blockedUsers: [],
|
|
||||||
setBlockedUsers: () => undefined,
|
|
||||||
});
|
|
||||||
|
|
||||||
export function useBlockedUsers() {
|
|
||||||
return useContext(BlockedUsersContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface BlockedUsersProviderProps {
|
|
||||||
children: React.ReactNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function BlockedUsersProvider({ children }: BlockedUsersProviderProps) {
|
|
||||||
const [blockedUsers, setBlockedUsers] = useState<string[]>([]);
|
|
||||||
return (
|
|
||||||
<BlockedUsersContext.Provider
|
|
||||||
value={{ blockedUsers, setBlockedUsers }}
|
|
||||||
children={children}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -14,7 +14,8 @@ const MessengerContext = createContext<MessengerType>({
|
||||||
loadPrevDay: async () => undefined,
|
loadPrevDay: async () => undefined,
|
||||||
loadingMessages: false,
|
loadingMessages: false,
|
||||||
communityData: undefined,
|
communityData: undefined,
|
||||||
contacts: [],
|
contacts: {},
|
||||||
|
setContacts: () => undefined,
|
||||||
activeChannel: {
|
activeChannel: {
|
||||||
id: "",
|
id: "",
|
||||||
name: "",
|
name: "",
|
||||||
|
|
|
@ -6,8 +6,17 @@ import React, {
|
||||||
useState,
|
useState,
|
||||||
} from "react";
|
} from "react";
|
||||||
|
|
||||||
type ModalsState = {
|
import {
|
||||||
[name: string]: boolean;
|
ProfileModalName,
|
||||||
|
ProfileModalProps,
|
||||||
|
} from "../components/Modals/ProfileModal";
|
||||||
|
|
||||||
|
type TypeMap = {
|
||||||
|
[ProfileModalName]?: ProfileModalProps;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ModalsState = TypeMap & {
|
||||||
|
[name: string]: boolean | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ModalContextType = [
|
type ModalContextType = [
|
||||||
|
@ -17,11 +26,18 @@ type ModalContextType = [
|
||||||
|
|
||||||
const ModalContext = createContext<ModalContextType>([{}, () => undefined]);
|
const ModalContext = createContext<ModalContextType>([{}, () => undefined]);
|
||||||
|
|
||||||
export function useModal(name: string) {
|
export function useModal<T extends string>(name: T) {
|
||||||
const [modals, setModals] = useContext(ModalContext);
|
const [modals, setModals] = useContext(ModalContext);
|
||||||
|
|
||||||
const setModal = useCallback(
|
const setModal = useCallback(
|
||||||
(state: boolean) => {
|
(state: T extends keyof TypeMap ? TypeMap[T] | false : boolean) => {
|
||||||
setModals((prev) => {
|
setModals((prev) => {
|
||||||
|
if (!state) {
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
[name]: undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
...prev,
|
...prev,
|
||||||
[name]: state,
|
[name]: state,
|
||||||
|
@ -30,8 +46,11 @@ export function useModal(name: string) {
|
||||||
},
|
},
|
||||||
[name, modals]
|
[name, modals]
|
||||||
);
|
);
|
||||||
const isVisible = useMemo(() => modals?.[name] ?? false, [modals, name]);
|
const isVisible = useMemo(() => !!modals?.[name], [modals, name]);
|
||||||
return { isVisible, setModal };
|
|
||||||
|
const props = useMemo(() => modals?.[name], [modals, name]);
|
||||||
|
|
||||||
|
return { isVisible, setModal, props };
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IdentityProviderProps {
|
interface IdentityProviderProps {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import {
|
import {
|
||||||
Community,
|
Community,
|
||||||
Contacts,
|
Contacts as ContactsClass,
|
||||||
Identity,
|
Identity,
|
||||||
Messenger,
|
Messenger,
|
||||||
} from "status-communities/dist/cjs";
|
} from "status-communities/dist/cjs";
|
||||||
|
@ -10,7 +10,7 @@ import {
|
||||||
import { ChannelData } from "../../models/ChannelData";
|
import { ChannelData } from "../../models/ChannelData";
|
||||||
import { ChatMessage } from "../../models/ChatMessage";
|
import { ChatMessage } from "../../models/ChatMessage";
|
||||||
import { CommunityData } from "../../models/CommunityData";
|
import { CommunityData } from "../../models/CommunityData";
|
||||||
import { Contact } from "../../models/Contact";
|
import { Contacts } from "../../models/Contact";
|
||||||
import { createCommunity } from "../../utils/createCommunity";
|
import { createCommunity } from "../../utils/createCommunity";
|
||||||
import { createMessenger } from "../../utils/createMessenger";
|
import { createMessenger } from "../../utils/createMessenger";
|
||||||
import { uintToImgUrl } from "../../utils/uintToImgUrl";
|
import { uintToImgUrl } from "../../utils/uintToImgUrl";
|
||||||
|
@ -32,7 +32,8 @@ export type MessengerType = {
|
||||||
loadPrevDay: (id: string) => Promise<void>;
|
loadPrevDay: (id: string) => Promise<void>;
|
||||||
loadingMessages: boolean;
|
loadingMessages: boolean;
|
||||||
communityData: CommunityData | undefined;
|
communityData: CommunityData | undefined;
|
||||||
contacts: Contact[];
|
contacts: Contacts;
|
||||||
|
setContacts: React.Dispatch<React.SetStateAction<Contacts>>;
|
||||||
channels: ChannelData[];
|
channels: ChannelData[];
|
||||||
activeChannel: ChannelData;
|
activeChannel: ChannelData;
|
||||||
setActiveChannel: (channel: ChannelData) => void;
|
setActiveChannel: (channel: ChannelData) => void;
|
||||||
|
@ -58,7 +59,7 @@ export function useMessenger(
|
||||||
|
|
||||||
const contactsClass = useMemo(() => {
|
const contactsClass = useMemo(() => {
|
||||||
if (messenger && identity) {
|
if (messenger && identity) {
|
||||||
const newContacts = new Contacts(
|
const newContacts = new ContactsClass(
|
||||||
identity,
|
identity,
|
||||||
messenger.waku,
|
messenger.waku,
|
||||||
(id, clock) => {
|
(id, clock) => {
|
||||||
|
@ -71,13 +72,25 @@ export function useMessenger(
|
||||||
}
|
}
|
||||||
}, [messenger, identity]);
|
}, [messenger, identity]);
|
||||||
|
|
||||||
const contacts = useMemo<Contact[]>(() => {
|
const [contacts, setContacts] = useState<Contacts>({});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
return Object.entries(internalContacts).map(([id, clock]) => {
|
setContacts((prev) => {
|
||||||
return {
|
const newContacts: Contacts = {};
|
||||||
id,
|
Object.entries(internalContacts).forEach(([id, clock]) => {
|
||||||
online: clock > now - 301000,
|
newContacts[id] = {
|
||||||
};
|
id,
|
||||||
|
online: clock > now - 301000,
|
||||||
|
trueName: id.slice(0, 10),
|
||||||
|
isUntrustworthy: false,
|
||||||
|
blocked: false,
|
||||||
|
};
|
||||||
|
if (prev[id]) {
|
||||||
|
newContacts[id] = { ...prev[id], ...newContacts[id] };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return newContacts;
|
||||||
});
|
});
|
||||||
}, [internalContacts]);
|
}, [internalContacts]);
|
||||||
|
|
||||||
|
@ -185,6 +198,7 @@ export function useMessenger(
|
||||||
loadingMessages,
|
loadingMessages,
|
||||||
communityData,
|
communityData,
|
||||||
contacts,
|
contacts,
|
||||||
|
setContacts,
|
||||||
channels,
|
channels,
|
||||||
activeChannel,
|
activeChannel,
|
||||||
setActiveChannel,
|
setActiveChannel,
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { useCallback, useMemo } from "react";
|
||||||
|
|
||||||
|
import { useMessengerContext } from "../contexts/messengerProvider";
|
||||||
|
|
||||||
|
export function useManageContact(id: string) {
|
||||||
|
const { contacts, setContacts } = useMessengerContext();
|
||||||
|
const contact = useMemo(() => contacts[id], [id, contacts]);
|
||||||
|
|
||||||
|
const setCustomName = useCallback(
|
||||||
|
(customName: string | undefined) => {
|
||||||
|
setContacts((prev) => {
|
||||||
|
const prevUser = prev[id];
|
||||||
|
if (!prevUser) return prev;
|
||||||
|
return { ...prev, [id]: { ...prevUser, customName } };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[id]
|
||||||
|
);
|
||||||
|
|
||||||
|
const setBlocked = useCallback(
|
||||||
|
(blocked: boolean) => {
|
||||||
|
setContacts((prev) => {
|
||||||
|
const prevUser = prev[id];
|
||||||
|
if (!prevUser) return prev;
|
||||||
|
return { ...prev, [id]: { ...prevUser, blocked } };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[id]
|
||||||
|
);
|
||||||
|
|
||||||
|
const setIsUntrustworthy = useCallback(
|
||||||
|
(isUntrustworthy: boolean) => {
|
||||||
|
setContacts((prev) => {
|
||||||
|
const prevUser = prev[id];
|
||||||
|
if (!prevUser) return prev;
|
||||||
|
return { ...prev, [id]: { ...prevUser, isUntrustworthy } };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[id]
|
||||||
|
);
|
||||||
|
return { contact, setCustomName, setBlocked, setIsUntrustworthy };
|
||||||
|
}
|
|
@ -1,4 +1,12 @@
|
||||||
export type Contact = {
|
export type Contact = {
|
||||||
id: string;
|
id: string;
|
||||||
online: boolean;
|
online: boolean;
|
||||||
|
trueName: string;
|
||||||
|
customName?: string;
|
||||||
|
isUntrustworthy: boolean;
|
||||||
|
blocked: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Contacts = {
|
||||||
|
[id: string]: Contact;
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { StatusUpdate_StatusType } from "./proto/communities/v1/status_update";
|
||||||
import { bufToHex } from "./utils";
|
import { bufToHex } from "./utils";
|
||||||
import { StatusUpdate } from "./wire/status_update";
|
import { StatusUpdate } from "./wire/status_update";
|
||||||
|
|
||||||
const STATUS_BROADCAST_INTERVAL = 300000;
|
const STATUS_BROADCAST_INTERVAL = 30000;
|
||||||
|
|
||||||
export class Contacts {
|
export class Contacts {
|
||||||
waku: Waku;
|
waku: Waku;
|
||||||
|
|
Loading…
Reference in New Issue