Fix group chats nicknames (#172)
This commit is contained in:
parent
64a67dce68
commit
f8d24eb264
|
@ -1,8 +1,9 @@
|
||||||
import React from "react";
|
import React, { useMemo } from "react";
|
||||||
import { utils } from "status-communities/dist/cjs";
|
import { utils } from "status-communities/dist/cjs";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
|
||||||
import { useIdentity } from "../../contexts/identityProvider";
|
import { useIdentity } from "../../contexts/identityProvider";
|
||||||
|
import { useMessengerContext } from "../../contexts/messengerProvider";
|
||||||
import { ChannelData } from "../../models/ChannelData";
|
import { ChannelData } from "../../models/ChannelData";
|
||||||
import { textMediumStyles } from "../Text";
|
import { textMediumStyles } from "../Text";
|
||||||
|
|
||||||
|
@ -13,9 +14,17 @@ type EmptyChannelProps = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export function EmptyChannel({ channel }: EmptyChannelProps) {
|
export function EmptyChannel({ channel }: EmptyChannelProps) {
|
||||||
const groupName = channel.name.split(", ");
|
|
||||||
const identity = useIdentity();
|
const identity = useIdentity();
|
||||||
|
const { contacts } = useMessengerContext();
|
||||||
|
const members = useMemo(() => {
|
||||||
|
if (channel?.members && identity) {
|
||||||
|
const publicKey = utils.bufToHex(identity.publicKey);
|
||||||
|
return channel.members
|
||||||
|
.filter((contact) => contact.id !== publicKey)
|
||||||
|
.map((member) => contacts?.[member.id] ?? member);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}, [channel, contacts]);
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<ChannelInfoEmpty>
|
<ChannelInfoEmpty>
|
||||||
|
@ -36,8 +45,12 @@ export function EmptyChannel({ channel }: EmptyChannelProps) {
|
||||||
<EmptyTextGroup>
|
<EmptyTextGroup>
|
||||||
{identity && <span>{utils.bufToHex(identity.publicKey)}</span>}{" "}
|
{identity && <span>{utils.bufToHex(identity.publicKey)}</span>}{" "}
|
||||||
created a group with{" "}
|
created a group with{" "}
|
||||||
<span>{groupName.slice(groupName.length - 1)}</span> and{" "}
|
{members.map((contact, idx) => (
|
||||||
<span>{groupName.at(-1)}</span>
|
<span key={contact.id}>
|
||||||
|
{contact?.customName ?? contact.trueName.slice(0, 10)}
|
||||||
|
{idx < members.length - 1 && <> and </>}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
</EmptyTextGroup>
|
</EmptyTextGroup>
|
||||||
) : (
|
) : (
|
||||||
<EmptyText>
|
<EmptyText>
|
||||||
|
|
|
@ -33,7 +33,9 @@ export function useGroupChats(
|
||||||
const groupChat = useMemo(() => {
|
const groupChat = useMemo(() => {
|
||||||
if (messenger && identity) {
|
if (messenger && identity) {
|
||||||
const addChat = (chat: GroupChat) => {
|
const addChat = (chat: GroupChat) => {
|
||||||
const members = chat.members.map(contactFromId);
|
const members = chat.members
|
||||||
|
.map((member) => member.id)
|
||||||
|
.map(contactFromId);
|
||||||
const channel: ChannelData = {
|
const channel: ChannelData = {
|
||||||
id: chat.chatId,
|
id: chat.chatId,
|
||||||
name: chat.name ?? chat.chatId,
|
name: chat.name ?? chat.chatId,
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import { Waku, WakuMessage } from "js-waku";
|
import { Waku, WakuMessage } from "js-waku";
|
||||||
|
|
||||||
|
import { createSymKeyFromPassword } from "./encryption";
|
||||||
import { Identity } from "./identity";
|
import { Identity } from "./identity";
|
||||||
import { MembershipUpdateEvent_EventType } from "./proto/communities/v1/membership_update_message";
|
import { MembershipUpdateEvent_EventType } from "./proto/communities/v1/membership_update_message";
|
||||||
import { getNegotiatedTopic, getPartitionedTopic } from "./topics";
|
import { getNegotiatedTopic, getPartitionedTopic } from "./topics";
|
||||||
import { bufToHex } from "./utils";
|
import { bufToHex, compressPublicKey } from "./utils";
|
||||||
import {
|
import {
|
||||||
MembershipSignedEvent,
|
MembershipSignedEvent,
|
||||||
MembershipUpdateMessage,
|
MembershipUpdateMessage,
|
||||||
|
@ -11,9 +12,16 @@ import {
|
||||||
|
|
||||||
import { ChatMessage, Content } from ".";
|
import { ChatMessage, Content } from ".";
|
||||||
|
|
||||||
|
type GroupMember = {
|
||||||
|
id: string;
|
||||||
|
topic: string;
|
||||||
|
symKey: Uint8Array;
|
||||||
|
partitionedTopic: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type GroupChat = {
|
export type GroupChat = {
|
||||||
chatId: string;
|
chatId: string;
|
||||||
members: string[];
|
members: GroupMember[];
|
||||||
admins?: string[];
|
admins?: string[];
|
||||||
name?: string;
|
name?: string;
|
||||||
removed: boolean;
|
removed: boolean;
|
||||||
|
@ -32,7 +40,6 @@ export class GroupChats {
|
||||||
private addMessage: (message: ChatMessage, sender: string) => void;
|
private addMessage: (message: ChatMessage, sender: string) => void;
|
||||||
|
|
||||||
public chats: GroupChatsType = {};
|
public chats: GroupChatsType = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GroupChats holds a list of private chats and listens to their status broadcast
|
* GroupChats holds a list of private chats and listens to their status broadcast
|
||||||
*
|
*
|
||||||
|
@ -87,7 +94,8 @@ export class GroupChats {
|
||||||
);
|
);
|
||||||
const wakuMessage = await WakuMessage.fromBytes(
|
const wakuMessage = await WakuMessage.fromBytes(
|
||||||
chatMessage.encode(),
|
chatMessage.encode(),
|
||||||
await getNegotiatedTopic(this.identity, member)
|
member.topic,
|
||||||
|
{ sigPrivKey: this.identity.privateKey, symKey: member.symKey }
|
||||||
);
|
);
|
||||||
this.waku.relay.send(wakuMessage);
|
this.waku.relay.send(wakuMessage);
|
||||||
})
|
})
|
||||||
|
@ -106,10 +114,19 @@ export class GroupChats {
|
||||||
if (signer) {
|
if (signer) {
|
||||||
switch (event.event.type) {
|
switch (event.event.type) {
|
||||||
case MembershipUpdateEvent_EventType.CHAT_CREATED: {
|
case MembershipUpdateEvent_EventType.CHAT_CREATED: {
|
||||||
|
const members: GroupMember[] = [];
|
||||||
|
await Promise.all(
|
||||||
|
event.event.members.map(async (member) => {
|
||||||
|
const topic = await getNegotiatedTopic(this.identity, member);
|
||||||
|
const symKey = await createSymKeyFromPassword(topic);
|
||||||
|
const partitionedTopic = getPartitionedTopic(member);
|
||||||
|
members.push({ topic, symKey, id: member, partitionedTopic });
|
||||||
|
})
|
||||||
|
);
|
||||||
await this.addChat(
|
await this.addChat(
|
||||||
{
|
{
|
||||||
chatId: chatId,
|
chatId: chatId,
|
||||||
members: event.event.members,
|
members,
|
||||||
admins: [signer],
|
admins: [signer],
|
||||||
removed: false,
|
removed: false,
|
||||||
},
|
},
|
||||||
|
@ -120,7 +137,7 @@ export class GroupChats {
|
||||||
case MembershipUpdateEvent_EventType.MEMBER_REMOVED: {
|
case MembershipUpdateEvent_EventType.MEMBER_REMOVED: {
|
||||||
if (chat) {
|
if (chat) {
|
||||||
chat.members = chat.members.filter(
|
chat.members = chat.members.filter(
|
||||||
(member) => !event.event.members.includes(member)
|
(member) => !event.event.members.includes(member.id)
|
||||||
);
|
);
|
||||||
if (event.event.members.includes(thisUser)) {
|
if (event.event.members.includes(thisUser)) {
|
||||||
await this.removeChat(
|
await this.removeChat(
|
||||||
|
@ -140,8 +157,19 @@ export class GroupChats {
|
||||||
}
|
}
|
||||||
case MembershipUpdateEvent_EventType.MEMBERS_ADDED: {
|
case MembershipUpdateEvent_EventType.MEMBERS_ADDED: {
|
||||||
if (chat && chat.admins?.includes(signer)) {
|
if (chat && chat.admins?.includes(signer)) {
|
||||||
chat.members.push(...event.event.members);
|
const members: GroupMember[] = [];
|
||||||
if (chat.members.includes(thisUser)) {
|
await Promise.all(
|
||||||
|
event.event.members.map(async (member) => {
|
||||||
|
const topic = await getNegotiatedTopic(this.identity, member);
|
||||||
|
const symKey = await createSymKeyFromPassword(topic);
|
||||||
|
const partitionedTopic = getPartitionedTopic(member);
|
||||||
|
members.push({ topic, symKey, id: member, partitionedTopic });
|
||||||
|
})
|
||||||
|
);
|
||||||
|
chat.members.push(...members);
|
||||||
|
if (
|
||||||
|
chat.members.findIndex((member) => member.id === thisUser) > -1
|
||||||
|
) {
|
||||||
chat.removed = false;
|
chat.removed = false;
|
||||||
await this.addChat(chat, useCallback);
|
await this.addChat(chat, useCallback);
|
||||||
}
|
}
|
||||||
|
@ -196,7 +224,11 @@ export class GroupChats {
|
||||||
const chatMessage = ChatMessage.decode(message.payload);
|
const chatMessage = ChatMessage.decode(message.payload);
|
||||||
if (chatMessage) {
|
if (chatMessage) {
|
||||||
if (chatMessage.chatId === chat.chatId) {
|
if (chatMessage.chatId === chat.chatId) {
|
||||||
this.addMessage(chatMessage, member);
|
let sender = member;
|
||||||
|
if (message.signaturePublicKey) {
|
||||||
|
sender = compressPublicKey(message.signaturePublicKey);
|
||||||
|
}
|
||||||
|
this.addMessage(chatMessage, sender);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -212,10 +244,12 @@ export class GroupChats {
|
||||||
const observerFunction = removeObserver ? "deleteObserver" : "addObserver";
|
const observerFunction = removeObserver ? "deleteObserver" : "addObserver";
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
chat.members.map(async (member) => {
|
chat.members.map(async (member) => {
|
||||||
const topic = await getNegotiatedTopic(this.identity, member);
|
if (!removeObserver) {
|
||||||
|
this.waku.relay.addDecryptionKey(member.symKey);
|
||||||
|
}
|
||||||
this.waku.relay[observerFunction](
|
this.waku.relay[observerFunction](
|
||||||
(message) => this.handleWakuChatMessage(message, chat, member),
|
(message) => this.handleWakuChatMessage(message, chat, member.id),
|
||||||
[topic]
|
[member.topic]
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -248,9 +282,8 @@ export class GroupChats {
|
||||||
}
|
}
|
||||||
|
|
||||||
private async listen(): Promise<void> {
|
private async listen(): Promise<void> {
|
||||||
const messages = await this.waku.store.queryHistory([
|
const topic = getPartitionedTopic(bufToHex(this.identity.publicKey));
|
||||||
getPartitionedTopic(bufToHex(this.identity.publicKey)),
|
const messages = await this.waku.store.queryHistory([topic]);
|
||||||
]);
|
|
||||||
messages.sort((a, b) =>
|
messages.sort((a, b) =>
|
||||||
(a?.timestamp?.getTime() ?? 0) < (b?.timestamp?.getTime() ?? 0) ? -1 : 1
|
(a?.timestamp?.getTime() ?? 0) < (b?.timestamp?.getTime() ?? 0) ? -1 : 1
|
||||||
);
|
);
|
||||||
|
@ -270,18 +303,18 @@ export class GroupChats {
|
||||||
);
|
);
|
||||||
this.waku.relay.addObserver(
|
this.waku.relay.addObserver(
|
||||||
(message) => this.decodeUpdateMessage(message, true),
|
(message) => this.decodeUpdateMessage(message, true),
|
||||||
[getPartitionedTopic(bufToHex(this.identity.publicKey))]
|
[topic]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async sendUpdateMessage(
|
private async sendUpdateMessage(
|
||||||
payload: Uint8Array,
|
payload: Uint8Array,
|
||||||
members: string[]
|
members: GroupMember[]
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const wakuMessages = await Promise.all(
|
const wakuMessages = await Promise.all(
|
||||||
members.map(
|
members.map(
|
||||||
async (member) =>
|
async (member) =>
|
||||||
await WakuMessage.fromBytes(payload, getPartitionedTopic(member))
|
await WakuMessage.fromBytes(payload, member.partitionedTopic)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
wakuMessages.forEach((msg) => this.waku.relay.send(msg));
|
wakuMessages.forEach((msg) => this.waku.relay.send(msg));
|
||||||
|
@ -314,10 +347,23 @@ export class GroupChats {
|
||||||
const payload = MembershipUpdateMessage.create(chatId, this.identity);
|
const payload = MembershipUpdateMessage.create(chatId, this.identity);
|
||||||
const chat = this.chats[chatId];
|
const chat = this.chats[chatId];
|
||||||
if (chat && payload) {
|
if (chat && payload) {
|
||||||
const newMembers = members.filter(
|
const newMembers: GroupMember[] = [];
|
||||||
(member) => !chat.members.includes(member)
|
|
||||||
|
await Promise.all(
|
||||||
|
members
|
||||||
|
.filter(
|
||||||
|
(member) =>
|
||||||
|
!chat.members.map((chatMember) => chatMember.id).includes(member)
|
||||||
|
)
|
||||||
|
.map(async (member) => {
|
||||||
|
const topic = await getNegotiatedTopic(this.identity, member);
|
||||||
|
const symKey = await createSymKeyFromPassword(topic);
|
||||||
|
const partitionedTopic = getPartitionedTopic(member);
|
||||||
|
newMembers.push({ topic, symKey, id: member, partitionedTopic });
|
||||||
|
})
|
||||||
);
|
);
|
||||||
payload.addMembersAddedEvent(newMembers);
|
|
||||||
|
payload.addMembersAddedEvent(newMembers.map((member) => member.id));
|
||||||
await this.sendUpdateMessage(payload.encode(), [
|
await this.sendUpdateMessage(payload.encode(), [
|
||||||
...chat.members,
|
...chat.members,
|
||||||
...newMembers,
|
...newMembers,
|
||||||
|
@ -335,7 +381,19 @@ export class GroupChats {
|
||||||
this.identity,
|
this.identity,
|
||||||
members
|
members
|
||||||
).encode();
|
).encode();
|
||||||
await this.sendUpdateMessage(payload, members);
|
|
||||||
|
const newMembers: GroupMember[] = [];
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
members.map(async (member) => {
|
||||||
|
const topic = await getNegotiatedTopic(this.identity, member);
|
||||||
|
const symKey = await createSymKeyFromPassword(topic);
|
||||||
|
const partitionedTopic = getPartitionedTopic(member);
|
||||||
|
newMembers.push({ topic, symKey, id: member, partitionedTopic });
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
await this.sendUpdateMessage(payload, newMembers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -374,11 +432,10 @@ export class GroupChats {
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
chat.members.map(async (member) => {
|
chat.members.map(async (member) => {
|
||||||
const topic = await getNegotiatedTopic(this.identity, member);
|
|
||||||
const msgLength = (
|
const msgLength = (
|
||||||
await this.waku.store.queryHistory([topic], {
|
await this.waku.store.queryHistory([member.topic], {
|
||||||
timeFilter: { startTime, endTime },
|
timeFilter: { startTime, endTime },
|
||||||
callback: (msg) => _callback(msg, member),
|
callback: (msg) => _callback(msg, member.id),
|
||||||
})
|
})
|
||||||
).length;
|
).length;
|
||||||
amountOfMessages.push(msgLength);
|
amountOfMessages.push(msgLength);
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
|
import { ec } from "elliptic";
|
||||||
import { utils } from "js-waku";
|
import { utils } from "js-waku";
|
||||||
|
|
||||||
|
const EC = new ec("secp256k1");
|
||||||
|
|
||||||
const hexToBuf = utils.hexToBuf;
|
const hexToBuf = utils.hexToBuf;
|
||||||
export { hexToBuf };
|
export { hexToBuf };
|
||||||
|
|
||||||
|
@ -9,3 +12,8 @@ export { hexToBuf };
|
||||||
export function bufToHex(buf: Uint8Array): string {
|
export function bufToHex(buf: Uint8Array): string {
|
||||||
return "0x" + utils.bufToHex(buf);
|
return "0x" + utils.bufToHex(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function compressPublicKey(key: Uint8Array): string {
|
||||||
|
const PubKey = EC.keyFromPublic(key);
|
||||||
|
return "0x" + PubKey.getPublic(true, "hex");
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue