Add user blocking (#110)

This commit is contained in:
Szymon Szlachtowicz 2021-11-03 22:18:49 +01:00 committed by GitHub
parent 2714e8e9dd
commit c7626f5deb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 152 additions and 38 deletions

View File

@ -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`

View File

@ -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;
`;

View File

@ -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>

View File

@ -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>

View File

@ -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}
/>
);
}