Add group chat name change handling (#143)
This commit is contained in:
parent
49417a5c25
commit
daabab46ed
|
@ -20,7 +20,7 @@ import {
|
|||
export const EditModalName = "editModal";
|
||||
|
||||
export const EditModal = () => {
|
||||
const { activeChannel } = useMessengerContext();
|
||||
const { activeChannel, changeGroupChatName } = useMessengerContext();
|
||||
const [groupName, setGroupName] = useState("");
|
||||
const [image, setImage] = useState("");
|
||||
|
||||
|
@ -34,7 +34,7 @@ export const EditModal = () => {
|
|||
|
||||
const handleUpload = () => {
|
||||
activeChannel.icon = image;
|
||||
activeChannel.name = groupName;
|
||||
changeGroupChatName(groupName, activeChannel.id);
|
||||
setModal(false);
|
||||
};
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ const MessengerContext = createContext<MessengerType>({
|
|||
removeChannel: () => undefined,
|
||||
setActiveChannel: () => undefined,
|
||||
createGroupChat: () => undefined,
|
||||
changeGroupChatName: () => undefined,
|
||||
});
|
||||
|
||||
export function useMessengerContext() {
|
||||
|
|
|
@ -34,7 +34,7 @@ export function useGroupChats(
|
|||
const members = chat.members.map(contactFromId);
|
||||
const channel: ChannelData = {
|
||||
id: chat.chatId,
|
||||
name: chat.chatId,
|
||||
name: chat.name ?? chat.chatId,
|
||||
type: "group",
|
||||
members: members,
|
||||
};
|
||||
|
@ -78,6 +78,15 @@ export function useGroupChats(
|
|||
[groupChat]
|
||||
);
|
||||
|
||||
const changeGroupChatName = useCallback(
|
||||
(name: string, chatId: string) => {
|
||||
if (groupChat) {
|
||||
groupChat.changeChatName(chatId, name);
|
||||
}
|
||||
},
|
||||
[groupChat]
|
||||
);
|
||||
|
||||
const removeChannel = useCallback(
|
||||
(channelId: string) => {
|
||||
if (groupChat) {
|
||||
|
@ -87,5 +96,5 @@ export function useGroupChats(
|
|||
[channels, groupChat]
|
||||
);
|
||||
|
||||
return { createGroupChat, removeChannel, groupChat };
|
||||
return { createGroupChat, removeChannel, groupChat, changeGroupChatName };
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ export type MessengerType = {
|
|||
activeChannel: ChannelData;
|
||||
setActiveChannel: (channel: ChannelData) => void;
|
||||
createGroupChat: (members: string[]) => void;
|
||||
changeGroupChatName: (name: string, chatId: string) => void;
|
||||
};
|
||||
|
||||
function useCreateMessenger(identity: Identity | undefined) {
|
||||
|
@ -167,14 +168,15 @@ export function useMessenger(
|
|||
});
|
||||
}, [contacts]);
|
||||
|
||||
const { groupChat, removeChannel, createGroupChat } = useGroupChats(
|
||||
messenger,
|
||||
identity,
|
||||
setChannels,
|
||||
setActiveChannel,
|
||||
addChatMessage,
|
||||
channels
|
||||
);
|
||||
const { groupChat, removeChannel, createGroupChat, changeGroupChatName } =
|
||||
useGroupChats(
|
||||
messenger,
|
||||
identity,
|
||||
setChannels,
|
||||
setActiveChannel,
|
||||
addChatMessage,
|
||||
channels
|
||||
);
|
||||
|
||||
const { loadPrevDay, loadingMessages } = useLoadPrevDay(
|
||||
activeChannel.id,
|
||||
|
@ -232,5 +234,6 @@ export function useMessenger(
|
|||
mentions,
|
||||
clearMentions,
|
||||
createGroupChat,
|
||||
changeGroupChatName,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@ import { ChatMessage, ContentType } from ".";
|
|||
export type GroupChat = {
|
||||
chatId: string;
|
||||
members: string[];
|
||||
admins?: string[];
|
||||
name?: string;
|
||||
};
|
||||
|
||||
export type GroupChatsType = {
|
||||
|
@ -91,37 +93,52 @@ export class GroupChats {
|
|||
const membershipUpdate = MembershipUpdateMessage.decode(
|
||||
message?.payload
|
||||
);
|
||||
if (membershipUpdate.events.length > 0) {
|
||||
if (
|
||||
membershipUpdate.events[0].event.type ==
|
||||
MembershipUpdateEvent_EventType.CHAT_CREATED
|
||||
) {
|
||||
await this.addChat(
|
||||
{
|
||||
chatId: membershipUpdate.chatId,
|
||||
members: membershipUpdate.events[0].event.members,
|
||||
},
|
||||
useCallback
|
||||
);
|
||||
}
|
||||
if (
|
||||
membershipUpdate.events[0].event.type ==
|
||||
MembershipUpdateEvent_EventType.MEMBER_REMOVED
|
||||
) {
|
||||
if (
|
||||
membershipUpdate.events[0].event.members[0] ==
|
||||
bufToHex(this.identity.publicKey)
|
||||
) {
|
||||
await this.removeChat(
|
||||
{
|
||||
chatId: membershipUpdate.chatId,
|
||||
members: membershipUpdate.events[0].event.members,
|
||||
},
|
||||
useCallback
|
||||
);
|
||||
await Promise.all(
|
||||
membershipUpdate.events.map(async (event) => {
|
||||
const bufSigner = event.signer;
|
||||
const signer = bufSigner ? bufToHex(bufSigner) : "";
|
||||
const chatId = membershipUpdate.chatId;
|
||||
if (signer) {
|
||||
switch (event.event.type) {
|
||||
case MembershipUpdateEvent_EventType.CHAT_CREATED: {
|
||||
await this.addChat(
|
||||
{
|
||||
chatId: chatId,
|
||||
members: event.event.members,
|
||||
admins: [signer],
|
||||
},
|
||||
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
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MembershipUpdateEvent_EventType.NAME_CHANGED: {
|
||||
const chat = this.chats[chatId];
|
||||
if (chat) {
|
||||
if (chat.admins?.includes(signer)) {
|
||||
chat.name = event.event.name;
|
||||
this.callback(chat);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
} catch {
|
||||
return;
|
||||
|
@ -207,6 +224,28 @@ export class GroupChats {
|
|||
);
|
||||
}
|
||||
|
||||
private async sendUpdateMessage(
|
||||
payload: Uint8Array,
|
||||
members: string[]
|
||||
): Promise<void> {
|
||||
const wakuMessages = await Promise.all(
|
||||
members.map(
|
||||
async (member) =>
|
||||
await WakuMessage.fromBytes(payload, getPartitionedTopic(member))
|
||||
)
|
||||
);
|
||||
wakuMessages.forEach((msg) => this.waku.relay.send(msg));
|
||||
}
|
||||
|
||||
public async changeChatName(chatId: string, name: string): Promise<void> {
|
||||
const payload = MembershipUpdateMessage.create(chatId, this.identity);
|
||||
const chat = this.chats[chatId];
|
||||
if (chat && payload) {
|
||||
payload.addNameChangeEvent(name);
|
||||
await this.sendUpdateMessage(payload.encode(), chat.members);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a create group chat membership update message with given members
|
||||
*
|
||||
|
@ -217,13 +256,7 @@ export class GroupChats {
|
|||
this.identity,
|
||||
members
|
||||
).encode();
|
||||
const wakuMessages = await Promise.all(
|
||||
members.map(
|
||||
async (member) =>
|
||||
await WakuMessage.fromBytes(payload, getPartitionedTopic(member))
|
||||
)
|
||||
);
|
||||
wakuMessages.forEach((msg) => this.waku.relay.send(msg));
|
||||
await this.sendUpdateMessage(payload, members);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -235,16 +268,7 @@ export class GroupChats {
|
|||
const payload = MembershipUpdateMessage.create(chatId, this.identity);
|
||||
const chat = this.chats[chatId];
|
||||
payload.addMemberRemovedEvent(bufToHex(this.identity.publicKey));
|
||||
const wakuMessages = await Promise.all(
|
||||
chat.members.map(
|
||||
async (member) =>
|
||||
await WakuMessage.fromBytes(
|
||||
payload.encode(),
|
||||
getPartitionedTopic(member)
|
||||
)
|
||||
)
|
||||
);
|
||||
wakuMessages.forEach((msg) => this.waku.relay.send(msg));
|
||||
await this.sendUpdateMessage(payload.encode(), chat.members);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -19,7 +19,6 @@ export class Identity {
|
|||
*/
|
||||
public sign(payload: Uint8Array): Uint8Array {
|
||||
const hash = keccak256(payload);
|
||||
|
||||
const { signature, recid } = secp256k1.ecdsaSign(
|
||||
hexToBuf(hash),
|
||||
this.privateKey
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import { keccak256 } from "js-sha3";
|
||||
import { Reader } from "protobufjs";
|
||||
import * as secp256k1 from "secp256k1";
|
||||
import { v4 as uuidV4 } from "uuid";
|
||||
|
||||
import { Identity } from "..";
|
||||
|
@ -34,6 +36,35 @@ export class MembershipUpdateEvent {
|
|||
}
|
||||
}
|
||||
|
||||
export class MembershipSignedEvent {
|
||||
public sig: Uint8Array;
|
||||
public event: MembershipUpdateEvent;
|
||||
private chatId: string;
|
||||
|
||||
public constructor(
|
||||
sig: Uint8Array,
|
||||
event: MembershipUpdateEvent,
|
||||
chatId: string
|
||||
) {
|
||||
this.sig = sig;
|
||||
this.event = event;
|
||||
this.chatId = chatId;
|
||||
}
|
||||
|
||||
public get signer(): Uint8Array | undefined {
|
||||
const encEvent = this.event.encode();
|
||||
const eventToSign = Buffer.concat([hexToBuf(this.chatId), encEvent]);
|
||||
|
||||
if (!this.sig || !eventToSign) return;
|
||||
|
||||
const signature = this.sig.slice(0, 64);
|
||||
const recid = this.sig.slice(64)[0];
|
||||
const hash = keccak256(eventToSign);
|
||||
|
||||
return secp256k1.ecdsaRecover(signature, recid, hexToBuf(hash));
|
||||
}
|
||||
}
|
||||
|
||||
export class MembershipUpdateMessage {
|
||||
private clock: number = Date.now();
|
||||
private identity: Identity = Identity.generate();
|
||||
|
@ -150,15 +181,13 @@ export class MembershipUpdateMessage {
|
|||
return new MembershipUpdateMessage(protoBuf);
|
||||
}
|
||||
|
||||
public get events(): {
|
||||
sig: Uint8Array;
|
||||
event: MembershipUpdateEvent;
|
||||
}[] {
|
||||
public get events(): MembershipSignedEvent[] {
|
||||
return this.proto.events.map((bufArray) => {
|
||||
return {
|
||||
sig: bufArray.slice(0, 65),
|
||||
event: MembershipUpdateEvent.decode(bufArray.slice(65)),
|
||||
};
|
||||
return new MembershipSignedEvent(
|
||||
bufArray.slice(0, 65),
|
||||
MembershipUpdateEvent.decode(bufArray.slice(65)),
|
||||
this.chatId
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue