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
theme={theme ? lightTheme : darkTheme}
communityKey={
"0x0235564b1b402e8f6ad540f4da0f6384fb828b8f7bb5a34942aa03719041b8b793"
"0x02516353a03fc3892d268a72f4c4b2b0fad179702a996a87346407139949767ba7"
}
fetchMetadata={fetchMetadata}
/>

View File

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

View File

@ -5,20 +5,28 @@ import styled from "styled-components";
import { ChatState, useChatState } from "../../contexts/chatStateProvider";
import { useIdentity } from "../../contexts/identityProvider";
import { useMessengerContext } from "../../contexts/messengerProvider";
import { ChannelData } from "../../models/ChannelData";
import { buttonStyles } from "../Buttons/buttonStyle";
import { CrossIcon } from "../Icons/CrossIcon";
import { Member } from "../Members/Member";
import { SearchBlock } from "../SearchBlock";
import { textMediumStyles } from "../Text";
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 [query, setQuery] = useState("");
const [styledGroup, setStyledGroup] = useState<string[]>([]);
const { contacts, createGroupChat } = useMessengerContext();
const [styledGroup, setStyledGroup] = useState<string[]>(
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) {
</InputBar>
<CreationBtn
disabled={styledGroup.length === 0}
onClick={() => createChat(styledGroup)}
onClick={() => {
if (!activeChannel) {
createChat(styledGroup);
} else {
addMembers(styledGroup, activeChannel.id);
}
setEditGroup?.(false);
}}
>
Confirm
</CreationBtn>
</CreationBar>
{!editGroup && !query && (
{!setEditGroup && !query && (
<Contacts>
<ContactsHeading>Contacts</ContactsHeading>
<ContactsList>

View File

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

View File

@ -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,
};
}

View File

@ -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,
};
}

View File

@ -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<void> {
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<void> {
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<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
*