Add user blocking (#110)
This commit is contained in:
parent
2714e8e9dd
commit
c7626f5deb
|
@ -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 (
|
||||
<MessageOuterWrapper>
|
||||
{(idx === 0 || !equalDate(prevMessage.date, message.date)) && (
|
||||
<DateSeparator>
|
||||
{equalDate(message.date, today)
|
||||
? "Today"
|
||||
: message.date.toLocaleDateString()}
|
||||
</DateSeparator>
|
||||
)}
|
||||
<MessageWrapper>
|
||||
<Icon onClick={() => setShowMenu((e) => !e)}>
|
||||
{showMenu && (
|
||||
<ContactMenu id={message.sender} setShowMenu={setShowMenu} />
|
||||
)}
|
||||
<UserIcon />
|
||||
</Icon>
|
||||
|
||||
<ContentWrapper>
|
||||
<MessageHeaderWrapper>
|
||||
<UserNameWrapper>{message.sender.slice(0, 10)}</UserNameWrapper>
|
||||
<TimeWrapper>{message.date.toLocaleString()}</TimeWrapper>
|
||||
</MessageHeaderWrapper>
|
||||
<MessageText>
|
||||
<ChatMessageContent
|
||||
message={message}
|
||||
setImage={setImage}
|
||||
setLinkOpen={setLink}
|
||||
/>
|
||||
</MessageText>
|
||||
</ContentWrapper>
|
||||
</MessageWrapper>
|
||||
</MessageOuterWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
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<HTMLHeadingElement>(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) {
|
|||
<LoadingIcon className="message" />
|
||||
</LoadingWrapper>
|
||||
)}
|
||||
{messages.map((message, idx) => {
|
||||
{shownMessages.map((message, idx) => {
|
||||
return (
|
||||
<MessageOuterWrapper key={message.date.getTime()}>
|
||||
{(idx === 0 ||
|
||||
!equalDate(messages[idx - 1].date, message.date)) && (
|
||||
<DateSeparator>
|
||||
{equalDate(message.date, today)
|
||||
? "Today"
|
||||
: message.date.toLocaleDateString()}
|
||||
</DateSeparator>
|
||||
)}
|
||||
<MessageWrapper>
|
||||
<Icon>
|
||||
<UserIcon />
|
||||
</Icon>
|
||||
|
||||
<ContentWrapper>
|
||||
<MessageHeaderWrapper>
|
||||
<UserNameWrapper>
|
||||
{message.sender.slice(0, 10)}
|
||||
</UserNameWrapper>
|
||||
<TimeWrapper>{message.date.toLocaleString()}</TimeWrapper>
|
||||
</MessageHeaderWrapper>
|
||||
<MessageText>
|
||||
<ChatMessageContent
|
||||
<ChatUiMessage
|
||||
key={message.date.getTime()}
|
||||
message={message}
|
||||
idx={idx}
|
||||
prevMessage={shownMessages[idx - 1]}
|
||||
setLink={setLink}
|
||||
setImage={setImage}
|
||||
setLinkOpen={setLink}
|
||||
/>
|
||||
</MessageText>
|
||||
</ContentWrapper>
|
||||
</MessageWrapper>
|
||||
</MessageOuterWrapper>
|
||||
);
|
||||
})}
|
||||
</MessagesWrapper>
|
||||
|
@ -178,6 +218,7 @@ export const Icon = styled.div`
|
|||
border-radius: 50%;
|
||||
background-color: #bcbdff;
|
||||
flex-shrink: 0;
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
const UserNameWrapper = styled.div`
|
||||
|
|
|
@ -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 (
|
||||
<ContactDropdown>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
userInBlocked
|
||||
? setBlockedUsers((prev) => prev.filter((e) => e != id))
|
||||
: setBlockedUsers((prev) => [...prev, id]);
|
||||
setShowMenu(false);
|
||||
}}
|
||||
>
|
||||
{userInBlocked ? "Unblock user" : "Block user"}
|
||||
</MenuItem>
|
||||
</ContactDropdown>
|
||||
);
|
||||
}
|
||||
|
||||
const ContactDropdown = styled(DropdownMenu)`
|
||||
top: 20px;
|
||||
left: 0px;
|
||||
`;
|
|
@ -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 (
|
||||
<MemberData
|
||||
onClick={() => {
|
||||
|
@ -49,7 +52,9 @@ export function Member({
|
|||
backgroundImage: "unset",
|
||||
}}
|
||||
className={isOnline ? "online" : "offline"}
|
||||
onClick={() => setShowMenu((e) => !e)}
|
||||
>
|
||||
{showMenu && <ContactMenu id={member} setShowMenu={setShowMenu} />}
|
||||
<UserIcon memberView={true} />
|
||||
</MemberIcon>
|
||||
<MemberName>{member}</MemberName>
|
||||
|
|
|
@ -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({
|
|||
<ThemeProvider theme={theme}>
|
||||
<NarrowProvider myRef={ref}>
|
||||
<FetchMetadataProvider fetchMetadata={fetchMetadata}>
|
||||
<BlockedUsersProvider>
|
||||
<Wrapper ref={ref}>
|
||||
<GlobalStyle />
|
||||
<ChatLoader communityKey={communityKey} />
|
||||
</Wrapper>
|
||||
</BlockedUsersProvider>
|
||||
</FetchMetadataProvider>
|
||||
</NarrowProvider>
|
||||
</ThemeProvider>
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
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}
|
||||
/>
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue