Refactor dm and private chat creation (#133)

This commit is contained in:
Szymon Szlachtowicz 2021-11-18 19:23:33 +01:00 committed by GitHub
parent 81a14fcb9a
commit 7922343fa6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 165 additions and 233 deletions

View File

@ -1,4 +1,4 @@
import React, { useEffect, useMemo } from "react"; import React, { useEffect } from "react";
import styled from "styled-components"; import styled from "styled-components";
import { useMessengerContext } from "../../contexts/messengerProvider"; import { useMessengerContext } from "../../contexts/messengerProvider";
@ -7,45 +7,30 @@ import { CreateIcon } from "../Icons/CreateIcon";
import { Channel } from "./Channel"; import { Channel } from "./Channel";
interface ChannelsProps { interface ChannelsProps {
membersList: string[];
groupList: string[][];
setCreateChat: (val: boolean) => void; setCreateChat: (val: boolean) => void;
onCommunityClick?: () => void; onCommunityClick?: () => void;
} }
export function Channels({ type GenerateChannelsProps = ChannelsProps & {
membersList, type: string;
groupList, };
setCreateChat,
onCommunityClick,
}: ChannelsProps) {
const {
clearNotifications,
clearMentions,
mentions,
notifications,
activeChannel,
setActiveChannel,
channels,
} = useMessengerContext();
const activeChannelId = useMemo(() => activeChannel.id, [activeChannel]);
useEffect(() => {
const channel = channels.find((channel) => channel.id === activeChannelId);
if (channel) {
if (notifications[channel.id] > 0) {
clearNotifications(channel.id);
clearMentions(channel.id);
}
}
}, [notifications, activeChannelId]);
function GenerateChannels({
type,
onCommunityClick,
setCreateChat,
}: GenerateChannelsProps) {
const { mentions, notifications, activeChannel, setActiveChannel, channels } =
useMessengerContext();
return ( return (
<ChannelList> <>
{channels.map((channel) => ( {Object.values(channels)
.filter((channel) => channel.type === type)
.map((channel) => (
<Channel <Channel
key={channel.id} key={channel.id}
channel={channel} channel={channel}
isActive={channel.id === activeChannelId} isActive={channel.id === activeChannel.id}
isMuted={channel.isMuted || false} isMuted={channel.isMuted || false}
notification={ notification={
notifications[channel.id] > 0 && !channel.isMuted notifications[channel.id] > 0 && !channel.isMuted
@ -66,6 +51,29 @@ export function Channels({
}} }}
/> />
))} ))}
</>
);
}
export function Channels({ setCreateChat, onCommunityClick }: ChannelsProps) {
const { clearNotifications, clearMentions, notifications, activeChannel } =
useMessengerContext();
useEffect(() => {
if (activeChannel) {
if (notifications[activeChannel.id] > 0) {
clearNotifications(activeChannel.id);
clearMentions(activeChannel.id);
}
}
}, [notifications, activeChannel]);
return (
<ChannelList>
<GenerateChannels
type={"channel"}
onCommunityClick={onCommunityClick}
setCreateChat={setCreateChat}
/>
<Chats> <Chats>
<ChatsBar> <ChatsBar>
@ -75,56 +83,16 @@ export function Channels({
</EditBtn> </EditBtn>
</ChatsBar> </ChatsBar>
<ChatsList> <ChatsList>
{groupList.length > 0 && <GenerateChannels
groupList.map((group) => ( type={"group"}
<Channel onCommunityClick={onCommunityClick}
key={group.join("")} setCreateChat={setCreateChat}
channel={{
id: group.join(""),
name: group.join(", "),
type: "group",
}}
isActive={group.join("") === activeChannelId}
isMuted={false}
onClick={() => {
setActiveChannel({
id: group.join(""),
name: group.join(", "),
type: "group",
});
setCreateChat(false);
if (onCommunityClick) {
onCommunityClick();
}
}}
/> />
))} <GenerateChannels
{membersList.length > 0 && type={"dm"}
membersList.map((member) => ( onCommunityClick={onCommunityClick}
<Channel setCreateChat={setCreateChat}
key={member}
channel={{
id: member,
name: member,
type: "dm",
description: "Contact",
}}
isActive={member === activeChannelId}
isMuted={false}
onClick={() => {
setActiveChannel({
id: member,
name: member.slice(0, 10),
type: "dm",
description: "Contact",
});
if (onCommunityClick) {
onCommunityClick();
}
setCreateChat(false);
}}
/> />
))}
</ChatsList> </ChatsList>
</Chats> </Chats>
</ChannelList> </ChannelList>

View File

@ -12,10 +12,18 @@ import { CommunityModal } from "./Modals/CommunityModal";
import { EditModal } from "./Modals/EditModal"; import { EditModal } from "./Modals/EditModal";
import { ProfileModal } from "./Modals/ProfileModal"; import { ProfileModal } from "./Modals/ProfileModal";
function Modals() {
return (
<>
<CommunityModal subtitle="Public Community" />
<EditModal />
<ProfileModal />
</>
);
}
export function Chat() { export function Chat() {
const [showMembers, setShowMembers] = useState(true); const [showMembers, setShowMembers] = useState(true);
const [membersList, setMembersList] = useState([]);
const [groupList, setGroupList] = useState([]);
const [createChat, setCreateChat] = useState(false); const [createChat, setCreateChat] = useState(false);
const narrow = useNarrow(); const narrow = useNarrow();
@ -25,38 +33,19 @@ export function Chat() {
{!narrow && ( {!narrow && (
<ChannelsWrapper> <ChannelsWrapper>
<StyledCommunity /> <StyledCommunity />
<Channels <Channels setCreateChat={setCreateChat} />
membersList={membersList}
groupList={groupList}
setCreateChat={setCreateChat}
/>
</ChannelsWrapper> </ChannelsWrapper>
)} )}
{!createChat && ( {!createChat && (
<ChatBody <ChatBody
onClick={() => setShowMembers(!showMembers)} onClick={() => setShowMembers(!showMembers)}
showMembers={showMembers} showMembers={showMembers}
membersList={membersList}
groupList={groupList}
setMembersList={setMembersList}
setGroupList={setGroupList}
setCreateChat={setCreateChat} setCreateChat={setCreateChat}
/> />
)} )}
{showMembers && !narrow && !createChat && ( {showMembers && !narrow && !createChat && <Members />}
<Members setMembersList={setMembersList} /> {createChat && <ChatCreation setCreateChat={setCreateChat} />}
)} <Modals />
{createChat && (
<ChatCreation
setMembersList={setMembersList}
setGroupList={setGroupList}
setCreateChat={setCreateChat}
/>
)}
<CommunityModal subtitle="Public Community" />
<EditModal />
<ProfileModal />
</ChatWrapper> </ChatWrapper>
); );
} }

View File

@ -27,30 +27,22 @@ enum ChatBodyState {
interface ChatBodyProps { interface ChatBodyProps {
onClick: () => void; onClick: () => void;
showMembers: boolean; showMembers: boolean;
membersList: string[];
groupList: [][];
setMembersList: any;
setGroupList: any;
setCreateChat: (val: boolean) => void; setCreateChat: (val: boolean) => void;
} }
export function ChatBody({ export function ChatBody({
onClick, onClick,
showMembers, showMembers,
membersList,
groupList,
setMembersList,
setGroupList,
setCreateChat, setCreateChat,
}: ChatBodyProps) { }: ChatBodyProps) {
const { messenger, activeChannel, communityData } = useMessengerContext(); const { messenger, activeChannel, communityData } = useMessengerContext();
const narrow = useNarrow(); const narrow = useNarrow();
const [showChannelMenu, setShowChannelMenu] = useState(false); const [showChannelMenu, setShowChannelMenu] = useState(false);
const [showState, setShowState] = useState<ChatBodyState>(ChatBodyState.Chat);
const [editGroup, setEditGroup] = useState(false); const [editGroup, setEditGroup] = useState(false);
const className = useMemo(() => (narrow ? "narrow" : ""), [narrow]); const className = useMemo(() => (narrow ? "narrow" : ""), [narrow]);
const [showState, setShowState] = useState<ChatBodyState>(ChatBodyState.Chat);
const switchShowState = useCallback( const switchShowState = useCallback(
(state: ChatBodyState) => { (state: ChatBodyState) => {
if (narrow) { if (narrow) {
@ -69,12 +61,7 @@ export function ChatBody({
return ( return (
<ChatBodyWrapper className={className}> <ChatBodyWrapper className={className}>
{editGroup && communityData ? ( {editGroup && communityData ? (
<ChatCreation <ChatCreation setCreateChat={setCreateChat} editGroup={editGroup} />
setMembersList={setMembersList}
setGroupList={setGroupList}
setCreateChat={setCreateChat}
editGroup={editGroup}
/>
) : ( ) : (
<ChatTopbar <ChatTopbar
className={narrow && showState !== ChatBodyState.Chat ? "narrow" : ""} className={narrow && showState !== ChatBodyState.Chat ? "narrow" : ""}
@ -125,7 +112,6 @@ export function ChatBody({
switchMemberList={() => switchShowState(ChatBodyState.Members)} switchMemberList={() => switchShowState(ChatBodyState.Members)}
setShowChannelMenu={setShowChannelMenu} setShowChannelMenu={setShowChannelMenu}
setEditGroup={setEditGroup} setEditGroup={setEditGroup}
setGroupList={setGroupList}
/> />
)} )}
</ChatTopbar> </ChatTopbar>
@ -146,8 +132,6 @@ export function ChatBody({
{showState === ChatBodyState.Channels && ( {showState === ChatBodyState.Channels && (
<NarrowChannels <NarrowChannels
setShowChannels={() => switchShowState(ChatBodyState.Channels)} setShowChannels={() => switchShowState(ChatBodyState.Channels)}
membersList={membersList}
groupList={groupList}
setCreateChat={setCreateChat} setCreateChat={setCreateChat}
/> />
)} )}
@ -156,7 +140,6 @@ export function ChatBody({
switchShowMembersList={() => switchShowMembersList={() =>
switchShowState(ChatBodyState.Members) switchShowState(ChatBodyState.Members)
} }
setMembersList={setMembersList}
/> />
)} )}
</> </>

View File

@ -10,23 +10,16 @@ import { Member } from "../Members/Member";
import { SearchBlock } from "../SearchBlock"; import { SearchBlock } from "../SearchBlock";
import { textMediumStyles } from "../Text"; import { textMediumStyles } from "../Text";
interface ChatCreationProps { interface ChatCreationProps {
setMembersList: any;
setGroupList: any;
setCreateChat: (val: boolean) => void; setCreateChat: (val: boolean) => void;
editGroup?: boolean; editGroup?: boolean;
} }
export function ChatCreation({ export function ChatCreation({ setCreateChat, editGroup }: ChatCreationProps) {
setMembersList,
setGroupList,
setCreateChat,
editGroup,
}: ChatCreationProps) {
const identity = useIdentity(); const identity = useIdentity();
const [query, setQuery] = useState(""); const [query, setQuery] = useState("");
const [styledGroup, setStyledGroup] = useState<string[]>([]); const [styledGroup, setStyledGroup] = useState<string[]>([]);
const { contacts, setActiveChannel } = useMessengerContext(); const { contacts, setChannel } = useMessengerContext();
const addMember = useCallback( const addMember = useCallback(
(member: string) => { (member: string) => {
@ -47,28 +40,17 @@ export function ChatCreation({
const createChat = (group: string[]) => { const createChat = (group: string[]) => {
group.length > 1 group.length > 1
? (setGroupList((prevGroups: string[][]) => { ? setChannel({
prevGroups.push(group);
return prevGroups;
}),
setActiveChannel({
id: group.join(""), id: group.join(""),
name: group.join(", "), name: group.join(", "),
type: "group", type: "group",
})) })
: (setMembersList((prevMembers: string[]) => { : setChannel({
if (prevMembers.find((chat) => chat === group[0])) {
return prevMembers;
} else {
return [...prevMembers, group[0]];
}
}),
setActiveChannel({
id: group[0], id: group[0],
name: group[0], name: group[0],
type: "dm", type: "dm",
description: "Contact", description: "Contact",
})); });
setCreateChat(false); setCreateChat(false);
}; };
@ -112,9 +94,7 @@ export function ChatCreation({
</InputBar> </InputBar>
<CreationBtn <CreationBtn
disabled={styledGroup.length === 0} disabled={styledGroup.length === 0}
onClick={() => onClick={() => createChat(styledGroup)}
editGroup ? setMembersList(styledGroup) : createChat(styledGroup)
}
> >
Confirm Confirm
</CreationBtn> </CreationBtn>

View File

@ -20,7 +20,6 @@ interface ChannelMenuProps {
switchMemberList: () => void; switchMemberList: () => void;
setShowChannelMenu: (val: boolean) => void; setShowChannelMenu: (val: boolean) => void;
setEditGroup: (val: boolean) => void; setEditGroup: (val: boolean) => void;
setGroupList: any;
} }
export const ChannelMenu = ({ export const ChannelMenu = ({
@ -28,11 +27,9 @@ export const ChannelMenu = ({
switchMemberList, switchMemberList,
setShowChannelMenu, setShowChannelMenu,
setEditGroup, setEditGroup,
setGroupList,
}: ChannelMenuProps) => { }: ChannelMenuProps) => {
const narrow = useNarrow(); const narrow = useNarrow();
const { clearNotifications, setActiveChannel, channels } = const { clearNotifications, removeChannel } = useMessengerContext();
useMessengerContext();
const { setModal } = useModal(EditModalName); const { setModal } = useModal(EditModalName);
return ( return (
<DropdownMenu> <DropdownMenu>
@ -77,16 +74,12 @@ export const ChannelMenu = ({
<CheckSvg width={16} height={16} /> <CheckSvg width={16} height={16} />
<MenuText>Mark as Read</MenuText> <MenuText>Mark as Read</MenuText>
</MenuItem> </MenuItem>
{channel.type === "group" && ( {(channel.type === "group" || channel.type === "dm") && (
<MenuSection> <MenuSection>
{" "} {" "}
<MenuItem <MenuItem
onClick={() => { onClick={() => {
setGroupList((prevGroups: string[][]) => { removeChannel(channel.id);
const idx = prevGroups.indexOf(channel.name.split(", "));
return idx >= 0 ? prevGroups.splice(idx, 1) : [];
});
setActiveChannel(channels[0]);
setShowChannelMenu(false); setShowChannelMenu(false);
}} }}
> >

View File

@ -1,6 +1,7 @@
import React, { useState } from "react"; import React, { useState } from "react";
import styled from "styled-components"; import styled from "styled-components";
import { useMessengerContext } from "../../contexts/messengerProvider";
import { Contact } from "../../models/Contact"; import { Contact } from "../../models/Contact";
import { Icon } from "../Chat/ChatMessages"; import { Icon } from "../Chat/ChatMessages";
import { ContactMenu } from "../Form/ContactMenu"; import { ContactMenu } from "../Form/ContactMenu";
@ -10,7 +11,6 @@ interface MemberProps {
contact: Contact; contact: Contact;
isOnline?: boolean; isOnline?: boolean;
switchShowMembers?: () => void; switchShowMembers?: () => void;
setMembersList?: any;
onClick?: () => void; onClick?: () => void;
} }
@ -18,24 +18,21 @@ export function Member({
contact, contact,
isOnline, isOnline,
switchShowMembers, switchShowMembers,
setMembersList,
onClick, onClick,
}: MemberProps) { }: MemberProps) {
const startDialog = (member: string) => { const { setChannel } = useMessengerContext();
setMembersList((prevMembers: string[]) => {
if (prevMembers.find((chat) => chat === member)) {
return prevMembers;
} else {
return [...prevMembers, member];
}
});
};
const [showMenu, setShowMenu] = useState(false); const [showMenu, setShowMenu] = useState(false);
const onMemberClick = () => { const onMemberClick = () => {
switchShowMembers?.(); switchShowMembers?.();
startDialog(contact.id); setChannel({
id: contact.id,
name: contact.customName ?? contact.trueName,
type: "dm",
description: "DM",
members: [contact],
});
}; };
return ( return (

View File

@ -3,15 +3,11 @@ import styled from "styled-components";
import { MembersList } from "./MembersList"; import { MembersList } from "./MembersList";
interface MembersProps { export function Members() {
setMembersList: any;
}
export function Members({ setMembersList }: MembersProps) {
return ( return (
<MembersWrapper> <MembersWrapper>
<MemberHeading>Members</MemberHeading> <MemberHeading>Members</MemberHeading>
<MembersList setMembersList={setMembersList} /> <MembersList />
</MembersWrapper> </MembersWrapper>
); );
} }

View File

@ -11,13 +11,9 @@ import { Member, MemberData, MemberIcon } from "./Member";
interface MembersListProps { interface MembersListProps {
switchShowMembers?: () => void; switchShowMembers?: () => void;
setMembersList: any;
} }
export function MembersList({ export function MembersList({ switchShowMembers }: MembersListProps) {
switchShowMembers,
setMembersList,
}: MembersListProps) {
const { contacts } = useMessengerContext(); const { contacts } = useMessengerContext();
const identity = useIdentity(); const identity = useIdentity();
@ -43,7 +39,6 @@ export function MembersList({
contact={contact} contact={contact}
isOnline={contact.online} isOnline={contact.online}
switchShowMembers={switchShowMembers} switchShowMembers={switchShowMembers}
setMembersList={setMembersList}
/> />
))} ))}
</MemberCategory> </MemberCategory>
@ -58,7 +53,6 @@ export function MembersList({
contact={contact} contact={contact}
isOnline={contact.online} isOnline={contact.online}
switchShowMembers={switchShowMembers} switchShowMembers={switchShowMembers}
setMembersList={setMembersList}
/> />
))} ))}
</MemberCategory> </MemberCategory>

View File

@ -89,7 +89,7 @@ export const ProfileModal = () => {
<NameInputWrapper> <NameInputWrapper>
<NameInput <NameInput
placeholder="Only you will see this nickname" placeholder="Only you will see this nickname"
value={contact.customName} value={customNameInput}
onChange={(e) => setCustomNameInput(e.currentTarget.value)} onChange={(e) => setCustomNameInput(e.currentTarget.value)}
/> />
{contact.customName && ( {contact.customName && (

View File

@ -7,15 +7,11 @@ import { NarrowTopbar } from "./NarrowTopbar";
interface NarrowChannelsProps { interface NarrowChannelsProps {
setShowChannels: (val: boolean) => void; setShowChannels: (val: boolean) => void;
membersList: string[];
groupList: string[][];
setCreateChat: (val: boolean) => void; setCreateChat: (val: boolean) => void;
} }
export function NarrowChannels({ export function NarrowChannels({
setShowChannels, setShowChannels,
membersList,
groupList,
setCreateChat, setCreateChat,
}: NarrowChannelsProps) { }: NarrowChannelsProps) {
return ( return (
@ -23,8 +19,6 @@ export function NarrowChannels({
<NarrowTopbar list="Channels" /> <NarrowTopbar list="Channels" />
<Channels <Channels
onCommunityClick={() => setShowChannels(false)} onCommunityClick={() => setShowChannels(false)}
membersList={membersList}
groupList={groupList}
setCreateChat={setCreateChat} setCreateChat={setCreateChat}
/> />
</ListWrapper> </ListWrapper>

View File

@ -7,20 +7,13 @@ import { NarrowTopbar } from "./NarrowTopbar";
interface NarrowMembersProps { interface NarrowMembersProps {
switchShowMembersList: () => void; switchShowMembersList: () => void;
setMembersList: any;
} }
export function NarrowMembers({ export function NarrowMembers({ switchShowMembersList }: NarrowMembersProps) {
switchShowMembersList,
setMembersList,
}: NarrowMembersProps) {
return ( return (
<ListWrapper> <ListWrapper>
<NarrowTopbar list="Community members" /> <NarrowTopbar list="Community members" />
<MembersList <MembersList switchShowMembers={switchShowMembersList} />
switchShowMembers={switchShowMembersList}
setMembersList={setMembersList}
/>
</ListWrapper> </ListWrapper>
); );
} }

View File

@ -20,7 +20,9 @@ const MessengerContext = createContext<MessengerType>({
id: "", id: "",
name: "", name: "",
}, },
channels: [], channels: {},
setChannel: () => undefined,
removeChannel: () => undefined,
setActiveChannel: () => undefined, setActiveChannel: () => undefined,
}); });

View File

@ -7,7 +7,7 @@ import {
Messenger, Messenger,
} from "status-communities/dist/cjs"; } from "status-communities/dist/cjs";
import { ChannelData } from "../../models/ChannelData"; import { ChannelData, ChannelsData } from "../../models/ChannelData";
import { ChatMessage } from "../../models/ChatMessage"; import { ChatMessage } from "../../models/ChatMessage";
import { CommunityData } from "../../models/CommunityData"; import { CommunityData } from "../../models/CommunityData";
import { Contacts } from "../../models/Contact"; import { Contacts } from "../../models/Contact";
@ -34,7 +34,9 @@ export type MessengerType = {
communityData: CommunityData | undefined; communityData: CommunityData | undefined;
contacts: Contacts; contacts: Contacts;
setContacts: React.Dispatch<React.SetStateAction<Contacts>>; setContacts: React.Dispatch<React.SetStateAction<Contacts>>;
channels: ChannelData[]; channels: ChannelsData;
setChannel: (channel: ChannelData) => void;
removeChannel: (channelId: string) => void;
activeChannel: ChannelData; activeChannel: ChannelData;
setActiveChannel: (channel: ChannelData) => void; setActiveChannel: (channel: ChannelData) => void;
}; };
@ -148,24 +150,56 @@ export function useMessenger(
[chatId, messenger] [chatId, messenger]
); );
const channels = useMemo<ChannelData[]>(() => { const [channels, setChannels] = useState<ChannelsData>({});
const setChannel = useCallback((channel: ChannelData) => {
setChannels((prev) => {
return { ...prev, [channel.id]: channel };
});
setActiveChannel(channel);
}, []);
const removeChannel = useCallback(
(channelId: string) => {
setChannels((prev) => {
delete prev[channelId];
return prev;
});
const newActiveChannel: ChannelData = Object.values(channels)?.[0] ?? {
id: "",
name: "",
};
setActiveChannel(newActiveChannel);
},
[channels]
);
useEffect(() => {
if (community?.chats) { if (community?.chats) {
return Array.from(community.chats.values()).map((chat) => { for (const chat of community.chats.values()) {
return { setChannel({
id: chat.id, id: chat.id,
name: chat.communityChat?.identity?.displayName ?? "", name: chat.communityChat?.identity?.displayName ?? "",
description: chat.communityChat?.identity?.description ?? "", description: chat.communityChat?.identity?.description ?? "",
type: "channel", type: "channel",
};
}); });
} else { }
return [];
} }
}, [community]); }, [community]);
useEffect(() => { useEffect(() => {
if (channels.length > 0) setActiveChannel(channels[0]); Object.values(channels)
}, [channels]); .filter((channel) => channel.type === "dm")
.forEach((channel) => {
const contact = contacts?.[channel?.members?.[0]?.id ?? ""];
if (contact && channel.name !== (contact?.customName ?? channel.name)) {
setChannel({
...channel,
name: contact?.customName ?? channel.name,
});
}
});
}, [contacts]);
const communityData = useMemo(() => { const communityData = useMemo(() => {
if (community?.description) { if (community?.description) {
@ -200,6 +234,8 @@ export function useMessenger(
contacts, contacts,
setContacts, setContacts,
channels, channels,
setChannel,
removeChannel,
activeChannel, activeChannel,
setActiveChannel, setActiveChannel,
mentions, mentions,

View File

@ -1,3 +1,5 @@
import { Contact } from "./Contact";
export type ChannelData = { export type ChannelData = {
id: string; id: string;
name: string; name: string;
@ -5,4 +7,9 @@ export type ChannelData = {
description?: string; description?: string;
icon?: string; icon?: string;
isMuted?: boolean; isMuted?: boolean;
members?: Contact[];
};
export type ChannelsData = {
[id: string]: ChannelData;
}; };