diff --git a/packages/status-js/src/community.spec.ts b/packages/status-js/src/community.spec.ts deleted file mode 100644 index 94b2c8d1..00000000 --- a/packages/status-js/src/community.spec.ts +++ /dev/null @@ -1,78 +0,0 @@ -// todo?: move to __tests__ -import { Community } from './community' - -import type { CommunityDescription } from './wire/community_description' -import type { Waku, WakuMessage } from 'js-waku' - -// todo: move to __mocks__ -const WakuMock = { - store: { - queryHistory: jest.fn(), - }, -} - -describe('Community', () => { - beforeEach(() => { - // todo?: set as config instead - jest.resetAllMocks() - }) - - test('Retrieves community description', async () => { - // todo: move to __fixtures__ - const messages = [ - { - payload: new Uint8Array([ - 10, 65, 34, 159, 71, 242, 74, 40, 51, 11, 24, 120, 155, 102, 213, 241, - 204, 130, 230, 70, 184, 67, 216, 119, 77, 31, 226, 210, 217, 31, 82, - 178, 170, 106, 1, 67, 78, 127, 79, 198, 106, 0, 184, 167, 1, 117, 214, - 100, 35, 204, 53, 241, 245, 213, 210, 12, 223, 184, 203, 240, 170, - 153, 47, 186, 157, 134, 1, 18, 191, 2, 8, 3, 18, 140, 1, 10, 132, 1, - 48, 120, 48, 52, 97, 99, 52, 49, 57, 100, 97, 99, 57, 97, 56, 98, 98, - 98, 53, 56, 56, 50, 53, 97, 51, 99, 100, 101, 54, 48, 101, 101, 102, - 48, 101, 101, 55, 49, 98, 56, 99, 102, 54, 99, 54, 51, 100, 102, 54, - 49, 49, 101, 101, 101, 102, 99, 56, 101, 55, 97, 97, 99, 55, 99, 55, - 57, 98, 53, 53, 57, 53, 52, 98, 54, 55, 57, 100, 50, 52, 99, 102, 53, - 101, 99, 56, 50, 100, 97, 55, 101, 100, 57, 50, 49, 99, 97, 102, 50, - 52, 48, 54, 50, 56, 97, 57, 98, 102, 98, 51, 52, 53, 48, 99, 53, 49, - 49, 49, 97, 57, 99, 102, 102, 101, 53, 52, 101, 54, 51, 49, 56, 49, - 49, 18, 3, 10, 1, 1, 26, 2, 24, 3, 42, 41, 34, 16, 66, 111, 114, 105, - 110, 103, 32, 99, 111, 109, 109, 117, 110, 105, 116, 121, 42, 12, 68, - 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 46, 50, 7, 35, 52, - 51, 54, 48, 68, 70, 50, 60, 10, 36, 56, 52, 57, 53, 48, 98, 53, 50, - 45, 51, 97, 52, 52, 45, 52, 98, 102, 102, 45, 97, 52, 56, 99, 45, 52, - 98, 100, 102, 99, 48, 57, 55, 51, 49, 102, 57, 18, 20, 18, 2, 24, 1, - 26, 14, 34, 6, 114, 97, 110, 100, 111, 109, 42, 4, 100, 101, 115, 99, - 50, 63, 10, 36, 48, 48, 100, 51, 102, 53, 50, 53, 45, 97, 48, 99, 102, - 45, 52, 99, 52, 48, 45, 56, 51, 50, 100, 45, 53, 52, 51, 101, 99, 57, - 102, 56, 49, 56, 56, 98, 18, 23, 18, 2, 24, 1, 26, 15, 34, 8, 109, - 101, 115, 115, 97, 103, 101, 115, 42, 3, 100, 115, 99, 40, 1, 24, 25, - ]), - }, - ] as unknown as WakuMessage[] - - WakuMock.store.queryHistory.mockImplementationOnce((_, { callback }) => - Promise.resolve(callback(messages)) - ) - - const community = await Community.instantiateCommunity( - '0x02cf13719c8b836bebd4e430c497ee38e798a43e4d8c4760c34bbd9bf4f2434d26', - WakuMock as unknown as Waku - ) - const desc = community.description as CommunityDescription - expect(desc).toBeDefined() - - expect(desc.identity?.displayName).toEqual('Boring community') - - const descChats = Array.from(desc.chats.values()).map( - chat => chat?.identity?.displayName - ) - expect(descChats).toEqual(expect.arrayContaining(['random'])) - expect(descChats).toEqual(expect.arrayContaining(['messages'])) - - const chats = Array.from(community.chats.values()).map( - chat => chat?.communityChat?.identity?.displayName - ) - expect(chats).toEqual(expect.arrayContaining(['random'])) - expect(chats).toEqual(expect.arrayContaining(['messages'])) - }) -}) diff --git a/packages/status-js/src/community.ts b/packages/status-js/src/community.ts deleted file mode 100644 index 010ece27..00000000 --- a/packages/status-js/src/community.ts +++ /dev/null @@ -1,301 +0,0 @@ -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, WakuMessage } from 'js-waku' -import { createSymKeyFromPassword } from './encryption' -// import { createSymKeyFromPassword } from './encryption' - -type Events = 'communityfetch' | 'communityudpate' | 'channelmessagereceive' - -const dbg = debug('communities:community') - -export class Community { - public publicKey: Uint8Array - private waku: Waku - // TODO?: rename to channels - public chats: Map // Chat id, Chat - public description?: CommunityDescription - - constructor(publicKey: Uint8Array, waku: Waku) { - this.publicKey = publicKey - this.waku = waku - this.chats = new Map() - } - - // TODO: explain why init func instead of constructor - /** - * Instantiate a Community by retrieving its details from the Waku network. - * - * This class is used to interact with existing communities only, - * the Status Desktop or Mobile app must be used to manage a community. - * - * @param publicKey The community's public key in hex format. - * Can be found in the community's invite link: https://join.status.im/c/ - * @param waku The Waku instance, used to retrieve Community information from the network. - */ - public static async instantiateCommunity( - publicKey: string, - waku: Waku - ): Promise { - const community = new Community(hexToBuf(publicKey), waku) - - await community.refreshCommunityDescription() - - return community - } - - public get publicKeyStr(): string { - return bufToHex(this.publicKey) - } - - /** - * Retrieve and update community information from the network. - * Uses most recent community description message available. - */ - async refreshCommunityDescription(): Promise { - const desc = await CommunityDescription.retrieve( - this.publicKey, - this.waku.store - ) - - if (!desc) { - dbg(`Failed to retrieve Community Description for ${this.publicKeyStr}`) - return - } - - this.description = desc - - await Promise.all( - Array.from(this.description.chats).map(([chatUuid, communityChat]) => { - return this.instantiateChat(chatUuid, communityChat) - }) - ) - } - - /** - * Instantiate [[Chat]] object based on the passed chat name. - * The Chat MUST already be part of the Community and the name MUST be exact (including casing). - * - * @throws string If the Community Description is unavailable or the chat is not found; - */ - private async instantiateChat( - chatUuid: string, - communityChat: CommunityChat - ): Promise { - if (!this.description) - throw 'Failed to retrieve community description, cannot instantiate chat' - - const chatId = this.publicKeyStr + chatUuid - if (this.chats.get(chatId)) return - - const chat = await Chat.create(chatId, communityChat) - - 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 = {} - - 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 { - 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 -}