Add name and member handling in group chats (#144)

This commit is contained in:
Szymon Szlachtowicz 2021-12-03 11:51:55 +01:00 committed by GitHub
parent daabab46ed
commit ff733a15fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 125 additions and 39 deletions

View File

@ -76,7 +76,7 @@ function DragDiv() {
<ReactChat <ReactChat
theme={theme ? lightTheme : darkTheme} theme={theme ? lightTheme : darkTheme}
communityKey={ communityKey={
"0x0235564b1b402e8f6ad540f4da0f6384fb828b8f7bb5a34942aa03719041b8b793" "0x02516353a03fc3892d268a72f4c4b2b0fad179702a996a87346407139949767ba7"
} }
fetchMetadata={fetchMetadata} fetchMetadata={fetchMetadata}
/> />

View File

@ -56,7 +56,10 @@ export function ChatBody({ onClick, showMembers }: ChatBodyProps) {
return ( return (
<ChatBodyWrapper className={className}> <ChatBodyWrapper className={className}>
{editGroup && communityData ? ( {editGroup && communityData ? (
<ChatCreation editGroup={editGroup} /> <ChatCreation
setEditGroup={setEditGroup}
activeChannel={activeChannel}
/>
) : ( ) : (
<ChatTopbar <ChatTopbar
className={narrow && showState !== ChatBodyState.Chat ? "narrow" : ""} className={narrow && showState !== ChatBodyState.Chat ? "narrow" : ""}

View File

@ -5,20 +5,28 @@ import styled from "styled-components";
import { ChatState, useChatState } from "../../contexts/chatStateProvider"; import { ChatState, useChatState } from "../../contexts/chatStateProvider";
import { useIdentity } from "../../contexts/identityProvider"; import { useIdentity } from "../../contexts/identityProvider";
import { useMessengerContext } from "../../contexts/messengerProvider"; import { useMessengerContext } from "../../contexts/messengerProvider";
import { ChannelData } from "../../models/ChannelData";
import { buttonStyles } from "../Buttons/buttonStyle"; import { buttonStyles } from "../Buttons/buttonStyle";
import { CrossIcon } from "../Icons/CrossIcon"; import { CrossIcon } from "../Icons/CrossIcon";
import { Member } from "../Members/Member"; 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 {
editGroup?: boolean; setEditGroup?: (val: boolean) => void;
activeChannel?: ChannelData;
} }
export function ChatCreation({ editGroup }: ChatCreationProps) { export function ChatCreation({
setEditGroup,
activeChannel,
}: 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, createGroupChat } = useMessengerContext(); activeChannel?.members?.map((member) => member?.customName ?? member.id) ??
[]
);
const { contacts, createGroupChat, addMembers } = useMessengerContext();
const setChatState = useChatState()[1]; const setChatState = useChatState()[1];
const addMember = useCallback( const addMember = useCallback(
@ -33,7 +41,6 @@ export function ChatCreation({ editGroup }: ChatCreationProps) {
}, },
[setStyledGroup, styledGroup] [setStyledGroup, styledGroup]
); );
const removeMember = (member: string) => { const removeMember = (member: string) => {
setStyledGroup((prev) => prev.filter((e) => e != member)); setStyledGroup((prev) => prev.filter((e) => e != member));
}; };
@ -85,12 +92,19 @@ export function ChatCreation({ editGroup }: ChatCreationProps) {
</InputBar> </InputBar>
<CreationBtn <CreationBtn
disabled={styledGroup.length === 0} disabled={styledGroup.length === 0}
onClick={() => createChat(styledGroup)} onClick={() => {
if (!activeChannel) {
createChat(styledGroup);
} else {
addMembers(styledGroup, activeChannel.id);
}
setEditGroup?.(false);
}}
> >
Confirm Confirm
</CreationBtn> </CreationBtn>
</CreationBar> </CreationBar>
{!editGroup && !query && ( {!setEditGroup && !query && (
<Contacts> <Contacts>
<ContactsHeading>Contacts</ContactsHeading> <ContactsHeading>Contacts</ContactsHeading>
<ContactsList> <ContactsList>

View File

@ -27,6 +27,7 @@ const MessengerContext = createContext<MessengerType>({
setActiveChannel: () => undefined, setActiveChannel: () => undefined,
createGroupChat: () => undefined, createGroupChat: () => undefined,
changeGroupChatName: () => undefined, changeGroupChatName: () => undefined,
addMembers: () => undefined,
}); });
export function useMessengerContext() { export function useMessengerContext() {

View File

@ -96,5 +96,20 @@ export function useGroupChats(
[channels, groupChat] [channels, groupChat]
); );
return { createGroupChat, removeChannel, groupChat, changeGroupChatName }; const addMembers = useCallback(
(members: string[], chatId: string) => {
if (groupChat) {
groupChat.addMembers(chatId, members);
}
},
[groupChat]
);
return {
createGroupChat,
removeChannel,
groupChat,
changeGroupChatName,
addMembers,
};
} }

View File

@ -44,6 +44,7 @@ export type MessengerType = {
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;
addMembers: (members: string[], chatId: string) => void;
}; };
function useCreateMessenger(identity: Identity | undefined) { function useCreateMessenger(identity: Identity | undefined) {
@ -168,8 +169,13 @@ export function useMessenger(
}); });
}, [contacts]); }, [contacts]);
const { groupChat, removeChannel, createGroupChat, changeGroupChatName } = const {
useGroupChats( groupChat,
removeChannel,
createGroupChat,
changeGroupChatName,
addMembers,
} = useGroupChats(
messenger, messenger,
identity, identity,
setChannels, setChannels,
@ -235,5 +241,6 @@ export function useMessenger(
clearMentions, clearMentions,
createGroupChat, createGroupChat,
changeGroupChatName, changeGroupChatName,
addMembers,
}; };
} }

View File

@ -13,6 +13,7 @@ export type GroupChat = {
members: string[]; members: string[];
admins?: string[]; admins?: string[];
name?: string; name?: string;
removed: boolean;
}; };
export type GroupChatsType = { export type GroupChatsType = {
@ -95,9 +96,11 @@ export class GroupChats {
); );
await Promise.all( await Promise.all(
membershipUpdate.events.map(async (event) => { membershipUpdate.events.map(async (event) => {
const bufSigner = event.signer; const signer = event.signer ? bufToHex(event.signer) : "";
const signer = bufSigner ? bufToHex(bufSigner) : ""; const thisUser = bufToHex(this.identity.publicKey);
const chatId = membershipUpdate.chatId; const chatId = membershipUpdate.chatId;
const chat: GroupChat | undefined = this.chats[chatId];
if (signer) { if (signer) {
switch (event.event.type) { switch (event.event.type) {
case MembershipUpdateEvent_EventType.CHAT_CREATED: { case MembershipUpdateEvent_EventType.CHAT_CREATED: {
@ -106,27 +109,47 @@ export class GroupChats {
chatId: chatId, chatId: chatId,
members: event.event.members, members: event.event.members,
admins: [signer], admins: [signer],
removed: false,
}, },
useCallback useCallback
); );
break; break;
} }
case MembershipUpdateEvent_EventType.MEMBER_REMOVED: { case MembershipUpdateEvent_EventType.MEMBER_REMOVED: {
if ( if (chat) {
event.event.members[0] == bufToHex(this.identity.publicKey) chat.members = chat.members.filter(
) { (member) => !event.event.members.includes(member)
);
if (event.event.members.includes(thisUser)) {
await this.removeChat( await this.removeChat(
{ {
chatId: chatId, ...chat,
members: event.event.members, removed: true,
}, },
useCallback useCallback
); );
} else {
if (!chat.removed && useCallback) {
this.callback(this.chats[chatId]);
}
}
}
break;
}
case MembershipUpdateEvent_EventType.MEMBERS_ADDED: {
if (chat) {
chat.members.push(...event.event.members);
if (
chat.members.includes(thisUser) &&
chat.admins?.includes(signer)
) {
chat.removed = false;
await this.addChat(chat, useCallback);
}
} }
break; break;
} }
case MembershipUpdateEvent_EventType.NAME_CHANGED: { case MembershipUpdateEvent_EventType.NAME_CHANGED: {
const chat = this.chats[chatId];
if (chat) { if (chat) {
if (chat.admins?.includes(signer)) { if (chat.admins?.includes(signer)) {
chat.name = event.event.name; chat.name = event.event.name;
@ -181,20 +204,26 @@ export class GroupChats {
} }
private async addChat(chat: GroupChat, useCallback: boolean): Promise<void> { private async addChat(chat: GroupChat, useCallback: boolean): Promise<void> {
if (this.chats[chat.chatId]) {
this.chats[chat.chatId] = chat;
if (useCallback) {
this.callback(chat);
}
} else {
this.chats[chat.chatId] = chat; this.chats[chat.chatId] = chat;
if (useCallback) { if (useCallback) {
await this.handleChatObserver(chat); await this.handleChatObserver(chat);
this.callback(chat); this.callback(chat);
} }
} }
}
private async removeChat( private async removeChat(
chat: GroupChat, chat: GroupChat,
useCallback: boolean useCallback: boolean
): Promise<void> { ): Promise<void> {
delete this.chats[chat.chatId]; this.chats[chat.chatId] = chat;
if (useCallback) { if (useCallback) {
await this.handleChatObserver(chat, true);
this.removeCallback(chat); this.removeCallback(chat);
} }
} }
@ -214,8 +243,10 @@ export class GroupChats {
); );
await Promise.all( await Promise.all(
Object.values(this.chats).map(async (chat) => { Object.values(this.chats).map(async (chat) => {
if (!chat?.removed) {
await this.handleChatObserver(chat); await this.handleChatObserver(chat);
this.callback(chat); this.callback(chat);
}
}) })
); );
this.waku.relay.addObserver( this.waku.relay.addObserver(
@ -246,6 +277,21 @@ export class GroupChats {
} }
} }
public async addMembers(chatId: string, members: string[]): Promise<void> {
const payload = MembershipUpdateMessage.create(chatId, this.identity);
const chat = this.chats[chatId];
if (chat && payload) {
const newMembers = members.filter(
(member) => !chat.members.includes(member)
);
payload.addMembersAddedEvent(newMembers);
await this.sendUpdateMessage(payload.encode(), [
...chat.members,
...newMembers,
]);
}
}
/** /**
* Sends a create group chat membership update message with given members * Sends a create group chat membership update message with given members
* *