diff --git a/packages/react-chat/src/components/Channels/Channels.tsx b/packages/react-chat/src/components/Channels/Channels.tsx index d8dc3429..d930fcfe 100644 --- a/packages/react-chat/src/components/Channels/Channels.tsx +++ b/packages/react-chat/src/components/Channels/Channels.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useMemo } from "react"; import styled from "styled-components"; import { useMessengerContext } from "../../contexts/messengerProvider"; -import { EditIcon } from "../Icons/EditIcon"; +import { CreateIcon } from "../Icons/CreateIcon"; import { Channel } from "./Channel"; @@ -63,7 +63,7 @@ export function Channels({ Chat setCreateChat(true)}> - + diff --git a/packages/react-chat/src/components/Chat/ChatMessages.tsx b/packages/react-chat/src/components/Chat/ChatMessages.tsx index 5f70e055..c04c9322 100644 --- a/packages/react-chat/src/components/Chat/ChatMessages.tsx +++ b/packages/react-chat/src/components/Chat/ChatMessages.tsx @@ -10,10 +10,12 @@ import { equalDate } from "../../utils"; import { EmptyChannel } from "../Channels/EmptyChannel"; import { ContactMenu } from "../Form/ContactMenu"; import { LoadingIcon } from "../Icons/LoadingIcon"; +import { UntrustworthIcon } from "../Icons/UntrustworthIcon"; import { UserIcon } from "../Icons/UserIcon"; import { LinkModal, LinkModalName } from "../Modals/LinkModal"; import { PictureModal, PictureModalName } from "../Modals/PictureModal"; -import { textSmallStyles } from "../Text"; +import { ProfileModal } from "../Modals/ProfileModal"; +import { textMediumStyles, textSmallStyles } from "../Text"; import { ChatMessageContent } from "./ChatMessageContent"; @@ -25,6 +27,7 @@ type ChatUiMessageProps = { prevMessage: ChatMessage; setImage: (img: string) => void; setLink: (link: string) => void; + setUser: (user: string) => void; }; function ChatUiMessage({ @@ -33,8 +36,10 @@ function ChatUiMessage({ prevMessage, setImage, setLink, + setUser, }: ChatUiMessageProps) { const [showMenu, setShowMenu] = useState(false); + const [isUntrustworthy, setIsUntrustworthy] = useState(false); return ( @@ -46,16 +51,29 @@ function ChatUiMessage({ )} - setShowMenu((e) => !e)}> + { + setShowMenu((e) => !e); + setUser(message.sender); + }} + > {showMenu && ( - + )} - {message.sender.slice(0, 10)} + + {message.sender.slice(0, 10)} + {isUntrustworthy && } + {message.date.toLocaleString()} @@ -85,6 +103,7 @@ export function ChatMessages() { const [image, setImage] = useState(""); const [link, setLink] = useState(""); + const [user, setUser] = useState(""); const { setModal: setPictureModal, isVisible: showPictureModal } = useModal(PictureModalName); @@ -104,6 +123,7 @@ export function ChatMessages() { + {loadingMessages && ( @@ -118,6 +138,7 @@ export function ChatMessages() { prevMessage={shownMessages[idx - 1]} setLink={setLink} setImage={setImage} + setUser={setUser} /> ))} @@ -186,14 +207,23 @@ export const Icon = styled.div` 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` - font-size: 15px; - line-height: 22px; + display: flex; + align-items: center; +`; + +const UserName = styled.p` color: ${({ theme }) => theme.tertiary}; + margin-right: 4px; + + ${textMediumStyles} `; const TimeWrapper = styled.div` diff --git a/packages/react-chat/src/components/Form/ChannelMenu.tsx b/packages/react-chat/src/components/Form/ChannelMenu.tsx index 2e60c214..0239db83 100644 --- a/packages/react-chat/src/components/Form/ChannelMenu.tsx +++ b/packages/react-chat/src/components/Form/ChannelMenu.tsx @@ -7,7 +7,7 @@ import { useNarrow } from "../../contexts/narrowProvider"; import { ChannelData } from "../../models/ChannelData"; import { AddMemberIconSvg } from "../Icons/AddMemberIcon"; import { CheckSvg } from "../Icons/CheckIcon"; -import { EgitGroupSvg } from "../Icons/EditGroupIcon"; +import { EditSvg } from "../Icons/EditIcon"; import { LeftIconSvg } from "../Icons/LeftIcon"; import { MembersSmallSvg } from "../Icons/MembersSmallIcon"; import { MuteSvg } from "../Icons/MuteIcon"; @@ -43,7 +43,7 @@ export const ChannelMenu = ({ setShowChannelMenu(false); }} > - + View Members )} @@ -59,7 +59,7 @@ export const ChannelMenu = ({ Add / remove from group setModal(true)}> - + Edit name and image diff --git a/packages/react-chat/src/components/Form/ContactMenu.tsx b/packages/react-chat/src/components/Form/ContactMenu.tsx index 90b604a4..c5597fde 100644 --- a/packages/react-chat/src/components/Form/ContactMenu.tsx +++ b/packages/react-chat/src/components/Form/ContactMenu.tsx @@ -2,32 +2,106 @@ import React, { useMemo } from "react"; import styled from "styled-components"; 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 = { - id: string; + message: ChatMessage; 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 userInBlocked = useMemo( () => blockedUsers.includes(id), [blockedUsers, id] ); + + const { setModal } = useModal(ProfileModalName); + return ( - { - userInBlocked - ? setBlockedUsers((prev) => prev.filter((e) => e != id)) - : setBlockedUsers((prev) => [...prev, id]); - setShowMenu(false); - }} - > - {userInBlocked ? "Unblock user" : "Block user"} - + + {message.image ? ( + + ) : ( + + )} + + {message.sender.slice(0, 10)} + {isUntrustworthy && } + + + {message.sender.slice(0, 10)}...{message.sender.slice(-3)} + + + + setModal(true)}> + + View Profile + + + + Send Contact Request + + + + Rename + + + + setIsUntrustworthy(!isUntrustworthy)}> + + + {isUntrustworthy + ? "Remove Untrustworthy Mark" + : "Mark as Untrustworthy"} + + + + {!userInBlocked && ( + { + userInBlocked + ? setBlockedUsers((prev) => prev.filter((e) => e != id)) + : setBlockedUsers((prev) => [...prev, id]); + setShowMenu(false); + }} + > + + Block User + + )} + ); } @@ -35,4 +109,38 @@ export function ContactMenu({ id, setShowMenu }: ContactMenuProps) { const ContactDropdown = styled(DropdownMenu)` top: 20px; 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}; `; diff --git a/packages/react-chat/src/components/Form/DropdownMenu.tsx b/packages/react-chat/src/components/Form/DropdownMenu.tsx index 5d468225..c1b02ecc 100644 --- a/packages/react-chat/src/components/Form/DropdownMenu.tsx +++ b/packages/react-chat/src/components/Form/DropdownMenu.tsx @@ -20,11 +20,12 @@ export const MenuItem = styled.li` width: 100%; display: flex; align-items: center; - padding: 8px 14px; + padding: 8px 8px 8px 14px; cursor: pointer; color: ${({ theme }) => theme.primary}; - &:hover { + &:hover, + &:hover > span { background: ${({ theme }) => theme.tertiary}; color: ${({ theme }) => theme.bodyBackgroundColor}; } @@ -33,6 +34,10 @@ export const MenuItem = styled.li` fill: ${({ theme }) => theme.tertiary}; } + & > svg.red { + fill: ${({ theme }) => theme.redColor}; + } + &:hover > svg { fill: ${({ theme }) => theme.bodyBackgroundColor}; } @@ -40,9 +45,15 @@ export const MenuItem = styled.li` export const MenuText = styled.span` margin-left: 6px; + color: ${({ theme }) => theme.tertiary}; + + &.red { + color: ${({ theme }) => theme.redColor}; + } ${textSmallStyles} `; + const MenuBlock = styled.div` width: 207px; background: ${({ theme }) => theme.bodyBackgroundColor}; diff --git a/packages/react-chat/src/components/Icons/AddContactIcon.tsx b/packages/react-chat/src/components/Icons/AddContactIcon.tsx new file mode 100644 index 00000000..425c80e3 --- /dev/null +++ b/packages/react-chat/src/components/Icons/AddContactIcon.tsx @@ -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 ( + + + + + + ); +} + +export const AddContactIcon = () => { + return ; +}; + +const Icon = styled(AddContactSvg)` + & > path { + fill: ${({ theme }) => theme.tertiary}; + } + + &:hover > path { + fill: ${({ theme }) => theme.bodyBackgroundColor}; + } +`; diff --git a/packages/react-chat/src/components/Icons/BlockIcon.tsx b/packages/react-chat/src/components/Icons/BlockIcon.tsx new file mode 100644 index 00000000..eae2daa4 --- /dev/null +++ b/packages/react-chat/src/components/Icons/BlockIcon.tsx @@ -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 ( + + + + ); +} + +export const BlockIcon = () => { + return ; +}; + +const Icon = styled(BlockSvg)` + & > path { + fill: ${({ theme }) => theme.redColor}; + } + + &:hover > path { + fill: ${({ theme }) => theme.bodyBackgroundColor}; + } +`; diff --git a/packages/react-chat/src/components/Icons/CreateIcon.tsx b/packages/react-chat/src/components/Icons/CreateIcon.tsx new file mode 100644 index 00000000..bbac7845 --- /dev/null +++ b/packages/react-chat/src/components/Icons/CreateIcon.tsx @@ -0,0 +1,27 @@ +import React from "react"; +import styled from "styled-components"; + +export const CreateIcon = () => { + return ( + + + + + ); +}; + +const Icon = styled.svg` + & > path { + fill: ${({ theme }) => theme.primary}; + } +`; diff --git a/packages/react-chat/src/components/Icons/EditGroupIcon.tsx b/packages/react-chat/src/components/Icons/EditGroupIcon.tsx deleted file mode 100644 index 012a9daa..00000000 --- a/packages/react-chat/src/components/Icons/EditGroupIcon.tsx +++ /dev/null @@ -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 ( - - - - ); -} - -export const EgitGroupIcon = () => { - return ; -}; - -const Icon = styled(EgitGroupSvg)` - & > path { - fill: ${({ theme }) => theme.tertiary}; - } - - &:hover > path { - fill: ${({ theme }) => theme.bodyBackgroundColor}; - } -`; diff --git a/packages/react-chat/src/components/Icons/EditIcon.tsx b/packages/react-chat/src/components/Icons/EditIcon.tsx index 78744b87..c2b516c6 100644 --- a/packages/react-chat/src/components/Icons/EditIcon.tsx +++ b/packages/react-chat/src/components/Icons/EditIcon.tsx @@ -1,27 +1,40 @@ import React from "react"; import styled from "styled-components"; -export const EditIcon = () => { +type EditSvgProps = { + width: number; + height: number; + className?: string; +}; + +export function EditSvg({ width, height, className }: EditSvgProps) { return ( - - - + ); +} + +export const EditIcon = () => { + return ; }; -const Icon = styled.svg` +const Icon = styled(EditSvg)` & > path { - fill: ${({ theme }) => theme.primary}; + fill: ${({ theme }) => theme.tertiary}; + } + + &:hover > path { + fill: ${({ theme }) => theme.bodyBackgroundColor}; } `; diff --git a/packages/react-chat/src/components/Icons/ProfileIcon.tsx b/packages/react-chat/src/components/Icons/ProfileIcon.tsx new file mode 100644 index 00000000..a12470e5 --- /dev/null +++ b/packages/react-chat/src/components/Icons/ProfileIcon.tsx @@ -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 ( + + + + + + ); +} + +export const ProfileIcon = () => { + return ; +}; + +const Icon = styled(ProfileSvg)` + & > path { + fill: ${({ theme }) => theme.tertiary}; + } + + &:hover > path { + fill: ${({ theme }) => theme.bodyBackgroundColor}; + } +`; diff --git a/packages/react-chat/src/components/Icons/UntrustworthIcon.tsx b/packages/react-chat/src/components/Icons/UntrustworthIcon.tsx new file mode 100644 index 00000000..99797a38 --- /dev/null +++ b/packages/react-chat/src/components/Icons/UntrustworthIcon.tsx @@ -0,0 +1,28 @@ +import React from "react"; +import styled from "styled-components"; + +export const UntrustworthIcon = () => { + return ( + + + + + ); +}; + +const Icon = styled.svg` + & > circle { + fill: ${({ theme }) => theme.redColor}; + } +`; diff --git a/packages/react-chat/src/components/Icons/WarningIcon.tsx b/packages/react-chat/src/components/Icons/WarningIcon.tsx new file mode 100644 index 00000000..6e06ec57 --- /dev/null +++ b/packages/react-chat/src/components/Icons/WarningIcon.tsx @@ -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 ( + + + + + + ); +} + +export const WarningIcon = () => { + return ; +}; + +const Icon = styled(WarningSvg)` + & > path { + fill: ${({ theme }) => theme.tertiary}; + } + + &.red > path { + fill: ${({ theme }) => theme.redColor}; + } + + &:hover > path { + fill: ${({ theme }) => theme.bodyBackgroundColor}; + } +`; diff --git a/packages/react-chat/src/components/Members/Member.tsx b/packages/react-chat/src/components/Members/Member.tsx index 78f15c46..56fe65f9 100644 --- a/packages/react-chat/src/components/Members/Member.tsx +++ b/packages/react-chat/src/components/Members/Member.tsx @@ -1,8 +1,8 @@ -import React, { useState } from "react"; +import React from "react"; import styled from "styled-components"; import { Icon } from "../Chat/ChatMessages"; -import { ContactMenu } from "../Form/ContactMenu"; +// import { ContactMenu } from '../Form/ContactMenu'; import { UserIcon } from "../Icons/UserIcon"; interface MemberProps { @@ -30,7 +30,7 @@ export function Member({ }); }; - const [showMenu, setShowMenu] = useState(false); + // const [showMenu, setShowMenu] = useState(false); const onMemberClick = () => { switchShowMembers?.(); @@ -44,9 +44,9 @@ export function Member({ backgroundImage: "unset", }} className={isOnline ? "online" : "offline"} - onClick={() => setShowMenu((e) => !e)} + // onClick={() => setShowMenu(e => !e)} > - {showMenu && } + {/* {showMenu && } */} {member} diff --git a/packages/react-chat/src/components/Modals/EditModal.tsx b/packages/react-chat/src/components/Modals/EditModal.tsx index 4adac4f3..69e5f0e5 100644 --- a/packages/react-chat/src/components/Modals/EditModal.tsx +++ b/packages/react-chat/src/components/Modals/EditModal.tsx @@ -9,7 +9,7 @@ import { AddIcon } from "../Icons/AddIcon"; import { textMediumStyles } from "../Text"; import { Modal } from "./Modal"; -import { Heading, Section } from "./ModalStyle"; +import { ButtonSection, Heading, Section } from "./ModalStyle"; export const EditModalName = "editModal"; @@ -167,11 +167,6 @@ const AddPictureInput = styled.input` cursor: pointer; `; -const ButtonSection = styled(Section)` - display: flex; - justify-content: flex-end; -`; - const SaveBtn = styled.button` padding: 11px 24px; diff --git a/packages/react-chat/src/components/Modals/LinkModal.tsx b/packages/react-chat/src/components/Modals/LinkModal.tsx index d20ecc17..8f01dc93 100644 --- a/packages/react-chat/src/components/Modals/LinkModal.tsx +++ b/packages/react-chat/src/components/Modals/LinkModal.tsx @@ -6,7 +6,7 @@ import { buttonStyles } from "../Buttons/buttonStyle"; import { textMediumStyles } from "../Text"; import { Modal } from "./Modal"; -import { Heading, Section } from "./ModalStyle"; +import { ButtonSection, Heading, Section } from "./ModalStyle"; export const LinkModalName = "LinkModal"; @@ -47,11 +47,6 @@ const Link = styled.a` ${textMediumStyles} `; -const ButtonSection = styled(Section)` - display: flex; - justify-content: flex-end; -`; - const ButtonNo = styled.button` padding: 11px 24px; margin-right: 16px; diff --git a/packages/react-chat/src/components/Modals/Modal.tsx b/packages/react-chat/src/components/Modals/Modal.tsx index d2a5a0b1..1b098fdb 100644 --- a/packages/react-chat/src/components/Modals/Modal.tsx +++ b/packages/react-chat/src/components/Modals/Modal.tsx @@ -82,6 +82,10 @@ const ModalBody = styled.div` max-width: 820px; border-radius: 0; } + + &.profile { + max-width: 640px; + } `; const ModalOverlay = styled.div` diff --git a/packages/react-chat/src/components/Modals/ModalStyle.ts b/packages/react-chat/src/components/Modals/ModalStyle.ts index 421a3ae8..cadc3dad 100644 --- a/packages/react-chat/src/components/Modals/ModalStyle.ts +++ b/packages/react-chat/src/components/Modals/ModalStyle.ts @@ -22,3 +22,8 @@ export const Text = styled.p` ${textMediumStyles} `; + +export const ButtonSection = styled(Section)` + display: flex; + justify-content: flex-end; +`; diff --git a/packages/react-chat/src/components/Modals/ProfileModal.tsx b/packages/react-chat/src/components/Modals/ProfileModal.tsx new file mode 100644 index 00000000..bc96f0fb --- /dev/null +++ b/packages/react-chat/src/components/Modals/ProfileModal.tsx @@ -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 ( + +
+ {user.slice(0, 10)}’s Profile +
+ +
+ + {image ? ( + + ) : ( + + )} + + {user.slice(0, 10)} + {isUntrustworthy && } + + + + + + Chatkey: {user.slice(0, 30)} + + copy(user)}> + + + + + 🎩🍞πŸ₯‘πŸ¦πŸŒˆπŸ“‘πŸ’…πŸ»β™£οΈπŸ””β›ΈπŸ‘΅πŸ…± +
+ + Remove Contact + setIsUntrustworthy(!isUntrustworthy)} + > + {isUntrustworthy + ? "Remove Untrustworthy Mark" + : "Mark as Untrustworthy"} + + +
+ ); +}; + +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}; + } +`;