Contact menu (#122)

This commit is contained in:
Maria Rushkova 2021-11-15 13:34:18 +01:00 committed by GitHub
parent 045a64a353
commit 420271b4d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 615 additions and 95 deletions

View File

@ -2,7 +2,7 @@ import React, { useEffect, useMemo } from "react";
import styled from "styled-components"; import styled from "styled-components";
import { useMessengerContext } from "../../contexts/messengerProvider"; import { useMessengerContext } from "../../contexts/messengerProvider";
import { EditIcon } from "../Icons/EditIcon"; import { CreateIcon } from "../Icons/CreateIcon";
import { Channel } from "./Channel"; import { Channel } from "./Channel";
@ -63,7 +63,7 @@ export function Channels({
<ChatsBar> <ChatsBar>
<Heading>Chat</Heading> <Heading>Chat</Heading>
<EditBtn onClick={() => setCreateChat(true)}> <EditBtn onClick={() => setCreateChat(true)}>
<EditIcon /> <CreateIcon />
</EditBtn> </EditBtn>
</ChatsBar> </ChatsBar>
<ChatsList> <ChatsList>

View File

@ -10,10 +10,12 @@ import { equalDate } from "../../utils";
import { EmptyChannel } from "../Channels/EmptyChannel"; import { EmptyChannel } from "../Channels/EmptyChannel";
import { ContactMenu } from "../Form/ContactMenu"; import { ContactMenu } from "../Form/ContactMenu";
import { LoadingIcon } from "../Icons/LoadingIcon"; import { LoadingIcon } from "../Icons/LoadingIcon";
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 { textSmallStyles } from "../Text"; import { ProfileModal } from "../Modals/ProfileModal";
import { textMediumStyles, textSmallStyles } from "../Text";
import { ChatMessageContent } from "./ChatMessageContent"; import { ChatMessageContent } from "./ChatMessageContent";
@ -25,6 +27,7 @@ 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;
}; };
function ChatUiMessage({ function ChatUiMessage({
@ -33,8 +36,10 @@ function ChatUiMessage({
prevMessage, prevMessage,
setImage, setImage,
setLink, setLink,
setUser,
}: ChatUiMessageProps) { }: ChatUiMessageProps) {
const [showMenu, setShowMenu] = useState(false); const [showMenu, setShowMenu] = useState(false);
const [isUntrustworthy, setIsUntrustworthy] = useState(false);
return ( return (
<MessageOuterWrapper> <MessageOuterWrapper>
@ -46,16 +51,29 @@ function ChatUiMessage({
</DateSeparator> </DateSeparator>
)} )}
<MessageWrapper> <MessageWrapper>
<Icon onClick={() => setShowMenu((e) => !e)}> <Icon
onClick={() => {
setShowMenu((e) => !e);
setUser(message.sender);
}}
>
{showMenu && ( {showMenu && (
<ContactMenu id={message.sender} setShowMenu={setShowMenu} /> <ContactMenu
message={message}
setShowMenu={setShowMenu}
isUntrustworthy={isUntrustworthy}
setIsUntrustworthy={setIsUntrustworthy}
/>
)} )}
<UserIcon /> <UserIcon />
</Icon> </Icon>
<ContentWrapper> <ContentWrapper>
<MessageHeaderWrapper> <MessageHeaderWrapper>
<UserNameWrapper>{message.sender.slice(0, 10)}</UserNameWrapper> <UserNameWrapper>
<UserName>{message.sender.slice(0, 10)}</UserName>
{isUntrustworthy && <UntrustworthIcon />}
</UserNameWrapper>
<TimeWrapper>{message.date.toLocaleString()}</TimeWrapper> <TimeWrapper>{message.date.toLocaleString()}</TimeWrapper>
</MessageHeaderWrapper> </MessageHeaderWrapper>
<MessageText> <MessageText>
@ -85,6 +103,7 @@ export function ChatMessages() {
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);
@ -104,6 +123,7 @@ export function ChatMessages() {
<MessagesWrapper ref={ref}> <MessagesWrapper ref={ref}>
<PictureModal image={image} /> <PictureModal image={image} />
<LinkModal link={link} /> <LinkModal link={link} />
<ProfileModal user={user} />
<EmptyChannel channel={activeChannel} /> <EmptyChannel channel={activeChannel} />
{loadingMessages && ( {loadingMessages && (
<LoadingWrapper> <LoadingWrapper>
@ -118,6 +138,7 @@ export function ChatMessages() {
prevMessage={shownMessages[idx - 1]} prevMessage={shownMessages[idx - 1]}
setLink={setLink} setLink={setLink}
setImage={setImage} setImage={setImage}
setUser={setUser}
/> />
))} ))}
</MessagesWrapper> </MessagesWrapper>
@ -186,14 +207,23 @@ export const Icon = styled.div`
align-items: end; align-items: end;
border-radius: 50%; border-radius: 50%;
background-color: #bcbdff; background-color: #bcbdff;
background-size: contain;
background-position: center;
flex-shrink: 0; flex-shrink: 0;
position: relative; position: relative;
cursor: pointer;
`; `;
const UserNameWrapper = styled.div` const UserNameWrapper = styled.div`
font-size: 15px; display: flex;
line-height: 22px; align-items: center;
`;
const UserName = styled.p`
color: ${({ theme }) => theme.tertiary}; color: ${({ theme }) => theme.tertiary};
margin-right: 4px;
${textMediumStyles}
`; `;
const TimeWrapper = styled.div` const TimeWrapper = styled.div`

View File

@ -7,7 +7,7 @@ import { useNarrow } from "../../contexts/narrowProvider";
import { ChannelData } from "../../models/ChannelData"; import { ChannelData } from "../../models/ChannelData";
import { AddMemberIconSvg } from "../Icons/AddMemberIcon"; import { AddMemberIconSvg } from "../Icons/AddMemberIcon";
import { CheckSvg } from "../Icons/CheckIcon"; import { CheckSvg } from "../Icons/CheckIcon";
import { EgitGroupSvg } from "../Icons/EditGroupIcon"; import { EditSvg } from "../Icons/EditIcon";
import { LeftIconSvg } from "../Icons/LeftIcon"; import { LeftIconSvg } from "../Icons/LeftIcon";
import { MembersSmallSvg } from "../Icons/MembersSmallIcon"; import { MembersSmallSvg } from "../Icons/MembersSmallIcon";
import { MuteSvg } from "../Icons/MuteIcon"; import { MuteSvg } from "../Icons/MuteIcon";
@ -43,7 +43,7 @@ export const ChannelMenu = ({
setShowChannelMenu(false); setShowChannelMenu(false);
}} }}
> >
<MembersSmallSvg height={16} width={16} /> <MembersSmallSvg width={16} height={16} />
<MenuText>View Members</MenuText> <MenuText>View Members</MenuText>
</MenuItem> </MenuItem>
)} )}
@ -59,7 +59,7 @@ export const ChannelMenu = ({
<MenuText>Add / remove from group</MenuText> <MenuText>Add / remove from group</MenuText>
</MenuItem> </MenuItem>
<MenuItem onClick={() => setModal(true)}> <MenuItem onClick={() => setModal(true)}>
<EgitGroupSvg width={16} height={16} /> <EditSvg width={16} height={16} />
<MenuText>Edit name and image</MenuText> <MenuText>Edit name and image</MenuText>
</MenuItem> </MenuItem>
</> </>

View File

@ -2,22 +2,93 @@ import React, { useMemo } from "react";
import styled from "styled-components"; import styled from "styled-components";
import { useBlockedUsers } from "../../contexts/blockedUsersProvider"; import { useBlockedUsers } from "../../contexts/blockedUsersProvider";
import { useModal } from "../../contexts/modalProvider";
import { ChatMessage } from "../../models/ChatMessage";
import { Icon } from "../Chat/ChatMessages";
import { AddContactSvg } from "../Icons/AddContactIcon";
import { BlockSvg } from "../Icons/BlockIcon";
import { EditSvg } from "../Icons/EditIcon";
import { ProfileSvg } from "../Icons/ProfileIcon";
import { UntrustworthIcon } from "../Icons/UntrustworthIcon";
import { UserIcon } from "../Icons/UserIcon";
import { WarningSvg } from "../Icons/WarningIcon";
import { ProfileModalName } from "../Modals/ProfileModal";
import { textMediumStyles } from "../Text";
import { DropdownMenu, MenuItem } from "./DropdownMenu"; import { DropdownMenu, MenuItem, MenuText } from "./DropdownMenu";
type ContactMenuProps = { type ContactMenuProps = {
id: string; message: ChatMessage;
setShowMenu: (val: boolean) => void; setShowMenu: (val: boolean) => void;
isUntrustworthy: boolean;
setIsUntrustworthy: (val: boolean) => void;
}; };
export function ContactMenu({ id, setShowMenu }: ContactMenuProps) { export function ContactMenu({
message,
setShowMenu,
isUntrustworthy,
setIsUntrustworthy,
}: ContactMenuProps) {
const id = message.sender;
const { blockedUsers, setBlockedUsers } = useBlockedUsers(); const { blockedUsers, setBlockedUsers } = useBlockedUsers();
const userInBlocked = useMemo( const userInBlocked = useMemo(
() => blockedUsers.includes(id), () => blockedUsers.includes(id),
[blockedUsers, id] [blockedUsers, id]
); );
const { setModal } = useModal(ProfileModalName);
return ( return (
<ContactDropdown> <ContactDropdown>
<ContactInfo>
{message.image ? (
<Icon
style={{
backgroundImage: `url(${message.image}`,
}}
/>
) : (
<UserIcon />
)}
<UserNameWrapper>
<UserName>{message.sender.slice(0, 10)}</UserName>
{isUntrustworthy && <UntrustworthIcon />}
</UserNameWrapper>
<UserAddress>
{message.sender.slice(0, 10)}...{message.sender.slice(-3)}
</UserAddress>
</ContactInfo>
<MenuSection>
<MenuItem onClick={() => setModal(true)}>
<ProfileSvg width={16} height={16} />
<MenuText>View Profile</MenuText>
</MenuItem>
<MenuItem>
<AddContactSvg width={16} height={16} />
<MenuText>Send Contact Request</MenuText>
</MenuItem>
<MenuItem>
<EditSvg width={16} height={16} />
<MenuText>Rename</MenuText>
</MenuItem>
</MenuSection>
<MenuSection>
<MenuItem onClick={() => setIsUntrustworthy(!isUntrustworthy)}>
<WarningSvg
width={16}
height={16}
className={isUntrustworthy ? "" : "red"}
/>
<MenuText className={isUntrustworthy ? "" : "red"}>
{isUntrustworthy
? "Remove Untrustworthy Mark"
: "Mark as Untrustworthy"}
</MenuText>
</MenuItem>
{!userInBlocked && (
<MenuItem <MenuItem
onClick={() => { onClick={() => {
userInBlocked userInBlocked
@ -26,8 +97,11 @@ export function ContactMenu({ id, setShowMenu }: ContactMenuProps) {
setShowMenu(false); setShowMenu(false);
}} }}
> >
{userInBlocked ? "Unblock user" : "Block user"} <BlockSvg width={16} height={16} className="red" />
<MenuText className="red">Block User</MenuText>
</MenuItem> </MenuItem>
)}
</MenuSection>
</ContactDropdown> </ContactDropdown>
); );
} }
@ -35,4 +109,38 @@ export function ContactMenu({ id, setShowMenu }: ContactMenuProps) {
const ContactDropdown = styled(DropdownMenu)` const ContactDropdown = styled(DropdownMenu)`
top: 20px; top: 20px;
left: 0px; left: 0px;
width: 222px;
`;
const ContactInfo = styled.div`
display: flex;
flex-direction: column;
align-items: center;
`;
const MenuSection = styled.div`
margin-top: 5px;
padding-top: 5px;
border-top: 1px solid ${({ theme }) => theme.inputColor};
`;
const UserNameWrapper = styled.div`
display: flex;
align-items: center;
margin-bottom: 4px;
`;
const UserName = styled.p`
color: ${({ theme }) => theme.primary};
margin-right: 4px;
${textMediumStyles}
`;
const UserAddress = styled.p`
font-size: 10px;
line-height: 14px;
letter-spacing: 0.2px;
margin-bottom: 8px;
color: ${({ theme }) => theme.secondary};
`; `;

View File

@ -20,11 +20,12 @@ export const MenuItem = styled.li`
width: 100%; width: 100%;
display: flex; display: flex;
align-items: center; align-items: center;
padding: 8px 14px; padding: 8px 8px 8px 14px;
cursor: pointer; cursor: pointer;
color: ${({ theme }) => theme.primary}; color: ${({ theme }) => theme.primary};
&:hover { &:hover,
&:hover > span {
background: ${({ theme }) => theme.tertiary}; background: ${({ theme }) => theme.tertiary};
color: ${({ theme }) => theme.bodyBackgroundColor}; color: ${({ theme }) => theme.bodyBackgroundColor};
} }
@ -33,6 +34,10 @@ export const MenuItem = styled.li`
fill: ${({ theme }) => theme.tertiary}; fill: ${({ theme }) => theme.tertiary};
} }
& > svg.red {
fill: ${({ theme }) => theme.redColor};
}
&:hover > svg { &:hover > svg {
fill: ${({ theme }) => theme.bodyBackgroundColor}; fill: ${({ theme }) => theme.bodyBackgroundColor};
} }
@ -40,9 +45,15 @@ export const MenuItem = styled.li`
export const MenuText = styled.span` export const MenuText = styled.span`
margin-left: 6px; margin-left: 6px;
color: ${({ theme }) => theme.tertiary};
&.red {
color: ${({ theme }) => theme.redColor};
}
${textSmallStyles} ${textSmallStyles}
`; `;
const MenuBlock = styled.div` const MenuBlock = styled.div`
width: 207px; width: 207px;
background: ${({ theme }) => theme.bodyBackgroundColor}; background: ${({ theme }) => theme.bodyBackgroundColor};

View File

@ -0,0 +1,47 @@
import React from "react";
import styled from "styled-components";
type AddContactSvgProps = {
width: number;
height: number;
className?: string;
};
export function AddContactSvg({
width,
height,
className,
}: AddContactSvgProps) {
return (
<svg
width={width}
height={height}
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className={className}
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M11.3337 5.33333C11.3337 7.17428 9.84127 8.66667 8.00033 8.66667C6.15938 8.66667 4.66699 7.17428 4.66699 5.33333C4.66699 3.49238 6.15938 2 8.00033 2C9.84127 2 11.3337 3.49238 11.3337 5.33333ZM10.3337 5.33333C10.3337 6.622 9.28899 7.66667 8.00033 7.66667C6.71166 7.66667 5.66699 6.622 5.66699 5.33333C5.66699 4.04467 6.71166 3 8.00033 3C9.28899 3 10.3337 4.04467 10.3337 5.33333Z"
/>
<path d="M8.07357 11.6671C8.35289 11.6707 8.60016 11.4677 8.62336 11.1893C8.64601 10.9175 8.44624 10.6758 8.17357 10.6689C8.11597 10.6674 8.05818 10.6667 8.00022 10.6667C5.97321 10.6667 4.15749 11.5713 2.93477 12.9989C2.77487 13.1856 2.79579 13.4622 2.96961 13.636C3.18548 13.8519 3.54282 13.8208 3.74449 13.5916C4.78296 12.4114 6.30462 11.6667 8.00022 11.6667C8.02471 11.6667 8.04916 11.6668 8.07357 11.6671Z" />
<path d="M10.167 11.1667C10.167 10.8905 10.3908 10.6667 10.667 10.6667H11.5003C11.6844 10.6667 11.8337 10.5174 11.8337 10.3333V9.5C11.8337 9.22386 12.0575 9 12.3337 9C12.6098 9 12.8337 9.22386 12.8337 9.5V10.3333C12.8337 10.5174 12.9829 10.6667 13.167 10.6667H14.0003C14.2765 10.6667 14.5003 10.8905 14.5003 11.1667C14.5003 11.4428 14.2765 11.6667 14.0003 11.6667H13.167C12.9829 11.6667 12.8337 11.8159 12.8337 12V12.8333C12.8337 13.1095 12.6098 13.3333 12.3337 13.3333C12.0575 13.3333 11.8337 13.1095 11.8337 12.8333V12C11.8337 11.8159 11.6844 11.6667 11.5003 11.6667H10.667C10.3908 11.6667 10.167 11.4428 10.167 11.1667Z" />
</svg>
);
}
export const AddContactIcon = () => {
return <Icon width={16} height={16} />;
};
const Icon = styled(AddContactSvg)`
& > path {
fill: ${({ theme }) => theme.tertiary};
}
&:hover > path {
fill: ${({ theme }) => theme.bodyBackgroundColor};
}
`;

View File

@ -0,0 +1,41 @@
import React from "react";
import styled from "styled-components";
type BlockSvgProps = {
width: number;
height: number;
className?: string;
};
export function BlockSvg({ width, height, className }: BlockSvgProps) {
return (
<svg
width={width}
height={height}
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className={className}
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M8.00065 14.6673C4.31875 14.6673 1.33398 11.6825 1.33398 8.00065C1.33398 4.31875 4.31875 1.33398 8.00065 1.33398C11.6826 1.33398 14.6673 4.31875 14.6673 8.00065C14.6673 11.6826 11.6825 14.6673 8.00065 14.6673ZM3.91306 11.3811C3.77473 11.5195 3.54679 11.5099 3.43096 11.3523C2.74134 10.4136 2.33398 9.2547 2.33398 8.00065C2.33398 4.87104 4.87104 2.33398 8.00065 2.33398C9.2547 2.33398 10.4136 2.74135 11.3523 3.43096C11.5099 3.54679 11.5195 3.77473 11.3811 3.91306L3.91306 11.3811ZM4.62017 12.0882C4.48183 12.2266 4.49138 12.4545 4.64904 12.5703C5.58769 13.26 6.7466 13.6673 8.00065 13.6673C11.1303 13.6673 13.6673 11.1303 13.6673 8.00065C13.6673 6.7466 13.26 5.58769 12.5703 4.64904C12.4545 4.49138 12.2266 4.48183 12.0882 4.62017L4.62017 12.0882Z"
/>
</svg>
);
}
export const BlockIcon = () => {
return <Icon width={16} height={16} />;
};
const Icon = styled(BlockSvg)`
& > path {
fill: ${({ theme }) => theme.redColor};
}
&:hover > path {
fill: ${({ theme }) => theme.bodyBackgroundColor};
}
`;

View File

@ -0,0 +1,27 @@
import React from "react";
import styled from "styled-components";
export const CreateIcon = () => {
return (
<Icon
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M11.25 3.50019C11.6642 3.50019 12 3.1644 12 2.75019C12 2.33598 11.6642 2.00019 11.25 2.00019L6 2.00019C3.79086 2.00019 2 3.79105 2 6.00019L2 18.0002C2 20.2093 3.79086 22.0002 6 22.0002H18C20.2091 22.0002 22 20.2093 22 18.0002V12.7502C22 12.336 21.6642 12.0002 21.25 12.0002C20.8358 12.0002 20.5 12.336 20.5 12.7502V18.0002C20.5 19.3809 19.3807 20.5002 18 20.5002H6C4.61929 20.5002 3.5 19.3809 3.5 18.0002L3.5 6.00019C3.5 4.61948 4.61929 3.50019 6 3.50019L11.25 3.50019Z" />
<path
fillRule="evenodd"
clipRule="evenodd"
d="M21.2803 2.71986C20.2971 1.73661 18.7029 1.73661 17.7197 2.71986L9.33613 11.1034C9.00567 11.4339 8.76487 11.8431 8.63648 12.2925L7.77886 15.2941C7.70403 15.556 7.77707 15.8379 7.96967 16.0305C8.16227 16.2231 8.44415 16.2962 8.70604 16.2213L11.7077 15.3637C12.1571 15.2353 12.5663 14.9945 12.8968 14.6641L21.2803 6.28052C22.2636 5.29727 22.2636 3.70311 21.2803 2.71986ZM18.7803 3.78052C19.1778 3.38306 19.8222 3.38306 20.2197 3.78052C20.6171 4.17798 20.6171 4.8224 20.2197 5.21986L11.8361 13.6034C11.6859 13.7536 11.4999 13.8631 11.2956 13.9214C10.5531 14.1336 9.86662 13.4471 10.0788 12.7045C10.1371 12.5003 10.2466 12.3143 10.3968 12.1641L18.7803 3.78052Z"
/>
</Icon>
);
};
const Icon = styled.svg`
& > path {
fill: ${({ theme }) => theme.primary};
}
`;

View File

@ -1,41 +0,0 @@
import React from "react";
import styled from "styled-components";
type EgitGroupSvgProps = {
width: number;
height: number;
className?: string;
};
export function EgitGroupSvg({ width, height, className }: EgitGroupSvgProps) {
return (
<svg
width={width}
height={height}
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className={className}
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M10.7914 2.16376C11.6321 1.32302 12.9952 1.32302 13.836 2.16376C14.6767 3.00451 14.6767 4.36763 13.836 5.20838L6.0015 13.0429C5.71671 13.3276 5.36405 13.5352 4.97679 13.6458L2.1717 14.4472C1.99679 14.4972 1.80854 14.4484 1.67992 14.3198C1.55129 14.1912 1.50251 14.0029 1.55249 13.828L2.35394 11.0229C2.46459 10.6357 2.6721 10.283 2.95688 9.99823L10.7914 2.16376ZM13.1276 2.87212C12.6781 2.42258 11.9492 2.42258 11.4997 2.87212L3.66524 10.7066C3.50083 10.871 3.38103 11.0746 3.31716 11.2981C3.07579 12.1429 3.85682 12.9239 4.70159 12.6826C4.92515 12.6187 5.12874 12.4989 5.29315 12.3345L13.1276 4.50003C13.5772 4.05049 13.5772 3.32165 13.1276 2.87212Z"
/>
</svg>
);
}
export const EgitGroupIcon = () => {
return <Icon width={16} height={16} />;
};
const Icon = styled(EgitGroupSvg)`
& > path {
fill: ${({ theme }) => theme.tertiary};
}
&:hover > path {
fill: ${({ theme }) => theme.bodyBackgroundColor};
}
`;

View File

@ -1,27 +1,40 @@
import React from "react"; import React from "react";
import styled from "styled-components"; import styled from "styled-components";
export const EditIcon = () => { type EditSvgProps = {
width: number;
height: number;
className?: string;
};
export function EditSvg({ width, height, className }: EditSvgProps) {
return ( return (
<Icon <svg
width="24" width={width}
height="24" height={height}
viewBox="0 0 24 24" viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
className={className}
> >
<path d="M11.25 3.50019C11.6642 3.50019 12 3.1644 12 2.75019C12 2.33598 11.6642 2.00019 11.25 2.00019L6 2.00019C3.79086 2.00019 2 3.79105 2 6.00019L2 18.0002C2 20.2093 3.79086 22.0002 6 22.0002H18C20.2091 22.0002 22 20.2093 22 18.0002V12.7502C22 12.336 21.6642 12.0002 21.25 12.0002C20.8358 12.0002 20.5 12.336 20.5 12.7502V18.0002C20.5 19.3809 19.3807 20.5002 18 20.5002H6C4.61929 20.5002 3.5 19.3809 3.5 18.0002L3.5 6.00019C3.5 4.61948 4.61929 3.50019 6 3.50019L11.25 3.50019Z" />
<path <path
fillRule="evenodd" fillRule="evenodd"
clipRule="evenodd" clipRule="evenodd"
d="M21.2803 2.71986C20.2971 1.73661 18.7029 1.73661 17.7197 2.71986L9.33613 11.1034C9.00567 11.4339 8.76487 11.8431 8.63648 12.2925L7.77886 15.2941C7.70403 15.556 7.77707 15.8379 7.96967 16.0305C8.16227 16.2231 8.44415 16.2962 8.70604 16.2213L11.7077 15.3637C12.1571 15.2353 12.5663 14.9945 12.8968 14.6641L21.2803 6.28052C22.2636 5.29727 22.2636 3.70311 21.2803 2.71986ZM18.7803 3.78052C19.1778 3.38306 19.8222 3.38306 20.2197 3.78052C20.6171 4.17798 20.6171 4.8224 20.2197 5.21986L11.8361 13.6034C11.6859 13.7536 11.4999 13.8631 11.2956 13.9214C10.5531 14.1336 9.86662 13.4471 10.0788 12.7045C10.1371 12.5003 10.2466 12.3143 10.3968 12.1641L18.7803 3.78052Z" d="M10.7914 2.16376C11.6321 1.32302 12.9952 1.32302 13.836 2.16376C14.6767 3.00451 14.6767 4.36763 13.836 5.20838L6.0015 13.0429C5.71671 13.3276 5.36405 13.5352 4.97679 13.6458L2.1717 14.4472C1.99679 14.4972 1.80854 14.4484 1.67992 14.3198C1.55129 14.1912 1.50251 14.0029 1.55249 13.828L2.35394 11.0229C2.46459 10.6357 2.6721 10.283 2.95688 9.99823L10.7914 2.16376ZM13.1276 2.87212C12.6781 2.42258 11.9492 2.42258 11.4997 2.87212L3.66524 10.7066C3.50083 10.871 3.38103 11.0746 3.31716 11.2981C3.07579 12.1429 3.85682 12.9239 4.70159 12.6826C4.92515 12.6187 5.12874 12.4989 5.29315 12.3345L13.1276 4.50003C13.5772 4.05049 13.5772 3.32165 13.1276 2.87212Z"
/> />
</Icon> </svg>
); );
}
export const EditIcon = () => {
return <Icon width={16} height={16} />;
}; };
const Icon = styled.svg` const Icon = styled(EditSvg)`
& > path { & > path {
fill: ${({ theme }) => theme.primary}; fill: ${({ theme }) => theme.tertiary};
}
&:hover > path {
fill: ${({ theme }) => theme.bodyBackgroundColor};
} }
`; `;

View File

@ -0,0 +1,47 @@
import React from "react";
import styled from "styled-components";
type ProfileSvgProps = {
width: number;
height: number;
className?: string;
};
export function ProfileSvg({ width, height, className }: ProfileSvgProps) {
return (
<svg
width={width}
height={height}
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className={className}
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M8.00065 8.66732C9.28932 8.66732 10.334 7.62265 10.334 6.33398C10.334 5.04532 9.28932 4.00065 8.00065 4.00065C6.71199 4.00065 5.66732 5.04532 5.66732 6.33398C5.66732 7.62265 6.71199 8.66732 8.00065 8.66732ZM8.00065 7.66732C8.73703 7.66732 9.33398 7.07036 9.33398 6.33398C9.33398 5.5976 8.73703 5.00065 8.00065 5.00065C7.26427 5.00065 6.66732 5.5976 6.66732 6.33398C6.66732 7.07036 7.26427 7.66732 8.00065 7.66732Z"
/>
<path d="M10.965 10.8494C11.2086 11.0388 11.2153 11.3976 10.9778 11.5946C10.7747 11.763 10.4827 11.734 10.2702 11.5777C9.63485 11.1102 8.85006 10.834 8.00075 10.834C7.15145 10.834 6.36666 11.1102 5.7313 11.5777C5.51884 11.734 5.22678 11.763 5.02374 11.5946C4.78617 11.3976 4.79289 11.0388 5.03654 10.8494C5.85506 10.213 6.88365 9.83398 8.00075 9.83398C9.11786 9.83398 10.1465 10.213 10.965 10.8494Z" />
<path
fillRule="evenodd"
clipRule="evenodd"
d="M8.00065 14.6673C11.6825 14.6673 14.6673 11.6825 14.6673 8.00065C14.6673 4.31875 11.6825 1.33398 8.00065 1.33398C4.31875 1.33398 1.33398 4.31875 1.33398 8.00065C1.33398 11.6825 4.31875 14.6673 8.00065 14.6673ZM8.00065 13.6673C11.1303 13.6673 13.6673 11.1303 13.6673 8.00065C13.6673 4.87104 11.1303 2.33398 8.00065 2.33398C4.87104 2.33398 2.33398 4.87104 2.33398 8.00065C2.33398 11.1303 4.87104 13.6673 8.00065 13.6673Z"
/>
</svg>
);
}
export const ProfileIcon = () => {
return <Icon width={16} height={16} />;
};
const Icon = styled(ProfileSvg)`
& > path {
fill: ${({ theme }) => theme.tertiary};
}
&:hover > path {
fill: ${({ theme }) => theme.bodyBackgroundColor};
}
`;

View File

@ -0,0 +1,28 @@
import React from "react";
import styled from "styled-components";
export const UntrustworthIcon = () => {
return (
<Icon
width="11"
height="10"
viewBox="0 0 11 10"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="5.5" cy="5" r="5" />
<path
fillRule="evenodd"
clipRule="evenodd"
d="M6.28125 7.65625C6.28125 8.08772 5.93147 8.4375 5.5 8.4375C5.06853 8.4375 4.71875 8.08772 4.71875 7.65625C4.71875 7.22478 5.06853 6.875 5.5 6.875C5.93147 6.875 6.28125 7.22478 6.28125 7.65625ZM5.5 1.875C5.15482 1.875 4.875 2.15482 4.875 2.5V5.3125C4.875 5.65768 5.15482 5.9375 5.5 5.9375C5.84518 5.9375 6.125 5.65768 6.125 5.3125V2.5C6.125 2.15482 5.84518 1.875 5.5 1.875Z"
fill="white"
/>
</Icon>
);
};
const Icon = styled.svg`
& > circle {
fill: ${({ theme }) => theme.redColor};
}
`;

View File

@ -0,0 +1,47 @@
import React from "react";
import styled from "styled-components";
type WarningSvgProps = {
width: number;
height: number;
className?: string;
};
export function WarningSvg({ width, height, className }: WarningSvgProps) {
return (
<svg
width={width}
height={height}
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className={className}
>
<path d="M8.00065 4.16732C8.27679 4.16732 8.50065 4.39118 8.50065 4.66732V9.33398C8.50065 9.61013 8.27679 9.83398 8.00065 9.83398C7.72451 9.83398 7.50065 9.61013 7.50065 9.33398V4.66732C7.50065 4.39118 7.72451 4.16732 8.00065 4.16732Z" />
<path d="M8.00065 12.5007C8.46089 12.5007 8.83399 12.1276 8.83399 11.6673C8.83399 11.2071 8.46089 10.834 8.00065 10.834C7.54041 10.834 7.16732 11.2071 7.16732 11.6673C7.16732 12.1276 7.54041 12.5007 8.00065 12.5007Z" />
<path
fillRule="evenodd"
clipRule="evenodd"
d="M1.33398 8.00065C1.33398 11.6825 4.31875 14.6673 8.00065 14.6673C11.6825 14.6673 14.6673 11.6825 14.6673 8.00065C14.6673 4.31875 11.6826 1.33398 8.00065 1.33398C4.31875 1.33398 1.33398 4.31875 1.33398 8.00065ZM2.33398 8.00065C2.33398 11.1303 4.87104 13.6673 8.00065 13.6673C11.1303 13.6673 13.6673 11.1303 13.6673 8.00065C13.6673 4.87104 11.1303 2.33398 8.00065 2.33398C4.87104 2.33398 2.33398 4.87104 2.33398 8.00065Z"
/>
</svg>
);
}
export const WarningIcon = () => {
return <Icon width={16} height={16} />;
};
const Icon = styled(WarningSvg)`
& > path {
fill: ${({ theme }) => theme.tertiary};
}
&.red > path {
fill: ${({ theme }) => theme.redColor};
}
&:hover > path {
fill: ${({ theme }) => theme.bodyBackgroundColor};
}
`;

View File

@ -1,8 +1,8 @@
import React, { useState } from "react"; import React from "react";
import styled from "styled-components"; import styled from "styled-components";
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 {
@ -30,7 +30,7 @@ export function Member({
}); });
}; };
const [showMenu, setShowMenu] = useState(false); // const [showMenu, setShowMenu] = useState(false);
const onMemberClick = () => { const onMemberClick = () => {
switchShowMembers?.(); switchShowMembers?.();
@ -44,9 +44,9 @@ 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={member} setShowMenu={setShowMenu} />} */}
<UserIcon memberView={true} /> <UserIcon memberView={true} />
</MemberIcon> </MemberIcon>
<MemberName>{member}</MemberName> <MemberName>{member}</MemberName>

View File

@ -9,7 +9,7 @@ import { AddIcon } from "../Icons/AddIcon";
import { textMediumStyles } from "../Text"; import { textMediumStyles } from "../Text";
import { Modal } from "./Modal"; import { Modal } from "./Modal";
import { Heading, Section } from "./ModalStyle"; import { ButtonSection, Heading, Section } from "./ModalStyle";
export const EditModalName = "editModal"; export const EditModalName = "editModal";
@ -167,11 +167,6 @@ const AddPictureInput = styled.input`
cursor: pointer; cursor: pointer;
`; `;
const ButtonSection = styled(Section)`
display: flex;
justify-content: flex-end;
`;
const SaveBtn = styled.button` const SaveBtn = styled.button`
padding: 11px 24px; padding: 11px 24px;

View File

@ -6,7 +6,7 @@ import { buttonStyles } from "../Buttons/buttonStyle";
import { textMediumStyles } from "../Text"; import { textMediumStyles } from "../Text";
import { Modal } from "./Modal"; import { Modal } from "./Modal";
import { Heading, Section } from "./ModalStyle"; import { ButtonSection, Heading, Section } from "./ModalStyle";
export const LinkModalName = "LinkModal"; export const LinkModalName = "LinkModal";
@ -47,11 +47,6 @@ const Link = styled.a`
${textMediumStyles} ${textMediumStyles}
`; `;
const ButtonSection = styled(Section)`
display: flex;
justify-content: flex-end;
`;
const ButtonNo = styled.button` const ButtonNo = styled.button`
padding: 11px 24px; padding: 11px 24px;
margin-right: 16px; margin-right: 16px;

View File

@ -82,6 +82,10 @@ const ModalBody = styled.div`
max-width: 820px; max-width: 820px;
border-radius: 0; border-radius: 0;
} }
&.profile {
max-width: 640px;
}
`; `;
const ModalOverlay = styled.div` const ModalOverlay = styled.div`

View File

@ -22,3 +22,8 @@ export const Text = styled.p`
${textMediumStyles} ${textMediumStyles}
`; `;
export const ButtonSection = styled(Section)`
display: flex;
justify-content: flex-end;
`;

View File

@ -0,0 +1,163 @@
import React, { useState } from "react";
import styled from "styled-components";
import { copy } from "../../utils";
import { buttonStyles } from "../Buttons/buttonStyle";
import { CopySvg } from "../Icons/CopyIcon";
import { EditSvg } from "../Icons/EditIcon";
import { UntrustworthIcon } from "../Icons/UntrustworthIcon";
import { UserIcon } from "../Icons/UserIcon";
import { textMediumStyles } from "../Text";
import { Modal } from "./Modal";
import { ButtonSection, Heading, Section } from "./ModalStyle";
export const ProfileModalName = "profileModal";
interface ProfileModalProps {
user: string;
image?: string;
}
export const ProfileModal = ({ user, image }: ProfileModalProps) => {
const [isUntrustworthy, setIsUntrustworthy] = useState(false);
return (
<Modal name={ProfileModalName} className="profile">
<Section>
<Heading>{user.slice(0, 10)}s Profile</Heading>
</Section>
<Section>
<NameSection>
{image ? (
<ProfileIcon
style={{
backgroundImage: `url(${image}`,
}}
/>
) : (
<UserIcon />
)}
<UserNameWrapper>
<UserName>{user.slice(0, 10)}</UserName>
{isUntrustworthy && <UntrustworthIcon />}
<EditSvg width={24} height={24} />
</UserNameWrapper>
</NameSection>
<UserAddressWrapper>
<UserAddress>Chatkey: {user.slice(0, 30)}</UserAddress>
<CopyButton onClick={() => copy(user)}>
<CopySvg width={24} height={24} />
</CopyButton>
</UserAddressWrapper>
<EmojiKey>🎩🍞🥑🦍🌈📡💅🏻🔔👵🅱</EmojiKey>
</Section>
<ButtonSection>
<ProfileBtn className="red">Remove Contact</ProfileBtn>
<ProfileBtn
className={isUntrustworthy ? "" : "red"}
onClick={() => setIsUntrustworthy(!isUntrustworthy)}
>
{isUntrustworthy
? "Remove Untrustworthy Mark"
: "Mark as Untrustworthy"}
</ProfileBtn>
</ButtonSection>
</Modal>
);
};
const NameSection = styled.div`
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 16px;
`;
const ProfileIcon = styled.div`
width: 80px;
height: 80px;
display: flex;
justify-content: center;
align-items: end;
border-radius: 50%;
background-color: #bcbdff;
background-size: contain;
background-position: center;
flex-shrink: 0;
position: relative;
cursor: pointer;
`;
const UserNameWrapper = styled.div`
display: flex;
justify-content: center;
align-items: center;
margin-top: 4px;
& > svg {
fill: ${({ theme }) => theme.tertiary};
}
`;
const UserName = styled.p`
color: ${({ theme }) => theme.primary};
font-weight: bold;
font-size: 22px;
line-height: 30px;
letter-spacing: -0.2px;
margin-right: 8px;
`;
const UserAddressWrapper = styled.div`
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 24px;
`;
const UserAddress = styled.p`
display: flex;
letter-spacing: 1px;
margin-right: 8px;
color: ${({ theme }) => theme.secondary};
${textMediumStyles}
`;
const EmojiKey = styled.div`
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
gap: 8px;
font-size: 15px;
line-height: 14px;
letter-spacing: 0.2px;
`;
const ProfileBtn = styled.button`
padding: 11px 24px;
${buttonStyles}
background: ${({ theme }) => theme.bodyBackgroundColor};
border: 1px solid ${({ theme }) => theme.border};
border-radius: 8px;
margin-left: 8px;
&.red {
color: ${({ theme }) => theme.redColor};
}
&:hover {
background: ${({ theme }) => theme.buttonBgHover};
}
`;
const CopyButton = styled.button`
& > svg {
fill: ${({ theme }) => theme.tertiary};
}
`;