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":
return (
<div className={className}>
<GroupIcon activeView={channel.id === activeChannel.id} />
<GroupIcon activeView={channel.id === activeChannel?.id} />
{` ${channel.name}`}
</div>
);

View File

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

View File

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

View File

@ -7,6 +7,7 @@ import React, {
} from "react";
import styled from "styled-components";
import { useIdentity } from "../../contexts/identityProvider";
import { useMessengerContext } from "../../contexts/messengerProvider";
import { useModal } from "../../contexts/modalProvider";
import { Reply } from "../../hooks/useReply";
@ -27,10 +28,11 @@ import { EmojiPicker } from "./EmojiPicker";
interface ChatInputProps {
reply: Reply | undefined;
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 [content, setContent] = useState("");
const [clearComponent, setClearComponent] = useState("");

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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