Refactor and add replies to group chats (#149)
This commit is contained in:
parent
8b79cf397e
commit
44da36dfdf
|
@ -4,22 +4,16 @@ import styled from "styled-components";
|
||||||
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 { Channel } from "../Channels/Channel";
|
|
||||||
import { Community } from "../Community";
|
|
||||||
import { ChannelMenu } from "../Form/ChannelMenu";
|
|
||||||
import { MembersIcon } from "../Icons/MembersIcon";
|
|
||||||
import { MoreIcon } from "../Icons/MoreIcon";
|
|
||||||
import { NarrowChannels } from "../NarrowMode/NarrowChannels";
|
import { NarrowChannels } from "../NarrowMode/NarrowChannels";
|
||||||
import { NarrowMembers } from "../NarrowMode/NarrowMembers";
|
import { NarrowMembers } from "../NarrowMode/NarrowMembers";
|
||||||
import { CommunitySkeleton } from "../Skeleton/CommunitySkeleton";
|
|
||||||
import { Loading } from "../Skeleton/Loading";
|
|
||||||
import { LoadingSkeleton } from "../Skeleton/LoadingSkeleton";
|
import { LoadingSkeleton } from "../Skeleton/LoadingSkeleton";
|
||||||
|
|
||||||
import { ChatCreation } from "./ChatCreation";
|
import { ChatCreation } from "./ChatCreation";
|
||||||
import { ChatInput } from "./ChatInput";
|
import { ChatInput } from "./ChatInput";
|
||||||
import { ChatMessages } from "./ChatMessages";
|
import { ChatMessages } from "./ChatMessages";
|
||||||
|
import { ChatTopbar } from "./ChatTopbar";
|
||||||
|
|
||||||
enum ChatBodyState {
|
export enum ChatBodyState {
|
||||||
Chat,
|
Chat,
|
||||||
Channels,
|
Channels,
|
||||||
Members,
|
Members,
|
||||||
|
@ -33,7 +27,6 @@ interface ChatBodyProps {
|
||||||
export function ChatBody({ onClick, showMembers }: ChatBodyProps) {
|
export function ChatBody({ onClick, showMembers }: ChatBodyProps) {
|
||||||
const { messenger, activeChannel, communityData } = useMessengerContext();
|
const { messenger, activeChannel, communityData } = useMessengerContext();
|
||||||
const narrow = useNarrow();
|
const narrow = useNarrow();
|
||||||
const [showChannelMenu, setShowChannelMenu] = useState(false);
|
|
||||||
|
|
||||||
const [editGroup, setEditGroup] = useState(false);
|
const [editGroup, setEditGroup] = useState(false);
|
||||||
const className = useMemo(() => (narrow ? "narrow" : ""), [narrow]);
|
const className = useMemo(() => (narrow ? "narrow" : ""), [narrow]);
|
||||||
|
@ -65,56 +58,13 @@ export function ChatBody({ onClick, showMembers }: ChatBodyProps) {
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<ChatTopbar
|
<ChatTopbar
|
||||||
className={narrow && showState !== ChatBodyState.Chat ? "narrow" : ""}
|
className={className}
|
||||||
>
|
onClick={onClick}
|
||||||
<ChannelWrapper className={className}>
|
setEditGroup={setEditGroup}
|
||||||
{messenger && communityData ? (
|
showMembers={showMembers}
|
||||||
<>
|
showState={showState}
|
||||||
{narrow && (
|
switchShowState={switchShowState}
|
||||||
<CommunityWrap className={className}>
|
/>
|
||||||
<Community />
|
|
||||||
</CommunityWrap>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Channel
|
|
||||||
channel={activeChannel}
|
|
||||||
isActive={
|
|
||||||
narrow ? showState === ChatBodyState.Channels : false
|
|
||||||
}
|
|
||||||
activeView={true}
|
|
||||||
onClick={() => switchShowState(ChatBodyState.Channels)}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<SkeletonWrapper>
|
|
||||||
<CommunitySkeleton />
|
|
||||||
</SkeletonWrapper>
|
|
||||||
)}
|
|
||||||
</ChannelWrapper>
|
|
||||||
|
|
||||||
<MenuWrapper>
|
|
||||||
{!narrow && (
|
|
||||||
<MemberBtn
|
|
||||||
onClick={onClick}
|
|
||||||
className={showMembers ? "active" : ""}
|
|
||||||
>
|
|
||||||
<MembersIcon />
|
|
||||||
</MemberBtn>
|
|
||||||
)}
|
|
||||||
<MoreBtn onClick={() => setShowChannelMenu(!showChannelMenu)}>
|
|
||||||
<MoreIcon />
|
|
||||||
</MoreBtn>
|
|
||||||
</MenuWrapper>
|
|
||||||
{!messenger && !communityData && <Loading />}
|
|
||||||
{showChannelMenu && (
|
|
||||||
<ChannelMenu
|
|
||||||
channel={activeChannel}
|
|
||||||
switchMemberList={() => switchShowState(ChatBodyState.Members)}
|
|
||||||
setShowChannelMenu={setShowChannelMenu}
|
|
||||||
setEditGroup={setEditGroup}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</ChatTopbar>
|
|
||||||
)}
|
)}
|
||||||
{messenger ? (
|
{messenger ? (
|
||||||
<>
|
<>
|
||||||
|
@ -164,91 +114,3 @@ const ChatBodyWrapper = styled.div`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const ChannelWrapper = styled.div`
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
max-width: 85%;
|
|
||||||
|
|
||||||
&.narrow {
|
|
||||||
width: calc(100% - 46px);
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const SkeletonWrapper = styled.div`
|
|
||||||
padding: 8px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const ChatTopbar = styled.div`
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 5px 8px;
|
|
||||||
background: ${({ theme }) => theme.bodyBackgroundColor};
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&.narrow {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const CommunityWrap = styled.div`
|
|
||||||
padding-right: 10px;
|
|
||||||
margin-right: 16px;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&.narrow {
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
top: 50%;
|
|
||||||
width: 2px;
|
|
||||||
height: 24px;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
border-radius: 1px;
|
|
||||||
background: ${({ theme }) => theme.primary};
|
|
||||||
opacity: 0.1;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const MenuWrapper = styled.div`
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const MemberBtn = styled.button`
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 0;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: ${({ theme }) => theme.border};
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active,
|
|
||||||
&.active {
|
|
||||||
background: ${({ theme }) => theme.inputColor};
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const MoreBtn = styled.button`
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0 8px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: ${({ theme }) => theme.border};
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active,
|
|
||||||
&.active {
|
|
||||||
background: ${({ theme }) => theme.inputColor};
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
|
@ -79,7 +79,7 @@ export function ChatCreation({
|
||||||
{query && (
|
{query && (
|
||||||
<SearchBlock
|
<SearchBlock
|
||||||
query={query}
|
query={query}
|
||||||
dsicludeList={styledGroup}
|
discludeList={styledGroup}
|
||||||
onClick={addMember}
|
onClick={addMember}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -284,7 +284,7 @@ export function ChatInput({ reply, setReply }: ChatInputProps) {
|
||||||
{query && (
|
{query && (
|
||||||
<SearchBlock
|
<SearchBlock
|
||||||
query={query}
|
query={query}
|
||||||
dsicludeList={[]}
|
discludeList={[]}
|
||||||
onClick={addMention}
|
onClick={addMention}
|
||||||
onBotttom
|
onBotttom
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -0,0 +1,173 @@
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import styled from "styled-components";
|
||||||
|
|
||||||
|
import { useMessengerContext } from "../../contexts/messengerProvider";
|
||||||
|
import { useNarrow } from "../../contexts/narrowProvider";
|
||||||
|
import { Channel } from "../Channels/Channel";
|
||||||
|
import { Community } from "../Community";
|
||||||
|
import { ChannelMenu } from "../Form/ChannelMenu";
|
||||||
|
import { MembersIcon } from "../Icons/MembersIcon";
|
||||||
|
import { MoreIcon } from "../Icons/MoreIcon";
|
||||||
|
import { CommunitySkeleton } from "../Skeleton/CommunitySkeleton";
|
||||||
|
import { Loading } from "../Skeleton/Loading";
|
||||||
|
|
||||||
|
import { ChatBodyState } from "./ChatBody";
|
||||||
|
|
||||||
|
type ChatTopbarProps = {
|
||||||
|
showState: ChatBodyState;
|
||||||
|
className: string;
|
||||||
|
onClick: () => void;
|
||||||
|
switchShowState: (state: ChatBodyState) => void;
|
||||||
|
showMembers: boolean;
|
||||||
|
setEditGroup: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function ChatTopbar({
|
||||||
|
showState,
|
||||||
|
className,
|
||||||
|
onClick,
|
||||||
|
switchShowState,
|
||||||
|
showMembers,
|
||||||
|
setEditGroup,
|
||||||
|
}: ChatTopbarProps) {
|
||||||
|
const { messenger, activeChannel, communityData } = useMessengerContext();
|
||||||
|
const narrow = useNarrow();
|
||||||
|
const [showChannelMenu, setShowChannelMenu] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Topbar
|
||||||
|
className={narrow && showState !== ChatBodyState.Chat ? "narrow" : ""}
|
||||||
|
>
|
||||||
|
<ChannelWrapper className={className}>
|
||||||
|
{messenger && communityData ? (
|
||||||
|
<>
|
||||||
|
{narrow && (
|
||||||
|
<CommunityWrap className={className}>
|
||||||
|
<Community />
|
||||||
|
</CommunityWrap>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Channel
|
||||||
|
channel={activeChannel}
|
||||||
|
isActive={narrow ? showState === ChatBodyState.Channels : false}
|
||||||
|
activeView={true}
|
||||||
|
onClick={() => switchShowState(ChatBodyState.Channels)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<SkeletonWrapper>
|
||||||
|
<CommunitySkeleton />
|
||||||
|
</SkeletonWrapper>
|
||||||
|
)}
|
||||||
|
</ChannelWrapper>
|
||||||
|
|
||||||
|
<MenuWrapper>
|
||||||
|
{!narrow && (
|
||||||
|
<MemberBtn onClick={onClick} className={showMembers ? "active" : ""}>
|
||||||
|
<MembersIcon />
|
||||||
|
</MemberBtn>
|
||||||
|
)}
|
||||||
|
<MoreBtn onClick={() => setShowChannelMenu(!showChannelMenu)}>
|
||||||
|
<MoreIcon />
|
||||||
|
</MoreBtn>
|
||||||
|
</MenuWrapper>
|
||||||
|
{!messenger && !communityData && <Loading />}
|
||||||
|
{showChannelMenu && (
|
||||||
|
<ChannelMenu
|
||||||
|
channel={activeChannel}
|
||||||
|
switchMemberList={() => switchShowState(ChatBodyState.Members)}
|
||||||
|
setShowChannelMenu={setShowChannelMenu}
|
||||||
|
setEditGroup={setEditGroup}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Topbar>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ChannelWrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
max-width: 85%;
|
||||||
|
|
||||||
|
&.narrow {
|
||||||
|
width: calc(100% - 46px);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const SkeletonWrapper = styled.div`
|
||||||
|
padding: 8px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Topbar = styled.div`
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 5px 8px;
|
||||||
|
background: ${({ theme }) => theme.bodyBackgroundColor};
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&.narrow {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const CommunityWrap = styled.div`
|
||||||
|
padding-right: 10px;
|
||||||
|
margin-right: 16px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&.narrow {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 50%;
|
||||||
|
width: 2px;
|
||||||
|
height: 24px;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
border-radius: 1px;
|
||||||
|
background: ${({ theme }) => theme.primary};
|
||||||
|
opacity: 0.1;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const MenuWrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const MemberBtn = styled.button`
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: ${({ theme }) => theme.border};
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active,
|
||||||
|
&.active {
|
||||||
|
background: ${({ theme }) => theme.inputColor};
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const MoreBtn = styled.button`
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0 8px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: ${({ theme }) => theme.border};
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active,
|
||||||
|
&.active {
|
||||||
|
background: ${({ theme }) => theme.inputColor};
|
||||||
|
}
|
||||||
|
`;
|
|
@ -8,14 +8,14 @@ import { Member } from "./Members/Member";
|
||||||
|
|
||||||
interface SearchBlockProps {
|
interface SearchBlockProps {
|
||||||
query: string;
|
query: string;
|
||||||
dsicludeList: string[];
|
discludeList: string[];
|
||||||
onClick: (member: string) => void;
|
onClick: (member: string) => void;
|
||||||
onBotttom?: boolean;
|
onBotttom?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SearchBlock = ({
|
export const SearchBlock = ({
|
||||||
query,
|
query,
|
||||||
dsicludeList,
|
discludeList,
|
||||||
onClick,
|
onClick,
|
||||||
onBotttom,
|
onBotttom,
|
||||||
}: SearchBlockProps) => {
|
}: SearchBlockProps) => {
|
||||||
|
@ -24,8 +24,8 @@ export const SearchBlock = ({
|
||||||
const searchList = useMemo(() => {
|
const searchList = useMemo(() => {
|
||||||
return Object.values(contacts)
|
return Object.values(contacts)
|
||||||
.filter((member) => member.id.includes(query))
|
.filter((member) => member.id.includes(query))
|
||||||
.filter((member) => !dsicludeList.includes(member.id));
|
.filter((member) => !discludeList.includes(member.id));
|
||||||
}, [query, dsicludeList, contacts]);
|
}, [query, discludeList, contacts]);
|
||||||
|
|
||||||
if (searchList.length === 0) {
|
if (searchList.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -37,7 +37,7 @@ export const SearchBlock = ({
|
||||||
<ContactsList>
|
<ContactsList>
|
||||||
{Object.values(contacts)
|
{Object.values(contacts)
|
||||||
.filter((member) => member.id.includes(query))
|
.filter((member) => member.id.includes(query))
|
||||||
.filter((member) => !dsicludeList.includes(member.id))
|
.filter((member) => !discludeList.includes(member.id))
|
||||||
.map((member) => (
|
.map((member) => (
|
||||||
<Member
|
<Member
|
||||||
key={member.id}
|
key={member.id}
|
||||||
|
|
|
@ -65,7 +65,8 @@ export function useGroupChats(
|
||||||
msg.text ?? "",
|
msg.text ?? "",
|
||||||
new Date(msg.clock ?? 0),
|
new Date(msg.clock ?? 0),
|
||||||
sender,
|
sender,
|
||||||
image
|
image,
|
||||||
|
msg.responseTo
|
||||||
),
|
),
|
||||||
msg.chatId
|
msg.chatId
|
||||||
);
|
);
|
||||||
|
|
|
@ -217,7 +217,7 @@ export function useMessenger(
|
||||||
}
|
}
|
||||||
if (content) {
|
if (content) {
|
||||||
if (activeChannel.type === "group") {
|
if (activeChannel.type === "group") {
|
||||||
await groupChat?.sendMessage(activeChannel.id, content);
|
await groupChat?.sendMessage(activeChannel.id, content, responseTo);
|
||||||
} else {
|
} else {
|
||||||
await messenger?.sendMessage(activeChannel.id, content, responseTo);
|
await messenger?.sendMessage(activeChannel.id, content, responseTo);
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,11 @@ export class GroupChats {
|
||||||
*
|
*
|
||||||
* @param text text message to send
|
* @param text text message to send
|
||||||
*/
|
*/
|
||||||
public async sendMessage(chatId: string, content: Content): Promise<void> {
|
public async sendMessage(
|
||||||
|
chatId: string,
|
||||||
|
content: Content,
|
||||||
|
responseTo?: string
|
||||||
|
): Promise<void> {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const chat = this.chats[chatId];
|
const chat = this.chats[chatId];
|
||||||
if (chat) {
|
if (chat) {
|
||||||
|
@ -78,7 +82,8 @@ export class GroupChats {
|
||||||
now,
|
now,
|
||||||
now,
|
now,
|
||||||
chatId,
|
chatId,
|
||||||
content
|
content,
|
||||||
|
responseTo
|
||||||
);
|
);
|
||||||
const wakuMessage = await WakuMessage.fromBytes(
|
const wakuMessage = await WakuMessage.fromBytes(
|
||||||
chatMessage.encode(),
|
chatMessage.encode(),
|
||||||
|
|
Loading…
Reference in New Issue