diff --git a/packages/backend/src/libp2p/connectionsManager.ts b/packages/backend/src/libp2p/connectionsManager.ts index d452e9677c..e3c590c18c 100644 --- a/packages/backend/src/libp2p/connectionsManager.ts +++ b/packages/backend/src/libp2p/connectionsManager.ts @@ -566,7 +566,7 @@ export class ConnectionsManager extends EventEmitter { await this.storage?.sendMessage(args.message) }) this.dataServer.on(SocketActionTypes.ASK_FOR_MESSAGES, async (args: AskForMessagesPayload) => { - await this.storage?.askForMessages(args.channelAddress, args.ids) + await this.storage?.askForMessages(args.channelId, args.ids) }) // Files @@ -595,8 +595,8 @@ export class ConnectionsManager extends EventEmitter { }) this.dataServer.on( SocketActionTypes.SEND_DIRECT_MESSAGE, - async (channelAddress: string, messagePayload) => { - await this.storage?.sendDirectMessage(channelAddress, messagePayload) + async (channelId: string, messagePayload) => { + await this.storage?.sendDirectMessage(channelId, messagePayload) } ) this.dataServer.on( @@ -615,14 +615,14 @@ export class ConnectionsManager extends EventEmitter { this.dataServer.on(SocketActionTypes.CLOSE, async () => { await this.closeAllServices() }) - this.dataServer.on(SocketActionTypes.DELETE_CHANNEL, async (payload: {channel: string}) => { + this.dataServer.on(SocketActionTypes.DELETE_CHANNEL, async (payload: {channelId: string}) => { await this.storage?.deleteChannel(payload) }) this.dataServer.on(SocketActionTypes.DELETE_FILES_FROM_CHANNEL, async (payload: DeleteFilesFromChannelSocketPayload) => { log('DELETE_FILES_FROM_CHANNEL : payload', payload) await this.deleteFilesFromChannel(payload) - await this.deleteFilesFromTemporaryDir() + // await this.deleteFilesFromTemporaryDir() //crashes on mobile, will be fixes in next versions }) } @@ -712,7 +712,7 @@ export class ConnectionsManager extends EventEmitter { this.storage.on(StorageEvents.CHECK_FOR_MISSING_FILES, (payload: CommunityId) => { this.io.emit(SocketActionTypes.CHECK_FOR_MISSING_FILES, payload) }) - this.storage.on(StorageEvents.CHANNEL_DELETION_RESPONSE, (payload: any) => { + this.storage.on(StorageEvents.CHANNEL_DELETION_RESPONSE, (payload: {channelId: string}) => { console.log('emitting deleted channel event back to state manager') this.io.emit(SocketActionTypes.CHANNEL_DELETION_RESPONSE, payload) }) diff --git a/packages/backend/src/socket/DataServer.ts b/packages/backend/src/socket/DataServer.ts index 515167bec9..f2604baaf3 100644 --- a/packages/backend/src/socket/DataServer.ts +++ b/packages/backend/src/socket/DataServer.ts @@ -90,15 +90,15 @@ export class DataServer extends EventEmitter { SocketActionTypes.SEND_DIRECT_MESSAGE, async ( peerId: string, - { channelAddress, message }: { channelAddress: string; message: string } + { channelId, message }: { channelId: string; message: string } ) => { - this.emit(SocketActionTypes.SEND_DIRECT_MESSAGE, { channelAddress, message }) + this.emit(SocketActionTypes.SEND_DIRECT_MESSAGE, { channelId, message }) } ) socket.on( SocketActionTypes.SUBSCRIBE_FOR_DIRECT_MESSAGE_THREAD, - async (peerId: string, channelAddress: string) => { - this.emit(SocketActionTypes.SUBSCRIBE_FOR_DIRECT_MESSAGE_THREAD, { peerId, channelAddress }) + async (peerId: string, channelId: string) => { + this.emit(SocketActionTypes.SUBSCRIBE_FOR_DIRECT_MESSAGE_THREAD, { peerId, channelId }) } ) socket.on( @@ -156,8 +156,8 @@ export class DataServer extends EventEmitter { log('leaving community') this.emit(SocketActionTypes.LEAVE_COMMUNITY) }) - socket.on(SocketActionTypes.DELETE_CHANNEL, async (payload: {channel: string}) => { - log('deleting channel ', payload.channel) + socket.on(SocketActionTypes.DELETE_CHANNEL, async (payload: {channelId: string}) => { + log('deleting channel ', payload.channelId) this.emit(SocketActionTypes.DELETE_CHANNEL, payload) }) socket.on(SocketActionTypes.DELETE_FILES_FROM_CHANNEL, async (payload: DeleteFilesFromChannelSocketPayload) => { diff --git a/packages/backend/src/storage/ipfsFileManager.test.ts b/packages/backend/src/storage/ipfsFileManager.test.ts index 2bce64f8e2..b49ef71ae4 100644 --- a/packages/backend/src/storage/ipfsFileManager.test.ts +++ b/packages/backend/src/storage/ipfsFileManager.test.ts @@ -56,7 +56,7 @@ describe('Ipfs file manager', () => { cid: 'uploading_id', message: { id: 'id', - channelAddress: 'channelAddress' + channelId: 'channelId' } } @@ -70,7 +70,7 @@ describe('Ipfs file manager', () => { expect(eventSpy).toHaveBeenNthCalledWith(1, StorageEvents.REMOVE_DOWNLOAD_STATUS, { cid: 'uploading_id' }) }) await waitForExpect(() => { - expect(eventSpy).toHaveBeenNthCalledWith(2, StorageEvents.UPLOADED_FILE, expect.objectContaining({ cid: cid, ext: '.png', height: 44, message: { channelAddress: 'channelAddress', id: 'id' }, name: 'test-image', size: 15858, width: 824 }) + expect(eventSpy).toHaveBeenNthCalledWith(2, StorageEvents.UPLOADED_FILE, expect.objectContaining({ cid: cid, ext: '.png', height: 44, message: { channelId: 'channelId', id: 'id' }, name: 'test-image', size: 15858, width: 824 }) ) }) await waitForExpect(() => { @@ -93,7 +93,7 @@ describe('Ipfs file manager', () => { cid: 'uploading_id', message: { id: 'id', - channelAddress: 'channelAddress' + channelId: 'channelId' } } @@ -103,7 +103,7 @@ describe('Ipfs file manager', () => { expect(eventSpy).toHaveBeenNthCalledWith(1, StorageEvents.REMOVE_DOWNLOAD_STATUS, { cid: 'uploading_id' }) }) await waitForExpect(() => { - expect(eventSpy).toHaveBeenNthCalledWith(2, StorageEvents.UPLOADED_FILE, expect.objectContaining({ cid: cid, ext: '.pdf', height: undefined, message: { channelAddress: 'channelAddress', id: 'id' }, name: 'test-file', size: 761991, width: undefined } + expect(eventSpy).toHaveBeenNthCalledWith(2, StorageEvents.UPLOADED_FILE, expect.objectContaining({ cid: cid, ext: '.pdf', height: undefined, message: { channelId: 'channelId', id: 'id' }, name: 'test-file', size: 761991, width: undefined } ) ) }) @@ -111,7 +111,7 @@ describe('Ipfs file manager', () => { expect(eventSpy).toHaveBeenNthCalledWith(3, StorageEvents.UPDATE_DOWNLOAD_PROGRESS, { cid: cid, downloadProgress: undefined, downloadState: 'hosted', mid: 'id' }) }) await waitForExpect(() => { - expect(eventSpy).toHaveBeenNthCalledWith(4, StorageEvents.UPDATE_MESSAGE_MEDIA, expect.objectContaining({ cid: cid, ext: '.pdf', height: undefined, message: { channelAddress: 'channelAddress', id: 'id' }, name: 'test-file', size: 761991, width: undefined }) + expect(eventSpy).toHaveBeenNthCalledWith(4, StorageEvents.UPDATE_MESSAGE_MEDIA, expect.objectContaining({ cid: cid, ext: '.pdf', height: undefined, message: { channelId: 'channelId', id: 'id' }, name: 'test-file', size: 761991, width: undefined }) ) }) }) @@ -130,7 +130,7 @@ describe('Ipfs file manager', () => { cid: 'uploading_id', message: { id: 'id', - channelAddress: 'channelAddress' + channelId: 'channelId' } } @@ -157,7 +157,7 @@ describe('Ipfs file manager', () => { cid: 'uploading_id', message: { id: 'id', - channelAddress: 'channelAddress' + channelId: 'channelId' } } @@ -167,13 +167,13 @@ describe('Ipfs file manager', () => { expect(eventSpy).toHaveBeenNthCalledWith(1, StorageEvents.REMOVE_DOWNLOAD_STATUS, { cid: 'uploading_id' }) }) await waitForExpect(() => { - expect(eventSpy).toHaveBeenNthCalledWith(2, StorageEvents.UPLOADED_FILE, expect.objectContaining({ cid: cid, ext: '.pdf', height: undefined, message: { channelAddress: 'channelAddress', id: 'id' }, name: 'test-file', size: 761991, width: undefined })) + expect(eventSpy).toHaveBeenNthCalledWith(2, StorageEvents.UPLOADED_FILE, expect.objectContaining({ cid: cid, ext: '.pdf', height: undefined, message: { channelId: 'channelId', id: 'id' }, name: 'test-file', size: 761991, width: undefined })) }) await waitForExpect(() => { expect(eventSpy).toHaveBeenNthCalledWith(3, StorageEvents.UPDATE_DOWNLOAD_PROGRESS, { cid: cid, downloadProgress: undefined, downloadState: 'hosted', mid: 'id' }) }) await waitForExpect(() => { - expect(eventSpy).toHaveBeenNthCalledWith(4, StorageEvents.UPDATE_MESSAGE_MEDIA, expect.objectContaining({ cid: cid, ext: '.pdf', height: undefined, message: { channelAddress: 'channelAddress', id: 'id' }, name: 'test-file', size: 761991, width: undefined })) + expect(eventSpy).toHaveBeenNthCalledWith(4, StorageEvents.UPDATE_MESSAGE_MEDIA, expect.objectContaining({ cid: cid, ext: '.pdf', height: undefined, message: { channelId: 'channelId', id: 'id' }, name: 'test-file', size: 761991, width: undefined })) }) // Downloading @@ -212,7 +212,7 @@ describe('Ipfs file manager', () => { cid, message: { id: 'id', - channelAddress: 'channelAddress' + channelId: 'channelId' } } @@ -223,7 +223,7 @@ describe('Ipfs file manager', () => { expect(eventSpy).toHaveBeenNthCalledWith(1, StorageEvents.REMOVE_DOWNLOAD_STATUS, { cid }) }) await waitForExpect(() => { - expect(eventSpy).toHaveBeenNthCalledWith(2, StorageEvents.UPLOADED_FILE, expect.objectContaining({ cid: cid, ext: '.pdf', height: undefined, message: { channelAddress: 'channelAddress', id: 'id' }, name: 'test-file', size: 761797, width: undefined }) + expect(eventSpy).toHaveBeenNthCalledWith(2, StorageEvents.UPLOADED_FILE, expect.objectContaining({ cid: cid, ext: '.pdf', height: undefined, message: { channelId: 'channelId', id: 'id' }, name: 'test-file', size: 761797, width: undefined }) ) }) await waitForExpect(() => { @@ -231,7 +231,7 @@ describe('Ipfs file manager', () => { ) }) await waitForExpect(() => { - expect(eventSpy).toHaveBeenNthCalledWith(4, StorageEvents.UPDATE_MESSAGE_MEDIA, expect.objectContaining({ cid: cid, ext: '.pdf', height: undefined, message: { channelAddress: 'channelAddress', id: 'id' }, name: 'test-file', size: 761797, width: undefined }) + expect(eventSpy).toHaveBeenNthCalledWith(4, StorageEvents.UPDATE_MESSAGE_MEDIA, expect.objectContaining({ cid: cid, ext: '.pdf', height: undefined, message: { channelId: 'channelId', id: 'id' }, name: 'test-file', size: 761797, width: undefined }) ) }) @@ -272,7 +272,7 @@ describe('Ipfs file manager', () => { cid: 'uploading_id', message: { id: 'id', - channelAddress: 'channelAddress' + channelId: 'channelId' } } @@ -281,13 +281,13 @@ describe('Ipfs file manager', () => { expect(eventSpy).toHaveBeenNthCalledWith(1, StorageEvents.REMOVE_DOWNLOAD_STATUS, { cid: 'uploading_id' } ) - expect(eventSpy).toHaveBeenNthCalledWith(2, StorageEvents.UPLOADED_FILE, expect.objectContaining({ cid: cid, ext: '.png', height: 44, message: { channelAddress: 'channelAddress', id: 'id' }, name: 'test-image', size: 15858, width: 824 }) + expect(eventSpy).toHaveBeenNthCalledWith(2, StorageEvents.UPLOADED_FILE, expect.objectContaining({ cid: cid, ext: '.png', height: 44, message: { channelId: 'channelId', id: 'id' }, name: 'test-image', size: 15858, width: 824 }) ) expect(eventSpy).toHaveBeenNthCalledWith(3, StorageEvents.UPDATE_DOWNLOAD_PROGRESS, { cid: cid, downloadProgress: undefined, downloadState: 'hosted', mid: 'id' } ) - expect(eventSpy).toHaveBeenNthCalledWith(4, StorageEvents.UPDATE_MESSAGE_MEDIA, expect.objectContaining({ cid: cid, ext: '.png', height: 44, message: { channelAddress: 'channelAddress', id: 'id' }, name: 'test-image', size: 15858, width: 824 }) + expect(eventSpy).toHaveBeenNthCalledWith(4, StorageEvents.UPDATE_MESSAGE_MEDIA, expect.objectContaining({ cid: cid, ext: '.png', height: 44, message: { channelId: 'channelId', id: 'id' }, name: 'test-image', size: 15858, width: 824 }) ) // Downloading @@ -308,7 +308,7 @@ describe('Ipfs file manager', () => { expect(eventSpy).toHaveBeenNthCalledWith(7, StorageEvents.UPDATE_DOWNLOAD_PROGRESS, { cid: cid, downloadProgress: { downloaded: 15855, size: 15858, transferSpeed: 0 }, downloadState: 'completed', mid: 'id' } ) }, 20000) - expect(eventSpy).toHaveBeenNthCalledWith(8, StorageEvents.UPDATE_MESSAGE_MEDIA, expect.objectContaining({ cid: cid, ext: '.png', height: 44, message: { channelAddress: 'channelAddress', id: 'id' }, name: 'test-image', size: 15858, width: 824 }) + expect(eventSpy).toHaveBeenNthCalledWith(8, StorageEvents.UPDATE_MESSAGE_MEDIA, expect.objectContaining({ cid: cid, ext: '.png', height: 44, message: { channelId: 'channelId', id: 'id' }, name: 'test-image', size: 15858, width: 824 }) ) }) it('downloaded file matches uploaded file', async () => { @@ -326,7 +326,7 @@ describe('Ipfs file manager', () => { cid: 'uploading_id', message: { id: 'id', - channelAddress: 'channelAddress' + channelId: 'channelId' } } @@ -370,7 +370,7 @@ describe('Ipfs file manager', () => { cid: 'uploading_id', message: { id: 'id', - channelAddress: 'channelAddress' + channelId: 'channelId' } } diff --git a/packages/backend/src/storage/storage.test.ts b/packages/backend/src/storage/storage.test.ts index 160c730561..7c2ebdd7ef 100644 --- a/packages/backend/src/storage/storage.test.ts +++ b/packages/backend/src/storage/storage.test.ts @@ -15,7 +15,7 @@ import { sleep } from '../sleep' import { StorageEvents } from './types' import type { Storage as StorageType } from './storage' import { ChannelMessage, Community, Identity, PublicChannel, TestMessage } from '@quiet/types' -import { Store, getFactory, prepareStore, publicChannels } from '@quiet/state-manager' +import { Store, getFactory, prepareStore, publicChannels, generateMessageFactoryContentWithId } from '@quiet/state-manager' const filename = fileURLToPath(import.meta.url) const dirname = path.dirname(filename) @@ -70,7 +70,7 @@ beforeAll(async () => { description: channel.description, owner: channel.owner, timestamp: channel.timestamp, - address: channel.address + id: channel.id } alice = await factory.create( @@ -87,7 +87,8 @@ beforeAll(async () => { await factory.create( 'Message', { - identity: alice + identity: alice, + message: generateMessageFactoryContentWithId(channel.id) } ) ).message @@ -170,10 +171,10 @@ describe('Channels', () => { const eventSpy = jest.spyOn(storage, 'emit') - await storage.deleteChannel({ channel: channelio.address }) + await storage.deleteChannel({ channelId: channelio.id }) expect(eventSpy).toBeCalledWith('channelDeletionResponse', { - channel: channelio.address + channelId: channelio.id }) }) }) @@ -312,7 +313,8 @@ describe('Certificate', () => { const aliceMessage = await factory.create< ReturnType['payload'] >('Message', { - identity: alice + identity: alice, + message: generateMessageFactoryContentWithId(channel.id) }) storage = new Storage(tmpAppDataPath, community.id, { createPaths: false }) @@ -326,7 +328,8 @@ describe('Certificate', () => { await storage.subscribeToChannel(channelio) const eventSpy = jest.spyOn(storage, 'emit') - const publicChannelRepo = storage.publicChannelsRepos.get(message.channelAddress) + console.log('storage.publicChannelsRepos.get(message.channelId)', storage.publicChannelsRepos.get(message.channelId)) + const publicChannelRepo = storage.publicChannelsRepos.get(message.channelId) expect(publicChannelRepo).not.toBeUndefined() // @ts-expect-error const db = publicChannelRepo.db @@ -358,13 +361,16 @@ describe('Certificate', () => { const aliceMessage = await factory.create< ReturnType['payload'] >('Message', { - identity: alice + identity: alice, + message: generateMessageFactoryContentWithId(channel.id) }) const johnMessage = await factory.create< ReturnType['payload'] >('Message', { - identity: john + identity: john, + message: generateMessageFactoryContentWithId(channel.id) + }) const aliceMessageWithJohnsPublicKey: ChannelMessage = { @@ -382,7 +388,7 @@ describe('Certificate', () => { await storage.subscribeToChannel(channelio) const spyOnEmit = jest.spyOn(storage, 'emit') - const publicChannelRepo = storage.publicChannelsRepos.get(message.channelAddress) + const publicChannelRepo = storage.publicChannelsRepos.get(message.channelId) expect(publicChannelRepo).not.toBeUndefined() // @ts-expect-error const db = publicChannelRepo.db @@ -443,7 +449,7 @@ describe('Message access controller', () => { await storage.subscribeToChannel(channelio) - const publicChannelRepo = storage.publicChannelsRepos.get(message.channelAddress) + const publicChannelRepo = storage.publicChannelsRepos.get(message.channelId) expect(publicChannelRepo).not.toBeUndefined() // @ts-expect-error const db = publicChannelRepo.db @@ -468,7 +474,8 @@ describe('Message access controller', () => { const aliceMessage = await factory.create< ReturnType['payload'] >('Message', { - identity: alice + identity: alice, + message: generateMessageFactoryContentWithId(channel.id) }) // @ts-expect-error userCertificate can be undefined const johnCertificate: string = john.userCertificate @@ -476,7 +483,7 @@ describe('Message access controller', () => { const spoofedMessage = { ...aliceMessage.message, - channelAddress: channelio.address, + channelId: channelio.id, pubKey: johnPublicKey } delete spoofedMessage.media // Media 'undefined' is not accepted by db.add @@ -492,7 +499,7 @@ describe('Message access controller', () => { await storage.subscribeToChannel(channelio) - const publicChannelRepo = storage.publicChannelsRepos.get(message.channelAddress) + const publicChannelRepo = storage.publicChannelsRepos.get(message.channelId) expect(publicChannelRepo).not.toBeUndefined() // @ts-expect-error const db = publicChannelRepo.db diff --git a/packages/backend/src/storage/storage.ts b/packages/backend/src/storage/storage.ts index f3c11156ae..afe40ac4c3 100644 --- a/packages/backend/src/storage/storage.ts +++ b/packages/backend/src/storage/storage.ts @@ -8,6 +8,7 @@ import { verifyUserCert } from '@quiet/identity' import type { IPFS, create as createType } from 'ipfs-core' +import { create } from 'ipfs-core' import type { Libp2p } from 'libp2p' import OrbitDB from 'orbit-db' import EventStore from 'orbit-db-eventstore' @@ -32,7 +33,6 @@ import { stringToArrayBuffer } from 'pvutils' import { StorageEvents } from './types' import { IpfsFilesManager, IpfsFilesManagerEvents } from './ipfsFileManager' -import { create } from 'ipfs-core' import { CID } from 'multiformats/cid' import { ChannelMessage, ConnectionProcessInfo, FileMetadata, NoCryptoEngineError, PublicChannel, PushNotificationPayload, SaveCertificatePayload, SocketActionTypes, User } from '@quiet/types' @@ -267,20 +267,24 @@ export class Storage extends EventEmitter { this.emit(SocketActionTypes.CONNECTION_PROCESS_INFO, ConnectionProcessInfo.CHANNELS_REPLICATED) // @ts-expect-error - OrbitDB's type declaration of `load` lacks 'options' await this.channels.load({ fetchEntryTimeout: 2000 }) - this.emit(StorageEvents.LOAD_PUBLIC_CHANNELS, { - channels: this.channels.all as unknown as { [key: string]: PublicChannel } + + const channels = Object.values(this.channels.all).map(channel => { + return this.transformChannel(channel) + }) + + const keyValueChannels: { + [key: string]: PublicChannel + } = {} + + channels.forEach(channel => { + keyValueChannels[channel.id] = channel }) - // Delete channel on replication - // Array.from(this.publicChannelsRepos.keys()).forEach(e => { - // const isDeleted = !Object.keys(this.channels.all).includes(e) - // if (isDeleted) { - // log('deleting channel ', e) - // void this.deleteChannel({ channel: e }) - // } - // }) + this.emit(StorageEvents.LOAD_PUBLIC_CHANNELS, { + channels: keyValueChannels + }) - Object.values(this.channels.all).forEach(async (channel: PublicChannel) => { + channels.forEach(async (channel: PublicChannel) => { await this.subscribeToChannel(channel) }) }) @@ -290,6 +294,7 @@ export class Storage extends EventEmitter { log('ALL CHANNELS COUNT:', Object.keys(this.channels.all).length) log('ALL CHANNELS COUNT:', Object.keys(this.channels.all)) Object.values(this.channels.all).forEach(async (channel: PublicChannel) => { + channel = this.transformChannel(channel) await this.subscribeToChannel(channel) }) log('STORAGE: Finished createDbForChannels') @@ -364,28 +369,33 @@ export class Storage extends EventEmitter { public async subscribeToChannel(channelData: PublicChannel): Promise { let db: EventStore - let repo = this.publicChannelsRepos.get(channelData.address) + // @ts-ignore + if (channelData.address) { + // @ts-ignore + channelData.id = channelData.address + } + let repo = this.publicChannelsRepos.get(channelData.id) if (repo) { db = repo.db } else { try { db = await this.createChannel(channelData) } catch (e) { - log.error(`Can't subscribe to channel ${channelData.address}`, e.message) + log.error(`Can't subscribe to channel ${channelData.id}`, e.message) return } if (!db) { - log(`Can't subscribe to channel ${channelData.address}`) + log(`Can't subscribe to channel ${channelData.id}`) return } - repo = this.publicChannelsRepos.get(channelData.address) + repo = this.publicChannelsRepos.get(channelData.id) } if (repo && !repo.eventsAttached) { - log('Subscribing to channel ', channelData.address) + log('Subscribing to channel ', channelData.id) db.events.on('write', async (_address, entry) => { - log(`Writing to public channel db ${channelData.address}`) + log(`Writing to public channel db ${channelData.id}`) const verified = await this.verifyMessage(entry.payload.value) this.emit(StorageEvents.LOAD_MESSAGES, { @@ -396,12 +406,14 @@ export class Storage extends EventEmitter { db.events.on('replicate.progress', async (address, _hash, entry, progress, total) => { log(`progress ${progress as string}/${total as string}. Address: ${address as string}`) - const message = entry.payload.value + const messages = this.transformMessages([entry.payload.value]) + + const verified = await this.verifyMessage(messages[0]) - const verified = await this.verifyMessage(message) + const message = messages[0] this.emit(StorageEvents.LOAD_MESSAGES, { - messages: [entry.payload.value], + messages: [message], isVerified: verified }) @@ -410,6 +422,7 @@ export class Storage extends EventEmitter { if (!verified) return // Do not notify about old messages + // @ts-ignore if (parseInt(message.createdAt) < parseInt(process.env.CONNECTION_TIME || '')) return const username = this.getUserNameFromCert(message.pubKey) @@ -431,7 +444,7 @@ export class Storage extends EventEmitter { const ids = this.getAllEventLogEntries(db).map(msg => msg.id) this.emit(StorageEvents.SEND_MESSAGES_IDS, { ids, - channelAddress: channelData.address, + channelId: channelData.id, communityId: this.communityId }) }) @@ -439,7 +452,7 @@ export class Storage extends EventEmitter { const ids = this.getAllEventLogEntries(db).map(msg => msg.id) this.emit(StorageEvents.SEND_MESSAGES_IDS, { ids, - channelAddress: channelData.address, + channelId: channelData.id, communityId: this.communityId }) }) @@ -447,20 +460,54 @@ export class Storage extends EventEmitter { repo.eventsAttached = true } - log(`Subscribed to channel ${channelData.address}`) + log(`Subscribed to channel ${channelData.id}`) this.emit(StorageEvents.SET_CHANNEL_SUBSCRIBED, { - channelAddress: channelData.address + channelId: channelData.id + }) + } + + public transformMessages(msgs: ChannelMessage[]) { + console.log('---------------- TRANSFORMING MESSAGES ----------------------') + const messages = msgs.map((msg) => { + console.log('processing message ', msg.id) + // @ts-ignore + if (msg.channelAddress) { + console.log('message before transformation ', msg) + // @ts-ignore + msg.channelId = msg.channelAddress + // @ts-ignore + delete msg.channelAddress + console.log('transformed message to new format ', msg) + return msg + } + return msg }) + return messages } - public async askForMessages(channelAddress: string, ids: string[]) { - const repo = this.publicChannelsRepos.get(channelAddress) + public transformChannel(channel: PublicChannel) { + // @ts-ignore + if (channel.address) { + console.log('channel before transformation ', channel) + // @ts-ignore + channel.id = channel.address + // @ts-ignore + delete channel.address + console.log('transformed channel to new format ', channel) + return channel + } + return channel + } + + public async askForMessages(channelId: string, ids: string[]) { + const repo = this.publicChannelsRepos.get(channelId) if (!repo) return const messages = this.getAllEventLogEntries(repo.db) - const filteredMessages: ChannelMessage[] = [] + let filteredMessages: ChannelMessage[] = [] for (const id of ids) { filteredMessages.push(...messages.filter(i => i.id === id)) } + filteredMessages = this.transformMessages(filteredMessages) this.emit(StorageEvents.LOAD_MESSAGES, { messages: filteredMessages, isVerified: true @@ -469,14 +516,18 @@ export class Storage extends EventEmitter { } private async createChannel(data: PublicChannel): Promise> { + console.log('creating channel') if (!validate.isChannel(data)) { log.error('STORAGE: Invalid channel format') throw new Error('Create channel validation error') } - log(`Creating channel ${data.address}`) + log(`Creating channel ${data.id}`) + + // @ts-ignore + const channelId = data.id || data.address const db: EventStore = await this.orbitdb.log( - `channels.${data.address}`, + `channels.${channelId}`, { accessController: { type: 'messagesaccess', @@ -485,9 +536,9 @@ export class Storage extends EventEmitter { } ) - const channel = this.channels.get(data.address) + const channel = this.channels.get(channelId) if (channel === undefined) { - await this.channels.put(data.address, { + await this.channels.put(channelId, { ...data }) this.emit(StorageEvents.CREATED_CHANNEL, { @@ -495,26 +546,27 @@ export class Storage extends EventEmitter { }) } - this.publicChannelsRepos.set(data.address, { db, eventsAttached: false }) - log(`Set ${data.address} to local channels`) + this.publicChannelsRepos.set(channelId, { db, eventsAttached: false }) + log(`Set ${channelId} to local channels`) // @ts-expect-error - OrbitDB's type declaration of `load` lacks 'options' await db.load({ fetchEntryTimeout: 2000, }) - log(`Created channel ${data.address}`) + log(`Created channel ${channelId}`) return db } - public async deleteChannel(payload: { channel: string }) { + public async deleteChannel(payload: { channelId: string }) { console.log('deleting channel storage', payload) + const { channelId } = payload // @ts-expect-error - OrbitDB's type declaration of `load` lacks 'options' await this.channels.load({ fetchEntryTimeout: 15000 }) - const channel = this.channels.get(payload.channel) + const channel = this.channels.get(channelId) if (channel) { - await this.channels.del(payload.channel) + await this.channels.del(channelId) } - let repo = this.publicChannelsRepos.get(payload.channel) + let repo = this.publicChannelsRepos.get(channelId) if (!repo) { const db = await this.orbitdb.log( - `channels.${payload.channel}`, + `channels.${channelId}`, { accessController: { type: 'messagesaccess', @@ -537,7 +589,7 @@ export class Storage extends EventEmitter { }).filter(isDefined) // await this.deleteChannelFiles(files) // await this.deleteChannelMessages(hashes) - this.publicChannelsRepos.delete(payload.channel) + this.publicChannelsRepos.delete(channelId) this.emit(StorageEvents.CHANNEL_DELETION_RESPONSE, payload) } @@ -573,10 +625,10 @@ export class Storage extends EventEmitter { log.error('STORAGE: public channel message is invalid') return } - const repo = this.publicChannelsRepos.get(message.channelAddress) + const repo = this.publicChannelsRepos.get(message.channelId) if (!repo) { log.error( - `Could not send message. No '${message.channelAddress}' channel in saved public channels` + `Could not send message. No '${message.channelId}' channel in saved public channels` ) return } @@ -644,58 +696,58 @@ export class Storage extends EventEmitter { ) } - public async subscribeToDirectMessageThread(channelAddress: string) { + public async subscribeToDirectMessageThread(channelId: string) { let db: EventStore - let repo = this.directMessagesRepos.get(channelAddress) + let repo = this.directMessagesRepos.get(channelId) if (repo) { db = repo.db } else { - db = await this.createDirectMessageThread(channelAddress) + db = await this.createDirectMessageThread(channelId) if (!db) { - log(`Can't subscribe to direct messages thread ${channelAddress}`) + log(`Can't subscribe to direct messages thread ${channelId}`) return } - repo = this.directMessagesRepos.get(channelAddress) + repo = this.directMessagesRepos.get(channelId) } if (repo && !repo.eventsAttached) { - log('Subscribing to direct messages thread ', channelAddress) + log('Subscribing to direct messages thread ', channelId) this.emit(StorageEvents.LOAD_ALL_DIRECT_MESSAGES, { messages: this.getAllEventLogEntries(db), - channelAddress + channelId }) db.events.on('write', (_address, _entry) => { log('Writing') this.emit(StorageEvents.LOAD_ALL_DIRECT_MESSAGES, { messages: this.getAllEventLogEntries(db), - channelAddress + channelId }) }) db.events.on('replicated', () => { log('Message replicated') this.emit(StorageEvents.LOAD_ALL_DIRECT_MESSAGES, { messages: this.getAllEventLogEntries(db), - channelAddress + channelId }) }) db.events.on('ready', () => { log('DIRECT Messages thread ready') }) repo.eventsAttached = true - log('Subscription to channel ready', channelAddress) + log('Subscription to channel ready', channelId) } } - private async createDirectMessageThread(channelAddress: string): Promise> { - if (!channelAddress) { - log("No channel address, can't create channel") - throw new Error('No channel address, can\'t create channel') + private async createDirectMessageThread(channelId: string): Promise> { + if (!channelId) { + log("No channel ID, can't create channel") + throw new Error('No channel ID, can\'t create channel') } - log(`creatin direct message thread for ${channelAddress}`) + log(`creatin direct message thread for ${channelId}`) - const db: EventStore = await this.orbitdb.log(`dms.${channelAddress}`, { + const db: EventStore = await this.orbitdb.log(`dms.${channelId}`, { accessController: { write: ['*'] } @@ -706,20 +758,20 @@ export class Storage extends EventEmitter { // @ts-expect-error - OrbitDB's type declaration of `load` lacks 'options' await db.load({ fetchEntryTimeout: 2000 }) - this.directMessagesRepos.set(channelAddress, { db, eventsAttached: false }) + this.directMessagesRepos.set(channelId, { db, eventsAttached: false }) return db } - public async sendDirectMessage(channelAddress: string, message: string) { + public async sendDirectMessage(channelId: string, message: string) { if (!validate.isDirectMessage(message)) { log.error('STORAGE: Invalid direct message format') return } - await this.subscribeToDirectMessageThread(channelAddress) // Is it necessary? Yes it is atm + await this.subscribeToDirectMessageThread(channelId) // Is it necessary? Yes it is atm log('STORAGE: sendDirectMessage entered') - log(`STORAGE: sendDirectMessage channelAddress is ${channelAddress}`) + log(`STORAGE: sendDirectMessage channelId is ${channelId}`) log(`STORAGE: sendDirectMessage message is ${JSON.stringify(message)}`) - const db = this.directMessagesRepos.get(channelAddress)?.db + const db = this.directMessagesRepos.get(channelId)?.db if (!db) return log(`STORAGE: sendDirectMessage db is ${db.address.root}`) log(`STORAGE: sendDirectMessage db is ${db.address.path}`) diff --git a/packages/backend/src/storage/storageLargeFiles.test.ts b/packages/backend/src/storage/storageLargeFiles.test.ts index c61d80cf04..dcb47bf085 100644 --- a/packages/backend/src/storage/storageLargeFiles.test.ts +++ b/packages/backend/src/storage/storageLargeFiles.test.ts @@ -47,7 +47,7 @@ describe('Storage', () => { cid: 'uploading_id', message: { id: 'id', - channelAddress: 'channelAddress' + channelId: 'channelId' } } diff --git a/packages/backend/src/validation/validators.test.ts b/packages/backend/src/validation/validators.test.ts index b96b667df8..23b9ad0ddb 100644 --- a/packages/backend/src/validation/validators.test.ts +++ b/packages/backend/src/validation/validators.test.ts @@ -70,7 +70,7 @@ describe('Validators - Messages', () => { type: 1, message: 'hello', createdAt: 1234567, - channelAddress: '123n23l234lk234', + channelId: '123n23l234lk234', signature: 'asdfasdf', pubKey: 'afsdf' } @@ -82,14 +82,14 @@ describe('Validators - Messages', () => { type: 1, message: 'hello', createdAt: 1234567, - channelAddress: '123n23l234lk234', + channelId: '123n23l234lk234', signature: 'asdfasdf', pubKey: 'afsdf', media: { cid: '123', message: { id: 'fzxjdiasf8ashfisfd', - channelAddress: '123n23l234lk234' + channelId: '123n23l234lk234' }, path: '/path/to/file', name: 'file', @@ -103,7 +103,7 @@ describe('Validators - Messages', () => { type: 1, message: 'hello', createdAt: 1234567, - channelAddress: '123n23l234lk234', + channelId: '123n23l234lk234', signature: 'asdfasdf', pubKey: 'afsdf' } @@ -127,13 +127,13 @@ describe('Validators - Messages', () => { type: 1, message: 'hello', createdAt: 1234567, - channelAddress: '123n23l234lk234', + channelId: '123n23l234lk234', signature: 'asdfasdf', pubKey: 'afsdf', media: { message: { id: 'fzxjdiasf8ashfisfd', - channelAddress: '123n23l234lk234' + channelId: '123n23l234lk234' }, path: '/path/to/file', name: 'file', @@ -151,7 +151,7 @@ describe('Validators - Channels', () => { description: 'quiet', owner: 'szakalakakaaakaka', timestamp: 12341234, - address: 'sadfdasfsadfsdfsnfsdjfdsfsdfjsdf' + id: 'sadfdasfsadfsdfsnfsdjfdsfsdfjsdf' } expect(isChannel(channel)).toBeTruthy() }) @@ -160,7 +160,7 @@ describe('Validators - Channels', () => { name: 'quiet', description: 'quiet', owner: 'szakalakakaaakaka', - address: 'sadfdasfsadfsdfsnfsdjfdsfsdfjsdf' + id: 'sadfdasfsadfsdfsnfsdjfdsfsdfjsdf' } expect(isChannel(channel as unknown as PublicChannel)).toBeFalsy() }) @@ -170,7 +170,7 @@ describe('Validators - Channels', () => { description: 'quiet', owner: 'szakalakakaaakaka', timestamp: 'asfasdf', - address: 'sadfdasfsadfsdfsnfsdjfdsfsdfjsdf' + id: 'sadfdasfsadfsdfsnfsdjfdsfsdfjsdf' } expect(isChannel((channel as unknown) as PublicChannel)).toBeFalsy() }) diff --git a/packages/backend/src/validation/validators.ts b/packages/backend/src/validation/validators.ts index a011bcaff8..1902c1aa18 100644 --- a/packages/backend/src/validation/validators.ts +++ b/packages/backend/src/validation/validators.ts @@ -14,7 +14,8 @@ const messageMediaSchema = joi.object({ height: joi.number().allow(null), message: joi.object({ id: joi.string().required(), - channelAddress: joi.string().required() + channelId: joi.string(), + channelAddress: joi.string() }) }) @@ -24,7 +25,8 @@ const messageSchema = joi.object({ message: joi.alternatives(joi.string(), joi.binary()).required(), media: messageMediaSchema, createdAt: joi.number().required(), - channelAddress: joi.string().required(), + channelId: joi.string(), + channelAddress: joi.string(), signature: joi.string().required(), pubKey: joi.string().required() }) @@ -34,7 +36,8 @@ const channelSchema = joi.object({ description: joi.string().required(), owner: joi.string().required(), timestamp: joi.number().required(), - address: joi.string().required() + id: joi.string(), + address: joi.string() }) export const isUser = (publicKey: string, halfKey: string): boolean => { diff --git a/packages/common/src/channelAddress.ts b/packages/common/src/channelAddress.ts new file mode 100644 index 0000000000..fbc8531cca --- /dev/null +++ b/packages/common/src/channelAddress.ts @@ -0,0 +1,4 @@ +import crypto from 'crypto' + +export const generateChannelId = (channelName: String) => + `${channelName}_${crypto.randomBytes(16).toString('hex')}` diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index ca4a8ab172..e2bfdd4163 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -4,3 +4,4 @@ export * from './capitalize' export * from './process' export * from './helpers' export * from './sortPeers' +export * from './channelAddress' diff --git a/packages/desktop/src/renderer/components/Channel/Channel.stories.cy.tsx b/packages/desktop/src/renderer/components/Channel/Channel.stories.cy.tsx index 6040b06858..7e30236e41 100644 --- a/packages/desktop/src/renderer/components/Channel/Channel.stories.cy.tsx +++ b/packages/desktop/src/renderer/components/Channel/Channel.stories.cy.tsx @@ -46,7 +46,7 @@ const Template: ComponentStory = () => { type: 1, message: 'I agree!', createdAt: 0, - channelAddress: 'general', + channelId: 'general', signature: 'signature', pubKey: 'pubKey' }} @@ -85,7 +85,7 @@ const Template: ComponentStory = () => { handleClose: function (): any {}, src: 'images/butterfly.jpeg' }} - channelAddress={'general'} + channelId={'general'} channelName={'general'} lazyLoading={function (_load: boolean): void {}} onInputChange={function (_value: string): void {}} diff --git a/packages/desktop/src/renderer/components/Channel/Channel.stories.tsx b/packages/desktop/src/renderer/components/Channel/Channel.stories.tsx index 36b6dfc5b5..aa2933cc64 100644 --- a/packages/desktop/src/renderer/components/Channel/Channel.stories.tsx +++ b/packages/desktop/src/renderer/components/Channel/Channel.stories.tsx @@ -52,12 +52,12 @@ const args: Partial = { type: 1, message: 'I agree!', createdAt: 0, - channelAddress: 'general', + channelId: 'general', signature: 'signature', pubKey: 'pubKey' }, pendingMessages: {}, - channelAddress: 'general', + channelId: 'general', channelName: 'general', lazyLoading: function (_load: boolean): void {}, onInputChange: function (_value: string): void {}, @@ -113,7 +113,7 @@ ImagePlaceholder.args = { media: { cid: 'QmWUCSApiy76nW9DAk5M9QbH1nkW5XCYwxUHRSULjATyqs', message: { - channelAddress: 'general', + channelId: 'general', id: 'wgtlstx3u7' }, ext: '.png', @@ -143,7 +143,7 @@ SentImage.args = { media: { cid: 'QmWUCSApiy76nW9DAk5M9QbH1nkW5XCYwxUHRSULjATyqs', message: { - channelAddress: 'general', + channelId: 'general', id: 'wgtlstx3u7' }, ext: '.png', @@ -211,7 +211,7 @@ UploadingFile.args = { media: { cid: 'uploading_32', message: { - channelAddress: 'general', + channelId: 'general', id: 'wgtlstx3u7' }, ext: '.zip', @@ -241,7 +241,7 @@ HostedFile.args = { media: { cid: 'QmWUCSApiy76nW9DAk5M9QbH1nkW5XCYwxUHRSULjATyqs', message: { - channelAddress: 'general', + channelId: 'general', id: 'wgtlstx3u7' }, ext: '.zip', @@ -272,7 +272,7 @@ ReadyDownload.args = { media: { cid: 'QmWUCSApiy76nW9DAk5M9QbH1nkW5XCYwxUHRSULjATyqs', message: { - channelAddress: 'general', + channelId: 'general', id: 'wgtlstx3u7' }, ext: '.zip', @@ -303,7 +303,7 @@ Downloading.args = { media: { cid: 'QmWUCSApiy76nW9DAk5M9QbH1nkW5XCYwxUHRSULjATyqs', message: { - channelAddress: 'general', + channelId: 'general', id: 'wgtlstx3u7' }, ext: '.zip', @@ -338,7 +338,7 @@ CompletedDownload.args = { media: { cid: 'QmWUCSApiy76nW9DAk5M9QbH1nkW5XCYwxUHRSULjATyqs', message: { - channelAddress: 'general', + channelId: 'general', id: 'wgtlstx3u7' }, ext: '.zip', @@ -373,7 +373,7 @@ CancelingDownload.args = { media: { cid: 'QmWUCSApiy76nW9DAk5M9QbH1nkW5XCYwxUHRSULjATyqs', message: { - channelAddress: 'general', + channelId: 'general', id: 'wgtlstx3u7' }, ext: '.zip', @@ -408,7 +408,7 @@ CanceledDownload.args = { media: { cid: 'QmWUCSApiy76nW9DAk5M9QbH1nkW5XCYwxUHRSULjATyqs', message: { - channelAddress: 'general', + channelId: 'general', id: 'wgtlstx3u7' }, ext: '.zip', @@ -439,7 +439,7 @@ MaliciousDownload.args = { media: { cid: 'QmWUCSApiy76nW9DAk5M9QbH1nkW5XCYwxUHRSULjATyqs', message: { - channelAddress: 'general', + channelId: 'general', id: 'wgtlstx3u7' }, ext: '.zip', diff --git a/packages/desktop/src/renderer/components/Channel/Channel.tsx b/packages/desktop/src/renderer/components/Channel/Channel.tsx index 995fbb0087..a51e5df894 100644 --- a/packages/desktop/src/renderer/components/Channel/Channel.tsx +++ b/packages/desktop/src/renderer/components/Channel/Channel.tsx @@ -19,10 +19,7 @@ import ChannelComponent, { ChannelComponentProps } from './ChannelComponent' import { useModal } from '../../containers/hooks' import { ModalName } from '../../sagas/modals/modals.types' -import { - FilePreviewData, - UploadFilesPreviewsProps -} from './File/UploadingPreview' +import { FilePreviewData, UploadFilesPreviewsProps } from './File/UploadingPreview' import { getFilesData } from '../../../utils/functions/fileData' @@ -36,7 +33,7 @@ const Channel = () => { const user = useSelector(identity.selectors.currentIdentity) - const currentChannelAddress = useSelector(publicChannels.selectors.currentChannelAddress) + const currentChannelId = useSelector(publicChannels.selectors.currentChannelId) const currentChannelName = useSelector(publicChannels.selectors.currentChannelName) const currentChannelMessagesCount = useSelector( @@ -58,8 +55,14 @@ const Channel = () => { const initializedCommunities = useSelector(network.selectors.initializedCommunities) const isCommunityInitialized = Boolean(initializedCommunities[community?.id]) - const pendingGeneralChannelRecreationSelector = useSelector(publicChannels.selectors.pendingGeneralChannelRecreation) - const pendingGeneralChannelRecreation = pendingGeneralChannelRecreationSelector && currentChannelAddress === 'general' && currentChannelMessagesCount === 0 + const pendingGeneralChannelRecreationSelector = useSelector( + publicChannels.selectors.pendingGeneralChannelRecreation + ) + + const pendingGeneralChannelRecreation = + pendingGeneralChannelRecreationSelector && + (currentChannelName === 'general' || currentChannelName === '') && + currentChannelMessagesCount === 0 let enableContextMenu: boolean = false if (community) { @@ -182,13 +185,19 @@ const Channel = () => { shell.showItemInFolder(path) }, []) - const downloadFile = useCallback((media: FileMetadata) => { - dispatch(files.actions.downloadFile(media)) - }, [dispatch]) + const downloadFile = useCallback( + (media: FileMetadata) => { + dispatch(files.actions.downloadFile(media)) + }, + [dispatch] + ) - const cancelDownload = useCallback((cancelDownload: CancelDownload) => { - dispatch(files.actions.cancelDownload(cancelDownload)) - }, [dispatch]) + const cancelDownload = useCallback( + (cancelDownload: CancelDownload) => { + dispatch(files.actions.cancelDownload(cancelDownload)) + }, + [dispatch] + ) const openContextMenu = useCallback(() => { contextMenu.handleOpen() @@ -196,11 +205,11 @@ const Channel = () => { useEffect(() => { dispatch(messages.actions.resetCurrentPublicChannelCache()) - }, [currentChannelAddress]) + }, [currentChannelId]) const channelComponentProps: ChannelComponentProps = { user: user, - channelAddress: currentChannelAddress, + channelId: currentChannelId, channelName: currentChannelName, messages: { count: currentChannelMessagesCount, @@ -236,8 +245,13 @@ const Channel = () => { return ( <> - {currentChannelAddress && ( - + {currentChannelId && ( + )} ) diff --git a/packages/desktop/src/renderer/components/Channel/ChannelComponent.tsx b/packages/desktop/src/renderer/components/Channel/ChannelComponent.tsx index 392c0c146e..a20669430b 100644 --- a/packages/desktop/src/renderer/components/Channel/ChannelComponent.tsx +++ b/packages/desktop/src/renderer/components/Channel/ChannelComponent.tsx @@ -41,7 +41,7 @@ const ChannelMessagesWrapperStyled = styled(Grid)(({ theme }) => ({ export interface ChannelComponentProps { user: Identity - channelAddress: string + channelId: string channelName: string messages: { count: number @@ -78,7 +78,7 @@ export const ChannelComponent: React.FC< ChannelComponentProps & UploadFilesPreviewsProps & FileActionsProps > = ({ user, - channelAddress, + channelId, channelName, messages, newestMessage, @@ -213,7 +213,7 @@ export const ChannelComponent: React.FC< useEffect(() => { scrollBottom() - }, [channelAddress]) + }, [channelId]) return ( @@ -244,7 +244,7 @@ export const ChannelComponent: React.FC< { const dispatch = useDispatch() @@ -37,7 +38,7 @@ export const CreateChannel = () => { ) { dispatch( publicChannels.actions.setCurrentChannel({ - channelAddress: newChannel.address + channelId: newChannel.id }) ) setNewChannel(null) @@ -66,12 +67,13 @@ export const CreateChannel = () => { ) return } + // Move to state manager // Create channel const channel: PublicChannel = { name: name, description: `Welcome to #${name}`, owner: user.nickname, - address: name, + id: generateChannelId(name), timestamp: DateTime.utc().valueOf() } flushSync(() => { diff --git a/packages/desktop/src/renderer/components/Channel/DeleteChannel/DeleteChannel.test.tsx b/packages/desktop/src/renderer/components/Channel/DeleteChannel/DeleteChannel.test.tsx index 637f0aa20d..63cb9d4884 100644 --- a/packages/desktop/src/renderer/components/Channel/DeleteChannel/DeleteChannel.test.tsx +++ b/packages/desktop/src/renderer/components/Channel/DeleteChannel/DeleteChannel.test.tsx @@ -6,7 +6,7 @@ describe('LeaveCommunity', () => { it('renders component', () => { const result = renderComponent( { const dispatch = useDispatch() const deleteChannel = useCallback(() => { - dispatch(publicChannels.actions.deleteChannel({ channel: channel.name })) + dispatch(publicChannels.actions.deleteChannel({ channelId: channel.id })) modal.handleClose() // Close self }, [modal]) - return + return } export default DeleteChannel diff --git a/packages/desktop/src/renderer/components/Channel/DeleteChannel/DeleteChannelComponent.tsx b/packages/desktop/src/renderer/components/Channel/DeleteChannel/DeleteChannelComponent.tsx index 82d00076e9..83d8046665 100644 --- a/packages/desktop/src/renderer/components/Channel/DeleteChannel/DeleteChannelComponent.tsx +++ b/packages/desktop/src/renderer/components/Channel/DeleteChannel/DeleteChannelComponent.tsx @@ -80,14 +80,14 @@ const StyledGrid = styled(Grid)(({ theme }) => ({ })) export interface DeleteChannelProps { - channel: string + channelName: string deleteChannel: () => void } export const DeleteChannelComponent: React.FC & DeleteChannelProps> = ({ open, handleClose, - channel, + channelName, deleteChannel }) => { return ( @@ -111,7 +111,7 @@ export const DeleteChannelComponent: React.FC & Dele justifyContent='center'> Delete{' '} - #{channel}? This cannot be undone. + #{channelName}? This cannot be undone. diff --git a/packages/desktop/src/renderer/components/Channel/File/FileComponent/FileComponent.stories.tsx b/packages/desktop/src/renderer/components/Channel/File/FileComponent/FileComponent.stories.tsx index b830e302f7..03d89def3f 100644 --- a/packages/desktop/src/renderer/components/Channel/File/FileComponent/FileComponent.stories.tsx +++ b/packages/desktop/src/renderer/components/Channel/File/FileComponent/FileComponent.stories.tsx @@ -33,7 +33,7 @@ const args: FileComponentProps = { media: { cid: cid, message: { - channelAddress: 'general', + channelId: 'general', id: 'wgtlstx3u7' }, ext: '.zip', diff --git a/packages/desktop/src/renderer/components/Channel/File/FileComponent/FileComponent.test.tsx b/packages/desktop/src/renderer/components/Channel/File/FileComponent/FileComponent.test.tsx index caf60d53f0..68e075227b 100644 --- a/packages/desktop/src/renderer/components/Channel/File/FileComponent/FileComponent.test.tsx +++ b/packages/desktop/src/renderer/components/Channel/File/FileComponent/FileComponent.test.tsx @@ -14,7 +14,7 @@ describe('FileComponent', () => { media: { cid: 'QmWUCSApiy76nW9DAk5M9QbH1nkW5XCYwxUHRSULjATyqs', message: { - channelAddress: 'general', + channelId: 'general', id: 'wgtlstx3u7' }, ext: '.zip', diff --git a/packages/desktop/src/renderer/components/Channel/File/UploadedImage/UploadedImage.test.tsx b/packages/desktop/src/renderer/components/Channel/File/UploadedImage/UploadedImage.test.tsx index 2f3376c1d7..d72920b4db 100644 --- a/packages/desktop/src/renderer/components/Channel/File/UploadedImage/UploadedImage.test.tsx +++ b/packages/desktop/src/renderer/components/Channel/File/UploadedImage/UploadedImage.test.tsx @@ -30,7 +30,7 @@ describe('UploadedFile', () => { height: 600, message: { id: 'string', - channelAddress: 'general' + channelId: 'general' } } } @@ -112,7 +112,7 @@ describe('UploadedFile', () => { message.media.path = 'path/to/file/test.png' message.media.message = { id: 'string', - channelAddress: 'general' + channelId: 'general' } const result = renderComponent( diff --git a/packages/desktop/src/renderer/components/SearchModal/ChannelItem.tsx b/packages/desktop/src/renderer/components/SearchModal/ChannelItem.tsx index 1ba138328d..9c582bb314 100644 --- a/packages/desktop/src/renderer/components/SearchModal/ChannelItem.tsx +++ b/packages/desktop/src/renderer/components/SearchModal/ChannelItem.tsx @@ -19,7 +19,7 @@ const ChannelItem = ({ useEnterPress(() => { if (focused) { - onClickHandler(item.address) + onClickHandler(item.id) } }, [focused, channelInput]) @@ -31,7 +31,7 @@ const ChannelItem = ({ })} tabIndex={0} onClick={() => { - onClickHandler(item.address) + onClickHandler(item.id) }}> {`# ${item.name}`} diff --git a/packages/desktop/src/renderer/components/SearchModal/SearchModal.stories.tsx b/packages/desktop/src/renderer/components/SearchModal/SearchModal.stories.tsx index f1e97d0ff5..0da2cfce87 100644 --- a/packages/desktop/src/renderer/components/SearchModal/SearchModal.stories.tsx +++ b/packages/desktop/src/renderer/components/SearchModal/SearchModal.stories.tsx @@ -12,15 +12,15 @@ export const Component = Template.bind({}) const args = { open: true, dynamicSearchedChannelsSelector: [ - { name: 'fun', address: 'fun' }, - { name: 'mobile', address: 'mobile' }, - { name: 'new-york-plans', address: 'new-york-plans' } + { name: 'fun', id: 'fun' }, + { name: 'mobile', id: 'mobile' }, + { name: 'new-york-plans', id: 'new-york-plans' } ], publicChannelsSelector: [ - { name: 'fun', address: 'fun' }, - { name: 'mobile', address: 'mobile' }, - { name: 'new-york-plans', address: 'new-york-plans' }, - { name: 'general', address: 'general' } + { name: 'fun', id: 'fun' }, + { name: 'mobile', id: 'mobile' }, + { name: 'new-york-plans', id: 'new-york-plans' }, + { name: 'general', id: 'general' } ], unreadChannelsSelector: [], channelInput: '' diff --git a/packages/desktop/src/renderer/components/SearchModal/SearchModal.test.tsx b/packages/desktop/src/renderer/components/SearchModal/SearchModal.test.tsx index 6feb244d58..f8307a0204 100644 --- a/packages/desktop/src/renderer/components/SearchModal/SearchModal.test.tsx +++ b/packages/desktop/src/renderer/components/SearchModal/SearchModal.test.tsx @@ -5,6 +5,7 @@ import { prepareStore } from '../../testUtils/prepareStore' import { renderComponent } from '../../testUtils/renderComponent' import { getFactory, publicChannels, communities, identity } from '@quiet/state-manager' import SearchModalComponent from './SearchModelComponent' +import { generateChannelId } from '@quiet/common' describe('Search Modal', () => { let socket: MockedSocket @@ -46,7 +47,7 @@ describe('Search Modal', () => { description: `Welcome to #${channelMock.name}`, timestamp: channelMock.timestamp, owner: alice.nickname, - address: channelMock.name + id: generateChannelId(channelMock.name) } } ) @@ -59,8 +60,8 @@ describe('Search Modal', () => { const result = renderComponent( { const publicChannelsSelector = useSelector(publicChannels.selectors.publicChannels) const setCurrentChannel = useCallback( - (address: string) => { + (id: string) => { dispatch( publicChannels.actions.setCurrentChannel({ - channelAddress: address + channelId: id }) ) searchChannelModal.handleClose() diff --git a/packages/desktop/src/renderer/components/SearchModal/SearchModelComponent.tsx b/packages/desktop/src/renderer/components/SearchModal/SearchModelComponent.tsx index 7ebdeea3ef..4bb0e457fa 100644 --- a/packages/desktop/src/renderer/components/SearchModal/SearchModelComponent.tsx +++ b/packages/desktop/src/renderer/components/SearchModal/SearchModelComponent.tsx @@ -165,10 +165,10 @@ const SearchModalComponent: React.FC = ({ const channelList = unread && channelInput.length === 0 ? unreadChannels : dynamicSearchedChannelsSelector - const onChannelClickHandler = (address: string) => { + const onChannelClickHandler = (id: string) => { setChannelInput('') setCurrentFocus(0) - setCurrentChannel(address) + setCurrentChannel(id) } const [focusedIndex, setCurrentFocus] = useCyclingFocus( diff --git a/packages/desktop/src/renderer/components/Sidebar/ChannelsPanel/ChannelsListItem.tsx b/packages/desktop/src/renderer/components/Sidebar/ChannelsPanel/ChannelsListItem.tsx index 491aad5af8..47a892968a 100644 --- a/packages/desktop/src/renderer/components/Sidebar/ChannelsPanel/ChannelsListItem.tsx +++ b/packages/desktop/src/renderer/components/Sidebar/ChannelsPanel/ChannelsListItem.tsx @@ -99,7 +99,7 @@ export const ChannelsListItem: React.FC = ({ ref={ref} disableGutters onClick={() => { - setCurrentChannel(channel.name) + setCurrentChannel(channel.id) }} className={classNames(classes.root, { [classes.selected]: selected, diff --git a/packages/desktop/src/renderer/components/Sidebar/ChannelsPanel/ChannelsPanel.test.tsx b/packages/desktop/src/renderer/components/Sidebar/ChannelsPanel/ChannelsPanel.test.tsx index 8b9905cd82..4f06db7684 100644 --- a/packages/desktop/src/renderer/components/Sidebar/ChannelsPanel/ChannelsPanel.test.tsx +++ b/packages/desktop/src/renderer/components/Sidebar/ChannelsPanel/ChannelsPanel.test.tsx @@ -6,6 +6,7 @@ import { renderComponent } from '../../../testUtils/renderComponent' import { getFactory, publicChannels, communities, identity } from '@quiet/state-manager' import ChannelsPanel from './ChannelsPanel' import { DateTime } from 'luxon' +import { generateChannelId } from '@quiet/common' describe('Channels panel', () => { let socket: MockedSocket @@ -26,7 +27,7 @@ describe('Channels panel', () => { const community = await factory.create< ReturnType['payload'] >('Community') - + const generalChannel = publicChannels.selectors.generalChannel(store.getState()) const alice = await factory.create< ReturnType['payload'] >('Identity', { id: community.id, nickname: 'alice' }) @@ -43,7 +44,7 @@ describe('Channels panel', () => { description: `Welcome to #${name}`, timestamp: DateTime.utc().valueOf(), owner: alice.nickname, - address: name + id: generateChannelId(name) } } ) @@ -55,8 +56,8 @@ describe('Channels panel', () => { void - currentChannel: string + setCurrentChannel: (id: string) => void + currentChannelId: string createChannelModal: ReturnType } @@ -18,7 +18,7 @@ const ChannelsPanel: React.FC = ({ channels, unreadChannels, setCurrentChannel, - currentChannel, + currentChannelId, createChannelModal }) => { return ( @@ -34,15 +34,15 @@ const ChannelsPanel: React.FC = ({ {channels.map((channel, index) => { - const unread = unreadChannels.some(address => address === channel.address) - const selected = currentChannel === channel.address + const unread = unreadChannels.some(id => id === channel.id) + const selected = currentChannelId === channel.id return ( ) diff --git a/packages/desktop/src/renderer/components/Sidebar/Sidebar.stories.tsx b/packages/desktop/src/renderer/components/Sidebar/Sidebar.stories.tsx index 08e06fcd2e..90fc13a1e9 100644 --- a/packages/desktop/src/renderer/components/Sidebar/Sidebar.stories.tsx +++ b/packages/desktop/src/renderer/components/Sidebar/Sidebar.stories.tsx @@ -25,7 +25,7 @@ const Template: ComponentStory = args => { position: 'relative' }}> - + @@ -45,22 +45,22 @@ const args: IdentityPanelProps & ChannelsPanelProps = { channels: [ // @ts-expect-error { - address: 'general', + id: 'general', name: 'general' }, // @ts-expect-error { - address: 'spooky', + id: 'spooky', name: 'spooky' }, // @ts-expect-error { - address: 'kalkan', + id: 'kalkan', name: 'kalkan' } ], unreadChannels: ['spooky'], - setCurrentChannel: function (_address: string): void {}, + setCurrentChannel: function (_id: string): void {}, currentChannel: 'general', createChannelModal: { open: false, diff --git a/packages/desktop/src/renderer/components/Sidebar/Sidebar.tsx b/packages/desktop/src/renderer/components/Sidebar/Sidebar.tsx index f97251f980..493b7cda88 100644 --- a/packages/desktop/src/renderer/components/Sidebar/Sidebar.tsx +++ b/packages/desktop/src/renderer/components/Sidebar/Sidebar.tsx @@ -17,16 +17,16 @@ const Sidebar = () => { const currentCommunity = useSelector(communities.selectors.currentCommunity) - const currentChannel = useSelector(publicChannels.selectors.currentChannelAddress) + const currentChannelId = useSelector(publicChannels.selectors.currentChannelId) // Workaround for Redux bug, issue: https://github.com/TryQuiet/quiet/issues/1332 useSelector(publicChannels.selectors.sortedChannels) const publicChannelsSelector = useSelector(publicChannels.selectors.publicChannels) - const setCurrentChannel = (address: string) => { + const setCurrentChannel = (id: string) => { dispatch( publicChannels.actions.setCurrentChannel({ - channelAddress: address + channelId: id }) ) } @@ -40,7 +40,7 @@ const Sidebar = () => { channels: publicChannelsSelector, unreadChannels: unreadChannels, setCurrentChannel: setCurrentChannel, - currentChannel: currentChannel, + currentChannelId: currentChannelId, createChannelModal: createChannelModal } diff --git a/packages/desktop/src/renderer/components/widgets/channels/ChannelInput/ChannelInput.stories.tsx b/packages/desktop/src/renderer/components/widgets/channels/ChannelInput/ChannelInput.stories.tsx index 055cbc6346..9dbd3027e3 100644 --- a/packages/desktop/src/renderer/components/widgets/channels/ChannelInput/ChannelInput.stories.tsx +++ b/packages/desktop/src/renderer/components/widgets/channels/ChannelInput/ChannelInput.stories.tsx @@ -18,7 +18,7 @@ export const Component = Template.bind({}) export const Disabled = Template.bind({}) const args: ChannelInputProps = { - channelAddress: 'channelAddress', + channelId: 'channelId', channelParticipants: [{ nickname: 'john' }, { nickname: 'emily' }], inputPlaceholder: '#general as @alice', onChange: function (_arg: string): void {}, @@ -32,7 +32,7 @@ const args: ChannelInputProps = { } const argsDisabledInput: ChannelInputProps = { - channelAddress: 'channelAddress', + channelId: 'channelId', channelParticipants: [{ nickname: 'john' }, { nickname: 'emily' }], inputPlaceholder: '#general as @alice', onChange: function (_arg: string): void {}, diff --git a/packages/desktop/src/renderer/components/widgets/channels/ChannelInput/ChannelInput.test.tsx b/packages/desktop/src/renderer/components/widgets/channels/ChannelInput/ChannelInput.test.tsx index ccc67d418c..ead1abc4d0 100644 --- a/packages/desktop/src/renderer/components/widgets/channels/ChannelInput/ChannelInput.test.tsx +++ b/packages/desktop/src/renderer/components/widgets/channels/ChannelInput/ChannelInput.test.tsx @@ -11,7 +11,7 @@ describe('ChannelInput', () => { it('renders component input available ', () => { const result = renderComponent( { it('renders component input unavailable', () => { const result = renderComponent( { renderComponent( inputPlaceholder: string @@ -221,7 +221,7 @@ export interface ChannelInputProps { } export const ChannelInputComponent: React.FC = ({ - channelAddress, + channelId, channelParticipants = [], inputPlaceholder, inputState = INPUT_STATE.AVAILABLE, @@ -263,7 +263,7 @@ export const ChannelInputComponent: React.FC = ({ React.useEffect(() => { inputRef.current.updater.enqueueForceUpdate(inputRef.current) - }, [inputPlaceholder, channelAddress]) + }, [inputPlaceholder, channelId]) // Use reference to bypass memorization React.useEffect(() => { @@ -295,7 +295,7 @@ export const ChannelInputComponent: React.FC = ({ } } isFirstRenderRef.current = false - }, [channelAddress]) + }, [channelId]) React.useEffect(() => { messageRef.current = message diff --git a/packages/desktop/src/renderer/components/widgets/channels/NestedMessageContent.test.tsx b/packages/desktop/src/renderer/components/widgets/channels/NestedMessageContent.test.tsx index 20228e34d4..431c7dfcbe 100644 --- a/packages/desktop/src/renderer/components/widgets/channels/NestedMessageContent.test.tsx +++ b/packages/desktop/src/renderer/components/widgets/channels/NestedMessageContent.test.tsx @@ -75,7 +75,7 @@ describe('NestedMessageContent', () => { size: AUTODOWNLOAD_SIZE_LIMIT - 2048, message: { id: 'string', - channelAddress: 'general' + channelId: 'general' } } } @@ -138,7 +138,7 @@ describe('NestedMessageContent', () => { size: AUTODOWNLOAD_SIZE_LIMIT - 2048, message: { id: 'string', - channelAddress: 'general' + channelId: 'general' } } } @@ -200,7 +200,7 @@ describe('NestedMessageContent', () => { size: AUTODOWNLOAD_SIZE_LIMIT + 2048, message: { id: 'string', - channelAddress: 'general' + channelId: 'general' } } } diff --git a/packages/desktop/src/renderer/containers/widgets/channels/DirectMessageInput.tsx b/packages/desktop/src/renderer/containers/widgets/channels/DirectMessageInput.tsx index 1dbef7b284..706f06e3bc 100644 --- a/packages/desktop/src/renderer/containers/widgets/channels/DirectMessageInput.tsx +++ b/packages/desktop/src/renderer/containers/widgets/channels/DirectMessageInput.tsx @@ -31,13 +31,13 @@ export const ChannelInput = () => { const { onChange, onEnter, resetDebounce } = useDirectMessageInputActions() - const currentChannelAddress = useSelector(publicChannels.selectors.currentChannelAddress) + const currentChannelId = useSelector(publicChannels.selectors.currentChannelId) const currentChannelName = useSelector(publicChannels.selectors.currentChannelName) const user = useSelector(identity.selectors.currentIdentity) return ( { -// return conv.conversationId === action.payload.channelAddress +// return conv.conversationId === action.payload.channelId // }) // const contact = conversation[0] @@ -70,7 +70,7 @@ // ): Generator { // const conversations = yield* select(directMessagesSelectors.conversations) // const conversation = Array.from(Object.values(conversations)).filter(conv => { -// return conv.conversationId === action.payload.channelAddress +// return conv.conversationId === action.payload.channelId // }) // const contact = conversation[0] // const contactPublicKey = contact.contactPublicKey @@ -83,7 +83,7 @@ // const channel = yield* select(contactsSelectors.contact(user.nickname)) // if (!channel) { -// log.error(`Couldn't load all messages. No channel ${action.payload.channelAddress} in contacts`) +// log.error(`Couldn't load all messages. No channel ${action.payload.channelId} in contacts`) // return // } diff --git a/packages/desktop/src/renderer/sagas/notifications/notifications.click.test.ts b/packages/desktop/src/renderer/sagas/notifications/notifications.click.test.ts index 9252d455db..1521d231ed 100644 --- a/packages/desktop/src/renderer/sagas/notifications/notifications.click.test.ts +++ b/packages/desktop/src/renderer/sagas/notifications/notifications.click.test.ts @@ -4,12 +4,18 @@ import { ioMock } from '../../../shared/setupTests' import { prepareStore } from '../../testUtils/prepareStore' import { setupCrypto } from '@quiet/identity' import { call, fork } from 'typed-redux-saga' -import { publicChannels, NotificationsSounds, MessageType, FileMetadata } from '@quiet/state-manager' +import { + publicChannels, + NotificationsSounds, + MessageType, + FileMetadata +} from '@quiet/state-manager' import { createNotification, handleNotificationActions, NotificationData } from './notifications.saga' +import { generateChannelId } from '@quiet/common' const notification = jest.fn().mockImplementation(() => { return jest.fn() @@ -47,26 +53,29 @@ describe('clicking in notification', () => { const { store, runSaga } = await prepareStore({}, socket) - const channel = 'sailing' + const generalId = generateChannelId('general') + const sailingId = generateChannelId('sailing') const notificationData: NotificationData = { label: 'label', body: 'body', - channel: channel, + channel: sailingId, sound: NotificationsSounds.splat } + store.dispatch(publicChannels.actions.setCurrentChannel({ channelId: generalId })) + // Verify current channel is 'general - expect(publicChannels.selectors.currentChannelAddress(store.getState())).toBe('general') + expect(publicChannels.selectors.currentChannelId(store.getState())).toBe(generalId) runSaga(function* (): Generator { const notification = yield* call(createNotification, notificationData) - yield* fork(handleNotificationActions, notification, MessageType.Basic, channel) + yield* fork(handleNotificationActions, notification, MessageType.Basic, sailingId) yield* call(notification.onclick, new Event('')) }) // Confirm current channel address has changed - expect(publicChannels.selectors.currentChannelAddress(store.getState())).toBe(channel) + expect(publicChannels.selectors.currentChannelId(store.getState())).toBe(sailingId) }) it('opens file explorer', async () => { @@ -75,7 +84,7 @@ describe('clicking in notification', () => { const { runSaga } = await prepareStore({}, socket) - const channel = 'sailing' + const sailingId = generateChannelId('sailing') const media: FileMetadata = { cid: 'cid', @@ -84,14 +93,14 @@ describe('clicking in notification', () => { path: 'path/file.ext', message: { id: 'id', - channelAddress: channel + channelId: sailingId } } const notificationData: NotificationData = { label: 'label', body: 'body', - channel: channel, + channel: sailingId, sound: NotificationsSounds.splat } @@ -99,7 +108,7 @@ describe('clicking in notification', () => { runSaga(function* (): Generator { const notification = yield* call(createNotification, notificationData) - yield* fork(handleNotificationActions, notification, MessageType.File, channel, media) + yield* fork(handleNotificationActions, notification, MessageType.File, sailingId, media) yield* call(notification.onclick, new Event('')) }) diff --git a/packages/desktop/src/renderer/sagas/notifications/notifications.saga.ts b/packages/desktop/src/renderer/sagas/notifications/notifications.saga.ts index 15ea297068..cf960a43f4 100644 --- a/packages/desktop/src/renderer/sagas/notifications/notifications.saga.ts +++ b/packages/desktop/src/renderer/sagas/notifications/notifications.saga.ts @@ -35,7 +35,9 @@ export function* displayMessageNotificationSaga( ): Generator { const incomingMessages = action.payload.messages - const currentChannel = yield* select(publicChannels.selectors.currentChannel) + const currentChannelId = yield* select(publicChannels.selectors.currentChannelId) + const publicChannelsSelector = yield* select(publicChannels.selectors.publicChannels) + const currentIdentity = yield* select(identity.selectors.currentIdentity) const certificatesMapping = yield* select(users.selectors.certificatesMapping) @@ -48,9 +50,10 @@ export function* displayMessageNotificationSaga( for (const message of incomingMessages) { const focused = yield* call(isWindowFocused) + const channelName = publicChannelsSelector.find((channel) => channel.id === message.channelId).name // Do not display notifications for active channel (when the app is in foreground) - if (focused && message.channelAddress === currentChannel.address) return + if (focused && message.channelId === currentChannelId) return // Do not display notifications for own messages const sender = certificatesMapping[message.pubKey]?.username @@ -65,12 +68,12 @@ export function* displayMessageNotificationSaga( // Do not display when message is not verified if (!action.payload.isVerified) return - let label = `New message from @${sender} in #${message.channelAddress}` + let label = `New message from @${sender} in #${channelName}` let body = `${message.message.substring(0, 64)}${message.message.length > 64 ? '...' : ''}` // Change notification's label for the image if (message.type === MessageType.Image) { - label = `@${sender} sent an image in #${message.channelAddress}` + label = `@${sender} sent an image in #${channelName}` body = undefined } @@ -78,16 +81,16 @@ export function* displayMessageNotificationSaga( if (message.type === MessageType.File) { const status = downloadStatuses[message.id] - label = `@${sender} sends file in #${message.channelAddress}` + label = `@${sender} sends file in #${channelName}` body = undefined if (status?.downloadState === DownloadState.Completed) { - label = `@${sender} sent a file in #${message.channelAddress}` + label = `@${sender} sent a file in #${channelName}` body = 'Download complete. Click to show file in folder.' } } - const channel = message.channelAddress + const channel = message.channelId const type = message.type const media = message.media @@ -153,7 +156,7 @@ function subscribeNotificationEvents( const [browserWindow] = remote.BrowserWindow.getAllWindows() browserWindow.show() // Emit store action - emit(publicChannels.actions.setCurrentChannel({ channelAddress: channel })) + emit(publicChannels.actions.setCurrentChannel({ channelId: channel })) } } return () => {} diff --git a/packages/desktop/src/renderer/sagas/notifications/notifications.test.ts b/packages/desktop/src/renderer/sagas/notifications/notifications.test.ts index e1b2cf9589..2d626a3071 100644 --- a/packages/desktop/src/renderer/sagas/notifications/notifications.test.ts +++ b/packages/desktop/src/renderer/sagas/notifications/notifications.test.ts @@ -98,7 +98,8 @@ beforeAll(async () => { community = await factory.create< ReturnType['payload'] >('Community') - + const generalChannel = publicChannels.selectors.generalChannel(store.getState()) + store.dispatch(publicChannels.actions.setCurrentChannel({ channelId: generalChannel.id })) sailingChannel = ( await factory.create['payload']>( 'PublicChannel' @@ -127,7 +128,7 @@ beforeAll(async () => { type: MessageType.Basic, message: 'hello there!', createdAt: lastConnectedTime + 1, - channelAddress: sailingChannel.address, + channelId: sailingChannel.id, signature: '', pubKey: '' } @@ -142,7 +143,7 @@ beforeAll(async () => { type: MessageType.Basic, message: 'how are you?', createdAt: lastConnectedTime + 1, - channelAddress: sailingChannel.address, + channelId: sailingChannel.id, signature: '', pubKey: '' } @@ -182,14 +183,14 @@ describe('displayNotificationsSaga', () => { .withState(store.getState()) .provide([[call.fn(isWindowFocused), false]]) .call(createNotification, { - label: `New message from @${bob.nickname} in #${sailingChannel.address}`, + label: `New message from @${bob.nickname} in #${sailingChannel.name}`, body: message.message, - channel: sailingChannel.address, + channel: sailingChannel.id, sound: NotificationsSounds.pow }) .run() - expect(notification).toBeCalledWith(`New message from @${bob.nickname} in #${sailingChannel.address}`, { + expect(notification).toBeCalledWith(`New message from @${bob.nickname} in #${sailingChannel.name}`, { body: message.message, icon: '../../build/icon.png', silent: true @@ -254,7 +255,7 @@ describe('displayNotificationsSaga', () => { test('do not display notification when the user is on the active channel', async () => { store.dispatch( - publicChannels.actions.setCurrentChannel({ channelAddress: sailingChannel.address }) + publicChannels.actions.setCurrentChannel({ channelId: sailingChannel.id }) ) const reducer = combineReducers(reducers) @@ -276,7 +277,7 @@ describe('displayNotificationsSaga', () => { test('notification shows for message in current channel when app window does not have focus', async () => { store.dispatch( - publicChannels.actions.setCurrentChannel({ channelAddress: sailingChannel.address }) + publicChannels.actions.setCurrentChannel({ channelId: sailingChannel.id }) ) const reducer = combineReducers(reducers) @@ -291,14 +292,14 @@ describe('displayNotificationsSaga', () => { .withState(store.getState()) .provide([[call.fn(isWindowFocused), false]]) .call(createNotification, { - label: `New message from @${bob.nickname} in #${sailingChannel.address}`, + label: `New message from @${bob.nickname} in #${sailingChannel.name}`, body: message.message, - channel: sailingChannel.address, + channel: sailingChannel.id, sound: NotificationsSounds.librarianShhh }) .run() - expect(notification).toBeCalledWith(`New message from @${bob.nickname} in #${sailingChannel.address}`, { + expect(notification).toBeCalledWith(`New message from @${bob.nickname} in #${sailingChannel.name}`, { body: message.message, icon: '../../build/icon.png', silent: true @@ -307,7 +308,7 @@ describe('displayNotificationsSaga', () => { test('notification shows for message in non-active channel when app window has focus', async () => { store.dispatch( - publicChannels.actions.setCurrentChannel({ channelAddress: 'general' }) + publicChannels.actions.setCurrentChannel({ channelId: 'general' }) ) const reducer = combineReducers(reducers) @@ -322,14 +323,14 @@ describe('displayNotificationsSaga', () => { .withState(store.getState()) .provide([[call.fn(isWindowFocused), true]]) .call(createNotification, { - label: `New message from @${bob.nickname} in #${sailingChannel.address}`, + label: `New message from @${bob.nickname} in #${sailingChannel.name}`, body: message.message, - channel: sailingChannel.address, + channel: sailingChannel.id, sound: NotificationsSounds.librarianShhh }) .run() - expect(notification).toBeCalledWith(`New message from @${bob.nickname} in #${sailingChannel.address}`, { + expect(notification).toBeCalledWith(`New message from @${bob.nickname} in #${sailingChannel.name}`, { body: message.message, icon: '../../build/icon.png', silent: true @@ -414,9 +415,9 @@ describe('displayNotificationsSaga', () => { .withState(store.getState()) .provide([[call.fn(isWindowFocused), false]]) .call(createNotification, { - label: `New message from @${bob.nickname} in #${sailingChannel.address}`, + label: `New message from @${bob.nickname} in #${sailingChannel.name}`, body: message.message, - channel: sailingChannel.address, + channel: sailingChannel.id, sound: NotificationsSounds.none }) .run() @@ -462,7 +463,7 @@ describe('displayNotificationsSaga', () => { ext: '.png', message: { id: message.id, - channelAddress: message.channelAddress + channelId: message.channelId } } } @@ -476,14 +477,14 @@ describe('displayNotificationsSaga', () => { .withState(store.getState()) .provide([[call.fn(isWindowFocused), false]]) .call(createNotification, { - label: `@${bob.nickname} sent an image in #${sailingChannel.address}`, + label: `@${bob.nickname} sent an image in #${sailingChannel.name}`, body: undefined, - channel: sailingChannel.address, + channel: sailingChannel.id, sound: NotificationsSounds.librarianShhh }) .run() - expect(notification).toBeCalledWith(`@${bob.nickname} sent an image in #${sailingChannel.address}`, { + expect(notification).toBeCalledWith(`@${bob.nickname} sent an image in #${sailingChannel.name}`, { body: undefined, icon: '../../build/icon.png', silent: true @@ -503,7 +504,7 @@ describe('displayNotificationsSaga', () => { ext: '.ext', message: { id: message.id, - channelAddress: message.channelAddress + channelId: message.channelId } } } @@ -517,14 +518,14 @@ describe('displayNotificationsSaga', () => { .withState(store.getState()) .provide([[call.fn(isWindowFocused), false]]) .call(createNotification, { - label: `@${bob.nickname} sends file in #${sailingChannel.address}`, + label: `@${bob.nickname} sends file in #${sailingChannel.name}`, body: undefined, - channel: sailingChannel.address, + channel: sailingChannel.id, sound: NotificationsSounds.librarianShhh }) .run() - expect(notification).toBeCalledWith(`@${bob.nickname} sends file in #${sailingChannel.address}`, { + expect(notification).toBeCalledWith(`@${bob.nickname} sends file in #${sailingChannel.name}`, { body: undefined, icon: '../../build/icon.png', silent: true diff --git a/packages/desktop/src/rtl-tests/app.restart.test.tsx b/packages/desktop/src/rtl-tests/app.restart.test.tsx index e70a99c405..28ee718514 100644 --- a/packages/desktop/src/rtl-tests/app.restart.test.tsx +++ b/packages/desktop/src/rtl-tests/app.restart.test.tsx @@ -72,12 +72,14 @@ describe('Restart app works correctly', () => { await act(async () => { store.dispatch(network.actions.addInitializedCommunity(community.id)) - store.dispatch(publicChannels.actions.createGeneralChannel()) - const general = store.getState().PublicChannels.channels.entities['general'] + + const entities = store.getState().PublicChannels.channels.entities + + const generalId = Object.keys(entities).find(key => entities[key].name === 'general') store.dispatch( publicChannels.actions.sendInitialChannelMessage({ - channelAddress: general.address, - channelName: general.name + channelId: generalId, + channelName: 'general' }) ) }) diff --git a/packages/desktop/src/rtl-tests/channel.add.test.tsx b/packages/desktop/src/rtl-tests/channel.add.test.tsx index b59c860ab9..d62f93f583 100644 --- a/packages/desktop/src/rtl-tests/channel.add.test.tsx +++ b/packages/desktop/src/rtl-tests/channel.add.test.tsx @@ -106,7 +106,7 @@ describe('Add new channel', () => { if (action === SocketActionTypes.SEND_MESSAGE) { const data = input as socketEventData<[SendMessagePayload]> const { message } = data[0] - expect(message.channelAddress).toEqual(channelName.output) + expect(message.channelId).toEqual(channelName.output) expect(message.message).toEqual(`Created #${channelName.output}`) return socket.socketClient.emit(SocketActionTypes.INCOMING_MESSAGES, { messages: [message], @@ -315,7 +315,7 @@ describe('Add new channel', () => { if (action === SocketActionTypes.SEND_MESSAGE) { const data = input as socketEventData<[SendMessagePayload]> const { message } = data[0] - expect(message.channelAddress).toEqual(channelName) + expect(message.channelId).toEqual(channelName) expect(message.message).toEqual(`Created #${channelName}`) return socket.socketClient.emit(SocketActionTypes.INCOMING_MESSAGES, { messages: [message], diff --git a/packages/desktop/src/rtl-tests/channel.test.tsx b/packages/desktop/src/rtl-tests/channel.main.test.tsx similarity index 92% rename from packages/desktop/src/rtl-tests/channel.test.tsx rename to packages/desktop/src/rtl-tests/channel.main.test.tsx index 5f408f164b..b49ea8d83a 100644 --- a/packages/desktop/src/rtl-tests/channel.test.tsx +++ b/packages/desktop/src/rtl-tests/channel.main.test.tsx @@ -33,7 +33,8 @@ import { SendMessagePayload, MessageVerificationStatus, network, - connection + connection, + generateMessageFactoryContentWithId } from '@quiet/state-manager' import { keyFromCertificate, parseCertificate } from '@quiet/identity' @@ -151,6 +152,14 @@ describe('Channel', () => { ReturnType['payload'] >('Community') + const entities = store.getState().PublicChannels.channels.entities + + const generalId = Object.keys(entities).find(key => entities[key].name === 'general') + + await act(async () => { + store.dispatch(publicChannels.actions.setCurrentChannel({ channelId: generalId })) + }) + const alice = await factory.create< ReturnType['payload'] >('Identity', { id: community.id, nickname: 'alice' }) @@ -164,20 +173,35 @@ describe('Channel', () => { const authenticMessage: ChannelMessage = { ...( await factory.build('Message', { - identity: alice + identity: alice, + message: { + id: Math.random().toString(36).substr(2.9), + type: MessageType.Basic, + message: 'authenticMessage', + createdAt: DateTime.utc().valueOf(), + channelId: generalId, + signature: '', + pubKey: '' + } }) - ).payload.message, - id: Math.random().toString(36).substr(2.9) + ).payload.message } const spoofedMessage: ChannelMessage = { ...( await factory.build('Message', { - identity: alice + identity: alice, + message: { + id: Math.random().toString(36).substr(2.9), + type: MessageType.Basic, + message: 'spoofedMessage', + createdAt: DateTime.utc().valueOf(), + channelId: generalId, + signature: '', + pubKey: johnPublicKey + } }) - ).payload.message, - id: Math.random().toString(36).substr(2.9), - pubKey: johnPublicKey + ).payload.message } window.HTMLElement.prototype.scrollTo = jest.fn() @@ -232,13 +256,26 @@ describe('Channel', () => { ReturnType['payload'] >('Community') + const entities = store.getState().PublicChannels.channels.entities + + const generalId = Object.keys(entities).find(key => entities[key].name === 'general') + const alice = await factory.create< ReturnType['payload'] >('Identity', { id: community.id, nickname: 'alice' }) const aliceMessage = ( await factory.build('Message', { - identity: alice + identity: alice, + message: { + id: Math.random().toString(36).substr(2.9), + type: MessageType.Basic, + message: 'message', + createdAt: DateTime.utc().valueOf(), + channelId: generalId, + signature: '', + pubKey: '' + } }) ).payload.message @@ -311,6 +348,10 @@ describe('Channel', () => { ReturnType['payload'] >('Community') + const entities = store.getState().PublicChannels.channels.entities + + const generalId = Object.keys(entities).find(key => entities[key].name === 'general') + const alice = await factory.create< ReturnType['payload'] >('Identity', { id: community.id, nickname: 'alice' }) @@ -319,6 +360,7 @@ describe('Channel', () => { 'Message', { identity: alice, + message: generateMessageFactoryContentWithId(generalId), verifyAutomatically: true } ) @@ -436,6 +478,10 @@ describe('Channel', () => { ReturnType['payload'] >('Community') + const entities = store.getState().PublicChannels.channels.entities + + const generalId = Object.keys(entities).find(key => entities[key].name === 'general') + const alice = await factory.create< ReturnType['payload'] >('Identity', { id: community.id, nickname: 'alice' }) @@ -461,7 +507,7 @@ describe('Channel', () => { type: MessageType.Basic, message: msg, createdAt: messagesText.indexOf(msg) + 1, - channelAddress: 'general', + channelId: generalId, signature: '', pubKey: '' } @@ -569,7 +615,7 @@ describe('Channel', () => { it('renders a multi-line message', async () => { renderComponent( { it.skip('traverses a multi-line message it with arrow keys', async () => { renderComponent( { ReturnType['payload'] >('Community') + const entities = initialState.getState().PublicChannels.channels.entities + + const generalId = Object.keys(entities).find(key => entities[key].name === 'general') + const alice = await factory.create< ReturnType['payload'] >('Identity', { id: community.id, nickname: 'alice' }) @@ -747,6 +797,7 @@ describe('Channel', () => { if (action === SocketActionTypes.SEND_MESSAGE) { const data = input as socketEventData<[SendMessagePayload]> const payload = data[0] + console.log({ payload }) return socket.socketClient.emit(SocketActionTypes.INCOMING_MESSAGES, { messages: [payload.message] }) @@ -838,7 +889,10 @@ describe('Channel', () => { >('Identity', { id: community.id, nickname: 'alice' }) const message = Math.random().toString(36).substr(2.9) - const channelAddress = 'general' + + const entities = initialState.getState().PublicChannels.channels.entities + + const generalId = Object.keys(entities).find(key => entities[key].name === 'general') const missingFile: FileMetadata = { cid: Math.random().toString(36).substr(2.9), @@ -847,7 +901,7 @@ describe('Channel', () => { ext: '.jpeg', message: { id: message, - channelAddress: channelAddress + channelId: generalId }, size: AUTODOWNLOAD_SIZE_LIMIT - 2048 } @@ -861,7 +915,7 @@ describe('Channel', () => { type: MessageType.Image, message: '', createdAt: DateTime.utc().valueOf(), - channelAddress: 'general', + channelId: generalId, signature: '', pubKey: '', media: missingFile @@ -1060,7 +1114,8 @@ describe('Channel', () => { >('Identity', { id: community.id, nickname: 'alice' }) const messageId = Math.random().toString(36).substr(2.9) - const channelAddress = 'general' + const entities = initialState.getState().PublicChannels.channels.entities + const generalId = Object.keys(entities).find(key => entities[key].name === 'general') const media: FileMetadata = { cid: Math.random().toString(36).substr(2.9), @@ -1070,7 +1125,7 @@ describe('Channel', () => { size: AUTODOWNLOAD_SIZE_LIMIT - 1024, message: { id: messageId, - channelAddress: channelAddress + channelId: generalId } } @@ -1082,7 +1137,7 @@ describe('Channel', () => { type: MessageType.File, message: '', createdAt: DateTime.utc().valueOf(), - channelAddress: channelAddress, + channelId: generalId, signature: '', pubKey: '', media: media @@ -1174,7 +1229,8 @@ describe('Channel', () => { >('Identity', { id: community.id, nickname: 'alice' }) const messageId = Math.random().toString(36).substr(2.9) - const channelAddress = 'general' + const entities = initialState.getState().PublicChannels.channels.entities + const generalId = Object.keys(entities).find(key => entities[key].name === 'general') const media: FileMetadata = { cid: Math.random().toString(36).substr(2.9), @@ -1184,7 +1240,7 @@ describe('Channel', () => { size: AUTODOWNLOAD_SIZE_LIMIT + 1024, message: { id: messageId, - channelAddress: channelAddress + channelId: generalId } } @@ -1196,7 +1252,7 @@ describe('Channel', () => { type: MessageType.File, message: '', createdAt: DateTime.utc().valueOf(), - channelAddress: channelAddress, + channelId: generalId, signature: '', pubKey: '', media: media @@ -1290,7 +1346,8 @@ describe('Channel', () => { >('Identity', { id: community.id, nickname: 'alice' }) const messageId = Math.random().toString(36).substr(2.9) - const channelAddress = 'general' + const entities = initialState.getState().PublicChannels.channels.entities + const generalId = Object.keys(entities).find(key => entities[key].name === 'general') const media: FileMetadata = { cid: Math.random().toString(36).substr(2.9), @@ -1300,7 +1357,7 @@ describe('Channel', () => { size: AUTODOWNLOAD_SIZE_LIMIT + 1024, message: { id: messageId, - channelAddress: channelAddress + channelId: generalId } } @@ -1312,7 +1369,7 @@ describe('Channel', () => { type: MessageType.File, message: '', createdAt: DateTime.utc().valueOf(), - channelAddress: channelAddress, + channelId: generalId, signature: '', pubKey: '', media: media diff --git a/packages/desktop/src/rtl-tests/channel.switch.test.tsx b/packages/desktop/src/rtl-tests/channel.switch.test.tsx index 428022dca3..4169c3c180 100644 --- a/packages/desktop/src/rtl-tests/channel.switch.test.tsx +++ b/packages/desktop/src/rtl-tests/channel.switch.test.tsx @@ -24,7 +24,8 @@ import { SocketActionTypes, MessageType, ChannelMessage, - messages + messages, + generateMessageFactoryContentWithId } from '@quiet/state-manager' import { FactoryGirl } from 'factory-girl' @@ -36,14 +37,15 @@ jest.setTimeout(20_000) jest.mock('electron', () => { return { ipcRenderer: { on: () => {}, send: jest.fn(), sendSync: jest.fn() }, - remote: - { + remote: { BrowserWindow: { getAllWindows: () => { - return [{ - show: jest.fn(), - isFocused: jest.fn() - }] + return [ + { + show: jest.fn(), + isFocused: jest.fn() + } + ] } } } @@ -61,6 +63,7 @@ describe('Switch channels', () => { let community: Community let alice: Identity + let generalId: string beforeEach(async () => { socket = new MockedSocket() @@ -75,16 +78,17 @@ describe('Switch channels', () => { factory = await getFactory(redux.store) community = await factory.create< - ReturnType['payload'] + ReturnType['payload'] >('Community') alice = await factory.create['payload']>( 'Identity', { id: community.id, nickname: 'alice' } ) + const entities = redux.store.getState().PublicChannels.channels.entities + generalId = Object.keys(entities).find(key => entities[key].name === 'general') const channelNames = ['memes', 'pets', 'travels'] - // Automatically create channels for (const name of channelNames) { await factory.create['payload']>( @@ -95,7 +99,7 @@ describe('Switch channels', () => { description: `Welcome to #${name}`, timestamp: DateTime.utc().valueOf(), owner: alice.nickname, - address: name + id: name } } ) @@ -104,8 +108,12 @@ describe('Switch channels', () => { it('Opens another channel', async () => { const generalChannelMessage = await factory.create< - ReturnType['payload'] - >('Message', { identity: alice, verifyAutomatically: true }) + ReturnType['payload'] + >('Message', { + identity: alice, + message: generateMessageFactoryContentWithId(generalId), + verifyAutomatically: true + }) window.HTMLElement.prototype.scrollTo = jest.fn() @@ -144,11 +152,11 @@ describe('Switch channels', () => { }) it('Highlights channel with unread messages and removes the highlight when entered', async () => { - const messagesAddresses = ['memes', 'pets'] + const messagesIds = ['memes', 'pets'] const messages: ChannelMessage[] = [] // Automatically create messages - for (const address of messagesAddresses) { + for (const id of messagesIds) { const message = ( await factory.build('Message', { identity: alice, @@ -157,7 +165,7 @@ describe('Switch channels', () => { type: MessageType.Basic, message: 'message', createdAt: DateTime.utc().valueOf(), - channelAddress: address, + channelId: id, signature: '', pubKey: '' }, @@ -175,9 +183,11 @@ describe('Switch channels', () => { ) // Set 'general' as active channel - store.dispatch(publicChannels.actions.setCurrentChannel({ - channelAddress: 'general' - })) + store.dispatch( + publicChannels.actions.setCurrentChannel({ + channelId: 'general' + }) + ) // Assert channel is not highglighted const memesChannelLink = screen.getByTestId('memes-link-text') @@ -220,7 +230,7 @@ describe('Switch channels', () => { type: MessageType.Basic, message: 'message', createdAt: DateTime.utc().valueOf(), - channelAddress: 'general', + channelId: 'general', signature: '', pubKey: '' }, @@ -270,7 +280,7 @@ describe('Switch channels', () => { type: MessageType.Basic, message: 'message', createdAt: DateTime.utc().valueOf(), - channelAddress: 'travels', + channelId: 'travels', signature: '', pubKey: '' }, @@ -289,9 +299,11 @@ describe('Switch channels', () => { ) // Set 'general' as active channel - store.dispatch(publicChannels.actions.setCurrentChannel({ - channelAddress: 'general' - })) + store.dispatch( + publicChannels.actions.setCurrentChannel({ + channelId: 'general' + }) + ) // Assert channel is not highglighted const travelsChannelLink = screen.getByTestId('travels-link-text') @@ -309,9 +321,13 @@ describe('Switch channels', () => { expect(travelsChannelLink).toHaveStyle('opacity: 0.7') // Verify replicated message in present in repository - expect(messages.selectors.validCurrentPublicChannelMessagesEntries(redux.store.getState())[0]).toStrictEqual(message) + expect( + messages.selectors.validCurrentPublicChannelMessagesEntries(redux.store.getState())[0] + ).toStrictEqual(message) // Verify replicated messages was placed in cache - expect(publicChannels.selectors.currentChannelMessages(redux.store.getState())[0]).toStrictEqual(message) + expect( + publicChannels.selectors.currentChannelMessages(redux.store.getState())[0] + ).toStrictEqual(message) // Confirm new message was properly cached and is visible expect(await screen.findByText(message.message)).toBeVisible() diff --git a/packages/desktop/src/rtl-tests/community.create.test.tsx b/packages/desktop/src/rtl-tests/community.create.test.tsx index 850a173cb3..fa6da6991a 100644 --- a/packages/desktop/src/rtl-tests/community.create.test.tsx +++ b/packages/desktop/src/rtl-tests/community.create.test.tsx @@ -18,16 +18,19 @@ import { Community, InitCommunityPayload, LaunchRegistrarPayload, + publicChannels, RegisterOwnerCertificatePayload, SocketActionTypes } from '@quiet/state-manager' import Channel from '../renderer/components/Channel/Channel' import LoadingPanel from '../renderer/components/LoadingPanel/LoadingPanel' +import { generateChannelId } from '@quiet/common' jest.setTimeout(20_000) describe('User', () => { let socket: MockedSocket + const generalId = generateChannelId('general') beforeEach(() => { socket = new MockedSocket() @@ -100,6 +103,7 @@ describe('User', () => { socket.socketClient.emit(SocketActionTypes.NEW_COMMUNITY, { id: payload.id }) + socket.socketClient.emit(SocketActionTypes.CHANNELS_REPLICATED, { communityId: payload.id, channels: { @@ -108,7 +112,7 @@ describe('User', () => { description: 'string', owner: 'owner', timestamp: 0, - address: 'general' + id: generalId } } }) @@ -159,7 +163,10 @@ describe('User', () => { await userEvent.click(createUsernameButton) // Wait for the actions that updates the store - await act(async () => {}) + await act(async () => { + // Little workaround + store.dispatch(publicChannels.actions.setCurrentChannel({ channelId: generalId })) + }) // Check if create/username modals are gone expect(createCommunityTitle).not.toBeVisible() @@ -200,6 +207,14 @@ describe('User', () => { "Messages/addPublicChannelsMessagesBase", "PublicChannels/clearUnreadChannel", "Modals/closeModal", + "Messages/lazyLoading", + "Messages/resetCurrentPublicChannelCache", + "Messages/resetCurrentPublicChannelCache", + "PublicChannels/setCurrentChannel", + "PublicChannels/clearUnreadChannel", + "Messages/lazyLoading", + "Messages/resetCurrentPublicChannelCache", + "Messages/resetCurrentPublicChannelCache", ] `) }) diff --git a/packages/desktop/src/rtl-tests/community.join.test.tsx b/packages/desktop/src/rtl-tests/community.join.test.tsx index 87a27623c5..07aa088cca 100644 --- a/packages/desktop/src/rtl-tests/community.join.test.tsx +++ b/packages/desktop/src/rtl-tests/community.join.test.tsx @@ -91,11 +91,12 @@ describe('User', () => { const payload = data[0] const user = identity.selectors.currentIdentity(store.getState()) // This community serves only as a mocked object for generating valid crytpo data (certificate, rootCA) - const communityHelper: ReturnType['payload'] = ( - await factory.build('Community', { - id: data[0] - }) - ).payload + const communityHelper: ReturnType['payload'] = + ( + await factory.build('Community', { + id: data[0] + }) + ).payload const certificateHelper = await createUserCertificateTestHelper( { nickname: user.nickname, @@ -132,7 +133,7 @@ describe('User', () => { description: 'string', owner: 'owner', timestamp: 0, - address: 'general' + id: 'general' } } }) @@ -210,6 +211,9 @@ describe('User', () => { "PublicChannels/addChannel", "Messages/addPublicChannelsMessagesBase", "Modals/closeModal", + "Messages/lazyLoading", + "Messages/resetCurrentPublicChannelCache", + "Messages/resetCurrentPublicChannelCache", ] `) }) diff --git a/packages/desktop/src/rtl-tests/loadingPanel.test.tsx b/packages/desktop/src/rtl-tests/loadingPanel.test.tsx index bc4766af28..672fdb0043 100644 --- a/packages/desktop/src/rtl-tests/loadingPanel.test.tsx +++ b/packages/desktop/src/rtl-tests/loadingPanel.test.tsx @@ -98,7 +98,7 @@ describe('Loading panel', () => { description: 'Welcome to #general', timestamp: DateTime.utc().valueOf(), owner: 'owner', - address: 'general' + id: 'general' } }) ).payload diff --git a/packages/desktop/src/rtl-tests/searchModal.test.tsx b/packages/desktop/src/rtl-tests/searchModal.test.tsx index 12f483340b..074ae84ea5 100644 --- a/packages/desktop/src/rtl-tests/searchModal.test.tsx +++ b/packages/desktop/src/rtl-tests/searchModal.test.tsx @@ -98,7 +98,7 @@ describe('Switch channels', () => { description: `Welcome to #${channelMock.name}`, timestamp: channelMock.timestamp, owner: alice.nickname, - address: channelMock.name + id: channelMock.name } } ) @@ -197,7 +197,7 @@ describe('Switch channels', () => { type: MessageType.Basic, message: 'message', createdAt: DateTime.utc().valueOf(), - channelAddress: 'fun', + channelId: 'fun', signature: '', pubKey: '' }, diff --git a/packages/e2e-tests/src/tests/twoClients.test.ts b/packages/e2e-tests/src/tests/twoClients.test.ts index a5d6be3269..74b72d1572 100644 --- a/packages/e2e-tests/src/tests/twoClients.test.ts +++ b/packages/e2e-tests/src/tests/twoClients.test.ts @@ -220,23 +220,6 @@ describe('Two Clients', () => { expect(channels.length).toEqual(2) }) - it('Channel deletion - Owner recreate general channel', async () => { - const isGeneralChannel = await generalChannel.messageInput.isDisplayed() - expect(isGeneralChannel).toBeTruthy() - await channelContextMenu.openMenu() - await channelContextMenu.openDeletionChannelModal() - await channelContextMenu.deleteChannel() - const channels = await sidebar.getChannelList() - expect(channels.length).toEqual(2) - }) - it('Channel deletion - User see information about recreation general channel', async () => { - await sidebar2.switchChannel('general') - await new Promise(resolve => setTimeout(() => resolve(), 2000)) - const messages = await generalChannel2.getUserMessages(ownerUsername) - const text = await messages[0].getText() - expect(text).toEqual(`@${ownerUsername} deleted all messages in #general`) - }) - it('Leave community', async () => { const settingsModal = await new Sidebar(guestApp.driver).openSettings() const isSettingsModal = await settingsModal.element.isDisplayed() @@ -250,6 +233,18 @@ describe('Two Clients', () => { await debugModal.close() }) } + // Delete general channel while guest is absent + it('Channel deletion - Owner recreate general channel', async () => { + await new Promise(resolve => setTimeout(() => resolve(), 2000)) + const isGeneralChannel = await generalChannel.messageInput.isDisplayed() + expect(isGeneralChannel).toBeTruthy() + await channelContextMenu.openMenu() + await channelContextMenu.openDeletionChannelModal() + await channelContextMenu.deleteChannel() + const channels = await sidebar.getChannelList() + expect(channels.length).toEqual(2) + }) + it('Leave community - Guest re-join to community successfully', async () => { const joinCommunityModal = new JoinCommunityModal(guestApp.driver) const isJoinCommunityModal = await joinCommunityModal.element.isDisplayed() @@ -264,6 +259,20 @@ describe('Two Clients', () => { await registerModal2.typeUsername(joiningUserUsername2) await registerModal2.submit() }) + + // Check correct channels replication + it('Channel deletion - User see information about recreation general channel and see correct amount of messages', async () => { + generalChannel2 = new Channel(guestApp.driver, 'general') + await generalChannel2.element.isDisplayed() + await new Promise(resolve => setTimeout(() => resolve(), 10000)) + const messages = await generalChannel2.getUserMessages(ownerUsername) + const text1 = await messages[0].getText() + const text2 = await messages[1].getText() + expect(messages.length).toEqual(2) + expect(text1).toEqual(`@${ownerUsername} deleted all messages in #general`) + expect(text2).toEqual(`@${joiningUserUsername2} has joined Testcommunity! 🎉`) + }) + it('Leave community - Guest sends a message', async () => { generalChannel2 = new Channel(guestApp.driver, 'general') await generalChannel2.element.isDisplayed() diff --git a/packages/integration-tests/src/integrationTests/appActions.ts b/packages/integration-tests/src/integrationTests/appActions.ts index 5cc68eca4f..5b5e4638c3 100644 --- a/packages/integration-tests/src/integrationTests/appActions.ts +++ b/packages/integration-tests/src/integrationTests/appActions.ts @@ -251,7 +251,7 @@ export async function sendMessage( if (channelName) { store.dispatch( publicChannels.actions.setCurrentChannel({ - channelAddress: channelName + channelId: channelName }) ) } diff --git a/packages/integration-tests/src/integrationTests/assertions.ts b/packages/integration-tests/src/integrationTests/assertions.ts index a7e180a17e..b10dfaa6b1 100644 --- a/packages/integration-tests/src/integrationTests/assertions.ts +++ b/packages/integration-tests/src/integrationTests/assertions.ts @@ -40,7 +40,7 @@ export async function assertReceivedChannelsAndSubscribe( store.dispatch( publicChannels.actions.setCurrentChannel({ - channelAddress: store.getState().PublicChannels.channels.ids[0] as string + channelId: store.getState().PublicChannels.channels.ids[0] as string }) ) diff --git a/packages/integration-tests/src/testUtils/actions.ts b/packages/integration-tests/src/testUtils/actions.ts index 440358d40b..641d028bf6 100644 --- a/packages/integration-tests/src/testUtils/actions.ts +++ b/packages/integration-tests/src/testUtils/actions.ts @@ -180,7 +180,7 @@ export const switchChannel = async ({ channelName, store }) => { const communityId = store.getState().Communities.communities.ids[0] store.dispatch( publicChannels.actions.setCurrentChannel({ - channelAddress: channelName + channelId: channelName }) ) } diff --git a/packages/integration-tests/src/testUtils/assertions.ts b/packages/integration-tests/src/testUtils/assertions.ts index 455a7949f5..e8d93aa841 100644 --- a/packages/integration-tests/src/testUtils/assertions.ts +++ b/packages/integration-tests/src/testUtils/assertions.ts @@ -35,7 +35,7 @@ export async function assertReceivedChannel( store.dispatch( publicChannels.actions.setCurrentChannel({ - channelAddress: store.getState().PublicChannels.channels.ids[0] as string + channelId: store.getState().PublicChannels.channels.ids[0] as string }) ) diff --git a/packages/mobile/android/app/src/main/java/com/quietmobile/Notification/NotificationHandler.kt b/packages/mobile/android/app/src/main/java/com/quietmobile/Notification/NotificationHandler.kt index 239cec55dd..9ca2109df2 100644 --- a/packages/mobile/android/app/src/main/java/com/quietmobile/Notification/NotificationHandler.kt +++ b/packages/mobile/android/app/src/main/java/com/quietmobile/Notification/NotificationHandler.kt @@ -35,7 +35,7 @@ class NotificationHandler(private val context: Context) { try { // Parse channel name - val _channel = _message.getString("channelAddress") + val _channel = _message.getString("channelId") channel = String.format("#%s", _channel) // Parse message content val _content = _message.getString("message") diff --git a/packages/mobile/e2e/starter.test.js b/packages/mobile/e2e/starter.test.js index 0f4baf688a..25544c03a3 100644 --- a/packages/mobile/e2e/starter.test.js +++ b/packages/mobile/e2e/starter.test.js @@ -72,7 +72,7 @@ describe('User', () => { test('sees channels list', async () => { await waitFor(element(by.id('channels_list'))) .toBeVisible() - .withTimeout(5000) + .withTimeout(10000) }) test('enters #general channel', async () => { diff --git a/packages/mobile/package.json b/packages/mobile/package.json index f5d8af38bf..5054ce3a75 100644 --- a/packages/mobile/package.json +++ b/packages/mobile/package.json @@ -25,6 +25,7 @@ "@quiet/backend": "^1.2.1-alpha.4", "@quiet/common": "^1.2.1-alpha.3", "@quiet/identity": "^1.2.1-alpha.2", + "@quiet/types": "^1.2.1-alpha.2", "@quiet/state-manager": "^1.2.1-alpha.3", "@react-native-clipboard/clipboard": "^1.11.2", "@react-navigation/native": "^6.0.0", diff --git a/packages/mobile/src/components/ChannelList/ChannelList.stories.tsx b/packages/mobile/src/components/ChannelList/ChannelList.stories.tsx index 4975f96c3f..60dd2a254e 100644 --- a/packages/mobile/src/components/ChannelList/ChannelList.stories.tsx +++ b/packages/mobile/src/components/ChannelList/ChannelList.stories.tsx @@ -12,39 +12,39 @@ storiesOf('ChannelList', module).add('Default', () => ( tiles={[ { name: 'general', - address: 'general', + id: 'general', message: 'Text from latest chat message. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Id massa venenatis id eget massa commodo posuere faucibus aliquam. At scelerisque nisi mauris facilisis.', date: '1:55pm', unread: false, - redirect: (address: string) => { console.log(`Clicked ${address}`) } + redirect: (id: string) => { console.log(`Clicked ${id}`) } }, { name: 'spam', - address: 'spam', + id: 'spam', message: 'Text from latest chat message. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Id massa venenatis id eget massa commodo posuere faucibus aliquam. At scelerisque nisi mauris facilisis.', date: '1:55pm', unread: false, - redirect: (address: string) => { console.log(`Clicked ${address}`) } + redirect: (id: string) => { console.log(`Clicked ${id}`) } }, { name: 'design', - address: 'design', + id: 'design', message: 'Text from latest chat message. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Id massa venenatis id eget massa commodo posuere faucibus aliquam. At scelerisque nisi mauris facilisis.', date: '6/1/22', unread: true, - redirect: (address: string) => { console.log(`Clicked ${address}`) } + redirect: (id: string) => { console.log(`Clicked ${id}`) } }, { name: 'qa', - address: 'qa', + id: 'qa', message: 'Text from latest chat message. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Id massa venenatis id eget massa commodo posuere faucibus aliquam. At scelerisque nisi mauris facilisis.', date: 'Yesterday', unread: false, - redirect: (address: string) => { console.log(`Clicked ${address}`) } + redirect: (id: string) => { console.log(`Clicked ${id}`) } } ]} /> diff --git a/packages/mobile/src/components/ChannelList/ChannelList.test.tsx b/packages/mobile/src/components/ChannelList/ChannelList.test.tsx index f4b98f5806..035f462668 100644 --- a/packages/mobile/src/components/ChannelList/ChannelList.test.tsx +++ b/packages/mobile/src/components/ChannelList/ChannelList.test.tsx @@ -14,7 +14,7 @@ describe('ChannelList component', () => { tiles={[ { name: 'general', - address: 'general', + id: 'general', message: 'Text from latest chat message. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Id massa venenatis id eget massa commodo posuere faucibus aliquam. At scelerisque nisi mauris facilisis.', date: '1:55pm', @@ -23,7 +23,7 @@ describe('ChannelList component', () => { }, { name: 'spam', - address: 'spam', + id: 'spam', message: 'Text from latest chat message. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Id massa venenatis id eget massa commodo posuere faucibus aliquam. At scelerisque nisi mauris facilisis.', date: '1:55pm', @@ -32,7 +32,7 @@ describe('ChannelList component', () => { }, { name: 'design', - address: 'design', + id: 'design', message: 'Text from latest chat message. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Id massa venenatis id eget massa commodo posuere faucibus aliquam. At scelerisque nisi mauris facilisis.', date: '6/1/22', @@ -41,7 +41,7 @@ describe('ChannelList component', () => { }, { name: 'qa', - address: 'design', + id: 'design', message: 'Text from latest chat message. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Id massa venenatis id eget massa commodo posuere faucibus aliquam. At scelerisque nisi mauris facilisis.', date: 'Yesterday', @@ -189,32 +189,32 @@ describe('ChannelList component', () => { data={ [ { - "address": "general", "date": "1:55pm", + "id": "general", "message": "Text from latest chat message. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Id massa venenatis id eget massa commodo posuere faucibus aliquam. At scelerisque nisi mauris facilisis.", "name": "general", "redirect": [MockFunction], "unread": false, }, { - "address": "spam", "date": "1:55pm", + "id": "spam", "message": "Text from latest chat message. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Id massa venenatis id eget massa commodo posuere faucibus aliquam. At scelerisque nisi mauris facilisis.", "name": "spam", "redirect": [MockFunction], "unread": false, }, { - "address": "design", "date": "6/1/22", + "id": "design", "message": "Text from latest chat message. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Id massa venenatis id eget massa commodo posuere faucibus aliquam. At scelerisque nisi mauris facilisis.", "name": "design", "redirect": [MockFunction], "unread": true, }, { - "address": "design", "date": "Yesterday", + "id": "design", "message": "Text from latest chat message. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Id massa venenatis id eget massa commodo posuere faucibus aliquam. At scelerisque nisi mauris facilisis.", "name": "qa", "redirect": [MockFunction], diff --git a/packages/mobile/src/components/ChannelTile/ChannelTile.component.tsx b/packages/mobile/src/components/ChannelTile/ChannelTile.component.tsx index efe5cd6a8d..da52ee7b83 100644 --- a/packages/mobile/src/components/ChannelTile/ChannelTile.component.tsx +++ b/packages/mobile/src/components/ChannelTile/ChannelTile.component.tsx @@ -8,7 +8,7 @@ import { ChannelTileProps } from './ChannelTile.types' export const ChannelTile: FC = ({ name, - address, + id, message, date, unread, @@ -45,7 +45,7 @@ export const ChannelTile: FC = ({ { - redirect(address) + redirect(id) }}> ( { console.log(`Clicked ${address}`) }} + redirect={(id: string) => { console.log(`Clicked ${id}`) }} /> )) .add('Unread', () => ( { console.log(`Clicked ${address}`) }} + redirect={(id: string) => { console.log(`Clicked ${id}`) }} /> )) diff --git a/packages/mobile/src/components/ChannelTile/ChannelTile.test.tsx b/packages/mobile/src/components/ChannelTile/ChannelTile.test.tsx index 050a32808d..463ac665ec 100644 --- a/packages/mobile/src/components/ChannelTile/ChannelTile.test.tsx +++ b/packages/mobile/src/components/ChannelTile/ChannelTile.test.tsx @@ -8,7 +8,7 @@ describe('ChannelList component', () => { const { toJSON } = renderComponent( { const { toJSON } = renderComponent( void + redirect: (id: string) => void } diff --git a/packages/mobile/src/components/Chat/Chat.stories.tsx b/packages/mobile/src/components/Chat/Chat.stories.tsx index 45bd027eeb..c29d66d50b 100644 --- a/packages/mobile/src/components/Chat/Chat.stories.tsx +++ b/packages/mobile/src/components/Chat/Chat.stories.tsx @@ -20,7 +20,7 @@ storiesOf('Chat', module) description: '', owner: '', timestamp: 0, - address: '' + id: '' }} messages={{ count: 16, @@ -203,7 +203,7 @@ storiesOf('Chat', module) description: '', owner: '', timestamp: 0, - address: '' + id: '' }} user={'holmes'} /> diff --git a/packages/mobile/src/components/Chat/Chat.test.tsx b/packages/mobile/src/components/Chat/Chat.test.tsx index e21b647e1f..38025c9d44 100644 --- a/packages/mobile/src/components/Chat/Chat.test.tsx +++ b/packages/mobile/src/components/Chat/Chat.test.tsx @@ -27,7 +27,7 @@ describe('Chat component', () => { description: '', owner: '', timestamp: 0, - address: '' + id: '' }} pendingMessages={{}} messages={{ diff --git a/packages/mobile/src/components/ContextMenu/menus/ChannelContextMenu.container.tsx b/packages/mobile/src/components/ContextMenu/menus/ChannelContextMenu.container.tsx index 8bff2a7d2b..d0dbe9df11 100644 --- a/packages/mobile/src/components/ContextMenu/menus/ChannelContextMenu.container.tsx +++ b/packages/mobile/src/components/ContextMenu/menus/ChannelContextMenu.container.tsx @@ -49,7 +49,8 @@ export const ChannelContextMenu: FC = () => { title: 'Delete channel', action: () => redirect(ScreenNames.DeleteChannelScreen, { - channel: channel?.name + channelName: channel?.name, + channelId: channel?.id }) } ] diff --git a/packages/mobile/src/route.params.ts b/packages/mobile/src/route.params.ts index cb9c481e03..d02982f96f 100644 --- a/packages/mobile/src/route.params.ts +++ b/packages/mobile/src/route.params.ts @@ -18,7 +18,8 @@ export type RootStackParamList = { [ScreenNames.ChannelScreen]: undefined [ScreenNames.CreateChannelScreen]: undefined [ScreenNames.DeleteChannelScreen]: { - channel: string + channelName: string + channelId: string } [ScreenNames.SuccessScreen]: { onPress: () => void diff --git a/packages/mobile/src/screens/Channel/Channel.screen.tsx b/packages/mobile/src/screens/Channel/Channel.screen.tsx index 342eab2b45..a4b7086d4d 100644 --- a/packages/mobile/src/screens/Channel/Channel.screen.tsx +++ b/packages/mobile/src/screens/Channel/Channel.screen.tsx @@ -27,7 +27,7 @@ export const ChannelScreen: FC = () => { ) dispatch( publicChannels.actions.setCurrentChannel({ - channelAddress: '' // Necessary for marking channels as unread on channel's list + channelId: '' // Necessary for marking channels as unread on channel's list }) ) return true @@ -88,7 +88,7 @@ export const ChannelScreen: FC = () => { useEffect(() => { dispatch(messages.actions.resetCurrentPublicChannelCache()) - }, [currentChannel?.address]) + }, [currentChannel?.id]) const [imagePreview, setImagePreview] = useState(null) diff --git a/packages/mobile/src/screens/ChannelList/ChannelList.screen.tsx b/packages/mobile/src/screens/ChannelList/ChannelList.screen.tsx index 56eac63fca..60ac10130d 100644 --- a/packages/mobile/src/screens/ChannelList/ChannelList.screen.tsx +++ b/packages/mobile/src/screens/ChannelList/ChannelList.screen.tsx @@ -17,10 +17,10 @@ export const ChannelListScreen: FC = () => { const dispatch = useDispatch() const redirect = useCallback( - (address: string) => { + (id: string) => { dispatch( publicChannels.actions.setCurrentChannel({ - channelAddress: address + channelId: id }) ) dispatch( @@ -33,9 +33,10 @@ export const ChannelListScreen: FC = () => { ) const community = useSelector(communities.selectors.currentCommunity) - const channels = useSelector(publicChannels.selectors.channelsStatusSorted) - const tiles = channels.map(status => { + const channelsStatusWithName = useSelector(publicChannels.selectors.channelsStatusWithName) + + const tiles = channelsStatusWithName.map(status => { const newestMessage = status.newestMessage const message = newestMessage?.message || '...' @@ -44,8 +45,8 @@ export const ChannelListScreen: FC = () => { : undefined const tile: ChannelTileProps = { - name: status.address, - address: status.address, + name: status.name, + id: status.id, message: message, date: date, unread: status.unread, diff --git a/packages/mobile/src/screens/CreateChannel/CreateChannel.screen.tsx b/packages/mobile/src/screens/CreateChannel/CreateChannel.screen.tsx index 9d94b1b3ec..ba20aff213 100644 --- a/packages/mobile/src/screens/CreateChannel/CreateChannel.screen.tsx +++ b/packages/mobile/src/screens/CreateChannel/CreateChannel.screen.tsx @@ -15,11 +15,16 @@ import { DateTime } from 'luxon' import { navigationSelectors } from '../../store/navigation/navigation.selectors' import { ScreenNames } from '../../const/ScreenNames.enum' import { navigationActions } from '../../store/navigation/navigation.slice' +import { generateChannelId } from '@quiet/common' +import { ChannelStructure } from '@quiet/types' export const CreateChannelScreen: FC = () => { const dispatch = useDispatch() - const [channel, setChannel] = useState(null) + const [channel, setChannel] = useState({ + channelId: null, + channelName: null + }) const [clearComponent, setClearComponent] = useState(false) // How to clear component without using screen's state? const user = useSelector(identity.selectors.currentIdentity) @@ -34,11 +39,11 @@ export const CreateChannelScreen: FC = () => { useEffect(() => { if ( currentScreen === ScreenNames.CreateChannelScreen && - channels.filter(_channel => _channel.name === channel).length > 0 + channels.filter(_channel => _channel.name === channel.channelName).length > 0 ) { dispatch( publicChannels.actions.setCurrentChannel({ - channelAddress: channel + channelId: channel.channelId }) ) setChannel(null) @@ -79,11 +84,11 @@ export const CreateChannelScreen: FC = () => { name: name, description: `Welcome to #${name}`, owner: user.nickname, - address: name, + id: generateChannelId(name), timestamp: DateTime.utc().valueOf() } - setChannel(name) + setChannel({ channelId: channel.id, channelName: channel.name }) dispatch( publicChannels.actions.createChannel({ diff --git a/packages/mobile/src/screens/DeleteChannel/DeleteChannel.screen.tsx b/packages/mobile/src/screens/DeleteChannel/DeleteChannel.screen.tsx index 0a8b6799ce..36033b785c 100644 --- a/packages/mobile/src/screens/DeleteChannel/DeleteChannel.screen.tsx +++ b/packages/mobile/src/screens/DeleteChannel/DeleteChannel.screen.tsx @@ -10,7 +10,7 @@ import { navigationSelectors } from '../../store/navigation/navigation.selectors export const DeleteChannelScreen: FC = ({ route }) => { const dispatch = useDispatch() - const { channel } = route.params + const { channelName, channelId } = route.params const channels = useSelector(publicChannels.selectors.publicChannels) @@ -19,7 +19,7 @@ export const DeleteChannelScreen: FC = ({ route }) => console.log({ channels }) useEffect(() => { - if (screen === ScreenNames.DeleteChannelScreen && !channels.find(c => c.name === channel)) { + if (screen === ScreenNames.DeleteChannelScreen && !channels.find(c => c.name === channelName)) { dispatch(navigationActions.replaceScreen({ screen: ScreenNames.ChannelListScreen })) } }, [dispatch, screen, channels]) @@ -27,10 +27,10 @@ export const DeleteChannelScreen: FC = ({ route }) => const deleteChannel = useCallback(() => { dispatch( publicChannels.actions.deleteChannel({ - channel: channel + channelId }) ) - }, [dispatch]) + }, [dispatch, channels, channelName]) const handleBackButton = useCallback(() => { dispatch( @@ -42,7 +42,7 @@ export const DeleteChannelScreen: FC = ({ route }) => return ( diff --git a/packages/mobile/src/store/nativeServices/events/nativeServicesCallbacks.ts b/packages/mobile/src/store/nativeServices/events/nativeServicesCallbacks.ts index 1e6b49c963..35604e884f 100644 --- a/packages/mobile/src/store/nativeServices/events/nativeServicesCallbacks.ts +++ b/packages/mobile/src/store/nativeServices/events/nativeServicesCallbacks.ts @@ -49,9 +49,9 @@ export const deviceEvents = () => { ), nativeEventEmitter?.addListener( NativeEventKeys.Notification, - (channelAddress: string) => { + (channelId: string) => { // Change data source in state-manager - emit(publicChannels.actions.setCurrentChannel({ channelAddress })) + emit(publicChannels.actions.setCurrentChannel({ channelId })) // Redirect to proper screen in the application emit(navigationActions.navigation({ screen: ScreenNames.ChannelScreen })) } diff --git a/packages/mobile/src/store/nativeServices/showNotification/showNotification.saga.test.ts b/packages/mobile/src/store/nativeServices/showNotification/showNotification.saga.test.ts index 3c6c36f9ab..b38388310f 100644 --- a/packages/mobile/src/store/nativeServices/showNotification/showNotification.saga.test.ts +++ b/packages/mobile/src/store/nativeServices/showNotification/showNotification.saga.test.ts @@ -19,9 +19,9 @@ describe('showNotificationSaga', () => { beforeAll(async () => { payload = { - channelAddress: 'channelAddress', + channelId: 'channelId', message: { - channelAddress: 'address', + channelId: 'address', createdAt: 0, id: 'id', message: 'message', diff --git a/packages/state-manager/src/constants.ts b/packages/state-manager/src/constants.ts index 01b42342a0..a8f9ac9130 100644 --- a/packages/state-manager/src/constants.ts +++ b/packages/state-manager/src/constants.ts @@ -1,5 +1,3 @@ -export const MAIN_CHANNEL = 'general' - export const AUTODOWNLOAD_SIZE_LIMIT = 20971520 // 20 MB export const PUSH_NOTIFICATION_CHANNEL = '_PUSH_NOTIFICATION_' diff --git a/packages/state-manager/src/index.ts b/packages/state-manager/src/index.ts index c3cbb7ad62..08b37cc094 100644 --- a/packages/state-manager/src/index.ts +++ b/packages/state-manager/src/index.ts @@ -63,7 +63,7 @@ export { StoreKeys } from './sagas/store.keys' export { prepareStore } from './utils/tests/prepareStore' export { useIO } from './sagas/socket/startConnection/startConnection.saga' -export { getFactory } from './utils/tests/factories' +export { getFactory, generateMessageFactoryContentWithId } from './utils/tests/factories' export * from './utils/tests/helpers' export { Community } from './sagas/communities/communities.slice' // TODO: remove after setting strict in 'desktop' and 'mobile' packages diff --git a/packages/state-manager/src/sagas/files/autoDownloadFiles/autoDownloadFiles.saga.test.ts b/packages/state-manager/src/sagas/files/autoDownloadFiles/autoDownloadFiles.saga.test.ts index 8da0dccef0..8ee1f37785 100644 --- a/packages/state-manager/src/sagas/files/autoDownloadFiles/autoDownloadFiles.saga.test.ts +++ b/packages/state-manager/src/sagas/files/autoDownloadFiles/autoDownloadFiles.saga.test.ts @@ -1,8 +1,6 @@ -import { - setupCrypto -} from '@quiet/identity' +import { setupCrypto } from '@quiet/identity' import { Store } from '../../store.types' -import { getFactory, publicChannels } from '../../..' +import { generateMessageFactoryContentWithId, getFactory, publicChannels } from '../../..' import { prepareStore, reducers } from '../../../utils/tests/prepareStore' import { combineReducers } from '@reduxjs/toolkit' import { expectSaga } from 'redux-saga-test-plan' @@ -15,7 +13,16 @@ import { autoDownloadFilesSaga } from './autoDownloadFiles.saga' import { publicChannelsActions } from '../../publicChannels/publicChannels.slice' import { DateTime } from 'luxon' import { AUTODOWNLOAD_SIZE_LIMIT } from '../../../constants' -import { Community, FileMetadata, Identity, MessageType, PublicChannel, SocketActionTypes } from '@quiet/types' +import { generateChannelId } from '@quiet/common' +import { publicChannelsSelectors } from '../../publicChannels/publicChannels.selectors' +import { + Community, + FileMetadata, + Identity, + MessageType, + PublicChannel, + SocketActionTypes +} from '@quiet/types' describe('downloadFileSaga', () => { let store: Store @@ -25,6 +32,7 @@ describe('downloadFileSaga', () => { let alice: Identity let sailingChannel: PublicChannel + let generalChannel: PublicChannel beforeAll(async () => { setupCrypto() @@ -34,26 +42,32 @@ describe('downloadFileSaga', () => { factory = await getFactory(store) community = await factory.create< - ReturnType['payload'] + ReturnType['payload'] >('Community') + const generalChannelState = publicChannelsSelectors.generalChannel(store.getState()) + if (generalChannelState) generalChannel = generalChannelState + expect(generalChannel).not.toBeUndefined() + alice = await factory.create['payload']>( 'Identity', { id: community.id, nickname: 'alice' } ) - sailingChannel = (await factory.create['payload']>( - 'PublicChannel', - { - channel: { - name: 'sailing', - description: 'Welcome to #sailing', - timestamp: DateTime.utc().valueOf(), - owner: alice.nickname, - address: 'sailing' + sailingChannel = ( + await factory.create['payload']>( + 'PublicChannel', + { + channel: { + name: 'sailing', + description: 'Welcome to #sailing', + timestamp: DateTime.utc().valueOf(), + owner: alice.nickname, + id: generateChannelId('sailing') + } } - } - )).channel + ) + ).channel }) test('auto download file of type image', async () => { @@ -61,9 +75,11 @@ describe('downloadFileSaga', () => { const id = Math.random().toString(36).substr(2.9) - store.dispatch(publicChannelsActions.setCurrentChannel({ - channelAddress: 'general' - })) + store.dispatch( + publicChannelsActions.setCurrentChannel({ + channelId: generalChannel.id + }) + ) const media: FileMetadata = { cid: 'cid', @@ -72,7 +88,7 @@ describe('downloadFileSaga', () => { ext: 'png', message: { id: id, - channelAddress: 'general' + channelId: generalChannel.id } } @@ -81,16 +97,18 @@ describe('downloadFileSaga', () => { autoDownloadFilesSaga, socket, messagesActions.incomingMessages({ - messages: [{ - id: id, - type: MessageType.Image, - message: 'message', - createdAt: 8, - channelAddress: 'general', - signature: 'signature', - pubKey: 'publicKey', - media: media - }] + messages: [ + { + id: id, + type: MessageType.Image, + message: 'message', + createdAt: 8, + channelId: generalChannel.id, + signature: 'signature', + pubKey: 'publicKey', + media: media + } + ] }) ) .withReducer(reducer) @@ -110,9 +128,11 @@ describe('downloadFileSaga', () => { const id = Math.random().toString(36).substr(2.9) - store.dispatch(publicChannelsActions.setCurrentChannel({ - channelAddress: 'general' - })) + store.dispatch( + publicChannelsActions.setCurrentChannel({ + channelId: generalChannel.id + }) + ) const media: FileMetadata = { cid: 'cid', @@ -121,7 +141,7 @@ describe('downloadFileSaga', () => { ext: 'ext', message: { id: id, - channelAddress: 'general' + channelId: generalChannel.id } } @@ -130,16 +150,18 @@ describe('downloadFileSaga', () => { autoDownloadFilesSaga, socket, messagesActions.incomingMessages({ - messages: [{ - id: id, - type: MessageType.File, - message: 'message', - createdAt: 8, - channelAddress: 'general', - signature: 'signature', - pubKey: 'publicKey', - media: media - }] + messages: [ + { + id: id, + type: MessageType.File, + message: 'message', + createdAt: 8, + channelId: generalChannel.id, + signature: 'signature', + pubKey: 'publicKey', + media: media + } + ] }) ) .withReducer(reducer) @@ -159,9 +181,11 @@ describe('downloadFileSaga', () => { const id = Math.random().toString(36).substr(2.9) - store.dispatch(publicChannelsActions.setCurrentChannel({ - channelAddress: 'general' - })) + store.dispatch( + publicChannelsActions.setCurrentChannel({ + channelId: generalChannel.id + }) + ) const media: FileMetadata = { cid: 'cid', @@ -170,25 +194,19 @@ describe('downloadFileSaga', () => { ext: 'ext', message: { id: id, - channelAddress: 'general' + channelId: generalChannel.id } } - const message = (await factory.create< - ReturnType['payload'] - >('Message', { - identity: alice, - message: { - id: id, - type: MessageType.File, - message: '', - createdAt: DateTime.utc().valueOf(), - channelAddress: 'general', - signature: '', - pubKey: '', - media: media - } - })).message + const message = ( + await factory.create['payload']>( + 'Message', + { + identity: alice, + message: generateMessageFactoryContentWithId(generalChannel.id), + } + ) + ).message const reducer = combineReducers(reducers) await expectSaga( @@ -215,9 +233,11 @@ describe('downloadFileSaga', () => { const id = Math.random().toString(36).substr(2.9) - store.dispatch(publicChannelsActions.setCurrentChannel({ - channelAddress: sailingChannel.address - })) + store.dispatch( + publicChannelsActions.setCurrentChannel({ + channelId: sailingChannel.id + }) + ) const media: FileMetadata = { cid: 'cid', @@ -226,25 +246,19 @@ describe('downloadFileSaga', () => { ext: 'ext', message: { id: id, - channelAddress: 'general' + channelId: generalChannel.id } } - const message = (await factory.create< - ReturnType['payload'] - >('Message', { - identity: alice, - message: { - id: id, - type: MessageType.File, - message: '', - createdAt: DateTime.utc().valueOf(), - channelAddress: 'general', - signature: '', - pubKey: '', - media: media - } - })).message + const message = ( + await factory.create['payload']>( + 'Message', + { + identity: alice, + message: generateMessageFactoryContentWithId(generalChannel.id), + } + ) + ).message const reducer = combineReducers(reducers) await expectSaga( @@ -271,9 +285,11 @@ describe('downloadFileSaga', () => { const id = Math.random().toString(36).substr(2.9) - store.dispatch(publicChannelsActions.setCurrentChannel({ - channelAddress: 'general' - })) + store.dispatch( + publicChannelsActions.setCurrentChannel({ + channelId: generalChannel.id + }) + ) const media: FileMetadata = { cid: 'cid', @@ -283,7 +299,7 @@ describe('downloadFileSaga', () => { size: AUTODOWNLOAD_SIZE_LIMIT + 1024, message: { id: id, - channelAddress: 'general' + channelId: generalChannel.id } } @@ -292,16 +308,18 @@ describe('downloadFileSaga', () => { autoDownloadFilesSaga, socket, messagesActions.incomingMessages({ - messages: [{ - id: id, - type: MessageType.File, - message: 'message', - createdAt: 8, - channelAddress: 'general', - signature: 'signature', - pubKey: 'publicKey', - media: media - }] + messages: [ + { + id: id, + type: MessageType.File, + message: 'message', + createdAt: 8, + channelId: generalChannel.id, + signature: 'signature', + pubKey: 'publicKey', + media: media + } + ] }) ) .withReducer(reducer) @@ -321,9 +339,11 @@ describe('downloadFileSaga', () => { const id = Math.random().toString(36).substr(2.9) - store.dispatch(publicChannelsActions.setCurrentChannel({ - channelAddress: 'general' - })) + store.dispatch( + publicChannelsActions.setCurrentChannel({ + channelId: generalChannel.id + }) + ) const media: FileMetadata = { cid: 'cid', @@ -333,7 +353,7 @@ describe('downloadFileSaga', () => { size: AUTODOWNLOAD_SIZE_LIMIT + 1024, message: { id: id, - channelAddress: 'general' + channelId: generalChannel.id } } @@ -342,16 +362,18 @@ describe('downloadFileSaga', () => { autoDownloadFilesSaga, socket, messagesActions.incomingMessages({ - messages: [{ - id: id, - type: MessageType.Image, - message: 'message', - createdAt: 8, - channelAddress: 'general', - signature: 'signature', - pubKey: 'publicKey', - media: media - }] + messages: [ + { + id: id, + type: MessageType.Image, + message: 'message', + createdAt: 8, + channelId: generalChannel.id, + signature: 'signature', + pubKey: 'publicKey', + media: media + } + ] }) ) .withReducer(reducer) diff --git a/packages/state-manager/src/sagas/files/autoDownloadFiles/autoDownloadFiles.saga.ts b/packages/state-manager/src/sagas/files/autoDownloadFiles/autoDownloadFiles.saga.ts index 75cbf72205..6bde227abe 100644 --- a/packages/state-manager/src/sagas/files/autoDownloadFiles/autoDownloadFiles.saga.ts +++ b/packages/state-manager/src/sagas/files/autoDownloadFiles/autoDownloadFiles.saga.ts @@ -25,7 +25,7 @@ export function* autoDownloadFilesSaga( if (!message.media || (message.type !== MessageType.Image && message.type !== MessageType.File)) return const channelMessages = yield* select( - messagesSelectors.publicChannelMessagesEntities(message.channelAddress) + messagesSelectors.publicChannelMessagesEntities(message.channelId) ) const draft = channelMessages[message.id] diff --git a/packages/state-manager/src/sagas/files/broadcastHostedFile/broadcastHostedFile.saga.test.ts b/packages/state-manager/src/sagas/files/broadcastHostedFile/broadcastHostedFile.saga.test.ts index 795c44b470..2c28a5d750 100644 --- a/packages/state-manager/src/sagas/files/broadcastHostedFile/broadcastHostedFile.saga.test.ts +++ b/packages/state-manager/src/sagas/files/broadcastHostedFile/broadcastHostedFile.saga.test.ts @@ -13,6 +13,8 @@ import { broadcastHostedFileSaga } from './broadcastHostedFile.saga' import { publicChannelsActions } from '../../publicChannels/publicChannels.slice' import { DateTime } from 'luxon' import { Community, FileMetadata, Identity, PublicChannel, SocketActionTypes } from '@quiet/types' +import { publicChannelsSelectors } from '../../publicChannels/publicChannels.selectors' +import { generateChannelId } from '@quiet/common' describe('downloadFileSaga', () => { let store: Store @@ -22,6 +24,7 @@ describe('downloadFileSaga', () => { let alice: Identity let sailingChannel: PublicChannel + let generalChannel: PublicChannel beforeAll(async () => { setupCrypto() @@ -38,7 +41,9 @@ describe('downloadFileSaga', () => { 'Identity', { id: community.id, nickname: 'alice' } ) - + const generalChannelState = publicChannelsSelectors.generalChannel(store.getState()) + if (generalChannelState) generalChannel = generalChannelState + expect(generalChannel).not.toBeUndefined() sailingChannel = ( await factory.create['payload']>( 'PublicChannel', @@ -48,7 +53,7 @@ describe('downloadFileSaga', () => { description: 'Welcome to #sailing', timestamp: DateTime.utc().valueOf(), owner: alice.nickname, - address: 'sailing' + id: generateChannelId('sailing') } } ) @@ -59,7 +64,7 @@ describe('downloadFileSaga', () => { const socket = { emit: jest.fn() } as unknown as Socket store.dispatch(publicChannelsActions.setCurrentChannel({ - channelAddress: 'general' + channelId: generalChannel.id })) const id = Math.random().toString(36).substr(2.9) @@ -71,7 +76,7 @@ describe('downloadFileSaga', () => { ext: 'ext', message: { id: id, - channelAddress: 'general' + channelId: generalChannel.id } } @@ -85,7 +90,7 @@ describe('downloadFileSaga', () => { type: MessageType.File, message: '', createdAt: DateTime.utc().valueOf(), - channelAddress: 'general', + channelId: generalChannel.id, signature: '', pubKey: '', media: media @@ -118,7 +123,7 @@ describe('downloadFileSaga', () => { const socket = { emit: jest.fn() } as unknown as Socket store.dispatch(publicChannelsActions.setCurrentChannel({ - channelAddress: sailingChannel.address + channelId: sailingChannel.id })) const id = Math.random().toString(36).substr(2.9) @@ -130,7 +135,7 @@ describe('downloadFileSaga', () => { ext: 'ext', message: { id: id, - channelAddress: 'general' + channelId: generalChannel.id } } @@ -144,7 +149,7 @@ describe('downloadFileSaga', () => { type: MessageType.File, message: '', createdAt: DateTime.utc().valueOf(), - channelAddress: 'general', + channelId: generalChannel.id, signature: '', pubKey: '', media: media diff --git a/packages/state-manager/src/sagas/files/broadcastHostedFile/broadcastHostedFile.saga.ts b/packages/state-manager/src/sagas/files/broadcastHostedFile/broadcastHostedFile.saga.ts index 92cb167c93..07b98d9250 100644 --- a/packages/state-manager/src/sagas/files/broadcastHostedFile/broadcastHostedFile.saga.ts +++ b/packages/state-manager/src/sagas/files/broadcastHostedFile/broadcastHostedFile.saga.ts @@ -15,14 +15,14 @@ export function* broadcastHostedFileSaga( if (!identity) return const channelMessages = yield* select( - messagesSelectors.publicChannelMessagesEntities(action.payload.message.channelAddress) + messagesSelectors.publicChannelMessagesEntities(action.payload.message.channelId) ) const message = channelMessages[action.payload.message.id] if (!message || !instanceOfChannelMessage(message)) { console.error( - `Cannot broadcast message after uploading. Draft ${action.payload.message.id} from #${action.payload.message.channelAddress} does not exist in local storage.` + `Cannot broadcast message after uploading. Draft ${action.payload.message.id} from #${action.payload.message.channelId} does not exist in local storage.` ) return } diff --git a/packages/state-manager/src/sagas/files/checkForMissingFiles/checkForMissingFiles.saga.test.ts b/packages/state-manager/src/sagas/files/checkForMissingFiles/checkForMissingFiles.saga.test.ts index ffcdb01c2b..7c87742b56 100644 --- a/packages/state-manager/src/sagas/files/checkForMissingFiles/checkForMissingFiles.saga.test.ts +++ b/packages/state-manager/src/sagas/files/checkForMissingFiles/checkForMissingFiles.saga.test.ts @@ -1,5 +1,11 @@ import { setupCrypto } from '@quiet/identity' -import { AUTODOWNLOAD_SIZE_LIMIT, communities, getFactory, identity, publicChannels } from '../../..' +import { + AUTODOWNLOAD_SIZE_LIMIT, + communities, + getFactory, + identity, + publicChannels +} from '../../..' import { prepareStore, reducers } from '../../../utils/tests/prepareStore' import { combineReducers } from '@reduxjs/toolkit' import { expectSaga } from 'redux-saga-test-plan' @@ -8,7 +14,14 @@ import { checkForMissingFilesSaga } from './checkForMissingFiles.saga' import { Socket } from 'socket.io-client' import { filesActions } from '../files.slice' import { networkActions } from '../../network/network.slice' -import { DownloadState, FileMetadata, MessageType, SocketActionTypes } from '@quiet/types' +import { + DownloadState, + FileMetadata, + MessageType, + PublicChannel, + SocketActionTypes +} from '@quiet/types' +import { publicChannelsSelectors } from '../../publicChannels/publicChannels.selectors' describe('checkForMissingFilesSaga', () => { beforeAll(async () => { @@ -24,12 +37,16 @@ describe('checkForMissingFilesSaga', () => { ReturnType['payload'] >('Community') + const generalChannel = publicChannelsSelectors.generalChannel(initialState.getState()) + if (!generalChannel) return + expect(generalChannel).not.toBeUndefined() + const alice = await factory.create< ReturnType['payload'] >('Identity', { id: community.id, nickname: 'alice' }) const message = Math.random().toString(36).substr(2.9) - const channelAddress = 'general' + const channelId = generalChannel.id const missingFile: FileMetadata = { cid: Math.random().toString(36).substr(2.9), @@ -38,7 +55,7 @@ describe('checkForMissingFilesSaga', () => { ext: '.jpeg', message: { id: message, - channelAddress: channelAddress + channelId: channelId }, size: AUTODOWNLOAD_SIZE_LIMIT - 2048 } @@ -52,7 +69,7 @@ describe('checkForMissingFilesSaga', () => { type: MessageType.Image, message: '', createdAt: DateTime.utc().valueOf(), - channelAddress: 'general', + channelId: generalChannel.id, signature: '', pubKey: '', media: missingFile @@ -63,11 +80,13 @@ describe('checkForMissingFilesSaga', () => { const store = prepareStore(initialState.getState()).store const socket = { emit: jest.fn(), on: jest.fn() } as unknown as Socket - store.dispatch(filesActions.updateDownloadStatus({ - mid: missingFile.message.id, - cid: missingFile.cid, - downloadState: DownloadState.Downloading - })) + store.dispatch( + filesActions.updateDownloadStatus({ + mid: missingFile.message.id, + cid: missingFile.cid, + downloadState: DownloadState.Downloading + }) + ) const reducer = combineReducers(reducers) await expectSaga( @@ -77,11 +96,13 @@ describe('checkForMissingFilesSaga', () => { ) .withReducer(reducer) .withState(store.getState()) - .put(filesActions.updateDownloadStatus({ - mid: missingFile.message.id, - cid: missingFile.cid, - downloadState: DownloadState.Queued - })) + .put( + filesActions.updateDownloadStatus({ + mid: missingFile.message.id, + cid: missingFile.cid, + downloadState: DownloadState.Queued + }) + ) .apply(socket, socket.emit, [ SocketActionTypes.DOWNLOAD_FILE, { @@ -101,12 +122,15 @@ describe('checkForMissingFilesSaga', () => { ReturnType['payload'] >('Community') + const generalChannel = publicChannelsSelectors.generalChannel(initialState.getState()) + if (!generalChannel) return + expect(generalChannel).not.toBeUndefined() const alice = await factory.create< ReturnType['payload'] >('Identity', { id: community.id, nickname: 'alice' }) const message = Math.random().toString(36).substr(2.9) - const channelAddress = 'general' + const channelId = generalChannel.id const missingFile: FileMetadata = { cid: Math.random().toString(36).substr(2.9), @@ -115,7 +139,7 @@ describe('checkForMissingFilesSaga', () => { ext: '.zip', message: { id: message, - channelAddress: channelAddress + channelId: channelId }, size: AUTODOWNLOAD_SIZE_LIMIT - 2048 } @@ -129,7 +153,7 @@ describe('checkForMissingFilesSaga', () => { type: MessageType.File, message: '', createdAt: DateTime.utc().valueOf(), - channelAddress: 'general', + channelId: generalChannel.id, signature: '', pubKey: '', media: missingFile @@ -140,11 +164,13 @@ describe('checkForMissingFilesSaga', () => { const store = prepareStore(initialState.getState()).store const socket = { emit: jest.fn(), on: jest.fn() } as unknown as Socket - store.dispatch(filesActions.updateDownloadStatus({ - mid: missingFile.message.id, - cid: missingFile.cid, - downloadState: DownloadState.Downloading - })) + store.dispatch( + filesActions.updateDownloadStatus({ + mid: missingFile.message.id, + cid: missingFile.cid, + downloadState: DownloadState.Downloading + }) + ) const reducer = combineReducers(reducers) await expectSaga( @@ -154,11 +180,13 @@ describe('checkForMissingFilesSaga', () => { ) .withReducer(reducer) .withState(store.getState()) - .put(filesActions.updateDownloadStatus({ - mid: missingFile.message.id, - cid: missingFile.cid, - downloadState: DownloadState.Queued - })) + .put( + filesActions.updateDownloadStatus({ + mid: missingFile.message.id, + cid: missingFile.cid, + downloadState: DownloadState.Queued + }) + ) .apply(socket, socket.emit, [ SocketActionTypes.DOWNLOAD_FILE, { @@ -178,6 +206,9 @@ describe('checkForMissingFilesSaga', () => { ReturnType['payload'] >('Community') + const generalChannel = publicChannelsSelectors.generalChannel(initialState.getState()) + if (!generalChannel) return + expect(generalChannel).not.toBeUndefined() const alice = await factory.create< ReturnType['payload'] >('Identity', { id: community.id, nickname: 'alice' }) @@ -185,7 +216,7 @@ describe('checkForMissingFilesSaga', () => { const message1 = Math.random().toString(36).substr(2.9) const message2 = Math.random().toString(36).substr(2.9) - const channelAddress = 'general' + const channelId = generalChannel.id const missingFileCanceled: FileMetadata = { cid: Math.random().toString(36).substr(2.9), @@ -194,7 +225,7 @@ describe('checkForMissingFilesSaga', () => { ext: '.zip', message: { id: message1, - channelAddress: channelAddress + channelId: channelId }, size: AUTODOWNLOAD_SIZE_LIMIT - 2048 } @@ -206,7 +237,7 @@ describe('checkForMissingFilesSaga', () => { ext: '.zip', message: { id: message2, - channelAddress: channelAddress + channelId: channelId }, size: AUTODOWNLOAD_SIZE_LIMIT - 2048 } @@ -220,7 +251,7 @@ describe('checkForMissingFilesSaga', () => { type: MessageType.File, message: '', createdAt: DateTime.utc().valueOf(), - channelAddress: 'general', + channelId: generalChannel.id, signature: '', pubKey: '', media: missingFileCanceled @@ -237,7 +268,7 @@ describe('checkForMissingFilesSaga', () => { type: MessageType.File, message: '', createdAt: DateTime.utc().valueOf(), - channelAddress: 'general', + channelId: generalChannel.id, signature: '', pubKey: '', media: missingFilePending @@ -248,17 +279,21 @@ describe('checkForMissingFilesSaga', () => { const store = prepareStore(initialState.getState()).store const socket = { emit: jest.fn(), on: jest.fn() } as unknown as Socket - store.dispatch(filesActions.updateDownloadStatus({ - mid: missingFileCanceled.message.id, - cid: missingFileCanceled.cid, - downloadState: DownloadState.Canceled - })) + store.dispatch( + filesActions.updateDownloadStatus({ + mid: missingFileCanceled.message.id, + cid: missingFileCanceled.cid, + downloadState: DownloadState.Canceled + }) + ) - store.dispatch(filesActions.updateDownloadStatus({ - mid: missingFilePending.message.id, - cid: missingFilePending.cid, - downloadState: DownloadState.Downloading - })) + store.dispatch( + filesActions.updateDownloadStatus({ + mid: missingFilePending.message.id, + cid: missingFilePending.cid, + downloadState: DownloadState.Downloading + }) + ) const reducer = combineReducers(reducers) await expectSaga( @@ -268,11 +303,13 @@ describe('checkForMissingFilesSaga', () => { ) .withReducer(reducer) .withState(store.getState()) - .not.put(filesActions.updateDownloadStatus({ - mid: missingFileCanceled.message.id, - cid: missingFileCanceled.cid, - downloadState: DownloadState.Queued - })) + .not.put( + filesActions.updateDownloadStatus({ + mid: missingFileCanceled.message.id, + cid: missingFileCanceled.cid, + downloadState: DownloadState.Queued + }) + ) .not.apply(socket, socket.emit, [ SocketActionTypes.DOWNLOAD_FILE, { @@ -280,11 +317,13 @@ describe('checkForMissingFilesSaga', () => { metadata: missingFileCanceled } ]) - .put(filesActions.updateDownloadStatus({ - mid: missingFilePending.message.id, - cid: missingFilePending.cid, - downloadState: DownloadState.Queued - })) + .put( + filesActions.updateDownloadStatus({ + mid: missingFilePending.message.id, + cid: missingFilePending.cid, + downloadState: DownloadState.Queued + }) + ) .apply(socket, socket.emit, [ SocketActionTypes.DOWNLOAD_FILE, { @@ -304,12 +343,15 @@ describe('checkForMissingFilesSaga', () => { ReturnType['payload'] >('Community') + const generalChannel = publicChannelsSelectors.generalChannel(initialState.getState()) + if (!generalChannel) return + expect(generalChannel).not.toBeUndefined() const alice = await factory.create< ReturnType['payload'] >('Identity', { id: community.id, nickname: 'alice' }) const message = Math.random().toString(36).substr(2.9) - const channelAddress = 'general' + const channelId = generalChannel.id const missingFile: FileMetadata = { cid: Math.random().toString(36).substr(2.9), @@ -318,7 +360,7 @@ describe('checkForMissingFilesSaga', () => { ext: '.zip', message: { id: message, - channelAddress: channelAddress + channelId: channelId }, size: AUTODOWNLOAD_SIZE_LIMIT + 2048 } @@ -332,7 +374,7 @@ describe('checkForMissingFilesSaga', () => { type: MessageType.File, message: '', createdAt: DateTime.utc().valueOf(), - channelAddress: 'general', + channelId: generalChannel.id, signature: '', pubKey: '', media: missingFile @@ -343,11 +385,13 @@ describe('checkForMissingFilesSaga', () => { const store = prepareStore(initialState.getState()).store const socket = { emit: jest.fn(), on: jest.fn() } as unknown as Socket - store.dispatch(filesActions.updateDownloadStatus({ - mid: missingFile.message.id, - cid: missingFile.cid, - downloadState: DownloadState.Ready - })) + store.dispatch( + filesActions.updateDownloadStatus({ + mid: missingFile.message.id, + cid: missingFile.cid, + downloadState: DownloadState.Ready + }) + ) const reducer = combineReducers(reducers) await expectSaga( @@ -357,11 +401,13 @@ describe('checkForMissingFilesSaga', () => { ) .withReducer(reducer) .withState(store.getState()) - .not.put(filesActions.updateDownloadStatus({ - mid: missingFile.message.id, - cid: missingFile.cid, - downloadState: DownloadState.Queued - })) + .not.put( + filesActions.updateDownloadStatus({ + mid: missingFile.message.id, + cid: missingFile.cid, + downloadState: DownloadState.Queued + }) + ) .not.apply(socket, socket.emit, [ SocketActionTypes.DOWNLOAD_FILE, { @@ -381,12 +427,15 @@ describe('checkForMissingFilesSaga', () => { ReturnType['payload'] >('Community') + const generalChannel = publicChannelsSelectors.generalChannel(initialState.getState()) + if (!generalChannel) return + expect(generalChannel).not.toBeUndefined() const alice = await factory.create< ReturnType['payload'] >('Identity', { id: community.id, nickname: 'alice' }) const message = Math.random().toString(36).substr(2.9) - const channelAddress = 'general' + const channelId = generalChannel.id const missingFile: FileMetadata = { cid: Math.random().toString(36).substr(2.9), @@ -395,7 +444,7 @@ describe('checkForMissingFilesSaga', () => { ext: '.zip', message: { id: message, - channelAddress: channelAddress + channelId: channelId }, size: AUTODOWNLOAD_SIZE_LIMIT + 2048 } @@ -409,7 +458,7 @@ describe('checkForMissingFilesSaga', () => { type: MessageType.File, message: '', createdAt: DateTime.utc().valueOf(), - channelAddress: 'general', + channelId: generalChannel.id, signature: '', pubKey: '', media: missingFile @@ -420,11 +469,13 @@ describe('checkForMissingFilesSaga', () => { const store = prepareStore(initialState.getState()).store const socket = { emit: jest.fn(), on: jest.fn() } as unknown as Socket - store.dispatch(filesActions.updateDownloadStatus({ - mid: missingFile.message.id, - cid: missingFile.cid, - downloadState: DownloadState.Downloading - })) + store.dispatch( + filesActions.updateDownloadStatus({ + mid: missingFile.message.id, + cid: missingFile.cid, + downloadState: DownloadState.Downloading + }) + ) const reducer = combineReducers(reducers) await expectSaga( @@ -434,11 +485,13 @@ describe('checkForMissingFilesSaga', () => { ) .withReducer(reducer) .withState(store.getState()) - .put(filesActions.updateDownloadStatus({ - mid: missingFile.message.id, - cid: missingFile.cid, - downloadState: DownloadState.Queued - })) + .put( + filesActions.updateDownloadStatus({ + mid: missingFile.message.id, + cid: missingFile.cid, + downloadState: DownloadState.Queued + }) + ) .apply(socket, socket.emit, [ SocketActionTypes.DOWNLOAD_FILE, { @@ -461,9 +514,11 @@ describe('checkForMissingFilesSaga', () => { const alice = await factory.create< ReturnType['payload'] >('Identity', { id: community.id, nickname: 'alice' }) - + const generalChannel = publicChannelsSelectors.generalChannel(initialState.getState()) + if (!generalChannel) return + expect(generalChannel).not.toBeUndefined() const message = Math.random().toString(36).substr(2.9) - const channelAddress = 'general' + const channelId = generalChannel.id const missingFile: FileMetadata = { cid: Math.random().toString(36).substr(2.9), @@ -472,7 +527,7 @@ describe('checkForMissingFilesSaga', () => { ext: '.zip', message: { id: message, - channelAddress: channelAddress + channelId: channelId }, size: AUTODOWNLOAD_SIZE_LIMIT - 2048 } @@ -486,7 +541,7 @@ describe('checkForMissingFilesSaga', () => { type: MessageType.File, message: '', createdAt: DateTime.utc().valueOf(), - channelAddress: 'general', + channelId: generalChannel.id, signature: '', pubKey: '', media: missingFile @@ -497,11 +552,13 @@ describe('checkForMissingFilesSaga', () => { const store = (await prepareStore(initialState.getState())).store const socket = { emit: jest.fn(), on: jest.fn() } as unknown as Socket - store.dispatch(filesActions.updateDownloadStatus({ - mid: missingFile.message.id, - cid: missingFile.cid, - downloadState: DownloadState.Canceled - })) + store.dispatch( + filesActions.updateDownloadStatus({ + mid: missingFile.message.id, + cid: missingFile.cid, + downloadState: DownloadState.Canceled + }) + ) const reducer = combineReducers(reducers) await expectSaga( @@ -511,11 +568,13 @@ describe('checkForMissingFilesSaga', () => { ) .withReducer(reducer) .withState(store.getState()) - .not.put(filesActions.updateDownloadStatus({ - mid: missingFile.message.id, - cid: missingFile.cid, - downloadState: DownloadState.Queued - })) + .not.put( + filesActions.updateDownloadStatus({ + mid: missingFile.message.id, + cid: missingFile.cid, + downloadState: DownloadState.Queued + }) + ) .not.apply(socket, socket.emit, [ SocketActionTypes.DOWNLOAD_FILE, { @@ -535,12 +594,15 @@ describe('checkForMissingFilesSaga', () => { ReturnType['payload'] >('Community') + const generalChannel = publicChannelsSelectors.generalChannel(initialState.getState()) + if (!generalChannel) return + expect(generalChannel).not.toBeUndefined() const alice = await factory.create< ReturnType['payload'] >('Identity', { id: community.id, nickname: 'alice' }) const message = Math.random().toString(36).substr(2.9) - const channelAddress = 'general' + const channelId = generalChannel.id const missingFile: FileMetadata = { cid: Math.random().toString(36).substr(2.9), @@ -549,7 +611,7 @@ describe('checkForMissingFilesSaga', () => { ext: '.zip', message: { id: message, - channelAddress: channelAddress + channelId: channelId }, size: AUTODOWNLOAD_SIZE_LIMIT - 2048 } @@ -563,7 +625,7 @@ describe('checkForMissingFilesSaga', () => { type: MessageType.File, message: '', createdAt: DateTime.utc().valueOf(), - channelAddress: 'general', + channelId: generalChannel.id, signature: '', pubKey: '', media: missingFile @@ -574,11 +636,13 @@ describe('checkForMissingFilesSaga', () => { const store = (await prepareStore(initialState.getState())).store const socket = { emit: jest.fn(), on: jest.fn() } as unknown as Socket - store.dispatch(filesActions.updateDownloadStatus({ - mid: missingFile.message.id, - cid: missingFile.cid, - downloadState: DownloadState.Malicious - })) + store.dispatch( + filesActions.updateDownloadStatus({ + mid: missingFile.message.id, + cid: missingFile.cid, + downloadState: DownloadState.Malicious + }) + ) const reducer = combineReducers(reducers) await expectSaga( @@ -588,11 +652,13 @@ describe('checkForMissingFilesSaga', () => { ) .withReducer(reducer) .withState(store.getState()) - .not.put(filesActions.updateDownloadStatus({ - mid: missingFile.message.id, - cid: missingFile.cid, - downloadState: DownloadState.Queued - })) + .not.put( + filesActions.updateDownloadStatus({ + mid: missingFile.message.id, + cid: missingFile.cid, + downloadState: DownloadState.Queued + }) + ) .not.apply(socket, socket.emit, [ SocketActionTypes.DOWNLOAD_FILE, { @@ -612,12 +678,15 @@ describe('checkForMissingFilesSaga', () => { ReturnType['payload'] >('Community') + const generalChannel = publicChannelsSelectors.generalChannel(initialState.getState()) + if (!generalChannel) return + expect(generalChannel).not.toBeUndefined() const alice = await factory.create< ReturnType['payload'] >('Identity', { id: community.id, nickname: 'alice' }) const message = Math.random().toString(36).substr(2.9) - const channelAddress = 'general' + const channelId = generalChannel.id const missingFile: FileMetadata = { cid: Math.random().toString(36).substr(2.9), @@ -626,7 +695,7 @@ describe('checkForMissingFilesSaga', () => { ext: '.zip', message: { id: message, - channelAddress: channelAddress + channelId: channelId }, size: AUTODOWNLOAD_SIZE_LIMIT - 2048 } @@ -640,7 +709,7 @@ describe('checkForMissingFilesSaga', () => { type: MessageType.File, message: '', createdAt: DateTime.utc().valueOf(), - channelAddress: 'general', + channelId: generalChannel.id, signature: '', pubKey: '', media: missingFile @@ -651,9 +720,11 @@ describe('checkForMissingFilesSaga', () => { const store = (await prepareStore(initialState.getState())).store const socket = { emit: jest.fn(), on: jest.fn() } as unknown as Socket - store.dispatch(filesActions.removeDownloadStatus({ - cid: missingFile.cid - })) + store.dispatch( + filesActions.removeDownloadStatus({ + cid: missingFile.cid + }) + ) const reducer = combineReducers(reducers) await expectSaga( @@ -663,11 +734,13 @@ describe('checkForMissingFilesSaga', () => { ) .withReducer(reducer) .withState(store.getState()) - .put(filesActions.updateDownloadStatus({ - mid: missingFile.message.id, - cid: missingFile.cid, - downloadState: DownloadState.Queued - })) + .put( + filesActions.updateDownloadStatus({ + mid: missingFile.message.id, + cid: missingFile.cid, + downloadState: DownloadState.Queued + }) + ) .apply(socket, socket.emit, [ SocketActionTypes.DOWNLOAD_FILE, { @@ -678,74 +751,82 @@ describe('checkForMissingFilesSaga', () => { .run() }) - it.each([[AUTODOWNLOAD_SIZE_LIMIT + 1], [AUTODOWNLOAD_SIZE_LIMIT - 1024]])('resume downloading for file of any size if it is already in queue (%s)', async (size: number) => { - const initialState = prepareStore().store - - const factory = await getFactory(initialState) - - const community = await factory.create< - ReturnType['payload'] - >('Community') - - const alice = await factory.create< - ReturnType['payload'] - >('Identity', { id: community.id, nickname: 'alice' }) - - const message = Math.random().toString(36).substr(2.9) - const channelAddress = 'general' - const missingFile: FileMetadata = { - cid: Math.random().toString(36).substr(2.9), - path: null, - name: 'test-file', - ext: '.zip', - message: { - id: message, - channelAddress: channelAddress - }, - size: size - } - - await factory.create['payload']>( - 'Message', - { - identity: alice, + it.each([[AUTODOWNLOAD_SIZE_LIMIT + 1], [AUTODOWNLOAD_SIZE_LIMIT - 1024]])( + 'resume downloading for file of any size if it is already in queue (%s)', + async (size: number) => { + const initialState = prepareStore().store + + const factory = await getFactory(initialState) + + const community = await factory.create< + ReturnType['payload'] + >('Community') + + const generalChannel = publicChannelsSelectors.generalChannel(initialState.getState()) + if (!generalChannel) return + expect(generalChannel).not.toBeUndefined() + const alice = await factory.create< + ReturnType['payload'] + >('Identity', { id: community.id, nickname: 'alice' }) + + const message = Math.random().toString(36).substr(2.9) + const channelId = generalChannel.id + const missingFile: FileMetadata = { + cid: Math.random().toString(36).substr(2.9), + path: null, + name: 'test-file', + ext: '.zip', message: { id: message, - type: MessageType.File, - message: '', - createdAt: DateTime.utc().valueOf(), - channelAddress: 'general', - signature: '', - pubKey: '', - media: missingFile - } + channelId: channelId + }, + size: size } - ) - const store = prepareStore(initialState.getState()).store - const socket = { emit: jest.fn(), on: jest.fn() } as unknown as Socket - - store.dispatch(filesActions.updateDownloadStatus({ - mid: missingFile.message.id, - cid: missingFile.cid, - downloadState: DownloadState.Queued - })) - - const reducer = combineReducers(reducers) - await expectSaga( - checkForMissingFilesSaga, - socket, - networkActions.addInitializedCommunity(community.id) - ) - .withReducer(reducer) - .withState(store.getState()) - .apply(socket, socket.emit, [ - SocketActionTypes.DOWNLOAD_FILE, + await factory.create['payload']>( + 'Message', { - peerId: alice.peerId.id, - metadata: missingFile + identity: alice, + message: { + id: message, + type: MessageType.File, + message: '', + createdAt: DateTime.utc().valueOf(), + channelId: generalChannel.id, + signature: '', + pubKey: '', + media: missingFile + } } - ]) - .run() - }) + ) + + const store = prepareStore(initialState.getState()).store + const socket = { emit: jest.fn(), on: jest.fn() } as unknown as Socket + + store.dispatch( + filesActions.updateDownloadStatus({ + mid: missingFile.message.id, + cid: missingFile.cid, + downloadState: DownloadState.Queued + }) + ) + + const reducer = combineReducers(reducers) + await expectSaga( + checkForMissingFilesSaga, + socket, + networkActions.addInitializedCommunity(community.id) + ) + .withReducer(reducer) + .withState(store.getState()) + .apply(socket, socket.emit, [ + SocketActionTypes.DOWNLOAD_FILE, + { + peerId: alice.peerId.id, + metadata: missingFile + } + ]) + .run() + } + ) }) diff --git a/packages/state-manager/src/sagas/files/checkForMissingFiles/checkForMissingFiles.saga.ts b/packages/state-manager/src/sagas/files/checkForMissingFiles/checkForMissingFiles.saga.ts index cfafa7c14f..d08cf43a75 100644 --- a/packages/state-manager/src/sagas/files/checkForMissingFiles/checkForMissingFiles.saga.ts +++ b/packages/state-manager/src/sagas/files/checkForMissingFiles/checkForMissingFiles.saga.ts @@ -27,7 +27,7 @@ export function* checkForMissingFilesSaga( const downloadStatuses = yield* select(filesSelectors.downloadStatuses) for (const channel of channels) { - const missingFiles = yield* select(missingChannelFiles(channel.address)) + const missingFiles = yield* select(missingChannelFiles(channel.id)) if (missingFiles.length > 0) { for (const file of missingFiles) { diff --git a/packages/state-manager/src/sagas/files/deleteFilesFromChannel/deleteFilesFromChannel.saga.test.ts b/packages/state-manager/src/sagas/files/deleteFilesFromChannel/deleteFilesFromChannel.saga.test.ts index 9b70e35a7b..d22ab9d479 100644 --- a/packages/state-manager/src/sagas/files/deleteFilesFromChannel/deleteFilesFromChannel.saga.test.ts +++ b/packages/state-manager/src/sagas/files/deleteFilesFromChannel/deleteFilesFromChannel.saga.test.ts @@ -15,6 +15,7 @@ import { filesActions } from '../../files/files.slice' import { deleteFilesFromChannelSaga } from './deleteFilesFromChannel.saga' import { publicChannelsSelectors } from '../../publicChannels/publicChannels.selectors' import { publicChannelsActions } from '../../publicChannels/publicChannels.slice' +import { generateChannelId } from '@quiet/common' describe('deleteFilesFromChannelSaga', () => { let store: Store @@ -23,7 +24,7 @@ describe('deleteFilesFromChannelSaga', () => { let community: Community let owner: Identity - let generalChannel: PublicChannel | undefined + let generalChannel: PublicChannel let photoChannel: PublicChannel let message: any @@ -45,7 +46,8 @@ describe('deleteFilesFromChannelSaga', () => { { id: community.id, nickname: 'alice' } ) - generalChannel = publicChannelsSelectors.currentChannel(store.getState()) + const generalChannelState = publicChannelsSelectors.generalChannel(store.getState()) + if (generalChannelState) generalChannel = generalChannelState expect(generalChannel).not.toBeUndefined() photoChannel = ( @@ -57,7 +59,7 @@ describe('deleteFilesFromChannelSaga', () => { description: 'Welcome to #photo', timestamp: DateTime.utc().valueOf(), owner: owner.nickname, - address: 'photo' + id: generateChannelId('id') } } ) @@ -73,7 +75,7 @@ describe('deleteFilesFromChannelSaga', () => { type: MessageType.Basic, message: 'message', createdAt: DateTime.utc().valueOf(), - channelAddress: photoChannel.address, + channelId: photoChannel.id, signature: '', pubKey: '', media: { @@ -83,7 +85,7 @@ describe('deleteFilesFromChannelSaga', () => { ext: 'png', message: { id: id, - channelAddress: photoChannel.address + channelId: photoChannel.id } } }, @@ -94,13 +96,13 @@ describe('deleteFilesFromChannelSaga', () => { }) test('delete files from channel', async () => { - const channelAddress = photoChannel.address + const channelId = photoChannel.id const reducer = combineReducers(reducers) await expectSaga( deleteFilesFromChannelSaga, socket, - filesActions.deleteFilesFromChannel({ channelAddress }) + filesActions.deleteFilesFromChannel({ channelId }) ) .withReducer(reducer) .withState(store.getState()) diff --git a/packages/state-manager/src/sagas/files/deleteFilesFromChannel/deleteFilesFromChannel.saga.ts b/packages/state-manager/src/sagas/files/deleteFilesFromChannel/deleteFilesFromChannel.saga.ts index 293b17af32..aff9f9f689 100644 --- a/packages/state-manager/src/sagas/files/deleteFilesFromChannel/deleteFilesFromChannel.saga.ts +++ b/packages/state-manager/src/sagas/files/deleteFilesFromChannel/deleteFilesFromChannel.saga.ts @@ -9,9 +9,9 @@ export function* deleteFilesFromChannelSaga( socket: Socket, action: PayloadAction['payload']> ): Generator { - const { channelAddress } = action.payload + const { channelId } = action.payload - const messages = yield* select(messagesSelectors.publicChannelMessagesEntities(channelAddress)) + const messages = yield* select(messagesSelectors.publicChannelMessagesEntities(channelId)) yield* apply( socket, diff --git a/packages/state-manager/src/sagas/files/downloadFile/downloadFileSaga.test.ts b/packages/state-manager/src/sagas/files/downloadFile/downloadFileSaga.test.ts index 1058f93de3..fbfd2bd955 100644 --- a/packages/state-manager/src/sagas/files/downloadFile/downloadFileSaga.test.ts +++ b/packages/state-manager/src/sagas/files/downloadFile/downloadFileSaga.test.ts @@ -2,7 +2,7 @@ import { setupCrypto } from '@quiet/identity' import { Store } from '../../store.types' -import { getFactory } from '../../..' +import { getFactory, PublicChannel } from '../../..' import { prepareStore, reducers } from '../../../utils/tests/prepareStore' import { combineReducers } from '@reduxjs/toolkit' import { expectSaga } from 'redux-saga-test-plan' @@ -13,6 +13,7 @@ import { downloadFileSaga } from './downloadFileSaga' import { FactoryGirl } from 'factory-girl' import { filesActions } from '../files.slice' import { Community, DownloadState, FileMetadata, Identity, SocketActionTypes } from '@quiet/types' +import { publicChannelsSelectors } from '../../publicChannels/publicChannels.selectors' describe('downloadFileSaga', () => { let store: Store @@ -23,6 +24,8 @@ describe('downloadFileSaga', () => { let message: string + let generalChannel: PublicChannel + beforeAll(async () => { setupCrypto() @@ -40,6 +43,10 @@ describe('downloadFileSaga', () => { ) message = Math.random().toString(36).substr(2.9) + + const generalChannelState = publicChannelsSelectors.generalChannel(store.getState()) + if (generalChannelState) generalChannel = generalChannelState + expect(generalChannel).not.toBeUndefined() }) test('downloading file', async () => { @@ -52,7 +59,7 @@ describe('downloadFileSaga', () => { ext: 'ext', message: { id: message, - channelAddress: 'general' + channelId: generalChannel.id } } diff --git a/packages/state-manager/src/sagas/files/files.types.ts b/packages/state-manager/src/sagas/files/files.types.ts index ab4bb56f6d..28b246bee1 100644 --- a/packages/state-manager/src/sagas/files/files.types.ts +++ b/packages/state-manager/src/sagas/files/files.types.ts @@ -14,7 +14,7 @@ export interface FileMetadata extends FileContent { export interface FileMessage { id: string - channelAddress: string + channelId: string } export interface UploadFilePayload { diff --git a/packages/state-manager/src/sagas/files/resetTransferSpeed/resetTransferSpeed.saga.test.ts b/packages/state-manager/src/sagas/files/resetTransferSpeed/resetTransferSpeed.saga.test.ts index 94b88b1d5e..e2fd8dc5e9 100644 --- a/packages/state-manager/src/sagas/files/resetTransferSpeed/resetTransferSpeed.saga.test.ts +++ b/packages/state-manager/src/sagas/files/resetTransferSpeed/resetTransferSpeed.saga.test.ts @@ -1,6 +1,6 @@ import { setupCrypto } from '@quiet/identity' import { Store } from '../../store.types' -import { getFactory, MessageType, publicChannels } from '../../..' +import { getFactory, MessageType, PublicChannel, publicChannels } from '../../..' import { prepareStore, reducers } from '../../../utils/tests/prepareStore' import { combineReducers } from '@reduxjs/toolkit' import { expectSaga } from 'redux-saga-test-plan' @@ -13,6 +13,7 @@ import { DateTime } from 'luxon' import { filesActions } from '../files.slice' import { networkActions } from '../../network/network.slice' import { Community, DownloadState, FileMetadata, Identity } from '@quiet/types' +import { publicChannelsSelectors } from '../../publicChannels/publicChannels.selectors' describe('downloadFileSaga', () => { let store: Store @@ -21,6 +22,8 @@ describe('downloadFileSaga', () => { let community: Community let alice: Identity + let generalChannel: PublicChannel + beforeAll(async () => { setupCrypto() @@ -32,6 +35,10 @@ describe('downloadFileSaga', () => { ReturnType['payload'] >('Community') + const generalChannelState = publicChannelsSelectors.generalChannel(store.getState()) + if (generalChannelState) generalChannel = generalChannelState + expect(generalChannel).not.toBeUndefined() + alice = await factory.create['payload']>( 'Identity', { id: community.id, nickname: 'alice' } @@ -41,7 +48,7 @@ describe('downloadFileSaga', () => { test('reset transfer speed for files with existing transfer speed', async () => { store.dispatch( publicChannelsActions.setCurrentChannel({ - channelAddress: 'general' + channelId: generalChannel.id }) ) @@ -53,7 +60,7 @@ describe('downloadFileSaga', () => { ext: 'zip', message: { id: id, - channelAddress: 'general' + channelId: generalChannel.id } } @@ -68,7 +75,7 @@ describe('downloadFileSaga', () => { type: MessageType.File, message: '', createdAt: DateTime.utc().valueOf(), - channelAddress: 'general', + channelId: generalChannel.id, signature: '', pubKey: '', media: media @@ -90,10 +97,7 @@ describe('downloadFileSaga', () => { ) const reducer = combineReducers(reducers) - await expectSaga( - resetTransferSpeedSaga, - networkActions.addInitializedCommunity(community.id) - ) + await expectSaga(resetTransferSpeedSaga, networkActions.addInitializedCommunity(community.id)) .withReducer(reducer) .withState(store.getState()) .put( @@ -114,7 +118,7 @@ describe('downloadFileSaga', () => { test('do not reset transfer speed for files without existing transfer speed', async () => { store.dispatch( publicChannelsActions.setCurrentChannel({ - channelAddress: 'general' + channelId: generalChannel.id }) ) @@ -126,7 +130,7 @@ describe('downloadFileSaga', () => { ext: 'zip', message: { id: id, - channelAddress: 'general' + channelId: generalChannel.id } } @@ -141,7 +145,7 @@ describe('downloadFileSaga', () => { type: MessageType.File, message: '', createdAt: DateTime.utc().valueOf(), - channelAddress: 'general', + channelId: generalChannel.id, signature: '', pubKey: '', media: media @@ -158,10 +162,7 @@ describe('downloadFileSaga', () => { ) const reducer = combineReducers(reducers) - await expectSaga( - resetTransferSpeedSaga, - networkActions.addInitializedCommunity(community.id) - ) + await expectSaga(resetTransferSpeedSaga, networkActions.addInitializedCommunity(community.id)) .withReducer(reducer) .withState(store.getState()) .not.put( @@ -182,7 +183,7 @@ describe('downloadFileSaga', () => { test('do not reset transfer speed for files with download state other than downloading', async () => { store.dispatch( publicChannelsActions.setCurrentChannel({ - channelAddress: 'general' + channelId: generalChannel.id }) ) @@ -194,7 +195,7 @@ describe('downloadFileSaga', () => { ext: 'zip', message: { id: id, - channelAddress: 'general' + channelId: generalChannel.id } } @@ -209,7 +210,7 @@ describe('downloadFileSaga', () => { type: MessageType.File, message: '', createdAt: DateTime.utc().valueOf(), - channelAddress: 'general', + channelId: generalChannel.id, signature: '', pubKey: '', media: media @@ -231,10 +232,7 @@ describe('downloadFileSaga', () => { ) const reducer = combineReducers(reducers) - await expectSaga( - resetTransferSpeedSaga, - networkActions.addInitializedCommunity(community.id) - ) + await expectSaga(resetTransferSpeedSaga, networkActions.addInitializedCommunity(community.id)) .withReducer(reducer) .withState(store.getState()) .not.put( diff --git a/packages/state-manager/src/sagas/files/updateMessageMedia/updateMessageMedia.test.ts b/packages/state-manager/src/sagas/files/updateMessageMedia/updateMessageMedia.test.ts index 3dda8fa920..fa37ed016e 100644 --- a/packages/state-manager/src/sagas/files/updateMessageMedia/updateMessageMedia.test.ts +++ b/packages/state-manager/src/sagas/files/updateMessageMedia/updateMessageMedia.test.ts @@ -13,6 +13,8 @@ import { updateMessageMediaSaga } from './updateMessageMedia' import { publicChannelsActions } from '../../publicChannels/publicChannels.slice' import { DateTime } from 'luxon' import { Community, Identity, MessageType, PublicChannel } from '@quiet/types' +import { publicChannelsSelectors } from '../../publicChannels/publicChannels.selectors' +import { generateChannelId } from '@quiet/common' describe('downloadedFileSaga', () => { let store: Store @@ -23,6 +25,8 @@ describe('downloadedFileSaga', () => { let sailingChannel: PublicChannel + let generalChannel: PublicChannel + beforeAll(async () => { setupCrypto() @@ -31,9 +35,13 @@ describe('downloadedFileSaga', () => { factory = await getFactory(store) community = await factory.create< - ReturnType['payload'] + ReturnType['payload'] >('Community') + const generalChannelState = publicChannelsSelectors.generalChannel(store.getState()) + if (generalChannelState) generalChannel = generalChannelState + expect(generalChannel).not.toBeUndefined() + alice = await factory.create['payload']>( 'Identity', { id: community.id, nickname: 'alice' } @@ -48,7 +56,7 @@ describe('downloadedFileSaga', () => { description: 'Welcome to #sailing', timestamp: DateTime.utc().valueOf(), owner: alice.nickname, - address: 'sailing' + id: generateChannelId('sailing') } } ) @@ -58,7 +66,7 @@ describe('downloadedFileSaga', () => { test('update message media', async () => { store.dispatch( publicChannelsActions.setCurrentChannel({ - channelAddress: 'general' + channelId: 'general' }) ) @@ -71,7 +79,7 @@ describe('downloadedFileSaga', () => { ext: 'png', message: { id: id, - channelAddress: 'general' + channelId: generalChannel.id } } @@ -85,7 +93,7 @@ describe('downloadedFileSaga', () => { type: MessageType.Basic, message: '', createdAt: DateTime.utc().valueOf(), - channelAddress: 'general', + channelId: generalChannel.id, signature: '', pubKey: '', media: metadata @@ -115,7 +123,7 @@ describe('downloadedFileSaga', () => { test('update message media for non-active channel', async () => { store.dispatch( publicChannelsActions.setCurrentChannel({ - channelAddress: sailingChannel.address + channelId: sailingChannel.id }) ) @@ -128,7 +136,7 @@ describe('downloadedFileSaga', () => { ext: 'png', message: { id: id, - channelAddress: 'general' + channelId: generalChannel.id } } @@ -142,7 +150,7 @@ describe('downloadedFileSaga', () => { type: MessageType.Basic, message: '', createdAt: DateTime.utc().valueOf(), - channelAddress: 'general', + channelId: generalChannel.id, signature: '', pubKey: '', media: metadata diff --git a/packages/state-manager/src/sagas/files/updateMessageMedia/updateMessageMedia.ts b/packages/state-manager/src/sagas/files/updateMessageMedia/updateMessageMedia.ts index 3b2b083f8a..8f31cf6d67 100644 --- a/packages/state-manager/src/sagas/files/updateMessageMedia/updateMessageMedia.ts +++ b/packages/state-manager/src/sagas/files/updateMessageMedia/updateMessageMedia.ts @@ -9,14 +9,14 @@ export function* updateMessageMediaSaga( action: PayloadAction['payload']> ): Generator { const channelMessages = yield* select( - messagesSelectors.publicChannelMessagesEntities(action.payload.message.channelAddress) + messagesSelectors.publicChannelMessagesEntities(action.payload.message.channelId) ) const message = channelMessages[action.payload.message.id] if (!message || !instanceOfChannelMessage(message)) { console.error( - `Cannot update message media. Message ${action.payload.message.id} from #${action.payload.message.channelAddress} does not exist in local storage.` + `Cannot update message media. Message ${action.payload.message.id} from #${action.payload.message.channelId} does not exist in local storage.` ) return } diff --git a/packages/state-manager/src/sagas/files/uploadFile/uploadFile.saga.test.ts b/packages/state-manager/src/sagas/files/uploadFile/uploadFile.saga.test.ts index 1942d51a87..5eed57f225 100644 --- a/packages/state-manager/src/sagas/files/uploadFile/uploadFile.saga.test.ts +++ b/packages/state-manager/src/sagas/files/uploadFile/uploadFile.saga.test.ts @@ -1,6 +1,4 @@ -import { - setupCrypto -} from '@quiet/identity' +import { setupCrypto } from '@quiet/identity' import { call } from 'redux-saga-test-plan/matchers' import { Store } from '../../store.types' import { getFactory, MessageType } from '../../..' @@ -13,12 +11,20 @@ import { identityActions } from '../../identity/identity.slice' import { uploadFileSaga } from './uploadFile.saga' import { FactoryGirl } from 'factory-girl' import { publicChannelsActions } from '../../publicChannels/publicChannels.slice' -import { currentChannelAddress } from '../../publicChannels/publicChannels.selectors' import { filesActions } from '../files.slice' import { generateMessageId } from '../../messages/utils/message.utils' import { DateTime } from 'luxon' import { messagesActions } from '../../messages/messages.slice' -import { Community, DownloadState, FileMetadata, Identity, PublicChannel, SocketActionTypes } from '@quiet/types' +import { + Community, + DownloadState, + FileMetadata, + Identity, + PublicChannel, + SocketActionTypes +} from '@quiet/types' +import { generateChannelId } from '@quiet/common' +import { currentChannelId } from '../../publicChannels/publicChannels.selectors' describe('uploadFileSaga', () => { let store: Store @@ -39,7 +45,7 @@ describe('uploadFileSaga', () => { factory = await getFactory(store) community = await factory.create< - ReturnType['payload'] + ReturnType['payload'] >('Community') alice = await factory.create['payload']>( @@ -47,18 +53,20 @@ describe('uploadFileSaga', () => { { id: community.id, nickname: 'alice' } ) - sailingChannel = (await factory.create['payload']>( - 'PublicChannel', - { - channel: { - name: 'sailing', - description: 'Welcome to #sailing', - timestamp: DateTime.utc().valueOf(), - owner: alice.nickname, - address: 'sailing' + sailingChannel = ( + await factory.create['payload']>( + 'PublicChannel', + { + channel: { + name: 'sailing', + description: 'Welcome to #sailing', + timestamp: DateTime.utc().valueOf(), + owner: alice.nickname, + id: generateChannelId('sailing') + } } - } - )).channel + ) + ).channel message = Math.random().toString(36).substr(2.9) }) @@ -66,7 +74,9 @@ describe('uploadFileSaga', () => { test('uploading file', async () => { const socket = { emit: jest.fn() } as unknown as Socket - const currentChannel = currentChannelAddress(store.getState()) + const currentChannel = currentChannelId(store.getState()) + + if (!currentChannel) throw new Error('no current channel id') const peerId = alice.peerId.id @@ -77,32 +87,30 @@ describe('uploadFileSaga', () => { ext: 'ext', message: { id: message, - channelAddress: currentChannel + channelId: currentChannel } } const reducer = combineReducers(reducers) - await expectSaga( - uploadFileSaga, - socket, - filesActions.uploadFile(media) - ) + await expectSaga(uploadFileSaga, socket, filesActions.uploadFile(media)) .withReducer(reducer) .withState(store.getState()) - .provide([ - [call.fn(generateMessageId), message] - ]) - .put(messagesActions.sendMessage({ - id: message, - message: '', - type: MessageType.File, - media: media - })) - .put(filesActions.updateDownloadStatus({ - mid: message, - cid: `uploading_${message}`, - downloadState: DownloadState.Uploading, - downloadProgress: undefined - })) + .provide([[call.fn(generateMessageId), message]]) + .put( + messagesActions.sendMessage({ + id: message, + message: '', + type: MessageType.File, + media: media + }) + ) + .put( + filesActions.updateDownloadStatus({ + mid: message, + cid: `uploading_${message}`, + downloadState: DownloadState.Uploading, + downloadProgress: undefined + }) + ) .apply(socket, socket.emit, [ SocketActionTypes.UPLOAD_FILE, { diff --git a/packages/state-manager/src/sagas/files/uploadFile/uploadFile.saga.ts b/packages/state-manager/src/sagas/files/uploadFile/uploadFile.saga.ts index 2b8e38f670..242436ae0a 100644 --- a/packages/state-manager/src/sagas/files/uploadFile/uploadFile.saga.ts +++ b/packages/state-manager/src/sagas/files/uploadFile/uploadFile.saga.ts @@ -14,7 +14,7 @@ export function* uploadFileSaga( ): Generator { const identity = yield* select(identitySelectors.currentIdentity) - const currentChannel = yield* select(publicChannelsSelectors.currentChannelAddress) + const currentChannel = yield* select(publicChannelsSelectors.currentChannelId) if (!identity || !currentChannel) return const id = yield* call(generateMessageId) @@ -24,7 +24,7 @@ export function* uploadFileSaga( cid: `uploading_${id}`, message: { id: id, - channelAddress: currentChannel + channelId: currentChannel } } diff --git a/packages/state-manager/src/sagas/messages/askForMessages/askForMessages.saga.test.ts b/packages/state-manager/src/sagas/messages/askForMessages/askForMessages.saga.test.ts index 060cd7da1d..fe1437c6be 100644 --- a/packages/state-manager/src/sagas/messages/askForMessages/askForMessages.saga.test.ts +++ b/packages/state-manager/src/sagas/messages/askForMessages/askForMessages.saga.test.ts @@ -11,7 +11,7 @@ describe('askForMessagesSaga', () => { const askForMessagesPayload: AskForMessagesPayload = { peerId: '', communityId: '', - channelAddress: '', + channelId: '', ids: [] } const saga: TestApi = testSaga( diff --git a/packages/state-manager/src/sagas/messages/checkForMessages/checkForMessages.saga.test.ts b/packages/state-manager/src/sagas/messages/checkForMessages/checkForMessages.saga.test.ts index ad541405a1..5fd1cdfaaa 100644 --- a/packages/state-manager/src/sagas/messages/checkForMessages/checkForMessages.saga.test.ts +++ b/packages/state-manager/src/sagas/messages/checkForMessages/checkForMessages.saga.test.ts @@ -1,6 +1,6 @@ import { setupCrypto } from '@quiet/identity' import { Store } from '../../store.types' -import { getFactory, publicChannels } from '../../..' +import { generateMessageFactoryContentWithId, getFactory, publicChannels } from '../../..' import { prepareStore, reducers } from '../../../utils/tests/prepareStore' import { messagesActions } from './../messages.slice' import { communitiesActions } from '../../communities/communities.slice' @@ -9,9 +9,8 @@ import { FactoryGirl } from 'factory-girl' import { combineReducers } from 'redux' import { expectSaga } from 'redux-saga-test-plan' import { checkForMessagesSaga } from './checkForMessages.saga' -import { DateTime } from 'luxon' import { selectGeneralChannel } from '../../publicChannels/publicChannels.selectors' -import { Community, Identity, MessageType, PublicChannel } from '@quiet/types' +import { Community, Identity, PublicChannel } from '@quiet/types' describe('checkForMessagesSaga', () => { let store: Store @@ -55,15 +54,7 @@ describe('checkForMessagesSaga', () => { 'Message', { identity: alice, - message: { - id: Math.random().toString(36).substr(2.9), - type: MessageType.Basic, - message: 'message', - createdAt: DateTime.utc().valueOf(), - channelAddress: generalChannel.address, - signature: '', - pubKey: '' - }, + message: generateMessageFactoryContentWithId(generalChannel.id), verifyAutomatically: true } ) @@ -74,7 +65,7 @@ describe('checkForMessagesSaga', () => { checkForMessagesSaga, messagesActions.responseSendMessagesIds({ ids: [message.id, 'jf84hwwa', 'kl12sa0a'], - channelAddress: generalChannel.address, + channelId: generalChannel.id, communityId: community.id }) ) @@ -84,7 +75,7 @@ describe('checkForMessagesSaga', () => { messagesActions.askForMessages({ peerId: alice.peerId.id, communityId: community.id, - channelAddress: generalChannel.address, + channelId: generalChannel.id, ids: ['jf84hwwa', 'kl12sa0a'] }) ) diff --git a/packages/state-manager/src/sagas/messages/checkForMessages/checkForMessages.saga.ts b/packages/state-manager/src/sagas/messages/checkForMessages/checkForMessages.saga.ts index 96cd5a2595..fbc6d1ce3b 100644 --- a/packages/state-manager/src/sagas/messages/checkForMessages/checkForMessages.saga.ts +++ b/packages/state-manager/src/sagas/messages/checkForMessages/checkForMessages.saga.ts @@ -8,21 +8,21 @@ import { currentCommunity } from '../../communities/communities.selectors' import { currentIdentity } from '../../identity/identity.selectors' export function* checkForMessagesSaga(action: PayloadAction['payload']>): Generator { - const { ids, channelAddress } = action.payload + const { ids, channelId } = action.payload const community = yield* select(currentCommunity) const identity = yield* select(currentIdentity) if (!community || !identity) return - const missingMessages = yield* select(missingChannelMessages(ids, channelAddress)) + const missingMessages = yield* select(missingChannelMessages(ids, channelId)) if (missingMessages?.length > 0) { yield* put( messagesActions.askForMessages({ peerId: identity.peerId.id, communityId: community.id, - channelAddress: channelAddress, + channelId: channelId, ids: missingMessages }) ) diff --git a/packages/state-manager/src/sagas/messages/incomingMessages/incomingMessages.saga.test.ts b/packages/state-manager/src/sagas/messages/incomingMessages/incomingMessages.saga.test.ts index a556be18ad..34b5af740f 100644 --- a/packages/state-manager/src/sagas/messages/incomingMessages/incomingMessages.saga.test.ts +++ b/packages/state-manager/src/sagas/messages/incomingMessages/incomingMessages.saga.test.ts @@ -15,7 +15,15 @@ import { DateTime } from 'luxon' import { incomingMessagesSaga } from './incomingMessages.saga' import { messagesActions } from '../messages.slice' import { reducers } from '../../reducers' -import { ChannelMessage, Community, FileMetadata, Identity, MessageType, PublicChannel } from '@quiet/types' +import { + ChannelMessage, + Community, + FileMetadata, + Identity, + MessageType, + PublicChannel +} from '@quiet/types' +import { generateChannelId } from '@quiet/common' describe('incomingMessagesSaga', () => { let store: Store @@ -24,10 +32,9 @@ describe('incomingMessagesSaga', () => { let community: Community let alice: Identity - let generalChannel: PublicChannel | undefined + let generalChannel: PublicChannel let sailingChannel: PublicChannel let barbequeChannel: PublicChannel - let generalChannelAddress: string beforeAll(async () => { setupCrypto() @@ -37,7 +44,7 @@ describe('incomingMessagesSaga', () => { factory = await getFactory(store) community = await factory.create< - ReturnType['payload'] + ReturnType['payload'] >('Community') alice = await factory.create['payload']>( @@ -45,9 +52,9 @@ describe('incomingMessagesSaga', () => { { id: community.id, nickname: 'alice' } ) - generalChannel = selectGeneralChannel(store.getState()) - expect(generalChannel).toBeDefined() - generalChannelAddress = generalChannel?.address || '' + const generalChannelState = publicChannelsSelectors.generalChannel(store.getState()) + if (generalChannelState) generalChannel = generalChannelState + expect(generalChannel).not.toBeUndefined() sailingChannel = ( await factory.create['payload']>( @@ -58,7 +65,7 @@ describe('incomingMessagesSaga', () => { description: 'Welcome to #sailing', timestamp: DateTime.utc().valueOf(), owner: alice.nickname, - address: 'sailing' + id: generateChannelId('sailing') } } ) @@ -73,7 +80,7 @@ describe('incomingMessagesSaga', () => { description: 'Welcome to #barbeque', timestamp: DateTime.utc().valueOf(), owner: alice.nickname, - address: 'barbeque' + id: generateChannelId('barbeque') } } ) @@ -89,7 +96,7 @@ describe('incomingMessagesSaga', () => { type: MessageType.Basic, message: 'message', createdAt: DateTime.utc().valueOf(), - channelAddress: generalChannel?.address, + channelId: generalChannel.id, signature: '', pubKey: '' }, @@ -100,7 +107,7 @@ describe('incomingMessagesSaga', () => { // Set 'general' as active channel store.dispatch( publicChannelsActions.setCurrentChannel({ - channelAddress: generalChannelAddress + channelId: generalChannel.id }) ) @@ -119,7 +126,7 @@ describe('incomingMessagesSaga', () => { .put( publicChannelsActions.cacheMessages({ messages: [message], - channelAddress: message.channelAddress + channelId: message.channelId }) ) .run() @@ -136,7 +143,7 @@ describe('incomingMessagesSaga', () => { type: MessageType.Basic, message: 'message', createdAt: DateTime.utc().valueOf(), - channelAddress: generalChannelAddress, + channelId: generalChannel.id, signature: '', pubKey: '' }, @@ -151,7 +158,7 @@ describe('incomingMessagesSaga', () => { ext: 'png', message: { id: id, - channelAddress: generalChannelAddress + channelId: generalChannel.id } } @@ -163,7 +170,7 @@ describe('incomingMessagesSaga', () => { // Set 'general' as active channel store.dispatch( publicChannelsActions.setCurrentChannel({ - channelAddress: generalChannelAddress + channelId: generalChannel.id }) ) @@ -171,7 +178,7 @@ describe('incomingMessagesSaga', () => { store.dispatch( publicChannelsActions.cacheMessages({ messages: [message], - channelAddress: generalChannelAddress + channelId: generalChannel.id }) ) @@ -196,7 +203,7 @@ describe('incomingMessagesSaga', () => { .put( publicChannelsActions.cacheMessages({ messages: [message], - channelAddress: message.channelAddress + channelId: message.channelId }) ) .run() @@ -211,30 +218,42 @@ describe('incomingMessagesSaga', () => { ext: 'png', message: { id: id, - channelAddress: generalChannelAddress + channelId: generalChannel.id } } const message = ( - await factory.create['payload']>('Message', { - identity: alice, - message: { - id: id, - type: MessageType.Basic, - message: 'message', - createdAt: DateTime.utc().valueOf(), - channelAddress: generalChannelAddress, - media: media, - signature: '', - pubKey: '' - }, - verifyAutomatically: true - }) + await factory.create['payload']>( + 'Message', + { + identity: alice, + message: { + id: id, + type: MessageType.Basic, + message: 'message', + createdAt: DateTime.utc().valueOf(), + channelId: generalChannel.id, + media: { + cid: 'uploading', + path: 'path/to/image.png', + name: 'image', + ext: 'png', + message: { + id: id, + channelId: generalChannel.id + } + }, + signature: '', + pubKey: '' + }, + verifyAutomatically: true + } + ) ).message // Set 'general' as active channel store.dispatch( publicChannelsActions.setCurrentChannel({ - channelAddress: generalChannelAddress + channelId: generalChannel.id }) ) @@ -242,7 +261,7 @@ describe('incomingMessagesSaga', () => { store.dispatch( publicChannelsActions.cacheMessages({ messages: [message], - channelAddress: generalChannelAddress + channelId: generalChannel.id }) ) @@ -250,29 +269,33 @@ describe('incomingMessagesSaga', () => { await expectSaga( incomingMessagesSaga, messagesActions.incomingMessages({ - messages: [{ - ...message, - media: { - ...media, - cid: 'cid', - path: null + messages: [ + { + ...message, + media: { + ...media, + cid: 'cid', + path: null + } } - }] + ] }) ) .withReducer(reducer) .withState(store.getState()) .put( publicChannelsActions.cacheMessages({ - messages: [{ - ...message, - media: { - ...media, - cid: 'cid', - path: 'path/to/image.png' + messages: [ + { + ...message, + media: { + ...media, + cid: 'cid', + path: 'path/to/image.png' + } } - }], - channelAddress: message.channelAddress + ], + channelId: message.channelId }) ) .run() @@ -287,7 +310,7 @@ describe('incomingMessagesSaga', () => { type: MessageType.Basic, message: 'message', createdAt: DateTime.utc().valueOf(), - channelAddress: sailingChannel.address, + channelId: sailingChannel.id, signature: '', pubKey: '' }, @@ -298,7 +321,7 @@ describe('incomingMessagesSaga', () => { // Set 'general' as active channel store.dispatch( publicChannelsActions.setCurrentChannel({ - channelAddress: generalChannelAddress + channelId: generalChannel.id }) ) @@ -314,7 +337,7 @@ describe('incomingMessagesSaga', () => { .not.put( publicChannelsActions.cacheMessages({ messages: [message], - channelAddress: message.channelAddress + channelId: message.channelId }) ) .run() @@ -329,7 +352,7 @@ describe('incomingMessagesSaga', () => { type: MessageType.Basic, message: 'message', createdAt: DateTime.utc().valueOf(), - channelAddress: generalChannelAddress, + channelId: generalChannel.id, signature: '', pubKey: '' }, @@ -349,7 +372,7 @@ describe('incomingMessagesSaga', () => { // Set 'general' as active channel store.dispatch( publicChannelsActions.setCurrentChannel({ - channelAddress: generalChannelAddress + channelId: generalChannel.id }) ) @@ -365,7 +388,7 @@ describe('incomingMessagesSaga', () => { .not.put( publicChannelsActions.cacheMessages({ messages: [message], - channelAddress: message.channelAddress + channelId: message.channelId }) ) .run() @@ -381,7 +404,7 @@ describe('incomingMessagesSaga', () => { type: MessageType.Basic, message: 'message', createdAt: DateTime.utc().valueOf() - DateTime.utc().minus({ days: 1 }).valueOf(), - channelAddress: barbequeChannel.address, + channelId: barbequeChannel.id, signature: '', pubKey: '' }, @@ -392,7 +415,7 @@ describe('incomingMessagesSaga', () => { // Set 'barbeque' as active channel store.dispatch( publicChannelsActions.setCurrentChannel({ - channelAddress: barbequeChannel.address + channelId: barbequeChannel.id }) ) @@ -410,7 +433,7 @@ describe('incomingMessagesSaga', () => { message: 'message', createdAt: DateTime.utc().valueOf() + DateTime.utc().minus({ minutes: index }).valueOf(), - channelAddress: barbequeChannel.address, + channelId: barbequeChannel.id, signature: '', pubKey: '' }, @@ -428,7 +451,7 @@ describe('incomingMessagesSaga', () => { 'CacheMessages', { messages: messages, - channelAddress: barbequeChannel.address + channelId: barbequeChannel.id } ) @@ -445,7 +468,7 @@ describe('incomingMessagesSaga', () => { ) .withReducer(reducer) .withState(store.getState()) - .not.put(publicChannelsActions.cacheMessages({ messages, channelAddress: barbequeChannel.address })) + .not.put(publicChannelsActions.cacheMessages({ messages, channelId: barbequeChannel.id })) .run() // Verify cached messages hasn't changed @@ -464,7 +487,7 @@ describe('incomingMessagesSaga', () => { type: MessageType.Basic, message: 'message', createdAt: DateTime.utc().valueOf() - DateTime.utc().minus({ days: 1 }).valueOf(), - channelAddress: generalChannelAddress, + channelId: generalChannel.id, signature: '', pubKey: '' }, @@ -475,7 +498,7 @@ describe('incomingMessagesSaga', () => { // Set 'general' as active channel store.dispatch( publicChannelsActions.setCurrentChannel({ - channelAddress: generalChannelAddress + channelId: generalChannel.id }) ) @@ -493,7 +516,7 @@ describe('incomingMessagesSaga', () => { message: 'message', createdAt: DateTime.utc().valueOf() + DateTime.utc().minus({ minutes: index }).valueOf(), - channelAddress: generalChannelAddress, + channelId: generalChannel.id, signature: '', pubKey: '' }, @@ -511,7 +534,7 @@ describe('incomingMessagesSaga', () => { 'CacheMessages', { messages: messages, - channelAddress: generalChannelAddress + channelId: generalChannel.id } ) @@ -535,7 +558,7 @@ describe('incomingMessagesSaga', () => { .put( publicChannelsActions.cacheMessages({ messages: updatedCache, - channelAddress: message.channelAddress + channelId: message.channelId }) ) .run() diff --git a/packages/state-manager/src/sagas/messages/incomingMessages/incomingMessages.saga.ts b/packages/state-manager/src/sagas/messages/incomingMessages/incomingMessages.saga.ts index c3ff6c4804..5f4b258e64 100644 --- a/packages/state-manager/src/sagas/messages/incomingMessages/incomingMessages.saga.ts +++ b/packages/state-manager/src/sagas/messages/incomingMessages/incomingMessages.saga.ts @@ -11,8 +11,8 @@ export function* incomingMessagesSaga( ): Generator { for (const incomingMessage of action.payload.messages) { // Proceed only for messages from current channel - const currentChannelAddress = yield* select(publicChannelsSelectors.currentChannelAddress) - if (incomingMessage.channelAddress !== currentChannelAddress) { + const currentChannelId = yield* select(publicChannelsSelectors.currentChannelId) + if (incomingMessage.channelId !== currentChannelId) { return } @@ -75,7 +75,7 @@ export function* incomingMessagesSaga( const cacheMessagesPayload: CacheMessagesPayload = { messages: cachedMessages, - channelAddress: message.channelAddress + channelId: message.channelId } yield* put(publicChannelsActions.cacheMessages(cacheMessagesPayload)) diff --git a/packages/state-manager/src/sagas/messages/manageCache/extendChannelCache.saga.test.ts b/packages/state-manager/src/sagas/messages/manageCache/extendChannelCache.saga.test.ts index 204986b009..8bde54e2b1 100644 --- a/packages/state-manager/src/sagas/messages/manageCache/extendChannelCache.saga.test.ts +++ b/packages/state-manager/src/sagas/messages/manageCache/extendChannelCache.saga.test.ts @@ -25,8 +25,7 @@ describe('extendCurrentPublicChannelCacheSaga', () => { let community: Community let alice: Identity - let generalChannel: PublicChannel | undefined - let generalChannelAddress: string + let generalChannel: PublicChannel beforeAll(async () => { setupCrypto() @@ -36,7 +35,7 @@ describe('extendCurrentPublicChannelCacheSaga', () => { factory = await getFactory(store) community = await factory.create< - ReturnType['payload'] + ReturnType['payload'] >('Community') alice = await factory.create['payload']>( @@ -44,16 +43,16 @@ describe('extendCurrentPublicChannelCacheSaga', () => { { id: community.id, nickname: 'alice' } ) - generalChannel = selectGeneralChannel(store.getState()) - expect(generalChannel).toBeDefined() - generalChannelAddress = generalChannel?.address || '' + const generalChannelState = publicChannelsSelectors.generalChannel(store.getState()) + if (generalChannelState) generalChannel = generalChannelState + expect(generalChannel).not.toBeUndefined() }) test('extend current public channel cache', async () => { // Set 'general' as active channel store.dispatch( publicChannelsActions.setCurrentChannel({ - channelAddress: generalChannelAddress + channelId: generalChannel.id }) ) @@ -73,7 +72,7 @@ describe('extendCurrentPublicChannelCacheSaga', () => { message: 'message', createdAt: DateTime.utc().valueOf() + DateTime.utc().minus({ minutes: index }).valueOf(), - channelAddress: generalChannelAddress, + channelId: generalChannel.id, signature: '', pubKey: '' }, @@ -92,7 +91,7 @@ describe('extendCurrentPublicChannelCacheSaga', () => { 'CacheMessages', { messages: messages.slice(0, 50), - channelAddress: generalChannelAddress + channelId: generalChannel.id } ) @@ -115,12 +114,12 @@ describe('extendCurrentPublicChannelCacheSaga', () => { .put( publicChannelsActions.cacheMessages({ messages: updatedCache, - channelAddress: generalChannelAddress + channelId: generalChannel.id }) ) .put( messagesActions.setDisplayedMessagesNumber({ - channelAddress: generalChannelAddress, + channelId: generalChannel.id, display: 100 }) ) diff --git a/packages/state-manager/src/sagas/messages/manageCache/extendChannelCache.saga.ts b/packages/state-manager/src/sagas/messages/manageCache/extendChannelCache.saga.ts index 445c2771cd..5e3bcf5fde 100644 --- a/packages/state-manager/src/sagas/messages/manageCache/extendChannelCache.saga.ts +++ b/packages/state-manager/src/sagas/messages/manageCache/extendChannelCache.saga.ts @@ -6,8 +6,9 @@ import { messagesActions } from '../messages.slice' import { CacheMessagesPayload, SetDisplayedMessagesNumberPayload } from '@quiet/types' export function* extendCurrentPublicChannelCacheSaga(): Generator { - const channelAddress = yield* select(publicChannelsSelectors.currentChannelAddress) - if (!channelAddress) return + const channelId = yield* select(publicChannelsSelectors.currentChannelId) + const currentChannelId = yield* select(publicChannelsSelectors.currentChannelId) + if (!currentChannelId || !channelId) return const channelMessagesChunkSize = 50 @@ -19,7 +20,9 @@ export function* extendCurrentPublicChannelCacheSaga(): Generator { publicChannelsSelectors.currentChannelLastDisplayedMessage ) - const lastDisplayedMessageIndex = channelMessagesEntries.findIndex(i => i.id === lastDisplayedMessage.id) + const lastDisplayedMessageIndex = channelMessagesEntries.findIndex( + i => i.id === lastDisplayedMessage.id + ) const messages = channelMessagesEntries.slice( Math.max(0, lastDisplayedMessageIndex - channelMessagesChunkSize) @@ -27,7 +30,7 @@ export function* extendCurrentPublicChannelCacheSaga(): Generator { const cacheMessagesPayload: CacheMessagesPayload = { messages: messages, - channelAddress: channelAddress + channelId: channelId } yield* put(publicChannelsActions.cacheMessages(cacheMessagesPayload)) @@ -40,7 +43,7 @@ export function* extendCurrentPublicChannelCacheSaga(): Generator { } const setDisplayedMessagesNumberPayload: SetDisplayedMessagesNumberPayload = { - channelAddress: channelAddress, + channelId: channelId, display: display } diff --git a/packages/state-manager/src/sagas/messages/manageCache/resetChannelCache.saga.test.ts b/packages/state-manager/src/sagas/messages/manageCache/resetChannelCache.saga.test.ts index 929301cf89..2d4d157508 100644 --- a/packages/state-manager/src/sagas/messages/manageCache/resetChannelCache.saga.test.ts +++ b/packages/state-manager/src/sagas/messages/manageCache/resetChannelCache.saga.test.ts @@ -24,8 +24,7 @@ describe('resetChannelCacheSaga', () => { let community: Community let alice: Identity - let generalChannel: PublicChannel | undefined - let generalChannelAddress: string + let generalChannel: PublicChannel beforeAll(async () => { setupCrypto() @@ -35,7 +34,7 @@ describe('resetChannelCacheSaga', () => { factory = await getFactory(store) community = await factory.create< - ReturnType['payload'] + ReturnType['payload'] >('Community') alice = await factory.create['payload']>( @@ -43,16 +42,16 @@ describe('resetChannelCacheSaga', () => { { id: community.id, nickname: 'alice' } ) - generalChannel = selectGeneralChannel(store.getState()) - expect(generalChannel).toBeDefined() - generalChannelAddress = generalChannel?.address || '' + const generalChannelState = publicChannelsSelectors.generalChannel(store.getState()) + if (generalChannelState) generalChannel = generalChannelState + expect(generalChannel).not.toBeUndefined() }) test('reset current public channel cache', async () => { // Set 'general' as active channel store.dispatch( publicChannelsActions.setCurrentChannel({ - channelAddress: generalChannelAddress + channelId: generalChannel.id }) ) @@ -62,20 +61,23 @@ describe('resetChannelCacheSaga', () => { const iterations = 80 ;[...Array(iterations)].map(async (_, index) => { const item = ( - await factory.create['payload']>('Message', { - identity: alice, - message: { - id: Math.random().toString(36).substr(2.9), - type: MessageType.Basic, - message: 'message', - createdAt: - DateTime.utc().valueOf() + DateTime.utc().minus({ minutes: index }).valueOf(), - channelAddress: generalChannelAddress, - signature: '', - pubKey: '' - }, - verifyAutomatically: true - }) + await factory.create['payload']>( + 'Message', + { + identity: alice, + message: { + id: Math.random().toString(36).substr(2.9), + type: MessageType.Basic, + message: 'message', + createdAt: + DateTime.utc().valueOf() + DateTime.utc().minus({ minutes: index }).valueOf(), + channelId: generalChannel.id, + signature: '', + pubKey: '' + }, + verifyAutomatically: true + } + ) ).message messages.push(item) if (messages.length === iterations) { @@ -88,7 +90,7 @@ describe('resetChannelCacheSaga', () => { 'CacheMessages', { messages: messages, - channelAddress: generalChannelAddress + channelId: generalChannel.id } ) @@ -107,12 +109,12 @@ describe('resetChannelCacheSaga', () => { .put( publicChannelsActions.cacheMessages({ messages: updatedCache, - channelAddress: generalChannelAddress + channelId: generalChannel.id }) ) .put( messagesActions.setDisplayedMessagesNumber({ - channelAddress: generalChannelAddress, + channelId: generalChannel.id, display: 50 }) ) diff --git a/packages/state-manager/src/sagas/messages/manageCache/resetChannelCache.saga.ts b/packages/state-manager/src/sagas/messages/manageCache/resetChannelCache.saga.ts index 3a8c536654..c938a0fa44 100644 --- a/packages/state-manager/src/sagas/messages/manageCache/resetChannelCache.saga.ts +++ b/packages/state-manager/src/sagas/messages/manageCache/resetChannelCache.saga.ts @@ -6,8 +6,8 @@ import { messagesActions } from '../messages.slice' import { CacheMessagesPayload, SetDisplayedMessagesNumberPayload } from '@quiet/types' export function* resetCurrentPublicChannelCacheSaga(): Generator { - const channelAddress = yield* select(publicChannelsSelectors.currentChannelAddress) - if (!channelAddress) return + const channelId = yield* select(publicChannelsSelectors.currentChannelId) + if (!channelId) return const channelMessagesChunkSize = 50 @@ -25,13 +25,13 @@ export function* resetCurrentPublicChannelCacheSaga(): Generator { const cacheMessagesPayload: CacheMessagesPayload = { messages: messages, - channelAddress: channelAddress + channelId: channelId } yield* put(publicChannelsActions.cacheMessages(cacheMessagesPayload)) const setDisplayedMessagesNumberPayload: SetDisplayedMessagesNumberPayload = { - channelAddress: channelAddress, + channelId: channelId, display: channelMessagesChunkSize } diff --git a/packages/state-manager/src/sagas/messages/messages.adapter.ts.ts b/packages/state-manager/src/sagas/messages/messages.adapter.ts.ts index ad7322626f..77fc305c08 100644 --- a/packages/state-manager/src/sagas/messages/messages.adapter.ts.ts +++ b/packages/state-manager/src/sagas/messages/messages.adapter.ts.ts @@ -2,7 +2,7 @@ import { ChannelMessage, MessageSendingStatus, MessageVerificationStatus, Public import { createEntityAdapter } from '@reduxjs/toolkit' export const publicChannelsMessagesBaseAdapter = createEntityAdapter({ - selectId: base => base.channelAddress + selectId: base => base.channelId }) export const messagesBaseAdapter = createEntityAdapter() diff --git a/packages/state-manager/src/sagas/messages/messages.selectors.test.ts b/packages/state-manager/src/sagas/messages/messages.selectors.test.ts index 068aaf950d..01a5159e42 100644 --- a/packages/state-manager/src/sagas/messages/messages.selectors.test.ts +++ b/packages/state-manager/src/sagas/messages/messages.selectors.test.ts @@ -6,7 +6,10 @@ import { validCurrentPublicChannelMessagesEntries } from './messages.selectors' import { communitiesActions } from '../communities/communities.slice' import { identityActions } from '../identity/identity.slice' import { FactoryGirl } from 'factory-girl' -import { selectGeneralChannel } from '../publicChannels/publicChannels.selectors' +import { + publicChannelsSelectors, + selectGeneralChannel +} from '../publicChannels/publicChannels.selectors' import { Community, Identity, PublicChannel, ChannelMessage } from '@quiet/types' describe('messagesSelectors', () => { @@ -14,8 +17,8 @@ describe('messagesSelectors', () => { let factory: FactoryGirl let community: Community - let generalChannel: PublicChannel | undefined - let generalChannelAddress: string + let generalChannel: PublicChannel + let generalChannelId: string let alice: Identity let john: Identity @@ -31,12 +34,14 @@ describe('messagesSelectors', () => { factory = await getFactory(store) community = await factory.create< - ReturnType['payload'] + ReturnType['payload'] >('Community') - generalChannel = selectGeneralChannel(store.getState()) + const generalChannelState = publicChannelsSelectors.generalChannel(store.getState()) + if (generalChannelState) generalChannel = generalChannelState + expect(generalChannel).not.toBeUndefined() expect(generalChannel).toBeDefined() - generalChannelAddress = generalChannel?.address || '' + generalChannelId = generalChannel?.id || '' alice = await factory.create['payload']>( 'Identity', @@ -61,7 +66,7 @@ describe('messagesSelectors', () => { }) ).payload.message, id: Math.random().toString(36).substr(2.9), - channelAddress: generalChannelAddress + channelId: generalChannel.id } const spoofedMessage: ChannelMessage = { @@ -71,7 +76,7 @@ describe('messagesSelectors', () => { }) ).payload.message, id: Math.random().toString(36).substr(2.9), - channelAddress: generalChannelAddress, + channelId: generalChannel.id, pubKey: johnPublicKey } @@ -88,7 +93,7 @@ describe('messagesSelectors', () => { store.dispatch( publicChannels.actions.setCurrentChannel({ - channelAddress: generalChannelAddress + channelId: generalChannel.id }) ) diff --git a/packages/state-manager/src/sagas/messages/messages.selectors.ts b/packages/state-manager/src/sagas/messages/messages.selectors.ts index 4729ee69bc..fb9dad0ae7 100644 --- a/packages/state-manager/src/sagas/messages/messages.selectors.ts +++ b/packages/state-manager/src/sagas/messages/messages.selectors.ts @@ -1,6 +1,6 @@ import { createSelector } from 'reselect' import { channelMessagesAdapter } from '../publicChannels/publicChannels.adapter' -import { currentChannelAddress } from '../publicChannels/publicChannels.selectors' +import { currentChannelId } from '../publicChannels/publicChannels.selectors' import { StoreKeys } from '../store.keys' import { CreatedSelectors, StoreState } from '../store.types' import { certificatesMapping } from '../users/users.selectors' @@ -37,9 +37,10 @@ export const publicChannelsMessagesBase = createSelector(messagesSlice, reducerS export const currentPublicChannelMessagesBase = createSelector( publicChannelsMessagesBase, - currentChannelAddress, - (base, address) => { - return base[address] + currentChannelId, + (base, id) => { + if (!id) return undefined + return base[id] } ) @@ -98,9 +99,9 @@ export const sortedCurrentPublicChannelMessagesEntries = createSelector( } ) -export const missingChannelMessages = (ids: string[], channelAddress: string) => +export const missingChannelMessages = (ids: string[], channelId: string) => createSelector(publicChannelsMessagesBase, base => { - const channelMessagesBase = base[channelAddress] + const channelMessagesBase = base[channelId] if (!channelMessagesBase) return [] const channelMessages = channelMessagesAdapter .getSelectors() @@ -108,9 +109,9 @@ export const missingChannelMessages = (ids: string[], channelAddress: string) => return ids.filter(id => !channelMessages.includes(id)) }) -export const missingChannelFiles = (channelAddress: string) => +export const missingChannelFiles = (channelId: string) => createSelector(publicChannelsMessagesBase, downloadStatuses, (base, statuses) => { - const channelMessagesBase = base[channelAddress] + const channelMessagesBase = base[channelId] if (!channelMessagesBase) return [] const channelMessages = channelMessagesAdapter .getSelectors() diff --git a/packages/state-manager/src/sagas/messages/messages.slice.ts b/packages/state-manager/src/sagas/messages/messages.slice.ts index f8598b5c8a..7d01b8bdd6 100644 --- a/packages/state-manager/src/sagas/messages/messages.slice.ts +++ b/packages/state-manager/src/sagas/messages/messages.slice.ts @@ -29,13 +29,13 @@ export const messagesSlice = createSlice({ sendDeletionMessage: (state, _action: PayloadAction) => state, deleteChannelEntry: (state, action: PayloadAction) => { - const { channelAddress } = action.payload - publicChannelsMessagesBaseAdapter.removeOne(state.publicChannelsMessagesBase, channelAddress) + const { channelId } = action.payload + publicChannelsMessagesBaseAdapter.removeOne(state.publicChannelsMessagesBase, channelId) }, addPublicChannelsMessagesBase: (state, action: PayloadAction) => { - const { channelAddress } = action.payload + const { channelId } = action.payload publicChannelsMessagesBaseAdapter.addOne(state.publicChannelsMessagesBase, { - channelAddress: channelAddress, + channelId: channelId, messages: channelMessagesAdapter.getInitialState(), display: 50 }) @@ -60,12 +60,12 @@ export const messagesSlice = createSlice({ const { messages } = action.payload for (const message of messages) { if (!instanceOfChannelMessage(message)) return - if (!state.publicChannelsMessagesBase.entities[message.channelAddress]) return + if (!state.publicChannelsMessagesBase.entities[message.channelId]) return let incoming = message const draft = state.publicChannelsMessagesBase - .entities[message.channelAddress] + .entities[message.channelId] ?.messages .entities[message.id] @@ -79,7 +79,7 @@ export const messagesSlice = createSlice({ } } - const messagesBase = state.publicChannelsMessagesBase.entities[message.channelAddress] + const messagesBase = state.publicChannelsMessagesBase.entities[message.channelId] if (!messagesBase) return channelMessagesAdapter.upsertOne( @@ -89,10 +89,10 @@ export const messagesSlice = createSlice({ } }, setDisplayedMessagesNumber: (state, action: PayloadAction) => { - const { display, channelAddress } = action.payload + const { display, channelId } = action.payload publicChannelsMessagesBaseAdapter.updateOne( state.publicChannelsMessagesBase, { - id: channelAddress, + id: channelId, changes: { display: display } diff --git a/packages/state-manager/src/sagas/messages/messages.transform.ts b/packages/state-manager/src/sagas/messages/messages.transform.ts index 48fdc40733..2f2dcab043 100644 --- a/packages/state-manager/src/sagas/messages/messages.transform.ts +++ b/packages/state-manager/src/sagas/messages/messages.transform.ts @@ -1,3 +1,5 @@ +import { PublicChannelsMessagesBase, ChannelMessage } from '@quiet/types' +import { Dictionary, EntityState } from '@reduxjs/toolkit' import { createTransform } from 'redux-persist' import { StoreKeys } from '../store.keys' import { messageSendingStatusAdapter } from './messages.adapter.ts' @@ -8,11 +10,54 @@ export const MessagesTransform = createTransform( return { ...inboundState } }, (outboundState: MessagesState, _key: any) => { + outboundState.publicChannelsMessagesBase.entities = transformPublicChannelsMessagesBaseEntities( + outboundState.publicChannelsMessagesBase.entities + ) + return { ...outboundState, publicKeyMapping: {}, - messageSendingStatus: messageSendingStatusAdapter.getInitialState(), + publicChannelsMessagesBase: outboundState.publicChannelsMessagesBase, + messageSendingStatus: messageSendingStatusAdapter.getInitialState() } }, { whitelist: [StoreKeys.Messages] } ) + +const transformPublicChannelsMessagesBaseEntities = ( + messagesBaseEntities: Dictionary +) => { + const messagesRefactor = (messages: EntityState) => { + const transformedMessagesEntities = messages.entities + for (const [key, _message] of Object.entries(transformedMessagesEntities)) { + const message = { ..._message } as any + if (message.channelAddress) { + const transformedMessage = { + ...message, + channelId: message.channelAddress + } + delete transformedMessage.channelAddress + + transformedMessagesEntities[key] = transformedMessage + } + } + + return transformedMessagesEntities + } + + for (const [key, _message] of Object.entries(messagesBaseEntities)) { + const message = { ..._message } as any + if (message.channelAddress) { + const messages = { ...message.messages, entities: messagesRefactor(message.messages) } + const transformedMessage = { + ...message, + messages, + channelId: message.channelAddress + } + delete transformedMessage.channelAddress + + messagesBaseEntities[key] = transformedMessage + } + } + return messagesBaseEntities +} diff --git a/packages/state-manager/src/sagas/messages/messages.types.ts b/packages/state-manager/src/sagas/messages/messages.types.ts index 35fdd7f4e8..90a8720954 100644 --- a/packages/state-manager/src/sagas/messages/messages.types.ts +++ b/packages/state-manager/src/sagas/messages/messages.types.ts @@ -28,7 +28,7 @@ export interface PushNotificationPayload { export interface WriteMessagePayload { message: string id?: string - channelAddress?: string + channelId?: string type?: MessageType media?: FileMetadata } @@ -39,17 +39,17 @@ export interface PublicKeyMappingPayload { } export interface AddPublicChannelsMessagesBasePayload { - channelAddress: string + channelId: string } export interface PublicChannelsMessagesBase { - channelAddress: string + channelId: string messages: EntityState display: number } export interface SetDisplayedMessagesNumberPayload { - channelAddress: string + channelId: string display: number } @@ -71,20 +71,20 @@ export interface MessageSendingStatus { export interface AskForMessagesPayload { ids: string[] peerId: string - channelAddress: string + channelId: string communityId: string } export interface ChannelMessagesIdsResponse { ids: string[] - channelAddress: string + channelId: string communityId: string } export interface DeleteChannelEntryPayload { - channelAddress: string + channelId: string } export interface SendDeletionMessagePayload { - channelAddress: string + channelId: string } diff --git a/packages/state-manager/src/sagas/messages/sendDeletionMessage/sendDeletionMessage.saga.test.ts b/packages/state-manager/src/sagas/messages/sendDeletionMessage/sendDeletionMessage.saga.test.ts index 51f9625325..021b17a9c7 100644 --- a/packages/state-manager/src/sagas/messages/sendDeletionMessage/sendDeletionMessage.saga.test.ts +++ b/packages/state-manager/src/sagas/messages/sendDeletionMessage/sendDeletionMessage.saga.test.ts @@ -12,7 +12,9 @@ import { DateTime } from 'luxon' import { messagesActions } from '../../messages/messages.slice' import { publicChannelsActions } from '../../publicChannels/publicChannels.slice' import { sendDeletionMessageSaga } from './sendDeletionMessage.saga' +import { generateChannelId } from '@quiet/common' import { Community, Identity, MessageType, PublicChannel, WriteMessagePayload } from '@quiet/types' +import { publicChannelsSelectors } from '../../publicChannels/publicChannels.selectors' describe('sendDeletionMessage', () => { let store: Store @@ -22,6 +24,7 @@ describe('sendDeletionMessage', () => { let owner: Identity let photoChannel: PublicChannel + let generalChannel: PublicChannel beforeAll(async () => { setupCrypto() @@ -38,6 +41,10 @@ describe('sendDeletionMessage', () => { { id: community.id, nickname: 'alice' } ) + const generalChannelState = publicChannelsSelectors.generalChannel(store.getState()) + if (generalChannelState) generalChannel = generalChannelState + expect(generalChannel).not.toBeUndefined() + photoChannel = ( await factory.create['payload']>( 'PublicChannel', @@ -47,7 +54,7 @@ describe('sendDeletionMessage', () => { description: 'Welcome to #photo', timestamp: DateTime.utc().valueOf(), owner: owner.nickname, - address: 'photo' + id: generateChannelId('photo') } } ) @@ -55,18 +62,18 @@ describe('sendDeletionMessage', () => { }) test('send message after deletion standard channel', async () => { - const channelAddress = photoChannel.address - const message = `@${owner.nickname} deleted #${channelAddress}` + const channelId = photoChannel.id + const message = `@${owner.nickname} deleted #${photoChannel.name}` const messagePayload: WriteMessagePayload = { type: MessageType.Info, message, - channelAddress: 'general' + channelId: generalChannel.id } const reducer = combineReducers(reducers) await expectSaga( sendDeletionMessageSaga, messagesActions.sendDeletionMessage({ - channelAddress + channelId }) ) .withReducer(reducer) @@ -76,13 +83,13 @@ describe('sendDeletionMessage', () => { }) test('not send message after deletion general channel', async () => { - const channelAddress = 'general' + const channelId = 'general' const reducer = combineReducers(reducers) await expectSaga( sendDeletionMessageSaga, messagesActions.sendDeletionMessage({ - channelAddress + channelId }) ) .withReducer(reducer) diff --git a/packages/state-manager/src/sagas/messages/sendDeletionMessage/sendDeletionMessage.saga.ts b/packages/state-manager/src/sagas/messages/sendDeletionMessage/sendDeletionMessage.saga.ts index 28d3896bc2..a8632bafbf 100644 --- a/packages/state-manager/src/sagas/messages/sendDeletionMessage/sendDeletionMessage.saga.ts +++ b/packages/state-manager/src/sagas/messages/sendDeletionMessage/sendDeletionMessage.saga.ts @@ -1,14 +1,18 @@ import { PayloadAction } from '@reduxjs/toolkit' import { put, select } from 'typed-redux-saga' import { communitiesSelectors } from '../../communities/communities.selectors' +import { publicChannelsSelectors } from '../../publicChannels/publicChannels.selectors' import { messagesActions } from '../messages.slice' import { MessageType, WriteMessagePayload } from '@quiet/types' export function* sendDeletionMessageSaga( action: PayloadAction['payload']> ): Generator { - const { channelAddress } = action.payload - const isGeneral = channelAddress === 'general' + const { channelId } = action.payload + const generalChannel = yield* select(publicChannelsSelectors.generalChannel) + if (!generalChannel) return + + const isGeneral = channelId === generalChannel.id const ownerNickname = yield* select(communitiesSelectors.ownerNickname) @@ -18,8 +22,8 @@ export function* sendDeletionMessageSaga( const payload: WriteMessagePayload = { type: MessageType.Info, - message: `@${ownerNickname} deleted #${channelAddress}`, - channelAddress: 'general' + message: `@${ownerNickname} deleted #${channelId.slice(0, channelId.indexOf('_'))}`, // TEMPORARY + channelId: generalChannel.id } if (isOwner && !isGeneral) { diff --git a/packages/state-manager/src/sagas/messages/sendMessage/sendMessage.saga.test.ts b/packages/state-manager/src/sagas/messages/sendMessage/sendMessage.saga.test.ts index 48ddadca96..84d56034b6 100644 --- a/packages/state-manager/src/sagas/messages/sendMessage/sendMessage.saga.test.ts +++ b/packages/state-manager/src/sagas/messages/sendMessage/sendMessage.saga.test.ts @@ -19,10 +19,20 @@ import { messagesActions } from '../messages.slice' import { generateMessageId, getCurrentTime } from '../utils/message.utils' import { sendMessageSaga } from './sendMessage.saga' import { FactoryGirl } from 'factory-girl' -import { currentChannelAddress } from '../../publicChannels/publicChannels.selectors' + +import { generateChannelId } from '@quiet/common' + import { publicChannelsActions } from '../../publicChannels/publicChannels.slice' import { DateTime } from 'luxon' -import { Community, FileMetadata, Identity, MessageType, PublicChannel, SocketActionTypes } from '@quiet/types' +import { + Community, + FileMetadata, + Identity, + MessageType, + PublicChannel, + SocketActionTypes +} from '@quiet/types' +import { currentChannelId } from '../../publicChannels/publicChannels.selectors' describe('sendMessageSaga', () => { let store: Store @@ -41,7 +51,7 @@ describe('sendMessageSaga', () => { factory = await getFactory(store) community = await factory.create< - ReturnType['payload'] + ReturnType['payload'] >('Community') alice = await factory.create['payload']>( @@ -49,31 +59,29 @@ describe('sendMessageSaga', () => { { id: community.id, nickname: 'alice' } ) - sailingChannel = (await factory.create['payload']>( - 'PublicChannel', - { - channel: { - name: 'sailing', - description: 'Welcome to #sailing', - timestamp: DateTime.utc().valueOf(), - owner: alice.nickname, - address: 'sailing' + sailingChannel = ( + await factory.create['payload']>( + 'PublicChannel', + { + channel: { + name: 'sailing', + description: 'Welcome to #sailing', + timestamp: DateTime.utc().valueOf(), + owner: alice.nickname, + id: generateChannelId('sailing') + } } - } - )).channel + ) + ).channel }) test('sign and send message in current channel', async () => { const socket = { emit: jest.fn() } as unknown as Socket - const currentChannel = currentChannelAddress(store.getState()) + const currentChannel = currentChannelId(store.getState()) const reducer = combineReducers(reducers) - await expectSaga( - sendMessageSaga, - socket, - messagesActions.sendMessage({ message: 'message' }) - ) + await expectSaga(sendMessageSaga, socket, messagesActions.sendMessage({ message: 'message' })) .withReducer(reducer) .withState(store.getState()) .provide([ @@ -94,7 +102,7 @@ describe('sendMessageSaga', () => { type: MessageType.Basic, message: 'message', createdAt: 8, - channelAddress: currentChannel, + channelId: currentChannel, signature: 'signature', pubKey: 'publicKey', media: undefined @@ -111,7 +119,7 @@ describe('sendMessageSaga', () => { await expectSaga( sendMessageSaga, socket, - messagesActions.sendMessage({ message: 'message', channelAddress: sailingChannel.address }) + messagesActions.sendMessage({ message: 'message', channelId: sailingChannel.id }) ) .withReducer(reducer) .withState(store.getState()) @@ -133,7 +141,7 @@ describe('sendMessageSaga', () => { type: MessageType.Basic, message: 'message', createdAt: 24, - channelAddress: sailingChannel.address, + channelId: sailingChannel.id, signature: 'signature', pubKey: 'publicKey', media: undefined @@ -147,7 +155,10 @@ describe('sendMessageSaga', () => { const socket = { emit: jest.fn() } as unknown as Socket const messageId = Math.random().toString(36).substr(2.9) - const currentChannel = currentChannelAddress(store.getState()) + const currentChannel = currentChannelId(store.getState()) + if (!currentChannel) { + throw new Error('no currentChannel') + } const media: FileMetadata = { cid: 'cid', @@ -156,7 +167,7 @@ describe('sendMessageSaga', () => { ext: 'ext', message: { id: messageId, - channelAddress: currentChannel + channelId: currentChannel } } @@ -186,7 +197,7 @@ describe('sendMessageSaga', () => { type: MessageType.Basic, message: 'message', createdAt: 8, - channelAddress: currentChannel, + channelId: currentChannel, signature: 'signature', pubKey: 'publicKey', media: undefined diff --git a/packages/state-manager/src/sagas/messages/sendMessage/sendMessage.saga.ts b/packages/state-manager/src/sagas/messages/sendMessage/sendMessage.saga.ts index f9f5945377..8355207ea9 100644 --- a/packages/state-manager/src/sagas/messages/sendMessage/sendMessage.saga.ts +++ b/packages/state-manager/src/sagas/messages/sendMessage/sendMessage.saga.ts @@ -26,16 +26,16 @@ export function* sendMessageSaga( const signatureArrayBuffer = yield* call(sign, action.payload.message, keyObject) const signature = yield* call(arrayBufferToString, signatureArrayBuffer) - const currentChannel = yield* select(publicChannelsSelectors.currentChannelAddress) + const currentChannelId = yield* select(publicChannelsSelectors.currentChannelId) const createdAt = yield* call(getCurrentTime) const generatedMessageId = yield* call(generateMessageId) const id = action.payload.id || generatedMessageId - const channelAddress = action.payload.channelAddress || currentChannel - if (!channelAddress) { - console.error(`Could not send message with id ${id}, no channel address`) + const channelId = action.payload.channelId || currentChannelId + if (!channelId) { + console.error(`Could not send message with id ${id}, no channel id`) return } @@ -45,7 +45,7 @@ export function* sendMessageSaga( message: action.payload.message, media: action.payload.media, createdAt, - channelAddress, + channelId, signature, pubKey } diff --git a/packages/state-manager/src/sagas/publicChannels/__snapshots__/publicChannels.selectors.test.ts.snap b/packages/state-manager/src/sagas/publicChannels/__snapshots__/publicChannels.selectors.test.ts.snap index 68ca5b14cb..c1e2062753 100644 --- a/packages/state-manager/src/sagas/publicChannels/__snapshots__/publicChannels.selectors.test.ts.snap +++ b/packages/state-manager/src/sagas/publicChannels/__snapshots__/publicChannels.selectors.test.ts.snap @@ -2,7 +2,7 @@ exports[`publicChannelsSelectors get messages sorted by date 1`] = ` Object { - "channelAddress": "general", + "channelId": "general_ec4bca1fa76046c53dff1e49979c3647", "createdAt": Any, "id": "1", "message": "message_1", @@ -14,7 +14,7 @@ Object { exports[`publicChannelsSelectors get messages sorted by date 2`] = ` Object { - "channelAddress": "general", + "channelId": "general_ec4bca1fa76046c53dff1e49979c3647", "createdAt": Any, "id": "2", "message": "message_2", @@ -26,7 +26,7 @@ Object { exports[`publicChannelsSelectors get messages sorted by date 3`] = ` Object { - "channelAddress": "general", + "channelId": "general_ec4bca1fa76046c53dff1e49979c3647", "createdAt": Any, "id": "3", "message": "message_3", @@ -38,7 +38,7 @@ Object { exports[`publicChannelsSelectors get messages sorted by date 4`] = ` Object { - "channelAddress": "general", + "channelId": "general_ec4bca1fa76046c53dff1e49979c3647", "createdAt": Any, "id": "4", "message": "message_4", @@ -50,7 +50,7 @@ Object { exports[`publicChannelsSelectors get messages sorted by date 5`] = ` Object { - "channelAddress": "general", + "channelId": "general_ec4bca1fa76046c53dff1e49979c3647", "createdAt": Any, "id": "5", "message": "message_5", @@ -62,7 +62,7 @@ Object { exports[`publicChannelsSelectors get messages sorted by date 6`] = ` Object { - "channelAddress": "general", + "channelId": "general_ec4bca1fa76046c53dff1e49979c3647", "createdAt": Any, "id": "6", "message": "message_6", @@ -74,7 +74,7 @@ Object { exports[`publicChannelsSelectors get messages sorted by date 7`] = ` Object { - "channelAddress": "general", + "channelId": "general_ec4bca1fa76046c53dff1e49979c3647", "createdAt": Any, "id": "7", "message": "message_7", @@ -86,7 +86,7 @@ Object { exports[`publicChannelsSelectors get messages sorted by date 8`] = ` Object { - "channelAddress": "general", + "channelId": "general_ec4bca1fa76046c53dff1e49979c3647", "createdAt": Any, "id": "8", "message": "message_8", @@ -98,7 +98,7 @@ Object { exports[`publicChannelsSelectors get messages sorted by date 9`] = ` Object { - "channelAddress": "general", + "channelId": "general_ec4bca1fa76046c53dff1e49979c3647", "createdAt": Any, "id": "9", "message": "message_9", diff --git a/packages/state-manager/src/sagas/publicChannels/channelDeletionResponse/channelDeletionResponse.saga.test.ts b/packages/state-manager/src/sagas/publicChannels/channelDeletionResponse/channelDeletionResponse.saga.test.ts index caa07b7d86..8a2dffc4fb 100644 --- a/packages/state-manager/src/sagas/publicChannels/channelDeletionResponse/channelDeletionResponse.saga.test.ts +++ b/packages/state-manager/src/sagas/publicChannels/channelDeletionResponse/channelDeletionResponse.saga.test.ts @@ -12,7 +12,12 @@ import { communitiesActions } from '../../communities/communities.slice' import { DateTime } from 'luxon' import { messagesActions } from '../../messages/messages.slice' import { channelDeletionResponseSaga } from './channelDeletionResponse.saga' +import { generateChannelId } from '@quiet/common' import { Community, Identity, PublicChannel } from '@quiet/types' +import { publicChannelsSelectors } from '../publicChannels.selectors' +import { select } from 'redux-saga-test-plan/matchers' + +const provideDelay = ({ fn }: any, next: () => any) => (fn.name === 'delayP' ? null : next()) describe('channelDeletionResponseSaga', () => { let store: Store @@ -22,6 +27,7 @@ describe('channelDeletionResponseSaga', () => { let owner: Identity let photoChannel: PublicChannel + let generalChannel: PublicChannel beforeAll(async () => { setupCrypto() @@ -38,6 +44,10 @@ describe('channelDeletionResponseSaga', () => { { id: community.id, nickname: 'alice' } ) + const generalChannelState = publicChannelsSelectors.generalChannel(store.getState()) + if (generalChannelState) generalChannel = generalChannelState + expect(generalChannel).not.toBeUndefined() + photoChannel = ( await factory.create['payload']>( 'PublicChannel', @@ -47,7 +57,7 @@ describe('channelDeletionResponseSaga', () => { description: 'Welcome to #photo', timestamp: DateTime.utc().valueOf(), owner: owner.nickname, - address: 'photo' + id: generateChannelId('photo') } } ) @@ -55,44 +65,42 @@ describe('channelDeletionResponseSaga', () => { }) describe('handle saga logic as owner of community', () => { test('delete standard channel', async () => { - const channelAddress = photoChannel.address + const channelId = photoChannel.id const reducer = combineReducers(reducers) await expectSaga( channelDeletionResponseSaga, publicChannelsActions.channelDeletionResponse({ - channel: channelAddress + channelId }) ) .withReducer(reducer) .withState(store.getState()) - .put(publicChannelsActions.clearMessagesCache({ channelAddress })) - .put(messagesActions.deleteChannelEntry({ channelAddress })) - .put(publicChannelsActions.deleteChannelFromStore({ channelAddress })) - .put(messagesActions.sendDeletionMessage({ channelAddress })) + .put(publicChannelsActions.clearMessagesCache({ channelId })) + .put(messagesActions.deleteChannelEntry({ channelId })) + .put(publicChannelsActions.deleteChannelFromStore({ channelId })) + .put(messagesActions.sendDeletionMessage({ channelId })) .run() }) test('delete general channel', async () => { - const channelAddress = 'general' + const channelId = generalChannel.id const reducer = combineReducers(reducers) await expectSaga( channelDeletionResponseSaga, publicChannelsActions.channelDeletionResponse({ - channel: channelAddress + channelId }) ) .withReducer(reducer) .withState(store.getState()) .put(publicChannelsActions.startGeneralRecreation()) - .put(publicChannelsActions.clearMessagesCache({ channelAddress })) - .put(messagesActions.deleteChannelEntry({ channelAddress })) - .put(publicChannelsActions.deleteChannelFromStore({ channelAddress })) - .provide({ - call: (effect, next) => {} - }) + .put(publicChannelsActions.clearMessagesCache({ channelId })) + .put(messagesActions.deleteChannelEntry({ channelId })) + .put(publicChannelsActions.deleteChannelFromStore({ channelId })) + .provide([{ call: provideDelay }]) .put(publicChannelsActions.createGeneralChannel()) .run() @@ -104,39 +112,53 @@ describe('channelDeletionResponseSaga', () => { store.dispatch(communitiesActions.updateCommunityData({ ...community, CA: null })) }) test('delete standard channel', async () => { - const channelAddress = photoChannel.address + const channelId = photoChannel.id const reducer = combineReducers(reducers) await expectSaga( channelDeletionResponseSaga, publicChannelsActions.channelDeletionResponse({ - channel: channelAddress + channelId }) ) .withReducer(reducer) .withState(store.getState()) - .put(publicChannelsActions.clearMessagesCache({ channelAddress })) - .put(messagesActions.deleteChannelEntry({ channelAddress })) - .put(publicChannelsActions.deleteChannelFromStore({ channelAddress })) + .put(publicChannelsActions.clearMessagesCache({ channelId })) + .put(messagesActions.deleteChannelEntry({ channelId })) + .put(publicChannelsActions.deleteChannelFromStore({ channelId })) .run() }) test('delete general channel', async () => { - const channelAddress = 'general' + const channelId = generalChannel.id const reducer = combineReducers(reducers) await expectSaga( channelDeletionResponseSaga, publicChannelsActions.channelDeletionResponse({ - channel: channelAddress + channelId }) ) .withReducer(reducer) .withState(store.getState()) .put(publicChannelsActions.startGeneralRecreation()) - .put(publicChannelsActions.clearMessagesCache({ channelAddress })) - .put(messagesActions.deleteChannelEntry({ channelAddress })) - .put(publicChannelsActions.deleteChannelFromStore({ channelAddress })) + .put(publicChannelsActions.clearMessagesCache({ channelId })) + .put(messagesActions.deleteChannelEntry({ channelId })) + .put(publicChannelsActions.deleteChannelFromStore({ channelId })) + .provide([ + { call: provideDelay }, + [ + select(publicChannelsSelectors.generalChannel), + { + name: 'general', + description: 'general_description', + owner: 'general_owner', + timestamp: 'general_timestamp', + id: channelId + } + ] + ]) + .put(publicChannelsActions.setCurrentChannel({ channelId: channelId })) .run() }) }) diff --git a/packages/state-manager/src/sagas/publicChannels/channelDeletionResponse/channelDeletionResponse.saga.ts b/packages/state-manager/src/sagas/publicChannels/channelDeletionResponse/channelDeletionResponse.saga.ts index 9d5c21e3de..552ad25b03 100644 --- a/packages/state-manager/src/sagas/publicChannels/channelDeletionResponse/channelDeletionResponse.saga.ts +++ b/packages/state-manager/src/sagas/publicChannels/channelDeletionResponse/channelDeletionResponse.saga.ts @@ -4,26 +4,31 @@ import logger from '../../../utils/logger' import { put, delay, select } from 'typed-redux-saga' import { messagesActions } from '../../messages/messages.slice' import { communitiesSelectors } from '../../communities/communities.selectors' +import { publicChannelsSelectors } from '../publicChannels.selectors' const log = logger('publicChannels') export function* channelDeletionResponseSaga( action: PayloadAction['payload']> ): Generator { - log(`Deleted channel ${action.payload.channel} saga`) + log(`Deleted channel ${action.payload.channelId} saga`) - const channelAddress = action.payload.channel - const isGeneral = channelAddress === 'general' + const { channelId } = action.payload + + const generalChannel = yield* select(publicChannelsSelectors.generalChannel) + if (!generalChannel) return + + const isGeneral = channelId === generalChannel.id if (isGeneral) { yield* put(publicChannelsActions.startGeneralRecreation()) } - yield* put(publicChannelsActions.clearMessagesCache({ channelAddress })) + yield* put(publicChannelsActions.clearMessagesCache({ channelId })) - yield* put(messagesActions.deleteChannelEntry({ channelAddress })) + yield* put(messagesActions.deleteChannelEntry({ channelId })) - yield* put(publicChannelsActions.deleteChannelFromStore({ channelAddress })) + yield* put(publicChannelsActions.deleteChannelFromStore({ channelId })) const community = yield* select(communitiesSelectors.currentCommunity) @@ -34,7 +39,14 @@ export function* channelDeletionResponseSaga( yield* delay(1000) yield* put(publicChannelsActions.createGeneralChannel()) } else { - yield* put(messagesActions.sendDeletionMessage({ channelAddress })) + yield* put(messagesActions.sendDeletionMessage({ channelId })) + } + } else { + if (isGeneral) { + yield* delay(3000) + const generalChannel = yield* select(publicChannelsSelectors.generalChannel) + if (!generalChannel) return + yield* put(publicChannelsActions.setCurrentChannel({ channelId: generalChannel.id })) } } } diff --git a/packages/state-manager/src/sagas/publicChannels/channelsReplicated/channelsReplicated.saga.test.ts b/packages/state-manager/src/sagas/publicChannels/channelsReplicated/channelsReplicated.saga.test.ts index d751dd9349..6e10c56625 100644 --- a/packages/state-manager/src/sagas/publicChannels/channelsReplicated/channelsReplicated.saga.test.ts +++ b/packages/state-manager/src/sagas/publicChannels/channelsReplicated/channelsReplicated.saga.test.ts @@ -1,7 +1,7 @@ import { setupCrypto } from '@quiet/identity' import { Store } from '../../store.types' import { prepareStore } from '../../../utils/tests/prepareStore' -import { getFactory, messages, publicChannels } from '../../..' +import { generateMessageFactoryContentWithId, getFactory, messages, publicChannels } from '../../..' import { FactoryGirl } from 'factory-girl' import { combineReducers } from 'redux' import { reducers } from '../../reducers' @@ -13,7 +13,8 @@ import { channelsReplicatedSaga } from './channelsReplicated.saga' import { DateTime } from 'luxon' import { publicChannelsSelectors } from '../publicChannels.selectors' import { messagesActions } from '../../messages/messages.slice' -import { Community, Identity, PublicChannel } from '@quiet/types' +import { Community, Identity, MessageType, PublicChannel } from '@quiet/types' +import { generateChannelId } from '@quiet/common' describe('channelsReplicatedSaga', () => { let store: Store @@ -22,8 +23,8 @@ describe('channelsReplicatedSaga', () => { let community: Community let alice: Identity - let generalChannel: PublicChannel | undefined - let generalChannelAddress: string + let generalChannel: PublicChannel + let sailingChannel: PublicChannel let photoChannel: PublicChannel @@ -42,10 +43,11 @@ describe('channelsReplicatedSaga', () => { { id: community.id, nickname: 'alice' } ) - generalChannel = publicChannelsSelectors.currentChannel(store.getState()) + const generalChannelState = publicChannelsSelectors.generalChannel(store.getState()) + if (generalChannelState) generalChannel = generalChannelState expect(generalChannel).not.toBeUndefined() - generalChannelAddress = generalChannel?.address || '' + store.dispatch(publicChannelsActions.setCurrentChannel({ channelId: generalChannel.id })) sailingChannel = ( await factory.build('PublicChannel', { communityId: community.id, @@ -54,7 +56,7 @@ describe('channelsReplicatedSaga', () => { description: 'Welcome to #sailing', timestamp: DateTime.utc().valueOf(), owner: 'owner', - address: 'sailing' + id: generateChannelId('sailing') } }) ).payload.channel @@ -67,19 +69,20 @@ describe('channelsReplicatedSaga', () => { description: 'Welcome to #photo', timestamp: DateTime.utc().valueOf(), owner: 'owner', - address: 'photo' + id: generateChannelId('photo') } }) ).payload.channel }) test('save replicated channels in local storage', async () => { + console.log({ generalChannel }) const reducer = combineReducers(reducers) await expectSaga( channelsReplicatedSaga, publicChannelsActions.channelsReplicated({ channels: { - [sailingChannel.address]: sailingChannel + [sailingChannel.id]: sailingChannel } }) ) @@ -99,8 +102,8 @@ describe('channelsReplicatedSaga', () => { channelsReplicatedSaga, publicChannelsActions.channelsReplicated({ channels: { - [generalChannelAddress]: generalChannel, - [sailingChannel.address]: sailingChannel + [generalChannel.id]: generalChannel, + [sailingChannel.id]: sailingChannel } }) ) @@ -108,7 +111,6 @@ describe('channelsReplicatedSaga', () => { .withState(store.getState()) .not.put( publicChannelsActions.addChannel({ - // @ts-expect-error channel: generalChannel }) ) @@ -126,7 +128,7 @@ describe('channelsReplicatedSaga', () => { channelsReplicatedSaga, publicChannelsActions.channelsReplicated({ channels: { - [sailingChannel.address]: sailingChannel + [sailingChannel.id]: sailingChannel } }) ) @@ -139,7 +141,7 @@ describe('channelsReplicatedSaga', () => { ) .put( messagesActions.addPublicChannelsMessagesBase({ - channelAddress: sailingChannel.address + channelId: sailingChannel.id }) ) .run() @@ -151,8 +153,8 @@ describe('channelsReplicatedSaga', () => { channelsReplicatedSaga, publicChannelsActions.channelsReplicated({ channels: { - [generalChannelAddress]: generalChannel, - [sailingChannel.address]: sailingChannel + [generalChannel.id]: generalChannel, + [sailingChannel.id]: sailingChannel } }) ) @@ -165,18 +167,17 @@ describe('channelsReplicatedSaga', () => { ) .put( messagesActions.addPublicChannelsMessagesBase({ - channelAddress: sailingChannel.address + channelId: sailingChannel.id }) ) .not.put( publicChannelsActions.addChannel({ - // @ts-expect-error channel: generalChannel }) ) .not.put( messagesActions.addPublicChannelsMessagesBase({ - channelAddress: generalChannelAddress + channelId: generalChannel.id }) ) .run() @@ -186,13 +187,14 @@ describe('channelsReplicatedSaga', () => { const message = await factory.create< ReturnType['payload'] >('Message', { - identity: alice + identity: alice, + message: generateMessageFactoryContentWithId(generalChannel.id) }) store.dispatch( publicChannels.actions.cacheMessages({ messages: [], - channelAddress: generalChannelAddress + channelId: generalChannel.id }) ) @@ -201,8 +203,8 @@ describe('channelsReplicatedSaga', () => { channelsReplicatedSaga, publicChannelsActions.channelsReplicated({ channels: { - [generalChannelAddress]: generalChannel, - [sailingChannel.address]: sailingChannel + [generalChannel.id]: generalChannel, + [sailingChannel.id]: sailingChannel } }) ) @@ -216,7 +218,8 @@ describe('channelsReplicatedSaga', () => { const message = await factory.create< ReturnType['payload'] >('Message', { - identity: alice + identity: alice, + message: generateMessageFactoryContentWithId(generalChannel.id) }) const reducer = combineReducers(reducers) @@ -224,8 +227,8 @@ describe('channelsReplicatedSaga', () => { channelsReplicatedSaga, publicChannelsActions.channelsReplicated({ channels: { - [generalChannelAddress]: generalChannel, - [sailingChannel.address]: sailingChannel + [generalChannel.id]: generalChannel, + [sailingChannel.id]: sailingChannel } }) ) @@ -243,8 +246,8 @@ describe('channelsReplicatedSaga', () => { channelsReplicatedSaga, publicChannelsActions.channelsReplicated({ channels: { - [generalChannelAddress]: generalChannel, - [sailingChannel.address]: sailingChannel + [generalChannel.id]: generalChannel, + [sailingChannel.id]: sailingChannel } }) ) @@ -255,7 +258,7 @@ describe('channelsReplicatedSaga', () => { channel: sailingChannel }) ) - .put(publicChannelsActions.deleteChannel({ channel: photoChannel.address })) + .put(publicChannelsActions.deleteChannel({ channelId: photoChannel.id })) .run() }) }) diff --git a/packages/state-manager/src/sagas/publicChannels/channelsReplicated/channelsReplicated.saga.ts b/packages/state-manager/src/sagas/publicChannels/channelsReplicated/channelsReplicated.saga.ts index af0bbe2b62..3de1845c55 100644 --- a/packages/state-manager/src/sagas/publicChannels/channelsReplicated/channelsReplicated.saga.ts +++ b/packages/state-manager/src/sagas/publicChannels/channelsReplicated/channelsReplicated.saga.ts @@ -7,22 +7,25 @@ import { messagesActions } from '../../messages/messages.slice' import logger from '@quiet/logger' import { isDefined } from '@quiet/common' +import { PublicChannel } from '@quiet/types' const log = logger('channels') export function* channelsReplicatedSaga( action: PayloadAction['payload']> ): Generator { log('INSIDE CHANNELS REPLICATED SAGA') - + const { channels } = action.payload const _locallyStoredChannels = yield* select(publicChannelsSelectors.publicChannels) - const locallyStoredChannels = _locallyStoredChannels.map(channel => channel.address) + const locallyStoredChannels = _locallyStoredChannels.map(channel => channel.id) + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + const databaseStoredChannels = Object.values(channels) as PublicChannel[] - const databaseStoredChannels = Object.values(action.payload.channels).filter(isDefined) - const databaseStoredChannelsAddresses = databaseStoredChannels.map(channel => channel.address) - console.log({ locallyStoredChannels, databaseStoredChannelsAddresses }) + const databaseStoredChannelsIds = databaseStoredChannels.map(channel => channel.id) + console.log({ locallyStoredChannels, databaseStoredChannelsIds }) // Upserting channels to local storage for (const channel of databaseStoredChannels) { - if (!locallyStoredChannels.includes(channel.address)) { + if (!locallyStoredChannels.includes(channel.id)) { log(`ADDING #${channel.name} TO LOCAL STORAGE`) yield* put( publicChannelsActions.addChannel({ @@ -31,18 +34,17 @@ export function* channelsReplicatedSaga( ) yield* put( messagesActions.addPublicChannelsMessagesBase({ - channelAddress: channel.address + channelId: channel.id }) ) } } // Removing channels from store - for (const channelAddress of locallyStoredChannels) { - if (!databaseStoredChannelsAddresses.includes(channelAddress)) { - console.log({ channelAddress }) - log(`REMOVING #${channelAddress} FROM STORE`) - yield* put(publicChannelsActions.deleteChannel({ channel: channelAddress })) + for (const channelId of locallyStoredChannels) { + if (!databaseStoredChannelsIds.includes(channelId)) { + log(`REMOVING #${channelId} FROM STORE`) + yield* put(publicChannelsActions.deleteChannel({ channelId })) } } diff --git a/packages/state-manager/src/sagas/publicChannels/createChannel/createChannel.saga.test.ts b/packages/state-manager/src/sagas/publicChannels/createChannel/createChannel.saga.test.ts index 80ca5c6741..45dd465d00 100644 --- a/packages/state-manager/src/sagas/publicChannels/createChannel/createChannel.saga.test.ts +++ b/packages/state-manager/src/sagas/publicChannels/createChannel/createChannel.saga.test.ts @@ -22,6 +22,7 @@ import { setupCrypto } from '@quiet/identity' import { prepareStore } from '../../../utils/tests/prepareStore' import { getFactory } from '../../../utils/tests/factories' import { PublicChannel, SocketActionTypes } from '@quiet/types' +import { generateChannelId } from '@quiet/common' describe('createChannelSaga', () => { let store: Store @@ -40,7 +41,7 @@ describe('createChannelSaga', () => { description: 'desc', owner: 'Howdy', timestamp: Date.now(), - address: 'address' + id: generateChannelId('general') } test('ask for missing messages', async () => { diff --git a/packages/state-manager/src/sagas/publicChannels/createGeneralChannel/createGeneralChannel.saga.test.ts b/packages/state-manager/src/sagas/publicChannels/createGeneralChannel/createGeneralChannel.saga.test.ts index 8716d9f14b..6e19e69244 100644 --- a/packages/state-manager/src/sagas/publicChannels/createGeneralChannel/createGeneralChannel.saga.test.ts +++ b/packages/state-manager/src/sagas/publicChannels/createGeneralChannel/createGeneralChannel.saga.test.ts @@ -10,6 +10,7 @@ import { call } from 'redux-saga-test-plan/matchers' import { publicChannelsActions } from './../publicChannels.slice' import { identityActions } from '../../identity/identity.slice' import { createGeneralChannelSaga, getChannelTimestamp } from './createGeneralChannel.saga' +import { generateChannelId } from '@quiet/common' import { communitiesActions } from '../../communities/communities.slice' import { Community, Identity } from '@quiet/types' @@ -27,7 +28,7 @@ describe('createGeneralChannelSaga', () => { factory = await getFactory(store) community = await factory.create< - ReturnType['payload'] + ReturnType['payload'] >('Community') alice = await factory.create['payload']>( @@ -38,26 +39,30 @@ describe('createGeneralChannelSaga', () => { test('create general channel', async () => { const reducer = combineReducers(reducers) + const generalId = generateChannelId('general') + const channel = { + name: 'general', + description: 'Welcome to #general', + owner: alice.nickname, + id: generalId, + timestamp: 0 + } + console.log({ channel }) await expectSaga(createGeneralChannelSaga) .withReducer(reducer) .withState(store.getState()) .provide([ - [call.fn(getChannelTimestamp), 0] + [call.fn(getChannelTimestamp), 0], + [call.fn(generateChannelId), generalId] ]) .put( publicChannelsActions.createChannel({ - channel: { - name: 'general', - description: 'Welcome to #general', - owner: alice.nickname, - address: 'general', - timestamp: 0 - } + channel }) ) .put( publicChannelsActions.setCurrentChannel({ - channelAddress: 'general' + channelId: generalId }) ) .run() diff --git a/packages/state-manager/src/sagas/publicChannels/createGeneralChannel/createGeneralChannel.saga.ts b/packages/state-manager/src/sagas/publicChannels/createGeneralChannel/createGeneralChannel.saga.ts index 9ae55f42d1..48f0038635 100644 --- a/packages/state-manager/src/sagas/publicChannels/createGeneralChannel/createGeneralChannel.saga.ts +++ b/packages/state-manager/src/sagas/publicChannels/createGeneralChannel/createGeneralChannel.saga.ts @@ -3,6 +3,7 @@ import { publicChannelsActions } from '../publicChannels.slice' import { identitySelectors } from '../../identity/identity.selectors' import { DateTime } from 'luxon' import logger from '../../../utils/logger' +import { generateChannelId } from '@quiet/common' import { PublicChannel } from '@quiet/types' const log = logger('publicChannels') @@ -15,13 +16,14 @@ export function* createGeneralChannelSaga(): Generator { } log(`Creating general channel for ${identity.nickname}`) - const timestamp: number = yield* call(getChannelTimestamp) + const timestamp = yield* call(getChannelTimestamp) + const id = yield* call(generateChannelId, 'general') const channel: PublicChannel = { name: 'general', description: 'Welcome to #general', owner: identity.nickname, - address: 'general', + id, timestamp: timestamp } @@ -33,7 +35,7 @@ export function* createGeneralChannelSaga(): Generator { yield* put( publicChannelsActions.setCurrentChannel({ - channelAddress: channel.address + channelId: channel.id }) ) } diff --git a/packages/state-manager/src/sagas/publicChannels/createGeneralChannel/sendInitialChannelMessage.saga.test.ts b/packages/state-manager/src/sagas/publicChannels/createGeneralChannel/sendInitialChannelMessage.saga.test.ts index 5ab8a2117e..a23f9ca43d 100644 --- a/packages/state-manager/src/sagas/publicChannels/createGeneralChannel/sendInitialChannelMessage.saga.test.ts +++ b/packages/state-manager/src/sagas/publicChannels/createGeneralChannel/sendInitialChannelMessage.saga.test.ts @@ -13,6 +13,7 @@ import { DateTime } from 'luxon' import { publicChannelsSelectors } from '../publicChannels.selectors' import { combineReducers } from '@reduxjs/toolkit' import { reducers } from '../../reducers' +import { generateChannelId } from '@quiet/common' import { Community, PublicChannel } from '@quiet/types' describe('sendInitialChannelMessageSaga', () => { @@ -21,7 +22,7 @@ describe('sendInitialChannelMessageSaga', () => { let channel: PublicChannel - let generalChannel: PublicChannel | undefined + let generalChannel: PublicChannel let community: Community let owner: Identity @@ -41,7 +42,8 @@ describe('sendInitialChannelMessageSaga', () => { { id: community.id, nickname: 'alice' } ) - generalChannel = publicChannelsSelectors.currentChannel(store.getState()) + const generalChannelState = publicChannelsSelectors.generalChannel(store.getState()) + if (generalChannelState) generalChannel = generalChannelState expect(generalChannel).not.toBeUndefined() channel = ( @@ -53,7 +55,7 @@ describe('sendInitialChannelMessageSaga', () => { description: 'Welcome to #photo', timestamp: DateTime.utc().valueOf(), owner: owner.nickname, - address: 'photo' + id: generateChannelId('photo') } } ) @@ -66,7 +68,7 @@ describe('sendInitialChannelMessageSaga', () => { sendInitialChannelMessageSaga, publicChannelsActions.sendInitialChannelMessage({ channelName: channel.name, - channelAddress: channel.address + channelId: channel.id }) ) .withReducer(reducer) @@ -75,7 +77,7 @@ describe('sendInitialChannelMessageSaga', () => { messagesActions.sendMessage({ type: 3, message: `Created #${channel.name}`, - channelAddress: channel.address + channelId: channel.id }) ) .run() @@ -87,10 +89,8 @@ describe('sendInitialChannelMessageSaga', () => { await expectSaga( sendInitialChannelMessageSaga, publicChannelsActions.sendInitialChannelMessage({ - // @ts-expect-error channelName: generalChannel.name, - // @ts-expect-error - channelAddress: generalChannel.address + channelId: generalChannel.id }) ) .withReducer(reducer) @@ -99,8 +99,7 @@ describe('sendInitialChannelMessageSaga', () => { messagesActions.sendMessage({ type: 3, message: `@${owner.nickname} deleted all messages in #general`, - // @ts-expect-error - channelAddress: generalChannel.address + channelId: generalChannel.id }) ) .run() diff --git a/packages/state-manager/src/sagas/publicChannels/createGeneralChannel/sendInitialChannelMessage.saga.ts b/packages/state-manager/src/sagas/publicChannels/createGeneralChannel/sendInitialChannelMessage.saga.ts index 65cf2bb9b3..7d042e2fc3 100644 --- a/packages/state-manager/src/sagas/publicChannels/createGeneralChannel/sendInitialChannelMessage.saga.ts +++ b/packages/state-manager/src/sagas/publicChannels/createGeneralChannel/sendInitialChannelMessage.saga.ts @@ -11,8 +11,10 @@ export function* sendInitialChannelMessageSaga( ReturnType['payload'] > ): Generator { - const { channelName, channelAddress } = action.payload - const isGeneral = channelAddress === 'general' + const { channelName, channelId } = action.payload + const generalChannel = yield* select(publicChannelsSelectors.generalChannel) + if (!generalChannel) return + const isGeneral = channelId === generalChannel.id const pendingGeneralChannelRecreation = yield* select( publicChannelsSelectors.pendingGeneralChannelRecreation @@ -28,7 +30,7 @@ export function* sendInitialChannelMessageSaga( const payload: WriteMessagePayload = { type: MessageType.Info, message, - channelAddress: channelAddress + channelId: channelId } if (isGeneral) { diff --git a/packages/state-manager/src/sagas/publicChannels/deleteChannel/deleteChannel.saga.test.ts b/packages/state-manager/src/sagas/publicChannels/deleteChannel/deleteChannel.saga.test.ts index f2a901406e..b869ea92f1 100644 --- a/packages/state-manager/src/sagas/publicChannels/deleteChannel/deleteChannel.saga.test.ts +++ b/packages/state-manager/src/sagas/publicChannels/deleteChannel/deleteChannel.saga.test.ts @@ -12,8 +12,10 @@ import { communitiesActions } from '../../communities/communities.slice' import { DateTime } from 'luxon' import { deleteChannelSaga } from './deleteChannel.saga' import { Socket } from 'socket.io-client' +import { generateChannelId } from '@quiet/common' import { filesActions } from '../../files/files.slice' import { Community, Identity, PublicChannel, SocketActionTypes } from '@quiet/types' +import { publicChannelsSelectors } from '../publicChannels.selectors' describe('deleteChannelSaga', () => { let store: Store @@ -23,6 +25,7 @@ describe('deleteChannelSaga', () => { let owner: Identity let photoChannel: PublicChannel + let generalChannel: PublicChannel const socket = { emit: jest.fn(), on: jest.fn() } as unknown as Socket @@ -41,6 +44,10 @@ describe('deleteChannelSaga', () => { { id: community.id, nickname: 'alice' } ) + const generalChannelState = publicChannelsSelectors.generalChannel(store.getState()) + if (generalChannelState) generalChannel = generalChannelState + expect(generalChannel).not.toBeUndefined() + photoChannel = ( await factory.create['payload']>( 'PublicChannel', @@ -50,7 +57,7 @@ describe('deleteChannelSaga', () => { description: 'Welcome to #photo', timestamp: DateTime.utc().valueOf(), owner: owner.nickname, - address: 'photo' + id: generateChannelId('photo') } } ) @@ -58,46 +65,38 @@ describe('deleteChannelSaga', () => { }) test('delete standard channel', async () => { - const channelAddress = photoChannel.address - + console.log({ generalChannel }) + const channelId = photoChannel.id + store.dispatch(publicChannelsActions.setCurrentChannel({ channelId })) const reducer = combineReducers(reducers) - await expectSaga( - deleteChannelSaga, - socket, - publicChannelsActions.deleteChannel({ channel: channelAddress }) - ) + await expectSaga(deleteChannelSaga, socket, publicChannelsActions.deleteChannel({ channelId })) .withReducer(reducer) .withState(store.getState()) .apply(socket, socket.emit, [ SocketActionTypes.DELETE_CHANNEL, { - channel: channelAddress + channelId } ]) - .put(filesActions.deleteFilesFromChannel({ channelAddress })) - .put(publicChannelsActions.setCurrentChannel({ channelAddress: 'general' })) - .put(publicChannelsActions.disableChannel({ channelAddress })) + .put(publicChannelsActions.setCurrentChannel({ channelId: generalChannel.id })) + .put(publicChannelsActions.disableChannel({ channelId })) .run() }) test('delete general channel', async () => { - const channelAddress = 'general' + const channelId = generalChannel.id const reducer = combineReducers(reducers) - await expectSaga( - deleteChannelSaga, - socket, - publicChannelsActions.deleteChannel({ channel: channelAddress }) - ) + await expectSaga(deleteChannelSaga, socket, publicChannelsActions.deleteChannel({ channelId })) .withReducer(reducer) .withState(store.getState()) .apply(socket, socket.emit, [ SocketActionTypes.DELETE_CHANNEL, { - channel: channelAddress + channelId } ]) - .put(filesActions.deleteFilesFromChannel({ channelAddress })) + .put(filesActions.deleteFilesFromChannel({ channelId })) .run() }) }) diff --git a/packages/state-manager/src/sagas/publicChannels/deleteChannel/deleteChannel.saga.ts b/packages/state-manager/src/sagas/publicChannels/deleteChannel/deleteChannel.saga.ts index 24e4a980ce..1d3c6503b6 100644 --- a/packages/state-manager/src/sagas/publicChannels/deleteChannel/deleteChannel.saga.ts +++ b/packages/state-manager/src/sagas/publicChannels/deleteChannel/deleteChannel.saga.ts @@ -1,10 +1,11 @@ import { PayloadAction } from '@reduxjs/toolkit' import { publicChannelsActions } from '../publicChannels.slice' -import { apply, put } from 'typed-redux-saga' +import { apply, put, select } from 'typed-redux-saga' import { Socket, applyEmitParams } from '../../../types' import logger from '../../../utils/logger' import { filesActions } from '../../files/files.slice' import { SocketActionTypes } from '@quiet/types' +import { publicChannelsSelectors } from '../publicChannels.selectors' const log = logger('publicChannels') @@ -12,22 +13,30 @@ export function* deleteChannelSaga( socket: Socket, action: PayloadAction['payload']> ): Generator { - const channelAddress = action.payload.channel - const isGeneral = channelAddress === 'general' + const channelId = action.payload.channelId + const generalChannel = yield* select(publicChannelsSelectors.generalChannel) + const currentChannelId = yield* select(publicChannelsSelectors.currentChannelId) + if (generalChannel === undefined) { + return + } + + const isGeneral = channelId === generalChannel.id - log(`Deleting channel ${channelAddress}`) + log(`Deleting channel ${channelId}`) yield* apply( socket, socket.emit, applyEmitParams(SocketActionTypes.DELETE_CHANNEL, { - channel: channelAddress + channelId }) ) - yield* put(filesActions.deleteFilesFromChannel({ channelAddress })) + yield* put(filesActions.deleteFilesFromChannel({ channelId })) if (!isGeneral) { - yield* put(publicChannelsActions.setCurrentChannel({ channelAddress: 'general' })) - yield* put(publicChannelsActions.disableChannel({ channelAddress })) + if (currentChannelId === channelId) { + yield* put(publicChannelsActions.setCurrentChannel({ channelId: generalChannel.id })) + } + yield* put(publicChannelsActions.disableChannel({ channelId })) } } diff --git a/packages/state-manager/src/sagas/publicChannels/markUnreadChannels/markUnreadChannels.saga.test.ts b/packages/state-manager/src/sagas/publicChannels/markUnreadChannels/markUnreadChannels.saga.test.ts index 5b3b80465c..beabd446f1 100644 --- a/packages/state-manager/src/sagas/publicChannels/markUnreadChannels/markUnreadChannels.saga.test.ts +++ b/packages/state-manager/src/sagas/publicChannels/markUnreadChannels/markUnreadChannels.saga.test.ts @@ -11,6 +11,7 @@ import { identityActions } from '../../identity/identity.slice' import { DateTime } from 'luxon' import { markUnreadChannelsSaga } from './markUnreadChannels.saga' import { messagesActions } from '../../messages/messages.slice' +import { generateChannelId } from '@quiet/common' import { ChannelMessage, Community, Identity, MessageType } from '@quiet/types' describe('markUnreadChannelsSaga', () => { @@ -20,6 +21,8 @@ describe('markUnreadChannelsSaga', () => { let community: Community let alice: Identity + let channelIds: string[] = [] + beforeAll(async () => { setupCrypto() @@ -40,27 +43,27 @@ describe('markUnreadChannelsSaga', () => { // Automatically create channels for (const name of channelNames) { - await factory.create['payload']>( - 'PublicChannel', - { - channel: { - name: name, - description: `Welcome to #${name}`, - timestamp: DateTime.utc().valueOf(), - owner: alice.nickname, - address: name - } + const channel = await factory.create< + ReturnType['payload'] + >('PublicChannel', { + channel: { + name: name, + description: `Welcome to #${name}`, + timestamp: DateTime.utc().valueOf(), + owner: alice.nickname, + id: generateChannelId(name) } - ) + }) + channelIds = [...channelIds, channel.channel.id] } }) test('mark unread channels', async () => { - const messagesAddresses = ['general', 'memes', 'enya', 'travels'] + const messagesides = channelIds const messages: ChannelMessage[] = [] // Automatically create messages - for (const address of messagesAddresses) { + for (const id of messagesides) { const message = ( await factory.build('Message', { identity: alice, @@ -69,7 +72,7 @@ describe('markUnreadChannelsSaga', () => { type: MessageType.Basic, message: 'message', createdAt: DateTime.utc().valueOf(), - channelAddress: address, + channelId: id, signature: '', pubKey: '' }, @@ -80,6 +83,8 @@ describe('markUnreadChannelsSaga', () => { } // Set the newest message + const channelId = channelIds.find(id => id.includes('enya')) + if (!channelId) throw new Error('no channel id') const message = ( await factory.create['payload']>( 'Message', @@ -90,7 +95,7 @@ describe('markUnreadChannelsSaga', () => { type: MessageType.Basic, message: 'message', createdAt: 99999999999999, - channelAddress: 'enya', + channelId, signature: '', pubKey: '' }, @@ -101,6 +106,13 @@ describe('markUnreadChannelsSaga', () => { store.dispatch(publicChannelsActions.updateNewestMessage({ message })) + const channelIdMemes = channelIds.find(id => id.includes('memes')) + + const channelIdEnya = channelIds.find(id => id.includes('enya')) + + const channelIdTravels = channelIds.find(id => id.includes('travels')) + if (!channelIdMemes || !channelIdEnya || !channelIdTravels) throw new Error('no channel id') + const reducer = combineReducers(reducers) await expectSaga( markUnreadChannelsSaga, @@ -112,19 +124,19 @@ describe('markUnreadChannelsSaga', () => { .withState(store.getState()) .put( publicChannelsActions.markUnreadChannel({ - channelAddress: 'memes', - message: messages[1] + channelId: channelIdMemes, + message: messages[0] }) ) .not.put( publicChannelsActions.markUnreadChannel({ - channelAddress: 'enya', + channelId: channelIdEnya, message: messages[2] }) ) .put( publicChannelsActions.markUnreadChannel({ - channelAddress: 'travels', + channelId: channelIdTravels, message: messages[3] }) ) @@ -132,20 +144,19 @@ describe('markUnreadChannelsSaga', () => { }) test('do not mark unread channels if message is older than user', async () => { - const messagesAddresses = ['general', 'memes', 'enya', 'travels'] + const messagesides = channelIds const messages: ChannelMessage[] = [] const community = await factory.create< - ReturnType['payload'] - >('Community') + ReturnType['payload'] + >('Community') - const alice = await factory.create['payload']>( - 'Identity', - { id: community.id, nickname: 'alice', joinTimestamp: 9239423949 } - ) + const alice = await factory.create< + ReturnType['payload'] + >('Identity', { id: community.id, nickname: 'alice', joinTimestamp: 9239423949 }) // Automatically create older messages - for (const address of messagesAddresses) { + for (const id of messagesides) { const message = ( await factory.build('Message', { identity: alice, @@ -154,7 +165,7 @@ describe('markUnreadChannelsSaga', () => { type: MessageType.Basic, message: 'message', createdAt: 123, - channelAddress: address, + channelId: id, signature: '', pubKey: '' }, @@ -164,6 +175,8 @@ describe('markUnreadChannelsSaga', () => { messages.push(message) } + const channelId = channelIds.find(id => id.includes('enya')) + if (!channelId) throw new Error('no channel id') // Set the newest message const message = ( await factory.create['payload']>( @@ -175,7 +188,7 @@ describe('markUnreadChannelsSaga', () => { type: MessageType.Basic, message: 'message', createdAt: 99999999999999, - channelAddress: 'enya', + channelId, signature: '', pubKey: '' }, @@ -186,6 +199,12 @@ describe('markUnreadChannelsSaga', () => { messages.push(message) + const channelIdMemes = channelIds.find(id => id.includes('memes')) + + const channelIdEnya = channelIds.find(id => id.includes('enya')) + + const channelIdTravels = channelIds.find(id => id.includes('travels')) + if (!channelIdMemes || !channelIdEnya || !channelIdTravels) throw new Error('no channel id') const reducer = combineReducers(reducers) await expectSaga( markUnreadChannelsSaga, @@ -197,19 +216,19 @@ describe('markUnreadChannelsSaga', () => { .withState(store.getState()) .not.put( publicChannelsActions.markUnreadChannel({ - channelAddress: 'memes', + channelId: channelIdMemes, message: messages[1] }) ) .put( publicChannelsActions.markUnreadChannel({ - channelAddress: 'enya', + channelId: channelIdEnya, message: message }) ) .not.put( publicChannelsActions.markUnreadChannel({ - channelAddress: 'travels', + channelId: channelIdTravels, message: messages[3] }) ) diff --git a/packages/state-manager/src/sagas/publicChannels/markUnreadChannels/markUnreadChannels.saga.ts b/packages/state-manager/src/sagas/publicChannels/markUnreadChannels/markUnreadChannels.saga.ts index 0a10137102..dbeb4cdbad 100644 --- a/packages/state-manager/src/sagas/publicChannels/markUnreadChannels/markUnreadChannels.saga.ts +++ b/packages/state-manager/src/sagas/publicChannels/markUnreadChannels/markUnreadChannels.saga.ts @@ -9,15 +9,15 @@ import { MarkUnreadChannelPayload } from '@quiet/types' export function* markUnreadChannelsSaga( action: PayloadAction['payload']> ): Generator { - const currentChannelAddress = yield* select(publicChannelsSelectors.currentChannelAddress) + const currentChannelId = yield* select(publicChannelsSelectors.currentChannelId) const { messages } = action.payload for (const message of messages) { // Do not proceed for current channel - if (message.channelAddress !== currentChannelAddress) { + if (message.channelId !== currentChannelId) { const payload: MarkUnreadChannelPayload = { - channelAddress: message.channelAddress, + channelId: message.channelId, message: message } @@ -28,7 +28,7 @@ export function* markUnreadChannelsSaga( // For all messages created before user joined don't show notifications if (!joinTimestamp || joinTimestamp > message.createdAt * 1000) continue // If there are newer messages in the channel, don't show notification - const newestMessage = statuses[message.channelAddress]?.newestMessage + const newestMessage = statuses[message.channelId]?.newestMessage if (newestMessage?.createdAt && newestMessage.createdAt > message.createdAt) continue yield* put( publicChannelsActions.markUnreadChannel(payload) @@ -40,13 +40,13 @@ export function* markUnreadChannelsSaga( export function* clearUnreadChannelsSaga( action: PayloadAction['payload']> ): Generator { - const { channelAddress } = action.payload + const { channelId } = action.payload // Do not proceed with invalid channel - if (channelAddress === '') return + if (channelId === '') return const payload: MarkUnreadChannelPayload = { - channelAddress: channelAddress + channelId: channelId } yield* put( diff --git a/packages/state-manager/src/sagas/publicChannels/publicChannels.adapter.ts b/packages/state-manager/src/sagas/publicChannels/publicChannels.adapter.ts index 2ff9715be3..f52eac255f 100644 --- a/packages/state-manager/src/sagas/publicChannels/publicChannels.adapter.ts +++ b/packages/state-manager/src/sagas/publicChannels/publicChannels.adapter.ts @@ -2,15 +2,15 @@ import { ChannelMessage, PublicChannelStatus, PublicChannelStorage, PublicChanne import { createEntityAdapter } from '@reduxjs/toolkit' export const publicChannelsAdapter = createEntityAdapter({ - selectId: (channel) => channel.address + selectId: (channel) => channel.id }) export const publicChannelsStatusAdapter = createEntityAdapter({ - selectId: (channel) => channel.address + selectId: (channel) => channel.id }) export const publicChannelsSubscriptionsAdapter = createEntityAdapter({ - selectId: (channel) => channel.address + selectId: (channel) => channel.id }) export const channelMessagesAdapter = createEntityAdapter() diff --git a/packages/state-manager/src/sagas/publicChannels/publicChannels.selectors.test.ts b/packages/state-manager/src/sagas/publicChannels/publicChannels.selectors.test.ts index 7662b603b4..13cd30d064 100644 --- a/packages/state-manager/src/sagas/publicChannels/publicChannels.selectors.test.ts +++ b/packages/state-manager/src/sagas/publicChannels/publicChannels.selectors.test.ts @@ -11,7 +11,7 @@ import { publicChannelsSelectors } from './publicChannels.selectors' import { publicChannelsActions } from './publicChannels.slice' -import { communitiesActions } from '../communities/communities.slice' + import { identityActions } from '../identity/identity.slice' import { usersActions } from '../users/users.slice' import { @@ -20,7 +20,16 @@ import { } from '../../utils/functions/dates/formatMessageDisplayDate' import { displayableMessage } from '../../utils/functions/dates/formatDisplayableMessage' import { DateTime } from 'luxon' -import { ChannelMessage, Community, DisplayableMessage, Identity, MessageType } from '@quiet/types' +import { generateChannelId } from '@quiet/common' +import { + ChannelMessage, + Community, + DisplayableMessage, + Identity, + MessageType, + PublicChannel +} from '@quiet/types' +import { communitiesActions } from '../communities/communities.slice' describe('publicChannelsSelectors', () => { let store: Store @@ -30,6 +39,9 @@ describe('publicChannelsSelectors', () => { let alice: Identity let john: Identity + let generalChannel: PublicChannel + let channelIdes: string[] = [] + const msgs: { [id: string]: ChannelMessage } = {} const msgsOwners: { [id: string]: string } = {} @@ -44,35 +56,38 @@ describe('publicChannelsSelectors', () => { factory = await getFactory(store) community = await factory.create< - ReturnType['payload'] + ReturnType['payload'] >('Community') alice = await factory.create['payload']>( 'Identity', { id: community.id, nickname: 'alice' } ) - + const generalChannelState = publicChannelsSelectors.generalChannel(store.getState()) + if (generalChannelState) generalChannel = generalChannelState + expect(generalChannel).not.toBeUndefined() + channelIdes = [...channelIdes, generalChannel.id] john = await factory.create['payload']>( 'Identity', { id: community.id, nickname: 'john' } ) - + store.dispatch(publicChannelsActions.setCurrentChannel({ channelId: generalChannel.id })) // Setup channels const channelNames = ['croatia', 'allergies', 'sailing', 'pets', 'antiques'] for (const name of channelNames) { - await factory.create['payload']>( - 'PublicChannel', - { - channel: { - name: name, - description: `Welcome to #${name}`, - timestamp: DateTime.utc().valueOf(), - owner: alice.nickname, - address: name - } + const channel = await factory.create< + ReturnType['payload'] + >('PublicChannel', { + channel: { + name: name, + description: `Welcome to #${name}`, + timestamp: DateTime.utc().valueOf(), + owner: alice.nickname, + id: generateChannelId(name) } - ) + }) + channelIdes = [...channelIdes, channel.channel.id] } /* Messages ids are being used only for veryfing proper order... @@ -196,7 +211,7 @@ describe('publicChannelsSelectors', () => { for (const item of shuffled) { const message = await factory.create< - ReturnType['payload'] + ReturnType['payload'] >('Message', { identity: item.identity, message: { @@ -204,7 +219,7 @@ describe('publicChannelsSelectors', () => { type: item.type || MessageType.Basic, message: `message_${item.id}`, createdAt: item.createdAt, - channelAddress: 'general', + channelId: generalChannel.id, signature: '', pubKey: '' }, @@ -217,7 +232,18 @@ describe('publicChannelsSelectors', () => { it('get messages sorted by date', async () => { const messages = sortedCurrentChannelMessages(store.getState()) - messages.forEach(message => { + + const formattedMessages = messages.reduce((prev: ChannelMessage[], curr: ChannelMessage) => { + return [ + ...prev, + { + ...curr, + channelId: 'general_ec4bca1fa76046c53dff1e49979c3647' + } + ] + }, []) + + formattedMessages.forEach(message => { expect(message).toMatchSnapshot({ createdAt: expect.any(Number), pubKey: expect.any(String), @@ -228,7 +254,6 @@ describe('publicChannelsSelectors', () => { it('get grouped messages', async () => { const messages = currentChannelMessagesMergedBySender(store.getState()) - // Convert regular messages to displayable messages const displayable: { [id: string]: DisplayableMessage } = {} for (const message of Object.values(msgs)) { @@ -244,10 +269,7 @@ describe('publicChannelsSelectors', () => { expect(groupDay3).toBe('Today') const expectedGrouppedMessages = { - [groupDay1]: [ - [displayable['7']], - [displayable['8']] - ], + [groupDay1]: [[displayable['7']], [displayable['8']]], [groupDay2]: [ [displayable['1']], [displayable['2']], @@ -275,46 +297,53 @@ describe('publicChannelsSelectors', () => { }) it("don't select messages without author", async () => { - const channel = (await factory.create['payload']>( - 'PublicChannel', - { - channel: { - name: 'utah', - description: 'Welcome to #utah', - timestamp: DateTime.utc().valueOf(), - owner: alice.nickname, - address: 'utah' + const channelId = generateChannelId('utah') + const channel = ( + await factory.create['payload']>( + 'PublicChannel', + { + channel: { + name: 'utah', + description: 'Welcome to #utah', + timestamp: DateTime.utc().valueOf(), + owner: alice.nickname, + id: channelId + } } - } - )).channel + ) + ).channel - const elouise = await factory.create['payload']>( - 'Identity', - { id: community.id, nickname: 'elouise' } + const elouise = await factory.create< + ReturnType['payload'] + >('Identity', { id: community.id, nickname: 'elouise' }) + + if (!elouise.userCertificate) throw new Error('no elouise.userCertificate') + store.dispatch( + usersActions.test_remove_user_certificate({ certificate: elouise.userCertificate }) ) - // @ts-expect-error - store.dispatch(usersActions.test_remove_user_certificate({ certificate: elouise.userCertificate })) - - store.dispatch(publicChannelsActions.setCurrentChannel({ - channelAddress: channel.address - })) - - await factory.create< - ReturnType['payload'] - >('Message', { - identity: elouise, - message: { - id: '0', - type: MessageType.Basic, - message: 'elouise_message', - createdAt: DateTime.now().valueOf(), - channelAddress: channel.address, - signature: '', - pubKey: '' - }, - verifyAutomatically: true - }) + store.dispatch( + publicChannelsActions.setCurrentChannel({ + channelId: channel.id + }) + ) + + await factory.create['payload']>( + 'Message', + { + identity: elouise, + message: { + id: '0', + type: MessageType.Basic, + message: 'elouise_message', + createdAt: DateTime.now().valueOf(), + channelId: channel.id, + signature: '', + pubKey: '' + }, + verifyAutomatically: true + } + ) const messages = displayableCurrentChannelMessages(store.getState()) @@ -325,9 +354,9 @@ describe('publicChannelsSelectors', () => { // This case occurred in a built app const store = prepareStore().store const factory = await getFactory(store) - await factory.create< - ReturnType['payload'] - >('Community') + await factory.create['payload']>( + 'Community' + ) const oldState = store.getState() const channelId = oldState.PublicChannels.channels.ids[0] @@ -352,11 +381,15 @@ describe('publicChannelsSelectors', () => { }) it('unreadChannels selector returns only unread channels', async () => { - store.dispatch(publicChannelsActions.markUnreadChannel({ - channelAddress: 'allergies' - })) + const channelId = channelIdes.find(channelId => channelId.includes('allergies')) + if (!channelId) throw new Error('no channel id') + store.dispatch( + publicChannelsActions.markUnreadChannel({ + channelId + }) + ) const unreadChannels = publicChannelsSelectors.unreadChannels(store.getState()) - expect(unreadChannels).toEqual(['allergies']) + expect(unreadChannels).toEqual([channelId]) }) }) diff --git a/packages/state-manager/src/sagas/publicChannels/publicChannels.selectors.ts b/packages/state-manager/src/sagas/publicChannels/publicChannels.selectors.ts index 052c08a29a..72b966f7bd 100644 --- a/packages/state-manager/src/sagas/publicChannels/publicChannels.selectors.ts +++ b/packages/state-manager/src/sagas/publicChannels/publicChannels.selectors.ts @@ -11,17 +11,28 @@ import { certificatesMapping } from '../users/users.selectors' import { formatMessageDisplayDay } from '../../utils/functions/dates/formatMessageDisplayDate' import { displayableMessage } from '../../utils/functions/dates/formatDisplayableMessage' import { isDefined } from '@quiet/common' -import { ChannelMessage, DisplayableMessage, MessageType, MessagesDailyGroups, MessagesGroupsType, PublicChannel, PublicChannelStatus } from '@quiet/types' +import { + ChannelMessage, + DisplayableMessage, + MessageType, + MessagesDailyGroups, + MessagesGroupsType, + PublicChannel, + PublicChannelStatus, + INITIAL_CURRENT_CHANNEL_ID, + PublicChannelStatusWithName, + PublicChannelStorage +} from '@quiet/types' const selectState: CreatedSelectors[StoreKeys.PublicChannels] = (state: StoreState) => state[StoreKeys.PublicChannels] -export const selectChannels = createSelector(selectState, (state) => { +export const selectChannels = createSelector(selectState, state => { if (!state) return [] return publicChannelsAdapter.getSelectors().selectAll(state.channels) }) -const selectChannelsSubscriptions = createSelector(selectState, (state) => { +const selectChannelsSubscriptions = createSelector(selectState, state => { if (!state) return [] return publicChannelsSubscriptionsAdapter.getSelectors().selectAll(state.channelsSubscriptions) }) @@ -30,18 +41,15 @@ const pendingGeneralChannelRecreation = createSelector(selectState, state => { return state.pendingGeneralChannelRecreation }) -export const subscribedChannels = createSelector( - selectChannelsSubscriptions, - (subscriptions) => { - return subscriptions.map(subscription => { - if (subscription.subscribed) return subscription.address - }) - } -) +export const subscribedChannels = createSelector(selectChannelsSubscriptions, subscriptions => { + return subscriptions.map(subscription => { + if (subscription.subscribed) return subscription.id + }) +}) // Serves for testing purposes only export const selectGeneralChannel = createSelector(selectChannels, channels => { - const draft = channels.find(item => item.address === 'general') + const draft = channels.find(item => item.name === 'general') if (!draft) { console.error('No general channel') return @@ -51,12 +59,12 @@ export const selectGeneralChannel = createSelector(selectChannels, channels => { description: draft.description, owner: draft.owner, timestamp: draft.timestamp, - address: draft.address + id: draft.id } return channel }) -export const publicChannels = createSelector(selectChannels, (selectChannelsSelector) => { +export const publicChannels = createSelector(selectChannels, selectChannelsSelector => { const channels = Array.from(selectChannelsSelector) const sorted = channels.sort((a, b) => { if (a.name === 'general') { @@ -71,7 +79,7 @@ export const publicChannels = createSelector(selectChannels, (selectChannelsSele return sorted }) -export const sortedChannels = createSelector(publicChannels, (channels) => { +export const sortedChannels = createSelector(publicChannels, channels => { const sorted = channels.sort((a, b) => { if (a.name === 'general') { return -1 @@ -85,18 +93,25 @@ export const sortedChannels = createSelector(publicChannels, (channels) => { return sorted }) -export const currentChannelAddress = createSelector( - selectState, - (state) => { - if (!state) return '' - return state.currentChannelAddress - } -) - export const generalChannel = createSelector(publicChannels, publicChannelsSelector => { return publicChannelsSelector.find(channel => channel.name === 'general') }) +export const currentChannelId = createSelector(selectState, generalChannel, (state, general) => { + if (!state) { + return undefined + } + if (state.currentChannelId === INITIAL_CURRENT_CHANNEL_ID) { + if (general) { + return general.id + } else { + return undefined + } + } else { + return state.currentChannelId + } +}) + export const recentChannels = createSelector( publicChannels, generalChannel, @@ -126,41 +141,28 @@ export const dynamicSearchedChannels = (channelInput: string) => ) // Is being used in tests -export const currentChannel = createSelector( - currentChannelAddress, - selectChannels, - (address, channels) => { - if (!address) return undefined - return channels.find(channel => channel.address === address) - } -) +export const currentChannel = createSelector(currentChannelId, selectChannels, (id, channels) => { + if (!id) return undefined + return channels.find(channel => channel.id === id) +}) -export const currentChannelName = createSelector( - currentChannel, - (channel) => { - if (!channel) return '' - return channel.name - } -) +export const currentChannelName = createSelector(currentChannel, channel => { + if (!channel) return '' + return channel.name +}) -export const currentChannelMessages = createSelector( - currentChannel, - (channel) => { - if (!channel) return [] - return channelMessagesAdapter.getSelectors().selectAll(channel.messages) - } -) +export const currentChannelMessages = createSelector(currentChannel, channel => { + if (!channel) return [] + return channelMessagesAdapter.getSelectors().selectAll(channel.messages) +}) -export const sortedCurrentChannelMessages = createSelector( - currentChannelMessages, - messages => { - return messages.sort((a, b) => b.createdAt - a.createdAt).reverse() - } -) +export const sortedCurrentChannelMessages = createSelector(currentChannelMessages, messages => { + return messages.sort((a, b) => b.createdAt - a.createdAt).reverse() +}) export const currentChannelLastDisplayedMessage = createSelector( sortedCurrentChannelMessages, - (messages) => { + messages => { return messages[0] } ) @@ -188,7 +190,7 @@ export const displayableCurrentChannelMessages = createSelector( export const currentChannelMessagesCount = createSelector( displayableCurrentChannelMessages, - (messages) => { + messages => { return messages.length } ) @@ -196,16 +198,19 @@ export const currentChannelMessagesCount = createSelector( export const dailyGroupedCurrentChannelMessages = createSelector( displayableCurrentChannelMessages, messages => { - const result: MessagesGroupsType = messages.reduce((groups: MessagesGroupsType, message: DisplayableMessage) => { - const date = formatMessageDisplayDay(message.date) + const result: MessagesGroupsType = messages.reduce( + (groups: MessagesGroupsType, message: DisplayableMessage) => { + const date = formatMessageDisplayDay(message.date) - if (!groups[date]) { - groups[date] = [] - } + if (!groups[date]) { + groups[date] = [] + } - groups[date].push(message) - return groups - }, {}) + groups[date].push(message) + return groups + }, + {} + ) return result } @@ -216,74 +221,99 @@ export const currentChannelMessagesMergedBySender = createSelector( groups => { const result: MessagesDailyGroups = {} for (const day in groups) { - result[day] = groups[day].reduce((merged: DisplayableMessage[][], message: DisplayableMessage) => { - // Get last item from collected array for comparison - if (!merged.length) { - merged.push([message]) - return merged - } - const index = merged.length && merged.length - 1 - const last = merged[index][0] - - if (last.nickname === message.nickname && message.createdAt - last.createdAt < 300 && message.type !== MessageType.Info && last.type !== MessageType.Info) { - merged[index].push(message) - } else { - merged.push([message]) - } + result[day] = groups[day].reduce( + (merged: DisplayableMessage[][], message: DisplayableMessage) => { + // Get last item from collected array for comparison + if (!merged.length) { + merged.push([message]) + return merged + } + const index = merged.length && merged.length - 1 + const last = merged[index][0] + + if ( + last.nickname === message.nickname && + message.createdAt - last.createdAt < 300 && + message.type !== MessageType.Info && + last.type !== MessageType.Info + ) { + merged[index].push(message) + } else { + merged.push([message]) + } - return merged - }, []) + return merged + }, + [] + ) } return result } ) -export const channelsStatus = createSelector( - selectState, - state => { - if (!state?.channelsStatus) return {} - return publicChannelsStatusAdapter - .getSelectors() - .selectEntities(state.channelsStatus) - } -) +export const channelsStatus = createSelector(selectState, state => { + if (!state?.channelsStatus) return {} + return publicChannelsStatusAdapter.getSelectors().selectEntities(state.channelsStatus) +}) -export const channelsStatusSorted = createSelector( - selectState, - state => { - if (!state?.channelsStatus) return [] - const statuses = publicChannelsStatusAdapter - .getSelectors() - .selectAll(state.channelsStatus) +export const channelsStatusSorted = createSelector(selectState, state => { + if (!state?.channelsStatus) return [] + const statuses = publicChannelsStatusAdapter.getSelectors().selectAll(state.channelsStatus) - return statuses.sort((a, b) => { + return statuses + .sort((a, b) => { const aCreatedAt = a.newestMessage?.createdAt const bCreatedAt = b.newestMessage?.createdAt if (!aCreatedAt && !bCreatedAt) return 0 if (!aCreatedAt) return -1 if (!bCreatedAt) return 1 return aCreatedAt - bCreatedAt - }).reverse() - } -) + }) + .reverse() +}) -export const unreadChannels = createSelector( - channelsStatus, - status => { - return Object.values(status).filter(isDefined).reduce((result: string[], channel: PublicChannelStatus) => { +export const unreadChannels = createSelector(channelsStatus, status => { + return Object.values(status) + .filter(isDefined) + .reduce((result: string[], channel: PublicChannelStatus) => { if (channel.unread) { - result.push(channel.address) + result.push(channel.id) } return result }, []) +}) + +export const channelsStatusWithName = createSelector( + publicChannels, + channelsStatusSorted, + (publicChannelsSelector, channelsStatusSelector) => { + const channels = channelsStatusSelector.reduce( + (prev: PublicChannelStatusWithName[], curr: PublicChannelStatus) => { + const channel: PublicChannelStorage | undefined = publicChannelsSelector.find(channel => + curr.id.includes(channel.name) + ) + if (!channel?.name) return [] + const name = channel.name + return [ + ...prev, + { + ...curr, + name + } + ] + }, + [] + ) + + return channels } ) export const publicChannelsSelectors = { publicChannels, subscribedChannels, - currentChannelAddress, + currentChannelId, currentChannelName, currentChannel, currentChannelMessages, @@ -298,5 +328,7 @@ export const publicChannelsSelectors = { channelsStatusSorted, dynamicSearchedChannels, sortedChannels, - pendingGeneralChannelRecreation + pendingGeneralChannelRecreation, + generalChannel, + channelsStatusWithName } diff --git a/packages/state-manager/src/sagas/publicChannels/publicChannels.slice.ts b/packages/state-manager/src/sagas/publicChannels/publicChannels.slice.ts index da040a43dc..cfd8b92c4a 100644 --- a/packages/state-manager/src/sagas/publicChannels/publicChannels.slice.ts +++ b/packages/state-manager/src/sagas/publicChannels/publicChannels.slice.ts @@ -6,10 +6,36 @@ import { publicChannelsStatusAdapter, publicChannelsSubscriptionsAdapter } from './publicChannels.adapter' -import { CacheMessagesPayload, ChannelDeletionResponsePayload, ChannelMessage, ChannelsReplicatedPayload, ClearMessagesCachePayload, CreateChannelPayload, CreatedChannelResponse, DeleteChannelFromStorePayload, DeleteChannelPayload, DisableChannelPayload, Identity, MarkUnreadChannelPayload, PublicChannelStatus, PublicChannelStorage, PublicChannelSubscription, SendInitialChannelMessagePayload, SendNewUserInfoMessagePayload, SetChannelSubscribedPayload, SetCurrentChannelPayload, UpdateNewestMessagePayload } from '@quiet/types' + +import logger from '../../utils/logger' +import { + CacheMessagesPayload, + ChannelDeletionResponsePayload, + ChannelMessage, + ChannelsReplicatedPayload, + ClearMessagesCachePayload, + CreateChannelPayload, + CreatedChannelResponse, + DeleteChannelFromStorePayload, + DeleteChannelPayload, + DisableChannelPayload, + Identity, + INITIAL_CURRENT_CHANNEL_ID, + MarkUnreadChannelPayload, + PublicChannelStatus, + PublicChannelStorage, + PublicChannelSubscription, + SendInitialChannelMessagePayload, + SendNewUserInfoMessagePayload, + SetChannelSubscribedPayload, + SetCurrentChannelPayload, + UpdateNewestMessagePayload +} from '@quiet/types' +const log = logger('publicChannels') export class PublicChannelsState { - public currentChannelAddress: string = 'general' + public currentChannelId: string = INITIAL_CURRENT_CHANNEL_ID + public pendingGeneralChannelRecreation: boolean = false public channels: EntityState = publicChannelsAdapter.getInitialState() @@ -30,24 +56,24 @@ export const publicChannelsSlice = createSlice({ channelDeletionResponse: (state, _action: PayloadAction) => state, deleteChannelFromStore: (state, action: PayloadAction) => { - const { channelAddress } = action.payload + const { channelId } = action.payload - publicChannelsSubscriptionsAdapter.removeOne(state.channelsSubscriptions, channelAddress) - publicChannelsStatusAdapter.removeOne(state.channelsStatus, channelAddress) - publicChannelsAdapter.removeOne(state.channels, channelAddress) + publicChannelsSubscriptionsAdapter.removeOne(state.channelsSubscriptions, channelId) + publicChannelsStatusAdapter.removeOne(state.channelsStatus, channelId) + publicChannelsAdapter.removeOne(state.channels, channelId) }, disableChannel: (state, action: PayloadAction) => { - const { channelAddress } = action.payload + const { channelId } = action.payload publicChannelsAdapter.updateOne(state.channels, { - id: channelAddress, + id: channelId, changes: { disabled: true } }) }, clearMessagesCache: (state, action: PayloadAction) => { - const { channelAddress } = action.payload - const channel = state.channels.entities[channelAddress] + const { channelId } = action.payload + const channel = state.channels.entities[channelId] if (!channel) return channelMessagesAdapter.setAll(channel.messages, []) }, @@ -68,43 +94,43 @@ export const publicChannelsSlice = createSlice({ messages: channelMessagesAdapter.getInitialState() }) publicChannelsStatusAdapter.addOne(state.channelsStatus, { - address: channel.address, + id: channel.id, unread: false, newestMessage: null }) }, setChannelSubscribed: (state, action: PayloadAction) => { - const { channelAddress } = action.payload + const { channelId } = action.payload publicChannelsSubscriptionsAdapter.upsertOne(state.channelsSubscriptions, { - address: channelAddress, + id: channelId, subscribed: true }) }, channelsReplicated: (state, _action: PayloadAction) => state, setCurrentChannel: (state, action: PayloadAction) => { - const { channelAddress } = action.payload - state.currentChannelAddress = channelAddress + const { channelId } = action.payload + state.currentChannelId = channelId }, cacheMessages: (state, action: PayloadAction) => { - const { messages, channelAddress } = action.payload - const channel = state.channels.entities[channelAddress] + const { messages, channelId } = action.payload + const channel = state.channels.entities[channelId] if (!channel) return channelMessagesAdapter.setAll(channel.messages, messages) }, markUnreadChannel: (state, action: PayloadAction) => { - const { channelAddress } = action.payload + const { channelId } = action.payload publicChannelsStatusAdapter.updateOne(state.channelsStatus, { - id: channelAddress, + id: channelId, changes: { unread: true } }) }, clearUnreadChannel: (state, action: PayloadAction) => { - const { channelAddress } = action.payload + const { channelId } = action.payload publicChannelsStatusAdapter.updateOne(state.channelsStatus, { - id: channelAddress, + id: channelId, changes: { unread: false } @@ -113,7 +139,7 @@ export const publicChannelsSlice = createSlice({ updateNewestMessage: (state, action: PayloadAction) => { const { message } = action.payload publicChannelsStatusAdapter.updateOne(state.channelsStatus, { - id: message.channelAddress, + id: message.channelId, changes: { newestMessage: message } @@ -123,18 +149,16 @@ export const publicChannelsSlice = createSlice({ test_message: ( state, action: PayloadAction<{ + // [x: string]: ChannelMessage message: ChannelMessage identity: Identity verifyAutomatically: boolean }> ) => { const { message } = action.payload - const channel = state.channels.entities[message.channelAddress] + const channel = state.channels.entities[message.channelId] if (!channel) return - channelMessagesAdapter.addOne( - channel.messages, - message - ) + channelMessagesAdapter.addOne(channel.messages, message) } } }) diff --git a/packages/state-manager/src/sagas/publicChannels/publicChannels.transform.ts b/packages/state-manager/src/sagas/publicChannels/publicChannels.transform.ts index de92ed1f37..36bea5c84f 100644 --- a/packages/state-manager/src/sagas/publicChannels/publicChannels.transform.ts +++ b/packages/state-manager/src/sagas/publicChannels/publicChannels.transform.ts @@ -1,6 +1,8 @@ +import { ChannelMessage, PublicChannelStatus, PublicChannelStorage } from '@quiet/types' +import { Dictionary, EntityState } from '@reduxjs/toolkit' import { createTransform } from 'redux-persist' import { StoreKeys } from '../store.keys' -import { publicChannelsSubscriptionsAdapter } from './publicChannels.adapter' +import { publicChannelsAdapter, publicChannelsSubscriptionsAdapter } from './publicChannels.adapter' import { PublicChannelsState } from './publicChannels.slice' export const PublicChannelsTransform = createTransform( @@ -8,9 +10,22 @@ export const PublicChannelsTransform = createTransform( return { ...inboundState } }, (outboundState: PublicChannelsState, _key: any) => { + const generalChannelId = getGeneralChannelId(outboundState) + + const transformedOutboundState = { ...outboundState } as any + if (transformedOutboundState.currentChannelAddress) { + delete transformedOutboundState.currentChannelAddress + } + + outboundState.channels.entities = transformChannelsEntities(outboundState.channels.entities) + + outboundState.channelsStatus.entities = transformChannelStatusEntities( + outboundState.channelsStatus.entities + ) + return { - ...outboundState, - currentChannelAddress: 'general', + ...transformedOutboundState, + currentChannelId: generalChannelId, channels: outboundState.channels, channelsStatus: outboundState.channelsStatus, channelsSubscriptions: publicChannelsSubscriptionsAdapter.getInitialState() @@ -18,3 +33,77 @@ export const PublicChannelsTransform = createTransform( }, { whitelist: [StoreKeys.PublicChannels] } ) + +const getGeneralChannelId = (state: PublicChannelsState) => { + const selectors = publicChannelsAdapter.getSelectors() + const publicChannelStorage = selectors.selectAll(state.channels) + const generalChannel = publicChannelStorage.find(channel => channel.name === 'general') + const generalChannelId = generalChannel?.id || 'general' + + return generalChannelId +} + +const transformChannelsEntities = (channelsEntities: Dictionary) => { + const messagesRefactor = (messages: EntityState) => { + const transformedMessagesEntities = messages.entities + for (const [key, _message] of Object.entries(transformedMessagesEntities)) { + const message = { ..._message } as any + if (message.channelAddress) { + const transformedMessage = { + ...message, + channelId: message.channelAddress + } + delete transformedMessage.channelAddress + + transformedMessagesEntities[key] = transformedMessage + } + } + + return transformedMessagesEntities + } + + for (const [key, _channel] of Object.entries(channelsEntities)) { + const channel = { ..._channel } as any + if (channel.address) { + const messages = { ...channel.messages, entities: messagesRefactor(channel.messages) } + const transformedChannel = { + ...channel, + messages, + id: channel.address + } + delete transformedChannel.address + + channelsEntities[key] = transformedChannel + } + } + return channelsEntities +} + +const transformChannelStatusEntities = ( + channelsStatusEntities: Dictionary +) => { + const transformedChannelsStatusEntities = { ...channelsStatusEntities } + for (const [key, _channel] of Object.entries(transformedChannelsStatusEntities)) { + const channel = { ..._channel } as any + if (channel.address) { + let transformedNewestMessage = { ...channel.newestMessage } + if (transformedNewestMessage.channelAddress) { + transformedNewestMessage = { + channelId: channel.address, + ...transformedNewestMessage + } + delete transformedNewestMessage.channelAddress + } + const transformedChannel = { + ...channel, + id: channel.address, + newestMessage: transformedNewestMessage + } + delete transformedChannel.address + + transformedChannelsStatusEntities[key] = transformedChannel + } + } + + return transformedChannelsStatusEntities +} diff --git a/packages/state-manager/src/sagas/publicChannels/publicChannels.types.ts b/packages/state-manager/src/sagas/publicChannels/publicChannels.types.ts index 9bd6098f10..096203eda4 100644 --- a/packages/state-manager/src/sagas/publicChannels/publicChannels.types.ts +++ b/packages/state-manager/src/sagas/publicChannels/publicChannels.types.ts @@ -1,12 +1,14 @@ import { Dictionary, EntityState } from '@reduxjs/toolkit' import { FileMetadata } from '../files/files.types' +export const INITIAL_CURRENT_CHANNEL_ID = 'initialcurrentChannelId' + export interface PublicChannel { name: string description: string owner: string timestamp: number - address: string + id: string disabled?: boolean } @@ -15,13 +17,13 @@ export interface PublicChannelStorage extends PublicChannel { } export interface PublicChannelStatus { - address: string + id: string unread: boolean newestMessage: ChannelMessage | null } export interface PublicChannelSubscription { - address: string + id: string subscribed: boolean } @@ -30,7 +32,7 @@ export interface ChannelMessage { type: number message: string createdAt: number - channelAddress: string + channelId: string signature: string pubKey: string media?: FileMetadata @@ -59,10 +61,10 @@ export interface CreateChannelPayload { } export interface DeleteChannelPayload { - channel: string + channelId: string } export interface ChannelDeletionResponsePayload { - channel: string + channelId: string } export interface CreatedChannelResponse { @@ -70,16 +72,16 @@ export interface CreatedChannelResponse { } export interface SetChannelSubscribedPayload { - channelAddress: string + channelId: string } export interface SetCurrentChannelPayload { - channelAddress: string + channelId: string } export interface SetChannelMessagesSliceValuePayload { messagesSlice: number - channelAddress: string + channelId: string } export interface PendingMessage { @@ -88,7 +90,7 @@ export interface PendingMessage { export interface SendInitialChannelMessagePayload { channelName: string - channelAddress: string + channelId: string } export interface SendNewUserInfoMessagePayload { certificates: string[] @@ -101,11 +103,11 @@ export interface IncomingMessages { export interface CacheMessagesPayload { messages: ChannelMessage[] - channelAddress: string + channelId: string } export interface MarkUnreadChannelPayload { - channelAddress: string + channelId: string message?: ChannelMessage } @@ -114,17 +116,17 @@ export interface UpdateNewestMessagePayload { } export interface DeleteChannelFromStorePayload { - channelAddress: string + channelId: string } export interface ClearMessagesCachePayload { - channelAddress: string + channelId: string } export interface DisableChannelPayload { - channelAddress: string + channelId: string } export function instanceOfChannelMessage(object: any): object is ChannelMessage { - return 'channelAddress' in object + return 'channelId' in object } diff --git a/packages/state-manager/src/sagas/publicChannels/sendNewUserInfoMessage/sendNewUserInfoMessage.saga.test.ts b/packages/state-manager/src/sagas/publicChannels/sendNewUserInfoMessage/sendNewUserInfoMessage.saga.test.ts index 88b0d29e8a..8318b8d1b4 100644 --- a/packages/state-manager/src/sagas/publicChannels/sendNewUserInfoMessage/sendNewUserInfoMessage.saga.test.ts +++ b/packages/state-manager/src/sagas/publicChannels/sendNewUserInfoMessage/sendNewUserInfoMessage.saga.test.ts @@ -12,8 +12,7 @@ import { sendNewUserInfoMessageSaga } from './sendNewUserInfoMessage.saga' import { messagesActions } from '../../messages/messages.slice' import { combineReducers } from '@reduxjs/toolkit' import { reducers } from '../../reducers' - -import { MAIN_CHANNEL } from '../../../constants' +import { publicChannelsSelectors } from '../publicChannels.selectors' import { capitalizeFirstLetter } from '@quiet/common' import { Identity, PublicChannel } from '@quiet/types' @@ -23,6 +22,7 @@ describe('sendInitialChannelMessageSaga', () => { let channel: PublicChannel let user: Identity let user2: Identity + let generalChannel: PublicChannel beforeAll(async () => { setupCrypto() @@ -53,6 +53,9 @@ describe('sendInitialChannelMessageSaga', () => { channel = (await factory.build('PublicChannel')) .payload.channel + const generalChannelState = publicChannelsSelectors.generalChannel(store.getState()) + if (generalChannelState) generalChannel = generalChannelState + expect(generalChannel).not.toBeUndefined() expect(user2.userCertificate).not.toBeNull() store.dispatch( @@ -75,7 +78,7 @@ describe('sendInitialChannelMessageSaga', () => { messagesActions.sendMessage({ type: 3, message: `@${user2.nickname} has joined ${communityName}! 🎉`, - channelAddress: MAIN_CHANNEL + channelId: generalChannel.id }) ) .run() @@ -102,6 +105,10 @@ describe('sendInitialChannelMessageSaga', () => { channel = (await factory.build('PublicChannel')) .payload.channel + const generalChannelState = publicChannelsSelectors.generalChannel(store.getState()) + if (generalChannelState) generalChannel = generalChannelState + expect(generalChannel).not.toBeUndefined() + expect(community1.name).not.toBeNull() const communityName = capitalizeFirstLetter(community1.name || '') expect(user2.userCertificate).not.toBeNull() @@ -117,7 +124,7 @@ describe('sendInitialChannelMessageSaga', () => { messagesActions.sendMessage({ type: 3, message: `@${user2.nickname} has joined ${communityName}! 🎉`, - channelAddress: MAIN_CHANNEL + channelId: generalChannel.id }) ) .run() @@ -149,7 +156,7 @@ describe('sendInitialChannelMessageSaga', () => { messagesActions.sendMessage({ type: 3, message: `@${user2} has joined ${community1.name}! 🎉`, - channelAddress: MAIN_CHANNEL + channelId: generalChannel.id }) ) .run() @@ -182,7 +189,9 @@ describe('sendInitialChannelMessageSaga', () => { ) expect(community1.name).not.toBeNull() const communityName = capitalizeFirstLetter(community1.name || '') - + const generalChannelState = publicChannelsSelectors.generalChannel(store.getState()) + if (generalChannelState) generalChannel = generalChannelState + expect(generalChannel).not.toBeUndefined() const reducer = combineReducers(reducers) const result = await expectSaga( sendNewUserInfoMessageSaga, @@ -197,7 +206,7 @@ describe('sendInitialChannelMessageSaga', () => { messagesActions.sendMessage({ type: 3, message: `@${user2.nickname} has joined ${communityName}! 🎉`, - channelAddress: MAIN_CHANNEL + channelId: generalChannel.id }) ) .run() diff --git a/packages/state-manager/src/sagas/publicChannels/sendNewUserInfoMessage/sendNewUserInfoMessage.saga.ts b/packages/state-manager/src/sagas/publicChannels/sendNewUserInfoMessage/sendNewUserInfoMessage.saga.ts index 2e9ba0cbf2..520e3fe521 100644 --- a/packages/state-manager/src/sagas/publicChannels/sendNewUserInfoMessage/sendNewUserInfoMessage.saga.ts +++ b/packages/state-manager/src/sagas/publicChannels/sendNewUserInfoMessage/sendNewUserInfoMessage.saga.ts @@ -12,8 +12,8 @@ import { publicChannelsActions } from '../publicChannels.slice' import { usersSelectors } from '../../users/users.selectors' import { identitySelectors } from '../../identity/identity.selectors' import { communitiesSelectors } from '../../communities/communities.selectors' +import { publicChannelsSelectors } from '../publicChannels.selectors' -import { MAIN_CHANNEL } from '../../../constants' import { MessageType, WriteMessagePayload } from '@quiet/types' import { Certificate } from 'pkijs' @@ -30,6 +30,9 @@ export function* sendNewUserInfoMessageSaga( const incomingCertificates = action.payload.certificates const knownCertificates = yield* select(usersSelectors.certificates) + const generalChannel = yield* select(publicChannelsSelectors.generalChannel) + + if (!generalChannel) return const newCertificates = incomingCertificates.filter(cert => { const _cert = keyFromCertificate(parseCertificate(cert)) @@ -51,7 +54,7 @@ export function* sendNewUserInfoMessageSaga( const payload: WriteMessagePayload = { type: MessageType.Info, message: `@${user} has joined ${communityName}! 🎉`, - channelAddress: MAIN_CHANNEL + channelId: generalChannel.id } yield* put(messagesActions.sendMessage(payload)) diff --git a/packages/state-manager/src/sagas/publicChannels/updateNewestMessage/updateNewestMessage.saga.test.ts b/packages/state-manager/src/sagas/publicChannels/updateNewestMessage/updateNewestMessage.saga.test.ts index c00c473119..0a8254bde5 100644 --- a/packages/state-manager/src/sagas/publicChannels/updateNewestMessage/updateNewestMessage.saga.test.ts +++ b/packages/state-manager/src/sagas/publicChannels/updateNewestMessage/updateNewestMessage.saga.test.ts @@ -11,7 +11,9 @@ import { identityActions } from '../../identity/identity.slice' import { DateTime } from 'luxon' import { updateNewestMessageSaga } from './updateNewestMessage.saga' import { messagesActions } from '../../messages/messages.slice' -import { ChannelMessage, Community, Identity, MessageType } from '@quiet/types' +import { generateChannelId } from '@quiet/common' +import { publicChannelsSelectors } from '../publicChannels.selectors' +import { ChannelMessage, Community, Identity, MessageType, PublicChannel } from '@quiet/types' describe('markUnreadChannelsSaga', () => { let store: Store @@ -20,6 +22,10 @@ describe('markUnreadChannelsSaga', () => { let community: Community let alice: Identity + let generalChannel: PublicChannel + + let channelIds: string[] = [] + beforeAll(async () => { setupCrypto() @@ -28,39 +34,42 @@ describe('markUnreadChannelsSaga', () => { factory = await getFactory(store) community = await factory.create< - ReturnType['payload'] + ReturnType['payload'] >('Community') alice = await factory.create['payload']>( 'Identity', { id: community.id, nickname: 'alice' } ) - + const generalChannelState = publicChannelsSelectors.generalChannel(store.getState()) + if (generalChannelState) generalChannel = generalChannelState + expect(generalChannel).not.toBeUndefined() + channelIds = [...channelIds, generalChannel.id] const channelNames = ['memes', 'pets', 'travels'] // Automatically create channels for (const name of channelNames) { - await factory.create['payload']>( - 'PublicChannel', - { - channel: { - name: name, - description: `Welcome to #${name}`, - timestamp: DateTime.utc().valueOf(), - owner: alice.nickname, - address: name - } + const channel = await factory.create< + ReturnType['payload'] + >('PublicChannel', { + channel: { + name: name, + description: `Welcome to #${name}`, + timestamp: DateTime.utc().valueOf(), + owner: alice.nickname, + id: generateChannelId(name) } - ) + }) + channelIds = [...channelIds, channel.channel.id] } }) test('Update newest message if there is no newest message', async () => { - const messagesAddresses = ['general', 'memes'] + const messagesides = channelIds const messages: ChannelMessage[] = [] // Automatically create messages - for (const address of messagesAddresses) { + for (const id of messagesides) { const message = ( await factory.build('Message', { identity: alice, @@ -69,7 +78,7 @@ describe('markUnreadChannelsSaga', () => { type: MessageType.Basic, message: 'message', createdAt: DateTime.utc().valueOf(), - channelAddress: address, + channelId: id, signature: '', pubKey: '' }, @@ -88,21 +97,17 @@ describe('markUnreadChannelsSaga', () => { ) .withReducer(reducer) .withState(store.getState()) - .put( - publicChannelsActions.updateNewestMessage({ message: messages[0] }) - ) - .put( - publicChannelsActions.updateNewestMessage({ message: messages[1] }) - ) + .put(publicChannelsActions.updateNewestMessage({ message: messages[0] })) + .put(publicChannelsActions.updateNewestMessage({ message: messages[1] })) .run() }) test('update newest message if incoming message is newer', async () => { - const messagesAddresses = ['general'] + const messagesides = [generalChannel.id] const messages: ChannelMessage[] = [] // Automatically create messages - for (const address of messagesAddresses) { + for (const id of messagesides) { const message = ( await factory.build('Message', { identity: alice, @@ -111,7 +116,7 @@ describe('markUnreadChannelsSaga', () => { type: MessageType.Basic, message: 'message', createdAt: DateTime.utc().valueOf(), - channelAddress: address, + channelId: id, signature: '', pubKey: '' }, @@ -122,23 +127,26 @@ describe('markUnreadChannelsSaga', () => { } // Set the newest message - const message = (await factory.create< - ReturnType['payload'] - >('Message', { - identity: alice, - message: { - id: Math.random().toString(36).substr(2.9), - type: MessageType.Basic, - message: 'message', - createdAt: 9999, - channelAddress: 'general', - signature: '', - pubKey: '' - }, - verifyAutomatically: true - })).message - - store.dispatch(publicChannelsActions.updateNewestMessage({ message })) + const message = ( + await factory.create['payload']>( + 'Message', + { + identity: alice, + message: { + id: Math.random().toString(36).substr(2.9), + type: MessageType.Basic, + message: 'message', + createdAt: 9999, + channelId: generalChannel.id, + signature: '', + pubKey: '' + }, + verifyAutomatically: true + } + ) + ).message + + store.dispatch(publicChannelsActions.updateNewestMessage({ message })) const reducer = combineReducers(reducers) await expectSaga( @@ -149,17 +157,15 @@ describe('markUnreadChannelsSaga', () => { ) .withReducer(reducer) .withState(store.getState()) - .put( - publicChannelsActions.updateNewestMessage({ message: messages[0] }) - ) + .put(publicChannelsActions.updateNewestMessage({ message: messages[0] })) .run() }) test('do not update newest message if incoming message is older', async () => { - const messagesAddresses = ['general'] + const messagesides = [generalChannel.id] const messages: ChannelMessage[] = [] // Automatically create messages - for (const address of messagesAddresses) { + for (const id of messagesides) { const message = ( await factory.build('Message', { identity: alice, @@ -168,7 +174,7 @@ describe('markUnreadChannelsSaga', () => { type: MessageType.Basic, message: 'message', createdAt: DateTime.utc().valueOf(), - channelAddress: address, + channelId: id, signature: '', pubKey: '' }, @@ -179,23 +185,26 @@ describe('markUnreadChannelsSaga', () => { } // Set the newest message - const message = (await factory.create< - ReturnType['payload'] - >('Message', { - identity: alice, - message: { - id: Math.random().toString(36).substr(2.9), - type: MessageType.Basic, - message: 'message', - createdAt: 99999999999999, - channelAddress: 'general', - signature: '', - pubKey: '' - }, - verifyAutomatically: true - })).message - - store.dispatch(publicChannelsActions.updateNewestMessage({ message })) + const message = ( + await factory.create['payload']>( + 'Message', + { + identity: alice, + message: { + id: Math.random().toString(36).substr(2.9), + type: MessageType.Basic, + message: 'message', + createdAt: 99999999999999, + channelId: generalChannel.id, + signature: '', + pubKey: '' + }, + verifyAutomatically: true + } + ) + ).message + + store.dispatch(publicChannelsActions.updateNewestMessage({ message })) const reducer = combineReducers(reducers) await expectSaga( @@ -206,10 +215,7 @@ describe('markUnreadChannelsSaga', () => { ) .withReducer(reducer) .withState(store.getState()) - .not - .put( - publicChannelsActions.updateNewestMessage({ message: messages[0] }) - ) + .not.put(publicChannelsActions.updateNewestMessage({ message: messages[0] })) .run() }) }) diff --git a/packages/state-manager/src/sagas/publicChannels/updateNewestMessage/updateNewestMessage.saga.ts b/packages/state-manager/src/sagas/publicChannels/updateNewestMessage/updateNewestMessage.saga.ts index cdd4294a2b..43ee64c8b1 100644 --- a/packages/state-manager/src/sagas/publicChannels/updateNewestMessage/updateNewestMessage.saga.ts +++ b/packages/state-manager/src/sagas/publicChannels/updateNewestMessage/updateNewestMessage.saga.ts @@ -11,7 +11,7 @@ export function* updateNewestMessageSaga( const statuses = yield* select(publicChannelsSelectors.channelsStatus) for (const message of messages) { - const messageStatus = statuses[message.channelAddress] + const messageStatus = statuses[message.channelId] if (!messageStatus) return if ( !messageStatus.newestMessage || diff --git a/packages/state-manager/src/sagas/socket/startConnection/startConnection.saga.ts b/packages/state-manager/src/sagas/socket/startConnection/startConnection.saga.ts index 5ab3ca67c6..c5adf04ba1 100644 --- a/packages/state-manager/src/sagas/socket/startConnection/startConnection.saga.ts +++ b/packages/state-manager/src/sagas/socket/startConnection/startConnection.saga.ts @@ -132,14 +132,14 @@ export function subscribe(socket: Socket) { socket.on(SocketActionTypes.CREATED_CHANNEL, (payload: CreatedChannelResponse) => { emit( messagesActions.addPublicChannelsMessagesBase({ - channelAddress: payload.channel.address + channelId: payload.channel.id }) ) emit(publicChannelsActions.addChannel(payload)) emit( publicChannelsActions.sendInitialChannelMessage({ channelName: payload.channel.name, - channelAddress: payload.channel.address + channelId: payload.channel.id }) ) }) diff --git a/packages/state-manager/src/utils/tests/factories.ts b/packages/state-manager/src/utils/tests/factories.ts index 67539f04cd..208293542c 100644 --- a/packages/state-manager/src/utils/tests/factories.ts +++ b/packages/state-manager/src/utils/tests/factories.ts @@ -2,17 +2,38 @@ import factoryGirl from 'factory-girl' import { CustomReduxAdapter } from './reduxAdapter' import { Store } from '../../sagas/store.types' import { communities, identity, messages, publicChannels, users, errors } from '../..' -import { - createMessageSignatureTestHelper, - createPeerIdTestHelper -} from './helpers' +import { createMessageSignatureTestHelper, createPeerIdTestHelper } from './helpers' import { getCrypto } from 'pkijs' import { stringToArrayBuffer } from 'pvutils' -import { createRootCertificateTestHelper, createUserCertificateTestHelper, keyObjectFromString, verifySignature } from '@quiet/identity' + import { DateTime } from 'luxon' import { messagesActions } from '../../sagas/messages/messages.slice' import { publicChannelsActions } from '../../sagas/publicChannels/publicChannels.slice' -import { MessageType, SendingStatus } from '@quiet/types' +import { generateChannelId } from '@quiet/common' +import { + createRootCertificateTestHelper, + createUserCertificateTestHelper, + keyObjectFromString, + verifySignature +} from '@quiet/identity' +import { ChannelMessage, FileMetadata, MessageType, SendingStatus } from '@quiet/types' + +export const generateMessageFactoryContentWithId = ( + channelId: string, + type?: MessageType, + media?: FileMetadata +): ChannelMessage => { + return { + id: (Math.random() * 10 ** 18).toString(36), + type: type || MessageType.Basic, + message: (Math.random() * 10 ** 18).toString(36), + createdAt: DateTime.utc().valueOf(), + channelId: channelId, + signature: '', + pubKey: '', + media: media || undefined + } +} export const getFactory = async (store: Store) => { // @ts-ignore @@ -48,7 +69,7 @@ export const getFactory = async (store: Store) => { description: 'Welcome to channel #general', timestamp: DateTime.utc().toSeconds(), owner: 'alice', - address: 'general' + id: generateChannelId('general') } }) return payload @@ -116,11 +137,11 @@ export const getFactory = async (store: Store) => { }) factory.define('PublicChannelsMessagesBase', messages.actions.addPublicChannelsMessagesBase, { - channelAddress: factory.assoc('PublicChannel', 'address') + channelId: factory.assoc('PublicChannel', 'id') }) factory.define('PublicChannelSubscription', publicChannels.actions.setChannelSubscribed, { - channelAddress: factory.assoc('PublicChannel', 'address') + channelId: factory.assoc('PublicChannel', 'id') }) factory.define( @@ -132,19 +153,21 @@ export const getFactory = async (store: Store) => { description: 'Description', timestamp: DateTime.utc().toSeconds(), owner: factory.assoc('Identity', 'nickname'), - address: '' + id: generateChannelId( + factory.sequence('PublicChannel.name', n => `publicChannel${n}`).toString() + ) } }, { - afterBuild: (action: ReturnType) => { - action.payload.channel.address = action.payload.channel.name - return action - }, afterCreate: async ( payload: ReturnType['payload'] ) => { - await factory.create('PublicChannelsMessagesBase', ({ channelAddress: payload.channel.address })) - await factory.create('PublicChannelSubscription', ({ channelAddress: payload.channel.address })) + await factory.create('PublicChannelsMessagesBase', { + channelId: payload.channel.id + }) + await factory.create('PublicChannelSubscription', { + channelId: payload.channel.id + }) return payload } } @@ -160,7 +183,7 @@ export const getFactory = async (store: Store) => { type: MessageType.Basic, message: factory.sequence('Message.message', n => `message_${n}`), createdAt: DateTime.utc().valueOf(), - channelAddress: 'general', + channelId: generateChannelId('general'), signature: '', pubKey: '' }, @@ -212,9 +235,11 @@ export const getFactory = async (store: Store) => { afterCreate: async ( payload: ReturnType['payload'] ) => { - store.dispatch(messagesActions.incomingMessages({ - messages: [payload.message] - })) + store.dispatch( + messagesActions.incomingMessages({ + messages: [payload.message] + }) + ) return payload } @@ -223,7 +248,7 @@ export const getFactory = async (store: Store) => { factory.define('CacheMessages', publicChannelsActions.cacheMessages, { messages: [], - channelAddress: factory.assoc('PublicChannel', 'address'), + channelId: factory.assoc('PublicChannel', 'id'), communityId: factory.assoc('Community', 'id') }) diff --git a/packages/types/src/channel.ts b/packages/types/src/channel.ts index d27ea44c68..ab77a2b004 100644 --- a/packages/types/src/channel.ts +++ b/packages/types/src/channel.ts @@ -1,12 +1,14 @@ import { Dictionary, EntityState } from '@reduxjs/toolkit' import { FileMetadata } from './files' +export const INITIAL_CURRENT_CHANNEL_ID = 'initialcurrentChannelId' + export interface PublicChannel { name: string description: string owner: string timestamp: number - address: string + id: string disabled?: boolean } @@ -15,13 +17,17 @@ export interface PublicChannelStorage extends PublicChannel { } export interface PublicChannelStatus { - address: string + id: string unread: boolean newestMessage: ChannelMessage | null } +export interface PublicChannelStatusWithName extends PublicChannelStatus { + name: string +} + export interface PublicChannelSubscription { - address: string + id: string subscribed: boolean } @@ -30,7 +36,7 @@ export interface ChannelMessage { type: number message: string createdAt: number - channelAddress: string + channelId: string signature: string pubKey: string media?: FileMetadata @@ -63,10 +69,10 @@ export interface CreateChannelPayload { } export interface DeleteChannelPayload { - channel: string + channelId: string } export interface ChannelDeletionResponsePayload { - channel: string + channelId: string } export interface CreatedChannelResponse { @@ -74,16 +80,16 @@ export interface CreatedChannelResponse { } export interface SetChannelSubscribedPayload { - channelAddress: string + channelId: string } export interface SetCurrentChannelPayload { - channelAddress: string + channelId: string } export interface SetChannelMessagesSliceValuePayload { messagesSlice: number - channelAddress: string + channelId: string } export interface PendingMessage { @@ -92,7 +98,7 @@ export interface PendingMessage { export interface SendInitialChannelMessagePayload { channelName: string - channelAddress: string + channelId: string } export interface SendNewUserInfoMessagePayload { certificates: string[] @@ -105,11 +111,11 @@ export interface IncomingMessages { export interface CacheMessagesPayload { messages: ChannelMessage[] - channelAddress: string + channelId: string } export interface MarkUnreadChannelPayload { - channelAddress: string + channelId: string message?: ChannelMessage } @@ -118,17 +124,22 @@ export interface UpdateNewestMessagePayload { } export interface DeleteChannelFromStorePayload { - channelAddress: string + channelId: string } export interface ClearMessagesCachePayload { - channelAddress: string + channelId: string } export interface DisableChannelPayload { - channelAddress: string + channelId: string +} + +export interface ChannelStructure { + channelName: string | null + channelId: string | null } export function instanceOfChannelMessage(object: any): object is ChannelMessage { - return 'channelAddress' in object + return 'channelId' in object } diff --git a/packages/types/src/files.ts b/packages/types/src/files.ts index aa2404b914..0cefa5f988 100644 --- a/packages/types/src/files.ts +++ b/packages/types/src/files.ts @@ -14,11 +14,6 @@ export interface FileMetadata extends FileContent { height?: number } -export interface FileMessage { - id: string - channelAddress: string -} - export interface UploadFilePayload { file: FileMetadata peerId: string @@ -29,6 +24,10 @@ export interface DownloadFilePayload { peerId: string } +export interface FileMessage { + id: string + channelId: string +} export interface CancelDownload { mid: string cid: string @@ -65,7 +64,7 @@ export interface Dictionary extends DictionaryNum { } export interface DeleteFilesFromChannelPayload { - channelAddress: string + channelId: string } export interface DeleteFilesFromChannelSocketPayload { messages: Dictionary diff --git a/packages/types/src/message.ts b/packages/types/src/message.ts index 3bc7021af8..5ee50ab877 100644 --- a/packages/types/src/message.ts +++ b/packages/types/src/message.ts @@ -29,7 +29,7 @@ export interface PushNotificationPayload { export interface WriteMessagePayload { message: string id?: string - channelAddress?: string + channelId?: string type?: MessageType media?: FileMetadata } @@ -40,17 +40,17 @@ export interface PublicKeyMappingPayload { } export interface AddPublicChannelsMessagesBasePayload { - channelAddress: string + channelId: string } export interface PublicChannelsMessagesBase { - channelAddress: string + channelId: string messages: EntityState display: number } export interface SetDisplayedMessagesNumberPayload { - channelAddress: string + channelId: string display: number } @@ -72,22 +72,22 @@ export interface MessageSendingStatus { export interface AskForMessagesPayload { ids: string[] peerId: string - channelAddress: string + channelId: string communityId: string } export interface ChannelMessagesIdsResponse { ids: string[] - channelAddress: string + channelId: string communityId: string } export interface DeleteChannelEntryPayload { - channelAddress: string + channelId: string } export interface SendDeletionMessagePayload { - channelAddress: string + channelId: string } export interface TestMessage {