Refactor channels to use reducer (#207)
This commit is contained in:
parent
9cfba1f50c
commit
fc2d65f202
|
@ -47,10 +47,11 @@ export function ActivityMessage({
|
|||
activity,
|
||||
setShowActivityCenter,
|
||||
}: ActivityMessageProps) {
|
||||
const { contacts, setActiveChannel } = useMessengerContext();
|
||||
const { contacts, channelsDispatch } = useMessengerContext();
|
||||
const { setModal } = useModal(ProfileModalName);
|
||||
const showChannel = () => {
|
||||
activity.channel && setActiveChannel(activity.channel),
|
||||
activity.channel &&
|
||||
channelsDispatch({ type: "ChangeActive", payload: activity.channel.id }),
|
||||
setShowActivityCenter(false);
|
||||
};
|
||||
|
||||
|
|
|
@ -59,6 +59,7 @@ export function Channel({
|
|||
setEditGroup,
|
||||
}: ChannelProps) {
|
||||
const narrow = useNarrow();
|
||||
const { channelsDispatch } = useMessengerContext();
|
||||
|
||||
return (
|
||||
<ChannelWrapper
|
||||
|
@ -79,7 +80,11 @@ export function Channel({
|
|||
notified={notified}
|
||||
/>
|
||||
{channel?.isMuted && activeView && !narrow && (
|
||||
<MutedBtn onClick={() => (channel.isMuted = !channel.isMuted)}>
|
||||
<MutedBtn
|
||||
onClick={() =>
|
||||
channelsDispatch({ type: "ToggleMuted", payload: channel.id })
|
||||
}
|
||||
>
|
||||
<MutedIcon />
|
||||
<Tooltip tip="Unmute" className="muted" />
|
||||
</MutedBtn>
|
||||
|
@ -90,7 +95,7 @@ export function Channel({
|
|||
)}
|
||||
</ChannelTextInfo>
|
||||
</ChannelInfo>
|
||||
{!activeView && !!mention && mention > 0 && !channel?.isMuted && (
|
||||
{!activeView && !!mention && !channel?.isMuted && (
|
||||
<NotificationBagde>{mention}</NotificationBagde>
|
||||
)}
|
||||
{channel?.isMuted && !activeView && <MutedIcon />}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React from "react";
|
||||
import React, { useMemo } from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { ChatState, useChatState } from "../../contexts/chatStateProvider";
|
||||
|
@ -23,12 +23,15 @@ function GenerateChannels({
|
|||
onCommunityClick,
|
||||
setEditGroup,
|
||||
}: GenerateChannelsProps) {
|
||||
const { mentions, notifications, activeChannel, setActiveChannel, channels } =
|
||||
const { mentions, notifications, activeChannel, channelsDispatch, channels } =
|
||||
useMessengerContext();
|
||||
|
||||
const channelList = useMemo(() => Object.values(channels), [channels]);
|
||||
|
||||
const setChatState = useChatState()[1];
|
||||
return (
|
||||
<>
|
||||
{Object.values(channels)
|
||||
{channelList
|
||||
.filter((channel) => channel.type === type)
|
||||
.map((channel) => (
|
||||
<Channel
|
||||
|
@ -38,7 +41,7 @@ function GenerateChannels({
|
|||
notified={notifications?.[channel.id] > 0}
|
||||
mention={mentions?.[channel.id]}
|
||||
onClick={() => {
|
||||
setActiveChannel(channel);
|
||||
channelsDispatch({ type: "ChangeActive", payload: channel.id });
|
||||
if (onCommunityClick) {
|
||||
onCommunityClick();
|
||||
}
|
||||
|
|
|
@ -34,8 +34,7 @@ export function ChatCreation({
|
|||
(member) => member?.customName ?? member.trueName
|
||||
) ?? []
|
||||
);
|
||||
const { contacts, createGroupChat, addMembers, setChannel } =
|
||||
useMessengerContext();
|
||||
const { contacts, createGroupChat, addMembers } = useMessengerContext();
|
||||
const setChatState = useChatState()[1];
|
||||
|
||||
const addMember = useCallback(
|
||||
|
@ -71,7 +70,7 @@ export function ChatCreation({
|
|||
setChatState(ChatState.ChatBody);
|
||||
}
|
||||
},
|
||||
[identity, createGroupChat, setChannel]
|
||||
[identity, createGroupChat]
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import React, { useMemo, useState } from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { useMessengerContext } from "../../contexts/messengerProvider";
|
||||
|
@ -41,20 +41,12 @@ export const ChannelMenu = ({
|
|||
className,
|
||||
}: ChannelMenuProps) => {
|
||||
const narrow = useNarrow();
|
||||
const { clearNotifications } = useMessengerContext();
|
||||
const { clearNotifications, channelsDispatch } = useMessengerContext();
|
||||
const { setModal } = useModal(EditModalName);
|
||||
const { setModal: setLeavingModal } = useModal(LeavingModalName);
|
||||
const { setModal: setProfileModal } = useModal(ProfileModalName);
|
||||
const [showSubmenu, setShowSubmenu] = useState(false);
|
||||
|
||||
const muting = channel.isMuted;
|
||||
const [isMuted, setIsMuted] = useState(muting);
|
||||
|
||||
useEffect(() => {
|
||||
if (isMuted) channel.isMuted = true;
|
||||
if (!isMuted) channel.isMuted = false;
|
||||
}, [isMuted]);
|
||||
|
||||
const { showMenu, setShowMenu: setShowSideMenu } = useContextMenu(
|
||||
channel.id + "contextMenu"
|
||||
);
|
||||
|
@ -113,26 +105,31 @@ export const ChannelMenu = ({
|
|||
<MenuSection className={`${channel.type === "channel" && "channel"}`}>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
if (isMuted) {
|
||||
setIsMuted(false);
|
||||
if (channel.isMuted) {
|
||||
channelsDispatch({ type: "ToggleMuted", payload: channel.id });
|
||||
setShowMenu(false);
|
||||
}
|
||||
}}
|
||||
onMouseEnter={() => {
|
||||
if (!isMuted) setShowSubmenu(true);
|
||||
if (!channel.isMuted) setShowSubmenu(true);
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
if (!isMuted) setShowSubmenu(false);
|
||||
if (!channel.isMuted) setShowSubmenu(false);
|
||||
}}
|
||||
>
|
||||
<MuteIcon width={16} height={16} />
|
||||
{!isMuted && <NextIcon />}
|
||||
{!channel.isMuted && <NextIcon />}
|
||||
<MenuText>
|
||||
{(isMuted ? "Unmute" : "Mute") +
|
||||
{(channel.isMuted ? "Unmute" : "Mute") +
|
||||
(channel.type === "group" ? " Group" : " Chat")}
|
||||
</MenuText>
|
||||
{!isMuted && showSubmenu && (
|
||||
<MuteMenu setIsMuted={setIsMuted} className={className} />
|
||||
{!channel.isMuted && showSubmenu && (
|
||||
<MuteMenu
|
||||
setIsMuted={() =>
|
||||
channelsDispatch({ type: "ToggleMuted", payload: channel.id })
|
||||
}
|
||||
className={className}
|
||||
/>
|
||||
)}
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => clearNotifications(channel.id)}>
|
||||
|
|
|
@ -20,9 +20,8 @@ const MessengerContext = createContext<MessengerType>({
|
|||
setContacts: () => undefined,
|
||||
activeChannel: undefined,
|
||||
channels: {},
|
||||
setChannel: () => undefined,
|
||||
channelsDispatch: () => undefined,
|
||||
removeChannel: () => undefined,
|
||||
setActiveChannel: () => undefined,
|
||||
createGroupChat: () => undefined,
|
||||
changeGroupChatName: () => undefined,
|
||||
addMembers: () => undefined,
|
||||
|
|
|
@ -7,11 +7,13 @@ import {
|
|||
} from "@waku/status-communities/dist/cjs";
|
||||
import { useCallback, useMemo } from "react";
|
||||
|
||||
import { ChannelData, ChannelsData } from "../../models/ChannelData";
|
||||
import { ChannelData } from "../../models/ChannelData";
|
||||
import { ChatMessage } from "../../models/ChatMessage";
|
||||
import { Contact } from "../../models/Contact";
|
||||
import { uintToImgUrl } from "../../utils";
|
||||
|
||||
import { ChannelAction } from "./useMessenger";
|
||||
|
||||
const contactFromId = (member: string): Contact => {
|
||||
return {
|
||||
blocked: false,
|
||||
|
@ -25,10 +27,8 @@ const contactFromId = (member: string): Contact => {
|
|||
export function useGroupChats(
|
||||
messenger: Messenger | undefined,
|
||||
identity: Identity | undefined,
|
||||
setChannels: React.Dispatch<React.SetStateAction<ChannelsData>>,
|
||||
setActiveChannel: (channel: ChannelData) => void,
|
||||
addChatMessage: (newMessage: ChatMessage | undefined, id: string) => void,
|
||||
channels: ChannelsData
|
||||
dispatch: (action: ChannelAction) => void,
|
||||
addChatMessage: (newMessage: ChatMessage | undefined, id: string) => void
|
||||
) {
|
||||
const groupChat = useMemo(() => {
|
||||
if (messenger && identity) {
|
||||
|
@ -51,20 +51,10 @@ export function useGroupChats(
|
|||
type: "dm",
|
||||
description: `Chatkey: ${chat.members[0].id}`,
|
||||
};
|
||||
setChannels((prev) => {
|
||||
return { ...prev, [channel.id]: channel };
|
||||
});
|
||||
dispatch({ type: "AddChannel", payload: channel });
|
||||
};
|
||||
const removeChat = (chat: GroupChat) => {
|
||||
setChannels((prev) => {
|
||||
delete prev[chat.chatId];
|
||||
return prev;
|
||||
});
|
||||
setActiveChannel({
|
||||
id: "",
|
||||
name: "",
|
||||
type: "channel",
|
||||
} as ChannelData);
|
||||
dispatch({ type: "RemoveChannel", payload: chat.chatId });
|
||||
};
|
||||
const handleMessage = (msg: StatusChatMessage, sender: string) => {
|
||||
let image: string | undefined = undefined;
|
||||
|
@ -116,7 +106,7 @@ export function useGroupChats(
|
|||
groupChat.quitChat(channelId);
|
||||
}
|
||||
},
|
||||
[channels, groupChat]
|
||||
[groupChat]
|
||||
);
|
||||
|
||||
const addMembers = useCallback(
|
||||
|
|
|
@ -6,7 +6,7 @@ import {
|
|||
Identity,
|
||||
Messenger,
|
||||
} from "@waku/status-communities/dist/cjs";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { useCallback, useEffect, useMemo, useReducer, useState } from "react";
|
||||
|
||||
import { useConfig } from "../../contexts/configProvider";
|
||||
import { ChannelData, ChannelsData } from "../../models/ChannelData";
|
||||
|
@ -41,10 +41,9 @@ export type MessengerType = {
|
|||
contacts: Contacts;
|
||||
setContacts: React.Dispatch<React.SetStateAction<Contacts>>;
|
||||
channels: ChannelsData;
|
||||
setChannel: (channel: ChannelData) => void;
|
||||
channelsDispatch: (action: ChannelAction) => void;
|
||||
removeChannel: (channelId: string) => void;
|
||||
activeChannel: ChannelData | undefined;
|
||||
setActiveChannel: (channel: ChannelData) => void;
|
||||
createGroupChat: (members: string[]) => void;
|
||||
changeGroupChatName: (name: string, chatId: string) => void;
|
||||
addMembers: (members: string[], chatId: string) => void;
|
||||
|
@ -101,17 +100,84 @@ function useCreateCommunity(
|
|||
return { community, communityData };
|
||||
}
|
||||
|
||||
export type ChannelsState = {
|
||||
channels: ChannelsData;
|
||||
activeChannel: ChannelData;
|
||||
};
|
||||
|
||||
export type ChannelAction =
|
||||
| { type: "AddChannel"; payload: ChannelData }
|
||||
| { type: "UpdateActive"; payload: ChannelData }
|
||||
| { type: "ChangeActive"; payload: string }
|
||||
| { type: "ToggleMuted"; payload: string }
|
||||
| { type: "RemoveChannel"; payload: string };
|
||||
|
||||
function channelReducer(
|
||||
state: ChannelsState,
|
||||
action: ChannelAction
|
||||
): ChannelsState {
|
||||
switch (action.type) {
|
||||
case "AddChannel": {
|
||||
const channels = {
|
||||
...state.channels,
|
||||
[action.payload.id]: action.payload,
|
||||
};
|
||||
return { channels, activeChannel: action.payload };
|
||||
}
|
||||
case "UpdateActive": {
|
||||
const activeChannel = state.activeChannel;
|
||||
if (activeChannel) {
|
||||
return {
|
||||
channels: { ...state.channels, [activeChannel.id]: action.payload },
|
||||
activeChannel: action.payload,
|
||||
};
|
||||
}
|
||||
return state;
|
||||
}
|
||||
case "ChangeActive": {
|
||||
const newActive = state.channels[action.payload];
|
||||
if (newActive) {
|
||||
return { ...state, activeChannel: newActive };
|
||||
}
|
||||
return state;
|
||||
}
|
||||
case "ToggleMuted": {
|
||||
const channel = state.channels[action.payload];
|
||||
if (channel) {
|
||||
const updatedChannel: ChannelData = {
|
||||
...channel,
|
||||
isMuted: !channel.isMuted,
|
||||
};
|
||||
return {
|
||||
channels: { ...state.channels, [channel.id]: updatedChannel },
|
||||
activeChannel: updatedChannel,
|
||||
};
|
||||
}
|
||||
return state;
|
||||
}
|
||||
case "RemoveChannel": {
|
||||
const channelsCopy = { ...state.channels };
|
||||
delete channelsCopy[action.payload];
|
||||
let newActive = { id: "", name: "", type: "channel" } as ChannelData;
|
||||
if (Object.values(channelsCopy).length > 0) {
|
||||
newActive = Object.values(channelsCopy)[0];
|
||||
}
|
||||
return { channels: channelsCopy, activeChannel: newActive };
|
||||
}
|
||||
default:
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
export function useMessenger(
|
||||
communityKey: string,
|
||||
identity: Identity | undefined,
|
||||
newNickname: string | undefined
|
||||
) {
|
||||
const [activeChannel, setActiveChannel] = useState<ChannelData>({
|
||||
id: "",
|
||||
name: "",
|
||||
description: "",
|
||||
type: "channel",
|
||||
});
|
||||
const [channelsState, channelsDispatch] = useReducer(channelReducer, {
|
||||
channels: {},
|
||||
activeChannel: { id: "", name: "", type: "channel" },
|
||||
} as ChannelsState);
|
||||
|
||||
const messenger = useCreateMessenger(identity);
|
||||
|
||||
|
@ -129,7 +195,7 @@ export function useMessenger(
|
|||
messages,
|
||||
mentions,
|
||||
clearMentions,
|
||||
} = useMessages(activeChannel.id, identity, contactsClass);
|
||||
} = useMessages(channelsState?.activeChannel?.id, identity, contactsClass);
|
||||
|
||||
const { community, communityData } = useCreateCommunity(
|
||||
messenger,
|
||||
|
@ -138,37 +204,34 @@ export function useMessenger(
|
|||
contactsClass
|
||||
);
|
||||
|
||||
const [channels, setChannels] = useState<ChannelsData>({});
|
||||
|
||||
const setChannel = useCallback((channel: ChannelData) => {
|
||||
setChannels((prev) => {
|
||||
return { ...prev, [channel.id]: channel };
|
||||
});
|
||||
setActiveChannel(channel);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (community?.chats) {
|
||||
for (const chat of community.chats.values()) {
|
||||
setChannel({
|
||||
channelsDispatch({
|
||||
type: "AddChannel",
|
||||
payload: {
|
||||
id: chat.id,
|
||||
name: chat.communityChat?.identity?.displayName ?? "",
|
||||
description: chat.communityChat?.identity?.description ?? "",
|
||||
type: "channel",
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [community]);
|
||||
|
||||
useEffect(() => {
|
||||
Object.values(channels)
|
||||
Object.values(channelsState.channels)
|
||||
.filter((channel) => channel.type === "dm")
|
||||
.forEach((channel) => {
|
||||
const contact = contacts?.[channel?.members?.[0]?.id ?? ""];
|
||||
if (contact && channel.name !== (contact?.customName ?? channel.name)) {
|
||||
setChannel({
|
||||
channelsDispatch({
|
||||
type: "AddChannel",
|
||||
payload: {
|
||||
...channel,
|
||||
name: contact?.customName ?? channel.name,
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -180,17 +243,10 @@ export function useMessenger(
|
|||
createGroupChat,
|
||||
changeGroupChatName,
|
||||
addMembers,
|
||||
} = useGroupChats(
|
||||
messenger,
|
||||
identity,
|
||||
setChannels,
|
||||
setActiveChannel,
|
||||
addChatMessage,
|
||||
channels
|
||||
);
|
||||
} = useGroupChats(messenger, identity, channelsDispatch, addChatMessage);
|
||||
|
||||
const { loadPrevDay, loadingMessages } = useLoadPrevDay(
|
||||
activeChannel.id,
|
||||
channelsState.activeChannel.id,
|
||||
messenger,
|
||||
groupChat
|
||||
);
|
||||
|
@ -220,28 +276,36 @@ export function useMessenger(
|
|||
};
|
||||
}
|
||||
if (content) {
|
||||
if (activeChannel.type === "group") {
|
||||
await groupChat?.sendMessage(activeChannel.id, content, responseTo);
|
||||
if (channelsState.activeChannel.type === "group") {
|
||||
await groupChat?.sendMessage(
|
||||
channelsState.activeChannel.id,
|
||||
content,
|
||||
responseTo
|
||||
);
|
||||
} else {
|
||||
await messenger?.sendMessage(activeChannel.id, content, responseTo);
|
||||
await messenger?.sendMessage(
|
||||
channelsState.activeChannel.id,
|
||||
content,
|
||||
responseTo
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
[messenger, groupChat, activeChannel]
|
||||
[messenger, groupChat, channelsState.activeChannel]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeChannel) {
|
||||
if (notifications[activeChannel.id] > 0) {
|
||||
clearNotifications(activeChannel.id);
|
||||
clearMentions(activeChannel.id);
|
||||
if (channelsState.activeChannel) {
|
||||
if (notifications[channelsState.activeChannel.id] > 0) {
|
||||
clearNotifications(channelsState.activeChannel.id);
|
||||
clearMentions(channelsState.activeChannel.id);
|
||||
}
|
||||
}
|
||||
}, [notifications, activeChannel]);
|
||||
}, [notifications, channelsState]);
|
||||
|
||||
const loadingMessenger = useMemo(() => {
|
||||
return !communityData || !messenger || !activeChannel;
|
||||
}, [communityData, messenger, activeChannel]);
|
||||
return !communityData || !messenger || !channelsState.activeChannel.id;
|
||||
}, [communityData, messenger, channelsState]);
|
||||
|
||||
return {
|
||||
messenger,
|
||||
|
@ -255,11 +319,10 @@ export function useMessenger(
|
|||
communityData,
|
||||
contacts,
|
||||
setContacts,
|
||||
channels,
|
||||
setChannel,
|
||||
channels: channelsState.channels,
|
||||
channelsDispatch,
|
||||
removeChannel,
|
||||
activeChannel,
|
||||
setActiveChannel,
|
||||
activeChannel: channelsState.activeChannel,
|
||||
mentions,
|
||||
clearMentions,
|
||||
createGroupChat,
|
||||
|
|
|
@ -287,12 +287,9 @@ export class GroupChats {
|
|||
messages.sort((a, b) =>
|
||||
(a?.timestamp?.getTime() ?? 0) < (b?.timestamp?.getTime() ?? 0) ? -1 : 1
|
||||
);
|
||||
|
||||
await Promise.all(
|
||||
messages.map(
|
||||
async (message) => await this.decodeUpdateMessage(message, false)
|
||||
)
|
||||
);
|
||||
for (let i = 0; i < messages.length; i++) {
|
||||
await this.decodeUpdateMessage(messages[i], false);
|
||||
}
|
||||
this.waku.relay.addObserver(
|
||||
(message) => this.decodeUpdateMessage(message, true),
|
||||
[topic]
|
||||
|
|
Loading…
Reference in New Issue