Cleanup loading chat body (#178)

This commit is contained in:
Szymon Szlachtowicz 2022-01-10 10:17:22 +01:00 committed by GitHub
parent 97532cc88d
commit c170f81054
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 146 additions and 96 deletions

View File

@ -20,7 +20,7 @@ function RenderChannelName({
case "group": case "group":
return ( return (
<div className={className}> <div className={className}>
<GroupIcon activeView={channel.id === activeChannel.id} /> <GroupIcon activeView={channel.id === activeChannel?.id} />
{` ${channel.name}`} {` ${channel.name}`}
</div> </div>
); );

View File

@ -29,7 +29,7 @@ function GenerateChannels({ type, onCommunityClick }: GenerateChannelsProps) {
<Channel <Channel
key={channel.id} key={channel.id}
channel={channel} channel={channel}
isActive={channel.id === activeChannel.id} isActive={channel.id === activeChannel?.id}
notified={notifications?.[channel.id] > 0} notified={notifications?.[channel.id] > 0}
mention={mentions?.[channel.id]} mention={mentions?.[channel.id]}
onClick={() => { onClick={() => {

View File

@ -1,10 +1,10 @@
import React, { useCallback, useEffect, useMemo, useState } from "react"; import React, { useCallback, useEffect, useMemo, useState } from "react";
import styled from "styled-components"; import styled from "styled-components";
import { useIdentity } from "../../contexts/identityProvider";
import { useMessengerContext } from "../../contexts/messengerProvider"; import { useMessengerContext } from "../../contexts/messengerProvider";
import { useNarrow } from "../../contexts/narrowProvider"; import { useNarrow } from "../../contexts/narrowProvider";
import { Reply } from "../../hooks/useReply"; import { Reply } from "../../hooks/useReply";
import { ChannelData } from "../../models/ChannelData";
import { TokenRequirement } from "../Form/TokenRequirement"; import { TokenRequirement } from "../Form/TokenRequirement";
import { MessagesList } from "../Messages/MessagesList"; import { MessagesList } from "../Messages/MessagesList";
import { NarrowChannels } from "../NarrowMode/NarrowChannels"; import { NarrowChannels } from "../NarrowMode/NarrowChannels";
@ -13,7 +13,7 @@ import { LoadingSkeleton } from "../Skeleton/LoadingSkeleton";
import { ChatCreation } from "./ChatCreation"; import { ChatCreation } from "./ChatCreation";
import { ChatInput } from "./ChatInput"; import { ChatInput } from "./ChatInput";
import { ChatTopbar } from "./ChatTopbar"; import { ChatTopbar, ChatTopbarLoading } from "./ChatTopbar";
export enum ChatBodyState { export enum ChatBodyState {
Chat, Chat,
@ -21,6 +21,55 @@ export enum ChatBodyState {
Members, Members,
} }
function ChatBodyLoading() {
const narrow = useNarrow();
return (
<Wrapper>
<ChatBodyWrapper className={narrow ? "narrow" : ""}>
<ChatTopbarLoading />
<LoadingSkeleton />
<ChatInput reply={undefined} setReply={() => undefined} />
</ChatBodyWrapper>
</Wrapper>
);
}
type ChatBodyContentProps = {
showState: ChatBodyState;
switchShowState: (state: ChatBodyState) => void;
channel: ChannelData;
};
function ChatBodyContent({
showState,
switchShowState,
channel,
}: ChatBodyContentProps) {
const [reply, setReply] = useState<Reply | undefined>(undefined);
switch (showState) {
case ChatBodyState.Chat:
return (
<>
<MessagesList setReply={setReply} channel={channel} />
<ChatInput reply={reply} setReply={setReply} />
</>
);
case ChatBodyState.Channels:
return (
<NarrowChannels
setShowChannels={() => switchShowState(ChatBodyState.Channels)}
/>
);
case ChatBodyState.Members:
return (
<NarrowMembers
switchShowMembersList={() => switchShowState(ChatBodyState.Members)}
/>
);
}
}
interface ChatBodyProps { interface ChatBodyProps {
onClick: () => void; onClick: () => void;
showMembers: boolean; showMembers: boolean;
@ -29,9 +78,8 @@ interface ChatBodyProps {
export function ChatBody({ onClick, showMembers, permission }: ChatBodyProps) { export function ChatBody({ onClick, showMembers, permission }: ChatBodyProps) {
const { messenger, activeChannel, communityData } = useMessengerContext(); const { messenger, activeChannel, communityData } = useMessengerContext();
const narrow = useNarrow();
const identity = useIdentity();
const [editGroup, setEditGroup] = useState(false); const [editGroup, setEditGroup] = useState(false);
const narrow = useNarrow();
const className = useMemo(() => (narrow ? "narrow" : ""), [narrow]); const className = useMemo(() => (narrow ? "narrow" : ""), [narrow]);
const [showState, setShowState] = useState<ChatBodyState>(ChatBodyState.Chat); const [showState, setShowState] = useState<ChatBodyState>(ChatBodyState.Chat);
@ -50,70 +98,40 @@ export function ChatBody({ onClick, showMembers, permission }: ChatBodyProps) {
} }
}, [narrow]); }, [narrow]);
const [reply, setReply] = useState<Reply | undefined>(undefined); if (messenger && communityData && activeChannel) {
return (
return ( <Wrapper>
<Wrapper> <ChatBodyWrapper className={className}>
<ChatBodyWrapper className={className}> {editGroup && communityData ? (
{editGroup && communityData ? ( <ChatCreation
<ChatCreation setEditGroup={setEditGroup}
setEditGroup={setEditGroup} activeChannel={activeChannel}
activeChannel={activeChannel} />
/> ) : (
) : ( <ChatTopbar
<ChatTopbar onClick={onClick}
className={className} setEditGroup={setEditGroup}
onClick={onClick} showMembers={showMembers}
setEditGroup={setEditGroup} showState={showState}
showMembers={showMembers} switchShowState={switchShowState}
/>
)}
<ChatBodyContent
showState={showState} showState={showState}
switchShowState={switchShowState} switchShowState={switchShowState}
channel={activeChannel}
/> />
</ChatBodyWrapper>
{!permission && (
<BluredWrapper>
<TokenRequirement />
</BluredWrapper>
)} )}
{messenger ? ( </Wrapper>
<> );
{showState === ChatBodyState.Chat && ( }
<>
{messenger && communityData ? (
<MessagesList setReply={setReply} />
) : (
<LoadingSkeleton />
)}
<ChatInput
reply={reply}
setReply={setReply}
disabled={!identity}
/>
</>
)}
{showState === ChatBodyState.Channels && ( return <ChatBodyLoading />;
<NarrowChannels
setShowChannels={() => switchShowState(ChatBodyState.Channels)}
/>
)}
{showState === ChatBodyState.Members && (
<NarrowMembers
switchShowMembersList={() =>
switchShowState(ChatBodyState.Members)
}
/>
)}
</>
) : (
<>
<LoadingSkeleton />
<ChatInput reply={reply} setReply={setReply} disabled={!identity} />
</>
)}
</ChatBodyWrapper>
{!permission && (
<BluredWrapper>
<TokenRequirement />
</BluredWrapper>
)}
</Wrapper>
);
} }
const Wrapper = styled.div` const Wrapper = styled.div`

View File

@ -7,6 +7,7 @@ import React, {
} from "react"; } from "react";
import styled from "styled-components"; import styled from "styled-components";
import { useIdentity } from "../../contexts/identityProvider";
import { useMessengerContext } from "../../contexts/messengerProvider"; import { useMessengerContext } from "../../contexts/messengerProvider";
import { useModal } from "../../contexts/modalProvider"; import { useModal } from "../../contexts/modalProvider";
import { Reply } from "../../hooks/useReply"; import { Reply } from "../../hooks/useReply";
@ -27,10 +28,11 @@ import { EmojiPicker } from "./EmojiPicker";
interface ChatInputProps { interface ChatInputProps {
reply: Reply | undefined; reply: Reply | undefined;
setReply: (val: Reply | undefined) => void; setReply: (val: Reply | undefined) => void;
disabled: boolean;
} }
export function ChatInput({ reply, setReply, disabled }: ChatInputProps) { export function ChatInput({ reply, setReply }: ChatInputProps) {
const identity = useIdentity();
const disabled = useMemo(() => !identity, [identity]);
const { sendMessage, contacts } = useMessengerContext(); const { sendMessage, contacts } = useMessengerContext();
const [content, setContent] = useState(""); const [content, setContent] = useState("");
const [clearComponent, setClearComponent] = useState(""); const [clearComponent, setClearComponent] = useState("");

View File

@ -16,9 +16,38 @@ import { Loading } from "../Skeleton/Loading";
import { ChatBodyState } from "./ChatBody"; import { ChatBodyState } from "./ChatBody";
export function ChatTopbarLoading() {
const narrow = useNarrow();
return (
<Topbar className={narrow ? "narrow" : ""}>
<ChannelWrapper className={narrow ? "narrow" : ""}>
<SkeletonWrapper>
<CommunitySkeleton />
</SkeletonWrapper>
</ChannelWrapper>
<MenuWrapper>
{!narrow && (
<TopBtn>
<MembersIcon />
</TopBtn>
)}
<TopBtn>
<MoreIcon />
</TopBtn>
<ActivityWrapper>
<TopBtn>
<ActivityIcon />
</TopBtn>
</ActivityWrapper>
</MenuWrapper>
<Loading />
</Topbar>
);
}
type ChatTopbarProps = { type ChatTopbarProps = {
showState: ChatBodyState; showState: ChatBodyState;
className: string;
onClick: () => void; onClick: () => void;
switchShowState: (state: ChatBodyState) => void; switchShowState: (state: ChatBodyState) => void;
showMembers: boolean; showMembers: boolean;
@ -27,7 +56,6 @@ type ChatTopbarProps = {
export function ChatTopbar({ export function ChatTopbar({
showState, showState,
className,
onClick, onClick,
switchShowState, switchShowState,
showMembers, showMembers,
@ -39,15 +67,19 @@ export function ChatTopbar({
const [showChannelMenu, setShowChannelMenu] = useState(false); const [showChannelMenu, setShowChannelMenu] = useState(false);
const [showActivityCenter, setShowActivityCenter] = useState(false); const [showActivityCenter, setShowActivityCenter] = useState(false);
if (!activeChannel) {
return <ChatTopbarLoading />;
}
return ( return (
<Topbar <Topbar
className={narrow && showState !== ChatBodyState.Chat ? "narrow" : ""} className={narrow && showState !== ChatBodyState.Chat ? "narrow" : ""}
> >
<ChannelWrapper className={className}> <ChannelWrapper className={narrow ? "narrow" : ""}>
{messenger && communityData ? ( {messenger && communityData ? (
<> <>
{narrow && ( {narrow && (
<CommunityWrap className={className}> <CommunityWrap className={narrow ? "narrow" : ""}>
<Community /> <Community />
</CommunityWrap> </CommunityWrap>
)} )}

View File

@ -5,6 +5,7 @@ import { useMessengerContext } from "../../contexts/messengerProvider";
import { useModal } from "../../contexts/modalProvider"; import { useModal } from "../../contexts/modalProvider";
import { useChatScrollHandle } from "../../hooks/useChatScrollHandle"; import { useChatScrollHandle } from "../../hooks/useChatScrollHandle";
import { Reply } from "../../hooks/useReply"; import { Reply } from "../../hooks/useReply";
import { ChannelData } from "../../models/ChannelData";
import { EmptyChannel } from "../Channels/EmptyChannel"; import { EmptyChannel } from "../Channels/EmptyChannel";
import { LoadingIcon } from "../Icons/LoadingIcon"; import { LoadingIcon } from "../Icons/LoadingIcon";
import { LinkModal, LinkModalName } from "../Modals/LinkModal"; import { LinkModal, LinkModalName } from "../Modals/LinkModal";
@ -14,12 +15,13 @@ import { UiMessage } from "./UiMessage";
interface MessagesListProps { interface MessagesListProps {
setReply: (val: Reply | undefined) => void; setReply: (val: Reply | undefined) => void;
channel: ChannelData;
} }
export function MessagesList({ setReply }: MessagesListProps) { export function MessagesList({ setReply, channel }: MessagesListProps) {
const { messages, activeChannel, contacts } = useMessengerContext(); const { messages, contacts } = useMessengerContext();
const ref = useRef<HTMLHeadingElement>(null); const ref = useRef<HTMLHeadingElement>(null);
const loadingMessages = useChatScrollHandle(messages, ref, activeChannel); const loadingMessages = useChatScrollHandle(messages, ref);
const shownMessages = useMemo( const shownMessages = useMemo(
() => () =>
@ -50,7 +52,7 @@ export function MessagesList({ setReply }: MessagesListProps) {
<MessagesWrapper ref={ref}> <MessagesWrapper ref={ref}>
<PictureModal image={image} /> <PictureModal image={image} />
<LinkModal link={link} /> <LinkModal link={link} />
<EmptyChannel channel={activeChannel} /> <EmptyChannel channel={channel} />
{loadingMessages && ( {loadingMessages && (
<LoadingWrapper> <LoadingWrapper>
<LoadingIcon className="message" /> <LoadingIcon className="message" />
@ -60,7 +62,7 @@ export function MessagesList({ setReply }: MessagesListProps) {
<UiMessage <UiMessage
key={message.id} key={message.id}
message={message} message={message}
channel={activeChannel} channel={channel}
idx={idx} idx={idx}
prevMessage={shownMessages[idx - 1]} prevMessage={shownMessages[idx - 1]}
setLink={setLink} setLink={setLink}

View File

@ -34,9 +34,11 @@ export const EditModal = () => {
const { setModal } = useModal(EditModalName); const { setModal } = useModal(EditModalName);
const handleUpload = () => { const handleUpload = () => {
activeChannel.icon = image; if (activeChannel) {
changeGroupChatName(groupName, activeChannel.id); activeChannel.icon = image;
setModal(false); changeGroupChatName(groupName, activeChannel.id);
setModal(false);
}
}; };
return ( return (
@ -62,10 +64,10 @@ export const EditModal = () => {
</NameSection> </NameSection>
<LogoSection> <LogoSection>
<Label>Group image</Label> <Label>Group image</Label>
<GroupLogo icon={image || activeChannel.icon}> <GroupLogo icon={image || activeChannel?.icon}>
{!activeChannel.icon && {!activeChannel?.icon &&
!image && !image &&
activeChannel.name.slice(0, 1).toUpperCase()} activeChannel?.name?.slice(0, 1)?.toUpperCase()}
<AddPictureInputWrapper> <AddPictureInputWrapper>
<AddIcon /> <AddIcon />
<AddPictureInput <AddPictureInput

View File

@ -17,11 +17,7 @@ const MessengerContext = createContext<MessengerType>({
communityData: undefined, communityData: undefined,
contacts: {}, contacts: {},
setContacts: () => undefined, setContacts: () => undefined,
activeChannel: { activeChannel: undefined,
id: "",
name: "",
type: "channel",
},
channels: {}, channels: {},
setChannel: () => undefined, setChannel: () => undefined,
removeChannel: () => undefined, removeChannel: () => undefined,

View File

@ -42,7 +42,7 @@ export type MessengerType = {
channels: ChannelsData; channels: ChannelsData;
setChannel: (channel: ChannelData) => void; setChannel: (channel: ChannelData) => void;
removeChannel: (channelId: string) => void; removeChannel: (channelId: string) => void;
activeChannel: ChannelData; activeChannel: ChannelData | undefined;
setActiveChannel: (channel: ChannelData) => void; setActiveChannel: (channel: ChannelData) => void;
createGroupChat: (members: string[]) => void; createGroupChat: (members: string[]) => void;
changeGroupChatName: (name: string, chatId: string) => void; changeGroupChatName: (name: string, chatId: string) => void;

View File

@ -1,15 +1,13 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useMessengerContext } from "../contexts/messengerProvider"; import { useMessengerContext } from "../contexts/messengerProvider";
import { ChannelData } from "../models/ChannelData";
import { ChatMessage } from "../models/ChatMessage"; import { ChatMessage } from "../models/ChatMessage";
export function useChatScrollHandle( export function useChatScrollHandle(
messages: ChatMessage[], messages: ChatMessage[],
ref: React.RefObject<HTMLHeadingElement>, ref: React.RefObject<HTMLHeadingElement>
activeChannel: ChannelData
) { ) {
const { loadPrevDay, loadingMessages } = useMessengerContext(); const { loadPrevDay, loadingMessages, activeChannel } = useMessengerContext();
const [scrollOnBot, setScrollOnBot] = useState(true); const [scrollOnBot, setScrollOnBot] = useState(true);
useEffect(() => { useEffect(() => {
@ -19,7 +17,7 @@ export function useChatScrollHandle(
}, [messages.length, scrollOnBot]); }, [messages.length, scrollOnBot]);
useEffect(() => { useEffect(() => {
if (!loadingMessages) { if (!loadingMessages && activeChannel) {
if ( if (
(ref?.current?.clientHeight ?? 0) >= (ref?.current?.scrollHeight ?? 0) (ref?.current?.clientHeight ?? 0) >= (ref?.current?.scrollHeight ?? 0)
) { ) {
@ -27,11 +25,11 @@ export function useChatScrollHandle(
loadPrevDay(activeChannel.id, activeChannel.type === "group"); loadPrevDay(activeChannel.id, activeChannel.type === "group");
} }
} }
}, [messages.length, loadingMessages]); }, [messages.length, loadingMessages, activeChannel]);
useEffect(() => { useEffect(() => {
const setScroll = () => { const setScroll = () => {
if (ref?.current) { if (ref?.current && activeChannel) {
if (ref.current.scrollTop <= 0) { if (ref.current.scrollTop <= 0) {
loadPrevDay(activeChannel.id, activeChannel.type === "group"); loadPrevDay(activeChannel.id, activeChannel.type === "group");
} }
@ -51,6 +49,6 @@ export function useChatScrollHandle(
}; };
ref.current?.addEventListener("scroll", setScroll); ref.current?.addEventListener("scroll", setScroll);
return () => ref.current?.removeEventListener("scroll", setScroll); return () => ref.current?.removeEventListener("scroll", setScroll);
}, [ref, scrollOnBot]); }, [ref, scrollOnBot, activeChannel]);
return loadingMessages; return loadingMessages;
} }