diff --git a/packages/react-chat-example/src/index.tsx b/packages/react-chat-example/src/index.tsx index ec517b0..0f73f52 100644 --- a/packages/react-chat-example/src/index.tsx +++ b/packages/react-chat-example/src/index.tsx @@ -76,7 +76,7 @@ function DragDiv() { diff --git a/packages/react-chat/src/components/Chat/ChatBody.tsx b/packages/react-chat/src/components/Chat/ChatBody.tsx index b5c245c..d0c2923 100644 --- a/packages/react-chat/src/components/Chat/ChatBody.tsx +++ b/packages/react-chat/src/components/Chat/ChatBody.tsx @@ -56,7 +56,10 @@ export function ChatBody({ onClick, showMembers }: ChatBodyProps) { return ( {editGroup && communityData ? ( - + ) : ( void; + activeChannel?: ChannelData; } -export function ChatCreation({ editGroup }: ChatCreationProps) { +export function ChatCreation({ + setEditGroup, + activeChannel, +}: ChatCreationProps) { const identity = useIdentity(); const [query, setQuery] = useState(""); - const [styledGroup, setStyledGroup] = useState([]); - const { contacts, createGroupChat } = useMessengerContext(); + const [styledGroup, setStyledGroup] = useState( + activeChannel?.members?.map((member) => member?.customName ?? member.id) ?? + [] + ); + const { contacts, createGroupChat, addMembers } = useMessengerContext(); const setChatState = useChatState()[1]; const addMember = useCallback( @@ -33,7 +41,6 @@ export function ChatCreation({ editGroup }: ChatCreationProps) { }, [setStyledGroup, styledGroup] ); - const removeMember = (member: string) => { setStyledGroup((prev) => prev.filter((e) => e != member)); }; @@ -85,12 +92,19 @@ export function ChatCreation({ editGroup }: ChatCreationProps) { createChat(styledGroup)} + onClick={() => { + if (!activeChannel) { + createChat(styledGroup); + } else { + addMembers(styledGroup, activeChannel.id); + } + setEditGroup?.(false); + }} > Confirm - {!editGroup && !query && ( + {!setEditGroup && !query && ( Contacts diff --git a/packages/react-chat/src/contexts/messengerProvider.tsx b/packages/react-chat/src/contexts/messengerProvider.tsx index e4e9949..49bb1ba 100644 --- a/packages/react-chat/src/contexts/messengerProvider.tsx +++ b/packages/react-chat/src/contexts/messengerProvider.tsx @@ -27,6 +27,7 @@ const MessengerContext = createContext({ setActiveChannel: () => undefined, createGroupChat: () => undefined, changeGroupChatName: () => undefined, + addMembers: () => undefined, }); export function useMessengerContext() { diff --git a/packages/react-chat/src/hooks/messenger/useGroupChats.ts b/packages/react-chat/src/hooks/messenger/useGroupChats.ts index e4cf07a..736c804 100644 --- a/packages/react-chat/src/hooks/messenger/useGroupChats.ts +++ b/packages/react-chat/src/hooks/messenger/useGroupChats.ts @@ -96,5 +96,20 @@ export function useGroupChats( [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, + }; } diff --git a/packages/react-chat/src/hooks/messenger/useMessenger.ts b/packages/react-chat/src/hooks/messenger/useMessenger.ts index 279a6e6..62fb68d 100644 --- a/packages/react-chat/src/hooks/messenger/useMessenger.ts +++ b/packages/react-chat/src/hooks/messenger/useMessenger.ts @@ -44,6 +44,7 @@ export type MessengerType = { setActiveChannel: (channel: ChannelData) => void; createGroupChat: (members: string[]) => void; changeGroupChatName: (name: string, chatId: string) => void; + addMembers: (members: string[], chatId: string) => void; }; function useCreateMessenger(identity: Identity | undefined) { @@ -168,15 +169,20 @@ export function useMessenger( }); }, [contacts]); - const { groupChat, removeChannel, createGroupChat, changeGroupChatName } = - useGroupChats( - messenger, - identity, - setChannels, - setActiveChannel, - addChatMessage, - channels - ); + const { + groupChat, + removeChannel, + createGroupChat, + changeGroupChatName, + addMembers, + } = useGroupChats( + messenger, + identity, + setChannels, + setActiveChannel, + addChatMessage, + channels + ); const { loadPrevDay, loadingMessages } = useLoadPrevDay( activeChannel.id, @@ -235,5 +241,6 @@ export function useMessenger( clearMentions, createGroupChat, changeGroupChatName, + addMembers, }; } diff --git a/packages/status-communities/src/groupChats.ts b/packages/status-communities/src/groupChats.ts index 94cd3b0..d3ceb69 100644 --- a/packages/status-communities/src/groupChats.ts +++ b/packages/status-communities/src/groupChats.ts @@ -13,6 +13,7 @@ export type GroupChat = { members: string[]; admins?: string[]; name?: string; + removed: boolean; }; export type GroupChatsType = { @@ -95,9 +96,11 @@ export class GroupChats { ); await Promise.all( membershipUpdate.events.map(async (event) => { - const bufSigner = event.signer; - const signer = bufSigner ? bufToHex(bufSigner) : ""; + const signer = event.signer ? bufToHex(event.signer) : ""; + const thisUser = bufToHex(this.identity.publicKey); const chatId = membershipUpdate.chatId; + const chat: GroupChat | undefined = this.chats[chatId]; + if (signer) { switch (event.event.type) { case MembershipUpdateEvent_EventType.CHAT_CREATED: { @@ -106,27 +109,47 @@ export class GroupChats { chatId: chatId, members: event.event.members, admins: [signer], + removed: false, }, useCallback ); break; } case MembershipUpdateEvent_EventType.MEMBER_REMOVED: { - if ( - event.event.members[0] == bufToHex(this.identity.publicKey) - ) { - await this.removeChat( - { - chatId: chatId, - members: event.event.members, - }, - useCallback + if (chat) { + chat.members = chat.members.filter( + (member) => !event.event.members.includes(member) ); + if (event.event.members.includes(thisUser)) { + await this.removeChat( + { + ...chat, + removed: true, + }, + 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; } case MembershipUpdateEvent_EventType.NAME_CHANGED: { - const chat = this.chats[chatId]; if (chat) { if (chat.admins?.includes(signer)) { chat.name = event.event.name; @@ -181,10 +204,17 @@ export class GroupChats { } private async addChat(chat: GroupChat, useCallback: boolean): Promise { - this.chats[chat.chatId] = chat; - if (useCallback) { - await this.handleChatObserver(chat); - this.callback(chat); + if (this.chats[chat.chatId]) { + this.chats[chat.chatId] = chat; + if (useCallback) { + this.callback(chat); + } + } else { + this.chats[chat.chatId] = chat; + if (useCallback) { + await this.handleChatObserver(chat); + this.callback(chat); + } } } @@ -192,9 +222,8 @@ export class GroupChats { chat: GroupChat, useCallback: boolean ): Promise { - delete this.chats[chat.chatId]; + this.chats[chat.chatId] = chat; if (useCallback) { - await this.handleChatObserver(chat, true); this.removeCallback(chat); } } @@ -214,8 +243,10 @@ export class GroupChats { ); await Promise.all( Object.values(this.chats).map(async (chat) => { - await this.handleChatObserver(chat); - this.callback(chat); + if (!chat?.removed) { + await this.handleChatObserver(chat); + this.callback(chat); + } }) ); this.waku.relay.addObserver( @@ -246,6 +277,21 @@ export class GroupChats { } } + public async addMembers(chatId: string, members: string[]): Promise { + 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 *