diff --git a/packages/react-chat/src/components/Chat/ChatMessages.tsx b/packages/react-chat/src/components/Chat/ChatMessages.tsx index b1579070..d82f64f2 100644 --- a/packages/react-chat/src/components/Chat/ChatMessages.tsx +++ b/packages/react-chat/src/components/Chat/ChatMessages.tsx @@ -1,9 +1,11 @@ import React, { useEffect, useMemo, useRef, useState } from "react"; import styled from "styled-components"; +import { useBlockedUsers } from "../../contexts/blockedUsersProvider"; import { useMessengerContext } from "../../contexts/messengerProvider"; import { ChatMessage } from "../../models/ChatMessage"; import { equalDate } from "../../utils"; +import { ContactMenu } from "../Form/ContactMenu"; import { LoadingIcon } from "../Icons/LoadingIcon"; import { UserIcon } from "../Icons/UserIcon"; import { LinkModal } from "../Modals/LinkModal"; @@ -12,6 +14,60 @@ import { textSmallStyles } from "../Text"; import { ChatMessageContent } from "./ChatMessageContent"; +const today = new Date(); + +type ChatUiMessageProps = { + idx: number; + message: ChatMessage; + prevMessage: ChatMessage; + setImage: (img: string) => void; + setLink: (link: string) => void; +}; + +function ChatUiMessage({ + message, + idx, + prevMessage, + setImage, + setLink, +}: ChatUiMessageProps) { + const [showMenu, setShowMenu] = useState(false); + + return ( + + {(idx === 0 || !equalDate(prevMessage.date, message.date)) && ( + + {equalDate(message.date, today) + ? "Today" + : message.date.toLocaleDateString()} + + )} + + setShowMenu((e) => !e)}> + {showMenu && ( + + )} + + + + + + {message.sender.slice(0, 10)} + {message.date.toLocaleString()} + + + + + + + + ); +} + type ChatMessagesProps = { messages: ChatMessage[]; activeChannelId: string; @@ -19,9 +75,10 @@ type ChatMessagesProps = { export function ChatMessages({ messages, activeChannelId }: ChatMessagesProps) { const { loadPrevDay, loadingMessages } = useMessengerContext(); + const [scrollOnBot, setScrollOnBot] = useState(true); const ref = useRef(null); - const today = useMemo(() => new Date(), []); + useEffect(() => { if (ref && ref.current && scrollOnBot) { ref.current.scrollTop = ref.current.scrollHeight; @@ -39,6 +96,12 @@ export function ChatMessages({ messages, activeChannelId }: ChatMessagesProps) { } }, [messages, messages.length, loadingMessages]); + const { blockedUsers } = useBlockedUsers(); + + const shownMessages = useMemo(() => { + return messages.filter((message) => !blockedUsers.includes(message.sender)); + }, [messages, blockedUsers, messages.length]); + useEffect(() => { const setScroll = () => { if (ref && ref.current) { @@ -76,39 +139,16 @@ export function ChatMessages({ messages, activeChannelId }: ChatMessagesProps) { )} - {messages.map((message, idx) => { + {shownMessages.map((message, idx) => { return ( - - {(idx === 0 || - !equalDate(messages[idx - 1].date, message.date)) && ( - - {equalDate(message.date, today) - ? "Today" - : message.date.toLocaleDateString()} - - )} - - - - - - - - - {message.sender.slice(0, 10)} - - {message.date.toLocaleString()} - - - - - - - + ); })} @@ -178,6 +218,7 @@ export const Icon = styled.div` border-radius: 50%; background-color: #bcbdff; flex-shrink: 0; + position: relative; `; const UserNameWrapper = styled.div` diff --git a/packages/react-chat/src/components/Form/ContactMenu.tsx b/packages/react-chat/src/components/Form/ContactMenu.tsx new file mode 100644 index 00000000..90b604a4 --- /dev/null +++ b/packages/react-chat/src/components/Form/ContactMenu.tsx @@ -0,0 +1,38 @@ +import React, { useMemo } from "react"; +import styled from "styled-components"; + +import { useBlockedUsers } from "../../contexts/blockedUsersProvider"; + +import { DropdownMenu, MenuItem } from "./DropdownMenu"; + +type ContactMenuProps = { + id: string; + setShowMenu: (val: boolean) => void; +}; + +export function ContactMenu({ id, setShowMenu }: ContactMenuProps) { + const { blockedUsers, setBlockedUsers } = useBlockedUsers(); + const userInBlocked = useMemo( + () => blockedUsers.includes(id), + [blockedUsers, id] + ); + return ( + + { + userInBlocked + ? setBlockedUsers((prev) => prev.filter((e) => e != id)) + : setBlockedUsers((prev) => [...prev, id]); + setShowMenu(false); + }} + > + {userInBlocked ? "Unblock user" : "Block user"} + + + ); +} + +const ContactDropdown = styled(DropdownMenu)` + top: 20px; + left: 0px; +`; diff --git a/packages/react-chat/src/components/Members/Member.tsx b/packages/react-chat/src/components/Members/Member.tsx index 08eae260..304b6c2b 100644 --- a/packages/react-chat/src/components/Members/Member.tsx +++ b/packages/react-chat/src/components/Members/Member.tsx @@ -1,8 +1,9 @@ -import React, { useCallback } from "react"; +import React, { useCallback, useState } from "react"; import styled from "styled-components"; import { useNarrow } from "../../contexts/narrowProvider"; import { Icon } from "../Chat/ChatMessages"; +import { ContactMenu } from "../Form/ContactMenu"; import { UserIcon } from "../Icons/UserIcon"; interface MemberProps { @@ -38,6 +39,8 @@ export function Member({ setShowChannels(true); }; + const [showMenu, setShowMenu] = useState(false); + return ( { @@ -49,7 +52,9 @@ export function Member({ backgroundImage: "unset", }} className={isOnline ? "online" : "offline"} + onClick={() => setShowMenu((e) => !e)} > + {showMenu && } {member} diff --git a/packages/react-chat/src/components/ReactChat.tsx b/packages/react-chat/src/components/ReactChat.tsx index d04d144d..81e406c3 100644 --- a/packages/react-chat/src/components/ReactChat.tsx +++ b/packages/react-chat/src/components/ReactChat.tsx @@ -2,6 +2,7 @@ import React, { useRef } from "react"; import { ThemeProvider } from "styled-components"; import styled from "styled-components"; +import { BlockedUsersProvider } from "../contexts/blockedUsersProvider"; import { FetchMetadataProvider } from "../contexts/fetchMetadataProvider"; import { NarrowProvider } from "../contexts/narrowProvider"; import { Metadata } from "../models/Metadata"; @@ -26,10 +27,12 @@ export function ReactChat({ - - - - + + + + + + diff --git a/packages/react-chat/src/contexts/blockedUsersProvider.tsx b/packages/react-chat/src/contexts/blockedUsersProvider.tsx new file mode 100644 index 00000000..2250a27b --- /dev/null +++ b/packages/react-chat/src/contexts/blockedUsersProvider.tsx @@ -0,0 +1,27 @@ +import React, { createContext, useContext, useState } from "react"; + +const BlockedUsersContext = createContext<{ + blockedUsers: string[]; + setBlockedUsers: React.Dispatch>; +}>({ + blockedUsers: [], + setBlockedUsers: () => undefined, +}); + +export function useBlockedUsers() { + return useContext(BlockedUsersContext); +} + +interface BlockedUsersProviderProps { + children: React.ReactNode; +} + +export function BlockedUsersProvider({ children }: BlockedUsersProviderProps) { + const [blockedUsers, setBlockedUsers] = useState([]); + return ( + + ); +}