Add private group chat handling (#136)
This commit is contained in:
parent
77dfd154b2
commit
30984cdc05
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,2 +1,3 @@
|
||||||
yarnPath: .yarn/releases/yarn-3.0.2.cjs
|
nodeLinker: node-modules
|
||||||
nodeLinker: node-modules # Needed to work with Mocha, see https://github.com/yarnpkg/berry/issues/638
|
|
||||||
|
yarnPath: .yarn/releases/yarn-3.1.0.cjs
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "dappconnect-sdks",
|
"name": "dappconnect-sdks",
|
||||||
"packageManager": "yarn@3.0.2",
|
"packageManager": "yarn@3.1.0",
|
||||||
"license": "MIT OR Apache-2.0",
|
"license": "MIT OR Apache-2.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"fix": "run-s 'fix:*' && wsrun -e -c -s fix",
|
"fix": "run-s 'fix:*' && wsrun -e -c -s fix",
|
||||||
|
|
|
@ -20,13 +20,16 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@dappconnect/react-chat": "^0.1.0",
|
"@dappconnect/react-chat": "^0.1.0",
|
||||||
"assert": "^2.0.0",
|
"assert": "^2.0.0",
|
||||||
|
"browserify-zlib": "^0.2.0",
|
||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
"crypto-browserify": "^3.12.0",
|
"crypto-browserify": "^3.12.0",
|
||||||
|
"https-browserify": "^1.0.0",
|
||||||
"process": "^0.11.10",
|
"process": "^0.11.10",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
"stream-browserify": "^3.0.0",
|
"stream-browserify": "^3.0.0",
|
||||||
|
"stream-http": "^3.2.0",
|
||||||
"styled-components": "^5.3.1"
|
"styled-components": "^5.3.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -25,6 +25,9 @@ module.exports = env => {
|
||||||
crypto: require.resolve('crypto-browserify'),
|
crypto: require.resolve('crypto-browserify'),
|
||||||
stream: require.resolve('stream-browserify'),
|
stream: require.resolve('stream-browserify'),
|
||||||
assert: require.resolve('assert'),
|
assert: require.resolve('assert'),
|
||||||
|
http: require.resolve('stream-http'),
|
||||||
|
https: require.resolve('https-browserify'),
|
||||||
|
zlib: require.resolve('browserify-zlib')
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
|
|
|
@ -18,8 +18,7 @@ export function ChatCreation({ editGroup }: 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();
|
||||||
const { contacts, setChannel } = useMessengerContext();
|
|
||||||
const setChatState = useChatState()[1];
|
const setChatState = useChatState()[1];
|
||||||
|
|
||||||
const addMember = useCallback(
|
const addMember = useCallback(
|
||||||
|
@ -40,19 +39,9 @@ export function ChatCreation({ editGroup }: ChatCreationProps) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const createChat = (group: string[]) => {
|
const createChat = (group: string[]) => {
|
||||||
group.length > 1
|
const newGroup = group.slice();
|
||||||
? setChannel({
|
newGroup.push(bufToHex(identity.publicKey));
|
||||||
id: group.join(""),
|
group.length > 1 ? createGroupChat(newGroup) : createGroupChat(newGroup);
|
||||||
name: group.join(", "),
|
|
||||||
type: "group",
|
|
||||||
description: `${group.length + 1} members`,
|
|
||||||
})
|
|
||||||
: setChannel({
|
|
||||||
id: group[0],
|
|
||||||
name: group[0],
|
|
||||||
type: "dm",
|
|
||||||
description: "Contact",
|
|
||||||
});
|
|
||||||
setChatState(ChatState.ChatBody);
|
setChatState(ChatState.ChatBody);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ const MessengerContext = createContext<MessengerType>({
|
||||||
setChannel: () => undefined,
|
setChannel: () => undefined,
|
||||||
removeChannel: () => undefined,
|
removeChannel: () => undefined,
|
||||||
setActiveChannel: () => undefined,
|
setActiveChannel: () => undefined,
|
||||||
|
createGroupChat: () => undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
export function useMessengerContext() {
|
export function useMessengerContext() {
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
import { useCallback, useMemo } from "react";
|
||||||
|
import {
|
||||||
|
GroupChat,
|
||||||
|
GroupChats,
|
||||||
|
Identity,
|
||||||
|
Messenger,
|
||||||
|
ChatMessage as StatusChatMessage,
|
||||||
|
} from "status-communities/dist/cjs";
|
||||||
|
|
||||||
|
import { ChannelData, ChannelsData } from "../../models/ChannelData";
|
||||||
|
import { ChatMessage } from "../../models/ChatMessage";
|
||||||
|
import { Contact } from "../../models/Contact";
|
||||||
|
|
||||||
|
export function useGroupChats(
|
||||||
|
messenger: Messenger | undefined,
|
||||||
|
identity: Identity | undefined,
|
||||||
|
setChannels: React.Dispatch<React.SetStateAction<ChannelsData>>,
|
||||||
|
setActiveChannel: (channel: ChannelData) => void,
|
||||||
|
addChatMessage: (newMessage: ChatMessage | undefined, id: string) => void,
|
||||||
|
channels: ChannelsData
|
||||||
|
) {
|
||||||
|
const groupChat = useMemo(() => {
|
||||||
|
if (messenger && identity) {
|
||||||
|
const addChat = (chat: GroupChat) => {
|
||||||
|
const contactFromId = (member: string): Contact => {
|
||||||
|
return {
|
||||||
|
blocked: false,
|
||||||
|
id: member,
|
||||||
|
isUntrustworthy: false,
|
||||||
|
online: false,
|
||||||
|
trueName: member,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const members = chat.members.map(contactFromId);
|
||||||
|
const channel: ChannelData = {
|
||||||
|
id: chat.chatId,
|
||||||
|
name: chat.chatId,
|
||||||
|
type: "group",
|
||||||
|
members: members,
|
||||||
|
};
|
||||||
|
setChannels((prev) => {
|
||||||
|
return { ...prev, [channel.id]: channel };
|
||||||
|
});
|
||||||
|
setActiveChannel(channel);
|
||||||
|
};
|
||||||
|
const removeChat = (chat: GroupChat) => {
|
||||||
|
setChannels((prev) => {
|
||||||
|
delete prev[chat.chatId];
|
||||||
|
return prev;
|
||||||
|
});
|
||||||
|
setActiveChannel({
|
||||||
|
id: "",
|
||||||
|
name: "",
|
||||||
|
type: "channel",
|
||||||
|
} as ChannelData);
|
||||||
|
};
|
||||||
|
const handleMessage = (msg: StatusChatMessage, sender: string) =>
|
||||||
|
addChatMessage(
|
||||||
|
new ChatMessage(msg.text ?? "", new Date(msg.clock ?? 0), sender),
|
||||||
|
msg.chatId
|
||||||
|
);
|
||||||
|
return new GroupChats(
|
||||||
|
identity,
|
||||||
|
messenger.waku,
|
||||||
|
addChat,
|
||||||
|
removeChat,
|
||||||
|
handleMessage
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, [messenger, identity]);
|
||||||
|
|
||||||
|
const createGroupChat = useCallback(
|
||||||
|
(members: string[]) => {
|
||||||
|
if (groupChat) {
|
||||||
|
groupChat.createGroupChat(members);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[groupChat]
|
||||||
|
);
|
||||||
|
|
||||||
|
const removeChannel = useCallback(
|
||||||
|
(channelId: string) => {
|
||||||
|
if (groupChat) {
|
||||||
|
groupChat.quitChat(channelId);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[channels, groupChat]
|
||||||
|
);
|
||||||
|
|
||||||
|
return { createGroupChat, removeChannel, groupChat };
|
||||||
|
}
|
|
@ -24,9 +24,8 @@ export function useMessages(
|
||||||
|
|
||||||
const mentions = useNotifications();
|
const mentions = useNotifications();
|
||||||
|
|
||||||
const addMessage = useCallback(
|
const addChatMessage = useCallback(
|
||||||
(msg: ApplicationMetadataMessage, id: string, date: Date) => {
|
(newMessage: ChatMessage | undefined, id: string) => {
|
||||||
const newMessage = ChatMessage.fromMetadataMessage(msg, date);
|
|
||||||
if (newMessage) {
|
if (newMessage) {
|
||||||
contacts?.addContact(newMessage.sender);
|
contacts?.addContact(newMessage.sender);
|
||||||
setMessages((prev) => {
|
setMessages((prev) => {
|
||||||
|
@ -52,6 +51,14 @@ export function useMessages(
|
||||||
[contacts, identity]
|
[contacts, identity]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const addMessage = useCallback(
|
||||||
|
(msg: ApplicationMetadataMessage, id: string, date: Date) => {
|
||||||
|
const newMessage = ChatMessage.fromMetadataMessage(msg, date);
|
||||||
|
addChatMessage(newMessage, id);
|
||||||
|
},
|
||||||
|
[contacts, identity]
|
||||||
|
);
|
||||||
|
|
||||||
const activeMessages = useMemo(
|
const activeMessages = useMemo(
|
||||||
() => messages?.[chatId] ?? [],
|
() => messages?.[chatId] ?? [],
|
||||||
[messages, chatId]
|
[messages, chatId]
|
||||||
|
@ -64,5 +71,6 @@ export function useMessages(
|
||||||
clearNotifications,
|
clearNotifications,
|
||||||
mentions: mentions.notifications,
|
mentions: mentions.notifications,
|
||||||
clearMentions: mentions.clearNotifications,
|
clearMentions: mentions.clearNotifications,
|
||||||
|
addChatMessage,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { createCommunity } from "../../utils/createCommunity";
|
||||||
import { createMessenger } from "../../utils/createMessenger";
|
import { createMessenger } from "../../utils/createMessenger";
|
||||||
import { uintToImgUrl } from "../../utils/uintToImgUrl";
|
import { uintToImgUrl } from "../../utils/uintToImgUrl";
|
||||||
|
|
||||||
|
import { useGroupChats } from "./useGroupChats";
|
||||||
import { useLoadPrevDay } from "./useLoadPrevDay";
|
import { useLoadPrevDay } from "./useLoadPrevDay";
|
||||||
import { useMessages } from "./useMessages";
|
import { useMessages } from "./useMessages";
|
||||||
|
|
||||||
|
@ -39,6 +40,7 @@ export type MessengerType = {
|
||||||
removeChannel: (channelId: string) => void;
|
removeChannel: (channelId: string) => void;
|
||||||
activeChannel: ChannelData;
|
activeChannel: ChannelData;
|
||||||
setActiveChannel: (channel: ChannelData) => void;
|
setActiveChannel: (channel: ChannelData) => void;
|
||||||
|
createGroupChat: (members: string[]) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function useMessenger(
|
export function useMessenger(
|
||||||
|
@ -98,6 +100,7 @@ export function useMessenger(
|
||||||
}, [internalContacts]);
|
}, [internalContacts]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
addChatMessage,
|
||||||
addMessage,
|
addMessage,
|
||||||
clearNotifications,
|
clearNotifications,
|
||||||
notifications,
|
notifications,
|
||||||
|
@ -132,25 +135,6 @@ export function useMessenger(
|
||||||
}
|
}
|
||||||
}, [messenger, community]);
|
}, [messenger, community]);
|
||||||
|
|
||||||
const sendMessage = useCallback(
|
|
||||||
async (messageText?: string, image?: Uint8Array) => {
|
|
||||||
if (messageText) {
|
|
||||||
await messenger?.sendMessage(chatId, {
|
|
||||||
text: messageText,
|
|
||||||
contentType: 0,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (image) {
|
|
||||||
await messenger?.sendMessage(chatId, {
|
|
||||||
image,
|
|
||||||
imageType: 1,
|
|
||||||
contentType: 2,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[chatId, messenger]
|
|
||||||
);
|
|
||||||
|
|
||||||
const [channels, setChannels] = useState<ChannelsData>({});
|
const [channels, setChannels] = useState<ChannelsData>({});
|
||||||
|
|
||||||
const setChannel = useCallback((channel: ChannelData) => {
|
const setChannel = useCallback((channel: ChannelData) => {
|
||||||
|
@ -160,21 +144,6 @@ export function useMessenger(
|
||||||
setActiveChannel(channel);
|
setActiveChannel(channel);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const removeChannel = useCallback(
|
|
||||||
(channelId: string) => {
|
|
||||||
setChannels((prev) => {
|
|
||||||
delete prev[channelId];
|
|
||||||
return prev;
|
|
||||||
});
|
|
||||||
const newActiveChannel: ChannelData = Object.values(channels)?.[0] ?? {
|
|
||||||
id: "",
|
|
||||||
name: "",
|
|
||||||
};
|
|
||||||
setActiveChannel(newActiveChannel);
|
|
||||||
},
|
|
||||||
[channels]
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (community?.chats) {
|
if (community?.chats) {
|
||||||
for (const chat of community.chats.values()) {
|
for (const chat of community.chats.values()) {
|
||||||
|
@ -223,6 +192,38 @@ export function useMessenger(
|
||||||
}
|
}
|
||||||
}, [community]);
|
}, [community]);
|
||||||
|
|
||||||
|
const { groupChat, removeChannel, createGroupChat } = useGroupChats(
|
||||||
|
messenger,
|
||||||
|
identity,
|
||||||
|
setChannels,
|
||||||
|
setActiveChannel,
|
||||||
|
addChatMessage,
|
||||||
|
channels
|
||||||
|
);
|
||||||
|
|
||||||
|
const sendMessage = useCallback(
|
||||||
|
async (messageText?: string, image?: Uint8Array) => {
|
||||||
|
if (activeChannel.type === "group") {
|
||||||
|
groupChat?.sendMessage(activeChannel.id, messageText ?? "");
|
||||||
|
} else {
|
||||||
|
if (messageText) {
|
||||||
|
await messenger?.sendMessage(activeChannel.id, {
|
||||||
|
text: messageText,
|
||||||
|
contentType: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (image) {
|
||||||
|
await messenger?.sendMessage(activeChannel.id, {
|
||||||
|
image,
|
||||||
|
imageType: 1,
|
||||||
|
contentType: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[messenger, groupChat, activeChannel]
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
messenger,
|
messenger,
|
||||||
messages,
|
messages,
|
||||||
|
@ -241,5 +242,6 @@ export function useMessenger(
|
||||||
setActiveChannel,
|
setActiveChannel,
|
||||||
mentions,
|
mentions,
|
||||||
clearMentions,
|
clearMentions,
|
||||||
|
createGroupChat,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"repository": "https://github.com/status-im/dappconnect-chat-sdk/",
|
"repository": "https://github.com/status-im/dappconnect-chat-sdk/",
|
||||||
"license": "MIT OR Apache-2.0",
|
"license": "MIT OR Apache-2.0",
|
||||||
"packageManager": "yarn@3.0.1",
|
"packageManager": "yarn@3.1.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "run-s 'build:*'",
|
"build": "run-s 'build:*'",
|
||||||
"build:esm": "tsc --module es2020 --target es2017 --outDir dist/esm",
|
"build:esm": "tsc --module es2020 --target es2017 --outDir dist/esm",
|
||||||
|
@ -23,10 +23,13 @@
|
||||||
"proto:build": "buf generate"
|
"proto:build": "buf generate"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/bn.js": "^5.1.0",
|
||||||
"@types/chai": "^4.2.22",
|
"@types/chai": "^4.2.22",
|
||||||
|
"@types/elliptic": "^6.4.14",
|
||||||
"@types/mocha": "^9.0.0",
|
"@types/mocha": "^9.0.0",
|
||||||
"@types/pbkdf2": "^3.1.0",
|
"@types/pbkdf2": "^3.1.0",
|
||||||
"@types/secp256k1": "^4.0.3",
|
"@types/secp256k1": "^4.0.3",
|
||||||
|
"@types/uuid": "^8.3.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.31.1",
|
"@typescript-eslint/eslint-plugin": "^4.31.1",
|
||||||
"@typescript-eslint/parser": "^4.31.1",
|
"@typescript-eslint/parser": "^4.31.1",
|
||||||
"chai": "^4.3.4",
|
"chai": "^4.3.4",
|
||||||
|
@ -44,12 +47,15 @@
|
||||||
"typescript": "^4.4.3"
|
"typescript": "^4.4.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"bn.js": "^5.2.0",
|
||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
"ecies-geth": "^1.5.3",
|
"ecies-geth": "^1.5.3",
|
||||||
|
"elliptic": "^6.5.4",
|
||||||
"js-sha3": "^0.8.0",
|
"js-sha3": "^0.8.0",
|
||||||
"js-waku": "^0.13.1",
|
"js-waku": "^0.13.1",
|
||||||
"pbkdf2": "^3.1.2",
|
"pbkdf2": "^3.1.2",
|
||||||
"protobufjs": "^6.11.2",
|
"protobufjs": "^6.11.2",
|
||||||
"secp256k1": "^4.0.2"
|
"secp256k1": "^4.0.2",
|
||||||
|
"uuid": "^8.3.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package communities.v1;
|
||||||
|
|
||||||
|
import "communities/v1/enums.proto";
|
||||||
|
|
||||||
|
message EmojiReaction {
|
||||||
|
// clock Lamport timestamp of the chat message
|
||||||
|
uint64 clock = 1;
|
||||||
|
|
||||||
|
// chat_id the ID of the chat the message belongs to, for query efficiency the chat_id is stored in the db even though the
|
||||||
|
// target message also stores the chat_id
|
||||||
|
string chat_id = 2;
|
||||||
|
|
||||||
|
// message_id the ID of the target message that the user wishes to react to
|
||||||
|
string message_id = 3;
|
||||||
|
|
||||||
|
// message_type is (somewhat confusingly) the ID of the type of chat the message belongs to
|
||||||
|
MessageType message_type = 4;
|
||||||
|
|
||||||
|
// type the ID of the emoji the user wishes to react with
|
||||||
|
Type type = 5;
|
||||||
|
|
||||||
|
enum Type {
|
||||||
|
UNKNOWN_EMOJI_REACTION_TYPE = 0;
|
||||||
|
LOVE = 1;
|
||||||
|
THUMBS_UP = 2;
|
||||||
|
THUMBS_DOWN = 3;
|
||||||
|
LAUGH = 4;
|
||||||
|
SAD = 5;
|
||||||
|
ANGRY = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
// whether this is a rectraction of a previously sent emoji
|
||||||
|
bool retracted = 6;
|
||||||
|
|
||||||
|
// Grant for organisation chat messages
|
||||||
|
bytes grant = 7;
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package communities.v1;
|
||||||
|
|
||||||
|
import "communities/v1/chat_message.proto";
|
||||||
|
import "communities/v1/emoji_reaction.proto";
|
||||||
|
|
||||||
|
message MembershipUpdateEvent {
|
||||||
|
// Lamport timestamp of the event
|
||||||
|
uint64 clock = 1;
|
||||||
|
// List of public keys of objects of the action
|
||||||
|
repeated string members = 2;
|
||||||
|
// Name of the chat for the CHAT_CREATED/NAME_CHANGED event types
|
||||||
|
string name = 3;
|
||||||
|
// The type of the event
|
||||||
|
EventType type = 4;
|
||||||
|
|
||||||
|
enum EventType {
|
||||||
|
UNKNOWN = 0;
|
||||||
|
CHAT_CREATED = 1;
|
||||||
|
NAME_CHANGED = 2;
|
||||||
|
MEMBERS_ADDED = 3;
|
||||||
|
MEMBER_JOINED = 4;
|
||||||
|
MEMBER_REMOVED = 5;
|
||||||
|
ADMINS_ADDED = 6;
|
||||||
|
ADMIN_REMOVED = 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MembershipUpdateMessage is a message used to propagate information
|
||||||
|
// about group membership changes.
|
||||||
|
// For more information, see https://github.com/status-im/specs/blob/master/status-group-chats-spec.md.
|
||||||
|
message MembershipUpdateMessage {
|
||||||
|
// The chat id of the private group chat
|
||||||
|
string chat_id = 1;
|
||||||
|
// A list of events for this group chat, first x bytes are the signature, then is a
|
||||||
|
// protobuf encoded MembershipUpdateEvent
|
||||||
|
repeated bytes events = 2;
|
||||||
|
|
||||||
|
// An optional chat message
|
||||||
|
oneof chat_entity {
|
||||||
|
ChatMessage message = 3;
|
||||||
|
EmojiReaction emoji_reaction = 4;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,249 @@
|
||||||
|
import { Waku, WakuMessage } from "js-waku";
|
||||||
|
|
||||||
|
import { Identity } from "./identity";
|
||||||
|
import { MembershipUpdateEvent_EventType } from "./proto/communities/v1/membership_update_message";
|
||||||
|
import { getNegotiatedTopic, getPartitionedTopic } from "./topics";
|
||||||
|
import { bufToHex } from "./utils";
|
||||||
|
import { MembershipUpdateMessage } from "./wire/membership_update_message";
|
||||||
|
|
||||||
|
import { ChatMessage, ContentType } from ".";
|
||||||
|
|
||||||
|
export type GroupChat = {
|
||||||
|
chatId: string;
|
||||||
|
members: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GroupChatsType = {
|
||||||
|
[id: string]: GroupChat;
|
||||||
|
};
|
||||||
|
/* TODO: add chat messages encryption */
|
||||||
|
|
||||||
|
export class GroupChats {
|
||||||
|
waku: Waku;
|
||||||
|
identity: Identity;
|
||||||
|
private callback: (chats: GroupChat) => void;
|
||||||
|
private removeCallback: (chats: GroupChat) => void;
|
||||||
|
private addMessage: (message: ChatMessage, sender: string) => void;
|
||||||
|
|
||||||
|
public chats: GroupChatsType = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GroupChats holds a list of private chats and listens to their status broadcast
|
||||||
|
*
|
||||||
|
* @param identity identity of user
|
||||||
|
*
|
||||||
|
* @param waku waku class used to listen to broadcast and broadcast status
|
||||||
|
*
|
||||||
|
* @param callback callback function called when new private group chat is ceated
|
||||||
|
*
|
||||||
|
* @param removeCallback callback function when private group chat is to be removed
|
||||||
|
*
|
||||||
|
* @param addMessage callback function when
|
||||||
|
*/
|
||||||
|
public constructor(
|
||||||
|
identity: Identity,
|
||||||
|
waku: Waku,
|
||||||
|
callback: (chat: GroupChat) => void,
|
||||||
|
removeCallback: (chat: GroupChat) => void,
|
||||||
|
addMessage: (message: ChatMessage, sender: string) => void
|
||||||
|
) {
|
||||||
|
this.waku = waku;
|
||||||
|
this.identity = identity;
|
||||||
|
this.callback = callback;
|
||||||
|
this.removeCallback = removeCallback;
|
||||||
|
this.addMessage = addMessage;
|
||||||
|
this.listen();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send chat message on given private chat
|
||||||
|
*
|
||||||
|
* @param chatId chat id of private group chat
|
||||||
|
*
|
||||||
|
* @param text text message to send
|
||||||
|
*/
|
||||||
|
public async sendMessage(chatId: string, text: string): Promise<void> {
|
||||||
|
const now = Date.now();
|
||||||
|
const chat = this.chats[chatId];
|
||||||
|
if (chat) {
|
||||||
|
await Promise.all(
|
||||||
|
chat.members.map(async (member) => {
|
||||||
|
const chatMessage = ChatMessage.createMessage(now, now, chatId, {
|
||||||
|
text,
|
||||||
|
contentType: ContentType.Text,
|
||||||
|
});
|
||||||
|
const wakuMessage = await WakuMessage.fromBytes(
|
||||||
|
chatMessage.encode(),
|
||||||
|
await getNegotiatedTopic(this.identity, member)
|
||||||
|
);
|
||||||
|
this.waku.relay.send(wakuMessage);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async decodeUpdateMessage(
|
||||||
|
message: WakuMessage,
|
||||||
|
useCallback: boolean
|
||||||
|
): Promise<void> {
|
||||||
|
try {
|
||||||
|
if (message.payload) {
|
||||||
|
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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleWakuChatMessage(
|
||||||
|
message: WakuMessage,
|
||||||
|
chat: GroupChat,
|
||||||
|
member: string
|
||||||
|
): void {
|
||||||
|
try {
|
||||||
|
if (message.payload) {
|
||||||
|
const chatMessage = ChatMessage.decode(message.payload);
|
||||||
|
if (chatMessage) {
|
||||||
|
if (chatMessage.chatId === chat.chatId) {
|
||||||
|
this.addMessage(chatMessage, member);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleChatObserver(
|
||||||
|
chat: GroupChat,
|
||||||
|
removeObserver?: boolean
|
||||||
|
): Promise<void> {
|
||||||
|
const observerFunction = removeObserver ? "deleteObserver" : "addObserver";
|
||||||
|
await Promise.all(
|
||||||
|
chat.members.map(async (member) => {
|
||||||
|
const topic = await getNegotiatedTopic(this.identity, member);
|
||||||
|
this.waku.relay[observerFunction](
|
||||||
|
(message) => this.handleWakuChatMessage(message, chat, member),
|
||||||
|
[topic]
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async addChat(chat: GroupChat, useCallback: boolean): Promise<void> {
|
||||||
|
this.chats[chat.chatId] = chat;
|
||||||
|
if (useCallback) {
|
||||||
|
await this.handleChatObserver(chat);
|
||||||
|
this.callback(chat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async removeChat(
|
||||||
|
chat: GroupChat,
|
||||||
|
useCallback: boolean
|
||||||
|
): Promise<void> {
|
||||||
|
delete this.chats[chat.chatId];
|
||||||
|
if (useCallback) {
|
||||||
|
await this.handleChatObserver(chat, true);
|
||||||
|
this.removeCallback(chat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async listen(): Promise<void> {
|
||||||
|
const messages = await this.waku.store.queryHistory([
|
||||||
|
getPartitionedTopic(bufToHex(this.identity.publicKey)),
|
||||||
|
]);
|
||||||
|
messages.sort((a, b) =>
|
||||||
|
(a?.timestamp?.getTime() ?? 0) < (b?.timestamp?.getTime() ?? 0) ? -1 : 1
|
||||||
|
);
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
messages.map(
|
||||||
|
async (message) => await this.decodeUpdateMessage(message, false)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
await Promise.all(
|
||||||
|
Object.values(this.chats).map(async (chat) => {
|
||||||
|
await this.handleChatObserver(chat);
|
||||||
|
this.callback(chat);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
this.waku.relay.addObserver(
|
||||||
|
(message) => this.decodeUpdateMessage(message, true),
|
||||||
|
[getPartitionedTopic(bufToHex(this.identity.publicKey))]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a create group chat membership update message with given members
|
||||||
|
*
|
||||||
|
* @param members a list of public keys of members to be included in private group chat
|
||||||
|
*/
|
||||||
|
public async createGroupChat(members: string[]): Promise<void> {
|
||||||
|
const payload = MembershipUpdateMessage.createChat(
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a remove member to private group chat
|
||||||
|
*
|
||||||
|
* @param chatId id of private group chat
|
||||||
|
*/
|
||||||
|
public async quitChat(chatId: string): Promise<void> {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ export { Messenger } from "./messenger";
|
||||||
export { Community } from "./community";
|
export { Community } from "./community";
|
||||||
export { Contacts } from "./contacts";
|
export { Contacts } from "./contacts";
|
||||||
export { Chat } from "./chat";
|
export { Chat } from "./chat";
|
||||||
|
export * from "./groupChats";
|
||||||
export * as utils from "./utils";
|
export * as utils from "./utils";
|
||||||
export { ApplicationMetadataMessage } from "./wire/application_metadata_message";
|
export { ApplicationMetadataMessage } from "./wire/application_metadata_message";
|
||||||
export {
|
export {
|
||||||
|
|
|
@ -0,0 +1,328 @@
|
||||||
|
/* eslint-disable */
|
||||||
|
import Long from "long";
|
||||||
|
import _m0 from "protobufjs/minimal";
|
||||||
|
import {
|
||||||
|
MessageType,
|
||||||
|
messageTypeFromJSON,
|
||||||
|
messageTypeToJSON,
|
||||||
|
} from "../../communities/v1/enums";
|
||||||
|
|
||||||
|
export const protobufPackage = "communities.v1";
|
||||||
|
|
||||||
|
export interface EmojiReaction {
|
||||||
|
/** clock Lamport timestamp of the chat message */
|
||||||
|
clock: number;
|
||||||
|
/**
|
||||||
|
* chat_id the ID of the chat the message belongs to, for query efficiency the chat_id is stored in the db even though the
|
||||||
|
* target message also stores the chat_id
|
||||||
|
*/
|
||||||
|
chatId: string;
|
||||||
|
/** message_id the ID of the target message that the user wishes to react to */
|
||||||
|
messageId: string;
|
||||||
|
/** message_type is (somewhat confusingly) the ID of the type of chat the message belongs to */
|
||||||
|
messageType: MessageType;
|
||||||
|
/** type the ID of the emoji the user wishes to react with */
|
||||||
|
type: EmojiReaction_Type;
|
||||||
|
/** whether this is a rectraction of a previously sent emoji */
|
||||||
|
retracted: boolean;
|
||||||
|
/** Grant for organisation chat messages */
|
||||||
|
grant: Uint8Array;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum EmojiReaction_Type {
|
||||||
|
UNKNOWN_EMOJI_REACTION_TYPE = 0,
|
||||||
|
LOVE = 1,
|
||||||
|
THUMBS_UP = 2,
|
||||||
|
THUMBS_DOWN = 3,
|
||||||
|
LAUGH = 4,
|
||||||
|
SAD = 5,
|
||||||
|
ANGRY = 6,
|
||||||
|
UNRECOGNIZED = -1,
|
||||||
|
}
|
||||||
|
|
||||||
|
export function emojiReaction_TypeFromJSON(object: any): EmojiReaction_Type {
|
||||||
|
switch (object) {
|
||||||
|
case 0:
|
||||||
|
case "UNKNOWN_EMOJI_REACTION_TYPE":
|
||||||
|
return EmojiReaction_Type.UNKNOWN_EMOJI_REACTION_TYPE;
|
||||||
|
case 1:
|
||||||
|
case "LOVE":
|
||||||
|
return EmojiReaction_Type.LOVE;
|
||||||
|
case 2:
|
||||||
|
case "THUMBS_UP":
|
||||||
|
return EmojiReaction_Type.THUMBS_UP;
|
||||||
|
case 3:
|
||||||
|
case "THUMBS_DOWN":
|
||||||
|
return EmojiReaction_Type.THUMBS_DOWN;
|
||||||
|
case 4:
|
||||||
|
case "LAUGH":
|
||||||
|
return EmojiReaction_Type.LAUGH;
|
||||||
|
case 5:
|
||||||
|
case "SAD":
|
||||||
|
return EmojiReaction_Type.SAD;
|
||||||
|
case 6:
|
||||||
|
case "ANGRY":
|
||||||
|
return EmojiReaction_Type.ANGRY;
|
||||||
|
case -1:
|
||||||
|
case "UNRECOGNIZED":
|
||||||
|
default:
|
||||||
|
return EmojiReaction_Type.UNRECOGNIZED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function emojiReaction_TypeToJSON(object: EmojiReaction_Type): string {
|
||||||
|
switch (object) {
|
||||||
|
case EmojiReaction_Type.UNKNOWN_EMOJI_REACTION_TYPE:
|
||||||
|
return "UNKNOWN_EMOJI_REACTION_TYPE";
|
||||||
|
case EmojiReaction_Type.LOVE:
|
||||||
|
return "LOVE";
|
||||||
|
case EmojiReaction_Type.THUMBS_UP:
|
||||||
|
return "THUMBS_UP";
|
||||||
|
case EmojiReaction_Type.THUMBS_DOWN:
|
||||||
|
return "THUMBS_DOWN";
|
||||||
|
case EmojiReaction_Type.LAUGH:
|
||||||
|
return "LAUGH";
|
||||||
|
case EmojiReaction_Type.SAD:
|
||||||
|
return "SAD";
|
||||||
|
case EmojiReaction_Type.ANGRY:
|
||||||
|
return "ANGRY";
|
||||||
|
default:
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseEmojiReaction: object = {
|
||||||
|
clock: 0,
|
||||||
|
chatId: "",
|
||||||
|
messageId: "",
|
||||||
|
messageType: 0,
|
||||||
|
type: 0,
|
||||||
|
retracted: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const EmojiReaction = {
|
||||||
|
encode(
|
||||||
|
message: EmojiReaction,
|
||||||
|
writer: _m0.Writer = _m0.Writer.create()
|
||||||
|
): _m0.Writer {
|
||||||
|
if (message.clock !== 0) {
|
||||||
|
writer.uint32(8).uint64(message.clock);
|
||||||
|
}
|
||||||
|
if (message.chatId !== "") {
|
||||||
|
writer.uint32(18).string(message.chatId);
|
||||||
|
}
|
||||||
|
if (message.messageId !== "") {
|
||||||
|
writer.uint32(26).string(message.messageId);
|
||||||
|
}
|
||||||
|
if (message.messageType !== 0) {
|
||||||
|
writer.uint32(32).int32(message.messageType);
|
||||||
|
}
|
||||||
|
if (message.type !== 0) {
|
||||||
|
writer.uint32(40).int32(message.type);
|
||||||
|
}
|
||||||
|
if (message.retracted === true) {
|
||||||
|
writer.uint32(48).bool(message.retracted);
|
||||||
|
}
|
||||||
|
if (message.grant.length !== 0) {
|
||||||
|
writer.uint32(58).bytes(message.grant);
|
||||||
|
}
|
||||||
|
return writer;
|
||||||
|
},
|
||||||
|
|
||||||
|
decode(input: _m0.Reader | Uint8Array, length?: number): EmojiReaction {
|
||||||
|
const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input);
|
||||||
|
let end = length === undefined ? reader.len : reader.pos + length;
|
||||||
|
const message = { ...baseEmojiReaction } as EmojiReaction;
|
||||||
|
message.grant = new Uint8Array();
|
||||||
|
while (reader.pos < end) {
|
||||||
|
const tag = reader.uint32();
|
||||||
|
switch (tag >>> 3) {
|
||||||
|
case 1:
|
||||||
|
message.clock = longToNumber(reader.uint64() as Long);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
message.chatId = reader.string();
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
message.messageId = reader.string();
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
message.messageType = reader.int32() as any;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
message.type = reader.int32() as any;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
message.retracted = reader.bool();
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
message.grant = reader.bytes();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
reader.skipType(tag & 7);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
},
|
||||||
|
|
||||||
|
fromJSON(object: any): EmojiReaction {
|
||||||
|
const message = { ...baseEmojiReaction } as EmojiReaction;
|
||||||
|
message.grant = new Uint8Array();
|
||||||
|
if (object.clock !== undefined && object.clock !== null) {
|
||||||
|
message.clock = Number(object.clock);
|
||||||
|
} else {
|
||||||
|
message.clock = 0;
|
||||||
|
}
|
||||||
|
if (object.chatId !== undefined && object.chatId !== null) {
|
||||||
|
message.chatId = String(object.chatId);
|
||||||
|
} else {
|
||||||
|
message.chatId = "";
|
||||||
|
}
|
||||||
|
if (object.messageId !== undefined && object.messageId !== null) {
|
||||||
|
message.messageId = String(object.messageId);
|
||||||
|
} else {
|
||||||
|
message.messageId = "";
|
||||||
|
}
|
||||||
|
if (object.messageType !== undefined && object.messageType !== null) {
|
||||||
|
message.messageType = messageTypeFromJSON(object.messageType);
|
||||||
|
} else {
|
||||||
|
message.messageType = 0;
|
||||||
|
}
|
||||||
|
if (object.type !== undefined && object.type !== null) {
|
||||||
|
message.type = emojiReaction_TypeFromJSON(object.type);
|
||||||
|
} else {
|
||||||
|
message.type = 0;
|
||||||
|
}
|
||||||
|
if (object.retracted !== undefined && object.retracted !== null) {
|
||||||
|
message.retracted = Boolean(object.retracted);
|
||||||
|
} else {
|
||||||
|
message.retracted = false;
|
||||||
|
}
|
||||||
|
if (object.grant !== undefined && object.grant !== null) {
|
||||||
|
message.grant = bytesFromBase64(object.grant);
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
},
|
||||||
|
|
||||||
|
toJSON(message: EmojiReaction): unknown {
|
||||||
|
const obj: any = {};
|
||||||
|
message.clock !== undefined && (obj.clock = message.clock);
|
||||||
|
message.chatId !== undefined && (obj.chatId = message.chatId);
|
||||||
|
message.messageId !== undefined && (obj.messageId = message.messageId);
|
||||||
|
message.messageType !== undefined &&
|
||||||
|
(obj.messageType = messageTypeToJSON(message.messageType));
|
||||||
|
message.type !== undefined &&
|
||||||
|
(obj.type = emojiReaction_TypeToJSON(message.type));
|
||||||
|
message.retracted !== undefined && (obj.retracted = message.retracted);
|
||||||
|
message.grant !== undefined &&
|
||||||
|
(obj.grant = base64FromBytes(
|
||||||
|
message.grant !== undefined ? message.grant : new Uint8Array()
|
||||||
|
));
|
||||||
|
return obj;
|
||||||
|
},
|
||||||
|
|
||||||
|
fromPartial(object: DeepPartial<EmojiReaction>): EmojiReaction {
|
||||||
|
const message = { ...baseEmojiReaction } as EmojiReaction;
|
||||||
|
if (object.clock !== undefined && object.clock !== null) {
|
||||||
|
message.clock = object.clock;
|
||||||
|
} else {
|
||||||
|
message.clock = 0;
|
||||||
|
}
|
||||||
|
if (object.chatId !== undefined && object.chatId !== null) {
|
||||||
|
message.chatId = object.chatId;
|
||||||
|
} else {
|
||||||
|
message.chatId = "";
|
||||||
|
}
|
||||||
|
if (object.messageId !== undefined && object.messageId !== null) {
|
||||||
|
message.messageId = object.messageId;
|
||||||
|
} else {
|
||||||
|
message.messageId = "";
|
||||||
|
}
|
||||||
|
if (object.messageType !== undefined && object.messageType !== null) {
|
||||||
|
message.messageType = object.messageType;
|
||||||
|
} else {
|
||||||
|
message.messageType = 0;
|
||||||
|
}
|
||||||
|
if (object.type !== undefined && object.type !== null) {
|
||||||
|
message.type = object.type;
|
||||||
|
} else {
|
||||||
|
message.type = 0;
|
||||||
|
}
|
||||||
|
if (object.retracted !== undefined && object.retracted !== null) {
|
||||||
|
message.retracted = object.retracted;
|
||||||
|
} else {
|
||||||
|
message.retracted = false;
|
||||||
|
}
|
||||||
|
if (object.grant !== undefined && object.grant !== null) {
|
||||||
|
message.grant = object.grant;
|
||||||
|
} else {
|
||||||
|
message.grant = new Uint8Array();
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
declare var self: any | undefined;
|
||||||
|
declare var window: any | undefined;
|
||||||
|
declare var global: any | undefined;
|
||||||
|
var globalThis: any = (() => {
|
||||||
|
if (typeof globalThis !== "undefined") return globalThis;
|
||||||
|
if (typeof self !== "undefined") return self;
|
||||||
|
if (typeof window !== "undefined") return window;
|
||||||
|
if (typeof global !== "undefined") return global;
|
||||||
|
throw "Unable to locate global object";
|
||||||
|
})();
|
||||||
|
|
||||||
|
const atob: (b64: string) => string =
|
||||||
|
globalThis.atob ||
|
||||||
|
((b64) => globalThis.Buffer.from(b64, "base64").toString("binary"));
|
||||||
|
function bytesFromBase64(b64: string): Uint8Array {
|
||||||
|
const bin = atob(b64);
|
||||||
|
const arr = new Uint8Array(bin.length);
|
||||||
|
for (let i = 0; i < bin.length; ++i) {
|
||||||
|
arr[i] = bin.charCodeAt(i);
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const btoa: (bin: string) => string =
|
||||||
|
globalThis.btoa ||
|
||||||
|
((bin) => globalThis.Buffer.from(bin, "binary").toString("base64"));
|
||||||
|
function base64FromBytes(arr: Uint8Array): string {
|
||||||
|
const bin: string[] = [];
|
||||||
|
for (const byte of arr) {
|
||||||
|
bin.push(String.fromCharCode(byte));
|
||||||
|
}
|
||||||
|
return btoa(bin.join(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
type Builtin =
|
||||||
|
| Date
|
||||||
|
| Function
|
||||||
|
| Uint8Array
|
||||||
|
| string
|
||||||
|
| number
|
||||||
|
| boolean
|
||||||
|
| undefined;
|
||||||
|
export type DeepPartial<T> = T extends Builtin
|
||||||
|
? T
|
||||||
|
: T extends Array<infer U>
|
||||||
|
? Array<DeepPartial<U>>
|
||||||
|
: T extends ReadonlyArray<infer U>
|
||||||
|
? ReadonlyArray<DeepPartial<U>>
|
||||||
|
: T extends {}
|
||||||
|
? { [K in keyof T]?: DeepPartial<T[K]> }
|
||||||
|
: Partial<T>;
|
||||||
|
|
||||||
|
function longToNumber(long: Long): number {
|
||||||
|
if (long.gt(Number.MAX_SAFE_INTEGER)) {
|
||||||
|
throw new globalThis.Error("Value is larger than Number.MAX_SAFE_INTEGER");
|
||||||
|
}
|
||||||
|
return long.toNumber();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_m0.util.Long !== Long) {
|
||||||
|
_m0.util.Long = Long as any;
|
||||||
|
_m0.configure();
|
||||||
|
}
|
|
@ -0,0 +1,436 @@
|
||||||
|
/* eslint-disable */
|
||||||
|
import Long from "long";
|
||||||
|
import _m0 from "protobufjs/minimal";
|
||||||
|
import { ChatMessage } from "../../communities/v1/chat_message";
|
||||||
|
import { EmojiReaction } from "../../communities/v1/emoji_reaction";
|
||||||
|
|
||||||
|
export const protobufPackage = "communities.v1";
|
||||||
|
|
||||||
|
export interface MembershipUpdateEvent {
|
||||||
|
/** Lamport timestamp of the event */
|
||||||
|
clock: number;
|
||||||
|
/** List of public keys of objects of the action */
|
||||||
|
members: string[];
|
||||||
|
/** Name of the chat for the CHAT_CREATED/NAME_CHANGED event types */
|
||||||
|
name: string;
|
||||||
|
/** The type of the event */
|
||||||
|
type: MembershipUpdateEvent_EventType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum MembershipUpdateEvent_EventType {
|
||||||
|
UNKNOWN = 0,
|
||||||
|
CHAT_CREATED = 1,
|
||||||
|
NAME_CHANGED = 2,
|
||||||
|
MEMBERS_ADDED = 3,
|
||||||
|
MEMBER_JOINED = 4,
|
||||||
|
MEMBER_REMOVED = 5,
|
||||||
|
ADMINS_ADDED = 6,
|
||||||
|
ADMIN_REMOVED = 7,
|
||||||
|
UNRECOGNIZED = -1,
|
||||||
|
}
|
||||||
|
|
||||||
|
export function membershipUpdateEvent_EventTypeFromJSON(
|
||||||
|
object: any
|
||||||
|
): MembershipUpdateEvent_EventType {
|
||||||
|
switch (object) {
|
||||||
|
case 0:
|
||||||
|
case "UNKNOWN":
|
||||||
|
return MembershipUpdateEvent_EventType.UNKNOWN;
|
||||||
|
case 1:
|
||||||
|
case "CHAT_CREATED":
|
||||||
|
return MembershipUpdateEvent_EventType.CHAT_CREATED;
|
||||||
|
case 2:
|
||||||
|
case "NAME_CHANGED":
|
||||||
|
return MembershipUpdateEvent_EventType.NAME_CHANGED;
|
||||||
|
case 3:
|
||||||
|
case "MEMBERS_ADDED":
|
||||||
|
return MembershipUpdateEvent_EventType.MEMBERS_ADDED;
|
||||||
|
case 4:
|
||||||
|
case "MEMBER_JOINED":
|
||||||
|
return MembershipUpdateEvent_EventType.MEMBER_JOINED;
|
||||||
|
case 5:
|
||||||
|
case "MEMBER_REMOVED":
|
||||||
|
return MembershipUpdateEvent_EventType.MEMBER_REMOVED;
|
||||||
|
case 6:
|
||||||
|
case "ADMINS_ADDED":
|
||||||
|
return MembershipUpdateEvent_EventType.ADMINS_ADDED;
|
||||||
|
case 7:
|
||||||
|
case "ADMIN_REMOVED":
|
||||||
|
return MembershipUpdateEvent_EventType.ADMIN_REMOVED;
|
||||||
|
case -1:
|
||||||
|
case "UNRECOGNIZED":
|
||||||
|
default:
|
||||||
|
return MembershipUpdateEvent_EventType.UNRECOGNIZED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function membershipUpdateEvent_EventTypeToJSON(
|
||||||
|
object: MembershipUpdateEvent_EventType
|
||||||
|
): string {
|
||||||
|
switch (object) {
|
||||||
|
case MembershipUpdateEvent_EventType.UNKNOWN:
|
||||||
|
return "UNKNOWN";
|
||||||
|
case MembershipUpdateEvent_EventType.CHAT_CREATED:
|
||||||
|
return "CHAT_CREATED";
|
||||||
|
case MembershipUpdateEvent_EventType.NAME_CHANGED:
|
||||||
|
return "NAME_CHANGED";
|
||||||
|
case MembershipUpdateEvent_EventType.MEMBERS_ADDED:
|
||||||
|
return "MEMBERS_ADDED";
|
||||||
|
case MembershipUpdateEvent_EventType.MEMBER_JOINED:
|
||||||
|
return "MEMBER_JOINED";
|
||||||
|
case MembershipUpdateEvent_EventType.MEMBER_REMOVED:
|
||||||
|
return "MEMBER_REMOVED";
|
||||||
|
case MembershipUpdateEvent_EventType.ADMINS_ADDED:
|
||||||
|
return "ADMINS_ADDED";
|
||||||
|
case MembershipUpdateEvent_EventType.ADMIN_REMOVED:
|
||||||
|
return "ADMIN_REMOVED";
|
||||||
|
default:
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MembershipUpdateMessage is a message used to propagate information
|
||||||
|
* about group membership changes.
|
||||||
|
* For more information, see https://github.com/status-im/specs/blob/master/status-group-chats-spec.md.
|
||||||
|
*/
|
||||||
|
export interface MembershipUpdateMessage {
|
||||||
|
/** The chat id of the private group chat */
|
||||||
|
chatId: string;
|
||||||
|
/**
|
||||||
|
* A list of events for this group chat, first x bytes are the signature, then is a
|
||||||
|
* protobuf encoded MembershipUpdateEvent
|
||||||
|
*/
|
||||||
|
events: Uint8Array[];
|
||||||
|
message: ChatMessage | undefined;
|
||||||
|
emojiReaction: EmojiReaction | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseMembershipUpdateEvent: object = {
|
||||||
|
clock: 0,
|
||||||
|
members: "",
|
||||||
|
name: "",
|
||||||
|
type: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const MembershipUpdateEvent = {
|
||||||
|
encode(
|
||||||
|
message: MembershipUpdateEvent,
|
||||||
|
writer: _m0.Writer = _m0.Writer.create()
|
||||||
|
): _m0.Writer {
|
||||||
|
if (message.clock !== 0) {
|
||||||
|
writer.uint32(8).uint64(message.clock);
|
||||||
|
}
|
||||||
|
for (const v of message.members) {
|
||||||
|
writer.uint32(18).string(v!);
|
||||||
|
}
|
||||||
|
if (message.name !== "") {
|
||||||
|
writer.uint32(26).string(message.name);
|
||||||
|
}
|
||||||
|
if (message.type !== 0) {
|
||||||
|
writer.uint32(32).int32(message.type);
|
||||||
|
}
|
||||||
|
return writer;
|
||||||
|
},
|
||||||
|
|
||||||
|
decode(
|
||||||
|
input: _m0.Reader | Uint8Array,
|
||||||
|
length?: number
|
||||||
|
): MembershipUpdateEvent {
|
||||||
|
const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input);
|
||||||
|
let end = length === undefined ? reader.len : reader.pos + length;
|
||||||
|
const message = { ...baseMembershipUpdateEvent } as MembershipUpdateEvent;
|
||||||
|
message.members = [];
|
||||||
|
while (reader.pos < end) {
|
||||||
|
const tag = reader.uint32();
|
||||||
|
switch (tag >>> 3) {
|
||||||
|
case 1:
|
||||||
|
message.clock = longToNumber(reader.uint64() as Long);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
message.members.push(reader.string());
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
message.name = reader.string();
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
message.type = reader.int32() as any;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
reader.skipType(tag & 7);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
},
|
||||||
|
|
||||||
|
fromJSON(object: any): MembershipUpdateEvent {
|
||||||
|
const message = { ...baseMembershipUpdateEvent } as MembershipUpdateEvent;
|
||||||
|
message.members = [];
|
||||||
|
if (object.clock !== undefined && object.clock !== null) {
|
||||||
|
message.clock = Number(object.clock);
|
||||||
|
} else {
|
||||||
|
message.clock = 0;
|
||||||
|
}
|
||||||
|
if (object.members !== undefined && object.members !== null) {
|
||||||
|
for (const e of object.members) {
|
||||||
|
message.members.push(String(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (object.name !== undefined && object.name !== null) {
|
||||||
|
message.name = String(object.name);
|
||||||
|
} else {
|
||||||
|
message.name = "";
|
||||||
|
}
|
||||||
|
if (object.type !== undefined && object.type !== null) {
|
||||||
|
message.type = membershipUpdateEvent_EventTypeFromJSON(object.type);
|
||||||
|
} else {
|
||||||
|
message.type = 0;
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
},
|
||||||
|
|
||||||
|
toJSON(message: MembershipUpdateEvent): unknown {
|
||||||
|
const obj: any = {};
|
||||||
|
message.clock !== undefined && (obj.clock = message.clock);
|
||||||
|
if (message.members) {
|
||||||
|
obj.members = message.members.map((e) => e);
|
||||||
|
} else {
|
||||||
|
obj.members = [];
|
||||||
|
}
|
||||||
|
message.name !== undefined && (obj.name = message.name);
|
||||||
|
message.type !== undefined &&
|
||||||
|
(obj.type = membershipUpdateEvent_EventTypeToJSON(message.type));
|
||||||
|
return obj;
|
||||||
|
},
|
||||||
|
|
||||||
|
fromPartial(
|
||||||
|
object: DeepPartial<MembershipUpdateEvent>
|
||||||
|
): MembershipUpdateEvent {
|
||||||
|
const message = { ...baseMembershipUpdateEvent } as MembershipUpdateEvent;
|
||||||
|
message.members = [];
|
||||||
|
if (object.clock !== undefined && object.clock !== null) {
|
||||||
|
message.clock = object.clock;
|
||||||
|
} else {
|
||||||
|
message.clock = 0;
|
||||||
|
}
|
||||||
|
if (object.members !== undefined && object.members !== null) {
|
||||||
|
for (const e of object.members) {
|
||||||
|
message.members.push(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (object.name !== undefined && object.name !== null) {
|
||||||
|
message.name = object.name;
|
||||||
|
} else {
|
||||||
|
message.name = "";
|
||||||
|
}
|
||||||
|
if (object.type !== undefined && object.type !== null) {
|
||||||
|
message.type = object.type;
|
||||||
|
} else {
|
||||||
|
message.type = 0;
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const baseMembershipUpdateMessage: object = { chatId: "" };
|
||||||
|
|
||||||
|
export const MembershipUpdateMessage = {
|
||||||
|
encode(
|
||||||
|
message: MembershipUpdateMessage,
|
||||||
|
writer: _m0.Writer = _m0.Writer.create()
|
||||||
|
): _m0.Writer {
|
||||||
|
if (message.chatId !== "") {
|
||||||
|
writer.uint32(10).string(message.chatId);
|
||||||
|
}
|
||||||
|
for (const v of message.events) {
|
||||||
|
writer.uint32(18).bytes(v!);
|
||||||
|
}
|
||||||
|
if (message.message !== undefined) {
|
||||||
|
ChatMessage.encode(message.message, writer.uint32(26).fork()).ldelim();
|
||||||
|
}
|
||||||
|
if (message.emojiReaction !== undefined) {
|
||||||
|
EmojiReaction.encode(
|
||||||
|
message.emojiReaction,
|
||||||
|
writer.uint32(34).fork()
|
||||||
|
).ldelim();
|
||||||
|
}
|
||||||
|
return writer;
|
||||||
|
},
|
||||||
|
|
||||||
|
decode(
|
||||||
|
input: _m0.Reader | Uint8Array,
|
||||||
|
length?: number
|
||||||
|
): MembershipUpdateMessage {
|
||||||
|
const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input);
|
||||||
|
let end = length === undefined ? reader.len : reader.pos + length;
|
||||||
|
const message = {
|
||||||
|
...baseMembershipUpdateMessage,
|
||||||
|
} as MembershipUpdateMessage;
|
||||||
|
message.events = [];
|
||||||
|
while (reader.pos < end) {
|
||||||
|
const tag = reader.uint32();
|
||||||
|
switch (tag >>> 3) {
|
||||||
|
case 1:
|
||||||
|
message.chatId = reader.string();
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
message.events.push(reader.bytes());
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
message.message = ChatMessage.decode(reader, reader.uint32());
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
message.emojiReaction = EmojiReaction.decode(reader, reader.uint32());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
reader.skipType(tag & 7);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
},
|
||||||
|
|
||||||
|
fromJSON(object: any): MembershipUpdateMessage {
|
||||||
|
const message = {
|
||||||
|
...baseMembershipUpdateMessage,
|
||||||
|
} as MembershipUpdateMessage;
|
||||||
|
message.events = [];
|
||||||
|
if (object.chatId !== undefined && object.chatId !== null) {
|
||||||
|
message.chatId = String(object.chatId);
|
||||||
|
} else {
|
||||||
|
message.chatId = "";
|
||||||
|
}
|
||||||
|
if (object.events !== undefined && object.events !== null) {
|
||||||
|
for (const e of object.events) {
|
||||||
|
message.events.push(bytesFromBase64(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (object.message !== undefined && object.message !== null) {
|
||||||
|
message.message = ChatMessage.fromJSON(object.message);
|
||||||
|
} else {
|
||||||
|
message.message = undefined;
|
||||||
|
}
|
||||||
|
if (object.emojiReaction !== undefined && object.emojiReaction !== null) {
|
||||||
|
message.emojiReaction = EmojiReaction.fromJSON(object.emojiReaction);
|
||||||
|
} else {
|
||||||
|
message.emojiReaction = undefined;
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
},
|
||||||
|
|
||||||
|
toJSON(message: MembershipUpdateMessage): unknown {
|
||||||
|
const obj: any = {};
|
||||||
|
message.chatId !== undefined && (obj.chatId = message.chatId);
|
||||||
|
if (message.events) {
|
||||||
|
obj.events = message.events.map((e) =>
|
||||||
|
base64FromBytes(e !== undefined ? e : new Uint8Array())
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
obj.events = [];
|
||||||
|
}
|
||||||
|
message.message !== undefined &&
|
||||||
|
(obj.message = message.message
|
||||||
|
? ChatMessage.toJSON(message.message)
|
||||||
|
: undefined);
|
||||||
|
message.emojiReaction !== undefined &&
|
||||||
|
(obj.emojiReaction = message.emojiReaction
|
||||||
|
? EmojiReaction.toJSON(message.emojiReaction)
|
||||||
|
: undefined);
|
||||||
|
return obj;
|
||||||
|
},
|
||||||
|
|
||||||
|
fromPartial(
|
||||||
|
object: DeepPartial<MembershipUpdateMessage>
|
||||||
|
): MembershipUpdateMessage {
|
||||||
|
const message = {
|
||||||
|
...baseMembershipUpdateMessage,
|
||||||
|
} as MembershipUpdateMessage;
|
||||||
|
message.events = [];
|
||||||
|
if (object.chatId !== undefined && object.chatId !== null) {
|
||||||
|
message.chatId = object.chatId;
|
||||||
|
} else {
|
||||||
|
message.chatId = "";
|
||||||
|
}
|
||||||
|
if (object.events !== undefined && object.events !== null) {
|
||||||
|
for (const e of object.events) {
|
||||||
|
message.events.push(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (object.message !== undefined && object.message !== null) {
|
||||||
|
message.message = ChatMessage.fromPartial(object.message);
|
||||||
|
} else {
|
||||||
|
message.message = undefined;
|
||||||
|
}
|
||||||
|
if (object.emojiReaction !== undefined && object.emojiReaction !== null) {
|
||||||
|
message.emojiReaction = EmojiReaction.fromPartial(object.emojiReaction);
|
||||||
|
} else {
|
||||||
|
message.emojiReaction = undefined;
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
declare var self: any | undefined;
|
||||||
|
declare var window: any | undefined;
|
||||||
|
declare var global: any | undefined;
|
||||||
|
var globalThis: any = (() => {
|
||||||
|
if (typeof globalThis !== "undefined") return globalThis;
|
||||||
|
if (typeof self !== "undefined") return self;
|
||||||
|
if (typeof window !== "undefined") return window;
|
||||||
|
if (typeof global !== "undefined") return global;
|
||||||
|
throw "Unable to locate global object";
|
||||||
|
})();
|
||||||
|
|
||||||
|
const atob: (b64: string) => string =
|
||||||
|
globalThis.atob ||
|
||||||
|
((b64) => globalThis.Buffer.from(b64, "base64").toString("binary"));
|
||||||
|
function bytesFromBase64(b64: string): Uint8Array {
|
||||||
|
const bin = atob(b64);
|
||||||
|
const arr = new Uint8Array(bin.length);
|
||||||
|
for (let i = 0; i < bin.length; ++i) {
|
||||||
|
arr[i] = bin.charCodeAt(i);
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const btoa: (bin: string) => string =
|
||||||
|
globalThis.btoa ||
|
||||||
|
((bin) => globalThis.Buffer.from(bin, "binary").toString("base64"));
|
||||||
|
function base64FromBytes(arr: Uint8Array): string {
|
||||||
|
const bin: string[] = [];
|
||||||
|
for (const byte of arr) {
|
||||||
|
bin.push(String.fromCharCode(byte));
|
||||||
|
}
|
||||||
|
return btoa(bin.join(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
type Builtin =
|
||||||
|
| Date
|
||||||
|
| Function
|
||||||
|
| Uint8Array
|
||||||
|
| string
|
||||||
|
| number
|
||||||
|
| boolean
|
||||||
|
| undefined;
|
||||||
|
export type DeepPartial<T> = T extends Builtin
|
||||||
|
? T
|
||||||
|
: T extends Array<infer U>
|
||||||
|
? Array<DeepPartial<U>>
|
||||||
|
: T extends ReadonlyArray<infer U>
|
||||||
|
? ReadonlyArray<DeepPartial<U>>
|
||||||
|
: T extends {}
|
||||||
|
? { [K in keyof T]?: DeepPartial<T[K]> }
|
||||||
|
: Partial<T>;
|
||||||
|
|
||||||
|
function longToNumber(long: Long): number {
|
||||||
|
if (long.gt(Number.MAX_SAFE_INTEGER)) {
|
||||||
|
throw new globalThis.Error("Value is larger than Number.MAX_SAFE_INTEGER");
|
||||||
|
}
|
||||||
|
return long.toNumber();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_m0.util.Long !== Long) {
|
||||||
|
_m0.util.Long = Long as any;
|
||||||
|
_m0.configure();
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { BN } from "bn.js";
|
||||||
|
import { derive } from "ecies-geth";
|
||||||
|
import { ec } from "elliptic";
|
||||||
|
import { bufToHex } from "js-waku/build/main/lib/utils";
|
||||||
|
|
||||||
|
import { idToContentTopic } from "./contentTopic";
|
||||||
|
import { hexToBuf } from "./utils";
|
||||||
|
|
||||||
|
import { Identity } from ".";
|
||||||
|
|
||||||
|
const EC = new ec("secp256k1");
|
||||||
|
const partitionsNum = new BN(5000);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the partitioned topic https://specs.status.im/spec/3#partitioned-topic
|
||||||
|
* @param publicKey Public key of recipient
|
||||||
|
* @returns string The Waku v2 Content Topic.
|
||||||
|
*/
|
||||||
|
export function getPartitionedTopic(publicKey: string): string {
|
||||||
|
const key = EC.keyFromPublic(publicKey.slice(2), "hex");
|
||||||
|
const X = key.getPublic().getX();
|
||||||
|
|
||||||
|
const partition = X.mod(partitionsNum);
|
||||||
|
|
||||||
|
const partitionTopic = `contact-discovery-${partition.toString()}`;
|
||||||
|
|
||||||
|
return idToContentTopic(partitionTopic);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the negotiated topic https://specs.status.im/spec/3#negotiated-topic
|
||||||
|
* @param identity identity of user
|
||||||
|
* @param publicKey Public key of recipient
|
||||||
|
* @returns string The Waku v2 Content Topic.
|
||||||
|
*/
|
||||||
|
export async function getNegotiatedTopic(
|
||||||
|
identity: Identity,
|
||||||
|
publicKey: string
|
||||||
|
): Promise<string> {
|
||||||
|
const key = EC.keyFromPublic(publicKey.slice(2), "hex");
|
||||||
|
const sharedSecret = await derive(
|
||||||
|
Buffer.from(identity.privateKey),
|
||||||
|
hexToBuf(key.getPublic("hex"))
|
||||||
|
);
|
||||||
|
return idToContentTopic(bufToHex(sharedSecret));
|
||||||
|
}
|
|
@ -0,0 +1,172 @@
|
||||||
|
import { Reader } from "protobufjs";
|
||||||
|
import { v4 as uuidV4 } from "uuid";
|
||||||
|
|
||||||
|
import { Identity } from "..";
|
||||||
|
import * as proto from "../proto/communities/v1/membership_update_message";
|
||||||
|
import { bufToHex, hexToBuf } from "../utils";
|
||||||
|
|
||||||
|
export class MembershipUpdateEvent {
|
||||||
|
public constructor(public proto: proto.MembershipUpdateEvent) {}
|
||||||
|
|
||||||
|
static decode(bytes: Uint8Array): MembershipUpdateEvent {
|
||||||
|
const protoBuf = proto.MembershipUpdateEvent.decode(Reader.create(bytes));
|
||||||
|
return new MembershipUpdateEvent(protoBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
encode(): Uint8Array {
|
||||||
|
return proto.MembershipUpdateEvent.encode(this.proto).finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
public get members(): string[] {
|
||||||
|
return this.proto.members;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get name(): string {
|
||||||
|
return this.proto.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get clock(): number {
|
||||||
|
return this.proto.clock;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get type(): proto.MembershipUpdateEvent_EventType {
|
||||||
|
return this.proto.type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MembershipUpdateMessage {
|
||||||
|
private clock: number = Date.now();
|
||||||
|
private identity: Identity = Identity.generate();
|
||||||
|
public constructor(public proto: proto.MembershipUpdateMessage) {}
|
||||||
|
|
||||||
|
public static create(
|
||||||
|
chatId: string,
|
||||||
|
identity: Identity
|
||||||
|
): MembershipUpdateMessage {
|
||||||
|
const partial = proto.MembershipUpdateMessage.fromPartial({
|
||||||
|
chatId,
|
||||||
|
events: [],
|
||||||
|
});
|
||||||
|
const newMessage = new MembershipUpdateMessage(partial);
|
||||||
|
newMessage.clock = Date.now();
|
||||||
|
newMessage.identity = identity;
|
||||||
|
return newMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
private addEvent(event: MembershipUpdateEvent): void {
|
||||||
|
const encEvent = event.encode();
|
||||||
|
const eventToSign = Buffer.concat([hexToBuf(this.proto.chatId), encEvent]);
|
||||||
|
const signature = this.identity.sign(eventToSign);
|
||||||
|
this.proto.events.push(Buffer.concat([signature, encEvent]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static createChat(
|
||||||
|
identity: Identity,
|
||||||
|
members: string[],
|
||||||
|
name?: string
|
||||||
|
): MembershipUpdateMessage {
|
||||||
|
const chatId = `${uuidV4()}-${bufToHex(identity.publicKey)}`;
|
||||||
|
|
||||||
|
const message = this.create(chatId, identity);
|
||||||
|
const type = proto.MembershipUpdateEvent_EventType.CHAT_CREATED;
|
||||||
|
const event = new MembershipUpdateEvent({
|
||||||
|
clock: message.clock,
|
||||||
|
members,
|
||||||
|
name: name ?? "",
|
||||||
|
type,
|
||||||
|
});
|
||||||
|
message.addEvent(event);
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public addNameChangeEvent(name: string): void {
|
||||||
|
const type = proto.MembershipUpdateEvent_EventType.NAME_CHANGED;
|
||||||
|
const event = new MembershipUpdateEvent({
|
||||||
|
clock: this.clock,
|
||||||
|
members: [],
|
||||||
|
name: name,
|
||||||
|
type,
|
||||||
|
});
|
||||||
|
this.addEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public addMembersAddedEvent(members: string[]): void {
|
||||||
|
const type = proto.MembershipUpdateEvent_EventType.MEMBERS_ADDED;
|
||||||
|
const event = new MembershipUpdateEvent({
|
||||||
|
clock: this.clock,
|
||||||
|
members,
|
||||||
|
name: "",
|
||||||
|
type,
|
||||||
|
});
|
||||||
|
this.addEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public addMemberJoinedEvent(member: string): void {
|
||||||
|
const type = proto.MembershipUpdateEvent_EventType.MEMBER_JOINED;
|
||||||
|
const event = new MembershipUpdateEvent({
|
||||||
|
clock: this.clock,
|
||||||
|
members: [member],
|
||||||
|
name: "",
|
||||||
|
type,
|
||||||
|
});
|
||||||
|
this.addEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public addMemberRemovedEvent(member: string): void {
|
||||||
|
const type = proto.MembershipUpdateEvent_EventType.MEMBER_REMOVED;
|
||||||
|
const event = new MembershipUpdateEvent({
|
||||||
|
clock: this.clock,
|
||||||
|
members: [member],
|
||||||
|
name: "",
|
||||||
|
type,
|
||||||
|
});
|
||||||
|
this.addEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public addAdminsAddedEvent(members: string[]): void {
|
||||||
|
const type = proto.MembershipUpdateEvent_EventType.ADMINS_ADDED;
|
||||||
|
const event = new MembershipUpdateEvent({
|
||||||
|
clock: this.clock,
|
||||||
|
members,
|
||||||
|
name: "",
|
||||||
|
type,
|
||||||
|
});
|
||||||
|
this.addEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public addAdminRemovedEvent(member: string): void {
|
||||||
|
const type = proto.MembershipUpdateEvent_EventType.ADMINS_ADDED;
|
||||||
|
const event = new MembershipUpdateEvent({
|
||||||
|
clock: this.clock,
|
||||||
|
members: [member],
|
||||||
|
name: "",
|
||||||
|
type,
|
||||||
|
});
|
||||||
|
this.addEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static decode(bytes: Uint8Array): MembershipUpdateMessage {
|
||||||
|
const protoBuf = proto.MembershipUpdateMessage.decode(Reader.create(bytes));
|
||||||
|
return new MembershipUpdateMessage(protoBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get events(): {
|
||||||
|
sig: Uint8Array;
|
||||||
|
event: MembershipUpdateEvent;
|
||||||
|
}[] {
|
||||||
|
return this.proto.events.map((bufArray) => {
|
||||||
|
return {
|
||||||
|
sig: bufArray.slice(0, 65),
|
||||||
|
event: MembershipUpdateEvent.decode(bufArray.slice(65)),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public get chatId(): string {
|
||||||
|
return this.proto.chatId;
|
||||||
|
}
|
||||||
|
|
||||||
|
encode(): Uint8Array {
|
||||||
|
return proto.MembershipUpdateMessage.encode(this.proto).finish();
|
||||||
|
}
|
||||||
|
}
|
104
yarn.lock
104
yarn.lock
|
@ -2,7 +2,7 @@
|
||||||
# Manual changes might be lost - proceed with caution!
|
# Manual changes might be lost - proceed with caution!
|
||||||
|
|
||||||
__metadata:
|
__metadata:
|
||||||
version: 4
|
version: 5
|
||||||
cacheKey: 8
|
cacheKey: 8
|
||||||
|
|
||||||
"@babel/code-frame@npm:7.12.11":
|
"@babel/code-frame@npm:7.12.11":
|
||||||
|
@ -234,6 +234,7 @@ __metadata:
|
||||||
"@typescript-eslint/eslint-plugin": ^4.29.0
|
"@typescript-eslint/eslint-plugin": ^4.29.0
|
||||||
"@typescript-eslint/parser": ^4.29.0
|
"@typescript-eslint/parser": ^4.29.0
|
||||||
assert: ^2.0.0
|
assert: ^2.0.0
|
||||||
|
browserify-zlib: ^0.2.0
|
||||||
buffer: ^6.0.3
|
buffer: ^6.0.3
|
||||||
chai: ^4.3.4
|
chai: ^4.3.4
|
||||||
crypto-browserify: ^3.12.0
|
crypto-browserify: ^3.12.0
|
||||||
|
@ -245,6 +246,7 @@ __metadata:
|
||||||
file-loader: ^6.2.0
|
file-loader: ^6.2.0
|
||||||
fork-ts-checker-webpack-plugin: ^6.3.1
|
fork-ts-checker-webpack-plugin: ^6.3.1
|
||||||
html-webpack-plugin: ^5.3.2
|
html-webpack-plugin: ^5.3.2
|
||||||
|
https-browserify: ^1.0.0
|
||||||
jsdom: ^16.7.0
|
jsdom: ^16.7.0
|
||||||
jsdom-global: ^3.0.2
|
jsdom-global: ^3.0.2
|
||||||
mocha: ^9.0.3
|
mocha: ^9.0.3
|
||||||
|
@ -257,6 +259,7 @@ __metadata:
|
||||||
rimraf: ^3.0.2
|
rimraf: ^3.0.2
|
||||||
source-map-loader: ^3.0.0
|
source-map-loader: ^3.0.0
|
||||||
stream-browserify: ^3.0.0
|
stream-browserify: ^3.0.0
|
||||||
|
stream-http: ^3.2.0
|
||||||
style-loader: ^3.3.0
|
style-loader: ^3.3.0
|
||||||
styled-components: ^5.3.1
|
styled-components: ^5.3.1
|
||||||
ts-loader: ^9.2.5
|
ts-loader: ^9.2.5
|
||||||
|
@ -747,6 +750,15 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@types/bn.js@npm:*, @types/bn.js@npm:^5.1.0":
|
||||||
|
version: 5.1.0
|
||||||
|
resolution: "@types/bn.js@npm:5.1.0"
|
||||||
|
dependencies:
|
||||||
|
"@types/node": "*"
|
||||||
|
checksum: 1dc1cbbd7a1e8bf3614752e9602f558762a901031f499f3055828b5e3e2bba16e5b88c27b3c4152ad795248fbe4086c731a5c4b0f29bb243f1875beeeabee59c
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@types/chai@npm:^4.2.21, @types/chai@npm:^4.2.22":
|
"@types/chai@npm:^4.2.21, @types/chai@npm:^4.2.22":
|
||||||
version: 4.2.22
|
version: 4.2.22
|
||||||
resolution: "@types/chai@npm:4.2.22"
|
resolution: "@types/chai@npm:4.2.22"
|
||||||
|
@ -763,6 +775,15 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@types/elliptic@npm:^6.4.14":
|
||||||
|
version: 6.4.14
|
||||||
|
resolution: "@types/elliptic@npm:6.4.14"
|
||||||
|
dependencies:
|
||||||
|
"@types/bn.js": "*"
|
||||||
|
checksum: d5a64f540e0ed4b74a12dfa5cc88c0aa7b531eab3b7a9fab17948ffbfc6e01814230e63d7417ce1b607dbd8b5d70e1b64f5afac632deabf96e44875aaac0ae1b
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@types/emoji-mart@npm:^3.0.6":
|
"@types/emoji-mart@npm:^3.0.6":
|
||||||
version: 3.0.6
|
version: 3.0.6
|
||||||
resolution: "@types/emoji-mart@npm:3.0.6"
|
resolution: "@types/emoji-mart@npm:3.0.6"
|
||||||
|
@ -1054,6 +1075,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@types/uuid@npm:^8.3.3":
|
||||||
|
version: 8.3.3
|
||||||
|
resolution: "@types/uuid@npm:8.3.3"
|
||||||
|
checksum: 3f340155bb1161f9ffa7163926d6222fea8f3505c23619b72bcc230883354926672c08a06510c7a543f73553c792a84444cc1744cff631b4ba988763e4bd7a8f
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@types/yargs-parser@npm:*":
|
"@types/yargs-parser@npm:*":
|
||||||
version: 20.2.1
|
version: 20.2.1
|
||||||
resolution: "@types/yargs-parser@npm:20.2.1"
|
resolution: "@types/yargs-parser@npm:20.2.1"
|
||||||
|
@ -2182,7 +2210,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"bn.js@npm:^5.0.0, bn.js@npm:^5.1.1":
|
"bn.js@npm:^5.0.0, bn.js@npm:^5.1.1, bn.js@npm:^5.2.0":
|
||||||
version: 5.2.0
|
version: 5.2.0
|
||||||
resolution: "bn.js@npm:5.2.0"
|
resolution: "bn.js@npm:5.2.0"
|
||||||
checksum: 6117170393200f68b35a061ecbf55d01dd989302e7b3c798a3012354fa638d124f0b2f79e63f77be5556be80322a09c40339eda6413ba7468524c0b6d4b4cb7a
|
checksum: 6117170393200f68b35a061ecbf55d01dd989302e7b3c798a3012354fa638d124f0b2f79e63f77be5556be80322a09c40339eda6413ba7468524c0b6d4b4cb7a
|
||||||
|
@ -2366,6 +2394,15 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"browserify-zlib@npm:^0.2.0":
|
||||||
|
version: 0.2.0
|
||||||
|
resolution: "browserify-zlib@npm:0.2.0"
|
||||||
|
dependencies:
|
||||||
|
pako: ~1.0.5
|
||||||
|
checksum: 5cd9d6a665190fedb4a97dfbad8dabc8698d8a507298a03f42c734e96d58ca35d3c7d4085e283440bbca1cd1938cff85031728079bedb3345310c58ab1ec92d6
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"browserslist@npm:^4.14.5":
|
"browserslist@npm:^4.14.5":
|
||||||
version: 4.17.1
|
version: 4.17.1
|
||||||
resolution: "browserslist@npm:4.17.1"
|
resolution: "browserslist@npm:4.17.1"
|
||||||
|
@ -2412,6 +2449,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"builtin-status-codes@npm:^3.0.0":
|
||||||
|
version: 3.0.0
|
||||||
|
resolution: "builtin-status-codes@npm:3.0.0"
|
||||||
|
checksum: 1119429cf4b0d57bf76b248ad6f529167d343156ebbcc4d4e4ad600484f6bc63002595cbb61b67ad03ce55cd1d3c4711c03bbf198bf24653b8392420482f3773
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"bytes@npm:3.0.0":
|
"bytes@npm:3.0.0":
|
||||||
version: 3.0.0
|
version: 3.0.0
|
||||||
resolution: "bytes@npm:3.0.0"
|
resolution: "bytes@npm:3.0.0"
|
||||||
|
@ -4818,25 +4862,26 @@ fsevents@^1.2.7:
|
||||||
bindings: ^1.5.0
|
bindings: ^1.5.0
|
||||||
nan: ^2.12.1
|
nan: ^2.12.1
|
||||||
checksum: ae855aa737aaa2f9167e9f70417cf6e45a5cd11918e1fee9923709a0149be52416d765433b4aeff56c789b1152e718cd1b13ddec6043b78cdda68260d86383c1
|
checksum: ae855aa737aaa2f9167e9f70417cf6e45a5cd11918e1fee9923709a0149be52416d765433b4aeff56c789b1152e718cd1b13ddec6043b78cdda68260d86383c1
|
||||||
|
conditions: os=darwin
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"fsevents@patch:fsevents@^1.2.7#~builtin<compat/fsevents>":
|
"fsevents@patch:fsevents@^1.2.7#~builtin<compat/fsevents>":
|
||||||
version: 1.2.13
|
version: 1.2.13
|
||||||
resolution: "fsevents@patch:fsevents@npm%3A1.2.13#~builtin<compat/fsevents>::version=1.2.13&hash=1cc4b2"
|
resolution: "fsevents@patch:fsevents@npm%3A1.2.13#~builtin<compat/fsevents>::version=1.2.13&hash=18f3a7"
|
||||||
dependencies:
|
dependencies:
|
||||||
bindings: ^1.5.0
|
bindings: ^1.5.0
|
||||||
nan: ^2.12.1
|
nan: ^2.12.1
|
||||||
checksum: b264407498db2cfdcc2a05287334a4160c985a88e4a989e2f2f8dcc6afc8b04a4fcd82c797266442452e11c1fb07d7747d138b078fe4bb1f8f4fd2a6f2484d7e
|
conditions: os=darwin
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"fsevents@patch:fsevents@~2.3.2#~builtin<compat/fsevents>":
|
"fsevents@patch:fsevents@~2.3.2#~builtin<compat/fsevents>":
|
||||||
version: 2.3.2
|
version: 2.3.2
|
||||||
resolution: "fsevents@patch:fsevents@npm%3A2.3.2#~builtin<compat/fsevents>::version=2.3.2&hash=1cc4b2"
|
resolution: "fsevents@patch:fsevents@npm%3A2.3.2#~builtin<compat/fsevents>::version=2.3.2&hash=18f3a7"
|
||||||
dependencies:
|
dependencies:
|
||||||
node-gyp: latest
|
node-gyp: latest
|
||||||
checksum: 78db9daf1f6526a49cefee3917cc988f62dc7f25b5dd80ad6de4ffc4af7f0cab7491ac737626ff53e482a111bc53aac9e411fe3602458eca36f6a003ecf69c16
|
conditions: os=darwin
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
@ -4846,6 +4891,7 @@ fsevents@~2.3.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
node-gyp: latest
|
node-gyp: latest
|
||||||
checksum: 97ade64e75091afee5265e6956cb72ba34db7819b4c3e94c431d4be2b19b8bb7a2d4116da417950c3425f17c8fe693d25e20212cac583ac1521ad066b77ae31f
|
checksum: 97ade64e75091afee5265e6956cb72ba34db7819b4c3e94c431d4be2b19b8bb7a2d4116da417950c3425f17c8fe693d25e20212cac583ac1521ad066b77ae31f
|
||||||
|
conditions: os=darwin
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
@ -5485,6 +5531,13 @@ fsevents@~2.3.2:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"https-browserify@npm:^1.0.0":
|
||||||
|
version: 1.0.0
|
||||||
|
resolution: "https-browserify@npm:1.0.0"
|
||||||
|
checksum: 09b35353e42069fde2435760d13f8a3fb7dd9105e358270e2e225b8a94f811b461edd17cb57594e5f36ec1218f121c160ddceeec6e8be2d55e01dcbbbed8cbae
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"https-proxy-agent@npm:^5.0.0":
|
"https-proxy-agent@npm:^5.0.0":
|
||||||
version: 5.0.0
|
version: 5.0.0
|
||||||
resolution: "https-proxy-agent@npm:5.0.0"
|
resolution: "https-proxy-agent@npm:5.0.0"
|
||||||
|
@ -8584,6 +8637,13 @@ fsevents@~2.3.2:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"pako@npm:~1.0.5":
|
||||||
|
version: 1.0.11
|
||||||
|
resolution: "pako@npm:1.0.11"
|
||||||
|
checksum: 1be2bfa1f807608c7538afa15d6f25baa523c30ec870a3228a89579e474a4d992f4293859524e46d5d87fd30fa17c5edf34dbef0671251d9749820b488660b16
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"param-case@npm:^3.0.3":
|
"param-case@npm:^3.0.3":
|
||||||
version: 3.0.4
|
version: 3.0.4
|
||||||
resolution: "param-case@npm:3.0.4"
|
resolution: "param-case@npm:3.0.4"
|
||||||
|
@ -9709,21 +9769,21 @@ resolve@^2.0.0-next.3:
|
||||||
|
|
||||||
"resolve@patch:resolve@^1.10.0#~builtin<compat/resolve>, resolve@patch:resolve@^1.20.0#~builtin<compat/resolve>, resolve@patch:resolve@^1.9.0#~builtin<compat/resolve>":
|
"resolve@patch:resolve@^1.10.0#~builtin<compat/resolve>, resolve@patch:resolve@^1.20.0#~builtin<compat/resolve>, resolve@patch:resolve@^1.9.0#~builtin<compat/resolve>":
|
||||||
version: 1.20.0
|
version: 1.20.0
|
||||||
resolution: "resolve@patch:resolve@npm%3A1.20.0#~builtin<compat/resolve>::version=1.20.0&hash=00b1ff"
|
resolution: "resolve@patch:resolve@npm%3A1.20.0#~builtin<compat/resolve>::version=1.20.0&hash=07638b"
|
||||||
dependencies:
|
dependencies:
|
||||||
is-core-module: ^2.2.0
|
is-core-module: ^2.2.0
|
||||||
path-parse: ^1.0.6
|
path-parse: ^1.0.6
|
||||||
checksum: bed00be983cd20a8af0e7840664f655c4b269786dbd9595c5f156cd9d8a0050e65cdbbbdafc30ee9b6245b230c78a2c8ab6447a52545b582f476c29adb188cc5
|
checksum: a0dd7d16a8e47af23afa9386df2dff10e3e0debb2c7299a42e581d9d9b04d7ad5d2c53f24f1e043f7b3c250cbdc71150063e53d0b6559683d37f790b7c8c3cd5
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"resolve@patch:resolve@^2.0.0-next.3#~builtin<compat/resolve>":
|
"resolve@patch:resolve@^2.0.0-next.3#~builtin<compat/resolve>":
|
||||||
version: 2.0.0-next.3
|
version: 2.0.0-next.3
|
||||||
resolution: "resolve@patch:resolve@npm%3A2.0.0-next.3#~builtin<compat/resolve>::version=2.0.0-next.3&hash=00b1ff"
|
resolution: "resolve@patch:resolve@npm%3A2.0.0-next.3#~builtin<compat/resolve>::version=2.0.0-next.3&hash=07638b"
|
||||||
dependencies:
|
dependencies:
|
||||||
is-core-module: ^2.2.0
|
is-core-module: ^2.2.0
|
||||||
path-parse: ^1.0.6
|
path-parse: ^1.0.6
|
||||||
checksum: eb88c5e53843bc022215744307a5f5664446c0fdb8f43c33456dce98d5ee6b3162d0cd0a177bb6f1c3d5c8bf01391ac7ab2de0e936e35318725fb40ba7efdaf6
|
checksum: 21684b4d99a4877337cdbd5484311c811b3e8910edb5d868eec85c6e6550b0f570d911f9a384f9e176172d6713f2715bd0b0887fa512cb8c6aeece018de6a9f8
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
@ -10494,15 +10554,20 @@ resolve@^2.0.0-next.3:
|
||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "status-communities@workspace:packages/status-communities"
|
resolution: "status-communities@workspace:packages/status-communities"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
"@types/bn.js": ^5.1.0
|
||||||
"@types/chai": ^4.2.22
|
"@types/chai": ^4.2.22
|
||||||
|
"@types/elliptic": ^6.4.14
|
||||||
"@types/mocha": ^9.0.0
|
"@types/mocha": ^9.0.0
|
||||||
"@types/pbkdf2": ^3.1.0
|
"@types/pbkdf2": ^3.1.0
|
||||||
"@types/secp256k1": ^4.0.3
|
"@types/secp256k1": ^4.0.3
|
||||||
|
"@types/uuid": ^8.3.3
|
||||||
"@typescript-eslint/eslint-plugin": ^4.31.1
|
"@typescript-eslint/eslint-plugin": ^4.31.1
|
||||||
"@typescript-eslint/parser": ^4.31.1
|
"@typescript-eslint/parser": ^4.31.1
|
||||||
|
bn.js: ^5.2.0
|
||||||
buffer: ^6.0.3
|
buffer: ^6.0.3
|
||||||
chai: ^4.3.4
|
chai: ^4.3.4
|
||||||
ecies-geth: ^1.5.3
|
ecies-geth: ^1.5.3
|
||||||
|
elliptic: ^6.5.4
|
||||||
eslint: ^7.32.0
|
eslint: ^7.32.0
|
||||||
eslint-config-prettier: ^8.3.0
|
eslint-config-prettier: ^8.3.0
|
||||||
eslint-import-resolver-node: ^0.3.6
|
eslint-import-resolver-node: ^0.3.6
|
||||||
|
@ -10520,6 +10585,7 @@ resolve@^2.0.0-next.3:
|
||||||
ts-node: ^10.2.1
|
ts-node: ^10.2.1
|
||||||
ts-proto: ^1.83.0
|
ts-proto: ^1.83.0
|
||||||
typescript: ^4.4.3
|
typescript: ^4.4.3
|
||||||
|
uuid: ^8.3.2
|
||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
|
@ -10540,6 +10606,18 @@ resolve@^2.0.0-next.3:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"stream-http@npm:^3.2.0":
|
||||||
|
version: 3.2.0
|
||||||
|
resolution: "stream-http@npm:3.2.0"
|
||||||
|
dependencies:
|
||||||
|
builtin-status-codes: ^3.0.0
|
||||||
|
inherits: ^2.0.4
|
||||||
|
readable-stream: ^3.6.0
|
||||||
|
xtend: ^4.0.2
|
||||||
|
checksum: c9b78453aeb0c84fcc59555518ac62bacab9fa98e323e7b7666e5f9f58af8f3155e34481078509b02929bd1268427f664d186604cdccee95abc446099b339f83
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"stream-to-it@npm:^0.2.2":
|
"stream-to-it@npm:^0.2.2":
|
||||||
version: 0.2.4
|
version: 0.2.4
|
||||||
resolution: "stream-to-it@npm:0.2.4"
|
resolution: "stream-to-it@npm:0.2.4"
|
||||||
|
@ -11329,11 +11407,11 @@ resolve@^2.0.0-next.3:
|
||||||
|
|
||||||
"typescript@patch:typescript@^4.3.5#~builtin<compat/typescript>, typescript@patch:typescript@^4.4.3#~builtin<compat/typescript>":
|
"typescript@patch:typescript@^4.3.5#~builtin<compat/typescript>, typescript@patch:typescript@^4.4.3#~builtin<compat/typescript>":
|
||||||
version: 4.4.3
|
version: 4.4.3
|
||||||
resolution: "typescript@patch:typescript@npm%3A4.4.3#~builtin<compat/typescript>::version=4.4.3&hash=32657b"
|
resolution: "typescript@patch:typescript@npm%3A4.4.3#~builtin<compat/typescript>::version=4.4.3&hash=ddd1e8"
|
||||||
bin:
|
bin:
|
||||||
tsc: bin/tsc
|
tsc: bin/tsc
|
||||||
tsserver: bin/tsserver
|
tsserver: bin/tsserver
|
||||||
checksum: 28ab98313afab46788ff41014fdb5932430ada6e03cf9e92ac47f406526a2cac1ae2894834e7da61e46b7429318e9c47f45ba8de323332f0cb9af99b72ebae74
|
checksum: 79f5c13d21c9dea3eb44d2b7002ff25a0569fefc432e083d65a360e3aca990aca25fc733e14aa6883b5e9a68e3e2f0330a34123e048806f91d701732ece00e6f
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
@ -12159,7 +12237,7 @@ resolve@^2.0.0-next.3:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"xtend@npm:~4.0.1":
|
"xtend@npm:^4.0.2, xtend@npm:~4.0.1":
|
||||||
version: 4.0.2
|
version: 4.0.2
|
||||||
resolution: "xtend@npm:4.0.2"
|
resolution: "xtend@npm:4.0.2"
|
||||||
checksum: ac5dfa738b21f6e7f0dd6e65e1b3155036d68104e67e5d5d1bde74892e327d7e5636a076f625599dc394330a731861e87343ff184b0047fef1360a7ec0a5a36a
|
checksum: ac5dfa738b21f6e7f0dd6e65e1b3155036d68104e67e5d5d1bde74892e327d7e5636a076f625599dc394330a731861e87343ff184b0047fef1360a7ec0a5a36a
|
||||||
|
|
Loading…
Reference in New Issue