add idToContentTopic
This commit is contained in:
parent
6e97051de9
commit
817b877634
|
@ -1,3 +1,4 @@
|
|||
**/dist
|
||||
**/node_modules
|
||||
**/protos
|
||||
**/proto
|
||||
|
|
|
@ -4,10 +4,12 @@
|
|||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Launch status-js",
|
||||
"program": "${workspaceFolder}/packages/status-js/src/index.ts",
|
||||
"name": "Launch Client",
|
||||
"program": "${workspaceFolder}/packages/status-js/src/debug.ts",
|
||||
"preLaunchTask": "npm: build:status-js",
|
||||
"outFiles": ["${workspaceFolder}/packages/status-js/dist/index.js"],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/packages/status-js/dist/index.js"
|
||||
],
|
||||
"sourceMaps": true,
|
||||
"smartStep": true,
|
||||
"internalConsoleOptions": "openOnSessionStart",
|
||||
|
|
|
@ -7,7 +7,12 @@ import { Community } from '@status-im/react'
|
|||
// TODO?: import/rename CommunityAndMessenger, or Status even
|
||||
|
||||
const App = () => {
|
||||
return <Community publicKey="<YOUR_COMMUNITY_KEY>" theme="light" />
|
||||
return (
|
||||
<Community
|
||||
publicKey="0x029f196bbfef4fa6a5eb81dd802133a63498325445ca1af1d154b1bb4542955133"
|
||||
theme="light"
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
render(
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"source": "./index.html",
|
||||
"browserslist": "> 0.5%, last 2 versions, not dead, not ios_saf < 13",
|
||||
"scripts": {
|
||||
"dev": "parcel --https",
|
||||
"dev": "parcel --https --host 192.168.86.38 --port 8080",
|
||||
"prebuild": "rm -rf dist",
|
||||
"build": "parcel build"
|
||||
},
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
|
||||
module.exports = {
|
||||
// preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
extensionsToTreatAsEsm: ['.ts'],
|
||||
preset: 'ts-jest/presets/default-esm',
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
useESM: true,
|
||||
tsconfig: 'tsconfig.base.json',
|
||||
},
|
||||
},
|
||||
moduleNameMapper: {
|
||||
'^(\\.{1,2}/.*)\\.js$': '$1',
|
||||
},
|
||||
}
|
15
package.json
15
package.json
|
@ -1,4 +1,8 @@
|
|||
{
|
||||
"alias": {
|
||||
"protons-runtime": "./node_modules/protons-runtime/dist/src/index.js",
|
||||
"uint8arraylist": "./node_modules/uint8arraylist/dist/src/index.js"
|
||||
},
|
||||
"private": true,
|
||||
"workspaces": [
|
||||
"packages/*",
|
||||
|
@ -9,7 +13,7 @@
|
|||
"prepare": "husky install",
|
||||
"fix": "run-s 'fix:*' && wsrun -e -c -s fix",
|
||||
"build": "wsrun -e -c -s build",
|
||||
"build:status-js": "yarn workspace @status-im/js build",
|
||||
"build:status-js": "yarn workspace @status-im/js debug",
|
||||
"build:status-react": "yarn workspace @status-im/react build",
|
||||
"lint": "eslint 'packages/**/*.{ts,tsx}'",
|
||||
"lint:fix": "eslint 'packages/**/*.{ts,tsx}' --fix",
|
||||
|
@ -23,10 +27,10 @@
|
|||
"@babel/preset-env": "^7.18.2",
|
||||
"@babel/preset-typescript": "^7.17.12",
|
||||
"@parcel/config-default": "^2.6.0",
|
||||
"@parcel/packager-ts": "2.3.2",
|
||||
"@parcel/packager-ts": "^2.6.0",
|
||||
"@parcel/transformer-js": "^2.6.0",
|
||||
"@parcel/transformer-react-refresh-wrap": "^2.6.0",
|
||||
"@parcel/transformer-typescript-types": "2.3.2",
|
||||
"@parcel/transformer-typescript-types": "^2.6.0",
|
||||
"@types/jest": "^27.5.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.12.0",
|
||||
"@typescript-eslint/parser": "^5.12.0",
|
||||
|
@ -47,10 +51,11 @@
|
|||
"jest": "^28.1.0",
|
||||
"lint-staged": "^12.3.4",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"parcel": "^2.3.2",
|
||||
"parcel": "^2.6.0",
|
||||
"prettier": "^2.5.1",
|
||||
"typescript": "^4.5.5",
|
||||
"wsrun": "^5.2.4"
|
||||
"wsrun": "^5.2.4",
|
||||
"ts-jest": "^28.0.4"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{ts,tsx,js,jsx}": [
|
||||
|
|
|
@ -1,27 +1,23 @@
|
|||
import { createClient } from '../src/client'
|
||||
|
||||
import { Community } from '../src/community'
|
||||
// import { Messenger } from '../src/messenger'
|
||||
|
||||
const COMMUNITY_PUBLIC_KEY =
|
||||
'0x029dd5fecbd689dc11e2a5b399afed92cf1fab65d315b883efca753e8f3882f3bd' // compressed; A catchy name
|
||||
// '0x02c788e419b56c714460220bedadc9c5d401ea10eee48d25ac81fc9a06fb75162e' // compressed; A boring name
|
||||
// const COMMUNITY_CHANNEL_KEY = '0x029dd5fecbd689dc11e2a5b399afed92cf1fab65d315b883efca753e8f3882f3bd06935bce-a863-4827-9990-1652ae375c89' // 06935bce-a863-4827-9990-1652ae375c89; #channel
|
||||
const COMMUNITY_CHANNEL_KEY = '6102c603-3246-4b90-986d-43c1b87b165f' // #random; UUID
|
||||
'0x029f196bbfef4fa6a5eb81dd802133a63498325445ca1af1d154b1bb4542955133' // Boring community
|
||||
// '0x0243611cc13cc4e4390180fe8fd35234ab0fe2a7ba8d32e8ae5dd23b60ac7ec177'
|
||||
// '0x02e7102c85ed78e5be30124f8f52014b1135f972c383f55f83ec8ff50436cd1260'
|
||||
const CHANNEL_ID = '00d3f525-a0cf-4c40-832d-543ec9f8188b' // messages
|
||||
|
||||
;(async () => {
|
||||
const client = await createClient()
|
||||
const client = await createClient(COMMUNITY_PUBLIC_KEY)
|
||||
|
||||
// Community (e.g. name, description, permissions, members, channels, channel messages)
|
||||
const community = await Community.instantiateCommunity(
|
||||
COMMUNITY_PUBLIC_KEY,
|
||||
client
|
||||
await client.start()
|
||||
|
||||
// const community = await client.community.fetchCommunity()
|
||||
|
||||
// client.community.onCommunityUpdate(() => console.log("community:update"))
|
||||
// client.community.onChannelUpdate(() => console.log("channel:update"))
|
||||
client.community.onChannelMessages(CHANNEL_ID, () =>
|
||||
console.log('channel:message')
|
||||
)
|
||||
|
||||
// // Messenger/Messages (e.g. direct messages)
|
||||
// const messenger = await Messenger.create(, client)
|
||||
|
||||
// history
|
||||
|
||||
await client.stop()
|
||||
// await client.stop()
|
||||
})()
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
version: v1beta1
|
||||
|
||||
plugins:
|
||||
- name: ts_proto
|
||||
out: ./src/proto
|
||||
opt: grpc_js,esModuleInterop=true
|
|
@ -0,0 +1,9 @@
|
|||
version: v1beta1
|
||||
|
||||
build:
|
||||
roots:
|
||||
- ./proto
|
||||
lint:
|
||||
except:
|
||||
- ENUM_ZERO_VALUE_SUFFIX
|
||||
- ENUM_VALUE_PREFIX
|
|
@ -24,15 +24,19 @@
|
|||
},
|
||||
"scripts": {
|
||||
"prebuild": "rm -rf dist",
|
||||
"dev": "parcel",
|
||||
"dev": "parcel --host 192.168.86.38 --port 8080",
|
||||
"build": "parcel build",
|
||||
"debug": "parcel build src/debug.ts --dist-dir output",
|
||||
"build:vite": "vite build",
|
||||
"build:types": "tsc --emitDeclarationOnly",
|
||||
"build:protos": "protons protos/*.proto",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"lint": "eslint src",
|
||||
"format": "prettier --write src",
|
||||
"clean": "rm -rf node_modules && rm -rf dist"
|
||||
"clean": "rm -rf node_modules && rm -rf dist",
|
||||
"proto": "run-s 'proto:*'",
|
||||
"proto:lint": "buf lint",
|
||||
"proto:build": "buf generate"
|
||||
},
|
||||
"dependencies": {
|
||||
"bn.js": "^5.2.0",
|
||||
|
@ -43,6 +47,7 @@
|
|||
"ethereum-cryptography": "^1.0.3",
|
||||
"js-sha3": "^0.8.0",
|
||||
"js-waku": "^0.23.0",
|
||||
"lodash": "^4.17.21",
|
||||
"long": "^5.2.0",
|
||||
"pbkdf2": "^3.1.2",
|
||||
"protobufjs": "^6.11.3",
|
||||
|
@ -53,11 +58,13 @@
|
|||
"devDependencies": {
|
||||
"@types/bn.js": "^5.1.0",
|
||||
"@types/elliptic": "^6.4.14",
|
||||
"@types/lodash": "^4.14.182",
|
||||
"@types/pbkdf2": "^3.1.0",
|
||||
"@types/secp256k1": "^4.0.3",
|
||||
"@types/uuid": "^8.3.3",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"protons": "^3.0.4",
|
||||
"ts-node": "^10.2.1"
|
||||
"ts-node": "^10.2.1",
|
||||
"ts-proto": "^1.115.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
syntax = "proto3";
|
||||
|
||||
package communities.v1;
|
||||
|
||||
import "communities/v1/chat_identity.proto";
|
||||
|
||||
message Grant {
|
||||
bytes community_id = 1;
|
||||
bytes member_id = 2;
|
||||
string chat_id = 3;
|
||||
uint64 clock = 4;
|
||||
}
|
||||
|
||||
message CommunityMember {
|
||||
enum Roles {
|
||||
UNKNOWN_ROLE = 0;
|
||||
ROLE_ALL = 1;
|
||||
ROLE_MANAGE_USERS = 2;
|
||||
}
|
||||
repeated Roles roles = 1;
|
||||
}
|
||||
|
||||
message CommunityPermissions {
|
||||
enum Access {
|
||||
UNKNOWN_ACCESS = 0;
|
||||
NO_MEMBERSHIP = 1;
|
||||
INVITATION_ONLY = 2;
|
||||
ON_REQUEST = 3;
|
||||
}
|
||||
|
||||
bool ens_only = 1;
|
||||
// https://gitlab.matrix.org/matrix-org/olm/blob/master/docs/megolm.md is a candidate for the algorithm to be used in case we want to have private communityal chats, lighter than pairwise encryption using the DR, less secure, but more efficient for large number of participants
|
||||
bool private = 2;
|
||||
Access access = 3;
|
||||
}
|
||||
|
||||
message CommunityDescription {
|
||||
uint64 clock = 1;
|
||||
map<string,CommunityMember> members = 2;
|
||||
CommunityPermissions permissions = 3;
|
||||
ChatIdentity identity = 5;
|
||||
map<string,CommunityChat> chats = 6;
|
||||
repeated string ban_list = 7;
|
||||
map<string,CommunityCategory> categories = 8;
|
||||
}
|
||||
|
||||
message CommunityChat {
|
||||
map<string,CommunityMember> members = 1;
|
||||
CommunityPermissions permissions = 2;
|
||||
ChatIdentity identity = 3;
|
||||
string category_id = 4;
|
||||
int32 position = 5;
|
||||
}
|
||||
|
||||
message CommunityCategory {
|
||||
string category_id = 1;
|
||||
string name = 2;
|
||||
int32 position = 3;
|
||||
}
|
||||
|
||||
message CommunityInvitation {
|
||||
bytes community_description = 1;
|
||||
bytes grant = 2;
|
||||
string chat_id = 3;
|
||||
bytes public_key = 4;
|
||||
}
|
||||
|
||||
message CommunityRequestToJoin {
|
||||
uint64 clock = 1;
|
||||
string ens_name = 2;
|
||||
string chat_id = 3;
|
||||
bytes community_id = 4;
|
||||
}
|
||||
|
||||
message CommunityRequestToJoinResponse {
|
||||
uint64 clock = 1;
|
||||
CommunityDescription community = 2;
|
||||
bool accepted = 3;
|
||||
bytes grant = 4;
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
syntax = "proto3";
|
||||
|
||||
message SignedPreKey {
|
||||
bytes signed_pre_key = 1;
|
||||
uint32 version = 2;
|
||||
uint32 protocol_version = 3;
|
||||
}
|
||||
|
||||
// X3DH prekey bundle
|
||||
message Bundle {
|
||||
// Identity key
|
||||
bytes identity = 1;
|
||||
// Installation id
|
||||
map<string,SignedPreKey> signed_pre_keys = 2;
|
||||
// Prekey signature
|
||||
bytes signature = 4;
|
||||
|
||||
// When the bundle was created locally
|
||||
int64 timestamp = 5;
|
||||
}
|
||||
|
||||
message BundleContainer {
|
||||
reserved 3;
|
||||
// X3DH prekey bundle
|
||||
Bundle bundle = 1;
|
||||
// Private signed prekey
|
||||
bytes private_signed_pre_key = 2;
|
||||
}
|
||||
|
||||
message DRHeader {
|
||||
// Current ratchet public key
|
||||
bytes key = 1;
|
||||
// Number of the message in the sending chain
|
||||
uint32 n = 2;
|
||||
// Length of the previous sending chain
|
||||
uint32 pn = 3;
|
||||
// Bundle ID
|
||||
bytes id = 4;
|
||||
}
|
||||
|
||||
message DHHeader {
|
||||
// Compressed ephemeral public key
|
||||
bytes key = 1;
|
||||
}
|
||||
|
||||
message X3DHHeader {
|
||||
reserved 3;
|
||||
// Ephemeral key used
|
||||
bytes key = 1;
|
||||
// Used bundle's signed prekey
|
||||
bytes id = 4;
|
||||
}
|
||||
|
||||
// Hash Ratchet Header
|
||||
message HRHeader {
|
||||
// community key ID
|
||||
uint32 key_id = 1;
|
||||
// Community message number for this key_id
|
||||
uint32 seq_no = 2;
|
||||
// Community ID
|
||||
string group_id = 3;
|
||||
}
|
||||
|
||||
// Direct message value
|
||||
message EncryptedMessageProtocol {
|
||||
X3DHHeader x3dh_header = 1;
|
||||
DRHeader dr_header = 2;
|
||||
DHHeader dh_header = 101;
|
||||
HRHeader hr_header = 102;
|
||||
// Encrypted payload
|
||||
bytes payload = 3;
|
||||
}
|
||||
|
||||
// Top-level protocol message
|
||||
message ProtocolMessage {
|
||||
// The device id of the sender
|
||||
string installation_id = 2;
|
||||
|
||||
// List of bundles
|
||||
repeated Bundle bundles = 3;
|
||||
|
||||
// One to one message, encrypted, indexed by installation_id
|
||||
// TODO map here is redundant in case of community messages
|
||||
map<string,EncryptedMessageProtocol> encrypted_message = 101;
|
||||
|
||||
// Public chats, not encrypted
|
||||
bytes public_message = 102;
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
syntax = "proto3";
|
||||
|
||||
message ApplicationMetadataMessage {
|
||||
// Signature of the payload field
|
||||
bytes signature = 1;
|
||||
// This is the encoded protobuf of the application level message, i.e ChatMessage
|
||||
bytes payload = 2;
|
||||
|
||||
// The type of protobuf message sent
|
||||
Type type = 3;
|
||||
|
||||
enum Type {
|
||||
TYPE_UNKNOWN_UNSPECIFIED = 0;
|
||||
TYPE_CHAT_MESSAGE = 1;
|
||||
TYPE_CONTACT_UPDATE = 2;
|
||||
TYPE_MEMBERSHIP_UPDATE_MESSAGE = 3;
|
||||
TYPE_PAIR_INSTALLATION = 4;
|
||||
TYPE_SYNC_INSTALLATION = 5;
|
||||
TYPE_REQUEST_ADDRESS_FOR_TRANSACTION = 6;
|
||||
TYPE_ACCEPT_REQUEST_ADDRESS_FOR_TRANSACTION = 7;
|
||||
TYPE_DECLINE_REQUEST_ADDRESS_FOR_TRANSACTION = 8;
|
||||
TYPE_REQUEST_TRANSACTION = 9;
|
||||
TYPE_SEND_TRANSACTION = 10;
|
||||
TYPE_DECLINE_REQUEST_TRANSACTION = 11;
|
||||
TYPE_SYNC_INSTALLATION_CONTACT = 12;
|
||||
TYPE_SYNC_INSTALLATION_ACCOUNT = 13;
|
||||
TYPE_SYNC_INSTALLATION_PUBLIC_CHAT = 14;
|
||||
TYPE_CONTACT_CODE_ADVERTISEMENT = 15;
|
||||
TYPE_PUSH_NOTIFICATION_REGISTRATION = 16;
|
||||
TYPE_PUSH_NOTIFICATION_REGISTRATION_RESPONSE = 17;
|
||||
TYPE_PUSH_NOTIFICATION_QUERY = 18;
|
||||
TYPE_PUSH_NOTIFICATION_QUERY_RESPONSE = 19;
|
||||
TYPE_PUSH_NOTIFICATION_REQUEST = 20;
|
||||
TYPE_PUSH_NOTIFICATION_RESPONSE = 21;
|
||||
TYPE_EMOJI_REACTION = 22;
|
||||
TYPE_GROUP_CHAT_INVITATION = 23;
|
||||
TYPE_CHAT_IDENTITY = 24;
|
||||
TYPE_COMMUNITY_DESCRIPTION = 25;
|
||||
TYPE_COMMUNITY_INVITATION = 26;
|
||||
TYPE_COMMUNITY_REQUEST_TO_JOIN = 27;
|
||||
TYPE_PIN_MESSAGE = 28;
|
||||
TYPE_EDIT_MESSAGE = 29;
|
||||
TYPE_STATUS_UPDATE = 30;
|
||||
TYPE_DELETE_MESSAGE = 31;
|
||||
TYPE_SYNC_INSTALLATION_COMMUNITY = 32;
|
||||
TYPE_ANONYMOUS_METRIC_BATCH = 33;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
/* eslint-disable import/export */
|
||||
/* eslint-disable @typescript-eslint/no-namespace */
|
||||
|
||||
import {
|
||||
enumeration,
|
||||
encodeMessage,
|
||||
decodeMessage,
|
||||
message,
|
||||
bytes,
|
||||
} from 'protons-runtime'
|
||||
import type { Codec } from 'protons-runtime'
|
||||
|
||||
export interface ApplicationMetadataMessage {
|
||||
signature: Uint8Array
|
||||
payload: Uint8Array
|
||||
type: ApplicationMetadataMessage.Type
|
||||
}
|
||||
|
||||
export namespace ApplicationMetadataMessage {
|
||||
export enum Type {
|
||||
TYPE_UNKNOWN_UNSPECIFIED = 'TYPE_UNKNOWN_UNSPECIFIED',
|
||||
TYPE_CHAT_MESSAGE = 'TYPE_CHAT_MESSAGE',
|
||||
TYPE_CONTACT_UPDATE = 'TYPE_CONTACT_UPDATE',
|
||||
TYPE_MEMBERSHIP_UPDATE_MESSAGE = 'TYPE_MEMBERSHIP_UPDATE_MESSAGE',
|
||||
TYPE_PAIR_INSTALLATION = 'TYPE_PAIR_INSTALLATION',
|
||||
TYPE_SYNC_INSTALLATION = 'TYPE_SYNC_INSTALLATION',
|
||||
TYPE_REQUEST_ADDRESS_FOR_TRANSACTION = 'TYPE_REQUEST_ADDRESS_FOR_TRANSACTION',
|
||||
TYPE_ACCEPT_REQUEST_ADDRESS_FOR_TRANSACTION = 'TYPE_ACCEPT_REQUEST_ADDRESS_FOR_TRANSACTION',
|
||||
TYPE_DECLINE_REQUEST_ADDRESS_FOR_TRANSACTION = 'TYPE_DECLINE_REQUEST_ADDRESS_FOR_TRANSACTION',
|
||||
TYPE_REQUEST_TRANSACTION = 'TYPE_REQUEST_TRANSACTION',
|
||||
TYPE_SEND_TRANSACTION = 'TYPE_SEND_TRANSACTION',
|
||||
TYPE_DECLINE_REQUEST_TRANSACTION = 'TYPE_DECLINE_REQUEST_TRANSACTION',
|
||||
TYPE_SYNC_INSTALLATION_CONTACT = 'TYPE_SYNC_INSTALLATION_CONTACT',
|
||||
TYPE_SYNC_INSTALLATION_ACCOUNT = 'TYPE_SYNC_INSTALLATION_ACCOUNT',
|
||||
TYPE_SYNC_INSTALLATION_PUBLIC_CHAT = 'TYPE_SYNC_INSTALLATION_PUBLIC_CHAT',
|
||||
TYPE_CONTACT_CODE_ADVERTISEMENT = 'TYPE_CONTACT_CODE_ADVERTISEMENT',
|
||||
TYPE_PUSH_NOTIFICATION_REGISTRATION = 'TYPE_PUSH_NOTIFICATION_REGISTRATION',
|
||||
TYPE_PUSH_NOTIFICATION_REGISTRATION_RESPONSE = 'TYPE_PUSH_NOTIFICATION_REGISTRATION_RESPONSE',
|
||||
TYPE_PUSH_NOTIFICATION_QUERY = 'TYPE_PUSH_NOTIFICATION_QUERY',
|
||||
TYPE_PUSH_NOTIFICATION_QUERY_RESPONSE = 'TYPE_PUSH_NOTIFICATION_QUERY_RESPONSE',
|
||||
TYPE_PUSH_NOTIFICATION_REQUEST = 'TYPE_PUSH_NOTIFICATION_REQUEST',
|
||||
TYPE_PUSH_NOTIFICATION_RESPONSE = 'TYPE_PUSH_NOTIFICATION_RESPONSE',
|
||||
TYPE_EMOJI_REACTION = 'TYPE_EMOJI_REACTION',
|
||||
TYPE_GROUP_CHAT_INVITATION = 'TYPE_GROUP_CHAT_INVITATION',
|
||||
TYPE_CHAT_IDENTITY = 'TYPE_CHAT_IDENTITY',
|
||||
TYPE_COMMUNITY_DESCRIPTION = 'TYPE_COMMUNITY_DESCRIPTION',
|
||||
TYPE_COMMUNITY_INVITATION = 'TYPE_COMMUNITY_INVITATION',
|
||||
TYPE_COMMUNITY_REQUEST_TO_JOIN = 'TYPE_COMMUNITY_REQUEST_TO_JOIN',
|
||||
TYPE_PIN_MESSAGE = 'TYPE_PIN_MESSAGE',
|
||||
TYPE_EDIT_MESSAGE = 'TYPE_EDIT_MESSAGE',
|
||||
TYPE_STATUS_UPDATE = 'TYPE_STATUS_UPDATE',
|
||||
TYPE_DELETE_MESSAGE = 'TYPE_DELETE_MESSAGE',
|
||||
TYPE_SYNC_INSTALLATION_COMMUNITY = 'TYPE_SYNC_INSTALLATION_COMMUNITY',
|
||||
TYPE_ANONYMOUS_METRIC_BATCH = 'TYPE_ANONYMOUS_METRIC_BATCH',
|
||||
}
|
||||
|
||||
enum __TypeValues {
|
||||
TYPE_UNKNOWN_UNSPECIFIED = 0,
|
||||
TYPE_CHAT_MESSAGE = 1,
|
||||
TYPE_CONTACT_UPDATE = 2,
|
||||
TYPE_MEMBERSHIP_UPDATE_MESSAGE = 3,
|
||||
TYPE_PAIR_INSTALLATION = 4,
|
||||
TYPE_SYNC_INSTALLATION = 5,
|
||||
TYPE_REQUEST_ADDRESS_FOR_TRANSACTION = 6,
|
||||
TYPE_ACCEPT_REQUEST_ADDRESS_FOR_TRANSACTION = 7,
|
||||
TYPE_DECLINE_REQUEST_ADDRESS_FOR_TRANSACTION = 8,
|
||||
TYPE_REQUEST_TRANSACTION = 9,
|
||||
TYPE_SEND_TRANSACTION = 10,
|
||||
TYPE_DECLINE_REQUEST_TRANSACTION = 11,
|
||||
TYPE_SYNC_INSTALLATION_CONTACT = 12,
|
||||
TYPE_SYNC_INSTALLATION_ACCOUNT = 13,
|
||||
TYPE_SYNC_INSTALLATION_PUBLIC_CHAT = 14,
|
||||
TYPE_CONTACT_CODE_ADVERTISEMENT = 15,
|
||||
TYPE_PUSH_NOTIFICATION_REGISTRATION = 16,
|
||||
TYPE_PUSH_NOTIFICATION_REGISTRATION_RESPONSE = 17,
|
||||
TYPE_PUSH_NOTIFICATION_QUERY = 18,
|
||||
TYPE_PUSH_NOTIFICATION_QUERY_RESPONSE = 19,
|
||||
TYPE_PUSH_NOTIFICATION_REQUEST = 20,
|
||||
TYPE_PUSH_NOTIFICATION_RESPONSE = 21,
|
||||
TYPE_EMOJI_REACTION = 22,
|
||||
TYPE_GROUP_CHAT_INVITATION = 23,
|
||||
TYPE_CHAT_IDENTITY = 24,
|
||||
TYPE_COMMUNITY_DESCRIPTION = 25,
|
||||
TYPE_COMMUNITY_INVITATION = 26,
|
||||
TYPE_COMMUNITY_REQUEST_TO_JOIN = 27,
|
||||
TYPE_PIN_MESSAGE = 28,
|
||||
TYPE_EDIT_MESSAGE = 29,
|
||||
TYPE_STATUS_UPDATE = 30,
|
||||
TYPE_DELETE_MESSAGE = 31,
|
||||
TYPE_SYNC_INSTALLATION_COMMUNITY = 32,
|
||||
TYPE_ANONYMOUS_METRIC_BATCH = 33,
|
||||
}
|
||||
|
||||
export namespace Type {
|
||||
export const codec = () => {
|
||||
return enumeration<typeof Type>(__TypeValues)
|
||||
}
|
||||
}
|
||||
|
||||
export const codec = (): Codec<ApplicationMetadataMessage> => {
|
||||
return message<ApplicationMetadataMessage>({
|
||||
1: { name: 'signature', codec: bytes },
|
||||
2: { name: 'payload', codec: bytes },
|
||||
3: { name: 'type', codec: ApplicationMetadataMessage.Type.codec() },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: ApplicationMetadataMessage): Uint8Array => {
|
||||
return encodeMessage(obj, ApplicationMetadataMessage.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): ApplicationMetadataMessage => {
|
||||
return decodeMessage(buf, ApplicationMetadataMessage.codec())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
syntax = "proto3";
|
||||
|
||||
import "enums.proto";
|
||||
|
||||
// ChatIdentity represents the user defined identity associated with their public chat key
|
||||
message ChatIdentity {
|
||||
// Lamport timestamp of the message
|
||||
uint64 clock = 1;
|
||||
|
||||
// ens_name is the valid ENS name associated with the chat key
|
||||
string ens_name = 2;
|
||||
|
||||
// images is a string indexed mapping of images associated with an identity
|
||||
map<string, IdentityImage> images = 3;
|
||||
|
||||
// display name is the user set identity
|
||||
string display_name = 4;
|
||||
|
||||
// description is the user set description, valid only for organisations
|
||||
string description = 5;
|
||||
|
||||
string color = 6;
|
||||
|
||||
string emoji = 7;
|
||||
}
|
||||
|
||||
// ProfileImage represents data associated with a user's profile image
|
||||
message IdentityImage {
|
||||
|
||||
// payload is a context based payload for the profile image data,
|
||||
// context is determined by the `source_type`
|
||||
bytes payload = 1;
|
||||
|
||||
// source_type signals the image payload source
|
||||
SourceType source_type = 2;
|
||||
|
||||
// image_type signals the image type and method of parsing the payload
|
||||
ImageType image_type = 3;
|
||||
|
||||
// encryption_keys is a list of encrypted keys that can be used to decrypted an encrypted payload
|
||||
repeated bytes encryption_keys = 4;
|
||||
|
||||
// encrypted signals the encryption state of the payload, default is false.
|
||||
bool encrypted = 5;
|
||||
|
||||
// SourceType are the predefined types of image source allowed
|
||||
enum SourceType {
|
||||
UNKNOWN_SOURCE_TYPE = 0;
|
||||
|
||||
// RAW_PAYLOAD image byte data
|
||||
RAW_PAYLOAD = 1;
|
||||
|
||||
// ENS_AVATAR uses the ENS record's resolver get-text-data.avatar data
|
||||
// The `payload` field will be ignored if ENS_AVATAR is selected
|
||||
// The application will read and parse the ENS avatar data as image payload data, URLs will be ignored
|
||||
// The parent `ChatMessageIdentity` must have a valid `ens_name` set
|
||||
ENS_AVATAR = 2;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
/* eslint-disable import/export */
|
||||
/* eslint-disable @typescript-eslint/no-namespace */
|
||||
|
||||
import {
|
||||
encodeMessage,
|
||||
decodeMessage,
|
||||
message,
|
||||
uint64,
|
||||
string,
|
||||
enumeration,
|
||||
bytes,
|
||||
bool,
|
||||
} from 'protons-runtime'
|
||||
import type { Codec } from 'protons-runtime'
|
||||
|
||||
export interface ChatIdentity {
|
||||
clock: bigint
|
||||
ensName: string
|
||||
images: IdentityImage
|
||||
displayName: string
|
||||
description: string
|
||||
color: string
|
||||
emoji: string
|
||||
}
|
||||
|
||||
export namespace ChatIdentity {
|
||||
export const codec = (): Codec<ChatIdentity> => {
|
||||
return message<ChatIdentity>({
|
||||
1: { name: 'clock', codec: uint64 },
|
||||
2: { name: 'ensName', codec: string },
|
||||
3: { name: 'images', codec: IdentityImage.codec() },
|
||||
4: { name: 'displayName', codec: string },
|
||||
5: { name: 'description', codec: string },
|
||||
6: { name: 'color', codec: string },
|
||||
7: { name: 'emoji', codec: string },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: ChatIdentity): Uint8Array => {
|
||||
return encodeMessage(obj, ChatIdentity.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): ChatIdentity => {
|
||||
return decodeMessage(buf, ChatIdentity.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface IdentityImage {
|
||||
payload: Uint8Array
|
||||
sourceType: IdentityImage.SourceType
|
||||
imageType: ImageType
|
||||
encryptionKeys: Uint8Array[]
|
||||
encrypted: boolean
|
||||
}
|
||||
|
||||
export namespace IdentityImage {
|
||||
export enum SourceType {
|
||||
UNKNOWN_SOURCE_TYPE = 'UNKNOWN_SOURCE_TYPE',
|
||||
RAW_PAYLOAD = 'RAW_PAYLOAD',
|
||||
ENS_AVATAR = 'ENS_AVATAR',
|
||||
}
|
||||
|
||||
enum __SourceTypeValues {
|
||||
UNKNOWN_SOURCE_TYPE = 0,
|
||||
RAW_PAYLOAD = 1,
|
||||
ENS_AVATAR = 2,
|
||||
}
|
||||
|
||||
export namespace SourceType {
|
||||
export const codec = () => {
|
||||
return enumeration<typeof SourceType>(__SourceTypeValues)
|
||||
}
|
||||
}
|
||||
|
||||
export const codec = (): Codec<IdentityImage> => {
|
||||
return message<IdentityImage>({
|
||||
1: { name: 'payload', codec: bytes },
|
||||
2: { name: 'sourceType', codec: IdentityImage.SourceType.codec() },
|
||||
3: { name: 'imageType', codec: ImageType.codec() },
|
||||
4: { name: 'encryptionKeys', codec: bytes, repeats: true },
|
||||
5: { name: 'encrypted', codec: bool },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: IdentityImage): Uint8Array => {
|
||||
return encodeMessage(obj, IdentityImage.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): IdentityImage => {
|
||||
return decodeMessage(buf, IdentityImage.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export enum MessageType {
|
||||
UNKNOWN_MESSAGE_TYPE = 'UNKNOWN_MESSAGE_TYPE',
|
||||
ONE_TO_ONE = 'ONE_TO_ONE',
|
||||
PUBLIC_GROUP = 'PUBLIC_GROUP',
|
||||
PRIVATE_GROUP = 'PRIVATE_GROUP',
|
||||
SYSTEM_MESSAGE_PRIVATE_GROUP = 'SYSTEM_MESSAGE_PRIVATE_GROUP',
|
||||
COMMUNITY_CHAT = 'COMMUNITY_CHAT',
|
||||
SYSTEM_MESSAGE_GAP = 'SYSTEM_MESSAGE_GAP',
|
||||
}
|
||||
|
||||
enum __MessageTypeValues {
|
||||
UNKNOWN_MESSAGE_TYPE = 0,
|
||||
ONE_TO_ONE = 1,
|
||||
PUBLIC_GROUP = 2,
|
||||
PRIVATE_GROUP = 3,
|
||||
SYSTEM_MESSAGE_PRIVATE_GROUP = 4,
|
||||
COMMUNITY_CHAT = 5,
|
||||
SYSTEM_MESSAGE_GAP = 6,
|
||||
}
|
||||
|
||||
export namespace MessageType {
|
||||
export const codec = () => {
|
||||
return enumeration<typeof MessageType>(__MessageTypeValues)
|
||||
}
|
||||
}
|
||||
export enum ImageType {
|
||||
UNKNOWN_IMAGE_TYPE = 'UNKNOWN_IMAGE_TYPE',
|
||||
PNG = 'PNG',
|
||||
JPEG = 'JPEG',
|
||||
WEBP = 'WEBP',
|
||||
GIF = 'GIF',
|
||||
}
|
||||
|
||||
enum __ImageTypeValues {
|
||||
UNKNOWN_IMAGE_TYPE = 0,
|
||||
PNG = 1,
|
||||
JPEG = 2,
|
||||
WEBP = 3,
|
||||
GIF = 4,
|
||||
}
|
||||
|
||||
export namespace ImageType {
|
||||
export const codec = () => {
|
||||
return enumeration<typeof ImageType>(__ImageTypeValues)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
syntax = "proto3";
|
||||
|
||||
import "enums.proto";
|
||||
|
||||
message StickerMessage {
|
||||
string hash = 1;
|
||||
int32 pack = 2;
|
||||
}
|
||||
|
||||
message ImageMessage {
|
||||
bytes payload = 1;
|
||||
ImageType type = 2;
|
||||
}
|
||||
|
||||
message AudioMessage {
|
||||
bytes payload = 1;
|
||||
AudioType type = 2;
|
||||
uint64 duration_ms = 3;
|
||||
enum AudioType {
|
||||
UNKNOWN_AUDIO_TYPE = 0;
|
||||
AAC = 1;
|
||||
AMR = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message EditMessage {
|
||||
uint64 clock = 1;
|
||||
// Text of the message
|
||||
string text = 2;
|
||||
|
||||
string chat_id = 3;
|
||||
string message_id = 4;
|
||||
|
||||
// Grant for community edit messages
|
||||
bytes grant = 5;
|
||||
|
||||
// The type of message (public/one-to-one/private-group-chat)
|
||||
MessageType message_type = 6;
|
||||
}
|
||||
|
||||
message DeleteMessage {
|
||||
uint64 clock = 1;
|
||||
|
||||
string chat_id = 2;
|
||||
string message_id = 3;
|
||||
|
||||
// Grant for community delete messages
|
||||
bytes grant = 4;
|
||||
|
||||
// The type of message (public/one-to-one/private-group-chat)
|
||||
MessageType message_type = 5;
|
||||
}
|
||||
|
||||
|
||||
message ChatMessage {
|
||||
// Lamport timestamp of the chat message
|
||||
uint64 clock = 1;
|
||||
// Unix timestamps in milliseconds, currently not used as we use whisper as more reliable, but here
|
||||
// so that we don't rely on it
|
||||
uint64 timestamp = 2;
|
||||
// Text of the message
|
||||
string text = 3;
|
||||
// Id of the message that we are replying to
|
||||
string response_to = 4;
|
||||
// Ens name of the sender
|
||||
string ens_name = 5;
|
||||
// Chat id, this field is symmetric for public-chats and private group chats,
|
||||
// but asymmetric in case of one-to-ones, as the sender will use the chat-id
|
||||
// of the received, while the receiver will use the chat-id of the sender.
|
||||
// Probably should be the concatenation of sender-pk & receiver-pk in alphabetical order
|
||||
string chat_id = 6;
|
||||
|
||||
// The type of message (public/one-to-one/private-group-chat)
|
||||
MessageType message_type = 7;
|
||||
// The type of the content of the message
|
||||
ContentType content_type = 8;
|
||||
|
||||
oneof payload {
|
||||
StickerMessage sticker = 9;
|
||||
ImageMessage image = 10;
|
||||
AudioMessage audio = 11;
|
||||
bytes community = 12;
|
||||
}
|
||||
|
||||
// Grant for community chat messages
|
||||
bytes grant = 13;
|
||||
|
||||
// Message author's display name, introduced in version 1
|
||||
string display_name = 14;
|
||||
|
||||
enum ContentType {
|
||||
UNKNOWN_CONTENT_TYPE = 0;
|
||||
TEXT_PLAIN = 1;
|
||||
STICKER = 2;
|
||||
STATUS = 3;
|
||||
EMOJI = 4;
|
||||
TRANSACTION_COMMAND = 5;
|
||||
// Only local
|
||||
SYSTEM_MESSAGE_CONTENT_PRIVATE_GROUP = 6;
|
||||
IMAGE = 7;
|
||||
AUDIO = 8;
|
||||
COMMUNITY = 9;
|
||||
// Only local
|
||||
SYSTEM_MESSAGE_GAP = 10;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,285 @@
|
|||
/* eslint-disable import/export */
|
||||
/* eslint-disable @typescript-eslint/no-namespace */
|
||||
|
||||
import {
|
||||
encodeMessage,
|
||||
decodeMessage,
|
||||
message,
|
||||
string,
|
||||
int32,
|
||||
bytes,
|
||||
enumeration,
|
||||
uint64,
|
||||
} from 'protons-runtime'
|
||||
import type { Codec } from 'protons-runtime'
|
||||
|
||||
export interface StickerMessage {
|
||||
hash: string
|
||||
pack: number
|
||||
}
|
||||
|
||||
export namespace StickerMessage {
|
||||
export const codec = (): Codec<StickerMessage> => {
|
||||
return message<StickerMessage>({
|
||||
1: { name: 'hash', codec: string },
|
||||
2: { name: 'pack', codec: int32 },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: StickerMessage): Uint8Array => {
|
||||
return encodeMessage(obj, StickerMessage.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): StickerMessage => {
|
||||
return decodeMessage(buf, StickerMessage.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface ImageMessage {
|
||||
payload: Uint8Array
|
||||
type: ImageType
|
||||
}
|
||||
|
||||
export namespace ImageMessage {
|
||||
export const codec = (): Codec<ImageMessage> => {
|
||||
return message<ImageMessage>({
|
||||
1: { name: 'payload', codec: bytes },
|
||||
2: { name: 'type', codec: ImageType.codec() },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: ImageMessage): Uint8Array => {
|
||||
return encodeMessage(obj, ImageMessage.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): ImageMessage => {
|
||||
return decodeMessage(buf, ImageMessage.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface AudioMessage {
|
||||
payload: Uint8Array
|
||||
type: AudioMessage.AudioType
|
||||
durationMs: bigint
|
||||
}
|
||||
|
||||
export namespace AudioMessage {
|
||||
export enum AudioType {
|
||||
UNKNOWN_AUDIO_TYPE = 'UNKNOWN_AUDIO_TYPE',
|
||||
AAC = 'AAC',
|
||||
AMR = 'AMR',
|
||||
}
|
||||
|
||||
enum __AudioTypeValues {
|
||||
UNKNOWN_AUDIO_TYPE = 0,
|
||||
AAC = 1,
|
||||
AMR = 2,
|
||||
}
|
||||
|
||||
export namespace AudioType {
|
||||
export const codec = () => {
|
||||
return enumeration<typeof AudioType>(__AudioTypeValues)
|
||||
}
|
||||
}
|
||||
|
||||
export const codec = (): Codec<AudioMessage> => {
|
||||
return message<AudioMessage>({
|
||||
1: { name: 'payload', codec: bytes },
|
||||
2: { name: 'type', codec: AudioMessage.AudioType.codec() },
|
||||
3: { name: 'durationMs', codec: uint64 },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: AudioMessage): Uint8Array => {
|
||||
return encodeMessage(obj, AudioMessage.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): AudioMessage => {
|
||||
return decodeMessage(buf, AudioMessage.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface EditMessage {
|
||||
clock: bigint
|
||||
text: string
|
||||
chatId: string
|
||||
messageId: string
|
||||
grant: Uint8Array
|
||||
messageType: MessageType
|
||||
}
|
||||
|
||||
export namespace EditMessage {
|
||||
export const codec = (): Codec<EditMessage> => {
|
||||
return message<EditMessage>({
|
||||
1: { name: 'clock', codec: uint64 },
|
||||
2: { name: 'text', codec: string },
|
||||
3: { name: 'chatId', codec: string },
|
||||
4: { name: 'messageId', codec: string },
|
||||
5: { name: 'grant', codec: bytes },
|
||||
6: { name: 'messageType', codec: MessageType.codec() },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: EditMessage): Uint8Array => {
|
||||
return encodeMessage(obj, EditMessage.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): EditMessage => {
|
||||
return decodeMessage(buf, EditMessage.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface DeleteMessage {
|
||||
clock: bigint
|
||||
chatId: string
|
||||
messageId: string
|
||||
grant: Uint8Array
|
||||
messageType: MessageType
|
||||
}
|
||||
|
||||
export namespace DeleteMessage {
|
||||
export const codec = (): Codec<DeleteMessage> => {
|
||||
return message<DeleteMessage>({
|
||||
1: { name: 'clock', codec: uint64 },
|
||||
2: { name: 'chatId', codec: string },
|
||||
3: { name: 'messageId', codec: string },
|
||||
4: { name: 'grant', codec: bytes },
|
||||
5: { name: 'messageType', codec: MessageType.codec() },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: DeleteMessage): Uint8Array => {
|
||||
return encodeMessage(obj, DeleteMessage.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): DeleteMessage => {
|
||||
return decodeMessage(buf, DeleteMessage.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface ChatMessage {
|
||||
clock: bigint
|
||||
timestamp: bigint
|
||||
text: string
|
||||
responseTo: string
|
||||
ensName: string
|
||||
chatId: string
|
||||
messageType: MessageType
|
||||
contentType: ChatMessage.ContentType
|
||||
sticker: StickerMessage
|
||||
image: ImageMessage
|
||||
audio: AudioMessage
|
||||
community: Uint8Array
|
||||
grant: Uint8Array
|
||||
displayName: string
|
||||
}
|
||||
|
||||
export namespace ChatMessage {
|
||||
export enum ContentType {
|
||||
UNKNOWN_CONTENT_TYPE = 'UNKNOWN_CONTENT_TYPE',
|
||||
TEXT_PLAIN = 'TEXT_PLAIN',
|
||||
STICKER = 'STICKER',
|
||||
STATUS = 'STATUS',
|
||||
EMOJI = 'EMOJI',
|
||||
TRANSACTION_COMMAND = 'TRANSACTION_COMMAND',
|
||||
SYSTEM_MESSAGE_CONTENT_PRIVATE_GROUP = 'SYSTEM_MESSAGE_CONTENT_PRIVATE_GROUP',
|
||||
IMAGE = 'IMAGE',
|
||||
AUDIO = 'AUDIO',
|
||||
COMMUNITY = 'COMMUNITY',
|
||||
SYSTEM_MESSAGE_GAP = 'SYSTEM_MESSAGE_GAP',
|
||||
}
|
||||
|
||||
enum __ContentTypeValues {
|
||||
UNKNOWN_CONTENT_TYPE = 0,
|
||||
TEXT_PLAIN = 1,
|
||||
STICKER = 2,
|
||||
STATUS = 3,
|
||||
EMOJI = 4,
|
||||
TRANSACTION_COMMAND = 5,
|
||||
SYSTEM_MESSAGE_CONTENT_PRIVATE_GROUP = 6,
|
||||
IMAGE = 7,
|
||||
AUDIO = 8,
|
||||
COMMUNITY = 9,
|
||||
SYSTEM_MESSAGE_GAP = 10,
|
||||
}
|
||||
|
||||
export namespace ContentType {
|
||||
export const codec = () => {
|
||||
return enumeration<typeof ContentType>(__ContentTypeValues)
|
||||
}
|
||||
}
|
||||
|
||||
export const codec = (): Codec<ChatMessage> => {
|
||||
return message<ChatMessage>({
|
||||
1: { name: 'clock', codec: uint64 },
|
||||
2: { name: 'timestamp', codec: uint64 },
|
||||
3: { name: 'text', codec: string },
|
||||
4: { name: 'responseTo', codec: string },
|
||||
5: { name: 'ensName', codec: string },
|
||||
6: { name: 'chatId', codec: string },
|
||||
7: { name: 'messageType', codec: MessageType.codec() },
|
||||
8: { name: 'contentType', codec: ChatMessage.ContentType.codec() },
|
||||
9: { name: 'sticker', codec: StickerMessage.codec() },
|
||||
10: { name: 'image', codec: ImageMessage.codec() },
|
||||
11: { name: 'audio', codec: AudioMessage.codec() },
|
||||
12: { name: 'community', codec: bytes },
|
||||
13: { name: 'grant', codec: bytes },
|
||||
14: { name: 'displayName', codec: string },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: ChatMessage): Uint8Array => {
|
||||
return encodeMessage(obj, ChatMessage.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): ChatMessage => {
|
||||
return decodeMessage(buf, ChatMessage.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export enum MessageType {
|
||||
UNKNOWN_MESSAGE_TYPE = 'UNKNOWN_MESSAGE_TYPE',
|
||||
ONE_TO_ONE = 'ONE_TO_ONE',
|
||||
PUBLIC_GROUP = 'PUBLIC_GROUP',
|
||||
PRIVATE_GROUP = 'PRIVATE_GROUP',
|
||||
SYSTEM_MESSAGE_PRIVATE_GROUP = 'SYSTEM_MESSAGE_PRIVATE_GROUP',
|
||||
COMMUNITY_CHAT = 'COMMUNITY_CHAT',
|
||||
SYSTEM_MESSAGE_GAP = 'SYSTEM_MESSAGE_GAP',
|
||||
}
|
||||
|
||||
enum __MessageTypeValues {
|
||||
UNKNOWN_MESSAGE_TYPE = 0,
|
||||
ONE_TO_ONE = 1,
|
||||
PUBLIC_GROUP = 2,
|
||||
PRIVATE_GROUP = 3,
|
||||
SYSTEM_MESSAGE_PRIVATE_GROUP = 4,
|
||||
COMMUNITY_CHAT = 5,
|
||||
SYSTEM_MESSAGE_GAP = 6,
|
||||
}
|
||||
|
||||
export namespace MessageType {
|
||||
export const codec = () => {
|
||||
return enumeration<typeof MessageType>(__MessageTypeValues)
|
||||
}
|
||||
}
|
||||
export enum ImageType {
|
||||
UNKNOWN_IMAGE_TYPE = 'UNKNOWN_IMAGE_TYPE',
|
||||
PNG = 'PNG',
|
||||
JPEG = 'JPEG',
|
||||
WEBP = 'WEBP',
|
||||
GIF = 'GIF',
|
||||
}
|
||||
|
||||
enum __ImageTypeValues {
|
||||
UNKNOWN_IMAGE_TYPE = 0,
|
||||
PNG = 1,
|
||||
JPEG = 2,
|
||||
WEBP = 3,
|
||||
GIF = 4,
|
||||
}
|
||||
|
||||
export namespace ImageType {
|
||||
export const codec = () => {
|
||||
return enumeration<typeof ImageType>(__ImageTypeValues)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
syntax = "proto3";
|
||||
|
||||
import "chat-identity.proto";
|
||||
|
||||
message Grant {
|
||||
bytes community_id = 1;
|
||||
bytes member_id = 2;
|
||||
string chat_id = 3;
|
||||
uint64 clock = 4;
|
||||
}
|
||||
|
||||
message CommunityMember {
|
||||
enum Roles {
|
||||
UNKNOWN_ROLE = 0;
|
||||
ROLE_ALL = 1;
|
||||
ROLE_MANAGE_USERS = 2;
|
||||
}
|
||||
repeated Roles roles = 1;
|
||||
}
|
||||
|
||||
message CommunityPermissions {
|
||||
enum Access {
|
||||
UNKNOWN_ACCESS = 0;
|
||||
NO_MEMBERSHIP = 1;
|
||||
INVITATION_ONLY = 2;
|
||||
ON_REQUEST = 3;
|
||||
}
|
||||
|
||||
bool ens_only = 1;
|
||||
// https://gitlab.matrix.org/matrix-org/olm/blob/master/docs/megolm.md is a candidate for the algorithm to be used in case we want to have private communityal chats, lighter than pairwise encryption using the DR, less secure, but more efficient for large number of participants
|
||||
bool private = 2;
|
||||
Access access = 3;
|
||||
}
|
||||
|
||||
message CommunityDescription {
|
||||
uint64 clock = 1;
|
||||
map<string,CommunityMember> members = 2;
|
||||
CommunityPermissions permissions = 3;
|
||||
ChatIdentity identity = 5;
|
||||
map<string,CommunityChat> chats = 6;
|
||||
repeated string ban_list = 7;
|
||||
map<string,CommunityCategory> categories = 8;
|
||||
uint64 archive_magnetlink_clock = 9;
|
||||
CommunityAdminSettings admin_settings = 10;
|
||||
}
|
||||
|
||||
message CommunityAdminSettings {
|
||||
bool pin_message_all_members_enabled = 1;
|
||||
}
|
||||
|
||||
message CommunityChat {
|
||||
map<string,CommunityMember> members = 1;
|
||||
CommunityPermissions permissions = 2;
|
||||
ChatIdentity identity = 3;
|
||||
string category_id = 4;
|
||||
int32 position = 5;
|
||||
}
|
||||
|
||||
message CommunityCategory {
|
||||
string category_id = 1;
|
||||
string name = 2;
|
||||
int32 position = 3;
|
||||
}
|
||||
|
||||
message CommunityInvitation {
|
||||
bytes community_description = 1;
|
||||
bytes grant = 2;
|
||||
string chat_id = 3;
|
||||
bytes public_key = 4;
|
||||
}
|
||||
|
||||
message CommunityRequestToJoin {
|
||||
uint64 clock = 1;
|
||||
string ens_name = 2;
|
||||
string chat_id = 3;
|
||||
bytes community_id = 4;
|
||||
}
|
||||
|
||||
message CommunityRequestToJoinResponse {
|
||||
uint64 clock = 1;
|
||||
CommunityDescription community = 2;
|
||||
bool accepted = 3;
|
||||
bytes grant = 4;
|
||||
}
|
||||
|
||||
message CommunityMessageArchiveMagnetlink {
|
||||
uint64 clock = 1;
|
||||
string magnet_uri = 2;
|
||||
}
|
||||
|
||||
message WakuMessage {
|
||||
bytes sig = 1;
|
||||
uint64 timestamp = 2;
|
||||
bytes topic = 3;
|
||||
bytes payload = 4;
|
||||
bytes padding = 5;
|
||||
bytes hash = 6;
|
||||
}
|
||||
|
||||
message WakuMessageArchiveMetadata {
|
||||
uint32 version = 1;
|
||||
uint64 from = 2;
|
||||
uint64 to = 3;
|
||||
repeated bytes contentTopic = 4;
|
||||
}
|
||||
|
||||
message WakuMessageArchive {
|
||||
uint32 version = 1;
|
||||
WakuMessageArchiveMetadata metadata = 2;
|
||||
repeated WakuMessage messages = 3;
|
||||
}
|
||||
|
||||
message WakuMessageArchiveIndexMetadata {
|
||||
uint32 version = 1;
|
||||
WakuMessageArchiveMetadata metadata = 2;
|
||||
uint64 offset = 3;
|
||||
uint64 size = 4;
|
||||
uint64 padding = 5;
|
||||
}
|
||||
|
||||
message WakuMessageArchiveIndex {
|
||||
map<string, WakuMessageArchiveIndexMetadata> archives = 1;
|
||||
}
|
|
@ -0,0 +1,589 @@
|
|||
/* eslint-disable import/export */
|
||||
/* eslint-disable @typescript-eslint/no-namespace */
|
||||
|
||||
import {
|
||||
encodeMessage,
|
||||
decodeMessage,
|
||||
message,
|
||||
bytes,
|
||||
string,
|
||||
uint64,
|
||||
enumeration,
|
||||
bool,
|
||||
int32,
|
||||
uint32,
|
||||
} from 'protons-runtime'
|
||||
import type { Codec } from 'protons-runtime'
|
||||
|
||||
export interface Grant {
|
||||
communityId: Uint8Array
|
||||
memberId: Uint8Array
|
||||
chatId: string
|
||||
clock: bigint
|
||||
}
|
||||
|
||||
export namespace Grant {
|
||||
export const codec = (): Codec<Grant> => {
|
||||
return message<Grant>({
|
||||
1: { name: 'communityId', codec: bytes },
|
||||
2: { name: 'memberId', codec: bytes },
|
||||
3: { name: 'chatId', codec: string },
|
||||
4: { name: 'clock', codec: uint64 },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: Grant): Uint8Array => {
|
||||
return encodeMessage(obj, Grant.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): Grant => {
|
||||
return decodeMessage(buf, Grant.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface CommunityMember {
|
||||
roles: CommunityMember.Roles[]
|
||||
}
|
||||
|
||||
export namespace CommunityMember {
|
||||
export enum Roles {
|
||||
UNKNOWN_ROLE = 'UNKNOWN_ROLE',
|
||||
ROLE_ALL = 'ROLE_ALL',
|
||||
ROLE_MANAGE_USERS = 'ROLE_MANAGE_USERS',
|
||||
}
|
||||
|
||||
enum __RolesValues {
|
||||
UNKNOWN_ROLE = 0,
|
||||
ROLE_ALL = 1,
|
||||
ROLE_MANAGE_USERS = 2,
|
||||
}
|
||||
|
||||
export namespace Roles {
|
||||
export const codec = () => {
|
||||
return enumeration<typeof Roles>(__RolesValues)
|
||||
}
|
||||
}
|
||||
|
||||
export const codec = (): Codec<CommunityMember> => {
|
||||
return message<CommunityMember>({
|
||||
1: { name: 'roles', codec: CommunityMember.Roles.codec(), repeats: true },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: CommunityMember): Uint8Array => {
|
||||
return encodeMessage(obj, CommunityMember.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): CommunityMember => {
|
||||
return decodeMessage(buf, CommunityMember.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface CommunityPermissions {
|
||||
ensOnly: boolean
|
||||
private: boolean
|
||||
access: CommunityPermissions.Access
|
||||
}
|
||||
|
||||
export namespace CommunityPermissions {
|
||||
export enum Access {
|
||||
UNKNOWN_ACCESS = 'UNKNOWN_ACCESS',
|
||||
NO_MEMBERSHIP = 'NO_MEMBERSHIP',
|
||||
INVITATION_ONLY = 'INVITATION_ONLY',
|
||||
ON_REQUEST = 'ON_REQUEST',
|
||||
}
|
||||
|
||||
enum __AccessValues {
|
||||
UNKNOWN_ACCESS = 0,
|
||||
NO_MEMBERSHIP = 1,
|
||||
INVITATION_ONLY = 2,
|
||||
ON_REQUEST = 3,
|
||||
}
|
||||
|
||||
export namespace Access {
|
||||
export const codec = () => {
|
||||
return enumeration<typeof Access>(__AccessValues)
|
||||
}
|
||||
}
|
||||
|
||||
export const codec = (): Codec<CommunityPermissions> => {
|
||||
return message<CommunityPermissions>({
|
||||
1: { name: 'ensOnly', codec: bool },
|
||||
2: { name: 'private', codec: bool },
|
||||
3: { name: 'access', codec: CommunityPermissions.Access.codec() },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: CommunityPermissions): Uint8Array => {
|
||||
return encodeMessage(obj, CommunityPermissions.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): CommunityPermissions => {
|
||||
return decodeMessage(buf, CommunityPermissions.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface CommunityDescription {
|
||||
clock: bigint
|
||||
members: CommunityMember
|
||||
permissions: CommunityPermissions
|
||||
identity: ChatIdentity
|
||||
chats: CommunityChat
|
||||
banList: string[]
|
||||
categories: CommunityCategory
|
||||
archiveMagnetlinkClock: bigint
|
||||
adminSettings: CommunityAdminSettings
|
||||
}
|
||||
|
||||
export namespace CommunityDescription {
|
||||
export const codec = (): Codec<CommunityDescription> => {
|
||||
return message<CommunityDescription>({
|
||||
1: { name: 'clock', codec: uint64 },
|
||||
2: { name: 'members', codec: CommunityMember.codec() },
|
||||
3: { name: 'permissions', codec: CommunityPermissions.codec() },
|
||||
5: { name: 'identity', codec: ChatIdentity.codec() },
|
||||
6: { name: 'chats', codec: CommunityChat.codec() },
|
||||
7: { name: 'banList', codec: string, repeats: true },
|
||||
8: { name: 'categories', codec: CommunityCategory.codec() },
|
||||
9: { name: 'archiveMagnetlinkClock', codec: uint64 },
|
||||
10: { name: 'adminSettings', codec: CommunityAdminSettings.codec() },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: CommunityDescription): Uint8Array => {
|
||||
return encodeMessage(obj, CommunityDescription.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): CommunityDescription => {
|
||||
return decodeMessage(buf, CommunityDescription.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface CommunityAdminSettings {
|
||||
pinMessageAllMembersEnabled: boolean
|
||||
}
|
||||
|
||||
export namespace CommunityAdminSettings {
|
||||
export const codec = (): Codec<CommunityAdminSettings> => {
|
||||
return message<CommunityAdminSettings>({
|
||||
1: { name: 'pinMessageAllMembersEnabled', codec: bool },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: CommunityAdminSettings): Uint8Array => {
|
||||
return encodeMessage(obj, CommunityAdminSettings.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): CommunityAdminSettings => {
|
||||
return decodeMessage(buf, CommunityAdminSettings.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface CommunityChat {
|
||||
members: CommunityMember
|
||||
permissions: CommunityPermissions
|
||||
identity: ChatIdentity
|
||||
categoryId: string
|
||||
position: number
|
||||
}
|
||||
|
||||
export namespace CommunityChat {
|
||||
export const codec = (): Codec<CommunityChat> => {
|
||||
return message<CommunityChat>({
|
||||
1: { name: 'members', codec: CommunityMember.codec() },
|
||||
2: { name: 'permissions', codec: CommunityPermissions.codec() },
|
||||
3: { name: 'identity', codec: ChatIdentity.codec() },
|
||||
4: { name: 'categoryId', codec: string },
|
||||
5: { name: 'position', codec: int32 },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: CommunityChat): Uint8Array => {
|
||||
return encodeMessage(obj, CommunityChat.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): CommunityChat => {
|
||||
return decodeMessage(buf, CommunityChat.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface CommunityCategory {
|
||||
categoryId: string
|
||||
name: string
|
||||
position: number
|
||||
}
|
||||
|
||||
export namespace CommunityCategory {
|
||||
export const codec = (): Codec<CommunityCategory> => {
|
||||
return message<CommunityCategory>({
|
||||
1: { name: 'categoryId', codec: string },
|
||||
2: { name: 'name', codec: string },
|
||||
3: { name: 'position', codec: int32 },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: CommunityCategory): Uint8Array => {
|
||||
return encodeMessage(obj, CommunityCategory.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): CommunityCategory => {
|
||||
return decodeMessage(buf, CommunityCategory.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface CommunityInvitation {
|
||||
communityDescription: Uint8Array
|
||||
grant: Uint8Array
|
||||
chatId: string
|
||||
publicKey: Uint8Array
|
||||
}
|
||||
|
||||
export namespace CommunityInvitation {
|
||||
export const codec = (): Codec<CommunityInvitation> => {
|
||||
return message<CommunityInvitation>({
|
||||
1: { name: 'communityDescription', codec: bytes },
|
||||
2: { name: 'grant', codec: bytes },
|
||||
3: { name: 'chatId', codec: string },
|
||||
4: { name: 'publicKey', codec: bytes },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: CommunityInvitation): Uint8Array => {
|
||||
return encodeMessage(obj, CommunityInvitation.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): CommunityInvitation => {
|
||||
return decodeMessage(buf, CommunityInvitation.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface CommunityRequestToJoin {
|
||||
clock: bigint
|
||||
ensName: string
|
||||
chatId: string
|
||||
communityId: Uint8Array
|
||||
}
|
||||
|
||||
export namespace CommunityRequestToJoin {
|
||||
export const codec = (): Codec<CommunityRequestToJoin> => {
|
||||
return message<CommunityRequestToJoin>({
|
||||
1: { name: 'clock', codec: uint64 },
|
||||
2: { name: 'ensName', codec: string },
|
||||
3: { name: 'chatId', codec: string },
|
||||
4: { name: 'communityId', codec: bytes },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: CommunityRequestToJoin): Uint8Array => {
|
||||
return encodeMessage(obj, CommunityRequestToJoin.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): CommunityRequestToJoin => {
|
||||
return decodeMessage(buf, CommunityRequestToJoin.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface CommunityRequestToJoinResponse {
|
||||
clock: bigint
|
||||
community: CommunityDescription
|
||||
accepted: boolean
|
||||
grant: Uint8Array
|
||||
}
|
||||
|
||||
export namespace CommunityRequestToJoinResponse {
|
||||
export const codec = (): Codec<CommunityRequestToJoinResponse> => {
|
||||
return message<CommunityRequestToJoinResponse>({
|
||||
1: { name: 'clock', codec: uint64 },
|
||||
2: { name: 'community', codec: CommunityDescription.codec() },
|
||||
3: { name: 'accepted', codec: bool },
|
||||
4: { name: 'grant', codec: bytes },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: CommunityRequestToJoinResponse): Uint8Array => {
|
||||
return encodeMessage(obj, CommunityRequestToJoinResponse.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): CommunityRequestToJoinResponse => {
|
||||
return decodeMessage(buf, CommunityRequestToJoinResponse.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface CommunityMessageArchiveMagnetlink {
|
||||
clock: bigint
|
||||
magnetUri: string
|
||||
}
|
||||
|
||||
export namespace CommunityMessageArchiveMagnetlink {
|
||||
export const codec = (): Codec<CommunityMessageArchiveMagnetlink> => {
|
||||
return message<CommunityMessageArchiveMagnetlink>({
|
||||
1: { name: 'clock', codec: uint64 },
|
||||
2: { name: 'magnetUri', codec: string },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (
|
||||
obj: CommunityMessageArchiveMagnetlink
|
||||
): Uint8Array => {
|
||||
return encodeMessage(obj, CommunityMessageArchiveMagnetlink.codec())
|
||||
}
|
||||
|
||||
export const decode = (
|
||||
buf: Uint8Array
|
||||
): CommunityMessageArchiveMagnetlink => {
|
||||
return decodeMessage(buf, CommunityMessageArchiveMagnetlink.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface WakuMessage {
|
||||
sig: Uint8Array
|
||||
timestamp: bigint
|
||||
topic: Uint8Array
|
||||
payload: Uint8Array
|
||||
padding: Uint8Array
|
||||
hash: Uint8Array
|
||||
}
|
||||
|
||||
export namespace WakuMessage {
|
||||
export const codec = (): Codec<WakuMessage> => {
|
||||
return message<WakuMessage>({
|
||||
1: { name: 'sig', codec: bytes },
|
||||
2: { name: 'timestamp', codec: uint64 },
|
||||
3: { name: 'topic', codec: bytes },
|
||||
4: { name: 'payload', codec: bytes },
|
||||
5: { name: 'padding', codec: bytes },
|
||||
6: { name: 'hash', codec: bytes },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: WakuMessage): Uint8Array => {
|
||||
return encodeMessage(obj, WakuMessage.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): WakuMessage => {
|
||||
return decodeMessage(buf, WakuMessage.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface WakuMessageArchiveMetadata {
|
||||
version: number
|
||||
from: bigint
|
||||
to: bigint
|
||||
contentTopic: Uint8Array[]
|
||||
}
|
||||
|
||||
export namespace WakuMessageArchiveMetadata {
|
||||
export const codec = (): Codec<WakuMessageArchiveMetadata> => {
|
||||
return message<WakuMessageArchiveMetadata>({
|
||||
1: { name: 'version', codec: uint32 },
|
||||
2: { name: 'from', codec: uint64 },
|
||||
3: { name: 'to', codec: uint64 },
|
||||
4: { name: 'contentTopic', codec: bytes, repeats: true },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: WakuMessageArchiveMetadata): Uint8Array => {
|
||||
return encodeMessage(obj, WakuMessageArchiveMetadata.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): WakuMessageArchiveMetadata => {
|
||||
return decodeMessage(buf, WakuMessageArchiveMetadata.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface WakuMessageArchive {
|
||||
version: number
|
||||
metadata: WakuMessageArchiveMetadata
|
||||
messages: WakuMessage[]
|
||||
}
|
||||
|
||||
export namespace WakuMessageArchive {
|
||||
export const codec = (): Codec<WakuMessageArchive> => {
|
||||
return message<WakuMessageArchive>({
|
||||
1: { name: 'version', codec: uint32 },
|
||||
2: { name: 'metadata', codec: WakuMessageArchiveMetadata.codec() },
|
||||
3: { name: 'messages', codec: WakuMessage.codec(), repeats: true },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: WakuMessageArchive): Uint8Array => {
|
||||
return encodeMessage(obj, WakuMessageArchive.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): WakuMessageArchive => {
|
||||
return decodeMessage(buf, WakuMessageArchive.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface WakuMessageArchiveIndexMetadata {
|
||||
version: number
|
||||
metadata: WakuMessageArchiveMetadata
|
||||
offset: bigint
|
||||
size: bigint
|
||||
padding: bigint
|
||||
}
|
||||
|
||||
export namespace WakuMessageArchiveIndexMetadata {
|
||||
export const codec = (): Codec<WakuMessageArchiveIndexMetadata> => {
|
||||
return message<WakuMessageArchiveIndexMetadata>({
|
||||
1: { name: 'version', codec: uint32 },
|
||||
2: { name: 'metadata', codec: WakuMessageArchiveMetadata.codec() },
|
||||
3: { name: 'offset', codec: uint64 },
|
||||
4: { name: 'size', codec: uint64 },
|
||||
5: { name: 'padding', codec: uint64 },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: WakuMessageArchiveIndexMetadata): Uint8Array => {
|
||||
return encodeMessage(obj, WakuMessageArchiveIndexMetadata.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): WakuMessageArchiveIndexMetadata => {
|
||||
return decodeMessage(buf, WakuMessageArchiveIndexMetadata.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface WakuMessageArchiveIndex {
|
||||
archives: WakuMessageArchiveIndexMetadata
|
||||
}
|
||||
|
||||
export namespace WakuMessageArchiveIndex {
|
||||
export const codec = (): Codec<WakuMessageArchiveIndex> => {
|
||||
return message<WakuMessageArchiveIndex>({
|
||||
1: { name: 'archives', codec: WakuMessageArchiveIndexMetadata.codec() },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: WakuMessageArchiveIndex): Uint8Array => {
|
||||
return encodeMessage(obj, WakuMessageArchiveIndex.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): WakuMessageArchiveIndex => {
|
||||
return decodeMessage(buf, WakuMessageArchiveIndex.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface ChatIdentity {
|
||||
clock: bigint
|
||||
ensName: string
|
||||
images: IdentityImage
|
||||
displayName: string
|
||||
description: string
|
||||
color: string
|
||||
emoji: string
|
||||
}
|
||||
|
||||
export namespace ChatIdentity {
|
||||
export const codec = (): Codec<ChatIdentity> => {
|
||||
return message<ChatIdentity>({
|
||||
1: { name: 'clock', codec: uint64 },
|
||||
2: { name: 'ensName', codec: string },
|
||||
3: { name: 'images', codec: IdentityImage.codec() },
|
||||
4: { name: 'displayName', codec: string },
|
||||
5: { name: 'description', codec: string },
|
||||
6: { name: 'color', codec: string },
|
||||
7: { name: 'emoji', codec: string },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: ChatIdentity): Uint8Array => {
|
||||
return encodeMessage(obj, ChatIdentity.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): ChatIdentity => {
|
||||
return decodeMessage(buf, ChatIdentity.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface IdentityImage {
|
||||
payload: Uint8Array
|
||||
sourceType: IdentityImage.SourceType
|
||||
imageType: ImageType
|
||||
encryptionKeys: Uint8Array[]
|
||||
encrypted: boolean
|
||||
}
|
||||
|
||||
export namespace IdentityImage {
|
||||
export enum SourceType {
|
||||
UNKNOWN_SOURCE_TYPE = 'UNKNOWN_SOURCE_TYPE',
|
||||
RAW_PAYLOAD = 'RAW_PAYLOAD',
|
||||
ENS_AVATAR = 'ENS_AVATAR',
|
||||
}
|
||||
|
||||
enum __SourceTypeValues {
|
||||
UNKNOWN_SOURCE_TYPE = 0,
|
||||
RAW_PAYLOAD = 1,
|
||||
ENS_AVATAR = 2,
|
||||
}
|
||||
|
||||
export namespace SourceType {
|
||||
export const codec = () => {
|
||||
return enumeration<typeof SourceType>(__SourceTypeValues)
|
||||
}
|
||||
}
|
||||
|
||||
export const codec = (): Codec<IdentityImage> => {
|
||||
return message<IdentityImage>({
|
||||
1: { name: 'payload', codec: bytes },
|
||||
2: { name: 'sourceType', codec: IdentityImage.SourceType.codec() },
|
||||
3: { name: 'imageType', codec: ImageType.codec() },
|
||||
4: { name: 'encryptionKeys', codec: bytes, repeats: true },
|
||||
5: { name: 'encrypted', codec: bool },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: IdentityImage): Uint8Array => {
|
||||
return encodeMessage(obj, IdentityImage.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): IdentityImage => {
|
||||
return decodeMessage(buf, IdentityImage.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export enum MessageType {
|
||||
UNKNOWN_MESSAGE_TYPE = 'UNKNOWN_MESSAGE_TYPE',
|
||||
ONE_TO_ONE = 'ONE_TO_ONE',
|
||||
PUBLIC_GROUP = 'PUBLIC_GROUP',
|
||||
PRIVATE_GROUP = 'PRIVATE_GROUP',
|
||||
SYSTEM_MESSAGE_PRIVATE_GROUP = 'SYSTEM_MESSAGE_PRIVATE_GROUP',
|
||||
COMMUNITY_CHAT = 'COMMUNITY_CHAT',
|
||||
SYSTEM_MESSAGE_GAP = 'SYSTEM_MESSAGE_GAP',
|
||||
}
|
||||
|
||||
enum __MessageTypeValues {
|
||||
UNKNOWN_MESSAGE_TYPE = 0,
|
||||
ONE_TO_ONE = 1,
|
||||
PUBLIC_GROUP = 2,
|
||||
PRIVATE_GROUP = 3,
|
||||
SYSTEM_MESSAGE_PRIVATE_GROUP = 4,
|
||||
COMMUNITY_CHAT = 5,
|
||||
SYSTEM_MESSAGE_GAP = 6,
|
||||
}
|
||||
|
||||
export namespace MessageType {
|
||||
export const codec = () => {
|
||||
return enumeration<typeof MessageType>(__MessageTypeValues)
|
||||
}
|
||||
}
|
||||
export enum ImageType {
|
||||
UNKNOWN_IMAGE_TYPE = 'UNKNOWN_IMAGE_TYPE',
|
||||
PNG = 'PNG',
|
||||
JPEG = 'JPEG',
|
||||
WEBP = 'WEBP',
|
||||
GIF = 'GIF',
|
||||
}
|
||||
|
||||
enum __ImageTypeValues {
|
||||
UNKNOWN_IMAGE_TYPE = 0,
|
||||
PNG = 1,
|
||||
JPEG = 2,
|
||||
WEBP = 3,
|
||||
GIF = 4,
|
||||
}
|
||||
|
||||
export namespace ImageType {
|
||||
export const codec = () => {
|
||||
return enumeration<typeof ImageType>(__ImageTypeValues)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
syntax = "proto3";
|
||||
|
||||
import "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,119 @@
|
|||
/* eslint-disable import/export */
|
||||
/* eslint-disable @typescript-eslint/no-namespace */
|
||||
|
||||
import {
|
||||
enumeration,
|
||||
encodeMessage,
|
||||
decodeMessage,
|
||||
message,
|
||||
uint64,
|
||||
string,
|
||||
bool,
|
||||
bytes,
|
||||
} from 'protons-runtime'
|
||||
import type { Codec } from 'protons-runtime'
|
||||
|
||||
export interface EmojiReaction {
|
||||
clock: bigint
|
||||
chatId: string
|
||||
messageId: string
|
||||
messageType: MessageType
|
||||
type: EmojiReaction.Type
|
||||
retracted: boolean
|
||||
grant: Uint8Array
|
||||
}
|
||||
|
||||
export namespace EmojiReaction {
|
||||
export enum Type {
|
||||
UNKNOWN_EMOJI_REACTION_TYPE = 'UNKNOWN_EMOJI_REACTION_TYPE',
|
||||
LOVE = 'LOVE',
|
||||
THUMBS_UP = 'THUMBS_UP',
|
||||
THUMBS_DOWN = 'THUMBS_DOWN',
|
||||
LAUGH = 'LAUGH',
|
||||
SAD = 'SAD',
|
||||
ANGRY = 'ANGRY',
|
||||
}
|
||||
|
||||
enum __TypeValues {
|
||||
UNKNOWN_EMOJI_REACTION_TYPE = 0,
|
||||
LOVE = 1,
|
||||
THUMBS_UP = 2,
|
||||
THUMBS_DOWN = 3,
|
||||
LAUGH = 4,
|
||||
SAD = 5,
|
||||
ANGRY = 6,
|
||||
}
|
||||
|
||||
export namespace Type {
|
||||
export const codec = () => {
|
||||
return enumeration<typeof Type>(__TypeValues)
|
||||
}
|
||||
}
|
||||
|
||||
export const codec = (): Codec<EmojiReaction> => {
|
||||
return message<EmojiReaction>({
|
||||
1: { name: 'clock', codec: uint64 },
|
||||
2: { name: 'chatId', codec: string },
|
||||
3: { name: 'messageId', codec: string },
|
||||
4: { name: 'messageType', codec: MessageType.codec() },
|
||||
5: { name: 'type', codec: EmojiReaction.Type.codec() },
|
||||
6: { name: 'retracted', codec: bool },
|
||||
7: { name: 'grant', codec: bytes },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: EmojiReaction): Uint8Array => {
|
||||
return encodeMessage(obj, EmojiReaction.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): EmojiReaction => {
|
||||
return decodeMessage(buf, EmojiReaction.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export enum MessageType {
|
||||
UNKNOWN_MESSAGE_TYPE = 'UNKNOWN_MESSAGE_TYPE',
|
||||
ONE_TO_ONE = 'ONE_TO_ONE',
|
||||
PUBLIC_GROUP = 'PUBLIC_GROUP',
|
||||
PRIVATE_GROUP = 'PRIVATE_GROUP',
|
||||
SYSTEM_MESSAGE_PRIVATE_GROUP = 'SYSTEM_MESSAGE_PRIVATE_GROUP',
|
||||
COMMUNITY_CHAT = 'COMMUNITY_CHAT',
|
||||
SYSTEM_MESSAGE_GAP = 'SYSTEM_MESSAGE_GAP',
|
||||
}
|
||||
|
||||
enum __MessageTypeValues {
|
||||
UNKNOWN_MESSAGE_TYPE = 0,
|
||||
ONE_TO_ONE = 1,
|
||||
PUBLIC_GROUP = 2,
|
||||
PRIVATE_GROUP = 3,
|
||||
SYSTEM_MESSAGE_PRIVATE_GROUP = 4,
|
||||
COMMUNITY_CHAT = 5,
|
||||
SYSTEM_MESSAGE_GAP = 6,
|
||||
}
|
||||
|
||||
export namespace MessageType {
|
||||
export const codec = () => {
|
||||
return enumeration<typeof MessageType>(__MessageTypeValues)
|
||||
}
|
||||
}
|
||||
export enum ImageType {
|
||||
UNKNOWN_IMAGE_TYPE = 'UNKNOWN_IMAGE_TYPE',
|
||||
PNG = 'PNG',
|
||||
JPEG = 'JPEG',
|
||||
WEBP = 'WEBP',
|
||||
GIF = 'GIF',
|
||||
}
|
||||
|
||||
enum __ImageTypeValues {
|
||||
UNKNOWN_IMAGE_TYPE = 0,
|
||||
PNG = 1,
|
||||
JPEG = 2,
|
||||
WEBP = 3,
|
||||
GIF = 4,
|
||||
}
|
||||
|
||||
export namespace ImageType {
|
||||
export const codec = () => {
|
||||
return enumeration<typeof ImageType>(__ImageTypeValues)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
syntax = "proto3";
|
||||
|
||||
enum MessageType {
|
||||
UNKNOWN_MESSAGE_TYPE = 0;
|
||||
ONE_TO_ONE = 1;
|
||||
PUBLIC_GROUP = 2;
|
||||
PRIVATE_GROUP = 3;
|
||||
// Only local
|
||||
SYSTEM_MESSAGE_PRIVATE_GROUP = 4;
|
||||
COMMUNITY_CHAT = 5;
|
||||
// Only local
|
||||
SYSTEM_MESSAGE_GAP = 6;
|
||||
}
|
||||
|
||||
enum ImageType {
|
||||
UNKNOWN_IMAGE_TYPE = 0;
|
||||
|
||||
// Raster image files is payload data that can be read as a raster image
|
||||
PNG = 1;
|
||||
JPEG = 2;
|
||||
WEBP = 3;
|
||||
GIF = 4;
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/* eslint-disable import/export */
|
||||
/* eslint-disable @typescript-eslint/no-namespace */
|
||||
|
||||
import { enumeration } from 'protons-runtime'
|
||||
|
||||
export enum MessageType {
|
||||
UNKNOWN_MESSAGE_TYPE = 'UNKNOWN_MESSAGE_TYPE',
|
||||
ONE_TO_ONE = 'ONE_TO_ONE',
|
||||
PUBLIC_GROUP = 'PUBLIC_GROUP',
|
||||
PRIVATE_GROUP = 'PRIVATE_GROUP',
|
||||
SYSTEM_MESSAGE_PRIVATE_GROUP = 'SYSTEM_MESSAGE_PRIVATE_GROUP',
|
||||
COMMUNITY_CHAT = 'COMMUNITY_CHAT',
|
||||
SYSTEM_MESSAGE_GAP = 'SYSTEM_MESSAGE_GAP',
|
||||
}
|
||||
|
||||
enum __MessageTypeValues {
|
||||
UNKNOWN_MESSAGE_TYPE = 0,
|
||||
ONE_TO_ONE = 1,
|
||||
PUBLIC_GROUP = 2,
|
||||
PRIVATE_GROUP = 3,
|
||||
SYSTEM_MESSAGE_PRIVATE_GROUP = 4,
|
||||
COMMUNITY_CHAT = 5,
|
||||
SYSTEM_MESSAGE_GAP = 6,
|
||||
}
|
||||
|
||||
export namespace MessageType {
|
||||
export const codec = () => {
|
||||
return enumeration<typeof MessageType>(__MessageTypeValues)
|
||||
}
|
||||
}
|
||||
export enum ImageType {
|
||||
UNKNOWN_IMAGE_TYPE = 'UNKNOWN_IMAGE_TYPE',
|
||||
PNG = 'PNG',
|
||||
JPEG = 'JPEG',
|
||||
WEBP = 'WEBP',
|
||||
GIF = 'GIF',
|
||||
}
|
||||
|
||||
enum __ImageTypeValues {
|
||||
UNKNOWN_IMAGE_TYPE = 0,
|
||||
PNG = 1,
|
||||
JPEG = 2,
|
||||
WEBP = 3,
|
||||
GIF = 4,
|
||||
}
|
||||
|
||||
export namespace ImageType {
|
||||
export const codec = () => {
|
||||
return enumeration<typeof ImageType>(__ImageTypeValues)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
syntax = "proto3";
|
||||
|
||||
import "chat-message.proto";
|
||||
import "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,423 @@
|
|||
/* eslint-disable import/export */
|
||||
/* eslint-disable @typescript-eslint/no-namespace */
|
||||
|
||||
import {
|
||||
enumeration,
|
||||
encodeMessage,
|
||||
decodeMessage,
|
||||
message,
|
||||
uint64,
|
||||
string,
|
||||
bytes,
|
||||
int32,
|
||||
bool,
|
||||
} from 'protons-runtime'
|
||||
import type { Codec } from 'protons-runtime'
|
||||
|
||||
export interface MembershipUpdateEvent {
|
||||
clock: bigint
|
||||
members: string[]
|
||||
name: string
|
||||
type: MembershipUpdateEvent.EventType
|
||||
}
|
||||
|
||||
export namespace MembershipUpdateEvent {
|
||||
export enum EventType {
|
||||
UNKNOWN = 'UNKNOWN',
|
||||
CHAT_CREATED = 'CHAT_CREATED',
|
||||
NAME_CHANGED = 'NAME_CHANGED',
|
||||
MEMBERS_ADDED = 'MEMBERS_ADDED',
|
||||
MEMBER_JOINED = 'MEMBER_JOINED',
|
||||
MEMBER_REMOVED = 'MEMBER_REMOVED',
|
||||
ADMINS_ADDED = 'ADMINS_ADDED',
|
||||
ADMIN_REMOVED = 'ADMIN_REMOVED',
|
||||
}
|
||||
|
||||
enum __EventTypeValues {
|
||||
UNKNOWN = 0,
|
||||
CHAT_CREATED = 1,
|
||||
NAME_CHANGED = 2,
|
||||
MEMBERS_ADDED = 3,
|
||||
MEMBER_JOINED = 4,
|
||||
MEMBER_REMOVED = 5,
|
||||
ADMINS_ADDED = 6,
|
||||
ADMIN_REMOVED = 7,
|
||||
}
|
||||
|
||||
export namespace EventType {
|
||||
export const codec = () => {
|
||||
return enumeration<typeof EventType>(__EventTypeValues)
|
||||
}
|
||||
}
|
||||
|
||||
export const codec = (): Codec<MembershipUpdateEvent> => {
|
||||
return message<MembershipUpdateEvent>({
|
||||
1: { name: 'clock', codec: uint64 },
|
||||
2: { name: 'members', codec: string, repeats: true },
|
||||
3: { name: 'name', codec: string },
|
||||
4: { name: 'type', codec: MembershipUpdateEvent.EventType.codec() },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: MembershipUpdateEvent): Uint8Array => {
|
||||
return encodeMessage(obj, MembershipUpdateEvent.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): MembershipUpdateEvent => {
|
||||
return decodeMessage(buf, MembershipUpdateEvent.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface MembershipUpdateMessage {
|
||||
chatId: string
|
||||
events: Uint8Array[]
|
||||
message: ChatMessage
|
||||
emojiReaction: EmojiReaction
|
||||
}
|
||||
|
||||
export namespace MembershipUpdateMessage {
|
||||
export const codec = (): Codec<MembershipUpdateMessage> => {
|
||||
return message<MembershipUpdateMessage>({
|
||||
1: { name: 'chatId', codec: string },
|
||||
2: { name: 'events', codec: bytes, repeats: true },
|
||||
3: { name: 'message', codec: ChatMessage.codec() },
|
||||
4: { name: 'emojiReaction', codec: EmojiReaction.codec() },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: MembershipUpdateMessage): Uint8Array => {
|
||||
return encodeMessage(obj, MembershipUpdateMessage.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): MembershipUpdateMessage => {
|
||||
return decodeMessage(buf, MembershipUpdateMessage.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface StickerMessage {
|
||||
hash: string
|
||||
pack: number
|
||||
}
|
||||
|
||||
export namespace StickerMessage {
|
||||
export const codec = (): Codec<StickerMessage> => {
|
||||
return message<StickerMessage>({
|
||||
1: { name: 'hash', codec: string },
|
||||
2: { name: 'pack', codec: int32 },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: StickerMessage): Uint8Array => {
|
||||
return encodeMessage(obj, StickerMessage.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): StickerMessage => {
|
||||
return decodeMessage(buf, StickerMessage.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface ImageMessage {
|
||||
payload: Uint8Array
|
||||
type: ImageType
|
||||
}
|
||||
|
||||
export namespace ImageMessage {
|
||||
export const codec = (): Codec<ImageMessage> => {
|
||||
return message<ImageMessage>({
|
||||
1: { name: 'payload', codec: bytes },
|
||||
2: { name: 'type', codec: ImageType.codec() },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: ImageMessage): Uint8Array => {
|
||||
return encodeMessage(obj, ImageMessage.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): ImageMessage => {
|
||||
return decodeMessage(buf, ImageMessage.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface AudioMessage {
|
||||
payload: Uint8Array
|
||||
type: AudioMessage.AudioType
|
||||
durationMs: bigint
|
||||
}
|
||||
|
||||
export namespace AudioMessage {
|
||||
export enum AudioType {
|
||||
UNKNOWN_AUDIO_TYPE = 'UNKNOWN_AUDIO_TYPE',
|
||||
AAC = 'AAC',
|
||||
AMR = 'AMR',
|
||||
}
|
||||
|
||||
enum __AudioTypeValues {
|
||||
UNKNOWN_AUDIO_TYPE = 0,
|
||||
AAC = 1,
|
||||
AMR = 2,
|
||||
}
|
||||
|
||||
export namespace AudioType {
|
||||
export const codec = () => {
|
||||
return enumeration<typeof AudioType>(__AudioTypeValues)
|
||||
}
|
||||
}
|
||||
|
||||
export const codec = (): Codec<AudioMessage> => {
|
||||
return message<AudioMessage>({
|
||||
1: { name: 'payload', codec: bytes },
|
||||
2: { name: 'type', codec: AudioMessage.AudioType.codec() },
|
||||
3: { name: 'durationMs', codec: uint64 },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: AudioMessage): Uint8Array => {
|
||||
return encodeMessage(obj, AudioMessage.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): AudioMessage => {
|
||||
return decodeMessage(buf, AudioMessage.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface EditMessage {
|
||||
clock: bigint
|
||||
text: string
|
||||
chatId: string
|
||||
messageId: string
|
||||
grant: Uint8Array
|
||||
messageType: MessageType
|
||||
}
|
||||
|
||||
export namespace EditMessage {
|
||||
export const codec = (): Codec<EditMessage> => {
|
||||
return message<EditMessage>({
|
||||
1: { name: 'clock', codec: uint64 },
|
||||
2: { name: 'text', codec: string },
|
||||
3: { name: 'chatId', codec: string },
|
||||
4: { name: 'messageId', codec: string },
|
||||
5: { name: 'grant', codec: bytes },
|
||||
6: { name: 'messageType', codec: MessageType.codec() },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: EditMessage): Uint8Array => {
|
||||
return encodeMessage(obj, EditMessage.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): EditMessage => {
|
||||
return decodeMessage(buf, EditMessage.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface DeleteMessage {
|
||||
clock: bigint
|
||||
chatId: string
|
||||
messageId: string
|
||||
grant: Uint8Array
|
||||
messageType: MessageType
|
||||
}
|
||||
|
||||
export namespace DeleteMessage {
|
||||
export const codec = (): Codec<DeleteMessage> => {
|
||||
return message<DeleteMessage>({
|
||||
1: { name: 'clock', codec: uint64 },
|
||||
2: { name: 'chatId', codec: string },
|
||||
3: { name: 'messageId', codec: string },
|
||||
4: { name: 'grant', codec: bytes },
|
||||
5: { name: 'messageType', codec: MessageType.codec() },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: DeleteMessage): Uint8Array => {
|
||||
return encodeMessage(obj, DeleteMessage.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): DeleteMessage => {
|
||||
return decodeMessage(buf, DeleteMessage.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface ChatMessage {
|
||||
clock: bigint
|
||||
timestamp: bigint
|
||||
text: string
|
||||
responseTo: string
|
||||
ensName: string
|
||||
chatId: string
|
||||
messageType: MessageType
|
||||
contentType: ChatMessage.ContentType
|
||||
sticker: StickerMessage
|
||||
image: ImageMessage
|
||||
audio: AudioMessage
|
||||
community: Uint8Array
|
||||
grant: Uint8Array
|
||||
displayName: string
|
||||
}
|
||||
|
||||
export namespace ChatMessage {
|
||||
export enum ContentType {
|
||||
UNKNOWN_CONTENT_TYPE = 'UNKNOWN_CONTENT_TYPE',
|
||||
TEXT_PLAIN = 'TEXT_PLAIN',
|
||||
STICKER = 'STICKER',
|
||||
STATUS = 'STATUS',
|
||||
EMOJI = 'EMOJI',
|
||||
TRANSACTION_COMMAND = 'TRANSACTION_COMMAND',
|
||||
SYSTEM_MESSAGE_CONTENT_PRIVATE_GROUP = 'SYSTEM_MESSAGE_CONTENT_PRIVATE_GROUP',
|
||||
IMAGE = 'IMAGE',
|
||||
AUDIO = 'AUDIO',
|
||||
COMMUNITY = 'COMMUNITY',
|
||||
SYSTEM_MESSAGE_GAP = 'SYSTEM_MESSAGE_GAP',
|
||||
}
|
||||
|
||||
enum __ContentTypeValues {
|
||||
UNKNOWN_CONTENT_TYPE = 0,
|
||||
TEXT_PLAIN = 1,
|
||||
STICKER = 2,
|
||||
STATUS = 3,
|
||||
EMOJI = 4,
|
||||
TRANSACTION_COMMAND = 5,
|
||||
SYSTEM_MESSAGE_CONTENT_PRIVATE_GROUP = 6,
|
||||
IMAGE = 7,
|
||||
AUDIO = 8,
|
||||
COMMUNITY = 9,
|
||||
SYSTEM_MESSAGE_GAP = 10,
|
||||
}
|
||||
|
||||
export namespace ContentType {
|
||||
export const codec = () => {
|
||||
return enumeration<typeof ContentType>(__ContentTypeValues)
|
||||
}
|
||||
}
|
||||
|
||||
export const codec = (): Codec<ChatMessage> => {
|
||||
return message<ChatMessage>({
|
||||
1: { name: 'clock', codec: uint64 },
|
||||
2: { name: 'timestamp', codec: uint64 },
|
||||
3: { name: 'text', codec: string },
|
||||
4: { name: 'responseTo', codec: string },
|
||||
5: { name: 'ensName', codec: string },
|
||||
6: { name: 'chatId', codec: string },
|
||||
7: { name: 'messageType', codec: MessageType.codec() },
|
||||
8: { name: 'contentType', codec: ChatMessage.ContentType.codec() },
|
||||
9: { name: 'sticker', codec: StickerMessage.codec() },
|
||||
10: { name: 'image', codec: ImageMessage.codec() },
|
||||
11: { name: 'audio', codec: AudioMessage.codec() },
|
||||
12: { name: 'community', codec: bytes },
|
||||
13: { name: 'grant', codec: bytes },
|
||||
14: { name: 'displayName', codec: string },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: ChatMessage): Uint8Array => {
|
||||
return encodeMessage(obj, ChatMessage.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): ChatMessage => {
|
||||
return decodeMessage(buf, ChatMessage.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export enum MessageType {
|
||||
UNKNOWN_MESSAGE_TYPE = 'UNKNOWN_MESSAGE_TYPE',
|
||||
ONE_TO_ONE = 'ONE_TO_ONE',
|
||||
PUBLIC_GROUP = 'PUBLIC_GROUP',
|
||||
PRIVATE_GROUP = 'PRIVATE_GROUP',
|
||||
SYSTEM_MESSAGE_PRIVATE_GROUP = 'SYSTEM_MESSAGE_PRIVATE_GROUP',
|
||||
COMMUNITY_CHAT = 'COMMUNITY_CHAT',
|
||||
SYSTEM_MESSAGE_GAP = 'SYSTEM_MESSAGE_GAP',
|
||||
}
|
||||
|
||||
enum __MessageTypeValues {
|
||||
UNKNOWN_MESSAGE_TYPE = 0,
|
||||
ONE_TO_ONE = 1,
|
||||
PUBLIC_GROUP = 2,
|
||||
PRIVATE_GROUP = 3,
|
||||
SYSTEM_MESSAGE_PRIVATE_GROUP = 4,
|
||||
COMMUNITY_CHAT = 5,
|
||||
SYSTEM_MESSAGE_GAP = 6,
|
||||
}
|
||||
|
||||
export namespace MessageType {
|
||||
export const codec = () => {
|
||||
return enumeration<typeof MessageType>(__MessageTypeValues)
|
||||
}
|
||||
}
|
||||
export enum ImageType {
|
||||
UNKNOWN_IMAGE_TYPE = 'UNKNOWN_IMAGE_TYPE',
|
||||
PNG = 'PNG',
|
||||
JPEG = 'JPEG',
|
||||
WEBP = 'WEBP',
|
||||
GIF = 'GIF',
|
||||
}
|
||||
|
||||
enum __ImageTypeValues {
|
||||
UNKNOWN_IMAGE_TYPE = 0,
|
||||
PNG = 1,
|
||||
JPEG = 2,
|
||||
WEBP = 3,
|
||||
GIF = 4,
|
||||
}
|
||||
|
||||
export namespace ImageType {
|
||||
export const codec = () => {
|
||||
return enumeration<typeof ImageType>(__ImageTypeValues)
|
||||
}
|
||||
}
|
||||
export interface EmojiReaction {
|
||||
clock: bigint
|
||||
chatId: string
|
||||
messageId: string
|
||||
messageType: MessageType
|
||||
type: EmojiReaction.Type
|
||||
retracted: boolean
|
||||
grant: Uint8Array
|
||||
}
|
||||
|
||||
export namespace EmojiReaction {
|
||||
export enum Type {
|
||||
UNKNOWN_EMOJI_REACTION_TYPE = 'UNKNOWN_EMOJI_REACTION_TYPE',
|
||||
LOVE = 'LOVE',
|
||||
THUMBS_UP = 'THUMBS_UP',
|
||||
THUMBS_DOWN = 'THUMBS_DOWN',
|
||||
LAUGH = 'LAUGH',
|
||||
SAD = 'SAD',
|
||||
ANGRY = 'ANGRY',
|
||||
}
|
||||
|
||||
enum __TypeValues {
|
||||
UNKNOWN_EMOJI_REACTION_TYPE = 0,
|
||||
LOVE = 1,
|
||||
THUMBS_UP = 2,
|
||||
THUMBS_DOWN = 3,
|
||||
LAUGH = 4,
|
||||
SAD = 5,
|
||||
ANGRY = 6,
|
||||
}
|
||||
|
||||
export namespace Type {
|
||||
export const codec = () => {
|
||||
return enumeration<typeof Type>(__TypeValues)
|
||||
}
|
||||
}
|
||||
|
||||
export const codec = (): Codec<EmojiReaction> => {
|
||||
return message<EmojiReaction>({
|
||||
1: { name: 'clock', codec: uint64 },
|
||||
2: { name: 'chatId', codec: string },
|
||||
3: { name: 'messageId', codec: string },
|
||||
4: { name: 'messageType', codec: MessageType.codec() },
|
||||
5: { name: 'type', codec: EmojiReaction.Type.codec() },
|
||||
6: { name: 'retracted', codec: bool },
|
||||
7: { name: 'grant', codec: bytes },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: EmojiReaction): Uint8Array => {
|
||||
return encodeMessage(obj, EmojiReaction.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): EmojiReaction => {
|
||||
return decodeMessage(buf, EmojiReaction.codec())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
syntax = "proto3";
|
||||
|
||||
import "enums.proto";
|
||||
|
||||
message PinMessage {
|
||||
uint64 clock = 1;
|
||||
string message_id = 2;
|
||||
string chat_id = 3;
|
||||
bool pinned = 4;
|
||||
// The type of message (public/one-to-one/private-group-chat)
|
||||
MessageType message_type = 5;
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
/* eslint-disable import/export */
|
||||
/* eslint-disable @typescript-eslint/no-namespace */
|
||||
|
||||
import {
|
||||
encodeMessage,
|
||||
decodeMessage,
|
||||
message,
|
||||
uint64,
|
||||
string,
|
||||
bool,
|
||||
enumeration,
|
||||
} from 'protons-runtime'
|
||||
import type { Codec } from 'protons-runtime'
|
||||
|
||||
export interface PinMessage {
|
||||
clock: bigint
|
||||
messageId: string
|
||||
chatId: string
|
||||
pinned: boolean
|
||||
messageType: MessageType
|
||||
}
|
||||
|
||||
export namespace PinMessage {
|
||||
export const codec = (): Codec<PinMessage> => {
|
||||
return message<PinMessage>({
|
||||
1: { name: 'clock', codec: uint64 },
|
||||
2: { name: 'messageId', codec: string },
|
||||
3: { name: 'chatId', codec: string },
|
||||
4: { name: 'pinned', codec: bool },
|
||||
5: { name: 'messageType', codec: MessageType.codec() },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: PinMessage): Uint8Array => {
|
||||
return encodeMessage(obj, PinMessage.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): PinMessage => {
|
||||
return decodeMessage(buf, PinMessage.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export enum MessageType {
|
||||
UNKNOWN_MESSAGE_TYPE = 'UNKNOWN_MESSAGE_TYPE',
|
||||
ONE_TO_ONE = 'ONE_TO_ONE',
|
||||
PUBLIC_GROUP = 'PUBLIC_GROUP',
|
||||
PRIVATE_GROUP = 'PRIVATE_GROUP',
|
||||
SYSTEM_MESSAGE_PRIVATE_GROUP = 'SYSTEM_MESSAGE_PRIVATE_GROUP',
|
||||
COMMUNITY_CHAT = 'COMMUNITY_CHAT',
|
||||
SYSTEM_MESSAGE_GAP = 'SYSTEM_MESSAGE_GAP',
|
||||
}
|
||||
|
||||
enum __MessageTypeValues {
|
||||
UNKNOWN_MESSAGE_TYPE = 0,
|
||||
ONE_TO_ONE = 1,
|
||||
PUBLIC_GROUP = 2,
|
||||
PRIVATE_GROUP = 3,
|
||||
SYSTEM_MESSAGE_PRIVATE_GROUP = 4,
|
||||
COMMUNITY_CHAT = 5,
|
||||
SYSTEM_MESSAGE_GAP = 6,
|
||||
}
|
||||
|
||||
export namespace MessageType {
|
||||
export const codec = () => {
|
||||
return enumeration<typeof MessageType>(__MessageTypeValues)
|
||||
}
|
||||
}
|
||||
export enum ImageType {
|
||||
UNKNOWN_IMAGE_TYPE = 'UNKNOWN_IMAGE_TYPE',
|
||||
PNG = 'PNG',
|
||||
JPEG = 'JPEG',
|
||||
WEBP = 'WEBP',
|
||||
GIF = 'GIF',
|
||||
}
|
||||
|
||||
enum __ImageTypeValues {
|
||||
UNKNOWN_IMAGE_TYPE = 0,
|
||||
PNG = 1,
|
||||
JPEG = 2,
|
||||
WEBP = 3,
|
||||
GIF = 4,
|
||||
}
|
||||
|
||||
export namespace ImageType {
|
||||
export const codec = () => {
|
||||
return enumeration<typeof ImageType>(__ImageTypeValues)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
syntax = "proto3";
|
||||
|
||||
message SignedPreKey {
|
||||
bytes signed_pre_key = 1;
|
||||
uint32 version = 2;
|
||||
uint32 protocol_version = 3;
|
||||
}
|
||||
|
||||
// X3DH prekey bundle
|
||||
message Bundle {
|
||||
// Identity key
|
||||
bytes identity = 1;
|
||||
// Installation id
|
||||
map<string,SignedPreKey> signed_pre_keys = 2;
|
||||
// Prekey signature
|
||||
bytes signature = 4;
|
||||
|
||||
// When the bundle was created locally
|
||||
int64 timestamp = 5;
|
||||
}
|
||||
|
||||
message BundleContainer {
|
||||
reserved 3;
|
||||
// X3DH prekey bundle
|
||||
Bundle bundle = 1;
|
||||
// Private signed prekey
|
||||
bytes private_signed_pre_key = 2;
|
||||
}
|
||||
|
||||
message DRHeader {
|
||||
// Current ratchet public key
|
||||
bytes key = 1;
|
||||
// Number of the message in the sending chain
|
||||
uint32 n = 2;
|
||||
// Length of the previous sending chain
|
||||
uint32 pn = 3;
|
||||
// Bundle ID
|
||||
bytes id = 4;
|
||||
}
|
||||
|
||||
message DHHeader {
|
||||
// Compressed ephemeral public key
|
||||
bytes key = 1;
|
||||
}
|
||||
|
||||
message X3DHHeader {
|
||||
reserved 3;
|
||||
// Ephemeral key used
|
||||
bytes key = 1;
|
||||
// Used bundle's signed prekey
|
||||
bytes id = 4;
|
||||
}
|
||||
|
||||
// Hash Ratchet Header
|
||||
message HRHeader {
|
||||
// community key ID
|
||||
uint32 key_id = 1;
|
||||
// Community message number for this key_id
|
||||
uint32 seq_no = 2;
|
||||
// Community ID
|
||||
string group_id = 3;
|
||||
}
|
||||
|
||||
// Direct message value
|
||||
message EncryptedMessageProtocol {
|
||||
X3DHHeader X3DH_header = 1;
|
||||
DRHeader DR_header = 2;
|
||||
DHHeader DH_header = 101;
|
||||
HRHeader HR_header = 102;
|
||||
// Encrypted payload
|
||||
bytes payload = 3;
|
||||
}
|
||||
|
||||
// Top-level protocol message
|
||||
message ProtocolMessage {
|
||||
// The device id of the sender
|
||||
string installation_id = 2;
|
||||
|
||||
// List of bundles
|
||||
repeated Bundle bundles = 3;
|
||||
|
||||
// One to one message, encrypted, indexed by installation_id
|
||||
// TODO map here is redundant in case of community messages
|
||||
map<string,EncryptedMessageProtocol> encrypted_message = 101;
|
||||
|
||||
// Public chats, not encrypted
|
||||
bytes public_message = 102;
|
||||
}
|
|
@ -0,0 +1,234 @@
|
|||
/* eslint-disable import/export */
|
||||
/* eslint-disable @typescript-eslint/no-namespace */
|
||||
|
||||
import {
|
||||
encodeMessage,
|
||||
decodeMessage,
|
||||
message,
|
||||
bytes,
|
||||
uint32,
|
||||
int64,
|
||||
string,
|
||||
} from 'protons-runtime'
|
||||
import type { Codec } from 'protons-runtime'
|
||||
|
||||
export interface SignedPreKey {
|
||||
signedPreKey: Uint8Array
|
||||
version: number
|
||||
protocolVersion: number
|
||||
}
|
||||
|
||||
export namespace SignedPreKey {
|
||||
export const codec = (): Codec<SignedPreKey> => {
|
||||
return message<SignedPreKey>({
|
||||
1: { name: 'signedPreKey', codec: bytes },
|
||||
2: { name: 'version', codec: uint32 },
|
||||
3: { name: 'protocolVersion', codec: uint32 },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: SignedPreKey): Uint8Array => {
|
||||
return encodeMessage(obj, SignedPreKey.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): SignedPreKey => {
|
||||
return decodeMessage(buf, SignedPreKey.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface Bundle {
|
||||
identity: Uint8Array
|
||||
signedPreKeys: SignedPreKey
|
||||
signature: Uint8Array
|
||||
timestamp: bigint
|
||||
}
|
||||
|
||||
export namespace Bundle {
|
||||
export const codec = (): Codec<Bundle> => {
|
||||
return message<Bundle>({
|
||||
1: { name: 'identity', codec: bytes },
|
||||
2: { name: 'signedPreKeys', codec: SignedPreKey.codec() },
|
||||
4: { name: 'signature', codec: bytes },
|
||||
5: { name: 'timestamp', codec: int64 },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: Bundle): Uint8Array => {
|
||||
return encodeMessage(obj, Bundle.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): Bundle => {
|
||||
return decodeMessage(buf, Bundle.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface BundleContainer {
|
||||
bundle: Bundle
|
||||
privateSignedPreKey: Uint8Array
|
||||
}
|
||||
|
||||
export namespace BundleContainer {
|
||||
export const codec = (): Codec<BundleContainer> => {
|
||||
return message<BundleContainer>({
|
||||
1: { name: 'bundle', codec: Bundle.codec() },
|
||||
2: { name: 'privateSignedPreKey', codec: bytes },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: BundleContainer): Uint8Array => {
|
||||
return encodeMessage(obj, BundleContainer.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): BundleContainer => {
|
||||
return decodeMessage(buf, BundleContainer.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface DRHeader {
|
||||
key: Uint8Array
|
||||
n: number
|
||||
pn: number
|
||||
id: Uint8Array
|
||||
}
|
||||
|
||||
export namespace DRHeader {
|
||||
export const codec = (): Codec<DRHeader> => {
|
||||
return message<DRHeader>({
|
||||
1: { name: 'key', codec: bytes },
|
||||
2: { name: 'n', codec: uint32 },
|
||||
3: { name: 'pn', codec: uint32 },
|
||||
4: { name: 'id', codec: bytes },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: DRHeader): Uint8Array => {
|
||||
return encodeMessage(obj, DRHeader.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): DRHeader => {
|
||||
return decodeMessage(buf, DRHeader.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface DHHeader {
|
||||
key: Uint8Array
|
||||
}
|
||||
|
||||
export namespace DHHeader {
|
||||
export const codec = (): Codec<DHHeader> => {
|
||||
return message<DHHeader>({
|
||||
1: { name: 'key', codec: bytes },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: DHHeader): Uint8Array => {
|
||||
return encodeMessage(obj, DHHeader.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): DHHeader => {
|
||||
return decodeMessage(buf, DHHeader.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface X3DHHeader {
|
||||
key: Uint8Array
|
||||
id: Uint8Array
|
||||
}
|
||||
|
||||
export namespace X3DHHeader {
|
||||
export const codec = (): Codec<X3DHHeader> => {
|
||||
return message<X3DHHeader>({
|
||||
1: { name: 'key', codec: bytes },
|
||||
4: { name: 'id', codec: bytes },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: X3DHHeader): Uint8Array => {
|
||||
return encodeMessage(obj, X3DHHeader.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): X3DHHeader => {
|
||||
return decodeMessage(buf, X3DHHeader.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface HRHeader {
|
||||
keyId: number
|
||||
seqNo: number
|
||||
groupId: string
|
||||
}
|
||||
|
||||
export namespace HRHeader {
|
||||
export const codec = (): Codec<HRHeader> => {
|
||||
return message<HRHeader>({
|
||||
1: { name: 'keyId', codec: uint32 },
|
||||
2: { name: 'seqNo', codec: uint32 },
|
||||
3: { name: 'groupId', codec: string },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: HRHeader): Uint8Array => {
|
||||
return encodeMessage(obj, HRHeader.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): HRHeader => {
|
||||
return decodeMessage(buf, HRHeader.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface EncryptedMessageProtocol {
|
||||
X3DHHeader: X3DHHeader
|
||||
DRHeader: DRHeader
|
||||
DHHeader: DHHeader
|
||||
HRHeader: HRHeader
|
||||
payload: Uint8Array
|
||||
}
|
||||
|
||||
export namespace EncryptedMessageProtocol {
|
||||
export const codec = (): Codec<EncryptedMessageProtocol> => {
|
||||
return message<EncryptedMessageProtocol>({
|
||||
1: { name: 'X3DHHeader', codec: X3DHHeader.codec() },
|
||||
2: { name: 'DRHeader', codec: DRHeader.codec() },
|
||||
101: { name: 'DHHeader', codec: DHHeader.codec() },
|
||||
102: { name: 'HRHeader', codec: HRHeader.codec() },
|
||||
3: { name: 'payload', codec: bytes },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: EncryptedMessageProtocol): Uint8Array => {
|
||||
return encodeMessage(obj, EncryptedMessageProtocol.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): EncryptedMessageProtocol => {
|
||||
return decodeMessage(buf, EncryptedMessageProtocol.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface ProtocolMessage {
|
||||
installationId: string
|
||||
bundles: Bundle[]
|
||||
encryptedMessage: EncryptedMessageProtocol
|
||||
publicMessage: Uint8Array
|
||||
}
|
||||
|
||||
export namespace ProtocolMessage {
|
||||
export const codec = (): Codec<ProtocolMessage> => {
|
||||
return message<ProtocolMessage>({
|
||||
2: { name: 'installationId', codec: string },
|
||||
3: { name: 'bundles', codec: Bundle.codec(), repeats: true },
|
||||
101: {
|
||||
name: 'encryptedMessage',
|
||||
codec: EncryptedMessageProtocol.codec(),
|
||||
},
|
||||
102: { name: 'publicMessage', codec: bytes },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: ProtocolMessage): Uint8Array => {
|
||||
return encodeMessage(obj, ProtocolMessage.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): ProtocolMessage => {
|
||||
return decodeMessage(buf, ProtocolMessage.codec())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
syntax = "proto3";
|
||||
|
||||
/* Specs:
|
||||
:AUTOMATIC
|
||||
To Send - "AUTOMATIC" status ping every 5 minutes
|
||||
Display - Online for up to 5 minutes from the last clock, after that Offline
|
||||
:ALWAYS_ONLINE
|
||||
To Send - "ALWAYS_ONLINE" status ping every 5 minutes
|
||||
Display - Online for up to 2 weeks from the last clock, after that Offline
|
||||
:INACTIVE
|
||||
To Send - A single "INACTIVE" status ping
|
||||
Display - Offline forever
|
||||
Note: Only send pings if the user interacted with the app in the last x minutes. */
|
||||
message StatusUpdate {
|
||||
|
||||
uint64 clock = 1;
|
||||
|
||||
StatusType status_type = 2;
|
||||
|
||||
string custom_text = 3;
|
||||
|
||||
enum StatusType {
|
||||
UNKNOWN_STATUS_TYPE = 0;
|
||||
AUTOMATIC = 1;
|
||||
DO_NOT_DISTURB = 2;
|
||||
ALWAYS_ONLINE = 3;
|
||||
INACTIVE = 4;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/* eslint-disable import/export */
|
||||
/* eslint-disable @typescript-eslint/no-namespace */
|
||||
|
||||
import {
|
||||
enumeration,
|
||||
encodeMessage,
|
||||
decodeMessage,
|
||||
message,
|
||||
uint64,
|
||||
string,
|
||||
} from 'protons-runtime'
|
||||
import type { Codec } from 'protons-runtime'
|
||||
|
||||
export interface StatusUpdate {
|
||||
clock: bigint
|
||||
statusType: StatusUpdate.StatusType
|
||||
customText: string
|
||||
}
|
||||
|
||||
export namespace StatusUpdate {
|
||||
export enum StatusType {
|
||||
UNKNOWN_STATUS_TYPE = 'UNKNOWN_STATUS_TYPE',
|
||||
AUTOMATIC = 'AUTOMATIC',
|
||||
DO_NOT_DISTURB = 'DO_NOT_DISTURB',
|
||||
ALWAYS_ONLINE = 'ALWAYS_ONLINE',
|
||||
INACTIVE = 'INACTIVE',
|
||||
}
|
||||
|
||||
enum __StatusTypeValues {
|
||||
UNKNOWN_STATUS_TYPE = 0,
|
||||
AUTOMATIC = 1,
|
||||
DO_NOT_DISTURB = 2,
|
||||
ALWAYS_ONLINE = 3,
|
||||
INACTIVE = 4,
|
||||
}
|
||||
|
||||
export namespace StatusType {
|
||||
export const codec = () => {
|
||||
return enumeration<typeof StatusType>(__StatusTypeValues)
|
||||
}
|
||||
}
|
||||
|
||||
export const codec = (): Codec<StatusUpdate> => {
|
||||
return message<StatusUpdate>({
|
||||
1: { name: 'clock', codec: uint64 },
|
||||
2: { name: 'statusType', codec: StatusUpdate.StatusType.codec() },
|
||||
3: { name: 'customText', codec: string },
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: StatusUpdate): Uint8Array => {
|
||||
return encodeMessage(obj, StatusUpdate.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): StatusUpdate => {
|
||||
return decodeMessage(buf, StatusUpdate.codec())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
syntax = "proto3";
|
||||
|
||||
import "chat-identity.proto";
|
||||
|
||||
message Grant {
|
||||
bytes community_id = 1;
|
||||
bytes member_id = 2;
|
||||
string chat_id = 3;
|
||||
uint64 clock = 4;
|
||||
}
|
||||
|
||||
message CommunityMember {
|
||||
enum Roles {
|
||||
UNKNOWN_ROLE = 0;
|
||||
ROLE_ALL = 1;
|
||||
ROLE_MANAGE_USERS = 2;
|
||||
}
|
||||
repeated Roles roles = 1;
|
||||
}
|
||||
|
||||
message CommunityPermissions {
|
||||
enum Access {
|
||||
UNKNOWN_ACCESS = 0;
|
||||
NO_MEMBERSHIP = 1;
|
||||
INVITATION_ONLY = 2;
|
||||
ON_REQUEST = 3;
|
||||
}
|
||||
|
||||
bool ens_only = 1;
|
||||
// https://gitlab.matrix.org/matrix-org/olm/blob/master/docs/megolm.md is a candidate for the algorithm to be used in case we want to have private communityal chats, lighter than pairwise encryption using the DR, less secure, but more efficient for large number of participants
|
||||
bool private = 2;
|
||||
Access access = 3;
|
||||
}
|
||||
|
||||
message CommunityDescription {
|
||||
uint64 clock = 1;
|
||||
map<string,CommunityMember> members = 2;
|
||||
CommunityPermissions permissions = 3;
|
||||
ChatIdentity identity = 5;
|
||||
map<string,CommunityChat> chats = 6;
|
||||
repeated string ban_list = 7;
|
||||
map<string,CommunityCategory> categories = 8;
|
||||
}
|
||||
|
||||
message CommunityChat {
|
||||
map<string,CommunityMember> members = 1;
|
||||
CommunityPermissions permissions = 2;
|
||||
ChatIdentity identity = 3;
|
||||
string category_id = 4;
|
||||
int32 position = 5;
|
||||
}
|
||||
|
||||
message CommunityCategory {
|
||||
string category_id = 1;
|
||||
string name = 2;
|
||||
int32 position = 3;
|
||||
}
|
||||
|
||||
message CommunityInvitation {
|
||||
bytes community_description = 1;
|
||||
bytes grant = 2;
|
||||
string chat_id = 3;
|
||||
bytes public_key = 4;
|
||||
}
|
||||
|
||||
message CommunityRequestToJoin {
|
||||
uint64 clock = 1;
|
||||
string ens_name = 2;
|
||||
string chat_id = 3;
|
||||
bytes community_id = 4;
|
||||
}
|
||||
|
||||
message CommunityRequestToJoinResponse {
|
||||
uint64 clock = 1;
|
||||
CommunityDescription community = 2;
|
||||
bool accepted = 3;
|
||||
bytes grant = 4;
|
||||
}
|
|
@ -0,0 +1,400 @@
|
|||
/* eslint-disable import/export */
|
||||
/* eslint-disable @typescript-eslint/no-namespace */
|
||||
|
||||
import { encodeMessage, decodeMessage, message, bytes, string, uint64, enumeration, bool, int32 } from 'protons-runtime'
|
||||
import type { Codec } from 'protons-runtime'
|
||||
|
||||
export interface Grant {
|
||||
communityId: Uint8Array
|
||||
memberId: Uint8Array
|
||||
chatId: string
|
||||
clock: bigint
|
||||
}
|
||||
|
||||
export namespace Grant {
|
||||
export const codec = (): Codec<Grant> => {
|
||||
return message<Grant>({
|
||||
1: { name: 'communityId', codec: bytes },
|
||||
2: { name: 'memberId', codec: bytes },
|
||||
3: { name: 'chatId', codec: string },
|
||||
4: { name: 'clock', codec: uint64 }
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: Grant): Uint8Array => {
|
||||
return encodeMessage(obj, Grant.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): Grant => {
|
||||
return decodeMessage(buf, Grant.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface CommunityMember {
|
||||
roles: CommunityMember.Roles[]
|
||||
}
|
||||
|
||||
export namespace CommunityMember {
|
||||
export enum Roles {
|
||||
UNKNOWN_ROLE = 'UNKNOWN_ROLE',
|
||||
ROLE_ALL = 'ROLE_ALL',
|
||||
ROLE_MANAGE_USERS = 'ROLE_MANAGE_USERS'
|
||||
}
|
||||
|
||||
enum __RolesValues {
|
||||
UNKNOWN_ROLE = 0,
|
||||
ROLE_ALL = 1,
|
||||
ROLE_MANAGE_USERS = 2
|
||||
}
|
||||
|
||||
export namespace Roles {
|
||||
export const codec = () => {
|
||||
return enumeration<typeof Roles>(__RolesValues)
|
||||
}
|
||||
}
|
||||
|
||||
export const codec = (): Codec<CommunityMember> => {
|
||||
return message<CommunityMember>({
|
||||
1: { name: 'roles', codec: CommunityMember.Roles.codec() }
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: CommunityMember): Uint8Array => {
|
||||
return encodeMessage(obj, CommunityMember.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): CommunityMember => {
|
||||
return decodeMessage(buf, CommunityMember.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface CommunityPermissions {
|
||||
ensOnly: boolean
|
||||
private: boolean
|
||||
access: CommunityPermissions.Access
|
||||
}
|
||||
|
||||
export namespace CommunityPermissions {
|
||||
export enum Access {
|
||||
UNKNOWN_ACCESS = 'UNKNOWN_ACCESS',
|
||||
NO_MEMBERSHIP = 'NO_MEMBERSHIP',
|
||||
INVITATION_ONLY = 'INVITATION_ONLY',
|
||||
ON_REQUEST = 'ON_REQUEST'
|
||||
}
|
||||
|
||||
enum __AccessValues {
|
||||
UNKNOWN_ACCESS = 0,
|
||||
NO_MEMBERSHIP = 1,
|
||||
INVITATION_ONLY = 2,
|
||||
ON_REQUEST = 3
|
||||
}
|
||||
|
||||
export namespace Access {
|
||||
export const codec = () => {
|
||||
return enumeration<typeof Access>(__AccessValues)
|
||||
}
|
||||
}
|
||||
|
||||
export const codec = (): Codec<CommunityPermissions> => {
|
||||
return message<CommunityPermissions>({
|
||||
1: { name: 'ensOnly', codec: bool },
|
||||
2: { name: 'private', codec: bool },
|
||||
3: { name: 'access', codec: CommunityPermissions.Access.codec() }
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: CommunityPermissions): Uint8Array => {
|
||||
return encodeMessage(obj, CommunityPermissions.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): CommunityPermissions => {
|
||||
return decodeMessage(buf, CommunityPermissions.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface CommunityDescription {
|
||||
clock: bigint
|
||||
members: CommunityMember
|
||||
permissions: CommunityPermissions
|
||||
identity: ChatIdentity
|
||||
chats: CommunityChat
|
||||
banList: string[]
|
||||
categories: CommunityCategory
|
||||
}
|
||||
|
||||
export namespace CommunityDescription {
|
||||
export const codec = (): Codec<CommunityDescription> => {
|
||||
return message<CommunityDescription>({
|
||||
1: { name: 'clock', codec: uint64 },
|
||||
2: { name: 'members', codec: CommunityMember.codec() },
|
||||
3: { name: 'permissions', codec: CommunityPermissions.codec() },
|
||||
5: { name: 'identity', codec: ChatIdentity.codec() },
|
||||
6: { name: 'chats', codec: CommunityChat.codec() },
|
||||
7: { name: 'banList', codec: string, repeats: true },
|
||||
8: { name: 'categories', codec: CommunityCategory.codec() }
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: CommunityDescription): Uint8Array => {
|
||||
return encodeMessage(obj, CommunityDescription.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): CommunityDescription => {
|
||||
return decodeMessage(buf, CommunityDescription.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface CommunityChat {
|
||||
members: CommunityMember
|
||||
permissions: CommunityPermissions
|
||||
identity: ChatIdentity
|
||||
categoryId: string
|
||||
position: number
|
||||
}
|
||||
|
||||
export namespace CommunityChat {
|
||||
export const codec = (): Codec<CommunityChat> => {
|
||||
return message<CommunityChat>({
|
||||
1: { name: 'members', codec: CommunityMember.codec() },
|
||||
2: { name: 'permissions', codec: CommunityPermissions.codec() },
|
||||
3: { name: 'identity', codec: ChatIdentity.codec() },
|
||||
4: { name: 'categoryId', codec: string },
|
||||
5: { name: 'position', codec: int32 }
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: CommunityChat): Uint8Array => {
|
||||
return encodeMessage(obj, CommunityChat.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): CommunityChat => {
|
||||
return decodeMessage(buf, CommunityChat.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface CommunityCategory {
|
||||
categoryId: string
|
||||
name: string
|
||||
position: number
|
||||
}
|
||||
|
||||
export namespace CommunityCategory {
|
||||
export const codec = (): Codec<CommunityCategory> => {
|
||||
return message<CommunityCategory>({
|
||||
1: { name: 'categoryId', codec: string },
|
||||
2: { name: 'name', codec: string },
|
||||
3: { name: 'position', codec: int32 }
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: CommunityCategory): Uint8Array => {
|
||||
return encodeMessage(obj, CommunityCategory.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): CommunityCategory => {
|
||||
return decodeMessage(buf, CommunityCategory.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface CommunityInvitation {
|
||||
communityDescription: Uint8Array
|
||||
grant: Uint8Array
|
||||
chatId: string
|
||||
publicKey: Uint8Array
|
||||
}
|
||||
|
||||
export namespace CommunityInvitation {
|
||||
export const codec = (): Codec<CommunityInvitation> => {
|
||||
return message<CommunityInvitation>({
|
||||
1: { name: 'communityDescription', codec: bytes },
|
||||
2: { name: 'grant', codec: bytes },
|
||||
3: { name: 'chatId', codec: string },
|
||||
4: { name: 'publicKey', codec: bytes }
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: CommunityInvitation): Uint8Array => {
|
||||
return encodeMessage(obj, CommunityInvitation.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): CommunityInvitation => {
|
||||
return decodeMessage(buf, CommunityInvitation.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface CommunityRequestToJoin {
|
||||
clock: bigint
|
||||
ensName: string
|
||||
chatId: string
|
||||
communityId: Uint8Array
|
||||
}
|
||||
|
||||
export namespace CommunityRequestToJoin {
|
||||
export const codec = (): Codec<CommunityRequestToJoin> => {
|
||||
return message<CommunityRequestToJoin>({
|
||||
1: { name: 'clock', codec: uint64 },
|
||||
2: { name: 'ensName', codec: string },
|
||||
3: { name: 'chatId', codec: string },
|
||||
4: { name: 'communityId', codec: bytes }
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: CommunityRequestToJoin): Uint8Array => {
|
||||
return encodeMessage(obj, CommunityRequestToJoin.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): CommunityRequestToJoin => {
|
||||
return decodeMessage(buf, CommunityRequestToJoin.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface CommunityRequestToJoinResponse {
|
||||
clock: bigint
|
||||
community: CommunityDescription
|
||||
accepted: boolean
|
||||
grant: Uint8Array
|
||||
}
|
||||
|
||||
export namespace CommunityRequestToJoinResponse {
|
||||
export const codec = (): Codec<CommunityRequestToJoinResponse> => {
|
||||
return message<CommunityRequestToJoinResponse>({
|
||||
1: { name: 'clock', codec: uint64 },
|
||||
2: { name: 'community', codec: CommunityDescription.codec() },
|
||||
3: { name: 'accepted', codec: bool },
|
||||
4: { name: 'grant', codec: bytes }
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: CommunityRequestToJoinResponse): Uint8Array => {
|
||||
return encodeMessage(obj, CommunityRequestToJoinResponse.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): CommunityRequestToJoinResponse => {
|
||||
return decodeMessage(buf, CommunityRequestToJoinResponse.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface ChatIdentity {
|
||||
clock: bigint
|
||||
ensName: string
|
||||
images: IdentityImage
|
||||
displayName: string
|
||||
description: string
|
||||
color: string
|
||||
emoji: string
|
||||
}
|
||||
|
||||
export namespace ChatIdentity {
|
||||
export const codec = (): Codec<ChatIdentity> => {
|
||||
return message<ChatIdentity>({
|
||||
1: { name: 'clock', codec: uint64 },
|
||||
2: { name: 'ensName', codec: string },
|
||||
3: { name: 'images', codec: IdentityImage.codec() },
|
||||
4: { name: 'displayName', codec: string },
|
||||
5: { name: 'description', codec: string },
|
||||
6: { name: 'color', codec: string },
|
||||
7: { name: 'emoji', codec: string }
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: ChatIdentity): Uint8Array => {
|
||||
return encodeMessage(obj, ChatIdentity.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): ChatIdentity => {
|
||||
return decodeMessage(buf, ChatIdentity.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export interface IdentityImage {
|
||||
payload: Uint8Array
|
||||
sourceType: IdentityImage.SourceType
|
||||
imageType: ImageType
|
||||
encryptionKeys: Uint8Array[]
|
||||
encrypted: boolean
|
||||
}
|
||||
|
||||
export namespace IdentityImage {
|
||||
export enum SourceType {
|
||||
UNKNOWN_SOURCE_TYPE = 'UNKNOWN_SOURCE_TYPE',
|
||||
RAW_PAYLOAD = 'RAW_PAYLOAD',
|
||||
ENS_AVATAR = 'ENS_AVATAR'
|
||||
}
|
||||
|
||||
enum __SourceTypeValues {
|
||||
UNKNOWN_SOURCE_TYPE = 0,
|
||||
RAW_PAYLOAD = 1,
|
||||
ENS_AVATAR = 2
|
||||
}
|
||||
|
||||
export namespace SourceType {
|
||||
export const codec = () => {
|
||||
return enumeration<typeof SourceType>(__SourceTypeValues)
|
||||
}
|
||||
}
|
||||
|
||||
export const codec = (): Codec<IdentityImage> => {
|
||||
return message<IdentityImage>({
|
||||
1: { name: 'payload', codec: bytes },
|
||||
2: { name: 'sourceType', codec: IdentityImage.SourceType.codec() },
|
||||
3: { name: 'imageType', codec: ImageType.codec() },
|
||||
4: { name: 'encryptionKeys', codec: bytes, repeats: true },
|
||||
5: { name: 'encrypted', codec: bool }
|
||||
})
|
||||
}
|
||||
|
||||
export const encode = (obj: IdentityImage): Uint8Array => {
|
||||
return encodeMessage(obj, IdentityImage.codec())
|
||||
}
|
||||
|
||||
export const decode = (buf: Uint8Array): IdentityImage => {
|
||||
return decodeMessage(buf, IdentityImage.codec())
|
||||
}
|
||||
}
|
||||
|
||||
export enum MessageType {
|
||||
UNKNOWN_MESSAGE_TYPE = 'UNKNOWN_MESSAGE_TYPE',
|
||||
ONE_TO_ONE = 'ONE_TO_ONE',
|
||||
PUBLIC_GROUP = 'PUBLIC_GROUP',
|
||||
PRIVATE_GROUP = 'PRIVATE_GROUP',
|
||||
SYSTEM_MESSAGE_PRIVATE_GROUP = 'SYSTEM_MESSAGE_PRIVATE_GROUP',
|
||||
COMMUNITY_CHAT = 'COMMUNITY_CHAT',
|
||||
SYSTEM_MESSAGE_GAP = 'SYSTEM_MESSAGE_GAP'
|
||||
}
|
||||
|
||||
enum __MessageTypeValues {
|
||||
UNKNOWN_MESSAGE_TYPE = 0,
|
||||
ONE_TO_ONE = 1,
|
||||
PUBLIC_GROUP = 2,
|
||||
PRIVATE_GROUP = 3,
|
||||
SYSTEM_MESSAGE_PRIVATE_GROUP = 4,
|
||||
COMMUNITY_CHAT = 5,
|
||||
SYSTEM_MESSAGE_GAP = 6
|
||||
}
|
||||
|
||||
export namespace MessageType {
|
||||
export const codec = () => {
|
||||
return enumeration<typeof MessageType>(__MessageTypeValues)
|
||||
}
|
||||
}
|
||||
export enum ImageType {
|
||||
UNKNOWN_IMAGE_TYPE = 'UNKNOWN_IMAGE_TYPE',
|
||||
PNG = 'PNG',
|
||||
JPEG = 'JPEG',
|
||||
WEBP = 'WEBP',
|
||||
GIF = 'GIF'
|
||||
}
|
||||
|
||||
enum __ImageTypeValues {
|
||||
UNKNOWN_IMAGE_TYPE = 0,
|
||||
PNG = 1,
|
||||
JPEG = 2,
|
||||
WEBP = 3,
|
||||
GIF = 4
|
||||
}
|
||||
|
||||
export namespace ImageType {
|
||||
export const codec = () => {
|
||||
return enumeration<typeof ImageType>(__ImageTypeValues)
|
||||
}
|
||||
}
|
|
@ -117,7 +117,7 @@ export interface CommunityDescription {
|
|||
members: CommunityMember
|
||||
permissions: CommunityPermissions
|
||||
identity: ChatIdentity
|
||||
chats: Record<string,CommunityChat>
|
||||
chats: CommunityChat
|
||||
banList: string[]
|
||||
categories: CommunityCategory
|
||||
archiveMagnetlinkClock: bigint
|
||||
|
|
|
@ -13,7 +13,11 @@ export class Account {
|
|||
const chatKey = getPublicKey(privateKey, true)
|
||||
|
||||
this.privateKey = bytesToHex(privateKey)
|
||||
this.publicKey = bytesToHex(publicKey)
|
||||
// this.publicKey = bytesToHex(publicKey)
|
||||
this.publicKey =
|
||||
'0x04ac419dac9a8bbb58825a3cde60eef0ee71b8cf6c63df611eeefc8e7aac7c79b55954b679d24cf5ec82da7ed921caf240628a9bfb3450c5111a9cffe54e631811'
|
||||
|
||||
// TODO?: add 0x prefix to public key
|
||||
this.chatKey = bytesToHex(chatKey)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
import { createClient } from './client'
|
||||
|
||||
describe('Client', () => {
|
||||
it('', async () => {
|
||||
const client = await createClient({ publicKey: '' })
|
||||
await client.start()
|
||||
|
||||
debugger
|
||||
})
|
||||
})
|
|
@ -1,23 +1,735 @@
|
|||
import { getPredefinedBootstrapNodes, Waku } from 'js-waku'
|
||||
// todo: validate sig
|
||||
// todo: observer contact updates
|
||||
// todo: replies
|
||||
// todo: subsribe to newly added channel
|
||||
// todo: identities/members?
|
||||
// todo: getmesages
|
||||
import { bytesToHex } from 'ethereum-cryptography/utils'
|
||||
import { Waku, waku_message } from 'js-waku'
|
||||
import difference from 'lodash/difference'
|
||||
import sortBy from 'lodash/sortBy'
|
||||
import uniqBy from 'lodash/uniqBy'
|
||||
import chunk from 'lodash/chunk'
|
||||
|
||||
// import { Fleet } from 'js-waku/build/main/lib/discovery/predefined'
|
||||
// TOOD: params
|
||||
// TODO?: reconnect/keep alive
|
||||
// TODO?: error handling
|
||||
// TOOD?: teardown
|
||||
export async function createClient(): Promise<Waku> {
|
||||
// TODO?: set tiemout
|
||||
const waku = await Waku.create({
|
||||
bootstrap: {
|
||||
default: false,
|
||||
// peers: ['/dns4/node-01.gc-us-central1-a.wakuv2.prod.statusim.net/tcp/443/wss/p2p/16Uiu2HAmVkKntsECaYfefR1V2yCR79CegLATuTPE6B9TxgxBiiiA']
|
||||
peers: [
|
||||
'/dns4/node-01.gc-us-central1-a.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAmJb2e28qLXxT5kZxVUUoJt72EMzNGXB47Rxx5hw3q4YjS',
|
||||
],
|
||||
},
|
||||
// TODO?: remove
|
||||
libp2p: { config: { pubsub: { enabled: true, emitSelf: true } } },
|
||||
})
|
||||
await waku.waitForRemotePeer()
|
||||
return waku
|
||||
import { ApplicationMetadataMessage } from '../protos/application-metadata-message'
|
||||
// import { ChatIdentity } from '../protos/chat-identity'
|
||||
import { ChatMessage, DeleteMessage, EditMessage } from '../protos/chat-message'
|
||||
import { EmojiReaction } from '../protos/emoji-reaction'
|
||||
import { PinMessage } from '../protos/pin-message'
|
||||
import { ProtocolMessage } from '../protos/protocol-message'
|
||||
import { Account } from './account'
|
||||
// import { ChatIdentity } from './wire/chat_identity'
|
||||
import { idToContentTopic } from './contentTopic'
|
||||
import { createSymKeyFromPassword } from './encryption'
|
||||
import { payloadToId } from './utils/payload-to-id'
|
||||
import { CommunityDescription } from './wire/community_description'
|
||||
|
||||
import type { WakuMessage } from 'js-waku'
|
||||
|
||||
// todo: rename to chat
|
||||
type CommunityType = CommunityDescription['proto']
|
||||
type ChannelType = any
|
||||
export type MessageType = ChatMessage & {
|
||||
messageId: string
|
||||
pinned: boolean
|
||||
reactions: Reactions
|
||||
}
|
||||
|
||||
type Reaction =
|
||||
| 'heart'
|
||||
| 'thumbs-up'
|
||||
| 'thumbs-down'
|
||||
| 'smile'
|
||||
| 'sad'
|
||||
| 'angry'
|
||||
|
||||
type Reactions = {
|
||||
[key in Reaction]: {
|
||||
count: number
|
||||
me: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export interface ClientOptions {
|
||||
publicKey: string
|
||||
env?: 'production' | 'test'
|
||||
}
|
||||
|
||||
class Client {
|
||||
private waku!: Waku
|
||||
private communityPublicKey: string
|
||||
|
||||
public account?: Account
|
||||
// fixme
|
||||
public community!: Community
|
||||
|
||||
constructor(options: ClientOptions) {
|
||||
this.communityPublicKey = options.publicKey
|
||||
}
|
||||
|
||||
public async start() {
|
||||
const waku = await Waku.create({
|
||||
bootstrap: {
|
||||
default: false,
|
||||
peers: [
|
||||
// '/dns4/node-01.gc-us-central1-a.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAmJb2e28qLXxT5kZxVUUoJt72EMzNGXB47Rxx5hw3q4YjS',
|
||||
'/dns4/node-01.do-ams3.wakuv2.test.statusim.net/tcp/8000/wss/p2p/16Uiu2HAmPLe7Mzm8TsYUubgCAW1aJoeFScxrLj8ppHFivPo97bUZ',
|
||||
// '/dns4/node-01.do-ams3.status.test.statusim.net/tcp/30303/p2p/16Uiu2HAkukebeXjTQ9QDBeNDWuGfbaSg79wkkhK4vPocLgR6QFDf'
|
||||
],
|
||||
},
|
||||
})
|
||||
await waku.waitForRemotePeer()
|
||||
this.waku = waku
|
||||
|
||||
const community = new Community(this, waku, this.communityPublicKey)
|
||||
await community.start()
|
||||
this.community = community
|
||||
}
|
||||
|
||||
public async stop() {
|
||||
await this.waku.stop()
|
||||
}
|
||||
|
||||
public createAccount = (): Account => {
|
||||
this.account = new Account()
|
||||
return this.account
|
||||
}
|
||||
|
||||
// TODO?: should this exist
|
||||
// public deleteAccount = () => {
|
||||
// this.account = undefined
|
||||
// }
|
||||
}
|
||||
|
||||
class Community {
|
||||
private client: Client
|
||||
private waku: Waku
|
||||
private communityPublicKey: string
|
||||
private communityContentTopic!: string
|
||||
private communityDecryptionKey!: Uint8Array
|
||||
public communityMetadata!: CommunityType
|
||||
private communityCallback: ((community: CommunityType) => void) | undefined
|
||||
private channelMessages: Partial<{ [key: string]: MessageType[] }> = {}
|
||||
private channelCallbacks: {
|
||||
[key: string]: (channel: ChannelType) => void
|
||||
} = {}
|
||||
private channelMessagesCallbacks: {
|
||||
[key: string]: (messages: MessageType[]) => void
|
||||
} = {}
|
||||
|
||||
constructor(client: Client, waku: Waku, publicKey: string) {
|
||||
this.client = client
|
||||
this.waku = waku
|
||||
this.communityPublicKey = publicKey
|
||||
}
|
||||
|
||||
public async start() {
|
||||
// Community
|
||||
this.communityContentTopic = idToContentTopic(this.communityPublicKey)
|
||||
this.communityDecryptionKey = await createSymKeyFromPassword(
|
||||
this.communityPublicKey
|
||||
)
|
||||
|
||||
this.waku.store.addDecryptionKey(this.communityDecryptionKey)
|
||||
await this.fetchCommunity()
|
||||
|
||||
// handle community not connected
|
||||
await this.observeCommunity()
|
||||
|
||||
// Channel messages
|
||||
await this.observeChannelMessages(Object.keys(this.communityMetadata.chats))
|
||||
|
||||
console.log('CLINET: STARTED')
|
||||
console.log('COMMUNITY:', this)
|
||||
}
|
||||
|
||||
// todo? and channels
|
||||
private async observeCommunity() {
|
||||
// console.log('here')
|
||||
this.waku.relay.addDecryptionKey(this.communityDecryptionKey)
|
||||
this.waku.relay.addObserver(
|
||||
message => {
|
||||
if (!message.payload) {
|
||||
return
|
||||
}
|
||||
|
||||
const decodedMetadata = ApplicationMetadataMessage.decode(
|
||||
message.payload
|
||||
)
|
||||
if (!decodedMetadata.payload) {
|
||||
return
|
||||
}
|
||||
|
||||
const decodedPayload = CommunityDescription.decode(
|
||||
decodedMetadata.payload
|
||||
)
|
||||
if (!decodedPayload.identity) {
|
||||
return
|
||||
}
|
||||
|
||||
const removedChats = difference(
|
||||
Object.keys(this.communityMetadata.chats),
|
||||
Object.keys(decodedPayload.proto.chats)
|
||||
)
|
||||
const addedChats = difference(
|
||||
Object.keys(decodedPayload.proto.chats),
|
||||
Object.keys(this.communityMetadata.chats)
|
||||
)
|
||||
|
||||
if (removedChats.length) {
|
||||
this.unobserveChannelMessages(removedChats)
|
||||
}
|
||||
|
||||
if (addedChats.length) {
|
||||
this.observeChannelMessages(addedChats)
|
||||
}
|
||||
|
||||
this.communityMetadata = decodedPayload.proto
|
||||
this.communityCallback?.(decodedPayload.proto)
|
||||
},
|
||||
[this.communityContentTopic]
|
||||
)
|
||||
}
|
||||
|
||||
private observeChannelMessages(chats: string[]) {
|
||||
const contentTopics: string[] = []
|
||||
|
||||
for (const chatId of chats) {
|
||||
const id = `${this.communityPublicKey}${chatId}`
|
||||
const channelContentTopic = idToContentTopic(id)
|
||||
const symKey = createSymKeyFromPassword(id)
|
||||
|
||||
contentTopics.push(channelContentTopic)
|
||||
|
||||
// todo: request waku feature to be passed as param
|
||||
// TODO?: use contentTopics as array instead of separate observer for each chat
|
||||
this.waku.relay.addDecryptionKey(symKey, {
|
||||
method: waku_message.DecryptionMethod.Symmetric,
|
||||
contentTopics: [channelContentTopic],
|
||||
})
|
||||
}
|
||||
|
||||
// todo?: delete Waku observers
|
||||
// todo?: check if waku propagates errors
|
||||
// todo!: request Waku feature to accept decryption keys as a param
|
||||
this.waku.relay.addObserver(this.handleMessage, contentTopics)
|
||||
console.log('Added observer', contentTopics)
|
||||
}
|
||||
|
||||
private unobserveChannelMessages(chatIds: string[]) {
|
||||
const contentTopics = chatIds.map(chatId => {
|
||||
const id = `${this.communityPublicKey}${chatId}`
|
||||
const channelContentTopic = idToContentTopic(id)
|
||||
|
||||
return channelContentTopic
|
||||
})
|
||||
|
||||
this.waku.relay.deleteObserver(this.handleMessage, contentTopics)
|
||||
}
|
||||
|
||||
private handleMessage = (wakuMessage: WakuMessage) => {
|
||||
console.log('MESSAGE: HANDLE')
|
||||
if (!wakuMessage.payload) {
|
||||
return
|
||||
}
|
||||
|
||||
const decodedProtocol = ProtocolMessage.decode(wakuMessage.payload)
|
||||
if (!decodedProtocol) {
|
||||
return
|
||||
}
|
||||
|
||||
const decodedMetadata = ApplicationMetadataMessage.decode(
|
||||
decodedProtocol.publicMessage
|
||||
// message.payload
|
||||
)
|
||||
if (!decodedMetadata.payload) {
|
||||
return
|
||||
}
|
||||
|
||||
console.log('MESSAGE: DECODED METADATA')
|
||||
|
||||
let shouldUpdate = false
|
||||
let _decodedPayload:
|
||||
| ChatMessage
|
||||
| EditMessage
|
||||
| DeleteMessage
|
||||
| PinMessage
|
||||
| EmojiReaction
|
||||
| undefined
|
||||
switch (decodedMetadata.type) {
|
||||
case ApplicationMetadataMessage.Type.TYPE_CHAT_MESSAGE: {
|
||||
console.log('MESSAGE:')
|
||||
|
||||
if (!wakuMessage.signaturePublicKey) {
|
||||
break
|
||||
}
|
||||
|
||||
const messageId = payloadToId(
|
||||
decodedProtocol.publicMessage,
|
||||
wakuMessage.signaturePublicKey
|
||||
)
|
||||
const decodedPayload = ChatMessage.decode(decodedMetadata.payload)
|
||||
|
||||
console.log('MESSAGE: DECODED')
|
||||
|
||||
// todo: explain
|
||||
// if (!decodedMetadata.identity) {
|
||||
// break
|
||||
// }
|
||||
// const decodedIdentity = ChatIdentity.decode(decodedProtocol.bundles[0].identity)
|
||||
|
||||
// todo: handle already received messages
|
||||
|
||||
// TODO?: ignore messages which are messageType !== COMMUNITY_CHAT
|
||||
|
||||
const channelId = decodedPayload.chatId.slice(68)
|
||||
|
||||
console.log('THIS:', this)
|
||||
|
||||
if (!this.channelMessages[channelId]) {
|
||||
this.channelMessages[channelId] = []
|
||||
}
|
||||
|
||||
console.log('THIS:', this)
|
||||
console.log('CHANNEL:', channelId)
|
||||
console.log('MESSAGES:', this.channelMessages)
|
||||
|
||||
const channelMessage: MessageType = {
|
||||
...decodedPayload,
|
||||
// ...decodedIdentity,
|
||||
messageId,
|
||||
pinned: false,
|
||||
reactions: {
|
||||
'thumbs-up': {
|
||||
count: 0,
|
||||
me: false,
|
||||
},
|
||||
'thumbs-down': {
|
||||
count: 0,
|
||||
me: false,
|
||||
},
|
||||
heart: {
|
||||
count: 0,
|
||||
me: false,
|
||||
},
|
||||
smile: {
|
||||
count: 0,
|
||||
me: false,
|
||||
},
|
||||
sad: {
|
||||
count: 0,
|
||||
me: false,
|
||||
},
|
||||
angry: {
|
||||
count: 0,
|
||||
me: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
console.log('MESSAGE: PREMAPPED')
|
||||
|
||||
this.channelMessages[channelId].push(channelMessage)
|
||||
|
||||
shouldUpdate = true
|
||||
_decodedPayload = decodedPayload
|
||||
|
||||
console.log('MESSAGE: MAPPED')
|
||||
|
||||
break
|
||||
}
|
||||
case ApplicationMetadataMessage.Type.TYPE_EDIT_MESSAGE: {
|
||||
if (!wakuMessage.signaturePublicKey) {
|
||||
break
|
||||
}
|
||||
|
||||
const decodedPayload = EditMessage.decode(decodedMetadata.payload)
|
||||
const channelId = decodedPayload.chatId.slice(68)
|
||||
const messageId = decodedPayload.messageId
|
||||
|
||||
const msgs = this.channelMessages[channelId].map(message => {
|
||||
if (message.messageId === messageId) {
|
||||
shouldUpdate = true
|
||||
|
||||
return {
|
||||
...message,
|
||||
// fixme?: other fields that user can edit
|
||||
text: decodedPayload.text,
|
||||
}
|
||||
}
|
||||
|
||||
return message
|
||||
})
|
||||
|
||||
this.channelMessages[channelId] = msgs
|
||||
_decodedPayload = decodedPayload
|
||||
|
||||
break
|
||||
}
|
||||
case ApplicationMetadataMessage.Type.TYPE_DELETE_MESSAGE: {
|
||||
const decodedPayload = DeleteMessage.decode(decodedMetadata.payload)
|
||||
const channelId = decodedPayload.chatId.slice(68)
|
||||
const messageId = decodedPayload.messageId
|
||||
|
||||
const msgs = this.channelMessages[channelId].filter(message => {
|
||||
if (message.messageId === messageId) {
|
||||
shouldUpdate = true
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
this.channelMessages[channelId] = msgs
|
||||
_decodedPayload = decodedPayload
|
||||
|
||||
break
|
||||
}
|
||||
case ApplicationMetadataMessage.Type.TYPE_PIN_MESSAGE: {
|
||||
const decodedPayload = PinMessage.decode(decodedMetadata.payload)
|
||||
const channelId = decodedPayload.chatId.slice(68)
|
||||
const messageId = decodedPayload.messageId
|
||||
|
||||
const message = this.channelMessages[channelId].find(
|
||||
message => message.messageId === messageId
|
||||
)
|
||||
|
||||
if (message) {
|
||||
message.pinned = Boolean(decodedPayload.pinned)
|
||||
shouldUpdate = true
|
||||
_decodedPayload = decodedPayload
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
case ApplicationMetadataMessage.Type.TYPE_EMOJI_REACTION: {
|
||||
if (!wakuMessage.signaturePublicKey) {
|
||||
break
|
||||
}
|
||||
|
||||
const decodedPayload = EmojiReaction.decode(decodedMetadata.payload)
|
||||
const channelId = decodedPayload.chatId.slice(68)
|
||||
const messageId = decodedPayload.messageId
|
||||
|
||||
const message = this.channelMessages[channelId].find(
|
||||
message => message.messageId === messageId
|
||||
)
|
||||
|
||||
if (message) {
|
||||
const isMe =
|
||||
this.client.account?.publicKey ===
|
||||
`0x${bytesToHex(wakuMessage.signaturePublicKey)}`
|
||||
|
||||
// TODO?: not needed anymore
|
||||
message.reactions ??= {
|
||||
'thumbs-up': {
|
||||
count: 0,
|
||||
me: false,
|
||||
},
|
||||
'thumbs-down': {
|
||||
count: 0,
|
||||
me: false,
|
||||
},
|
||||
heart: {
|
||||
count: 0,
|
||||
me: false,
|
||||
},
|
||||
smile: {
|
||||
count: 0,
|
||||
me: false,
|
||||
},
|
||||
sad: {
|
||||
count: 0,
|
||||
me: false,
|
||||
},
|
||||
angry: {
|
||||
count: 0,
|
||||
me: false,
|
||||
},
|
||||
}
|
||||
// fixme?: mutates
|
||||
setReactions(message.reactions, decodedPayload, isMe)
|
||||
|
||||
shouldUpdate = true
|
||||
_decodedPayload = decodedPayload
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
if (shouldUpdate && _decodedPayload) {
|
||||
const channelId = _decodedPayload.chatId.slice(68)
|
||||
const messages = this.channelMessages[channelId] ?? []
|
||||
|
||||
const sortedMessages = sortBy(messages, ['timestamp'])
|
||||
// todo: do not use
|
||||
const uniqueChannelMessages = uniqBy(sortedMessages, 'messageId')
|
||||
|
||||
this.channelMessages[channelId] = uniqueChannelMessages
|
||||
this.channelMessagesCallbacks[channelId]?.(
|
||||
this.channelMessages[channelId]
|
||||
)
|
||||
|
||||
console.log('MESSAGE: NEW', messages, channelId)
|
||||
}
|
||||
}
|
||||
|
||||
public async fetchCommunity() {
|
||||
let community: CommunityType | undefined
|
||||
|
||||
await this.waku.store.queryHistory([this.communityContentTopic], {
|
||||
decryptionKeys: [this.communityDecryptionKey],
|
||||
callback: messages => {
|
||||
for (const message of messages.reverse()) {
|
||||
if (!message.payload) {
|
||||
return
|
||||
}
|
||||
|
||||
const decodedMetadata = ApplicationMetadataMessage.decode(
|
||||
message.payload
|
||||
)
|
||||
if (!decodedMetadata.payload) {
|
||||
return
|
||||
}
|
||||
|
||||
const decodedPayload = CommunityDescription.decode(
|
||||
decodedMetadata.payload
|
||||
)
|
||||
// todo: explain
|
||||
if (!decodedPayload.identity) {
|
||||
return
|
||||
}
|
||||
|
||||
community = decodedPayload.proto
|
||||
this.communityMetadata = decodedPayload.proto
|
||||
|
||||
return true
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
return community
|
||||
}
|
||||
|
||||
public getMessages(channelId: string): MessageType[] {
|
||||
return this.channelMessages[channelId] ?? []
|
||||
}
|
||||
|
||||
public async fetchChannelMessages(
|
||||
channelId: string,
|
||||
callback: (messages: MessageType[], isDone: boolean) => boolean,
|
||||
options: { start: Date; end: Date; chunk: number /*total: number*/ }
|
||||
) {
|
||||
// const id = `${this.communityPublicKey}${channelId}`
|
||||
// const channelContentTopic = await idToContentTopic(channelId)
|
||||
// const symKey = await createSymKeyFromPassword(id)
|
||||
|
||||
// this.waku.store.addDecryptionKey(symKey, {
|
||||
// method: waku_message.DecryptionMethod.Symmetric,
|
||||
// contentTopics: [channelContentTopic],
|
||||
// })
|
||||
|
||||
const id = `${this.communityPublicKey}${channelId}`
|
||||
const channelContentTopic = await idToContentTopic(id)
|
||||
const symKey = await createSymKeyFromPassword(id)
|
||||
|
||||
// todo: request waku feature to be passed as param
|
||||
// this.waku.store.addDecryptionKey(symKey, {
|
||||
// method: waku_message.DecryptionMethod.Symmetric,
|
||||
// contentTopics: [channelContentTopic],
|
||||
// })
|
||||
|
||||
const messages: MessageType[] = []
|
||||
let count = 0
|
||||
let shouldStop = false
|
||||
|
||||
await this.waku.store.queryHistory([channelContentTopic], {
|
||||
// timeFilter: {
|
||||
// startTime: options.start,
|
||||
// endTime: options.end,
|
||||
// },
|
||||
// todo!: increase after testing
|
||||
// pageSize: 5,
|
||||
decryptionKeys: [symKey],
|
||||
callback: wakuMessages => {
|
||||
for (const wakuMessage of wakuMessages.reverse()) {
|
||||
if (!wakuMessage.payload) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (!wakuMessage.signaturePublicKey) {
|
||||
continue
|
||||
}
|
||||
|
||||
const decodedProtocol = ProtocolMessage.decode(wakuMessage.payload)
|
||||
if (!decodedProtocol) {
|
||||
continue
|
||||
}
|
||||
|
||||
const decodedMetadata = ApplicationMetadataMessage.decode(
|
||||
decodedProtocol.publicMessage
|
||||
)
|
||||
if (!decodedMetadata.payload) {
|
||||
continue
|
||||
}
|
||||
|
||||
const messageId = payloadToId(
|
||||
decodedProtocol.publicMessage,
|
||||
wakuMessage.signaturePublicKey
|
||||
)
|
||||
|
||||
const decodedPayload = ChatMessage.decode(decodedMetadata.payload)
|
||||
|
||||
messages.push({
|
||||
...decodedPayload,
|
||||
messageId: messageId,
|
||||
pinned: false,
|
||||
reactions: {
|
||||
'thumbs-up': {
|
||||
count: 0,
|
||||
me: false,
|
||||
},
|
||||
'thumbs-down': {
|
||||
count: 0,
|
||||
me: false,
|
||||
},
|
||||
heart: {
|
||||
count: 0,
|
||||
me: false,
|
||||
},
|
||||
smile: {
|
||||
count: 0,
|
||||
me: false,
|
||||
},
|
||||
sad: {
|
||||
count: 0,
|
||||
me: false,
|
||||
},
|
||||
angry: {
|
||||
count: 0,
|
||||
me: false,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
count++
|
||||
}
|
||||
|
||||
for (const _chunk of chunk(messages, options.chunk)) {
|
||||
if (_chunk.length === options.chunk) {
|
||||
const sortedMessages = sortBy(messages.splice(0, options.chunk), [
|
||||
'timestamp',
|
||||
])
|
||||
const _messages = [
|
||||
...sortedMessages,
|
||||
...(this.channelMessages[channelId] ?? []),
|
||||
]
|
||||
|
||||
this.channelMessages[channelId] = _messages
|
||||
|
||||
shouldStop = callback(_messages, false)
|
||||
|
||||
if (shouldStop) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
if (messages.length && !shouldStop) {
|
||||
const _messages = [
|
||||
...messages.splice(0),
|
||||
...(this.channelMessages[channelId] ?? []),
|
||||
]
|
||||
const sortedMessages = sortBy(_messages, ['timestamp'])
|
||||
|
||||
this.channelMessages[channelId] = sortedMessages
|
||||
|
||||
callback(sortedMessages, true)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// do?: always return at least last state
|
||||
// callback(this.channelMessages[channelId] ?? [], true)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
public onCommunityUpdate(callback: (community: CommunityType) => void) {
|
||||
this.communityCallback = callback
|
||||
|
||||
return () => {
|
||||
this.communityCallback = undefined
|
||||
}
|
||||
}
|
||||
|
||||
public onChannelUpdate(
|
||||
channelId: string,
|
||||
callback: (channel: ChannelType) => void
|
||||
) {
|
||||
this.channelCallbacks[channelId] = callback
|
||||
|
||||
return () => {
|
||||
delete this.channelCallbacks[channelId]
|
||||
}
|
||||
}
|
||||
|
||||
public onChannelMessageUpdate(
|
||||
channelId: string,
|
||||
callback: (messages: MessageType[]) => void
|
||||
) {
|
||||
this.channelMessagesCallbacks[channelId] = callback
|
||||
|
||||
return () => {
|
||||
delete this.channelMessagesCallbacks[channelId]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fixme: type
|
||||
const REACTION_MAP: Record<EmojiReaction.Type, string> = {
|
||||
[EmojiReaction.Type.LOVE]: 'heart',
|
||||
[EmojiReaction.Type.THUMBS_UP]: 'thumbs-up',
|
||||
[EmojiReaction.Type.THUMBS_DOWN]: 'thumbs-down',
|
||||
[EmojiReaction.Type.LAUGH]: 'smile',
|
||||
[EmojiReaction.Type.SAD]: 'sad',
|
||||
[EmojiReaction.Type.ANGRY]: 'angry',
|
||||
[EmojiReaction.Type.UNKNOWN_EMOJI_REACTION_TYPE]: 'unknown',
|
||||
}
|
||||
|
||||
function setReactions(
|
||||
reactions: Reactions,
|
||||
reaction: EmojiReaction,
|
||||
isMe: boolean
|
||||
) {
|
||||
const type = REACTION_MAP[reaction.type]
|
||||
const isRetracted = reaction.retracted
|
||||
|
||||
if (!reactions[type]) {
|
||||
reactions[type] = {
|
||||
count: 1,
|
||||
me: isMe,
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
reactions[type].count += isRetracted ? -1 : 1
|
||||
|
||||
if (isMe) {
|
||||
reactions[type].me = isRetracted ? false : true
|
||||
}
|
||||
}
|
||||
|
||||
// todo export community metadata type
|
||||
export type { Client, Community, CommunityType }
|
||||
|
||||
export async function createClient(options: ClientOptions): Promise<Client> {
|
||||
const client = new Client(options)
|
||||
// TODO?: add start
|
||||
return client
|
||||
}
|
||||
|
|
|
@ -1,11 +1,19 @@
|
|||
import debug from 'debug'
|
||||
|
||||
import { Chat } from './chat'
|
||||
import { idToContentTopic } from './contentTopic'
|
||||
import { ApplicationMetadataMessage_Type } from './proto/status/v1/application_metadata_message'
|
||||
import { bufToHex, hexToBuf } from './utils'
|
||||
import { ApplicationMetadataMessage } from './wire/application_metadata_message'
|
||||
import { ChatMessage } from './wire/chat_message'
|
||||
import { CommunityDescription } from './wire/community_description'
|
||||
|
||||
import type { CommunityChat } from './wire/community_chat'
|
||||
import type { Waku } from 'js-waku'
|
||||
import type { Waku, WakuMessage } from 'js-waku'
|
||||
import { createSymKeyFromPassword } from './encryption'
|
||||
// import { createSymKeyFromPassword } from './encryption'
|
||||
|
||||
type Events = 'communityfetch' | 'communityudpate' | 'channelmessagereceive'
|
||||
|
||||
const dbg = debug('communities:community')
|
||||
|
||||
|
@ -93,3 +101,201 @@ export class Community {
|
|||
this.chats.set(chatId, chat)
|
||||
}
|
||||
}
|
||||
|
||||
class CommunityBeta {
|
||||
private waku: Waku
|
||||
private publicKey: string
|
||||
private callbacks: any = {}
|
||||
private communityData: any = {}
|
||||
private channelEvents: any = {}
|
||||
private symKey
|
||||
|
||||
private channels: Record<string, ChatMessage[]> = {}
|
||||
|
||||
constructor(waku: Waku, publicKey: string) {
|
||||
this.waku = waku
|
||||
this.publicKey = publicKey
|
||||
}
|
||||
|
||||
// todo: observer only if some callbacks set
|
||||
// todo: doc as to be called after callbacks have been added
|
||||
public async start() {
|
||||
this.symKey = await createSymKeyFromPassword(this.publicKey)
|
||||
|
||||
const communityTopic = idToContentTopic(this.publicKey)
|
||||
|
||||
await this.fetchCommunity(communityTopic)
|
||||
// this.observeCommunity(communityTopic)
|
||||
|
||||
// const channelTopics = []
|
||||
// this.observeChannelMessages(channelTopics)
|
||||
}
|
||||
|
||||
// TODO: should return unsubscribe function
|
||||
public addCallback(type: Events, callback: () => void) {
|
||||
const callbacks = this.callbacks[type]
|
||||
|
||||
if (!callbacks?.length) {
|
||||
this.callbacks[type] = [callback]
|
||||
} else {
|
||||
this.callbacks[type].push(callback)
|
||||
}
|
||||
|
||||
return () => {
|
||||
this.callbacks[type].filter(cb => cb === callback)
|
||||
}
|
||||
}
|
||||
|
||||
private async fetchCommunity(topic: string): Promise<void> {
|
||||
let payload = undefined
|
||||
|
||||
// todo: request Waku to replace callbacks
|
||||
await this.waku.store
|
||||
.queryHistory([topic], {
|
||||
decryptionKeys: [this.symKey],
|
||||
callback: messages => {
|
||||
for (const message of messages.reverse()) {
|
||||
if (!message.payload) {
|
||||
return
|
||||
}
|
||||
|
||||
const decodedMetadata = ApplicationMetadataMessage.decode(
|
||||
message.payload
|
||||
)
|
||||
if (!decodedMetadata.payload) {
|
||||
return
|
||||
}
|
||||
|
||||
const decodedPayload = CommunityDescription.decode(
|
||||
decodedMetadata.payload
|
||||
)
|
||||
// todo: explain
|
||||
if (!decodedPayload.identity) {
|
||||
return
|
||||
}
|
||||
|
||||
payload = decodedPayload
|
||||
|
||||
// this.channels[chatId] = [
|
||||
// ...maessages,
|
||||
// ...this.channels[chatId]
|
||||
// ]
|
||||
|
||||
// this.callbacks[''].forEach(cb => cb(this.channels[chatId]))
|
||||
|
||||
return true
|
||||
}
|
||||
},
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error)
|
||||
})
|
||||
|
||||
if (payload) {
|
||||
const result = {
|
||||
name: undefined,
|
||||
description: undefined,
|
||||
color: undefined,
|
||||
channels: undefined,
|
||||
members: undefined,
|
||||
}
|
||||
|
||||
// todo: set `this.communityData`
|
||||
this.callbacks['communityfetch'].forEach(callback => callback(result))
|
||||
}
|
||||
|
||||
// todo: handle no (valid) messages case
|
||||
}
|
||||
|
||||
private observeCommunity(topic: string) {
|
||||
let payload = undefined
|
||||
|
||||
this.waku.relay.addObserver(
|
||||
message => {
|
||||
if (!message.payload) {
|
||||
return
|
||||
}
|
||||
|
||||
const decodedMetadata = ApplicationMetadataMessage.decode(
|
||||
message.payload
|
||||
)
|
||||
if (!decodedMetadata.payload) {
|
||||
return
|
||||
}
|
||||
|
||||
const decodedPayload = CommunityDescription.decode(
|
||||
decodedMetadata.payload
|
||||
)
|
||||
// todo: explain
|
||||
if (!decodedPayload.identity) {
|
||||
return
|
||||
}
|
||||
|
||||
payload = decodedPayload
|
||||
},
|
||||
[topic]
|
||||
)
|
||||
|
||||
if (payload) {
|
||||
const result = {
|
||||
name: undefined,
|
||||
description: undefined,
|
||||
color: undefined,
|
||||
channels: undefined,
|
||||
members: undefined,
|
||||
}
|
||||
|
||||
this.callbacks['communityupdate'].forEach(callback => callback(result))
|
||||
}
|
||||
|
||||
// todo: handle no (valid) messages case
|
||||
}
|
||||
|
||||
private observeChannelMessages(topics: string[]) {
|
||||
let payload = undefined
|
||||
|
||||
this.waku.relay.addObserver(message => {
|
||||
if (!message.payload || !message.timestamp) {
|
||||
return
|
||||
}
|
||||
|
||||
const decodedMetadata = ApplicationMetadataMessage.decode(message.payload)
|
||||
if (!decodedMetadata.payload) {
|
||||
return
|
||||
}
|
||||
|
||||
switch (decodedMetadata.type) {
|
||||
case ApplicationMetadataMessage_Type.TYPE_CHAT_MESSAGE: {
|
||||
const message = ChatMessage.decode(decodedMetadata.payload)
|
||||
|
||||
this.channels[message.chatId].push(message)
|
||||
this.callbacks.forEach(callback => callback(this.channels[chatId]))
|
||||
|
||||
// handle chat message
|
||||
// push
|
||||
|
||||
payload = message
|
||||
return
|
||||
}
|
||||
default:
|
||||
return
|
||||
}
|
||||
}, topics)
|
||||
|
||||
if (payload) {
|
||||
// todo: push to `this.channelEvents` by type
|
||||
|
||||
this.callbacks['channelmessagereceive'].forEach(callback =>
|
||||
callback(result)
|
||||
)
|
||||
}
|
||||
|
||||
// todo: handle no (valid) messages case
|
||||
}
|
||||
}
|
||||
|
||||
export function createCommunityBeta(waku: Waku, publicKey: string) {
|
||||
const community = new CommunityBeta(waku, publicKey)
|
||||
|
||||
return community
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// see
|
||||
import { PageDirection, WakuMessage } from 'js-waku'
|
||||
|
||||
import { idToContactCodeTopic } from './contentTopic'
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
import { createClient } from '../src/client'
|
||||
|
||||
const COMMUNITY_PUBLIC_KEY =
|
||||
'0x029f196bbfef4fa6a5eb81dd802133a63498325445ca1af1d154b1bb4542955133' // Boring community
|
||||
// '0x0243611cc13cc4e4390180fe8fd35234ab0fe2a7ba8d32e8ae5dd23b60ac7ec177'
|
||||
// '0x02e7102c85ed78e5be30124f8f52014b1135f972c383f55f83ec8ff50436cd1260'
|
||||
const CHANNEL_ID =
|
||||
// '00d3f525-a0cf-4c40-832d-543ec9f8188b' // #messages
|
||||
'30804ea7-bd66-4d5d-91eb-b2dcfe2515b3' // #test-messages
|
||||
|
||||
// 0x029f196bbfef4fa6a5eb81dd802133a63498325445ca1af1d154b1bb4542955133 c8b6df78-96be-4658-8bde-b51b2a09c599
|
||||
|
||||
;(async () => {
|
||||
const client = await createClient({ publicKey: COMMUNITY_PUBLIC_KEY })
|
||||
|
||||
await client.start()
|
||||
await client.createAccount()
|
||||
|
||||
const community = client.community.communityMetadata
|
||||
|
||||
client.community.fetchChannelMessages(
|
||||
CHANNEL_ID,
|
||||
(messages, isDone) => {
|
||||
console.log(messages)
|
||||
|
||||
return false
|
||||
},
|
||||
{ start: new Date('2022-01-01'), end: new Date(), chunk: 3 }
|
||||
)
|
||||
// client.community.onCommunityUpdate(community => console.log(community))
|
||||
// client.community.onChannelUpdate(CHANNEL_ID, channel => console.log(channel))
|
||||
// client.community.onChannelMessageUpdate(CHANNEL_ID, messages =>
|
||||
// console.log(messages)
|
||||
// )
|
||||
|
||||
// await client.stop()
|
||||
})()
|
|
@ -2,9 +2,7 @@ import pbkdf2 from 'pbkdf2'
|
|||
|
||||
const AESKeyLength = 32 // bytes
|
||||
|
||||
export async function createSymKeyFromPassword(
|
||||
password: string
|
||||
): Promise<Uint8Array> {
|
||||
export function createSymKeyFromPassword(password: string): Uint8Array {
|
||||
return pbkdf2.pbkdf2Sync(
|
||||
Buffer.from(password, 'utf-8'),
|
||||
'',
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
import { createClient } from '../src/client'
|
||||
|
||||
const COMMUNITY_PUBLIC_KEY =
|
||||
'0x029f196bbfef4fa6a5eb81dd802133a63498325445ca1af1d154b1bb4542955133' // Boring community
|
||||
// '0x0243611cc13cc4e4390180fe8fd35234ab0fe2a7ba8d32e8ae5dd23b60ac7ec177'
|
||||
// '0x02e7102c85ed78e5be30124f8f52014b1135f972c383f55f83ec8ff50436cd1260'
|
||||
const CHANNEL_ID = '00d3f525-a0cf-4c40-832d-543ec9f8188b' // messages
|
||||
|
||||
;(async () => {
|
||||
const client = await createClient({ publicKey: COMMUNITY_PUBLIC_KEY })
|
||||
|
||||
await client.start()
|
||||
|
||||
// client.community.onCommunityUpdate(() => console.log("community:update"))
|
||||
// client.community.onChannelUpdate(() => console.log("channel:update"))
|
||||
client.community.onChannelMessageUpdate(CHANNEL_ID, () =>
|
||||
console.log('channel:message')
|
||||
)
|
||||
|
||||
// await client.stop()
|
||||
})()
|
|
@ -1,61 +1,44 @@
|
|||
// export { Chat } from './chat'
|
||||
// // export type { Client, ClientOptions } from './client'
|
||||
// export { createClient } from './client'
|
||||
// export { Community } from './community'
|
||||
// export { Contacts } from './contacts'
|
||||
// export type { GroupChat, GroupChatsType } from './groupChats'
|
||||
// export { GroupChats } from './groupChats'
|
||||
// export { Identity } from './identity'
|
||||
// export { Messenger } from './messenger'
|
||||
// export {
|
||||
// bufToHex,
|
||||
// compressPublicKey,
|
||||
// genPrivateKeyWithEntropy,
|
||||
// getLatestUserNickname,
|
||||
// hexToBuf,
|
||||
// } from './utils'
|
||||
// export { ApplicationMetadataMessage } from './wire/application_metadata_message'
|
||||
// export type {
|
||||
// AudioContent,
|
||||
// Content,
|
||||
// ContentType,
|
||||
// ImageContent,
|
||||
// StickerContent,
|
||||
// TextContent,
|
||||
// } from './wire/chat_message'
|
||||
// export { ChatMessage } from './wire/chat_message'
|
||||
// export { getPredefinedBootstrapNodes } from 'js-waku'
|
||||
// import { createClient } from '../src/client'
|
||||
|
||||
import { createClient } from './client-v2'
|
||||
|
||||
const COMMUNITY_PUBLIC_KEY =
|
||||
'0x029dd5fecbd689dc11e2a5b399afed92cf1fab65d315b883efca753e8f3882f3bd' // compressed
|
||||
// const COMMUNITY_PUBLIC_KEY =
|
||||
// '0x0403aeff2fdd0044b136e06afa6d69bb563bb7b3fd518bb30c0d5115a2e020840a2247966c2cc9953ed02cc391e8883b3319f63a31e5f5369d0fb72b62b23dfcbd' // compressed
|
||||
// '0x029f196bbfef4fa6a5eb81dd802133a63498325445ca1af1d154b1bb4542955133' // Boring community
|
||||
// // '0x0243611cc13cc4e4390180fe8fd35234ab0fe2a7ba8d32e8ae5dd23b60ac7ec177'
|
||||
// // '0x02e7102c85ed78e5be30124f8f52014b1135f972c383f55f83ec8ff50436cd1260'
|
||||
// const CHANNEL_ID =
|
||||
// // '00d3f525-a0cf-4c40-832d-543ec9f8188b' // #messages
|
||||
// '30804ea7-bd66-4d5d-91eb-b2dcfe2515b3' // #test-messages
|
||||
|
||||
// import { Community } from '../src/community'
|
||||
// import { Messenger } from '../src/messenger'
|
||||
// ;(async () => {
|
||||
// const client = await createClient({ publicKey: COMMUNITY_PUBLIC_KEY })
|
||||
|
||||
console.log('🚀 > COMMUNITY_PUBLIC_KEY', COMMUNITY_PUBLIC_KEY)
|
||||
;(async () => {
|
||||
const client = await createClient({
|
||||
env: 'test',
|
||||
publicKey: COMMUNITY_PUBLIC_KEY,
|
||||
callback: msgs => {},
|
||||
})
|
||||
// await client.start()
|
||||
// await client.createAccount()
|
||||
|
||||
const communityDescription = await client.getCommunityDescription()
|
||||
// const community = client.community.communityMetadata
|
||||
|
||||
console.log('meow', communityDescription)
|
||||
// client.community.fetchChannelMessages(
|
||||
// CHANNEL_ID,
|
||||
// (messages, isDone) => {
|
||||
// console.log(messages)
|
||||
|
||||
// console.log(communityDescription)
|
||||
// Retrieve Community's metadata (e.g. description)
|
||||
// const community = await Community.instantiateCommunity(COMMUNITY_PUBLIC_KEY, client)
|
||||
// // Retrieve and subscribe to messages
|
||||
// const messenger = await Messenger.create(, client)
|
||||
// // TODO: Register observers/callbacks
|
||||
// messenger.addObserver(() => {})
|
||||
// await client.stop()
|
||||
})()
|
||||
// return false
|
||||
// },
|
||||
// { start: new Date('2022-01-01'), end: new Date(), chunk: 3 }
|
||||
// )
|
||||
// // client.community.onCommunityUpdate(community => console.log(community))
|
||||
// // client.community.onChannelUpdate(CHANNEL_ID, channel => console.log(channel))
|
||||
// // client.community.onChannelMessageUpdate(CHANNEL_ID, messages =>
|
||||
// // console.log(messages)
|
||||
// // )
|
||||
|
||||
// export {}
|
||||
// // await client.stop()
|
||||
// })()
|
||||
|
||||
// export type {} from './'
|
||||
export type { Client, ClientOptions, Community, MessageType } from './client'
|
||||
export type { Account } from './account'
|
||||
export { createClient } from './client'
|
||||
|
||||
// import { Community } from './client'
|
||||
|
||||
// type h = Community['communityMetadata']['members'][0]
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
import { keccak256 } from 'ethereum-cryptography/keccak'
|
||||
import { getPublicKey, sign, utils } from 'ethereum-cryptography/secp256k1'
|
||||
import { bytesToHex } from 'ethereum-cryptography/utils'
|
||||
|
||||
export class Members {
|
||||
constructor() {}
|
||||
}
|
|
@ -1,11 +1,7 @@
|
|||
/* eslint-disable */
|
||||
import Long from 'long'
|
||||
import _m0 from 'protobufjs/minimal'
|
||||
import {
|
||||
ImageType,
|
||||
imageTypeFromJSON,
|
||||
imageTypeToJSON,
|
||||
} from './enums'
|
||||
import { ImageType, imageTypeFromJSON, imageTypeToJSON } from './enums'
|
||||
|
||||
export const protobufPackage = 'communities.v1'
|
||||
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
/* eslint-disable */
|
||||
import Long from 'long'
|
||||
import _m0 from 'protobufjs/minimal'
|
||||
import {
|
||||
MessageType,
|
||||
messageTypeFromJSON,
|
||||
messageTypeToJSON,
|
||||
} from './enums'
|
||||
import { MessageType, messageTypeFromJSON, messageTypeToJSON } from './enums'
|
||||
|
||||
export const protobufPackage = 'communities.v1'
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,7 @@
|
|||
import { idToContentTopic } from './id-to-content-topic'
|
||||
|
||||
describe('idToContentTopic', () => {
|
||||
it('should return content topic', () => {
|
||||
expect(idToContentTopic).toBeDefined()
|
||||
})
|
||||
})
|
|
@ -0,0 +1,16 @@
|
|||
import { keccak256 } from 'ethereum-cryptography/keccak'
|
||||
import { bytesToHex, hexToBytes } from 'ethereum-cryptography/utils'
|
||||
|
||||
/**
|
||||
* waku spec: https://rfc.vac.dev/spec/23/#bridging-waku-v1-and-waku-v2
|
||||
* status-go: https://github.com/status-im/status-go/blob/f6dc6f752a/wakuv2/common/topic.go#L66
|
||||
*/
|
||||
|
||||
const TOPIC_LENGTH = 4
|
||||
|
||||
export function idToContentTopic(id: string): string {
|
||||
const hash = keccak256(hexToBytes(id))
|
||||
const topic = hash.slice(0, TOPIC_LENGTH)
|
||||
|
||||
return `/waku/1/${bytesToHex(topic)}/rfc26`
|
||||
}
|
|
@ -2,8 +2,8 @@ import { keccak256 } from 'ethereum-cryptography/keccak'
|
|||
import { bytesToHex } from 'ethereum-cryptography/utils'
|
||||
|
||||
export function payloadToId(
|
||||
publicKey: Uint8Array,
|
||||
payload: Uint8Array
|
||||
payload: Uint8Array,
|
||||
publicKey: Uint8Array
|
||||
): string {
|
||||
const hash = keccak256(new Uint8Array([...publicKey, ...payload]))
|
||||
const hex = bytesToHex(hash)
|
||||
|
|
|
@ -8,6 +8,26 @@ import { ChatMessage, ContentType } from './chat_message'
|
|||
import type { AudioContent, ImageContent, StickerContent } from './chat_message'
|
||||
|
||||
describe('Chat Message', () => {
|
||||
// todo:
|
||||
// test('Encode & decode Text message', () => {
|
||||
// const payload = Buffer.from([1, 1])
|
||||
|
||||
// const imageContent: ImageContent = {
|
||||
// image: payload,
|
||||
// imageType: ImageType.IMAGE_TYPE_PNG,
|
||||
// contentType: ContentType.Image,
|
||||
// }
|
||||
|
||||
// const message = ChatMessage.createMessage(1, 1, 'chat-id', imageContent)
|
||||
|
||||
// const buf = message.encode()
|
||||
// const dec = ChatMessage.decode(buf)
|
||||
|
||||
// expect(dec.contentType).toEqual(ChatMessage_ContentType.CONTENT_TYPE_IMAGE)
|
||||
// expect(dec.image?.payload?.toString()).toEqual(payload.toString())
|
||||
// expect(dec.image?.type).toEqual(ImageType.IMAGE_TYPE_PNG)
|
||||
// })
|
||||
|
||||
test('Encode & decode Image message', () => {
|
||||
const payload = Buffer.from([1, 1])
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
"html-entities": "^2.3.2",
|
||||
"qrcode.react": "^3.0.1",
|
||||
"react": "^17.0.2",
|
||||
"react-content-loader": "^6.2.0",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-is": "^17.0.2",
|
||||
"react-router-dom": "^6.3.0",
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
import React from 'react'
|
||||
import { styled } from '~/src/styles/config'
|
||||
|
||||
import ContentLoader from 'react-content-loader'
|
||||
import { Box } from '~/src/system'
|
||||
|
||||
const CommunityInfoLoader = () => (
|
||||
<ContentLoader
|
||||
speed={2}
|
||||
width={185}
|
||||
height={50}
|
||||
viewBox="0 0 185 50"
|
||||
backgroundColor="rgba(233, 237, 241, 1)"
|
||||
foregroundColor="rgba(246, 248, 250, 1)"
|
||||
>
|
||||
<rect x="50" y="10" rx="3" ry="3" width="120" height="12" />
|
||||
<rect x="50" y="30" rx="3" ry="3" width="120" height="8" />
|
||||
<circle cx="24" cy="24" r="18" />
|
||||
</ContentLoader>
|
||||
)
|
||||
|
||||
const ChannelLoader = () => {
|
||||
return (
|
||||
<ContentLoader
|
||||
speed={2}
|
||||
width={272}
|
||||
height={40}
|
||||
viewBox="0 0 272 40"
|
||||
backgroundColor="rgba(233, 237, 241, 1)"
|
||||
foregroundColor="rgba(246, 248, 250, 1)"
|
||||
>
|
||||
<rect x="41" y="13" rx="3" ry="3" width="140" height="14" />
|
||||
<circle cx="20" cy="20" r="12" />
|
||||
</ContentLoader>
|
||||
)
|
||||
}
|
||||
|
||||
export const Loading = () => {
|
||||
return (
|
||||
<Wrapper>
|
||||
<Sidebar>
|
||||
<CommunityInfoLoader />
|
||||
<Box css={{ padding: '8px 0' }}>
|
||||
<ChannelLoader />
|
||||
<ChannelLoader />
|
||||
<ChannelLoader />
|
||||
<ChannelLoader />
|
||||
<ChannelLoader />
|
||||
</Box>
|
||||
</Sidebar>
|
||||
<Box css={{ padding: '6px 10px' }}>
|
||||
<CommunityInfoLoader />
|
||||
|
||||
</Box>
|
||||
</Wrapper>
|
||||
)
|
||||
}
|
||||
|
||||
const Wrapper = styled('div', {
|
||||
overflow: 'hidden',
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
alignItems: 'stretch',
|
||||
background: '$background',
|
||||
})
|
||||
|
||||
const Sidebar = styled('div', {
|
||||
display: 'none',
|
||||
width: 304,
|
||||
flexShrink: 0,
|
||||
flexDirection: 'column',
|
||||
padding: '10px 16px',
|
||||
backgroundColor: '$gray-4',
|
||||
overflowY: 'scroll',
|
||||
|
||||
'@large': {
|
||||
display: 'flex',
|
||||
},
|
||||
})
|
|
@ -1,19 +1,30 @@
|
|||
import React from 'react'
|
||||
|
||||
import { Box } from '~/src/system'
|
||||
import { useChats } from '~/src/protocol'
|
||||
|
||||
import { ChannelGroup } from './channel-group'
|
||||
import { ChannelItem } from './channel-item'
|
||||
|
||||
const CHANNELS = {
|
||||
Public: ['welcome', 'general', 'random'],
|
||||
Internal: ['watercooler', 'pm'],
|
||||
}
|
||||
|
||||
export const Channels = () => {
|
||||
|
||||
const chats = useChats()
|
||||
|
||||
return (
|
||||
<Box>
|
||||
{Object.entries(CHANNELS).map(([group, channels]) => (
|
||||
<Box css={{padding:'8px 0'}}>
|
||||
{chats.map((chat) => (
|
||||
<ChannelItem
|
||||
key={chat.id}
|
||||
to={`/${chat.id}`}
|
||||
unread={false}
|
||||
muted={false}
|
||||
>
|
||||
{chat.identity!.displayName}
|
||||
</ChannelItem>
|
||||
))}
|
||||
|
||||
{/* {Object.entries(community.chats).map(([group, channels]) => (
|
||||
<ChannelGroup key={group} name={group}>
|
||||
{channels.map(channel => (
|
||||
<ChannelItem
|
||||
|
@ -26,7 +37,7 @@ export const Channels = () => {
|
|||
</ChannelItem>
|
||||
))}
|
||||
</ChannelGroup>
|
||||
))}
|
||||
))} */}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
import React from 'react'
|
||||
|
||||
import { useCommunity } from '~/src/protocol/use-community'
|
||||
import { useCommunity } from '~/src/protocol'
|
||||
import { Button, CopyInput, Dialog, Flex, Grid, Text } from '~/src/system'
|
||||
|
||||
export const CommunityDialog = () => {
|
||||
const { name, description, publicKey } = useCommunity()
|
||||
const { identity, publicKey='0xTODO' } = useCommunity()
|
||||
const { displayName, description} = identity
|
||||
|
||||
|
||||
return (
|
||||
<Dialog title={name}>
|
||||
<Dialog title={displayName}>
|
||||
<Dialog.Body>
|
||||
<Text>{description}</Text>
|
||||
</Dialog.Body>
|
||||
|
@ -17,7 +19,7 @@ export const CommunityDialog = () => {
|
|||
<CopyInput label="Community Public Key" value={publicKey} />
|
||||
<Text size="13" color="gray">
|
||||
To access this community, paste community public key in Status
|
||||
desktop or mobile app
|
||||
desktop or mobile app.
|
||||
</Text>
|
||||
</Grid>
|
||||
</Dialog.Body>
|
||||
|
|
|
@ -1,22 +1,24 @@
|
|||
import React from 'react'
|
||||
|
||||
import { useCommunity } from '~/src/protocol/use-community'
|
||||
import { useCommunity, useMembers } from '~/src/protocol'
|
||||
import { styled } from '~/src/styles/config'
|
||||
import { Avatar, DialogTrigger, Text } from '~/src/system'
|
||||
|
||||
import { CommunityDialog } from './community-dialog'
|
||||
|
||||
export const CommunityInfo = () => {
|
||||
const { name, imageUrl, membersCount } = useCommunity()
|
||||
const community = useCommunity()
|
||||
const members = useMembers()
|
||||
console.log("file: index.tsx > line 11 > CommunityInfo > community", community)
|
||||
|
||||
return (
|
||||
<DialogTrigger>
|
||||
<Button>
|
||||
<Avatar size={36} src={imageUrl} />
|
||||
<Avatar size={36} />
|
||||
<div>
|
||||
<Text>{name}</Text>
|
||||
<Text>{community.identity?.displayName}</Text>
|
||||
<Text color="gray" size={12}>
|
||||
{membersCount} members
|
||||
{members.length} members
|
||||
</Text>
|
||||
</div>
|
||||
</Button>
|
||||
|
|
|
@ -2,13 +2,14 @@ import React from 'react'
|
|||
|
||||
import { CreateProfileDialog } from '~/src/components/create-profile-dialog'
|
||||
import { useLocalStorage } from '~/src/hooks/use-local-storage'
|
||||
import { useAccount } from '~/src/protocol'
|
||||
import { Button, Flex } from '~/src/system'
|
||||
import { DialogTrigger } from '~/src/system/dialog'
|
||||
import { Grid } from '~/src/system/grid'
|
||||
import { Heading } from '~/src/system/heading'
|
||||
|
||||
import { ConnectWalletDialog } from './connect-wallet-dialog'
|
||||
import { SyncStatusProfileDialog } from './sync-status-profile-dialog'
|
||||
// import { ConnectWalletDialog } from './connect-wallet-dialog'
|
||||
// import { SyncStatusProfileDialog } from './sync-status-profile-dialog'
|
||||
import { ThrowawayProfileFoundDialog } from './throwaway-profile-found-dialog'
|
||||
|
||||
export const GetStarted = () => {
|
||||
|
@ -18,6 +19,8 @@ export const GetStarted = () => {
|
|||
// TODO: Add skip logic
|
||||
}
|
||||
|
||||
const [account, { createAccount }] = useAccount()
|
||||
|
||||
return (
|
||||
<Flex direction="column" align="center" gap={5}>
|
||||
<svg
|
||||
|
@ -138,29 +141,30 @@ export const GetStarted = () => {
|
|||
</defs>
|
||||
</svg>
|
||||
|
||||
<Heading align="center" size="17" weight="600">
|
||||
Want to jump into the discussion?
|
||||
</Heading>
|
||||
<Grid gap={3} align="center" justify="center">
|
||||
{/* <DialogTrigger>
|
||||
<Heading align="center" size="17" weight="600">
|
||||
Want to jump into the discussion?
|
||||
</Heading>
|
||||
<Grid gap={3} align="center" justify="center">
|
||||
{/* <DialogTrigger>
|
||||
<Button>Sync with Status profile</Button>
|
||||
<SyncStatusProfileDialog />
|
||||
</DialogTrigger> */}
|
||||
|
||||
<DialogTrigger>
|
||||
{/* <DialogTrigger>
|
||||
<Button>Connect Wallet</Button>
|
||||
<ConnectWalletDialog />
|
||||
</DialogTrigger>
|
||||
</DialogTrigger> */}
|
||||
|
||||
<DialogTrigger>
|
||||
<Button variant="outline">Use Throwaway Profile</Button>
|
||||
{throwawayProfile ? (
|
||||
<Button onClick={createAccount}>Use Throwaway Profile</Button>
|
||||
{/* <DialogTrigger>
|
||||
<Button>Use Throwaway Profile</Button>
|
||||
{account ? (
|
||||
<ThrowawayProfileFoundDialog onSkip={handleSkip} />
|
||||
) : (
|
||||
<CreateProfileDialog />
|
||||
)}
|
||||
</DialogTrigger>
|
||||
</Grid>
|
||||
</DialogTrigger> */}
|
||||
</Grid>
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react'
|
||||
|
||||
import { useProfile } from '~/src/protocol/use-profile'
|
||||
import { useAccount } from '~/src/protocol'
|
||||
import { Avatar, Dialog, EmojiHash, Flex, Heading, Text } from '~/src/system'
|
||||
|
||||
interface Props {
|
||||
|
@ -10,18 +10,22 @@ interface Props {
|
|||
export const ThrowawayProfileFoundDialog = (props: Props) => {
|
||||
const { onSkip } = props
|
||||
|
||||
const profile = useProfile()
|
||||
const [account] = useAccount()
|
||||
|
||||
const handleLoadThrowawayProfile = () => {
|
||||
// TODO: load throwaway profile
|
||||
}
|
||||
|
||||
if (!account) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog title="Throwaway Profile Found">
|
||||
<Dialog.Body gap="5">
|
||||
<Flex direction="column" align="center" gap="2">
|
||||
<Avatar size={64} src={profile.imageUrl} />
|
||||
<Heading weight="600">{profile.name}</Heading>
|
||||
<Avatar size={64} src={account.imageUrl} />
|
||||
<Heading weight="600">{account.name}</Heading>
|
||||
<Text color="gray">
|
||||
Chatkey: 0x63FaC9201494f0bd17B9892B9fae4d52fe3BD377
|
||||
</Text>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react'
|
||||
|
||||
import { ChatMenu } from '~/src/components/chat-menu'
|
||||
import { useChat } from '~/src/protocol/use-chat'
|
||||
import { useChannel } from '~/src/protocol'
|
||||
import { ContextMenuTrigger } from '~/src/system'
|
||||
|
||||
import { SidebarItem } from '../sidebar-item'
|
||||
|
@ -15,7 +15,7 @@ interface Props extends SidebarItemProps {
|
|||
export const ChatItem = (props: Props) => {
|
||||
const { children, ...sidebarItemProps } = props
|
||||
|
||||
const chat = useChat(children)
|
||||
const chat = useChannel(children)
|
||||
|
||||
return (
|
||||
<ContextMenuTrigger>
|
||||
|
|
|
@ -5,9 +5,9 @@ import { Box, Grid, Heading, IconButton } from '~/src/system'
|
|||
|
||||
import { ChatItem } from './chat-item'
|
||||
|
||||
const CHATS = ['vitalik.eth', 'pvl.eth', 'Climate Change']
|
||||
|
||||
export const Messages = () => {
|
||||
const chats = []
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Grid
|
||||
|
@ -21,7 +21,7 @@ export const Messages = () => {
|
|||
<EditIcon />
|
||||
</IconButton>
|
||||
</Grid>
|
||||
{CHATS.map(chat => (
|
||||
{chats.map(chat => (
|
||||
<ChatItem key={chat} to={`/${chat}`} unread={false} muted={false}>
|
||||
{chat}
|
||||
</ChatItem>
|
||||
|
|
|
@ -7,8 +7,10 @@ import { Separator } from '~/src/system'
|
|||
import { Channels } from './components/channels'
|
||||
import { CommunityInfo } from './components/community-info'
|
||||
import { GetStarted } from './components/get-started'
|
||||
import { useAccount } from '~/src/protocol'
|
||||
// import { Messages } from './components/messages'
|
||||
|
||||
|
||||
export const MainSidebar = () => {
|
||||
const { options } = useAppState()
|
||||
|
||||
|
@ -16,14 +18,20 @@ export const MainSidebar = () => {
|
|||
return null
|
||||
}
|
||||
|
||||
const [account] = useAccount()
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<CommunityInfo />
|
||||
<Channels />
|
||||
{/* <Separator css={{ margin: '16px 0' }} />
|
||||
<Messages /> */}
|
||||
{ !account && (
|
||||
<>
|
||||
<Separator css={{ margin: '16px 0' }} />
|
||||
<GetStarted />
|
||||
</>
|
||||
)}
|
||||
</Wrapper>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
import React from 'react'
|
||||
|
||||
import { useProfile } from '~/src/protocol/use-profile'
|
||||
import { useAccount } from '~/src/protocol'
|
||||
import { Avatar, Dialog, EmojiHash, Flex, Heading, Text } from '~/src/system'
|
||||
|
||||
export const DisconnectDialog = () => {
|
||||
const profile = useProfile()
|
||||
const [account] = useAccount()
|
||||
|
||||
return (
|
||||
<Dialog title="Disconnect">
|
||||
<Dialog.Body gap="5">
|
||||
<Text>Do you want to disconnect your profile from this browser?</Text>
|
||||
<Flex direction="column" align="center" gap="2">
|
||||
<Avatar size={64} src={profile.imageUrl} />
|
||||
<Heading weight="600">{profile.name}</Heading>
|
||||
<Avatar size={64} src={account.imageUrl} />
|
||||
<Heading weight="600">{account.name}</Heading>
|
||||
<Text color="gray">
|
||||
Chatkey: 0x63FaC9201494f0bd17B9892B9fae4d52fe3BD377
|
||||
Chatkey: {account.chatKey}
|
||||
</Text>
|
||||
<EmojiHash />
|
||||
</Flex>
|
||||
|
|
|
@ -6,8 +6,11 @@ import { Grid, Heading } from '~/src/system'
|
|||
import { MemberGroup } from './member-group'
|
||||
import { MemberItem } from './member-item'
|
||||
import { UserItem } from './user-item'
|
||||
import { useMembers } from '~/src/protocol'
|
||||
|
||||
export function MemberSidebar() {
|
||||
const members = useMembers()
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<Heading size="15" css={{ marginBottom: '$3' }}>
|
||||
|
@ -18,28 +21,19 @@ export function MemberSidebar() {
|
|||
<UserItem />
|
||||
</MemberGroup>
|
||||
<MemberGroup label="Online">
|
||||
<MemberItem verified={true} untrustworthy={false} indicator="online">
|
||||
pvl.eth
|
||||
</MemberItem>
|
||||
<MemberItem verified={false} untrustworthy={false} indicator="online">
|
||||
carmen
|
||||
</MemberItem>
|
||||
<MemberItem verified={false} untrustworthy={false} indicator="online">
|
||||
carmen
|
||||
</MemberItem>
|
||||
</MemberGroup>
|
||||
<MemberGroup label="Offline">
|
||||
<MemberItem verified={false} untrustworthy indicator="offline">
|
||||
mark
|
||||
</MemberItem>
|
||||
<MemberItem
|
||||
verified={false}
|
||||
untrustworthy={false}
|
||||
indicator="offline"
|
||||
>
|
||||
mark
|
||||
</MemberItem>
|
||||
{members.map(member => (
|
||||
<MemberItem
|
||||
key={member}
|
||||
verified={false}
|
||||
untrustworthy={false}
|
||||
indicator="online"
|
||||
chatKey={member}
|
||||
>
|
||||
{member}
|
||||
</MemberItem>
|
||||
))}
|
||||
</MemberGroup>
|
||||
{/* <MemberGroup label="Offline"></MemberGroup> */}
|
||||
</Grid>
|
||||
</Wrapper>
|
||||
)
|
||||
|
|
|
@ -6,24 +6,24 @@ import type { AvatarProps } from '~/src/system/avatar'
|
|||
|
||||
interface Props {
|
||||
children: string
|
||||
chatKey: string
|
||||
verified: boolean
|
||||
untrustworthy: boolean
|
||||
indicator?: AvatarProps['indicator']
|
||||
}
|
||||
|
||||
export const MemberItem = (props: Props) => {
|
||||
const { children, indicator, verified, untrustworthy } = props
|
||||
const { children, chatKey, indicator, verified, untrustworthy } = props
|
||||
|
||||
return (
|
||||
<Flex gap="2" align="center" css={{ height: 56 }}>
|
||||
<Avatar
|
||||
size={32}
|
||||
indicator={indicator}
|
||||
src="https://images.unsplash.com/photo-1499155286265-79a9dc9c6380?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1284&q=80"
|
||||
/>
|
||||
<div>
|
||||
<Flex align="center" gap={1}>
|
||||
<Text size="15" color="accent">
|
||||
<Text size="15" color="accent" truncate>
|
||||
{children}
|
||||
</Text>
|
||||
{verified && (
|
||||
|
@ -62,7 +62,7 @@ export const MemberItem = (props: Props) => {
|
|||
)}
|
||||
</Flex>
|
||||
<EthAddress size={10} color="gray">
|
||||
71C7656EC7ab88b098defB751B7401B5f6d8976F
|
||||
{chatKey}
|
||||
</EthAddress>
|
||||
</div>
|
||||
</Flex>
|
||||
|
|
|
@ -1,30 +1,36 @@
|
|||
import React from 'react'
|
||||
|
||||
import { useProfile } from '~/src/protocol/use-profile'
|
||||
import { useAccount } from '~/src/protocol'
|
||||
import { styled } from '~/src/styles/config'
|
||||
import { Avatar, DialogTrigger, EthAddress, Flex, Text } from '~/src/system'
|
||||
|
||||
import { DisconnectDialog } from './disconnect-dialog'
|
||||
|
||||
export const UserItem = () => {
|
||||
const profile = useProfile()
|
||||
const [account] = useAccount()
|
||||
console.log("file: user-item.tsx > line 11 > UserItem > account", account)
|
||||
|
||||
if (!account) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex align="center" justify="between">
|
||||
<Flex gap="2" align="center" css={{ height: 56 }}>
|
||||
<Avatar size={32} src={profile.imageUrl} />
|
||||
<Avatar size={32} src={account.imageUrl} />
|
||||
<div>
|
||||
<Flex align="center" gap={1}>
|
||||
<Text size="15" color="accent">
|
||||
{profile.name}
|
||||
{account.name}
|
||||
</Text>
|
||||
</Flex>
|
||||
<EthAddress size={10} color="gray">
|
||||
{profile.publicKey}
|
||||
{account.publicKey}
|
||||
</EthAddress>
|
||||
</div>
|
||||
</Flex>
|
||||
|
||||
|
||||
<DialogTrigger>
|
||||
<DisconnectButton>
|
||||
<svg
|
||||
|
@ -44,7 +50,10 @@ export const UserItem = () => {
|
|||
/>
|
||||
</svg>
|
||||
</DisconnectButton>
|
||||
<DisconnectDialog />
|
||||
{account && (
|
||||
|
||||
<DisconnectDialog />
|
||||
)}
|
||||
</DialogTrigger>
|
||||
</Flex>
|
||||
)
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
export { ClientProvider } from './provider'
|
||||
export { useCommunity } from './use-community'
|
||||
export { useChats } from './use-chats'
|
||||
export { useChannel, Channel } from './use-channel'
|
||||
export { useAccount } from './use-account'
|
||||
export { useMembers } from './use-members'
|
|
@ -1,14 +1,20 @@
|
|||
import React, { createContext, useContext, useMemo } from 'react'
|
||||
import React, {
|
||||
useEffect,
|
||||
createContext,
|
||||
useContext,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react'
|
||||
|
||||
import type { Config } from '~/src/types/config'
|
||||
// import { createClient } from '@status-im/js'
|
||||
// import type { Client } from '@status-im/js'
|
||||
import {Loading} from '~/src/components/loading'
|
||||
import { createClient } from '@status-im/js'
|
||||
import type { Client, ClientOptions, Community , Account} from '@status-im/js'
|
||||
|
||||
interface ClientContext {
|
||||
client: Config
|
||||
}
|
||||
|
||||
const Context = createContext<ClientContext | undefined>(undefined)
|
||||
const Context = createContext<Client | undefined>(undefined)
|
||||
const CommunityContext = createContext<
|
||||
Community['communityMetadata'] | undefined
|
||||
>(undefined)
|
||||
|
||||
export function useClient() {
|
||||
const context = useContext(Context)
|
||||
|
@ -20,18 +26,74 @@ export function useClient() {
|
|||
return context
|
||||
}
|
||||
|
||||
export function useCommunity() {
|
||||
const context = useContext(CommunityContext)
|
||||
|
||||
if (!context) {
|
||||
// return {}
|
||||
throw new Error(`useCommunity must be used within a ClientProvider`)
|
||||
}
|
||||
|
||||
return context
|
||||
}
|
||||
|
||||
interface ClientProviderProps {
|
||||
config: Config
|
||||
options: ClientOptions
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
export const ClientProvider = (props: ClientProviderProps) => {
|
||||
const { config, children } = props
|
||||
const [client, setClient] = useState<Client>()
|
||||
const [community, setCommunity] = useState<Community['communityMetadata']>()
|
||||
const [account, setAccount] = useState<Account>()
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
||||
const client = useMemo(() => {
|
||||
// return createClient({ ...config })
|
||||
return { client: config }
|
||||
}, [config])
|
||||
const { options, children } = props
|
||||
|
||||
return <Context.Provider value={client}>{children}</Context.Provider>
|
||||
// const client = useMemo(() => {
|
||||
// return createClient(options)
|
||||
// }, [options])
|
||||
|
||||
useEffect(() => {
|
||||
const loadClient = async () => {
|
||||
// setLoading(true)
|
||||
const client = await createClient({ publicKey: props.options.publicKey })
|
||||
console.log('starting')
|
||||
await client.start()
|
||||
console.log('init', client)
|
||||
setCommunity(client.community.communityMetadata)
|
||||
console.log("file: provider.tsx > line 64 > loadClient > client.community.communityMetadata", client.community.communityMetadata)
|
||||
|
||||
setClient(client)
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
loadClient()
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (client) {
|
||||
console.log('useEffect subscribe')
|
||||
return client.community.onCommunityUpdate(community => {
|
||||
setCommunity(community)
|
||||
console.log("file: provider.tsx > line 75 > useEffect > community", community)
|
||||
})
|
||||
}
|
||||
}, [client])
|
||||
|
||||
// if (!client) {
|
||||
// return
|
||||
// }
|
||||
|
||||
return (
|
||||
<Context.Provider value={client}>
|
||||
<CommunityContext.Provider value={community}>
|
||||
{loading ? (
|
||||
<Loading />
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
</CommunityContext.Provider>
|
||||
</Context.Provider>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
import { useCallback, useState ,useEffect} from 'react'
|
||||
import { useClient } from './provider'
|
||||
|
||||
// return {
|
||||
// name: 'Satoshi',
|
||||
// publicKey: '71C7656EC7ab88b098defB751B7401B5f6d8976F',
|
||||
// imageUrl:
|
||||
// 'https://images.unsplash.com/photo-1546776310-eef45dd6d63c?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1620&q=80',
|
||||
// }
|
||||
|
||||
export const useAccount = () => {
|
||||
const client = useClient()
|
||||
|
||||
const [account, setAccount] = useState<any>()
|
||||
console.log("file: use-account.tsx > line 15 > useAccount > account", account)
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
// load account
|
||||
},[])
|
||||
|
||||
const createAccount = useCallback(() => {
|
||||
const account = client.createAccount()
|
||||
console.log("createAccount > account", account)
|
||||
|
||||
|
||||
setAccount(account)
|
||||
// TODO: save account
|
||||
|
||||
|
||||
|
||||
return account
|
||||
}, [])
|
||||
|
||||
const deleteAccount = useCallback(() => {
|
||||
// client.deleteAccount()
|
||||
setAccount(undefined)
|
||||
}, [])
|
||||
|
||||
return [account, { createAccount, deleteAccount }] as const
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
import { useMemo } from 'react'
|
||||
import { useClient } from './provider'
|
||||
import { useCommunity } from '~/src/protocol/use-community'
|
||||
import { Community } from '@status-im/js'
|
||||
|
||||
export type Channel = Community['communityMetadata']['chats'][0]
|
||||
|
||||
export const useChannel = (id: string): Channel => {
|
||||
const client = useClient()
|
||||
|
||||
const community = useCommunity()
|
||||
|
||||
|
||||
|
||||
|
||||
const chat = useMemo(() => {
|
||||
return Object.entries(community.chats).find(
|
||||
([chatId]) => chatId === id
|
||||
)?.[1]
|
||||
}, [community, id])
|
||||
|
||||
return chat!
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
import { useClient } from './provider'
|
||||
|
||||
export const useChannels = () => {
|
||||
const client = useClient()
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
export interface Chat {
|
||||
type: 'channel' | 'group-chat' | 'chat'
|
||||
imageUrl?: string
|
||||
}
|
||||
|
||||
const chats: Record<string, Chat> = {
|
||||
welcome: { type: 'channel', imageUrl: '' },
|
||||
general: {
|
||||
type: 'channel',
|
||||
imageUrl:
|
||||
'https://images.unsplash.com/flagged/photo-1559717865-a99cac1c95d8?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8Y2l0eSxuaWdodHx8fHx8fDE2NDk4NDYyMzI&ixlib=rb-1.2.1&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080',
|
||||
},
|
||||
random: {
|
||||
type: 'channel',
|
||||
imageUrl:
|
||||
'https://images.unsplash.com/photo-1599420186946-7b6fb4e297f0?ixlib=rb-1.2.1&ixid=MnwxMjA3fDF8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1287&q=80',
|
||||
},
|
||||
pm: {
|
||||
type: 'channel',
|
||||
imageUrl:
|
||||
'https://images.unsplash.com/photo-1599420186946-7b6fb4e297f0?ixlib=rb-1.2.1&ixid=MnwxMjA3fDF8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1287&q=80',
|
||||
},
|
||||
watercooler: {
|
||||
type: 'channel',
|
||||
imageUrl:
|
||||
'https://images.unsplash.com/photo-1599420186946-7b6fb4e297f0?ixlib=rb-1.2.1&ixid=MnwxMjA3fDF8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1287&q=80',
|
||||
},
|
||||
'vitalik.eth': { type: 'chat', imageUrl: '' },
|
||||
'pvl.eth': { type: 'chat', imageUrl: '' },
|
||||
'Climate Change': { type: 'group-chat', imageUrl: '' },
|
||||
}
|
||||
|
||||
export const useChat = (id: string) => {
|
||||
return chats[id]
|
||||
}
|
|
@ -1,9 +1,24 @@
|
|||
import { useMemo } from 'react'
|
||||
import { useClient } from './provider'
|
||||
import { useCommunity } from '~/src/protocol/use-community'
|
||||
import { Community } from '@status-im/js'
|
||||
|
||||
interface Chat {
|
||||
type: 'channel' | 'group-chat' | 'chat'
|
||||
}
|
||||
export type Channel = Community['communityMetadata']['chats'][0]
|
||||
|
||||
export const useChats = (id: string) => {
|
||||
const client = useClient()
|
||||
export const useChats = (): Channel => {
|
||||
const community = useCommunity()
|
||||
|
||||
return useMemo(() => {
|
||||
return Object.entries(community.chats)
|
||||
.map(([chatId, chat]) => ({ id: chatId, ...chat }))
|
||||
.sort((a, b) => {
|
||||
if (a.position < b.position) {
|
||||
return -1
|
||||
}
|
||||
if (a.position > b.position) {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
})
|
||||
}, [community])
|
||||
}
|
||||
|
|
|
@ -1,11 +1,25 @@
|
|||
import { useState, useEffect, useReducer } from 'react'
|
||||
|
||||
import { useClient, useCommunity as useCommunityContext } from './provider'
|
||||
import { Community } from '@status-im/js'
|
||||
// return {
|
||||
// name: 'CryptoKitties',
|
||||
// description: 'A community of cat lovers, meow!',
|
||||
// publicKey: '0x2Ef1907d50926...6cEbd975aC5E0Ba',
|
||||
// imageUrl:
|
||||
// 'https://images.unsplash.com/photo-1592194996308-7b43878e84a6?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=987&q=80',
|
||||
// membersCount: 182,
|
||||
// requestNeeded: true,
|
||||
// }
|
||||
|
||||
// interface Community {
|
||||
// name: string
|
||||
// description: string
|
||||
// channels: any[]
|
||||
// }
|
||||
|
||||
export const useCommunity = () => {
|
||||
return {
|
||||
name: 'CryptoKitties',
|
||||
description: 'A community of cat lovers, meow!',
|
||||
publicKey: '0x2Ef1907d50926...6cEbd975aC5E0Ba',
|
||||
imageUrl:
|
||||
'https://images.unsplash.com/photo-1592194996308-7b43878e84a6?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=987&q=80',
|
||||
membersCount: 182,
|
||||
requestNeeded: true,
|
||||
}
|
||||
const context = useCommunityContext()
|
||||
|
||||
return context
|
||||
}
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
export const useContacts = () => {
|
||||
return []
|
||||
}
|
|
@ -1,31 +1,13 @@
|
|||
import { useEffect, useRef, useState } from 'react'
|
||||
|
||||
import { useClient } from './provider'
|
||||
import { useCommunity } from '~/src/protocol/use-community'
|
||||
import { Community } from '@status-im/js'
|
||||
|
||||
export type Member = Community['communityMetadata']['members'][0]
|
||||
|
||||
export const useMembers = (): string[] => {
|
||||
|
||||
const community = useCommunity()
|
||||
|
||||
return Object.keys(community.members)
|
||||
|
||||
interface State {
|
||||
fetching: boolean
|
||||
stale: boolean
|
||||
data?: any
|
||||
error?: Error
|
||||
}
|
||||
|
||||
export const useMembers = (): State => {
|
||||
const isMounted = useRef(true)
|
||||
const client = useClient()
|
||||
|
||||
const [state, setState] = useState<State>({
|
||||
fetching: false,
|
||||
stale: false,
|
||||
data: undefined,
|
||||
error: undefined,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
isMounted.current = true
|
||||
return () => {
|
||||
isMounted.current = false
|
||||
}
|
||||
}, [])
|
||||
|
||||
return state
|
||||
}
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
import { useEffect, useReducer, useState } from 'react'
|
||||
|
||||
import { useClient } from './provider'
|
||||
import { useChannel } from './use-channel'
|
||||
|
||||
import type { MessageType } from '@status-im/js'
|
||||
|
||||
export type Reaction =
|
||||
| 'heart'
|
||||
| 'thumbs-up'
|
||||
|
@ -70,195 +77,241 @@ interface ImageTextReply extends BaseReply {
|
|||
|
||||
export type Reply = TextReply | ImageReply | ImageTextReply
|
||||
|
||||
export const useMessages = (): Message[] => {
|
||||
return [
|
||||
{
|
||||
id: '1',
|
||||
type: 'text',
|
||||
contact: {
|
||||
name: 'Leila Joyner',
|
||||
imageUrl:
|
||||
'https://images.unsplash.com/photo-1593104547489-5cfb3839a3b5?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8cGVyc29ufHx8fHx8MTY0OTg0NjI0OQ&ixlib=rb-1.2.1&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080',
|
||||
},
|
||||
text: 'lorem, sit amet ultricies sem magna nec quam. Curabitur vel lectus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec dignissim magna a tortor. Nunc commodo auctor velit. Aliquam nisl. Nulla eu neque pellentesque massa lobortis ultrices. Vivamus rhoncus. Donec est. Nunc ullamcorper, velit in aliquet lobortis, nisi nibh lacinia orci, consectetuer euismod est arcu ac orci. Ut semper pretium neque. Morbi',
|
||||
owner: false,
|
||||
pinned: true,
|
||||
mention: false,
|
||||
reactions: {
|
||||
heart: { count: 0, me: false },
|
||||
'thumbs-up': { count: 0, me: false },
|
||||
'thumbs-down': { count: 0, me: false },
|
||||
smile: { count: 0, me: false },
|
||||
sad: { count: 0, me: false },
|
||||
angry: { count: 0, me: false },
|
||||
},
|
||||
reply: {
|
||||
contact: {
|
||||
name: 'Leila Joyner',
|
||||
imageUrl:
|
||||
'https://images.unsplash.com/photo-1593104547489-5cfb3839a3b5?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8cGVyc29ufHx8fHx8MTY0OTg0NjI0OQ&ixlib=rb-1.2.1&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080',
|
||||
},
|
||||
type: 'text',
|
||||
text: 'lorem, sit amet ultricies sem magna nec quam. Curabitur vel lectus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec dignissim magna a tortor. Nunc commodo auctor velit. Aliquam nisl. Nulla eu neque pellentesque massa lobortis ultrices. Vivamus rhoncus. Donec est. Nunc ullamcorper, velit in aliquet lobortis, nisi nibh lacinia orci, consectetuer euismod est arcu ac orci. Ut semper pretium neque. Morbi',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
type: 'text',
|
||||
contact: {
|
||||
name: 'Velma Mccarthy',
|
||||
imageUrl:
|
||||
'https://images.unsplash.com/photo-1522075469751-3a6694fb2f61?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8cGVyc29ufHx8fHx8MTY0OTg0NjMwOQ&ixlib=rb-1.2.1&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080',
|
||||
},
|
||||
text: 'est tempor bibendum. Donec felis orci, adipiscing non, luctus sit amet, faucibus ut, nulla. Cras eu tellus eu augue porttitor interdum. Sed auctor odio a purus. Duis elementum, dui quis accumsan convallis, ante lectus convallis est, vitae sodales nisi magna sed dui. Fusce aliquam, enim nec tempus scelerisque, lorem ipsum sodales purus, in molestie tortor nibh sit amet orci. Ut sagittis lobortis mauris. Suspendisse aliquet molestie tellus. Aenean egestas hendrerit neque. In ornare sagittis felis. Donec tempor, est ac mattis semper, dui lectus rutrum urna, nec luctus felis',
|
||||
owner: false,
|
||||
pinned: false,
|
||||
mention: false,
|
||||
reactions: {
|
||||
heart: { count: 0, me: false },
|
||||
'thumbs-up': { count: 0, me: false },
|
||||
'thumbs-down': { count: 0, me: false },
|
||||
smile: { count: 0, me: false },
|
||||
sad: { count: 0, me: false },
|
||||
angry: { count: 0, me: false },
|
||||
},
|
||||
reply: {
|
||||
contact: {
|
||||
name: 'Leila Joyner',
|
||||
imageUrl:
|
||||
'https://images.unsplash.com/photo-1593104547489-5cfb3839a3b5?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8cGVyc29ufHx8fHx8MTY0OTg0NjI0OQ&ixlib=rb-1.2.1&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080',
|
||||
},
|
||||
type: 'image',
|
||||
imageUrl:
|
||||
'https://images.unsplash.com/photo-1647531041383-fe7103712f16?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1287&q=80',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
type: 'text',
|
||||
contact: {
|
||||
name: 'Gareth Garrison',
|
||||
imageUrl:
|
||||
'https://images.unsplash.com/photo-1615473967657-9dc21773daa3?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8cGVyc29ufHx8fHx8MTY0OTg0NjMwNg&ixlib=rb-1.2.1&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080',
|
||||
},
|
||||
text: 'elit erat vitae risus. @satoshi Duis a mi fringilla mi lacinia mattis. Integer eu lacus. Quisque imperdiet, erat nonummy ultricies ornare, elit elit fermentum risus, at fringilla purus mauris a nunc. In at pede. Cras vulputate velit eu sem. Pellentesque ut ipsum ac mi eleifend egestas. Sed pharetra, felis',
|
||||
owner: false,
|
||||
pinned: false,
|
||||
mention: true,
|
||||
reactions: {
|
||||
heart: { count: 1, me: false },
|
||||
'thumbs-up': { count: 1, me: false },
|
||||
'thumbs-down': { count: 3, me: true },
|
||||
smile: { count: 0, me: false },
|
||||
sad: { count: 0, me: false },
|
||||
angry: { count: 0, me: false },
|
||||
},
|
||||
reply: {
|
||||
contact: {
|
||||
name: 'Leila Joyner',
|
||||
imageUrl:
|
||||
'https://images.unsplash.com/photo-1593104547489-5cfb3839a3b5?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8cGVyc29ufHx8fHx8MTY0OTg0NjI0OQ&ixlib=rb-1.2.1&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080',
|
||||
},
|
||||
type: 'image-text',
|
||||
text: 'lorem, sit amet ultricies sem magna nec quam. Curabitur vel lectus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec dignissim magna a tortor. Nunc commodo auctor velit. Aliquam nisl. Nulla eu neque pellentesque massa lobortis ultrices. Vivamus rhoncus. Donec est. Nunc ullamcorper, velit in aliquet lobortis, nisi nibh lacinia orci, consectetuer euismod est arcu ac orci. Ut semper pretium neque. Morbi',
|
||||
imageUrl:
|
||||
'https://images.unsplash.com/photo-1647531041383-fe7103712f16?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1287&q=80',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
type: 'image',
|
||||
contact: {
|
||||
name: 'Celeste Suarez',
|
||||
imageUrl: '',
|
||||
},
|
||||
imageUrl:
|
||||
'https://images.unsplash.com/photo-1647531041383-fe7103712f16?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1287&q=80',
|
||||
owner: false,
|
||||
pinned: false,
|
||||
mention: false,
|
||||
reactions: {
|
||||
heart: { count: 0, me: false },
|
||||
'thumbs-up': { count: 0, me: false },
|
||||
'thumbs-down': { count: 0, me: false },
|
||||
smile: { count: 0, me: false },
|
||||
sad: { count: 0, me: false },
|
||||
angry: { count: 0, me: false },
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
type: 'text',
|
||||
contact: {
|
||||
name: 'Satoshi',
|
||||
imageUrl:
|
||||
'https://images.unsplash.com/photo-1542838686-37da4a9fd1b3?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8cGVyc29ufHx8fHx8MTY0OTg0NjMwNA&ixlib=rb-1.2.1&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080',
|
||||
},
|
||||
text: 'tincidunt tempus risus. Donec egestas. Duis ac arcu. Nunc mauris. Morbi non sapien molestie orci tincidunt adipiscing. Mauris molestie pharetra nibh. Aliquam ornare, libero at auctor ullamcorper, nisl arcu iaculis enim, sit amet ornare lectus justo eu arcu. Morbi sit amet massa. Quisque porttitor eros nec tellus. Nunc lectus pede, ultrices a, auctor non, feugiat nec, diam. Duis mi',
|
||||
owner: true,
|
||||
pinned: false,
|
||||
mention: false,
|
||||
reactions: {
|
||||
heart: { count: 0, me: false },
|
||||
'thumbs-up': { count: 0, me: false },
|
||||
'thumbs-down': { count: 0, me: false },
|
||||
smile: { count: 0, me: false },
|
||||
sad: { count: 1, me: false },
|
||||
angry: { count: 1, me: true },
|
||||
},
|
||||
reply: {
|
||||
contact: {
|
||||
name: 'Leila Joyner',
|
||||
imageUrl:
|
||||
'https://images.unsplash.com/photo-1593104547489-5cfb3839a3b5?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8cGVyc29ufHx8fHx8MTY0OTg0NjI0OQ&ixlib=rb-1.2.1&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080',
|
||||
},
|
||||
type: 'text',
|
||||
text: 'tincidunt tempus risus. Donec egestas. Duis ac arcu. Nunc mauris. Morbi non sapien molestie orci tincidunt adipiscing. Mauris molestie pharetra nibh. Aliquam ornare, libero at auctor ullamcorper, nisl arcu iaculis enim, sit amet ornare lectus justo eu arcu. Morbi sit amet massa. Quisque porttitor eros nec tellus. Nunc lectus pede, ultrices a, auctor non, feugiat nec, diam. Duis mi',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
type: 'image-text',
|
||||
contact: {
|
||||
name: 'Leila Joyner',
|
||||
imageUrl:
|
||||
'https://images.unsplash.com/photo-1593104547489-5cfb3839a3b5?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8cGVyc29ufHx8fHx8MTY0OTg0NjI0OQ&ixlib=rb-1.2.1&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080',
|
||||
},
|
||||
text: 'lorem, sit amet ultricies sem magna nec quam.',
|
||||
imageUrl:
|
||||
'https://images.unsplash.com/photo-1647531041383-fe7103712f16?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1287&q=80',
|
||||
owner: false,
|
||||
pinned: false,
|
||||
mention: false,
|
||||
reactions: {
|
||||
heart: { count: 0, me: false },
|
||||
'thumbs-up': { count: 10, me: true },
|
||||
'thumbs-down': { count: 3, me: false },
|
||||
smile: { count: 0, me: false },
|
||||
sad: { count: 0, me: false },
|
||||
angry: { count: 0, me: false },
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
type: 'text',
|
||||
contact: {
|
||||
name: 'Satoshi',
|
||||
imageUrl:
|
||||
'https://images.unsplash.com/photo-1542838686-37da4a9fd1b3?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8cGVyc29ufHx8fHx8MTY0OTg0NjMwNA&ixlib=rb-1.2.1&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080',
|
||||
},
|
||||
text: 'tincidunt tempus risus. Donec egestas. Duis ac arcu. Nunc mauris. Morbi non sapien molestie orci tincidunt adipiscing. Mauris molestie pharetra nibh. Aliquam ornare, libero at auctor ullamcorper, nisl arcu iaculis enim, sit amet ornare lectus justo eu arcu. Morbi sit amet massa. Quisque porttitor eros nec tellus. Nunc lectus pede, ultrices a, auctor non, feugiat nec, diam. Duis mi',
|
||||
owner: true,
|
||||
pinned: false,
|
||||
mention: true,
|
||||
reactions: {
|
||||
heart: { count: 0, me: false },
|
||||
'thumbs-up': { count: 0, me: false },
|
||||
'thumbs-down': { count: 0, me: false },
|
||||
smile: { count: 0, me: false },
|
||||
sad: { count: 0, me: false },
|
||||
angry: { count: 0, me: false },
|
||||
},
|
||||
},
|
||||
]
|
||||
export type { MessageType }
|
||||
|
||||
interface Result {
|
||||
data: MessageType[]
|
||||
loading: boolean
|
||||
error?: Error
|
||||
}
|
||||
|
||||
export const useMessages = (channelId: string): Result => {
|
||||
const client = useClient()
|
||||
|
||||
// const [state, dispatch] = useReducer<Result>((state,action) => {}, {})
|
||||
|
||||
const [data, setData] = useState<any[]>(() => client.community.getMessages(channelId))
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [error, setError] = useState<Error>()
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
console.log("subscribed to", channelId)
|
||||
|
||||
setData(client.community.getMessages(channelId))
|
||||
|
||||
const handleUpdate = (messages: MessageType[]) => {
|
||||
setLoading(false)
|
||||
setData(messages)
|
||||
console.log('update for',channelId, messages)
|
||||
}
|
||||
const unsubscribe= client.community.onChannelMessageUpdate(channelId, handleUpdate)
|
||||
return () =>
|
||||
{
|
||||
|
||||
unsubscribe()
|
||||
console.log("unsubscribed from", channelId)
|
||||
|
||||
}
|
||||
}, [channelId])
|
||||
|
||||
return {
|
||||
data,
|
||||
loading,
|
||||
error,
|
||||
// hasMore
|
||||
// fetchMore
|
||||
// refetch
|
||||
}
|
||||
|
||||
// return [
|
||||
// {
|
||||
// id: '1',
|
||||
// type: 'text',
|
||||
// contact: {
|
||||
// name: 'Leila Joyner',
|
||||
// imageUrl:
|
||||
// 'https://images.unsplash.com/photo-1593104547489-5cfb3839a3b5?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8cGVyc29ufHx8fHx8MTY0OTg0NjI0OQ&ixlib=rb-1.2.1&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080',
|
||||
// },
|
||||
// text: 'lorem, sit amet ultricies sem magna nec quam. Curabitur vel lectus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec dignissim magna a tortor. Nunc commodo auctor velit. Aliquam nisl. Nulla eu neque pellentesque massa lobortis ultrices. Vivamus rhoncus. Donec est. Nunc ullamcorper, velit in aliquet lobortis, nisi nibh lacinia orci, consectetuer euismod est arcu ac orci. Ut semper pretium neque. Morbi',
|
||||
// owner: false,
|
||||
// pinned: true,
|
||||
// mention: false,
|
||||
// reactions: {
|
||||
// heart: { count: 0, me: false },
|
||||
// 'thumbs-up': { count: 0, me: false },
|
||||
// 'thumbs-down': { count: 0, me: false },
|
||||
// smile: { count: 0, me: false },
|
||||
// sad: { count: 0, me: false },
|
||||
// angry: { count: 0, me: false },
|
||||
// },
|
||||
// reply: {
|
||||
// contact: {
|
||||
// name: 'Leila Joyner',
|
||||
// imageUrl:
|
||||
// 'https://images.unsplash.com/photo-1593104547489-5cfb3839a3b5?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8cGVyc29ufHx8fHx8MTY0OTg0NjI0OQ&ixlib=rb-1.2.1&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080',
|
||||
// },
|
||||
// type: 'text',
|
||||
// text: 'lorem, sit amet ultricies sem magna nec quam. Curabitur vel lectus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec dignissim magna a tortor. Nunc commodo auctor velit. Aliquam nisl. Nulla eu neque pellentesque massa lobortis ultrices. Vivamus rhoncus. Donec est. Nunc ullamcorper, velit in aliquet lobortis, nisi nibh lacinia orci, consectetuer euismod est arcu ac orci. Ut semper pretium neque. Morbi',
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// id: '2',
|
||||
// type: 'text',
|
||||
// contact: {
|
||||
// name: 'Velma Mccarthy',
|
||||
// imageUrl:
|
||||
// 'https://images.unsplash.com/photo-1522075469751-3a6694fb2f61?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8cGVyc29ufHx8fHx8MTY0OTg0NjMwOQ&ixlib=rb-1.2.1&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080',
|
||||
// },
|
||||
// text: 'est tempor bibendum. Donec felis orci, adipiscing non, luctus sit amet, faucibus ut, nulla. Cras eu tellus eu augue porttitor interdum. Sed auctor odio a purus. Duis elementum, dui quis accumsan convallis, ante lectus convallis est, vitae sodales nisi magna sed dui. Fusce aliquam, enim nec tempus scelerisque, lorem ipsum sodales purus, in molestie tortor nibh sit amet orci. Ut sagittis lobortis mauris. Suspendisse aliquet molestie tellus. Aenean egestas hendrerit neque. In ornare sagittis felis. Donec tempor, est ac mattis semper, dui lectus rutrum urna, nec luctus felis',
|
||||
// owner: false,
|
||||
// pinned: false,
|
||||
// mention: false,
|
||||
// reactions: {
|
||||
// heart: { count: 0, me: false },
|
||||
// 'thumbs-up': { count: 0, me: false },
|
||||
// 'thumbs-down': { count: 0, me: false },
|
||||
// smile: { count: 0, me: false },
|
||||
// sad: { count: 0, me: false },
|
||||
// angry: { count: 0, me: false },
|
||||
// },
|
||||
// reply: {
|
||||
// contact: {
|
||||
// name: 'Leila Joyner',
|
||||
// imageUrl:
|
||||
// 'https://images.unsplash.com/photo-1593104547489-5cfb3839a3b5?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8cGVyc29ufHx8fHx8MTY0OTg0NjI0OQ&ixlib=rb-1.2.1&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080',
|
||||
// },
|
||||
// type: 'image',
|
||||
// imageUrl:
|
||||
// 'https://images.unsplash.com/photo-1647531041383-fe7103712f16?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1287&q=80',
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// id: '3',
|
||||
// type: 'text',
|
||||
// contact: {
|
||||
// name: 'Gareth Garrison',
|
||||
// imageUrl:
|
||||
// 'https://images.unsplash.com/photo-1615473967657-9dc21773daa3?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8cGVyc29ufHx8fHx8MTY0OTg0NjMwNg&ixlib=rb-1.2.1&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080',
|
||||
// },
|
||||
// text: 'elit erat vitae risus. @satoshi Duis a mi fringilla mi lacinia mattis. Integer eu lacus. Quisque imperdiet, erat nonummy ultricies ornare, elit elit fermentum risus, at fringilla purus mauris a nunc. In at pede. Cras vulputate velit eu sem. Pellentesque ut ipsum ac mi eleifend egestas. Sed pharetra, felis',
|
||||
// owner: false,
|
||||
// pinned: false,
|
||||
// mention: true,
|
||||
// reactions: {
|
||||
// heart: { count: 1, me: false },
|
||||
// 'thumbs-up': { count: 1, me: false },
|
||||
// 'thumbs-down': { count: 3, me: true },
|
||||
// smile: { count: 0, me: false },
|
||||
// sad: { count: 0, me: false },
|
||||
// angry: { count: 0, me: false },
|
||||
// },
|
||||
// reply: {
|
||||
// contact: {
|
||||
// name: 'Leila Joyner',
|
||||
// imageUrl:
|
||||
// 'https://images.unsplash.com/photo-1593104547489-5cfb3839a3b5?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8cGVyc29ufHx8fHx8MTY0OTg0NjI0OQ&ixlib=rb-1.2.1&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080',
|
||||
// },
|
||||
// type: 'image-text',
|
||||
// text: 'lorem, sit amet ultricies sem magna nec quam. Curabitur vel lectus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec dignissim magna a tortor. Nunc commodo auctor velit. Aliquam nisl. Nulla eu neque pellentesque massa lobortis ultrices. Vivamus rhoncus. Donec est. Nunc ullamcorper, velit in aliquet lobortis, nisi nibh lacinia orci, consectetuer euismod est arcu ac orci. Ut semper pretium neque. Morbi',
|
||||
// imageUrl:
|
||||
// 'https://images.unsplash.com/photo-1647531041383-fe7103712f16?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1287&q=80',
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// id: '4',
|
||||
// type: 'image',
|
||||
// contact: {
|
||||
// name: 'Celeste Suarez',
|
||||
// imageUrl: '',
|
||||
// },
|
||||
// imageUrl:
|
||||
// 'https://images.unsplash.com/photo-1647531041383-fe7103712f16?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1287&q=80',
|
||||
// owner: false,
|
||||
// pinned: false,
|
||||
// mention: false,
|
||||
// reactions: {
|
||||
// heart: { count: 0, me: false },
|
||||
// 'thumbs-up': { count: 0, me: false },
|
||||
// 'thumbs-down': { count: 0, me: false },
|
||||
// smile: { count: 0, me: false },
|
||||
// sad: { count: 0, me: false },
|
||||
// angry: { count: 0, me: false },
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// id: '5',
|
||||
// type: 'text',
|
||||
// contact: {
|
||||
// name: 'Satoshi',
|
||||
// imageUrl:
|
||||
// 'https://images.unsplash.com/photo-1542838686-37da4a9fd1b3?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8cGVyc29ufHx8fHx8MTY0OTg0NjMwNA&ixlib=rb-1.2.1&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080',
|
||||
// },
|
||||
// text: 'tincidunt tempus risus. Donec egestas. Duis ac arcu. Nunc mauris. Morbi non sapien molestie orci tincidunt adipiscing. Mauris molestie pharetra nibh. Aliquam ornare, libero at auctor ullamcorper, nisl arcu iaculis enim, sit amet ornare lectus justo eu arcu. Morbi sit amet massa. Quisque porttitor eros nec tellus. Nunc lectus pede, ultrices a, auctor non, feugiat nec, diam. Duis mi',
|
||||
// owner: true,
|
||||
// pinned: false,
|
||||
// mention: false,
|
||||
// reactions: {
|
||||
// heart: { count: 0, me: false },
|
||||
// 'thumbs-up': { count: 0, me: false },
|
||||
// 'thumbs-down': { count: 0, me: false },
|
||||
// smile: { count: 0, me: false },
|
||||
// sad: { count: 1, me: false },
|
||||
// angry: { count: 1, me: true },
|
||||
// },
|
||||
// reply: {
|
||||
// contact: {
|
||||
// name: 'Leila Joyner',
|
||||
// imageUrl:
|
||||
// 'https://images.unsplash.com/photo-1593104547489-5cfb3839a3b5?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8cGVyc29ufHx8fHx8MTY0OTg0NjI0OQ&ixlib=rb-1.2.1&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080',
|
||||
// },
|
||||
// type: 'text',
|
||||
// text: 'tincidunt tempus risus. Donec egestas. Duis ac arcu. Nunc mauris. Morbi non sapien molestie orci tincidunt adipiscing. Mauris molestie pharetra nibh. Aliquam ornare, libero at auctor ullamcorper, nisl arcu iaculis enim, sit amet ornare lectus justo eu arcu. Morbi sit amet massa. Quisque porttitor eros nec tellus. Nunc lectus pede, ultrices a, auctor non, feugiat nec, diam. Duis mi',
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// id: '6',
|
||||
// type: 'image-text',
|
||||
// contact: {
|
||||
// name: 'Leila Joyner',
|
||||
// imageUrl:
|
||||
// 'https://images.unsplash.com/photo-1593104547489-5cfb3839a3b5?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8cGVyc29ufHx8fHx8MTY0OTg0NjI0OQ&ixlib=rb-1.2.1&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080',
|
||||
// },
|
||||
// text: 'lorem, sit amet ultricies sem magna nec quam.',
|
||||
// imageUrl:
|
||||
// 'https://images.unsplash.com/photo-1647531041383-fe7103712f16?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1287&q=80',
|
||||
// owner: false,
|
||||
// pinned: false,
|
||||
// mention: false,
|
||||
// reactions: {
|
||||
// heart: { count: 0, me: false },
|
||||
// 'thumbs-up': { count: 10, me: true },
|
||||
// 'thumbs-down': { count: 3, me: false },
|
||||
// smile: { count: 0, me: false },
|
||||
// sad: { count: 0, me: false },
|
||||
// angry: { count: 0, me: false },
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// id: '5',
|
||||
// type: 'text',
|
||||
// contact: {
|
||||
// name: 'Satoshi',
|
||||
// imageUrl:
|
||||
// 'https://images.unsplash.com/photo-1542838686-37da4a9fd1b3?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8cGVyc29ufHx8fHx8MTY0OTg0NjMwNA&ixlib=rb-1.2.1&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080',
|
||||
// },
|
||||
// text: 'tincidunt tempus risus. Donec egestas. Duis ac arcu. Nunc mauris. Morbi non sapien molestie orci tincidunt adipiscing. Mauris molestie pharetra nibh. Aliquam ornare, libero at auctor ullamcorper, nisl arcu iaculis enim, sit amet ornare lectus justo eu arcu. Morbi sit amet massa. Quisque porttitor eros nec tellus. Nunc lectus pede, ultrices a, auctor non, feugiat nec, diam. Duis mi',
|
||||
// owner: true,
|
||||
// pinned: false,
|
||||
// mention: true,
|
||||
// reactions: {
|
||||
// heart: { count: 0, me: false },
|
||||
// 'thumbs-up': { count: 0, me: false },
|
||||
// 'thumbs-down': { count: 0, me: false },
|
||||
// smile: { count: 0, me: false },
|
||||
// sad: { count: 0, me: false },
|
||||
// angry: { count: 0, me: false },
|
||||
// },
|
||||
// },
|
||||
// ]
|
||||
}
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
export const useProfile = () => {
|
||||
return {
|
||||
name: 'Satoshi',
|
||||
publicKey: '71C7656EC7ab88b098defB751B7401B5f6d8976F',
|
||||
imageUrl:
|
||||
'https://images.unsplash.com/photo-1546776310-eef45dd6d63c?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1620&q=80',
|
||||
}
|
||||
}
|
|
@ -7,72 +7,73 @@ import { Avatar, DialogTrigger, Flex, Text } from '~/src/system'
|
|||
|
||||
import { PinnedMessagesDialog } from './pinned-messages-dialog'
|
||||
|
||||
import type { Chat } from '~/src/protocol/use-chat'
|
||||
import type { Channel } from '~/src/protocol'
|
||||
|
||||
interface Props {
|
||||
chat: Chat
|
||||
chat: Channel
|
||||
}
|
||||
|
||||
export const ChatInfo = (props: Props) => {
|
||||
const { chat } = props
|
||||
const { params } = useMatch(':id')! // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
||||
console.log("file: index.tsx > line 18 > ChatInfo > chat", chat)
|
||||
|
||||
if (chat.type == 'channel') {
|
||||
return (
|
||||
<Flex align="center" gap="2">
|
||||
<Avatar size={36} src={chat.imageUrl} />
|
||||
<div>
|
||||
<Text>#{params.id}</Text>
|
||||
<Flex align="center">
|
||||
<Text size={12} color="gray">
|
||||
<DialogTrigger>
|
||||
<button
|
||||
style={{ display: 'inline-flex', alignItems: 'center' }}
|
||||
>
|
||||
{/* <PinIcon width={7} height={13} />2 pinned messages */}2
|
||||
pinned messages
|
||||
</button>
|
||||
<PinnedMessagesDialog />
|
||||
</DialogTrigger>{' '}
|
||||
| General discussions about CryptoKitties.
|
||||
</Text>
|
||||
</Flex>
|
||||
</div>
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
if (chat.type == 'group-chat') {
|
||||
return (
|
||||
<Flex align="center" gap="2">
|
||||
<Avatar size={36} src={chat.imageUrl} />
|
||||
<div>
|
||||
<Text>Climate Change</Text>
|
||||
<Flex align="center">
|
||||
<DialogTrigger>
|
||||
<Text as="button" size={12} color="gray">
|
||||
<PinIcon width={7} /> 2 pinned messages
|
||||
</Text>
|
||||
<PinnedMessagesDialog />
|
||||
</DialogTrigger>
|
||||
<Text size={12} color="gray">
|
||||
| 5 members
|
||||
</Text>
|
||||
</Flex>
|
||||
</div>
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
// if (chat.type == 'channel') {
|
||||
return (
|
||||
<Flex align="center" gap="2">
|
||||
<Avatar size={36} src={chat.imageUrl} />
|
||||
<Avatar size={36} />
|
||||
<div>
|
||||
<Text>pvl.eth</Text>
|
||||
<Text size={12} color="gray">
|
||||
0x63FaC9201494f0bd17B9892B9fae4d52fe3BD377
|
||||
</Text>
|
||||
<Text>#{chat.identity?.displayName}</Text>
|
||||
<Flex align="center">
|
||||
<Text size={12} color="gray">
|
||||
{chat.identity?.description}
|
||||
</Text>
|
||||
</Flex>
|
||||
</div>
|
||||
</Flex>
|
||||
)
|
||||
|
||||
// <DialogTrigger>
|
||||
// <button style={{ display: 'inline-flex', alignItems: 'center' }}>
|
||||
// {/* <PinIcon width={7} height={13} />2 pinned messages */}2
|
||||
// pinned messages
|
||||
// </button>
|
||||
// <PinnedMessagesDialog />
|
||||
// </DialogTrigger>{' '}
|
||||
// |
|
||||
// }
|
||||
|
||||
// if (chat.type == 'group-chat') {
|
||||
// return (
|
||||
// <Flex align="center" gap="2">
|
||||
// <Avatar size={36} src={chat.imageUrl} />
|
||||
// <div>
|
||||
// <Text>Climate Change</Text>
|
||||
// <Flex align="center">
|
||||
// <DialogTrigger>
|
||||
// <Text as="button" size={12} color="gray">
|
||||
// <PinIcon width={7} /> 2 pinned messages
|
||||
// </Text>
|
||||
// <PinnedMessagesDialog />
|
||||
// </DialogTrigger>
|
||||
// <Text size={12} color="gray">
|
||||
// | 5 members
|
||||
// </Text>
|
||||
// </Flex>
|
||||
// </div>
|
||||
// </Flex>
|
||||
// )
|
||||
// }
|
||||
|
||||
// return (
|
||||
// <Flex align="center" gap="2">
|
||||
// <Avatar size={36} src={chat.imageUrl} />
|
||||
// <div>
|
||||
// <Text>pvl.eth</Text>
|
||||
// <Text size={12} color="gray">
|
||||
// 0x63FaC9201494f0bd17B9892B9fae4d52fe3BD377
|
||||
// </Text>
|
||||
// </div>
|
||||
// </Flex>
|
||||
// )
|
||||
}
|
||||
|
|
|
@ -25,10 +25,10 @@ import { Actions } from './actions'
|
|||
import { MessageReply } from './message-reply'
|
||||
import { MessageReactions } from './reactions'
|
||||
|
||||
import type { Message } from '~/src/protocol/use-messages'
|
||||
import type { MessageType } from '~/src/protocol/use-messages'
|
||||
|
||||
interface Props {
|
||||
message: Message
|
||||
message: MessageType
|
||||
}
|
||||
|
||||
// const MessageLink = forwardRef(function MessageLink(
|
||||
|
@ -54,8 +54,14 @@ interface Props {
|
|||
|
||||
export const ChatMessage = (props: Props) => {
|
||||
const { message } = props
|
||||
console.log("🚀 > message", message)
|
||||
|
||||
const { type, contact, owner, mention, pinned, reply, reactions } = message
|
||||
// const { type, contact, owner, mention, pinned, reply, reactions } = message
|
||||
const owner=false
|
||||
const mention=false
|
||||
const pinned = false
|
||||
const reply = false
|
||||
const { contentType, text, displayName, reactions } = message
|
||||
|
||||
const [editing, setEditing] = useState(false)
|
||||
const [reacting, setReacting] = useState(false)
|
||||
|
@ -72,7 +78,7 @@ export const ChatMessage = (props: Props) => {
|
|||
}
|
||||
|
||||
const handlePinClick = () => {
|
||||
console.log(pinned)
|
||||
// console.log(pinned)
|
||||
}
|
||||
|
||||
const handleReaction = (reaction: string) => {
|
||||
|
@ -98,8 +104,8 @@ export const ChatMessage = (props: Props) => {
|
|||
)
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 'text': {
|
||||
switch (contentType) {
|
||||
case 'TEXT_PLAIN': {
|
||||
// <AlertDialogTrigger>
|
||||
// <MessageLink href="https://specs.status.im/spec">
|
||||
// https://specs.status.im/spec
|
||||
|
@ -155,19 +161,20 @@ export const ChatMessage = (props: Props) => {
|
|||
<Box>
|
||||
<DropdownMenuTrigger>
|
||||
<button type="button">
|
||||
<Avatar size={44} src={contact.imageUrl} />
|
||||
{/* <Avatar size={44} src={contact.imageUrl} /> */}
|
||||
</button>
|
||||
<DropdownMenu>
|
||||
<Flex direction="column" align="center" gap="1">
|
||||
<Avatar size="36" src={contact.imageUrl} />
|
||||
<Text>{contact.name}</Text>
|
||||
{/* <Avatar size="36" src={contact.imageUrl} /> */}
|
||||
{/* <Text>{contact.name}</Text> */}
|
||||
<Text>{displayName}</Text>
|
||||
<EmojiHash />
|
||||
</Flex>
|
||||
<DropdownMenu.Separator />
|
||||
<DropdownMenu.Item
|
||||
icon={<BellIcon />}
|
||||
onSelect={() =>
|
||||
userProfileDialog.open({ name: contact.name })
|
||||
userProfileDialog.open({ name: displayName })
|
||||
}
|
||||
>
|
||||
View Profile
|
||||
|
@ -199,7 +206,8 @@ export const ChatMessage = (props: Props) => {
|
|||
|
||||
<Flex gap="1" align="center">
|
||||
<Text color="primary" weight="500" size="15">
|
||||
{contact.name}
|
||||
{displayName}
|
||||
{/* {contact.name} */}
|
||||
</Text>
|
||||
<Text size="10" color="gray">
|
||||
10:00 AM
|
||||
|
|
|
@ -7,7 +7,7 @@ import { useAppState } from '~/src/contexts/app-context'
|
|||
import { BellIcon } from '~/src/icons/bell-icon'
|
||||
import { DotsIcon } from '~/src/icons/dots-icon'
|
||||
import { GroupIcon } from '~/src/icons/group-icon'
|
||||
import { useChat } from '~/src/protocol/use-chat'
|
||||
import { useChannel } from '~/src/protocol'
|
||||
import { styled } from '~/src/styles/config'
|
||||
import { DropdownMenuTrigger, Flex, IconButton, Separator } from '~/src/system'
|
||||
|
||||
|
@ -23,7 +23,7 @@ export const Navbar = (props: Props) => {
|
|||
const { state, dispatch } = useAppState()
|
||||
const { params } = useMatch(':id')! // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
||||
|
||||
const chat = useChat(params.id!)
|
||||
const chat = useChannel(params.id!)
|
||||
|
||||
return (
|
||||
<NavbarWrapper>
|
||||
|
@ -44,7 +44,7 @@ export const Navbar = (props: Props) => {
|
|||
<IconButton label="Options">
|
||||
<DotsIcon />
|
||||
</IconButton>
|
||||
<ChatMenu type="dropdown" chatType={chat.type} />
|
||||
<ChatMenu type="dropdown" chatType="channel" />
|
||||
</DropdownMenuTrigger>
|
||||
|
||||
<Separator orientation="vertical" css={{ height: 24 }} />
|
||||
|
|
|
@ -5,7 +5,7 @@ import { useMatch } from 'react-router-dom'
|
|||
import { MemberSidebar } from '~/src/components/member-sidebar'
|
||||
import { useAppState } from '~/src/contexts/app-context'
|
||||
import { ChatProvider } from '~/src/contexts/chat-context'
|
||||
import { useChat } from '~/src/protocol/use-chat'
|
||||
import { useChannel } from '~/src/protocol'
|
||||
import { useMessages } from '~/src/protocol/use-messages'
|
||||
import { styled } from '~/src/styles/config'
|
||||
import { Avatar, Flex, Heading, Text } from '~/src/system'
|
||||
|
@ -18,13 +18,15 @@ const ChatStart = () => {
|
|||
// TODO: unify this with the useChat hook
|
||||
const { params } = useMatch(':id')! // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
||||
|
||||
const chat = useChat(params.id!)
|
||||
const chat = useChannel(params.id!)
|
||||
|
||||
return (
|
||||
<Flex direction="column" gap="3" align="center" css={{ marginBottom: 50 }}>
|
||||
<Avatar size={120} src={chat.imageUrl} />
|
||||
<Heading>general</Heading>
|
||||
<Text>Welcome to the beginning of the #general channel!</Text>
|
||||
{/* <Avatar size={120} src={chat.imageUrl} /> */}
|
||||
<Heading>{chat.identity?.displayName}</Heading>
|
||||
<Text>
|
||||
Welcome to the beginning of the #{chat.identity?.displayName} channel!
|
||||
</Text>
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
@ -32,18 +34,20 @@ const ChatStart = () => {
|
|||
const Content = () => {
|
||||
const contentRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
const { params } = useMatch(':id')! // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
||||
|
||||
useEffect(() => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
contentRef.current!.scrollTop = contentRef.current!.scrollHeight ?? 0
|
||||
}, [])
|
||||
|
||||
const messages = useMessages()
|
||||
const messages = useMessages(params.id!)
|
||||
|
||||
return (
|
||||
<ContentWrapper ref={contentRef}>
|
||||
<ChatStart />
|
||||
{messages.map(message => (
|
||||
<ChatMessage key={message.id} message={message} />
|
||||
{messages.data.map(message => (
|
||||
<ChatMessage key={message.messageId} message={message} />
|
||||
))}
|
||||
</ContentWrapper>
|
||||
)
|
||||
|
|
|
@ -2,6 +2,7 @@ import React from 'react'
|
|||
|
||||
import { BrowserRouter, Route, Routes } from 'react-router-dom'
|
||||
|
||||
import { ClientProvider } from '~/src/protocol'
|
||||
import { MainSidebar } from '~/src/components/main-sidebar'
|
||||
import { AppProvider } from '~/src/contexts/app-context'
|
||||
import { DialogProvider } from '~/src/contexts/dialog-context'
|
||||
|
@ -23,18 +24,20 @@ export const Community = (props: Props) => {
|
|||
return (
|
||||
<Router>
|
||||
<AppProvider config={props}>
|
||||
<ThemeProvider theme={theme}>
|
||||
<DialogProvider>
|
||||
<GlobalStyle />
|
||||
<Wrapper>
|
||||
<MainSidebar />
|
||||
<Routes>
|
||||
<Route path="/:id" element={<Chat />} />
|
||||
{/* <Route path="/new" element={<NewChat />} /> */}
|
||||
</Routes>
|
||||
</Wrapper>
|
||||
</DialogProvider>
|
||||
</ThemeProvider>
|
||||
<ClientProvider options={{ publicKey: props.publicKey }}>
|
||||
<ThemeProvider theme={theme}>
|
||||
<DialogProvider>
|
||||
<GlobalStyle />
|
||||
<Wrapper>
|
||||
<MainSidebar />
|
||||
<Routes>
|
||||
<Route path="/:id" element={<Chat />} />
|
||||
{/* <Route path="/new" element={<NewChat />} /> */}
|
||||
</Routes>
|
||||
</Wrapper>
|
||||
</DialogProvider>
|
||||
</ThemeProvider>
|
||||
</ClientProvider>
|
||||
</AppProvider>
|
||||
</Router>
|
||||
)
|
||||
|
|
|
@ -13,7 +13,7 @@ const EthAddress = (props: Props) => {
|
|||
|
||||
return (
|
||||
<Text {...textProps}>
|
||||
0x{children.substring(0, 3)}...{children.substring(children.length - 3)}
|
||||
{children.substring(0, 5)}...{children.substring(children.length - 3)}
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
|
|
209
yarn.lock
209
yarn.lock
|
@ -1556,7 +1556,7 @@
|
|||
"@parcel/transformer-react-refresh-wrap" "2.3.2"
|
||||
"@parcel/transformer-svg" "2.3.2"
|
||||
|
||||
"@parcel/config-default@^2.6.0":
|
||||
"@parcel/config-default@2.6.0", "@parcel/config-default@^2.6.0":
|
||||
version "2.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/config-default/-/config-default-2.6.0.tgz#2cc9a05d195a97a93b6e14cbbda96d47d2ccf118"
|
||||
integrity sha512-DXovFPhZITmTvFaSEdC8RRqROs9FLIJ4u8yFSU6FUyq2wpvtYVRXXoDrvXgClh2csXmK7JTJTp5JF7r0rd2UaA==
|
||||
|
@ -1622,6 +1622,36 @@
|
|||
nullthrows "^1.1.1"
|
||||
semver "^5.7.1"
|
||||
|
||||
"@parcel/core@2.6.0":
|
||||
version "2.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/core/-/core-2.6.0.tgz#dad1f5f529ffb47df772c155ef09119d3294538c"
|
||||
integrity sha512-8OOWbPuxpFydpwNyKoz6d3e3O4DmxNYmMw4DXwrPSj/jyg7oa+SDtMT0/VXEhujE0HYkQPCHt4npRajkSuf99A==
|
||||
dependencies:
|
||||
"@mischnic/json-sourcemap" "^0.1.0"
|
||||
"@parcel/cache" "2.6.0"
|
||||
"@parcel/diagnostic" "2.6.0"
|
||||
"@parcel/events" "2.6.0"
|
||||
"@parcel/fs" "2.6.0"
|
||||
"@parcel/graph" "2.6.0"
|
||||
"@parcel/hash" "2.6.0"
|
||||
"@parcel/logger" "2.6.0"
|
||||
"@parcel/package-manager" "2.6.0"
|
||||
"@parcel/plugin" "2.6.0"
|
||||
"@parcel/source-map" "^2.0.0"
|
||||
"@parcel/types" "2.6.0"
|
||||
"@parcel/utils" "2.6.0"
|
||||
"@parcel/workers" "2.6.0"
|
||||
abortcontroller-polyfill "^1.1.9"
|
||||
base-x "^3.0.8"
|
||||
browserslist "^4.6.6"
|
||||
clone "^2.1.1"
|
||||
dotenv "^7.0.0"
|
||||
dotenv-expand "^5.1.0"
|
||||
json5 "^2.2.0"
|
||||
msgpackr "^1.5.4"
|
||||
nullthrows "^1.1.1"
|
||||
semver "^5.7.1"
|
||||
|
||||
"@parcel/css-darwin-arm64@1.9.0":
|
||||
version "1.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/css-darwin-arm64/-/css-darwin-arm64-1.9.0.tgz#5a020c604249180afcf69ce0f6978b807e2011b3"
|
||||
|
@ -1748,6 +1778,14 @@
|
|||
"@parcel/utils" "2.3.2"
|
||||
nullthrows "^1.1.1"
|
||||
|
||||
"@parcel/graph@2.6.0":
|
||||
version "2.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/graph/-/graph-2.6.0.tgz#04f9660333e314a51af38483efefd766a5841bb0"
|
||||
integrity sha512-rxrAzWm6rwbCRPbu0Z+zwMscpG8omffODniVWPlX2G0jgQGpjKsutBQ6RMfFIcfaQ4MzL3pIQOTf8bkjQOPsbg==
|
||||
dependencies:
|
||||
"@parcel/utils" "2.6.0"
|
||||
nullthrows "^1.1.1"
|
||||
|
||||
"@parcel/hash@2.3.2":
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/hash/-/hash-2.3.2.tgz#33b8ff04bb44f6661bdc1054b302ef1b6bd3acb3"
|
||||
|
@ -2069,12 +2107,12 @@
|
|||
"@parcel/utils" "2.6.0"
|
||||
posthtml "^0.16.4"
|
||||
|
||||
"@parcel/packager-ts@2.3.2":
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/packager-ts/-/packager-ts-2.3.2.tgz#e2092e89bd7ee48b7b217ab9c4374c7ceef17e13"
|
||||
integrity sha512-8Yb0N8Rnvj4Ag+jd2nTJPSrUKZxFEqju77cTRkkg4jFSNSFw8GTRsappNAyCID0W1tjcTyxMTfxetPmCC1Xm9g==
|
||||
"@parcel/packager-ts@^2.6.0":
|
||||
version "2.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/packager-ts/-/packager-ts-2.6.0.tgz#7e4a19e880f5e097906812f49bf62fa40e356df8"
|
||||
integrity sha512-JDKmXHUEGsMy6plDigPKkNhXWPm/6KIvXpg7/WMswzO23adtgWr2x9yz1Filt4JO0lxnDrdRJedUJxTwaDSYiA==
|
||||
dependencies:
|
||||
"@parcel/plugin" "2.3.2"
|
||||
"@parcel/plugin" "2.6.0"
|
||||
|
||||
"@parcel/plugin@2.3.2":
|
||||
version "2.3.2"
|
||||
|
@ -2100,6 +2138,17 @@
|
|||
"@parcel/utils" "2.3.2"
|
||||
chalk "^4.1.0"
|
||||
|
||||
"@parcel/reporter-cli@2.6.0":
|
||||
version "2.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/reporter-cli/-/reporter-cli-2.6.0.tgz#09fe5a8eecc368f2bcaaf6ab8154378bab0e0242"
|
||||
integrity sha512-QFG957NXx3L0D8Zw0+B2j7IHy8f/UzOVu6VvKE3rMkhq/iR2qLrPohQ+uvxlee+CLC0cG2qRSgJ7Ve/rjQPoJg==
|
||||
dependencies:
|
||||
"@parcel/plugin" "2.6.0"
|
||||
"@parcel/types" "2.6.0"
|
||||
"@parcel/utils" "2.6.0"
|
||||
chalk "^4.1.0"
|
||||
term-size "^2.2.1"
|
||||
|
||||
"@parcel/reporter-dev-server@2.3.2":
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/reporter-dev-server/-/reporter-dev-server-2.3.2.tgz#46ee4c53ad08c8b8afd2c79fb37381b6ba55cfb5"
|
||||
|
@ -2474,21 +2523,21 @@
|
|||
posthtml-render "^3.0.0"
|
||||
semver "^5.7.1"
|
||||
|
||||
"@parcel/transformer-typescript-types@2.3.2":
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/transformer-typescript-types/-/transformer-typescript-types-2.3.2.tgz#569d06d876594766d48c7bca79464f49e7fe5002"
|
||||
integrity sha512-iYPah0UK2o10bsk5Ukr7meyI5vA27mnBWXHp8bOG5a2pJ/UzS1B7IKbgHBOzLmvpD7lSDfPLGpmbSWbIhsI5+g==
|
||||
"@parcel/transformer-typescript-types@^2.6.0":
|
||||
version "2.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/transformer-typescript-types/-/transformer-typescript-types-2.6.0.tgz#27297a8c59572bd00f54a6156f586f7da5570f7f"
|
||||
integrity sha512-T1ul8EFc/VxJp6jywDLKV66P0YseeROsZ/kwKpfJezua2mRO4iH2O6G9v5jn2H1C+bb6/M0JvfJqRxLj2Himlw==
|
||||
dependencies:
|
||||
"@parcel/diagnostic" "2.3.2"
|
||||
"@parcel/plugin" "2.3.2"
|
||||
"@parcel/diagnostic" "2.6.0"
|
||||
"@parcel/plugin" "2.6.0"
|
||||
"@parcel/source-map" "^2.0.0"
|
||||
"@parcel/ts-utils" "2.3.2"
|
||||
"@parcel/ts-utils" "2.6.0"
|
||||
nullthrows "^1.1.1"
|
||||
|
||||
"@parcel/ts-utils@2.3.2":
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/ts-utils/-/ts-utils-2.3.2.tgz#d63f7027574f3c1a128e1c865d683d6aacb4476d"
|
||||
integrity sha512-jYCHoSmU+oVtFA4q0BygVf74FpVnCDSNtVfLzd1EfGVHlBFMo9GzSY5luMTG4qhnNCDEEco89bkMIgjPHQ3qnA==
|
||||
"@parcel/ts-utils@2.6.0":
|
||||
version "2.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/ts-utils/-/ts-utils-2.6.0.tgz#f9b07be2faa62862933741669b758ce56e5c15bf"
|
||||
integrity sha512-U2Spr/vdOnxLzztXP6WpMO7JZTsaYO1G6F/cUTG5fReTQ0imM952FAc/WswpZWAPZqXqWCnvC/Z91JIkMDuYrA==
|
||||
dependencies:
|
||||
nullthrows "^1.1.1"
|
||||
|
||||
|
@ -3402,6 +3451,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
|
||||
integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4=
|
||||
|
||||
"@types/lodash@^4.14.182":
|
||||
version "4.14.182"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.182.tgz#05301a4d5e62963227eaafe0ce04dd77c54ea5c2"
|
||||
integrity sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==
|
||||
|
||||
"@types/long@^4.0.1":
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9"
|
||||
|
@ -3437,6 +3491,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301"
|
||||
integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==
|
||||
|
||||
"@types/object-hash@^1.3.0":
|
||||
version "1.3.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/object-hash/-/object-hash-1.3.4.tgz#079ba142be65833293673254831b5e3e847fe58b"
|
||||
integrity sha512-xFdpkAkikBgqBdG9vIlsqffDV8GpvnPEzs0IUtr1v3BEB97ijsFQ4RXVbUZwjFThhB4MDSTUfvmxUD5PGx0wXA==
|
||||
|
||||
"@types/parse-json@^4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
|
||||
|
@ -4128,6 +4187,13 @@ browserslist@^4.20.2, browserslist@^4.20.3:
|
|||
node-releases "^2.0.3"
|
||||
picocolors "^1.0.0"
|
||||
|
||||
bs-logger@0.x:
|
||||
version "0.2.6"
|
||||
resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8"
|
||||
integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==
|
||||
dependencies:
|
||||
fast-json-stable-stringify "2.x"
|
||||
|
||||
bser@2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05"
|
||||
|
@ -4620,6 +4686,11 @@ dashdash@^1.12.0:
|
|||
dependencies:
|
||||
assert-plus "^1.0.0"
|
||||
|
||||
dataloader@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-1.4.0.tgz#bca11d867f5d3f1b9ed9f737bd15970c65dff5c8"
|
||||
integrity sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==
|
||||
|
||||
datastore-core@^7.0.0:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/datastore-core/-/datastore-core-7.0.1.tgz#f50f30bb55474a569118d41bba6052896b096aec"
|
||||
|
@ -5412,7 +5483,7 @@ fast-glob@^3.2.9:
|
|||
merge2 "^1.3.0"
|
||||
micromatch "^4.0.4"
|
||||
|
||||
fast-json-stable-stringify@^2.0.0:
|
||||
fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
|
||||
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
|
||||
|
@ -6722,7 +6793,7 @@ jest-snapshot@^28.1.0:
|
|||
pretty-format "^28.1.0"
|
||||
semver "^7.3.5"
|
||||
|
||||
jest-util@^28.1.0:
|
||||
jest-util@^28.0.0, jest-util@^28.1.0:
|
||||
version "28.1.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-28.1.0.tgz#d54eb83ad77e1dd441408738c5a5043642823be5"
|
||||
integrity sha512-qYdCKD77k4Hwkose2YBEqQk7PzUf/NSE+rutzceduFveQREeH6b+89Dc9+wjX9dAwHcgdx4yedGA3FQlU/qCTA==
|
||||
|
@ -7271,7 +7342,7 @@ lodash.debounce@^4.0.8:
|
|||
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
|
||||
integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168=
|
||||
|
||||
lodash.memoize@^4.1.2:
|
||||
lodash.memoize@4.x, lodash.memoize@^4.1.2:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
|
||||
integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=
|
||||
|
@ -7286,7 +7357,7 @@ lodash.uniq@^4.5.0:
|
|||
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
|
||||
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
|
||||
|
||||
lodash@^4.17.11, lodash@^4.17.4:
|
||||
lodash@^4.17.11, lodash@^4.17.15, lodash@^4.17.21, lodash@^4.17.4:
|
||||
version "4.17.21"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||
|
@ -7339,7 +7410,7 @@ make-dir@^3.0.0:
|
|||
dependencies:
|
||||
semver "^6.0.0"
|
||||
|
||||
make-error@^1.1.1:
|
||||
make-error@1.x, make-error@^1.1.1:
|
||||
version "1.3.6"
|
||||
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
|
||||
integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
|
||||
|
@ -7634,7 +7705,6 @@ node-addon-api@^4.3.0:
|
|||
|
||||
"node-fetch@https://registry.npmjs.org/@achingbrain/node-fetch/-/node-fetch-2.6.7.tgz":
|
||||
version "2.6.7"
|
||||
uid "1b5d62978f2ed07b99444f64f0df39f960a6d34d"
|
||||
resolved "https://registry.npmjs.org/@achingbrain/node-fetch/-/node-fetch-2.6.7.tgz#1b5d62978f2ed07b99444f64f0df39f960a6d34d"
|
||||
|
||||
node-forge@^1.2.1:
|
||||
|
@ -7748,6 +7818,11 @@ object-assign@^4.1.1:
|
|||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
|
||||
|
||||
object-hash@^1.3.1:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.3.1.tgz#fde452098a951cb145f039bb7d455449ddc126df"
|
||||
integrity sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA==
|
||||
|
||||
object-inspect@^1.11.0, object-inspect@^1.12.0, object-inspect@^1.9.0:
|
||||
version "1.12.0"
|
||||
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0"
|
||||
|
@ -7993,7 +8068,7 @@ p-try@^2.0.0:
|
|||
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
|
||||
integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
|
||||
|
||||
parcel@^2.0.0, parcel@^2.3.2:
|
||||
parcel@^2.0.0:
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/parcel/-/parcel-2.3.2.tgz#d1cb475f27edae981edea7a7104e04d3a35a87ca"
|
||||
integrity sha512-4jhgoBcQaiGKmnmBvNyKyOvZrxCgzgUzdEoVup/fRCOP99hNmvYIN5IErIIJxsU9ObcG/RGCFF8wa4kVRsWfIg==
|
||||
|
@ -8013,6 +8088,26 @@ parcel@^2.0.0, parcel@^2.3.2:
|
|||
get-port "^4.2.0"
|
||||
v8-compile-cache "^2.0.0"
|
||||
|
||||
parcel@^2.6.0:
|
||||
version "2.6.0"
|
||||
resolved "https://registry.yarnpkg.com/parcel/-/parcel-2.6.0.tgz#801bd3af8339966fc52370033e1b7f3c9b9e457d"
|
||||
integrity sha512-pSTJ7wC6uTl16PKLXQV7RfL9FGoIDA1iVpNvaav47n6UkUdKqfx0spcVPpw35kWdRcHJF61YAvkPjP2hTwHQ+Q==
|
||||
dependencies:
|
||||
"@parcel/config-default" "2.6.0"
|
||||
"@parcel/core" "2.6.0"
|
||||
"@parcel/diagnostic" "2.6.0"
|
||||
"@parcel/events" "2.6.0"
|
||||
"@parcel/fs" "2.6.0"
|
||||
"@parcel/logger" "2.6.0"
|
||||
"@parcel/package-manager" "2.6.0"
|
||||
"@parcel/reporter-cli" "2.6.0"
|
||||
"@parcel/reporter-dev-server" "2.6.0"
|
||||
"@parcel/utils" "2.6.0"
|
||||
chalk "^4.1.0"
|
||||
commander "^7.0.0"
|
||||
get-port "^4.2.0"
|
||||
v8-compile-cache "^2.0.0"
|
||||
|
||||
parent-module@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
|
||||
|
@ -8575,6 +8670,11 @@ randomfill@^1.0.3:
|
|||
randombytes "^2.0.5"
|
||||
safe-buffer "^5.1.0"
|
||||
|
||||
react-content-loader@^6.2.0:
|
||||
version "6.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-content-loader/-/react-content-loader-6.2.0.tgz#cd8fee8160b8fda6610d0c69ce5aee7b8094cba6"
|
||||
integrity sha512-r1dI6S+uHNLW68qraLE2njJYOuy6976PpCExuCZUcABWbfnF3FMcmuESRI8L4Bj45wnZ7n8g71hkPLzbma7/Cw==
|
||||
|
||||
react-dom@^17.0.0, react-dom@^17.0.2:
|
||||
version "17.0.2"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23"
|
||||
|
@ -8966,18 +9066,18 @@ semver@7.0.0:
|
|||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e"
|
||||
integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==
|
||||
|
||||
semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0:
|
||||
version "6.3.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
|
||||
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
|
||||
|
||||
semver@^7.3.4:
|
||||
semver@7.x, semver@^7.3.4:
|
||||
version "7.3.7"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f"
|
||||
integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0:
|
||||
version "6.3.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
|
||||
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
|
||||
|
||||
semver@^7.3.5:
|
||||
version "7.3.5"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"
|
||||
|
@ -9428,6 +9528,11 @@ svgo@^2.4.0, svgo@^2.7.0:
|
|||
picocolors "^1.0.0"
|
||||
stable "^0.1.8"
|
||||
|
||||
term-size@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.1.tgz#2a6a54840432c2fb6320fea0f415531e90189f54"
|
||||
integrity sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==
|
||||
|
||||
terminal-link@^2.0.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994"
|
||||
|
@ -9529,6 +9634,20 @@ truncate-utf8-bytes@^1.0.0:
|
|||
dependencies:
|
||||
utf8-byte-length "^1.0.1"
|
||||
|
||||
ts-jest@^28.0.4:
|
||||
version "28.0.4"
|
||||
resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-28.0.4.tgz#0ab705a60fc4b9f3506f35e26edfa9e9c915c31b"
|
||||
integrity sha512-S6uRDDdCJBvnZqyGjB4VCnwbQrbgdL8WPeP4jevVSpYsBaeGRQAIS08o3Svav2Ex+oXwLgJ/m7F24TNq62kA1A==
|
||||
dependencies:
|
||||
bs-logger "0.x"
|
||||
fast-json-stable-stringify "2.x"
|
||||
jest-util "^28.0.0"
|
||||
json5 "^2.2.1"
|
||||
lodash.memoize "4.x"
|
||||
make-error "1.x"
|
||||
semver "7.x"
|
||||
yargs-parser "^20.x"
|
||||
|
||||
ts-node@^10.2.1:
|
||||
version "10.5.0"
|
||||
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.5.0.tgz#618bef5854c1fbbedf5e31465cbb224a1d524ef9"
|
||||
|
@ -9548,6 +9667,34 @@ ts-node@^10.2.1:
|
|||
v8-compile-cache-lib "^3.0.0"
|
||||
yn "3.1.1"
|
||||
|
||||
"ts-poet@^t 4.11.0":
|
||||
version "4.11.0"
|
||||
resolved "https://registry.yarnpkg.com/ts-poet/-/ts-poet-4.11.0.tgz#5566f499ec767920cc18b977624f084c52e8734a"
|
||||
integrity sha512-OaXnCKsRs0yrc0O7LFhnq/US2DB4Wd313cS+qjG2XMksZ74pF/jvMHkJdURXJiAo4kSahL2N4e8JOdwUjOMNdw==
|
||||
dependencies:
|
||||
lodash "^4.17.15"
|
||||
prettier "^2.5.1"
|
||||
|
||||
ts-proto-descriptors@1.6.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/ts-proto-descriptors/-/ts-proto-descriptors-1.6.0.tgz#ca6eafc882495a2e920da5b981d7b181b4e49c38"
|
||||
integrity sha512-Vrhue2Ti99us/o76mGy28nF3W/Uanl1/8detyJw2yyRwiBC5yxy+hEZqQ/ZX2PbZ1vyCpJ51A9L4PnCCnkBMTQ==
|
||||
dependencies:
|
||||
long "^4.0.0"
|
||||
protobufjs "^6.8.8"
|
||||
|
||||
ts-proto@^1.115.1:
|
||||
version "1.115.1"
|
||||
resolved "https://registry.yarnpkg.com/ts-proto/-/ts-proto-1.115.1.tgz#262d9506fe575e5d1a821397ae7f72df2ea8574d"
|
||||
integrity sha512-Zq1TvLQdnD6eNhbfdccnk1X9tVN5bwwPX4n2/gR9rVEHApZwHInV5Ntqd9lzl22lJ2LjlCNNYQdMlWak8d3EGA==
|
||||
dependencies:
|
||||
"@types/object-hash" "^1.3.0"
|
||||
dataloader "^1.4.0"
|
||||
object-hash "^1.3.1"
|
||||
protobufjs "^6.11.3"
|
||||
ts-poet "^t 4.11.0"
|
||||
ts-proto-descriptors "1.6.0"
|
||||
|
||||
tsconfig-paths@^3.12.0, tsconfig-paths@^3.9.0:
|
||||
version "3.12.0"
|
||||
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz#19769aca6ee8f6a1a341e38c8fa45dd9fb18899b"
|
||||
|
@ -9945,7 +10092,7 @@ yargs-parser@^13.1.2:
|
|||
camelcase "^5.0.0"
|
||||
decamelize "^1.2.0"
|
||||
|
||||
yargs-parser@^20.2.9:
|
||||
yargs-parser@^20.2.9, yargs-parser@^20.x:
|
||||
version "20.2.9"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
|
||||
integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
|
||||
|
|
Loading…
Reference in New Issue