diff --git a/packages/backend/classic_level.cjs b/packages/backend/classic_level.cjs index 0cf856e774..d5b244db96 100644 --- a/packages/backend/classic_level.cjs +++ b/packages/backend/classic_level.cjs @@ -22,7 +22,9 @@ if (!exists && process.platform === 'android') { } if (!exists) { - throw new Error(`Unfortunately we do not support this platform! There is no classic_level bindings binary for ${process.platform}-${process.arch}`) + throw new Error( + `Unfortunately we do not support this platform! There is no classic_level bindings binary for ${process.platform}-${process.arch}` + ) } bindings = require(binaryPath) diff --git a/packages/backend/src/backendManager.ts b/packages/backend/src/backendManager.ts index 65814c95c5..2b6e00e054 100644 --- a/packages/backend/src/backendManager.ts +++ b/packages/backend/src/backendManager.ts @@ -8,16 +8,16 @@ const log = logger('backendManager') const program = new Command() program -.option('-p, --platform ', 'platform') -.option('-dpth, --dataPath ', 'data directory path') -.option('-dprt, --dataPort ', 'data port') -.option('-t, --torBinary ', 'tor binary path') -.option('-ac, --authCookie ', 'tor authentication cookie') -.option('-cp, --controlPort ', 'tor control port') -.option('-htp, --httpTunnelPort ', 'http tunnel port') -.option('-a, --appDataPath ', 'Path of application data directory') -.option('-d, --socketIOPort ', 'Socket io data server port') -.option('-r, --resourcesPath ', 'Application resources path') + .option('-p, --platform ', 'platform') + .option('-dpth, --dataPath ', 'data directory path') + .option('-dprt, --dataPort ', 'data port') + .option('-t, --torBinary ', 'tor binary path') + .option('-ac, --authCookie ', 'tor authentication cookie') + .option('-cp, --controlPort ', 'tor control port') + .option('-htp, --httpTunnelPort ', 'http tunnel port') + .option('-a, --appDataPath ', 'Path of application data directory') + .option('-d, --socketIOPort ', 'Socket io data server port') + .option('-r, --resourcesPath ', 'Application resources path') program.parse(process.argv) const options = program.opts() @@ -40,12 +40,12 @@ export const runBackendDesktop = async () => { torResourcesPath: torDirForPlatform(resourcesPath), options: { env: { - appDataPath: path.join(options.appDataPath.trim(), 'Quiet') - } - } + appDataPath: path.join(options.appDataPath.trim(), 'Quiet'), + }, + }, }) - process.on('message', async (message) => { + process.on('message', async message => { if (message === 'close') { try { await connectionsManager.closeAllServices() @@ -80,10 +80,10 @@ export const runBackendMobile = async (): Promise => { torBinaryPath: options.torBinary ? options.torBinary : null, options: { env: { - appDataPath: options.dataPath + appDataPath: options.dataPath, }, - createPaths: false - } + createPaths: false, + }, }) await connectionsManager.init() @@ -97,11 +97,13 @@ if (platform === 'desktop') { throw error }) } else if (platform === 'mobile') { - runBackendMobile().catch(async (error) => { + runBackendMobile().catch(async error => { log.error('Error occurred while initializing backend', error) // Prevent stopping process before getting output - await new Promise((resolve) => { - setTimeout(() => { resolve() }, 10000) + await new Promise(resolve => { + setTimeout(() => { + resolve() + }, 10000) }) throw error }) diff --git a/packages/backend/src/common/testUtils.ts b/packages/backend/src/common/testUtils.ts index 4ea707ef78..c32facb691 100644 --- a/packages/backend/src/common/testUtils.ts +++ b/packages/backend/src/common/testUtils.ts @@ -15,7 +15,7 @@ import { getPorts, type Ports, torBinForPlatform, - torDirForPlatform + torDirForPlatform, } from './utils' import crypto from 'crypto' import logger from '../logger' @@ -27,16 +27,21 @@ export const rootPermsData: PermsData = { certificate: 'MIIBNjCB3AIBATAKBggqhkjOPQQDAjASMRAwDgYDVQQDEwdaYmF5IENBMCYYEzIwMjEwNjIyMDkzMDEwLjAyNVoYDzIwMzAwMTMxMjMwMDAwWjASMRAwDgYDVQQDEwdaYmF5IENBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEV5a3Czy+L7IfVX0FpJtSF5mi0GWGrtPqv5+CFSDPrHXijsxWdPTobR1wk8uCLP4sAgUbs/bIleCxQy41kSSyOaMgMB4wDwYDVR0TBAgwBgEB/wIBAzALBgNVHQ8EBAMCAAYwCgYIKoZIzj0EAwIDSQAwRgIhAPOzksuipKyBALt/o8O/XwsrVSzfSHXdAR4dOWThQ1lbAiEAmKqjhsmf50kxWX0ekhbAeCTjcRApXhjnslmJkIFGF2o=+lmBImw3BMNjA0FTlK5iRmVC+w/T6M04Es+yiYL608vOhx2slnoyAwHjAPBgNVHRMECDAGAQH/AgEDMAsGA1UdDwQEAwIABjAKBggqhkjOPQQDAgNIADBFAiEA+0kIz0ny/PLVERTcL0+KCpsztyA6Zuwzj05VW5NMdx0CICgdzf0lg0/2Ksl1AjSPYsy2w+Hn09PGlBnD7TiExBpx', privKey: - 'MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgTvNuJL0blaYq6zmFS53WmmOfHshlqn+8wNHDzo4df5WgCgYIKoZIzj0DAQehRANCAARXlrcLPL4vsh9VfQWkm1IXmaLQZYau0+q/n4IVIM+sdeKOzFZ09OhtHXCTy4Is/iwCBRuz9siV4LFDLjWRJLI5+lmBImw3BMNjA0FTlK5iRmVC+w/T6M04Es+yiYL608vOhx2sln' + 'MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgTvNuJL0blaYq6zmFS53WmmOfHshlqn+8wNHDzo4df5WgCgYIKoZIzj0DAQehRANCAARXlrcLPL4vsh9VfQWkm1IXmaLQZYau0+q/n4IVIM+sdeKOzFZ09OhtHXCTy4Is/iwCBRuz9siV4LFDLjWRJLI5+lmBImw3BMNjA0FTlK5iRmVC+w/T6M04Es+yiYL608vOhx2sln', } tmp.setGracefulCleanup() export const testBootstrapMultiaddrs = [ - createLibp2pAddress('abcd.onion', 'QmfLUJcDSLVYnNqSPSRK4mKG8MGw51m9K2v59k3yq1C8s4') + createLibp2pAddress('abcd.onion', 'QmfLUJcDSLVYnNqSPSRK4mKG8MGw51m9K2v59k3yq1C8s4'), ] -export const spawnTorProcess = async (quietDirPath: string, ports?: Ports, extraTorProcessParams?: TorParams, binName?: string): Promise => { +export const spawnTorProcess = async ( + quietDirPath: string, + ports?: Ports, + extraTorProcessParams?: TorParams, + binName?: string +): Promise => { const _ports = ports || (await getPorts()) const torPath = torBinForPlatform(undefined, binName) const libPath = torDirForPlatform() @@ -47,11 +52,11 @@ export const spawnTorProcess = async (quietDirPath: string, ports?: Ports, extra options: { env: { LD_LIBRARY_PATH: libPath, - HOME: quietDirPath + HOME: quietDirPath, }, - detached: true + detached: true, }, - extraTorProcessParams + extraTorProcessParams, }) return tor } @@ -69,11 +74,11 @@ export const createLibp2p = async (peerId: PeerId): Promise => { cert: pems.userCert, key: pems.userKey, ca: [pems.ca], - targetPort: port + targetPort: port, }) } -export const createTmpDir = (prefix: string = 'quietTestTmp_'): tmp.DirResult => { +export const createTmpDir = (prefix = 'quietTestTmp_'): tmp.DirResult => { return tmp.dirSync({ mode: 0o750, prefix, unsafeCleanup: true }) } diff --git a/packages/backend/src/common/types.ts b/packages/backend/src/common/types.ts index 86a3be3fd2..0337d54875 100644 --- a/packages/backend/src/common/types.ts +++ b/packages/backend/src/common/types.ts @@ -16,7 +16,7 @@ export type ChannelInfoResponse = Record export class StorageOptions { orbitDbDir?: string ipfsDir?: string - createPaths: boolean = true + createPaths = true } export interface IPublicKey { diff --git a/packages/backend/src/common/utils.test.ts b/packages/backend/src/common/utils.test.ts index 6f1a578023..a4b6a61be7 100644 --- a/packages/backend/src/common/utils.test.ts +++ b/packages/backend/src/common/utils.test.ts @@ -1,7 +1,15 @@ import mock from 'mock-fs' import path from 'path' import { beforeEach, describe, it, expect, afterEach } from '@jest/globals' -const { getFilesRecursively, removeFiles, getDirsRecursively, removeDirs, compare, getUsersAddresses, createLibp2pAddress } = await import ('./utils') +const { + getFilesRecursively, + removeFiles, + getDirsRecursively, + removeDirs, + compare, + getUsersAddresses, + createLibp2pAddress, +} = await import('./utils') beforeEach(() => { mock({ @@ -9,15 +17,15 @@ beforeEach(() => { 'some-file.txt': 'file content here', IpfsQmQ18tV1dfGsEH8sCnbnzaYpMpb1QyCEjJ2KW96YtZ2MUn: { pins: { - LOCK: 'some data' + LOCK: 'some data', }, - 'repo.lock': {} + 'repo.lock': {}, }, OrbitDBQmQ18tV1dfGsEH8sCnbnzaYpMpb1QyCEjJ2KW96YtZ2MUn: { - LOCK: 'some data' - } + LOCK: 'some data', + }, }, - 'some/empty/dir': {} + 'some/empty/dir': {}, }) }) @@ -33,7 +41,7 @@ describe('Get files and dirs', () => { expect(arr).toEqual([ 'Quiet/IpfsQmQ18tV1dfGsEH8sCnbnzaYpMpb1QyCEjJ2KW96YtZ2MUn/pins/LOCK', 'Quiet/OrbitDBQmQ18tV1dfGsEH8sCnbnzaYpMpb1QyCEjJ2KW96YtZ2MUn/LOCK', - 'Quiet/some-file.txt' + 'Quiet/some-file.txt', ]) }) @@ -45,7 +53,7 @@ describe('Get files and dirs', () => { 'Quiet/IpfsQmQ18tV1dfGsEH8sCnbnzaYpMpb1QyCEjJ2KW96YtZ2MUn', 'Quiet/IpfsQmQ18tV1dfGsEH8sCnbnzaYpMpb1QyCEjJ2KW96YtZ2MUn/pins', 'Quiet/IpfsQmQ18tV1dfGsEH8sCnbnzaYpMpb1QyCEjJ2KW96YtZ2MUn/repo.lock', - 'Quiet/OrbitDBQmQ18tV1dfGsEH8sCnbnzaYpMpb1QyCEjJ2KW96YtZ2MUn' + 'Quiet/OrbitDBQmQ18tV1dfGsEH8sCnbnzaYpMpb1QyCEjJ2KW96YtZ2MUn', ]) }) @@ -64,15 +72,13 @@ describe('Remove files and dirs', () => { expect(arr).toEqual([ 'Quiet/IpfsQmQ18tV1dfGsEH8sCnbnzaYpMpb1QyCEjJ2KW96YtZ2MUn/pins/LOCK', 'Quiet/OrbitDBQmQ18tV1dfGsEH8sCnbnzaYpMpb1QyCEjJ2KW96YtZ2MUn/LOCK', - 'Quiet/some-file.txt' + 'Quiet/some-file.txt', ]) removeFiles('Quiet', 'LOCK') arr = [] getFilesRecursively('Quiet', arr) arr = arr.map(e => e.split(path.sep).join(path.posix.sep)) - expect(arr).toEqual([ - 'Quiet/some-file.txt' - ]) + expect(arr).toEqual(['Quiet/some-file.txt']) }) it('Remove directories by name', () => { @@ -83,7 +89,7 @@ describe('Remove files and dirs', () => { 'Quiet/IpfsQmQ18tV1dfGsEH8sCnbnzaYpMpb1QyCEjJ2KW96YtZ2MUn', 'Quiet/IpfsQmQ18tV1dfGsEH8sCnbnzaYpMpb1QyCEjJ2KW96YtZ2MUn/pins', 'Quiet/IpfsQmQ18tV1dfGsEH8sCnbnzaYpMpb1QyCEjJ2KW96YtZ2MUn/repo.lock', - 'Quiet/OrbitDBQmQ18tV1dfGsEH8sCnbnzaYpMpb1QyCEjJ2KW96YtZ2MUn' + 'Quiet/OrbitDBQmQ18tV1dfGsEH8sCnbnzaYpMpb1QyCEjJ2KW96YtZ2MUn', ]) removeDirs('Quiet', 'repo.lock') arr = [] @@ -92,11 +98,13 @@ describe('Remove files and dirs', () => { expect(arr).toEqual([ 'Quiet/IpfsQmQ18tV1dfGsEH8sCnbnzaYpMpb1QyCEjJ2KW96YtZ2MUn', 'Quiet/IpfsQmQ18tV1dfGsEH8sCnbnzaYpMpb1QyCEjJ2KW96YtZ2MUn/pins', - 'Quiet/OrbitDBQmQ18tV1dfGsEH8sCnbnzaYpMpb1QyCEjJ2KW96YtZ2MUn' + 'Quiet/OrbitDBQmQ18tV1dfGsEH8sCnbnzaYpMpb1QyCEjJ2KW96YtZ2MUn', ]) }) it("No error if directory doesn't exist", () => { - expect(() => { removeFiles('LOCK', 'non/existent/dir') }).not.toThrow() + expect(() => { + removeFiles('LOCK', 'non/existent/dir') + }).not.toThrow() }) }) @@ -120,11 +128,11 @@ describe('Compare actual and reported file size', () => { it('Gets users addresses based on user data', async () => { const userData = [ { onionAddress: '12345.onion', peerId: '54321', dmPublicKey: '324530833893', username: 'Bob' }, - { onionAddress: '67890.onion', peerId: '09876', dmPublicKey: '098830987898', username: 'Alice' } + { onionAddress: '67890.onion', peerId: '09876', dmPublicKey: '098830987898', username: 'Alice' }, ] const addresses = await getUsersAddresses(userData) expect(addresses).toStrictEqual([ createLibp2pAddress(userData[0].onionAddress, userData[0].peerId), - createLibp2pAddress(userData[1].onionAddress, userData[1].peerId) + createLibp2pAddress(userData[1].onionAddress, userData[1].peerId), ]) }) diff --git a/packages/backend/src/common/utils.ts b/packages/backend/src/common/utils.ts index 73e4fa8be2..dfbf262bcb 100644 --- a/packages/backend/src/common/utils.ts +++ b/packages/backend/src/common/utils.ts @@ -47,7 +47,7 @@ export const removeFiles = (appPath: string, filename: string) => { if (!fs.existsSync(appPath)) return const IpfsAndOrbitDb = fs.readdirSync(appPath).filter(i => i.startsWith('Ipfs') || i.startsWith('OrbitDB')) const files: string[] = [] - IpfsAndOrbitDb.forEach((e) => { + IpfsAndOrbitDb.forEach(e => { const directory = path.resolve(appPath, e) getFilesRecursively(directory, files) }) @@ -74,7 +74,7 @@ export const removeDirs = (appPath: string, filename: string) => { if (!fs.existsSync(appPath)) return const IpfsAndOrbitDb = fs.readdirSync(appPath).filter(i => i.startsWith('Ipfs')) const dirs: string[] = [] - IpfsAndOrbitDb.forEach((e) => { + IpfsAndOrbitDb.forEach(e => { const directory = path.resolve(appPath, e) getDirsRecursively(directory, dirs) }) @@ -96,7 +96,7 @@ export const getPorts = async (): Promise => { libp2pHiddenService, controlPort, dataServer, - httpTunnelPort + httpTunnelPort, } } @@ -111,7 +111,7 @@ export class DummyIOServer extends Server { } } -export const torBinForPlatform = (basePath: string = '', binName: string = 'tor'): string => { +export const torBinForPlatform = (basePath = '', binName = 'tor'): string => { if (process.env.BACKEND === 'mobile') { return basePath } @@ -156,7 +156,7 @@ export const getUsersAddresses = async (users: User[]): Promise => { * * @param tolerance In percentage (0.0 - 1.0) */ -export const compare = (given: number, base: number, tolerance: number = 0) => { +export const compare = (given: number, base: number, tolerance = 0) => { const margin = base * tolerance const min = base - margin const max = base + margin diff --git a/packages/backend/src/constants.ts b/packages/backend/src/constants.ts index 1819549aad..17c1f4ce5f 100644 --- a/packages/backend/src/constants.ts +++ b/packages/backend/src/constants.ts @@ -5,7 +5,7 @@ export enum Config { QUIET_DIR = '.quiet', PEER_ID_FILENAME = 'peerIdKey', ORBIT_DB_DIR = 'OrbitDB', - IPFS_REPO_PATH = 'QuietChannels' + IPFS_REPO_PATH = 'QuietChannels', } export const QUIET_DIR_PATH = path.join(os.homedir(), Config.QUIET_DIR) diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index cc03671307..2ef5b8bd76 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -12,7 +12,7 @@ export default { Tor, DataServer, ConnectionsManager, - version + version, } export * from './common/utils' diff --git a/packages/backend/src/libp2p/connectionsManager.test.ts b/packages/backend/src/libp2p/connectionsManager.test.ts index b17707c34b..d3331eec75 100644 --- a/packages/backend/src/libp2p/connectionsManager.test.ts +++ b/packages/backend/src/libp2p/connectionsManager.test.ts @@ -5,20 +5,20 @@ import { ConnectionsManager } from './connectionsManager' import { jest, beforeEach, describe, it, expect, afterEach } from '@jest/globals' import { LocalDBKeys } from '../storage/localDB' import { CustomEvent } from '@libp2p/interfaces/events' -import { - type communities, - getFactory, - prepareStore, - type identity, - type Store - } from '@quiet/state-manager' +import { type communities, getFactory, prepareStore, type identity, type Store } from '@quiet/state-manager' import { type FactoryGirl } from 'factory-girl' import { DateTime } from 'luxon' import waitForExpect from 'wait-for-expect' import { Libp2pEvents } from './types' import { DataServer } from '../socket/DataServer' import io from 'socket.io-client' -import { type Community, type Identity, type InitCommunityPayload, type LaunchRegistrarPayload, type NetworkStats } from '@quiet/types' +import { + type Community, + type Identity, + type InitCommunityPayload, + type LaunchRegistrarPayload, + type NetworkStats, +} from '@quiet/types' let tmpDir: DirResult let tmpAppDataPath: string @@ -37,12 +37,13 @@ beforeEach(async () => { store = prepareStore().store factory = await getFactory(store) communityRootCa = 'rootCa' - community = await factory.create['payload']>( - 'Community', { rootCa: communityRootCa } - ) - userIdentity = await factory.create['payload']>( - 'Identity', { id: community.id, nickname: 'john' } - ) + community = await factory.create['payload']>('Community', { + rootCa: communityRootCa, + }) + userIdentity = await factory.create['payload']>('Identity', { + id: community.id, + nickname: 'john', + }) }) afterEach(async () => { @@ -61,9 +62,9 @@ describe('Connections manager - no tor', () => { socketIOPort: port, options: { env: { - appDataPath: tmpAppDataPath - } - } + appDataPath: tmpAppDataPath, + }, + }, }) connectionsManager.dataServer = new DataServer(port) @@ -83,53 +84,43 @@ describe('Connections manager - no tor', () => { certificate: userIdentity.userCertificate, // @ts-expect-error key: userIdentity.userCsr.userKey, - CA: [communityRootCa] - } + CA: [communityRootCa], + }, }) expect(result.localAddress).toBe(localAddress) expect(result.libp2p.peerId).toBe(peerId) }) - it( - 'creates libp2p address with proper ws type (%s)', - async () => { - const address = '0.0.0.0' - const port = 1234 - const peerId = await PeerId.create() - connectionsManager = new ConnectionsManager({ - socketIOPort: port, - options: { - env: { - appDataPath: tmpAppDataPath - } - } - }) - const libp2pAddress = connectionsManager.createLibp2pAddress( - address, - peerId.toB58String() - ) - expect(libp2pAddress).toStrictEqual( - `/dns4/${address}/tcp/443/wss/p2p/${peerId.toB58String()}` - ) - } - ) - - it('creates libp2p listen address', - async () => { - const address = '0.0.0.0' - const port = 1234 - connectionsManager = new ConnectionsManager({ - socketIOPort: port, - options: { - env: { - appDataPath: tmpAppDataPath - } - } - }) - const libp2pListenAddress = connectionsManager.createLibp2pListenAddress(address) - expect(libp2pListenAddress).toStrictEqual(`/dns4/${address}/tcp/443/wss`) - } - ) + it('creates libp2p address with proper ws type (%s)', async () => { + const address = '0.0.0.0' + const port = 1234 + const peerId = await PeerId.create() + connectionsManager = new ConnectionsManager({ + socketIOPort: port, + options: { + env: { + appDataPath: tmpAppDataPath, + }, + }, + }) + const libp2pAddress = connectionsManager.createLibp2pAddress(address, peerId.toB58String()) + expect(libp2pAddress).toStrictEqual(`/dns4/${address}/tcp/443/wss/p2p/${peerId.toB58String()}`) + }) + + it('creates libp2p listen address', async () => { + const address = '0.0.0.0' + const port = 1234 + connectionsManager = new ConnectionsManager({ + socketIOPort: port, + options: { + env: { + appDataPath: tmpAppDataPath, + }, + }, + }) + const libp2pListenAddress = connectionsManager.createLibp2pListenAddress(address) + expect(libp2pListenAddress).toStrictEqual(`/dns4/${address}/tcp/443/wss`) + }) it('launches community on init if its data exists in local db', async () => { connectionsManager = new ConnectionsManager({ @@ -137,9 +128,9 @@ describe('Connections manager - no tor', () => { torControlPort: 4321, options: { env: { - appDataPath: tmpAppDataPath - } - } + appDataPath: tmpAppDataPath, + }, + }, }) const launchCommunityPayload: InitCommunityPayload = { id: community.id, @@ -150,9 +141,9 @@ describe('Connections manager - no tor', () => { certificate: userIdentity.userCertificate, // @ts-expect-error key: userIdentity.userCsr?.userKey, - CA: [communityRootCa] + CA: [communityRootCa], }, - peers: community.peerList + peers: community.peerList, } await connectionsManager.init() @@ -165,10 +156,12 @@ describe('Connections manager - no tor', () => { const url = `http://localhost:${1234}` const socket = io(url) - const init = new Promise(resolve => { - void connectionsManager?.init() - socket.connect() - setTimeout(() => { resolve() }, 200) + const init = new Promise(resolve => { + void connectionsManager?.init() + socket.connect() + setTimeout(() => { + resolve() + }, 200) }) await init @@ -185,9 +178,9 @@ describe('Connections manager - no tor', () => { torControlPort: 4321, options: { env: { - appDataPath: tmpAppDataPath - } - } + appDataPath: tmpAppDataPath, + }, + }, }) const launchCommunityPayload: InitCommunityPayload = { @@ -199,9 +192,9 @@ describe('Connections manager - no tor', () => { certificate: userIdentity.userCertificate, // @ts-expect-error key: userIdentity.userCsr?.userKey, - CA: [communityRootCa] + CA: [communityRootCa], }, - peers: community.peerList + peers: community.peerList, } const launchRegistrarPayload: LaunchRegistrarPayload = { @@ -211,7 +204,7 @@ describe('Connections manager - no tor', () => { rootCertString: community.CA?.rootCertString, // @ts-expect-error rootKeyString: community.CA?.rootKeyString, - privateKey: 'privateKey' + privateKey: 'privateKey', } await connectionsManager.init() @@ -221,11 +214,11 @@ describe('Connections manager - no tor', () => { const peerAddress = '/dns4/test.onion/tcp/443/wss/p2p/peerid' await connectionsManager.localStorage.put(LocalDBKeys.PEERS, { [peerAddress]: { - peerId: 'QmaEvCkpUG7GxhgvMkk8wxurfi1ehjHhSUNRksWTmXN2ix', - connectionTime: 50, - lastSeen: 1000 - } - }) + peerId: 'QmaEvCkpUG7GxhgvMkk8wxurfi1ehjHhSUNRksWTmXN2ix', + connectionTime: 50, + lastSeen: 1000, + }, + }) await connectionsManager.closeAllServices() @@ -235,10 +228,12 @@ describe('Connections manager - no tor', () => { const url = `http://localhost:${1234}` const socket = io(url) - const init = new Promise(resolve => { - void connectionsManager?.init() - socket.connect() - setTimeout(() => { resolve() }, 200) + const init = new Promise(resolve => { + void connectionsManager?.init() + socket.connect() + setTimeout(() => { + resolve() + }, 200) }) await init @@ -254,9 +249,9 @@ describe('Connections manager - no tor', () => { torControlPort: 4321, options: { env: { - appDataPath: tmpAppDataPath - } - } + appDataPath: tmpAppDataPath, + }, + }, }) const launchCommunitySpy = jest.spyOn(connectionsManager, 'launchCommunity') const launchRegistrarSpy = jest.spyOn(connectionsManager.registration, 'launchRegistrar') @@ -284,9 +279,9 @@ describe('Connections manager - no tor', () => { torControlPort: 4321, options: { env: { - appDataPath: tmpAppDataPath - } - } + appDataPath: tmpAppDataPath, + }, + }, }) await connectionsManager.init() @@ -301,8 +296,8 @@ describe('Connections manager - no tor', () => { certificate: userIdentity.userCertificate, // @ts-expect-error key: userIdentity.userCsr?.userKey, - CA: [communityRootCa] - } + CA: [communityRootCa], + }, }) const emitSpy = jest.spyOn(connectionsManager, 'emit') @@ -311,8 +306,13 @@ describe('Connections manager - no tor', () => { // Peer disconnected const remoteAddr = `test/p2p/${peerId.toString()}` - const peerDisconectEventDetail = { remotePeer: new RemotePeerEventDetail(peerId.toString()), remoteAddr: new RemotePeerEventDetail(remoteAddr) } - connectionsManager.libp2pInstance?.dispatchEvent(new CustomEvent('peer:disconnect', { detail: peerDisconectEventDetail })) + const peerDisconectEventDetail = { + remotePeer: new RemotePeerEventDetail(peerId.toString()), + remoteAddr: new RemotePeerEventDetail(remoteAddr), + } + connectionsManager.libp2pInstance?.dispatchEvent( + new CustomEvent('peer:disconnect', { detail: peerDisconectEventDetail }) + ) expect(connectionsManager.connectedPeers.size).toEqual(0) await waitForExpect(async () => { expect(await connectionsManager?.localStorage.get(LocalDBKeys.PEERS)).not.toBeNull() @@ -322,7 +322,7 @@ describe('Connections manager - no tor', () => { expect(emitSpy).toHaveBeenCalledWith(Libp2pEvents.PEER_DISCONNECTED, { peer: peerStats[remoteAddr].peerId, connectionDuration: peerStats[remoteAddr].connectionTime, - lastSeen: peerStats[remoteAddr].lastSeen + lastSeen: peerStats[remoteAddr].lastSeen, }) }) // At this moment, that test have to be skipped, because checking statues is called before launchCommunity method @@ -332,9 +332,9 @@ describe('Connections manager - no tor', () => { torControlPort: 4321, options: { env: { - appDataPath: tmpAppDataPath - } - } + appDataPath: tmpAppDataPath, + }, + }, }) const launchCommunityPayload: InitCommunityPayload = { id: community.id, @@ -345,9 +345,9 @@ describe('Connections manager - no tor', () => { certificate: userIdentity.userCertificate, // @ts-expect-error key: userIdentity.userCsr?.userKey, - CA: [communityRootCa] + CA: [communityRootCa], }, - peers: community.peerList + peers: community.peerList, } const launchSpy = jest.spyOn(connectionsManager, 'launch').mockResolvedValue('address') @@ -356,7 +356,7 @@ describe('Connections manager - no tor', () => { await Promise.all([ connectionsManager.launchCommunity(launchCommunityPayload), - connectionsManager.launchCommunity(launchCommunityPayload) + connectionsManager.launchCommunity(launchCommunityPayload), ]) expect(launchSpy).toBeCalledTimes(1) @@ -368,9 +368,9 @@ describe('Connections manager - no tor', () => { torControlPort: 4321, options: { env: { - appDataPath: tmpAppDataPath - } - } + appDataPath: tmpAppDataPath, + }, + }, }) const launchCommunityPayload: InitCommunityPayload = { @@ -382,9 +382,9 @@ describe('Connections manager - no tor', () => { certificate: userIdentity.userCertificate, // @ts-expect-error key: userIdentity.userCsr?.userKey, - CA: [communityRootCa] + CA: [communityRootCa], }, - peers: community.peerList + peers: community.peerList, } const launchRegistrarPayload: LaunchRegistrarPayload = { @@ -394,7 +394,7 @@ describe('Connections manager - no tor', () => { rootCertString: community.CA?.rootCertString, // @ts-expect-error rootKeyString: community.CA?.rootKeyString, - privateKey: '' + privateKey: '', } await connectionsManager.init() @@ -404,11 +404,11 @@ describe('Connections manager - no tor', () => { const peerAddress = '/dns4/test.onion/tcp/443/wss/p2p/peerid' await connectionsManager.localStorage.put(LocalDBKeys.PEERS, { [peerAddress]: { - peerId: 'QmaEvCkpUG7GxhgvMkk8wxurfi1ehjHhSUNRksWTmXN2ix', - connectionTime: 50, - lastSeen: 1000 - } - }) + peerId: 'QmaEvCkpUG7GxhgvMkk8wxurfi1ehjHhSUNRksWTmXN2ix', + connectionTime: 50, + lastSeen: 1000, + }, + }) await connectionsManager.closeAllServices() @@ -418,10 +418,12 @@ describe('Connections manager - no tor', () => { const url = `http://localhost:${1234}` const socket = io(url) - const init = new Promise(resolve => { - void connectionsManager?.init() - socket.connect() - setTimeout(() => { resolve() }, 200) + const init = new Promise(resolve => { + void connectionsManager?.init() + socket.connect() + setTimeout(() => { + resolve() + }, 200) }) await init diff --git a/packages/backend/src/libp2p/connectionsManager.tor.test.ts b/packages/backend/src/libp2p/connectionsManager.tor.test.ts index 4bf7cdc314..6c88b5805e 100644 --- a/packages/backend/src/libp2p/connectionsManager.tor.test.ts +++ b/packages/backend/src/libp2p/connectionsManager.tor.test.ts @@ -39,9 +39,9 @@ describe('Connections manager', () => { socketIOPort: ports.socksPort, options: { env: { - appDataPath: tmpAppDataPath - } - } + appDataPath: tmpAppDataPath, + }, + }, }) const socket = await initConnectionsManagerWithTor(connectionsManager, ports.socksPort) @@ -57,9 +57,9 @@ describe('Connections manager', () => { socketIOPort: ports.socksPort, options: { env: { - appDataPath: tmpAppDataPath - } - } + appDataPath: tmpAppDataPath, + }, + }, }) const socket = await initConnectionsManagerWithTor(connectionsManager, ports.socksPort) const spyOnDestroyHiddenService = jest.spyOn(connectionsManager.tor, 'destroyHiddenService') @@ -76,9 +76,7 @@ describe('Connections manager', () => { const store = prepareStore().store const factory = await getFactory(store) const community = await factory.create('Community', { rootCa: 'rootCa' }) - const userIdentity = await factory.create( - 'Identity', { id: community.id, nickname: 'john' } - ) + const userIdentity = await factory.create('Identity', { id: community.id, nickname: 'john' }) const ports = await getPorts() connectionsManager = new ConnectionsManager({ @@ -87,9 +85,9 @@ describe('Connections manager', () => { socketIOPort: ports.socksPort, options: { env: { - appDataPath: tmpAppDataPath - } - } + appDataPath: tmpAppDataPath, + }, + }, }) const socket = await initConnectionsManagerWithTor(connectionsManager, ports.socksPort) const spyOnDial = jest.spyOn(WebSockets.prototype, 'dial') @@ -97,7 +95,9 @@ describe('Connections manager', () => { const peerList: string[] = [] const peersCount = 11 for (let pCount = 0; pCount < peersCount; pCount++) { - peerList.push(createLibp2pAddress(`${Math.random().toString(36).substring(2, 13)}.onion`, (await createPeerId()).toString())) + peerList.push( + createLibp2pAddress(`${Math.random().toString(36).substring(2, 13)}.onion`, (await createPeerId()).toString()) + ) } const launchCommunityPayload: InitCommunityPayload = { @@ -110,9 +110,9 @@ describe('Connections manager', () => { // @ts-expect-error Identity.userCertificate userCsr.userKey can be undefined key: userIdentity.userCsr?.userKey, // @ts-expect-error - CA: [community.rootCa] + CA: [community.rootCa], }, - peers: peerList + peers: peerList, } await connectionsManager.launchCommunity(launchCommunityPayload) @@ -124,9 +124,7 @@ describe('Connections manager', () => { const store = prepareStore().store const factory = await getFactory(store) const community = await factory.create('Community', { rootCa: 'rootCa' }) - const userIdentity = await factory.create( - 'Identity', { id: community.id, nickname: 'john' } - ) + const userIdentity = await factory.create('Identity', { id: community.id, nickname: 'john' }) const ports = await getPorts() connectionsManager = new ConnectionsManager({ @@ -135,9 +133,9 @@ describe('Connections manager', () => { socketIOPort: ports.socksPort, options: { env: { - appDataPath: tmpAppDataPath - } - } + appDataPath: tmpAppDataPath, + }, + }, }) const socket = await initConnectionsManagerWithTor(connectionsManager, ports.socksPort) const spyOnDial = jest.spyOn(WebSockets.prototype, 'dial') @@ -145,7 +143,9 @@ describe('Connections manager', () => { const peerList: string[] = [] const peersCount = 11 for (let pCount = 0; pCount < peersCount; pCount++) { - peerList.push(createLibp2pAddress(`${Math.random().toString(36).substring(2, 13)}.onion`, (await createPeerId()).toString())) + peerList.push( + createLibp2pAddress(`${Math.random().toString(36).substring(2, 13)}.onion`, (await createPeerId()).toString()) + ) } const launchCommunityPayload: InitCommunityPayload = { @@ -158,9 +158,9 @@ describe('Connections manager', () => { // @ts-expect-error key: userIdentity.userCsr?.userKey, // @ts-expect-error - CA: [community.rootCa] + CA: [community.rootCa], }, - peers: peerList + peers: peerList, } await connectionsManager.launchCommunity(launchCommunityPayload) @@ -171,16 +171,16 @@ describe('Connections manager', () => { // IOS - const connectionsManager2 = new ConnectionsManager({ + const connectionsManager2 = new ConnectionsManager({ torControlPort: 4321, torBinaryPath: '../../3rd-party/tor/linux/tor', torResourcesPath: '../../3rd-party/tor/linux', socketIOPort: ports.socksPort, options: { env: { - appDataPath: tmpAppDataPath - } - } + appDataPath: tmpAppDataPath, + }, + }, }) const launchSpy = jest.spyOn(connectionsManager2, 'launch').mockResolvedValue('address') await connectionsManager2.init() diff --git a/packages/backend/src/libp2p/connectionsManager.ts b/packages/backend/src/libp2p/connectionsManager.ts index a82a88c984..11fa3e467c 100644 --- a/packages/backend/src/libp2p/connectionsManager.ts +++ b/packages/backend/src/libp2p/connectionsManager.ts @@ -38,7 +38,41 @@ import { LocalDB, LocalDBKeys } from '../storage/localDB' import { createLibp2pAddress, createLibp2pListenAddress, getPorts, removeFilesFromDir } from '../common/utils' import { ProcessInChunks } from './processInChunks' import { multiaddr } from '@multiformats/multiaddr' -import { type AskForMessagesPayload, type Certificates, ChannelMessage, type ChannelMessagesIdsResponse, type ChannelsReplicatedPayload, type Community, type CommunityId, ConnectionProcessInfo, type CreateChannelPayload, type CreatedChannelResponse, type DeleteFilesFromChannelSocketPayload, type DownloadStatus, ErrorMessages, type FileMetadata, type IncomingMessages, type InitCommunityPayload, type LaunchRegistrarPayload, type NetworkData, type NetworkDataPayload, type NetworkStats, type PushNotificationPayload, type RegisterOwnerCertificatePayload, type RegisterUserCertificatePayload, type RemoveDownloadStatus, type ResponseCreateNetworkPayload, type SaveCertificatePayload, type SaveOwnerCertificatePayload, type SendCertificatesResponse, type SendMessagePayload, type SetChannelSubscribedPayload, SocketActionTypes, type StorePeerListPayload, type UploadFilePayload } from '@quiet/types' +import { + type AskForMessagesPayload, + type Certificates, + ChannelMessage, + type ChannelMessagesIdsResponse, + type ChannelsReplicatedPayload, + type Community, + type CommunityId, + ConnectionProcessInfo, + type CreateChannelPayload, + type CreatedChannelResponse, + type DeleteFilesFromChannelSocketPayload, + type DownloadStatus, + ErrorMessages, + type FileMetadata, + type IncomingMessages, + type InitCommunityPayload, + type LaunchRegistrarPayload, + type NetworkData, + type NetworkDataPayload, + type NetworkStats, + type PushNotificationPayload, + type RegisterOwnerCertificatePayload, + type RegisterUserCertificatePayload, + type RemoveDownloadStatus, + type ResponseCreateNetworkPayload, + type SaveCertificatePayload, + type SaveOwnerCertificatePayload, + type SendCertificatesResponse, + type SendMessagePayload, + type SetChannelSubscribedPayload, + SocketActionTypes, + type StorePeerListPayload, + type UploadFilePayload, +} from '@quiet/types' const log = logger('conn') interface InitStorageParams { @@ -83,7 +117,7 @@ export interface InitLibp2pParams { export enum TorInitState { STARTING = 'starting', STARTED = 'started', - NOT_STARTED = 'not-started' + NOT_STARTED = 'not-started', } export class ConnectionsManager extends EventEmitter { @@ -109,12 +143,20 @@ export class ConnectionsManager extends EventEmitter { registrarState: ServiceState isTorInit: TorInitState = TorInitState.NOT_STARTED - constructor({ options, socketIOPort, httpTunnelPort, torControlPort, torAuthCookie, torResourcesPath, torBinaryPath }: IConstructor) { + constructor({ + options, + socketIOPort, + httpTunnelPort, + torControlPort, + torAuthCookie, + torResourcesPath, + torBinaryPath, + }: IConstructor) { super() this.registration = new CertificateRegistration() this.options = { ...new ConnectionsManagerOptions(), - ...options + ...options, } this.torResourcesPath = torResourcesPath @@ -141,11 +183,14 @@ export class ConnectionsManager extends EventEmitter { // @ts-ignore global.crypto = webcrypto - setEngine('newEngine', new CryptoEngine({ - name: 'newEngine', - // @ts-ignore - crypto: webcrypto - })) + setEngine( + 'newEngine', + new CryptoEngine({ + name: 'newEngine', + // @ts-ignore + crypto: webcrypto, + }) + ) } public readonly createAgent = (): void => { @@ -154,7 +199,8 @@ export class ConnectionsManager extends EventEmitter { log(`Creating https proxy agent on port ${this.httpTunnelPort}`) this.socksProxyAgent = createHttpsProxyAgent({ - port: this.httpTunnelPort, host: '127.0.0.1' + port: this.httpTunnelPort, + host: '127.0.0.1', }) } @@ -197,18 +243,18 @@ export class ConnectionsManager extends EventEmitter { await this.launchCommunityFromStorage() } - this.io.on('connection', async() => { + this.io.on('connection', async () => { if (this.isTorInit === TorInitState.STARTED || this.isTorInit === TorInitState.STARTING) return this.isTorInit = TorInitState.STARTING - if (this.torBinaryPath) { - await this.tor.init() - this.isTorInit = TorInitState.STARTED - } - await this.launchCommunityFromStorage() + if (this.torBinaryPath) { + await this.tor.init() + this.isTorInit = TorInitState.STARTED + } + await this.launchCommunityFromStorage() }) } - public async launchCommunityFromStorage () { + public async launchCommunityFromStorage() { log('launchCommunityFromStorage') const community: InitCommunityPayload = await this.localStorage.get(LocalDBKeys.COMMUNITY) if (community) { @@ -267,18 +313,23 @@ export class ConnectionsManager extends EventEmitter { this.storage = null this.libp2pInstance = null await this.init() - } + } - public async purgeData() { - console.log('removing data') - const dirsToRemove = fs.readdirSync(this.quietDir).filter(i => i.startsWith('Ipfs') || i.startsWith('OrbitDB') || i.startsWith('backendDB') || i.startsWith('Local Storage')) - for (const dir of dirsToRemove) { - removeFilesFromDir(path.join(this.quietDir, dir)) - } + public async purgeData() { + console.log('removing data') + const dirsToRemove = fs + .readdirSync(this.quietDir) + .filter( + i => + i.startsWith('Ipfs') || i.startsWith('OrbitDB') || i.startsWith('backendDB') || i.startsWith('Local Storage') + ) + for (const dir of dirsToRemove) { + removeFilesFromDir(path.join(this.quietDir, dir)) + } } public spawnTor = async () => { - if (!this.httpTunnelPort) throw new Error('Couldn\'t spawn tor, no httpTunnelPort!') + if (!this.httpTunnelPort) throw new Error("Couldn't spawn tor, no httpTunnelPort!") this.tor = new Tor({ torPath: this.torBinaryPath, @@ -289,10 +340,10 @@ export class ConnectionsManager extends EventEmitter { options: { env: { LD_LIBRARY_PATH: this.torResourcesPath, - HOME: os.homedir() + HOME: os.homedir(), }, - detached: true - } + detached: true, + }, }) if (this.torControlPort) { @@ -309,7 +360,7 @@ export class ConnectionsManager extends EventEmitter { return new Storage(this.quietDir, communityId, { ...this.options, orbitDbDir: `OrbitDB${peerId}`, - ipfsDir: `Ipfs${peerId}` + ipfsDir: `Ipfs${peerId}`, }) } @@ -323,7 +374,7 @@ export class ConnectionsManager extends EventEmitter { return { hiddenService, - peerId: peerId.toJSON() + peerId: peerId.toJSON(), } } @@ -339,7 +390,7 @@ export class ConnectionsManager extends EventEmitter { emitError(this.io, { type: SocketActionTypes.NETWORK, message: ErrorMessages.NETWORK_SETUP_FAILED, - community: community.id + community: community.id, }) return } @@ -350,9 +401,9 @@ export class ConnectionsManager extends EventEmitter { community: { ...community, privateKey: network2.hiddenService.privateKey, - registrarUrl: community.registrarUrl || network2.hiddenService.onionAddress.split('.')[0] + registrarUrl: community.registrarUrl || network2.hiddenService.onionAddress.split('.')[0], }, - network + network, } this.io.emit(SocketActionTypes.NETWORK, payload) } @@ -377,7 +428,7 @@ export class ConnectionsManager extends EventEmitter { emitError(this.io, { type: SocketActionTypes.COMMUNITY, message: ErrorMessages.COMMUNITY_LAUNCH_FAILED, - community: payload.id + community: payload.id, }) return } @@ -396,7 +447,7 @@ export class ConnectionsManager extends EventEmitter { this.io.emit(SocketActionTypes.CONNECTION_PROCESS_INFO, ConnectionProcessInfo.SPAWNING_HIDDEN_SERVICE) const onionAddress: string = await this.tor.spawnHiddenService({ targetPort: ports.libp2pHiddenService, - privKey: payload.hiddenService.privateKey + privKey: payload.hiddenService.privateKey, }) log(`Launching community ${payload.id}, peer: ${payload.peerId.id}`) @@ -409,7 +460,7 @@ export class ConnectionsManager extends EventEmitter { onionAddress, targetPort: ports.libp2pHiddenService, peers: payload.peers, - certs: payload.certs + certs: payload.certs, } return await this.initStorage(initStorageParams) } @@ -430,7 +481,7 @@ export class ConnectionsManager extends EventEmitter { addressPort: 443, targetPort: params.targetPort, bootstrapMultiaddrs: peers, - certs: params.certs + certs: params.certs, } const libp2pObj = await this.initLibp2p(libp2pParams) @@ -449,15 +500,15 @@ export class ConnectionsManager extends EventEmitter { } private attachTorEventsListeners = () => { - this.tor.on(SocketActionTypes.TOR_BOOTSTRAP_PROCESS, (data) => { + this.tor.on(SocketActionTypes.TOR_BOOTSTRAP_PROCESS, data => { this.io.emit(SocketActionTypes.TOR_BOOTSTRAP_PROCESS, data) }) - this.dataServer.on(SocketActionTypes.CONNECTION_PROCESS_INFO, (data) => { + this.dataServer.on(SocketActionTypes.CONNECTION_PROCESS_INFO, data => { this.io.emit(SocketActionTypes.CONNECTION_PROCESS_INFO, data) }) - this.registration.on(SocketActionTypes.CONNECTION_PROCESS_INFO, (data) => { + this.registration.on(SocketActionTypes.CONNECTION_PROCESS_INFO, data => { this.io.emit(SocketActionTypes.CONNECTION_PROCESS_INFO, data) }) } @@ -469,11 +520,11 @@ export class ConnectionsManager extends EventEmitter { this.registration.on(SocketActionTypes.SAVED_OWNER_CERTIFICATE, payload => { this.io.emit(SocketActionTypes.SAVED_OWNER_CERTIFICATE, payload) }) - this.registration.on(RegistrationEvents.SPAWN_HS_FOR_REGISTRAR, async (payload) => { + this.registration.on(RegistrationEvents.SPAWN_HS_FOR_REGISTRAR, async payload => { await this.tor.spawnHiddenService({ targetPort: payload.port, privKey: payload.privateKey, - virtPort: payload.targetPort + virtPort: payload.targetPort, }) }) this.registration.on(RegistrationEvents.ERROR, payload => { @@ -525,38 +576,29 @@ export class ConnectionsManager extends EventEmitter { this.registrarState = ServiceState.LAUNCHING await this.registration.launchRegistrar(args) }) - this.dataServer.on( - SocketActionTypes.SAVED_OWNER_CERTIFICATE, - async (args: SaveOwnerCertificatePayload) => { - const saveCertificatePayload: SaveCertificatePayload = { - certificate: args.certificate, - rootPermsData: args.permsData - } - await this.storage?.saveCertificate(saveCertificatePayload) + this.dataServer.on(SocketActionTypes.SAVED_OWNER_CERTIFICATE, async (args: SaveOwnerCertificatePayload) => { + const saveCertificatePayload: SaveCertificatePayload = { + certificate: args.certificate, + rootPermsData: args.permsData, } - ) - this.dataServer.on( - SocketActionTypes.REGISTER_USER_CERTIFICATE, - async (args: RegisterUserCertificatePayload) => { - if (!this.socksProxyAgent) { - this.createAgent() - } - - await this.registration.sendCertificateRegistrationRequest( - args.serviceAddress, - args.userCsr, - args.communityId, - 120_000, - this.socksProxyAgent - ) - } - ) - this.dataServer.on( - SocketActionTypes.REGISTER_OWNER_CERTIFICATE, - async (args: RegisterOwnerCertificatePayload) => { - await this.registration.registerOwnerCertificate(args) + await this.storage?.saveCertificate(saveCertificatePayload) + }) + this.dataServer.on(SocketActionTypes.REGISTER_USER_CERTIFICATE, async (args: RegisterUserCertificatePayload) => { + if (!this.socksProxyAgent) { + this.createAgent() } - ) + + await this.registration.sendCertificateRegistrationRequest( + args.serviceAddress, + args.userCsr, + args.communityId, + 120_000, + this.socksProxyAgent + ) + }) + this.dataServer.on(SocketActionTypes.REGISTER_OWNER_CERTIFICATE, async (args: RegisterOwnerCertificatePayload) => { + await this.registration.registerOwnerCertificate(args) + }) // Public Channels this.dataServer.on(SocketActionTypes.CREATE_CHANNEL, async (args: CreateChannelPayload) => { @@ -584,51 +626,45 @@ export class ConnectionsManager extends EventEmitter { }) // Direct Messages - this.dataServer.on( - SocketActionTypes.INITIALIZE_CONVERSATION, - async (address, encryptedPhrase) => { - await this.storage?.initializeConversation(address, encryptedPhrase) - } - ) + this.dataServer.on(SocketActionTypes.INITIALIZE_CONVERSATION, async (address, encryptedPhrase) => { + await this.storage?.initializeConversation(address, encryptedPhrase) + }) this.dataServer.on(SocketActionTypes.GET_PRIVATE_CONVERSATIONS, async () => { await this.storage?.getPrivateConversations() }) + this.dataServer.on(SocketActionTypes.SEND_DIRECT_MESSAGE, async (channelId: string, messagePayload) => { + await this.storage?.sendDirectMessage(channelId, messagePayload) + }) + this.dataServer.on(SocketActionTypes.SUBSCRIBE_FOR_DIRECT_MESSAGE_THREAD, async (address: string) => { + await this.storage?.subscribeToDirectMessageThread(address) + }) + this.dataServer.on(SocketActionTypes.SUBSCRIBE_FOR_ALL_CONVERSATIONS, async (conversations: string[]) => { + await this.storage?.subscribeToAllConversations(conversations) + }) + + this.dataServer.on(SocketActionTypes.CLOSE, async () => { + await this.closeAllServices() + }) this.dataServer.on( - SocketActionTypes.SEND_DIRECT_MESSAGE, - async (channelId: string, messagePayload) => { - await this.storage?.sendDirectMessage(channelId, messagePayload) - } - ) - this.dataServer.on( - SocketActionTypes.SUBSCRIBE_FOR_DIRECT_MESSAGE_THREAD, - async (address: string) => { - await this.storage?.subscribeToDirectMessageThread(address) + SocketActionTypes.DELETE_CHANNEL, + async (payload: { channelId: string; ownerPeerId: string }) => { + await this.storage?.deleteChannel(payload) } ) + this.dataServer.on( - SocketActionTypes.SUBSCRIBE_FOR_ALL_CONVERSATIONS, - async (conversations: string[]) => { - await this.storage?.subscribeToAllConversations(conversations) + SocketActionTypes.DELETE_FILES_FROM_CHANNEL, + async (payload: DeleteFilesFromChannelSocketPayload) => { + log('DELETE_FILES_FROM_CHANNEL : payload', payload) + await this.storage?.deleteFilesFromChannel(payload) + // await this.deleteFilesFromTemporaryDir() //crashes on mobile, will be fixes in next versions } ) - - this.dataServer.on(SocketActionTypes.CLOSE, async () => { - await this.closeAllServices() - }) - this.dataServer.on(SocketActionTypes.DELETE_CHANNEL, async (payload: { channelId: string; ownerPeerId: 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.storage?.deleteFilesFromChannel(payload) - // await this.deleteFilesFromTemporaryDir() //crashes on mobile, will be fixes in next versions - }) } private attachStorageListeners = () => { if (!this.storage) return - this.storage.on(SocketActionTypes.CONNECTION_PROCESS_INFO, (data) => { + this.storage.on(SocketActionTypes.CONNECTION_PROCESS_INFO, data => { this.io.emit(SocketActionTypes.CONNECTION_PROCESS_INFO, data) }) this.storage.on(StorageEvents.LOAD_CERTIFICATES, (payload: SendCertificatesResponse) => { @@ -650,12 +686,9 @@ export class ConnectionsManager extends EventEmitter { } this.io.emit(SocketActionTypes.SEND_MESSAGES_IDS, payload) }) - this.storage.on( - StorageEvents.SET_CHANNEL_SUBSCRIBED, - (payload: SetChannelSubscribedPayload) => { - this.io.emit(SocketActionTypes.CHANNEL_SUBSCRIBED, payload) - } - ) + this.storage.on(StorageEvents.SET_CHANNEL_SUBSCRIBED, (payload: SetChannelSubscribedPayload) => { + this.io.emit(SocketActionTypes.CHANNEL_SUBSCRIBED, payload) + }) this.storage.on(StorageEvents.CREATED_CHANNEL, (payload: CreatedChannelResponse) => { this.io.emit(SocketActionTypes.CREATED_CHANNEL, payload) }) @@ -693,13 +726,13 @@ export class ConnectionsManager extends EventEmitter { } // REFACTORING: Move all the below methods to libp2p module - public initLibp2p = async ( - params: InitLibp2pParams - ): Promise<{ libp2p: Libp2p; localAddress: string }> => { + public initLibp2p = async (params: InitLibp2pParams): Promise<{ libp2p: Libp2p; localAddress: string }> => { const localAddress = this.createLibp2pAddress(params.address, params.peerId.toString()) log( - `Initializing libp2p for ${params.peerId.toString()}, bootstrapping with ${params.bootstrapMultiaddrs.length} peers` + `Initializing libp2p for ${params.peerId.toString()}, bootstrapping with ${ + params.bootstrapMultiaddrs.length + } peers` ) this.io.emit(SocketActionTypes.CONNECTION_PROCESS_INFO, ConnectionProcessInfo.INITIALIZING_LIBP2P) const nodeParams: Libp2pNodeParams = { @@ -710,18 +743,18 @@ export class ConnectionsManager extends EventEmitter { cert: params.certs.certificate, key: params.certs.key, ca: params.certs.CA, - targetPort: params.targetPort + targetPort: params.targetPort, } const libp2p: Libp2p = await ConnectionsManager.createBootstrapNode(nodeParams) this.libp2pInstance = libp2p const dialInChunks = new ProcessInChunks(params.bootstrapMultiaddrs, this.dialPeer) - libp2p.addEventListener('peer:discovery', (peer) => { + libp2p.addEventListener('peer:discovery', peer => { log(`${params.peerId.toString()} discovered ${peer.detail.id}`) }) - libp2p.addEventListener('peer:connect', async (peer) => { + libp2p.addEventListener('peer:connect', async peer => { const remotePeerId = peer.detail.remotePeer.toString() log(`${params.peerId.toString()} connected to ${remotePeerId}`) @@ -731,11 +764,11 @@ export class ConnectionsManager extends EventEmitter { this.connectedPeers.set(remotePeerId, DateTime.utc().valueOf()) this.emit(Libp2pEvents.PEER_CONNECTED, { - peers: [remotePeerId] + peers: [remotePeerId], }) }) - libp2p.addEventListener('peer:disconnect', async (peer) => { + libp2p.addEventListener('peer:disconnect', async peer => { const remotePeerId = peer.detail.remotePeer.toString() log(`${params.peerId.toString()} disconnected from ${remotePeerId}`) log(`${libp2p.getConnections().length} open connections`) @@ -760,18 +793,18 @@ export class ConnectionsManager extends EventEmitter { const peerStats: NetworkStats = { peerId: remotePeerId, connectionTime: prev + connectionDuration, - lastSeen: connectionEndTime + lastSeen: connectionEndTime, } // Save updates stats to db await this.localStorage.update(LocalDBKeys.PEERS, { - [remotePeerAddress]: peerStats + [remotePeerAddress]: peerStats, }) this.emit(Libp2pEvents.PEER_DISCONNECTED, { peer: remotePeerId, connectionDuration, - lastSeen: connectionEndTime + lastSeen: connectionEndTime, }) }) @@ -781,7 +814,7 @@ export class ConnectionsManager extends EventEmitter { return { libp2p, - localAddress + localAddress, } } @@ -789,9 +822,7 @@ export class ConnectionsManager extends EventEmitter { await this.libp2pInstance?.dial(multiaddr(peerAddress)) } - public static readonly createBootstrapNode = async ( - params: Libp2pNodeParams - ): Promise => { + public static readonly createBootstrapNode = async (params: Libp2pNodeParams): Promise => { return await ConnectionsManager.defaultLibp2pNode(params) } @@ -812,11 +843,11 @@ export class ConnectionsManager extends EventEmitter { minConnections: 3, maxConnections: 8, dialTimeout: 120_000, - maxParallelDials: 10 + maxParallelDials: 10, }, peerId: params.peerId, addresses: { - listen: params.listenAddresses + listen: params.listenAddresses, }, streamMuxers: [mplex()], connectionEncryption: [noise()], @@ -824,8 +855,8 @@ export class ConnectionsManager extends EventEmitter { enabled: false, hop: { enabled: true, - active: false - } + active: false, + }, }, transports: [ webSockets({ @@ -834,15 +865,16 @@ export class ConnectionsManager extends EventEmitter { agent: params.agent, cert: params.cert, key: params.key, - ca: params.ca + ca: params.ca, }, localAddress: params.localAddress, targetPort: params.targetPort, - createServer - })], + createServer, + }), + ], // @ts-ignore dht: kadDHT(), - pubsub: gossipsub({ allowPublishToZeroPeers: true }) + pubsub: gossipsub({ allowPublishToZeroPeers: true }), }) } catch (err) { log.error('Create libp2p:', err) diff --git a/packages/backend/src/libp2p/processInChunks.test.ts b/packages/backend/src/libp2p/processInChunks.test.ts index 0b62d73bd9..ced8ba536e 100644 --- a/packages/backend/src/libp2p/processInChunks.test.ts +++ b/packages/backend/src/libp2p/processInChunks.test.ts @@ -3,7 +3,8 @@ import { ProcessInChunks } from './processInChunks' describe('ProcessInChunks', () => { it('processes data', async () => { - const mockProcessItem = jest.fn(async () => {}) + const mockProcessItem = jest + .fn(async () => {}) .mockResolvedValueOnce() .mockRejectedValueOnce(new Error('Rejected 1')) .mockResolvedValueOnce() diff --git a/packages/backend/src/libp2p/tests/client-server.ts b/packages/backend/src/libp2p/tests/client-server.ts index 869ecb3c64..71e22c4728 100644 --- a/packages/backend/src/libp2p/tests/client-server.ts +++ b/packages/backend/src/libp2p/tests/client-server.ts @@ -11,7 +11,7 @@ export const createUsersCerts = async ( peerId: 'Qmf3ySkYqLET9xtAtDzvAr5Pp3egK1H3C5iJAZm1SpLert', dmPublicKey: 'dmPublicKey1', signAlg: configCrypto.signAlg, - hashAlg: configCrypto.hashAlg + hashAlg: configCrypto.hashAlg, } const notBeforeDate = new Date(Date.UTC(2010, 11, 28, 10, 10, 10)) @@ -28,7 +28,7 @@ export const createUsersCerts = async ( return { userCert: userCert.userCertString, - userKey: user.userKey + userKey: user.userKey, } } @@ -51,7 +51,7 @@ export const createCertificatesTestHelper = async (onion1: string, onion2: strin servKey: userData1.userKey, userCert: userData2.userCert, - userKey: userData2.userKey + userKey: userData2.userKey, } return pems } diff --git a/packages/backend/src/libp2p/types.ts b/packages/backend/src/libp2p/types.ts index 3baf6f0887..4e0971357d 100644 --- a/packages/backend/src/libp2p/types.ts +++ b/packages/backend/src/libp2p/types.ts @@ -1,10 +1,10 @@ export enum Libp2pEvents { PEER_CONNECTED = 'peerConnected', - PEER_DISCONNECTED = 'peerDisconnected' + PEER_DISCONNECTED = 'peerDisconnected', } export enum ServiceState { DEFAULT = 'notStarted', LAUNCHING = 'launching', - LAUNCHED = 'launched' + LAUNCHED = 'launched', } diff --git a/packages/backend/src/libp2p/utils.ts b/packages/backend/src/libp2p/utils.ts index 8c26bb7274..3675dafbf9 100644 --- a/packages/backend/src/libp2p/utils.ts +++ b/packages/backend/src/libp2p/utils.ts @@ -12,22 +12,20 @@ export function dumpPEM(tag: string, body: string | Certificate | CryptoKey) { // @ts-ignore bodyCert = formatPEM(Buffer.from(body).toString('base64')) } - const result = ( - `-----BEGIN ${tag}-----\n` + - `${bodyCert}\n` + - `-----END ${tag}-----\n` - ) + const result = `-----BEGIN ${tag}-----\n` + `${bodyCert}\n` + `-----END ${tag}-----\n` return Buffer.from(result) } -export async function initConnectionsManagerWithTor (connectionsManager: ConnectionsManager, port: number) { +export async function initConnectionsManagerWithTor(connectionsManager: ConnectionsManager, port: number) { const url = `http://localhost:${port}` const socket = io(url) - const init = new Promise(resolve => { - void connectionsManager.init() - socket.connect() - setTimeout(() => { resolve() }, 200) + const init = new Promise(resolve => { + void connectionsManager.init() + socket.connect() + setTimeout(() => { + resolve() + }, 200) }) await init diff --git a/packages/backend/src/libp2p/websocketOverTor/events.ts b/packages/backend/src/libp2p/websocketOverTor/events.ts index 350f26eb67..8df9682fba 100644 --- a/packages/backend/src/libp2p/websocketOverTor/events.ts +++ b/packages/backend/src/libp2p/websocketOverTor/events.ts @@ -1,6 +1,7 @@ - export type EventCallback = (evt: EventType) => void -export interface EventObject { handleEvent: EventCallback } +export interface EventObject { + handleEvent: EventCallback +} export type EventHandler = EventCallback | EventObject interface Listener { @@ -19,7 +20,7 @@ interface Listener { export class EventEmitter> extends EventTarget { #listeners = new Map() - listenerCount (type: string) { + listenerCount(type: string) { const listeners = this.#listeners.get(type) if (listeners == null) { @@ -29,8 +30,12 @@ export class EventEmitter> extends EventTar return listeners.length } - addEventListener(type: K, listener: EventHandler | null, options?: boolean | AddEventListenerOptions): void - addEventListener (type: string, listener: EventHandler, options?: boolean | AddEventListenerOptions): void { + addEventListener( + type: K, + listener: EventHandler | null, + options?: boolean | AddEventListenerOptions + ): void + addEventListener(type: string, listener: EventHandler, options?: boolean | AddEventListenerOptions): void { super.addEventListener(type, listener, options) let list = this.#listeners.get(type) @@ -42,12 +47,16 @@ export class EventEmitter> extends EventTar list.push({ callback: listener, - once: (options !== true && options !== false && options?.once) ?? false + once: (options !== true && options !== false && options?.once) ?? false, }) } - removeEventListener(type: K, listener?: EventHandler | null, options?: boolean | EventListenerOptions): void - removeEventListener (type: string, listener?: EventHandler, options?: boolean | EventListenerOptions): void { + removeEventListener( + type: K, + listener?: EventHandler | null, + options?: boolean | EventListenerOptions + ): void + removeEventListener(type: string, listener?: EventHandler, options?: boolean | EventListenerOptions): void { super.removeEventListener(type.toString(), listener ?? null, options) let list = this.#listeners.get(type) @@ -60,7 +69,7 @@ export class EventEmitter> extends EventTar this.#listeners.set(type, list) } - dispatchEvent (event: Event): boolean { + dispatchEvent(event: Event): boolean { const result = super.dispatchEvent(event) let list = this.#listeners.get(event.type) @@ -87,7 +96,7 @@ class CustomEventPolyfill extends Event { /** Returns any custom data event was created with. Typically used for synthetic events. */ public detail: T - constructor (message: string, data?: EventInit & { detail: T }) { + constructor(message: string, data?: EventInit & { detail: T }) { super(message, data) // @ts-ignore this.detail = data?.detail diff --git a/packages/backend/src/libp2p/websocketOverTor/filters.ts b/packages/backend/src/libp2p/websocketOverTor/filters.ts index 1b62a562c4..6fb38de8b6 100644 --- a/packages/backend/src/libp2p/websocketOverTor/filters.ts +++ b/packages/backend/src/libp2p/websocketOverTor/filters.ts @@ -1,7 +1,7 @@ import type { Multiaddr } from '@multiformats/multiaddr' -export function all (multiaddrs: Multiaddr[]) { - return multiaddrs.filter((ma) => { - return true - }) +export function all(multiaddrs: Multiaddr[]) { + return multiaddrs.filter(ma => { + return true + }) } diff --git a/packages/backend/src/libp2p/websocketOverTor/index.ts b/packages/backend/src/libp2p/websocketOverTor/index.ts index a40446edbf..0da03bfdf4 100644 --- a/packages/backend/src/libp2p/websocketOverTor/index.ts +++ b/packages/backend/src/libp2p/websocketOverTor/index.ts @@ -52,9 +52,9 @@ class Discovery extends EventEmitter { this.tag = 'channel_18' } - stop() { } - start() { } - end() { } + stop() {} + start() {} + end() {} } export class WebSockets extends EventEmitter { @@ -76,7 +76,7 @@ export class WebSockets extends EventEmitter { this.createServer = createServer } - readonly [Symbol.toStringTag] = '@libp2p/websockets' + readonly [Symbol.toStringTag] = '@libp2p/websockets'; readonly [symbol] = true @@ -89,8 +89,8 @@ export class WebSockets extends EventEmitter { socket = await this._connect(ma, { websocket: { ...this._websocketOpts, - ...this.certData - } + ...this.certData, + }, }) } catch (e) { log.error('error connecting to %s. Details: %s', ma, e.message) @@ -128,7 +128,7 @@ export class WebSockets extends EventEmitter { return { cert: dumpPEM('CERTIFICATE', cert.toString()), key: dumpPEM('PRIVATE KEY', key.toString()), - ca: [dumpPEM('CERTIFICATE', _ca.toString())] + ca: [dumpPEM('CERTIFICATE', _ca.toString())], } } @@ -145,9 +145,7 @@ export class WebSockets extends EventEmitter { errorPromise.reject(event) } - const myUri = `${toUri(ma)}/?remoteAddress=${encodeURIComponent( - this.localAddress - )}` + const myUri = `${toUri(ma)}/?remoteAddress=${encodeURIComponent(this.localAddress)}` const rawSocket = connect(myUri, Object.assign({ binary: true }, options)) if (rawSocket.socket.on) { @@ -175,7 +173,10 @@ export class WebSockets extends EventEmitter { } // Already aborted? - if (options.signal.aborted) { onAbort(); return } + if (options.signal.aborted) { + onAbort() + return + } options.signal.addEventListener('abort', onAbort) }) @@ -206,55 +207,57 @@ export class WebSockets extends EventEmitter { const serverHttps = https.createServer({ ...this.certData, requestCert: true, - enableTrace: false + enableTrace: false, }) const optionsServ = { server: serverHttps, verifyClient: function (_info: any, done: (res: boolean) => void) { done(true) - } + }, } const server = this.createServer(optionsServ) server.__connections = [] - server.on('connection', async (stream, request) => { - let maConn: MultiaddrConnection - let conn: Connection - // eslint-disable-next-line - const query = url.parse(request.url, true).query - log('server connecting with', query.remoteAddress) - if (!query.remoteAddress) return - - const remoteAddress = query.remoteAddress.toString() - try { - maConn = socketToMaConn(stream, multiaddr(remoteAddress)) - const peer = { - id: PeerId.createFromB58String(remoteAddress.split('/p2p/')[1]), - multiaddrs: [maConn.remoteAddr] + server + .on('connection', async (stream, request) => { + let maConn: MultiaddrConnection + let conn: Connection + // eslint-disable-next-line + const query = url.parse(request.url, true).query + log('server connecting with', query.remoteAddress) + if (!query.remoteAddress) return + + const remoteAddress = query.remoteAddress.toString() + try { + maConn = socketToMaConn(stream, multiaddr(remoteAddress)) + const peer = { + id: PeerId.createFromB58String(remoteAddress.split('/p2p/')[1]), + multiaddrs: [maConn.remoteAddr], + } + this.discovery.emit('peer', peer) + log('new inbound connection %s', maConn.remoteAddr) + } catch (e) { + log.error(`Failed to convert stream into a MultiaddrConnection for ${remoteAddress}:`, e) + return } - this.discovery.emit('peer', peer) - log('new inbound connection %s', maConn.remoteAddr) - } catch (e) { - log.error(`Failed to convert stream into a MultiaddrConnection for ${remoteAddress}:`, e) - return - } - try { - conn = await upgrader.upgradeInbound(maConn) - } catch (err) { - log.error('inbound connection failed to upgrade', err) - await maConn?.close(); return - } + try { + conn = await upgrader.upgradeInbound(maConn) + } catch (err) { + log.error('inbound connection failed to upgrade', err) + await maConn?.close() + return + } - log('inbound connection %s upgraded', maConn.remoteAddr) + log('inbound connection %s upgraded', maConn.remoteAddr) - trackConn(server, maConn) + trackConn(server, maConn) - if (handler) handler(conn) - listener.emit('connection', conn) - }) + if (handler) handler(conn) + listener.emit('connection', conn) + }) .on('listening', () => listener.emit('listening')) .on('error', err => listener.emit('error', err)) .on('close', () => listener.emit('close')) @@ -264,18 +267,20 @@ export class WebSockets extends EventEmitter { let listeningMultiaddr: Multiaddr listener.close = async () => { - server.__connections?.forEach(async maConn => { await maConn.close() }) + server.__connections?.forEach(async maConn => { + await maConn.close() + }) await server.close() } - listener.addEventListener = () => { } + listener.addEventListener = () => {} listener.listen = async (ma: Multiaddr) => { listeningMultiaddr = ma const listenOptions = { ...ma.toOptions(), - port: this.targetPort + port: this.targetPort, } return await server.listen(listenOptions) diff --git a/packages/backend/src/libp2p/websocketOverTor/listener.browser.ts b/packages/backend/src/libp2p/websocketOverTor/listener.browser.ts index 2d2457b9a8..00237de3d3 100644 --- a/packages/backend/src/libp2p/websocketOverTor/listener.browser.ts +++ b/packages/backend/src/libp2p/websocketOverTor/listener.browser.ts @@ -1,4 +1,3 @@ - -export function createListener () { +export function createListener() { throw new Error('WebSocket Servers can not be created in the browser!') } diff --git a/packages/backend/src/libp2p/websocketOverTor/socket-to-conn.ts b/packages/backend/src/libp2p/websocketOverTor/socket-to-conn.ts index 18e578024d..572e3408ba 100644 --- a/packages/backend/src/libp2p/websocketOverTor/socket-to-conn.ts +++ b/packages/backend/src/libp2p/websocketOverTor/socket-to-conn.ts @@ -16,12 +16,16 @@ export interface SocketToConnOptions extends AbortOptions { // Convert a stream into a MultiaddrConnection // https://github.com/libp2p/interface-transport#multiaddrconnection -export function socketToMaConn(stream: DuplexWebSocket, remoteAddr: Multiaddr, options?: SocketToConnOptions): MultiaddrConnection { +export function socketToMaConn( + stream: DuplexWebSocket, + remoteAddr: Multiaddr, + options?: SocketToConnOptions +): MultiaddrConnection { options = options ?? {} const maConn: MultiaddrConnection = { async sink(source) { - if ((options?.signal) != null) { + if (options?.signal != null) { source = AbortSource(source, options.signal) } @@ -34,7 +38,7 @@ export function socketToMaConn(stream: DuplexWebSocket, remoteAddr: Multiaddr, o } }, - source: (options.signal != null) ? AbortSource(stream.source, options.signal) : stream.source, + source: options.signal != null ? AbortSource(stream.source, options.signal) : stream.source, remoteAddr, @@ -45,29 +49,30 @@ export function socketToMaConn(stream: DuplexWebSocket, remoteAddr: Multiaddr, o try { // Possibly libp2p used the wrong pTimeout arguments and this was our problem, but why did they used it? TS off or something. - await pTimeout(stream.close(), - CLOSE_TIMEOUT - ) + await pTimeout(stream.close(), CLOSE_TIMEOUT) } catch (err) { const { host, port } = maConn.remoteAddr.toOptions() - log('timeout closing stream to %s:%s after %dms, destroying it manually', - host, port, Date.now() - start) + log('timeout closing stream to %s:%s after %dms, destroying it manually', host, port, Date.now() - start) stream.destroy() } finally { maConn.timeline.close = Date.now() } - } + }, } - stream.socket.addEventListener('close', () => { - // In instances where `close` was not explicitly called, - // such as an iterable stream ending, ensure we have set the close - // timeline - if (maConn.timeline.close == null) { - maConn.timeline.close = Date.now() - } - }, { once: true }) + stream.socket.addEventListener( + 'close', + () => { + // In instances where `close` was not explicitly called, + // such as an iterable stream ending, ensure we have set the close + // timeline + if (maConn.timeline.close == null) { + maConn.timeline.close = Date.now() + } + }, + { once: true } + ) return maConn } diff --git a/packages/backend/src/libp2p/websocketOverTor/websocketOverTor.tor.test.ts b/packages/backend/src/libp2p/websocketOverTor/websocketOverTor.tor.test.ts index ab7d16e659..acd2e3d186 100644 --- a/packages/backend/src/libp2p/websocketOverTor/websocketOverTor.tor.test.ts +++ b/packages/backend/src/libp2p/websocketOverTor/websocketOverTor.tor.test.ts @@ -56,10 +56,10 @@ describe('websocketOverTor', () => { options: { env: { LD_LIBRARY_PATH: torDirForPlatform(), - HOME: os.homedir() + HOME: os.homedir(), }, - detached: true - } + detached: true, + }, }) await tor.init() @@ -69,14 +69,14 @@ describe('websocketOverTor', () => { addEventListener, removeEventListener, aborted: false, - onabort: null, - reason: undefined, - throwIfAborted: function (): void { - throw new Error('Function not implemented.') - }, - dispatchEvent: function (event: Event): boolean { - throw new Error('Function not implemented.') - } + onabort: null, + reason: undefined, + throwIfAborted: function (): void { + throw new Error('Function not implemented.') + }, + dispatchEvent: function (event: Event): boolean { + throw new Error('Function not implemented.') + }, } }) @@ -91,7 +91,7 @@ describe('websocketOverTor', () => { it.each([ ['string', String], - ['array', Array] + ['array', Array], ])('connects successfully with CA passed as %s', async (_name: string, caType: (ca: string) => any) => { const pems = await createCertificatesTestHelper(`${service1.onionAddress}`, `${service2.onionAddress}`) // In case test fails on CI, we will be able to conduct test against failing credentials. @@ -102,19 +102,19 @@ describe('websocketOverTor', () => { console.log(`userCert ${pems.userCert}`) console.log(`userKey ${pems.userKey}`) const prepareListenerArg: CreateListenerOptions = { - handler: (x) => x, + handler: x => x, upgrader: { // @ts-expect-error upgradeOutbound, // @ts-expect-error - upgradeInbound - } + upgradeInbound, + }, } const signal = { ...abortSignalOpts, addEventListener, - removeEventListener + removeEventListener, } const peerId1 = 'Qme5NiSQ6V3cc3nyfYVtkkXDPGBSYEVUNCN5sM4DbyYc7s' @@ -128,11 +128,11 @@ describe('websocketOverTor', () => { agent, cert: pems.servCert, key: pems.servKey, - ca: caType(pems.ca) + ca: caType(pems.ca), }, localAddress: createLibp2pAddress(service1.onionAddress, peerId1), targetPort: port1Target, - createServer + createServer, } const websocketsOverTorData2 = { @@ -141,11 +141,11 @@ describe('websocketOverTor', () => { agent, cert: pems.servCert, key: pems.servKey, - ca: caType(pems.ca) + ca: caType(pems.ca), }, localAddress: createLibp2pAddress(service2.onionAddress, peerId2), targetPort: port2Target, - createServer + createServer, } const multiAddress = multiaddr(createLibp2pAddress(service1.onionAddress, peerId1)) @@ -167,7 +167,7 @@ describe('websocketOverTor', () => { try { await ws2.dial(multiAddress, { signal, - upgrader: prepareListenerArg.upgrader + upgrader: prepareListenerArg.upgrader, }) } catch (e) { console.log(`caught Error ${e.message as string}, retryCount is ${retryCount}`) @@ -189,19 +189,19 @@ describe('websocketOverTor', () => { const anotherPems = await createCertificatesTestHelper(`${service1.onionAddress}`, `${service2.onionAddress}`) const prepareListenerArg: CreateListenerOptions = { - handler: (x) => x, + handler: x => x, upgrader: { // @ts-expect-error upgradeOutbound, // @ts-expect-error - upgradeInbound - } + upgradeInbound, + }, } const signal = { ...abortSignalOpts, addEventListener, - removeEventListener + removeEventListener, } const peerId1 = 'Qme5NiSQ6V3cc3nyfYVtkkXDPGBSYEVUNCN5sM4DbyYc7s' @@ -215,11 +215,11 @@ describe('websocketOverTor', () => { agent, cert: pems.servCert, key: pems.servKey, - ca: [pems.ca] + ca: [pems.ca], }, localAddress: createLibp2pAddress(service1.onionAddress, peerId1), targetPort: port1Target, - createServer + createServer, } const websocketsOverTorDataClient = { @@ -228,11 +228,11 @@ describe('websocketOverTor', () => { agent, cert: pems.servCert, key: pems.servKey, - ca: [anotherPems.ca] + ca: [anotherPems.ca], }, localAddress: createLibp2pAddress(service2.onionAddress, peerId2), targetPort: port2Target, - createServer + createServer, } const multiAddress = multiaddr(createLibp2pAddress(service1.onionAddress, peerId1)) @@ -246,10 +246,12 @@ describe('websocketOverTor', () => { const onConnection = jest.fn() listener.on('connection', onConnection) - await expect(ws2.dial(multiAddress, { - signal, - upgrader: prepareListenerArg.upgrader - })).rejects.toBeTruthy() + await expect( + ws2.dial(multiAddress, { + signal, + upgrader: prepareListenerArg.upgrader, + }) + ).rejects.toBeTruthy() }) it('rejects connection if server cert is invalid', async () => { @@ -257,19 +259,19 @@ describe('websocketOverTor', () => { const anotherPems = await createCertificatesTestHelper(`${service1.onionAddress}`, `${service2.onionAddress}`) const prepareListenerArg: CreateListenerOptions = { - handler: (x) => x, + handler: x => x, upgrader: { // @ts-expect-error upgradeOutbound, // @ts-expect-error - upgradeInbound - } + upgradeInbound, + }, } const signal: AbortSignal = { ...abortSignalOpts, addEventListener, - removeEventListener + removeEventListener, } const peerId1 = 'Qme5NiSQ6V3cc3nyfYVtkkXDPGBSYEVUNCN5sM4DbyYc7s' @@ -283,11 +285,11 @@ describe('websocketOverTor', () => { agent, cert: anotherPems.servCert, key: anotherPems.servKey, - ca: [pems.ca] + ca: [pems.ca], }, localAddress: createLibp2pAddress(service1.onionAddress, peerId1), targetPort: port1Target, - createServer + createServer, } const websocketsOverTorData2 = { @@ -296,11 +298,11 @@ describe('websocketOverTor', () => { agent, cert: pems.servCert, key: pems.servKey, - ca: [pems.ca] + ca: [pems.ca], }, localAddress: createLibp2pAddress(service2.onionAddress, peerId2), targetPort: port2Target, - createServer + createServer, } const multiAddress = multiaddr(createLibp2pAddress(service1.onionAddress, peerId1)) @@ -315,9 +317,11 @@ describe('websocketOverTor', () => { const onConnection = jest.fn() listener.on('connection', onConnection) - await expect(ws2.dial(multiAddress, { - signal, - upgrader: prepareListenerArg.upgrader - })).rejects.toBeTruthy() + await expect( + ws2.dial(multiAddress, { + signal, + upgrader: prepareListenerArg.upgrader, + }) + ).rejects.toBeTruthy() }) }) diff --git a/packages/backend/src/registrarTest/parseRegistrarTestResults.ts b/packages/backend/src/registrarTest/parseRegistrarTestResults.ts index e114f1c150..1488e3b671 100644 --- a/packages/backend/src/registrarTest/parseRegistrarTestResults.ts +++ b/packages/backend/src/registrarTest/parseRegistrarTestResults.ts @@ -48,15 +48,17 @@ const getStatistics = (results: Record) => { slowestSuccessfullReceivedResultsTime = Math.max(slowestSuccessfullReceivedResultsTime, data.receivedResultsTime) // @ts-ignore if (data.receivedResultsTime) { -for (const [requestCount, requestData] of Object.entries(data)) { - if (requestData.fetchTime) { - fetchTimeSum += requestData.fetchTime - requestCountSuccessRate[requestCount] ? requestCountSuccessRate[requestCount]++ : requestCountSuccessRate[requestCount] = 1 - fastestFetch = Math.min(requestData.fetchTime, fastestFetch) - slowestFetch = Math.max(requestData.fetchTime, slowestFetch) + for (const [requestCount, requestData] of Object.entries(data)) { + if (requestData.fetchTime) { + fetchTimeSum += requestData.fetchTime + requestCountSuccessRate[requestCount] + ? requestCountSuccessRate[requestCount]++ + : (requestCountSuccessRate[requestCount] = 1) + fastestFetch = Math.min(requestData.fetchTime, fastestFetch) + slowestFetch = Math.max(requestData.fetchTime, slowestFetch) + } } } -} } return { dirPath, @@ -68,10 +70,15 @@ for (const [requestCount, requestData] of Object.entries(data)) { slowestSuccessfullReceivedResultsTime, fastestFetch, slowestFetch, - requestCountSuccessRate + requestCountSuccessRate, } } -parse().then((data) => { - console.log(getStatistics(data)) -}, (error) => { console.log('ERROR', error) }) +parse().then( + data => { + console.log(getStatistics(data)) + }, + error => { + console.log('ERROR', error) + } +) diff --git a/packages/backend/src/registrarTest/torRegistrarTest.ts b/packages/backend/src/registrarTest/torRegistrarTest.ts index b051312337..90a44a3593 100644 --- a/packages/backend/src/registrarTest/torRegistrarTest.ts +++ b/packages/backend/src/registrarTest/torRegistrarTest.ts @@ -2,11 +2,7 @@ import express from 'express' import createHttpsProxyAgent from 'https-proxy-agent' import fetch, { type Response } from 'node-fetch' import fs from 'fs' -import { - createTmpDir, - spawnTorProcess, - tmpQuietDirPath -} from '../common/testUtils' +import { createTmpDir, spawnTorProcess, tmpQuietDirPath } from '../common/testUtils' import logger from '../logger' import { EventEmitter } from 'events' @@ -17,7 +13,7 @@ const program = new Command() enum TestMode { NEWNYM = 'newnym', - REGULAR = 'regular' + REGULAR = 'regular', } program @@ -125,14 +121,14 @@ const sendRequest = async ( method: 'POST', body: JSON.stringify({ counter }), headers: { 'Content-Type': 'application/json' }, - agent + agent, } try { log('Sending request to', serviceAddress) console.time(`fetch (${counter}) ${serviceAddress}`) results[serviceAddress][counter] = { - startFetchTime: new Date() + startFetchTime: new Date(), } response = await fetch('http://' + serviceAddress + '.onion/test', options) } catch (e) { @@ -170,9 +166,7 @@ const destroyHiddenServices = async () => { for (const [key, data] of torServices) { if (!data.onionAddress) continue await data.tor.destroyHiddenService(data.onionAddress) - log( - `destroyed hidden service for instance ${key} with onion address ${data.onionAddress}` - ) + log(`destroyed hidden service for instance ${key} with onion address ${data.onionAddress}`) } } @@ -187,24 +181,36 @@ const testWithDelayedNewnym = async () => { } } - async function resolveTimeout(func: (...args: any[]) => Promise, address: string, port: number, tor: Tor, requestCounter: number, delay: number): Promise { - return await new Promise( - (resolve, reject) => { - const timeoutId = setTimeout(async (address, port, tor, requestCounter) => { + async function resolveTimeout( + func: (...args: any[]) => Promise, + address: string, + port: number, + tor: Tor, + requestCounter: number, + delay: number + ): Promise { + return await new Promise((resolve, reject) => { + const timeoutId = setTimeout( + async (address, port, tor, requestCounter) => { log(`Sending request after ${delay}ms`) try { resolve(func(address, port, tor, requestCounter)) } catch (e) { reject(new Error(`request rejected. ${e.message}`)) } - }, delay, address, port, tor, requestCounter) - - eventEmmiter.on(`${address}-success`, () => { - log(`Clearing timeout (${requestCounter}) ${address}`) - clearTimeout(timeoutId) - }) - } - ) + }, + delay, + address, + port, + tor, + requestCounter + ) + + eventEmmiter.on(`${address}-success`, () => { + log(`Clearing timeout (${requestCounter}) ${address}`) + clearTimeout(timeoutId) + }) + }) } for (let serverCounter = 0; serverCounter < servers.length; serverCounter++) { @@ -216,14 +222,9 @@ const testWithDelayedNewnym = async () => { } const tor = servers[serverCounter].tor for (let rq = 0; rq < requestsCount; rq++) { - requests.push(resolveTimeout( - sendRequest, - serverOnionAddress, - clients[serverCounter].httpTunnelPort, - tor, - rq, - rq * 10_500 - )) + requests.push( + resolveTimeout(sendRequest, serverOnionAddress, clients[serverCounter].httpTunnelPort, tor, rq, rq * 10_500) + ) } let responseData = null let response: Response | null = null @@ -239,7 +240,8 @@ const testWithDelayedNewnym = async () => { } const end = new Date() - results[serverOnionAddress].receivedResultsTime = (end.getTime() - results[serverOnionAddress].requestsStartTime.getTime()) / 1000 // Since scheduling Promise.any + results[serverOnionAddress].receivedResultsTime = + (end.getTime() - results[serverOnionAddress].requestsStartTime.getTime()) / 1000 // Since scheduling Promise.any try { responseData = await response?.json() @@ -251,12 +253,14 @@ const testWithDelayedNewnym = async () => { if (!responseData) return const { counter } = responseData - results[serverOnionAddress][counter].fetchTime = (end.getTime() - results[serverOnionAddress][counter].startFetchTime.getTime()) / 1000 + results[serverOnionAddress][counter].fetchTime = + (end.getTime() - results[serverOnionAddress][counter].startFetchTime.getTime()) / 1000 } log('END') } -const sendRequests = async () => { // No newnym, send next request if previous one timed out +const sendRequests = async () => { + // No newnym, send next request if previous one timed out const servers: TorService[] = [] const clients: TorService[] = [] for (const [key, torData] of torServices) { @@ -281,12 +285,7 @@ const sendRequests = async () => { // No newnym, send next request if previous o let responseData = null for (let rq = 0; rq < requestsCount; rq++) { try { - response = await sendRequest( - serverOnionAddress, - clients[serverCounter].httpTunnelPort, - tor, - rq - ) + response = await sendRequest(serverOnionAddress, clients[serverCounter].httpTunnelPort, tor, rq) log('got response', rq, serverOnionAddress) results[serverOnionAddress].endTime = new Date() results[serverOnionAddress].statusCode = response.status @@ -298,7 +297,8 @@ const sendRequests = async () => { // No newnym, send next request if previous o } const end = new Date() - results[serverOnionAddress].receivedResultsTime = (end.getTime() - results[serverOnionAddress].requestsStartTime.getTime()) / 1000 // Since scheduling Promise.any + results[serverOnionAddress].receivedResultsTime = + (end.getTime() - results[serverOnionAddress].requestsStartTime.getTime()) / 1000 // Since scheduling Promise.any try { responseData = await response?.json() @@ -310,7 +310,8 @@ const sendRequests = async () => { // No newnym, send next request if previous o if (!responseData) return const { counter } = responseData - results[serverOnionAddress][counter].fetchTime = (end.getTime() - results[serverOnionAddress][counter].startFetchTime.getTime()) / 1000 + results[serverOnionAddress][counter].fetchTime = + (end.getTime() - results[serverOnionAddress][counter].startFetchTime.getTime()) / 1000 } } @@ -329,7 +330,10 @@ const main = async () => { log('destroyed hidden services') await killMesh() log('RESULTS', JSON.stringify(results)) - fs.writeFileSync(`${torBinName}_${new Date().toISOString()}_mode_${mode}_guards${guardsCount}_vanguards${vanguargsLiteEnabled}.json`, JSON.stringify(results)) + fs.writeFileSync( + `${torBinName}_${new Date().toISOString()}_mode_${mode}_guards${guardsCount}_vanguards${vanguargsLiteEnabled}.json`, + JSON.stringify(results) + ) log('after killing mesh') } // eslint-disable-next-line diff --git a/packages/backend/src/registration/functions.test.ts b/packages/backend/src/registration/functions.test.ts index 9c137eadc1..13a87400e9 100644 --- a/packages/backend/src/registration/functions.test.ts +++ b/packages/backend/src/registration/functions.test.ts @@ -1,5 +1,12 @@ - -import { configCrypto, createRootCA, createUserCert, createUserCsr, type RootCA, verifyUserCert, type UserCsr } from '@quiet/identity' +import { + configCrypto, + createRootCA, + createUserCert, + createUserCsr, + type RootCA, + verifyUserCert, + type UserCsr, +} from '@quiet/identity' import createHttpsProxyAgent from 'https-proxy-agent' import { Time } from 'pkijs' import { type DirResult } from 'tmp' @@ -28,7 +35,11 @@ describe('Registration service', () => { jest.clearAllMocks() tmpDir = createTmpDir() registrationService = null - certRoot = await createRootCA(new Time({ type: 1, value: new Date() }), new Time({ type: 1, value: new Date(2030, 1, 1) }), 'testRootCA') + certRoot = await createRootCA( + new Time({ type: 1, value: new Date() }), + new Time({ type: 1, value: new Date(2030, 1, 1) }), + 'testRootCA' + ) permsData = { certificate: certRoot.rootCertString, privKey: certRoot.rootKeyString } userCsr = await createUserCsr({ nickname: 'userName', @@ -36,7 +47,7 @@ describe('Registration service', () => { peerId: 'Qmf3ySkYqLET9xtAtDzvAr5Pp3egK1H3C5iJAZm1SpLEp6', dmPublicKey: 'testdmPublicKey', signAlg: configCrypto.signAlg, - hashAlg: configCrypto.hashAlg + hashAlg: configCrypto.hashAlg, }) invalidUserCsr = 'invalidUserCsr' fetch = await import('node-fetch') @@ -69,12 +80,16 @@ describe('Registration service', () => { peerId: 'Qmf3ySkYqLET9xtAtDzvAr5Pp3egK1H3C5iJAZm1SpLEp6', dmPublicKey: 'testdmPublicKey1', signAlg: configCrypto.signAlg, - hashAlg: configCrypto.hashAlg + hashAlg: configCrypto.hashAlg, }) - const userCert = await createUserCert(certRoot.rootCertString, certRoot.rootKeyString, user.userCsr, new Date(), new Date(2030, 1, 1)) - const responseData = await registerUser( + const userCert = await createUserCert( + certRoot.rootCertString, + certRoot.rootKeyString, user.userCsr, - permsData, [userCert.userCertString], 'ownerCert') + new Date(), + new Date(2030, 1, 1) + ) + const responseData = await registerUser(user.userCsr, permsData, [userCert.userCertString], 'ownerCert') expect(responseData.status).toEqual(200) const isProperUserCert = await verifyUserCert(certRoot.rootCertString, responseData.body.certificate) expect(isProperUserCert.result).toBe(true) @@ -89,46 +104,44 @@ describe('Registration service', () => { peerId: 'Qmf3ySkYqLET9xtAtDzvAr5Pp3egK1H3C5iJAZm1SpLEp6', dmPublicKey: 'testdmPublicKey1', signAlg: configCrypto.signAlg, - hashAlg: configCrypto.hashAlg + hashAlg: configCrypto.hashAlg, }) - const userCert = await createUserCert(certRoot.rootCertString, certRoot.rootKeyString, user.userCsr, new Date(), new Date(2030, 1, 1)) + const userCert = await createUserCert( + certRoot.rootCertString, + certRoot.rootKeyString, + user.userCsr, + new Date(), + new Date(2030, 1, 1) + ) const userNew = await createUserCsr({ nickname: 'username', commonName: 'abcd.onion', peerId: 'QmS9vJkgbea9EgzHvVPqhj1u4tH7YKq7eteDN7gnG5zUmc', dmPublicKey: 'testdmPublicKey2', signAlg: configCrypto.signAlg, - hashAlg: configCrypto.hashAlg + hashAlg: configCrypto.hashAlg, }) - const response = await registerUser( - userNew.userCsr, - permsData, [userCert.userCertString], 'ownerCert' - ) + const response = await registerUser(userNew.userCsr, permsData, [userCert.userCertString], 'ownerCert') expect(response.status).toEqual(403) }) it('returns 400 if no csr in data or csr has wrong format', async () => { for (const invalidCsr of ['', 'abcd']) { - const response = await registerUser( - invalidCsr, - permsData, [], 'ownerCert' - ) + const response = await registerUser(invalidCsr, permsData, [], 'ownerCert') expect(response.status).toEqual(400) } }) it('returns 400 if csr is lacking a field', async () => { - const csr = 'MIIBFTCBvAIBADAqMSgwFgYKKwYBBAGDjBsCARMIdGVzdE5hbWUwDgYDVQQDEwdaYmF5IENBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEGPGHpJzE/CvL7l/OmTSfYQrhhnWQrYw3GgWB1raCTSeFI/MDVztkBOlxwdUWSm10+1OtKVUWeMKaMtyIYFcPPqAwMC4GCSqGSIb3DQEJDjEhMB8wHQYDVR0OBBYEFLjaEh+cnNhsi5qDsiMB/ZTzZFfqMAoGCCqGSM49BAMCA0gAMEUCIFwlob/Igab05EozU0e/lsG7c9BxEy4M4c4Jzru2vasGAiEAqFTQuQr/mVqTHO5vybWm/iNDk8vh88K6aBCCGYqIfdw=' - const response = await registerUser( - csr, - permsData, [], 'ownerCert' - ) + const csr = + 'MIIBFTCBvAIBADAqMSgwFgYKKwYBBAGDjBsCARMIdGVzdE5hbWUwDgYDVQQDEwdaYmF5IENBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEGPGHpJzE/CvL7l/OmTSfYQrhhnWQrYw3GgWB1raCTSeFI/MDVztkBOlxwdUWSm10+1OtKVUWeMKaMtyIYFcPPqAwMC4GCSqGSIb3DQEJDjEhMB8wHQYDVR0OBBYEFLjaEh+cnNhsi5qDsiMB/ZTzZFfqMAoGCCqGSM49BAMCA0gAMEUCIFwlob/Igab05EozU0e/lsG7c9BxEy4M4c4Jzru2vasGAiEAqFTQuQr/mVqTHO5vybWm/iNDk8vh88K6aBCCGYqIfdw=' + const response = await registerUser(csr, permsData, [], 'ownerCert') expect(response.status).toEqual(400) }) it('returns 404 if fetching registrar address throws error', async () => { - console.log(fetch); - (fetch).default.mockRejectedValue('User aborted request') + console.log(fetch) + fetch.default.mockRejectedValue('User aborted request') const communityId = 'communityID' const response = await sendCertificateRegistrationRequest( 'QmS9vJkgbea9EgzHvVPqhj1u4tH7YKq7eteDN7gnG5zUmc', @@ -142,16 +155,17 @@ describe('Registration service', () => { type: SocketActionTypes.REGISTRAR, code: ErrorCodes.NOT_FOUND, message: ErrorMessages.REGISTRAR_NOT_FOUND, - community: communityId + community: communityId, }) }) // FIXME: fix node-fetch mock it.skip('returns registration data on successfull registration', async () => { - const csr = 'MIIBFTCBvAIBADAqMSgwFgYKKwYBBAGDjBsCARMIdGVzdE5hbWUwDgYDVQQDEwdaYmF5IENBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEGPGHpJzE/CvL7l/OmTSfYQrhhnWQrYw3GgWB1raCTSeFI/MDVztkBOlxwdUWSm10+1OtKVUWeMKaMtyIYFcPPqAwMC4GCSqGSIb3DQEJDjEhMB8wHQYDVR0OBBYEFLjaEh+cnNhsi5qDsiMB/ZTzZFfqMAoGCCqGSM49BAMCA0gAMEUCIFwlob/Igab05EozU0e/lsG7c9BxEy4M4c4Jzru2vasGAiEAqFTQuQr/mVqTHO5vybWm/iNDk8vh88K6aBCCGYqIfdw=' + const csr = + 'MIIBFTCBvAIBADAqMSgwFgYKKwYBBAGDjBsCARMIdGVzdE5hbWUwDgYDVQQDEwdaYmF5IENBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEGPGHpJzE/CvL7l/OmTSfYQrhhnWQrYw3GgWB1raCTSeFI/MDVztkBOlxwdUWSm10+1OtKVUWeMKaMtyIYFcPPqAwMC4GCSqGSIb3DQEJDjEhMB8wHQYDVR0OBBYEFLjaEh+cnNhsi5qDsiMB/ZTzZFfqMAoGCCqGSM49BAMCA0gAMEUCIFwlob/Igab05EozU0e/lsG7c9BxEy4M4c4Jzru2vasGAiEAqFTQuQr/mVqTHO5vybWm/iNDk8vh88K6aBCCGYqIfdw=' const registrarResponse = { certificate: [csr], rootCa: certRoot.rootCertString } - console.log(new Response(JSON.stringify(registrarResponse))); - (fetch).default.mockReturnValue(new Response(JSON.stringify(registrarResponse))) + console.log(new Response(JSON.stringify(registrarResponse))) + fetch.default.mockReturnValue(new Response(JSON.stringify(registrarResponse))) const communityId = 'communityID' const response = await sendCertificateRegistrationRequest( 'QmS9vJkgbea9EgzHvVPqhj1u4tH7YKq7eteDN7gnG5zUmc', @@ -164,7 +178,7 @@ describe('Registration service', () => { expect(response.eventType).toBe(SocketActionTypes.SEND_USER_CERTIFICATE) expect(response.data).toEqual({ communityId, - payload: registrarResponse + payload: registrarResponse, }) }) }) diff --git a/packages/backend/src/registration/functions.ts b/packages/backend/src/registration/functions.ts index a3ebc9cd8e..bde4d2392e 100644 --- a/packages/backend/src/registration/functions.ts +++ b/packages/backend/src/registration/functions.ts @@ -1,4 +1,12 @@ -import { createUserCert, loadCSR, CertFieldsTypes, getReqFieldValue, keyFromCertificate, parseCertificate, getCertFieldValue } from '@quiet/identity' +import { + createUserCert, + loadCSR, + CertFieldsTypes, + getReqFieldValue, + keyFromCertificate, + parseCertificate, + getCertFieldValue, +} from '@quiet/identity' import { IsBase64, IsNotEmpty, validate } from 'class-validator' import { type CertificationRequest } from 'pkijs' import { type Agent } from 'http' @@ -9,7 +17,16 @@ import { CsrContainsFields, IsCsr } from './validators' import { RegistrationEvents } from './types' import { getUsersAddresses } from '../common/utils' -import { ErrorCodes, ErrorMessages, type ErrorPayload, type PermsData, SocketActionTypes, type SuccessfullRegistrarionResponse, type User, type UserCertificatePayload } from '@quiet/types' +import { + ErrorCodes, + ErrorMessages, + type ErrorPayload, + type PermsData, + SocketActionTypes, + type SuccessfullRegistrarionResponse, + type User, + type UserCertificatePayload, +} from '@quiet/types' const log = logger('registration') class UserCsrData { @@ -25,48 +42,48 @@ export interface RegistrarResponse { body: any } - // REFACTORING: Move this method to identity package - export const pubKeyMatch = (cert: string, parsedCsr: CertificationRequest): boolean => { - const parsedCertificate = parseCertificate(cert) - const pubKey = keyFromCertificate(parsedCertificate) - const pubKeyCsr = keyFromCertificate(parsedCsr) +// REFACTORING: Move this method to identity package +export const pubKeyMatch = (cert: string, parsedCsr: CertificationRequest): boolean => { + const parsedCertificate = parseCertificate(cert) + const pubKey = keyFromCertificate(parsedCertificate) + const pubKeyCsr = keyFromCertificate(parsedCsr) - if (pubKey === pubKeyCsr) { - return true - } - return false + if (pubKey === pubKeyCsr) { + return true } + return false +} - export const registerOwner = async (userCsr: string, permsData: PermsData): Promise => { - const userData = new UserCsrData() - userData.csr = userCsr - const validationErrors = await validate(userData) - if (validationErrors.length > 0) { - throw new Error(`Validation errors: ${validationErrors}`) - } - const userCert = await createUserCert( - permsData.certificate, - permsData.privKey, - userCsr, - new Date(), - new Date(2030, 1, 1) - ) - return userCert.userCertString +export const registerOwner = async (userCsr: string, permsData: PermsData): Promise => { + const userData = new UserCsrData() + userData.csr = userCsr + const validationErrors = await validate(userData) + if (validationErrors.length > 0) { + throw new Error(`Validation errors: ${validationErrors}`) } + const userCert = await createUserCert( + permsData.certificate, + permsData.privKey, + userCsr, + new Date(), + new Date(2030, 1, 1) + ) + return userCert.userCertString +} - const certificateByUsername = (username: string, certificates: string[]): string | null => { - /** - * Check if given username is already in use - */ - for (const cert of certificates) { - const parsedCert = parseCertificate(cert) - const certUsername = getCertFieldValue(parsedCert, CertFieldsTypes.nickName) - if (certUsername?.localeCompare(username, undefined, { sensitivity: 'base' }) === 0) { - return cert - } +const certificateByUsername = (username: string, certificates: string[]): string | null => { + /** + * Check if given username is already in use + */ + for (const cert of certificates) { + const parsedCert = parseCertificate(cert) + const certUsername = getCertFieldValue(parsedCert, CertFieldsTypes.nickName) + if (certUsername?.localeCompare(username, undefined, { sensitivity: 'base' }) === 0) { + return cert } - return null } + return null +} export interface RegistrationResponse { eventType: RegistrationEvents | SocketActionTypes @@ -77,47 +94,50 @@ export const sendCertificateRegistrationRequest = async ( serviceAddress: string, userCsr: string, communityId: string, - requestTimeout: number = 120000, + requestTimeout = 120000, socksProxyAgent: Agent - ): Promise => { - const controller = new AbortController() - const timeout = setTimeout(() => { - controller.abort() - }, requestTimeout) +): Promise => { + const controller = new AbortController() + const timeout = setTimeout(() => { + controller.abort() + }, requestTimeout) - let options = { - method: 'POST', - body: JSON.stringify({ data: userCsr }), - headers: { 'Content-Type': 'application/json' }, - signal: controller.signal - } + let options = { + method: 'POST', + body: JSON.stringify({ data: userCsr }), + headers: { 'Content-Type': 'application/json' }, + signal: controller.signal, + } - options = Object.assign({ - agent: socksProxyAgent - }, options) + options = Object.assign( + { + agent: socksProxyAgent, + }, + options + ) - let response: Response | null = null + let response: Response | null = null - try { - const start = new Date() - response = await fetch(`${serviceAddress}/register`, options) - const end = new Date() - const fetchTime = (end.getTime() - start.getTime()) / 1000 - log(`Fetched ${serviceAddress}, time: ${fetchTime}`) - } catch (e) { - log.error(e) - return { - eventType: RegistrationEvents.ERROR, - data: { - type: SocketActionTypes.REGISTRAR, - code: ErrorCodes.NOT_FOUND, - message: ErrorMessages.REGISTRAR_NOT_FOUND, - community: communityId - } - } - } finally { - clearTimeout(timeout) + try { + const start = new Date() + response = await fetch(`${serviceAddress}/register`, options) + const end = new Date() + const fetchTime = (end.getTime() - start.getTime()) / 1000 + log(`Fetched ${serviceAddress}, time: ${fetchTime}`) + } catch (e) { + log.error(e) + return { + eventType: RegistrationEvents.ERROR, + data: { + type: SocketActionTypes.REGISTRAR, + code: ErrorCodes.NOT_FOUND, + message: ErrorMessages.REGISTRAR_NOT_FOUND, + community: communityId, + }, } + } finally { + clearTimeout(timeout) + } switch (response?.status) { case 200: @@ -129,8 +149,8 @@ export const sendCertificateRegistrationRequest = async ( type: SocketActionTypes.REGISTRAR, code: ErrorCodes.BAD_REQUEST, message: ErrorMessages.INVALID_USERNAME, - community: communityId - } + community: communityId, + }, } case 403: return { @@ -139,8 +159,8 @@ export const sendCertificateRegistrationRequest = async ( type: SocketActionTypes.REGISTRAR, code: ErrorCodes.FORBIDDEN, message: ErrorMessages.USERNAME_TAKEN, - community: communityId - } + community: communityId, + }, } case 404: return { @@ -149,119 +169,121 @@ export const sendCertificateRegistrationRequest = async ( type: SocketActionTypes.REGISTRAR, code: ErrorCodes.NOT_FOUND, message: ErrorMessages.REGISTRAR_NOT_FOUND, - community: communityId - } + community: communityId, + }, } default: - log.error( - `Registrar responded with ${response?.status} "${response?.statusText}" (${communityId})` - ) + log.error(`Registrar responded with ${response?.status} "${response?.statusText}" (${communityId})`) return { eventType: RegistrationEvents.ERROR, data: { type: SocketActionTypes.REGISTRAR, code: ErrorCodes.SERVER_ERROR, message: ErrorMessages.REGISTRATION_FAILED, - community: communityId - } + community: communityId, + }, } - } + } - const registrarResponse: UserCertificatePayload = - await response.json() + const registrarResponse: UserCertificatePayload = await response.json() log(`Sending user certificate (${communityId})`) return { eventType: SocketActionTypes.SEND_USER_CERTIFICATE, data: { communityId, - payload: registrarResponse - } + payload: registrarResponse, + }, } } - export const registerUser = async (csr: string, permsData: PermsData, certificates: string[], ownerCertificate: string): Promise => { - let cert: string - const userData = new UserCsrData() - userData.csr = csr - const validationErrors = await validate(userData) - if (validationErrors.length > 0) { - log.error(`Received data is not valid: ${validationErrors.toString()}`) +export const registerUser = async ( + csr: string, + permsData: PermsData, + certificates: string[], + ownerCertificate: string +): Promise => { + let cert: string + const userData = new UserCsrData() + userData.csr = csr + const validationErrors = await validate(userData) + if (validationErrors.length > 0) { + log.error(`Received data is not valid: ${validationErrors.toString()}`) + return { + status: 400, + body: JSON.stringify(validationErrors), + } + } + + const parsedCsr = await loadCSR(userData.csr) + const username = getReqFieldValue(parsedCsr, CertFieldsTypes.nickName) + if (!username) { + log.error(`Could not parse certificate for field type ${CertFieldsTypes.nickName}`) + return { + // Should be internal server error code 500 + status: 400, + body: null, + } + } + // Use map here + const usernameCert = certificateByUsername(username, certificates) + if (usernameCert) { + if (!pubKeyMatch(usernameCert, parsedCsr)) { + log(`Username ${username} is taken`) return { - status: 400, - body: JSON.stringify(validationErrors) + // Should be conflict code 409 + status: 403, + body: null, } + } else { + log('Requesting same CSR again') + cert = usernameCert } - - const parsedCsr = await loadCSR(userData.csr) - const username = getReqFieldValue(parsedCsr, CertFieldsTypes.nickName) - if (!username) { - log.error(`Could not parse certificate for field type ${CertFieldsTypes.nickName}`) + } else { + log('username doesnt have existing cert, creating new') + try { + cert = await registerCertificate(userData.csr, permsData) + } catch (e) { + log.error(`Something went wrong with registering user: ${e.message as string}`) return { // Should be internal server error code 500 status: 400, - body: null - } - } - // Use map here - const usernameCert = certificateByUsername(username, certificates) - if (usernameCert) { - if (!pubKeyMatch(usernameCert, parsedCsr)) { - log(`Username ${username} is taken`) - return { - // Should be conflict code 409 - status: 403, - body: null - } - } else { - log('Requesting same CSR again') - cert = usernameCert - } - } else { - log('username doesnt have existing cert, creating new') - try { - cert = await registerCertificate(userData.csr, permsData) - } catch (e) { - log.error(`Something went wrong with registering user: ${e.message as string}`) - return { - // Should be internal server error code 500 - status: 400, - body: null - } + body: null, } } + } - const allUsers: User[] = [] - for (const cert of certificates) { - const parsedCert = parseCertificate(cert) - const onionAddress = getCertFieldValue(parsedCert, CertFieldsTypes.commonName) - const peerId = getCertFieldValue(parsedCert, CertFieldsTypes.peerId) - const username = getCertFieldValue(parsedCert, CertFieldsTypes.nickName) - const dmPublicKey = getCertFieldValue(parsedCert, CertFieldsTypes.dmPublicKey) - if (!onionAddress || !peerId || !username || !dmPublicKey) continue - allUsers.push({ onionAddress, peerId, username, dmPublicKey }) - } + const allUsers: User[] = [] + for (const cert of certificates) { + const parsedCert = parseCertificate(cert) + const onionAddress = getCertFieldValue(parsedCert, CertFieldsTypes.commonName) + const peerId = getCertFieldValue(parsedCert, CertFieldsTypes.peerId) + const username = getCertFieldValue(parsedCert, CertFieldsTypes.nickName) + const dmPublicKey = getCertFieldValue(parsedCert, CertFieldsTypes.dmPublicKey) + if (!onionAddress || !peerId || !username || !dmPublicKey) continue + allUsers.push({ onionAddress, peerId, username, dmPublicKey }) + } - const peerList = await getUsersAddresses(allUsers) + const peerList = await getUsersAddresses(allUsers) - return { - status: 200, - body: { - certificate: cert, - peers: peerList, - rootCa: permsData.certificate, - ownerCert: ownerCertificate - } - } + return { + status: 200, + body: { + certificate: cert, + peers: peerList, + rootCa: permsData.certificate, + ownerCert: ownerCertificate, + }, } +} - export const registerCertificate = async (userCsr: string, permsData: PermsData): Promise => { - const userCert = await createUserCert( - permsData.certificate, - permsData.privKey, - userCsr, - new Date(), - new Date(2030, 1, 1) - ) - return userCert.userCertString - } +export const registerCertificate = async (userCsr: string, permsData: PermsData): Promise => { + const userCert = await createUserCert( + permsData.certificate, + permsData.privKey, + userCsr, + new Date(), + new Date(2030, 1, 1) + ) + return userCert.userCertString +} diff --git a/packages/backend/src/registration/index.ts b/packages/backend/src/registration/index.ts index ee8ed653fe..fddb0fa88e 100644 --- a/packages/backend/src/registration/index.ts +++ b/packages/backend/src/registration/index.ts @@ -2,10 +2,24 @@ import express from 'express' import getPort from 'get-port' import { type Agent, type Server } from 'http' import { EventEmitter } from 'events' -import { registerOwner, registerUser, type RegistrarResponse, type RegistrationResponse, sendCertificateRegistrationRequest } from './functions' +import { + registerOwner, + registerUser, + type RegistrarResponse, + type RegistrationResponse, + sendCertificateRegistrationRequest, +} from './functions' import { RegistrationEvents } from './types' import { ServiceState } from '../libp2p/types' -import { ConnectionProcessInfo, ErrorCodes, ErrorMessages, type LaunchRegistrarPayload, type PermsData, type RegisterOwnerCertificatePayload, SocketActionTypes } from '@quiet/types' +import { + ConnectionProcessInfo, + ErrorCodes, + ErrorMessages, + type LaunchRegistrarPayload, + type PermsData, + type RegisterOwnerCertificatePayload, + SocketActionTypes, +} from '@quiet/types' import logger from '../logger' const log = logger('registration') @@ -21,7 +35,7 @@ export class CertificateRegistration extends EventEmitter { constructor() { super() this.certificates = [] - this.on(RegistrationEvents.SET_CERTIFICATES, (certs) => { + this.on(RegistrationEvents.SET_CERTIFICATES, certs => { this.setCertificates(certs) }) this._app = express() @@ -37,18 +51,15 @@ export class CertificateRegistration extends EventEmitter { private setRouting() { // @ts-ignore this._app.use(express.json()) - this._app.post( - '/register', - async (req, res): Promise => { - if (this.pendingPromise) return - this.pendingPromise = this.registerUser(req.body.data) - const result = await this.pendingPromise - if (result) { - res.status(result.status).send(result.body) - } - this.pendingPromise = null + this._app.post('/register', async (req, res): Promise => { + if (this.pendingPromise) return + this.pendingPromise = this.registerUser(req.body.data) + const result = await this.pendingPromise + if (result) { + res.status(result.status).send(result.body) } - ) + this.pendingPromise = null + }) } public async listen(): Promise { @@ -80,13 +91,13 @@ export class CertificateRegistration extends EventEmitter { type: SocketActionTypes.REGISTRAR, code: ErrorCodes.SERVER_ERROR, message: ErrorMessages.REGISTRATION_FAILED, - community: payload.communityId + community: payload.communityId, }) return } this.emit(SocketActionTypes.SAVED_OWNER_CERTIFICATE, { communityId: payload.communityId, - network: { certificate: cert, peers: [] } + network: { certificate: cert, peers: [] }, }) this._ownerCertificate = cert } @@ -95,10 +106,11 @@ export class CertificateRegistration extends EventEmitter { serviceAddress: string, userCsr: string, communityId: string, - requestTimeout: number = 120000, + requestTimeout = 120000, socksProxyAgent: Agent ): Promise => { - const response: RegistrationResponse = await sendCertificateRegistrationRequest(serviceAddress, + const response: RegistrationResponse = await sendCertificateRegistrationRequest( + serviceAddress, userCsr, communityId, requestTimeout, @@ -120,7 +132,7 @@ export class CertificateRegistration extends EventEmitter { this.emit(RegistrationEvents.REGISTRAR_STATE, ServiceState.LAUNCHING) this._permsData = { certificate: payload.rootCertString, - privKey: payload.rootKeyString + privKey: payload.rootKeyString, } log(`Initializing registration service for peer ${payload.peerId}...`) try { @@ -142,7 +154,7 @@ export class CertificateRegistration extends EventEmitter { this.emit(RegistrationEvents.SPAWN_HS_FOR_REGISTRAR, { port: this._port, privateKey: privKey, - targetPort: 80 + targetPort: 80, }) } } diff --git a/packages/backend/src/registration/testUtils.ts b/packages/backend/src/registration/testUtils.ts index d89ac4c77d..f1c4ff44be 100644 --- a/packages/backend/src/registration/testUtils.ts +++ b/packages/backend/src/registration/testUtils.ts @@ -4,15 +4,22 @@ import PeerId from 'peer-id' import { createLibp2p } from '../common/testUtils' const { Storage } = await import('../storage') -export async function registerUser(csr: string, httpTunnelPort: number, localhost: boolean = true, registrarPort: number = 7789): Promise { +export async function registerUser( + csr: string, + httpTunnelPort: number, + localhost = true, + registrarPort = 7789 +): Promise { let address = '127.0.0.1' let options = { method: 'POST', body: JSON.stringify({ data: csr }), - headers: { 'Content-Type': 'application/json' } + headers: { 'Content-Type': 'application/json' }, } if (!localhost) { - options = Object.assign(options, { agent: createHttpsProxyAgent({ port: httpTunnelPort, host: 'localhost', timeout: 100000 }) }) + options = Object.assign(options, { + agent: createHttpsProxyAgent({ port: httpTunnelPort, host: 'localhost', timeout: 100000 }), + }) address = '4avghtoehep5ebjngfqk5b43jolkiyyedfcvvq4ouzdnughodzoglzad.onion' return await fetch(`http://${address}/register`, options) } @@ -21,15 +28,11 @@ export async function registerUser(csr: string, httpTunnelPort: number, localhos export const getStorage = async (quietDir: string) => { const peerId = await PeerId.create() - const storage = new Storage( - quietDir, - 'communityid', - { - ...{}, - orbitDbDir: `OrbitDB${peerId.toB58String()}`, - ipfsDir: `Ipfs${peerId.toB58String()}` - } - ) + const storage = new Storage(quietDir, 'communityid', { + ...{}, + orbitDbDir: `OrbitDB${peerId.toB58String()}`, + ipfsDir: `Ipfs${peerId.toB58String()}`, + }) await storage.init(await createLibp2p(peerId), peerId) await storage.initDatabases() return storage diff --git a/packages/backend/src/registration/types.ts b/packages/backend/src/registration/types.ts index 36b1f55df5..62e81b59c1 100644 --- a/packages/backend/src/registration/types.ts +++ b/packages/backend/src/registration/types.ts @@ -1,7 +1,7 @@ export enum RegistrationEvents { - ERROR = 'error', - SPAWN_HS_FOR_REGISTRAR = 'spawnHsForRegistrar', - NEW_USER = 'newUser', - SET_CERTIFICATES = 'setCertificates', - REGISTRAR_STATE = 'registrarState' + ERROR = 'error', + SPAWN_HS_FOR_REGISTRAR = 'spawnHsForRegistrar', + NEW_USER = 'newUser', + SET_CERTIFICATES = 'setCertificates', + REGISTRAR_STATE = 'registrarState', } diff --git a/packages/backend/src/registration/validators.ts b/packages/backend/src/registration/validators.ts index aa03053e37..38e45844ac 100644 --- a/packages/backend/src/registration/validators.ts +++ b/packages/backend/src/registration/validators.ts @@ -4,7 +4,8 @@ import logger from '../logger' const log = logger('validators') export function IsCsr(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { // eslint-disable-line @typescript-eslint/ban-types + return function (object: object, propertyName: string) { + // eslint-disable-line @typescript-eslint/ban-types registerDecorator({ name: 'isCsr', target: object.constructor, @@ -13,21 +14,25 @@ export function IsCsr(validationOptions?: ValidationOptions) { validator: { async validate(value: any, _args: ValidationArguments) { const prom = new Promise(resolve => { - loadCSR(value).then(() => { - resolve(true) - }, () => { - resolve(false) - }) + loadCSR(value).then( + () => { + resolve(true) + }, + () => { + resolve(false) + } + ) }) return await prom - } - } + }, + }, }) } } export function CsrContainsFields(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { // eslint-disable-line @typescript-eslint/ban-types + return function (object: object, propertyName: string) { + // eslint-disable-line @typescript-eslint/ban-types registerDecorator({ name: 'csrContainsFields', target: object.constructor, @@ -36,22 +41,25 @@ export function CsrContainsFields(validationOptions?: ValidationOptions) { validator: { async validate(value: any, _args: ValidationArguments) { const prom = new Promise(resolve => { - loadCSR(value).then((loadedCsr) => { - for (const certType of [CertFieldsTypes.commonName, CertFieldsTypes.peerId, CertFieldsTypes.nickName]) { - if (!getReqFieldValue(loadedCsr, certType)) { - log.error(`Certificate is lacking a field '${certType}'`) - resolve(false) - return + loadCSR(value).then( + loadedCsr => { + for (const certType of [CertFieldsTypes.commonName, CertFieldsTypes.peerId, CertFieldsTypes.nickName]) { + if (!getReqFieldValue(loadedCsr, certType)) { + log.error(`Certificate is lacking a field '${certType}'`) + resolve(false) + return + } } + resolve(true) + }, + () => { + resolve(false) } - resolve(true) - }, () => { - resolve(false) - }) + ) }) return await prom - } - } + }, + }, }) } } diff --git a/packages/backend/src/sleep.ts b/packages/backend/src/sleep.ts index e63049af42..f026552dfc 100644 --- a/packages/backend/src/sleep.ts +++ b/packages/backend/src/sleep.ts @@ -1,5 +1,5 @@ export const sleep = async (time = 1000) => { - await new Promise(resolve => { + await new Promise(resolve => { setTimeout(() => { resolve() }, time) diff --git a/packages/backend/src/socket/DataServer.ts b/packages/backend/src/socket/DataServer.ts index b02044d5eb..ff8b70e74a 100644 --- a/packages/backend/src/socket/DataServer.ts +++ b/packages/backend/src/socket/DataServer.ts @@ -5,7 +5,23 @@ import logger from '../logger' import { EventEmitter } from 'events' import cors from 'cors' import type { CorsOptions } from 'cors' -import { type AskForMessagesPayload, type CancelDownloadPayload, type Community, ConnectionProcessInfo, type CreateChannelPayload, type DeleteFilesFromChannelSocketPayload, type DownloadFilePayload, type InitCommunityPayload, type LaunchRegistrarPayload, type RegisterOwnerCertificatePayload, type RegisterUserCertificatePayload, type SaveOwnerCertificatePayload, type SendMessagePayload, SocketActionTypes, type UploadFilePayload } from '@quiet/types' +import { + type AskForMessagesPayload, + type CancelDownloadPayload, + type Community, + ConnectionProcessInfo, + type CreateChannelPayload, + type DeleteFilesFromChannelSocketPayload, + type DownloadFilePayload, + type InitCommunityPayload, + type LaunchRegistrarPayload, + type RegisterOwnerCertificatePayload, + type RegisterUserCertificatePayload, + type SaveOwnerCertificatePayload, + type SendMessagePayload, + SocketActionTypes, + type UploadFilePayload, +} from '@quiet/types' const log = logger('socket') @@ -23,12 +39,13 @@ export class DataServer extends EventEmitter { this.initSocket() } - private get cors(): CorsOptions { // TODO: is this still necessary? + private get cors(): CorsOptions { + // TODO: is this still necessary? if (process.env.TEST_MODE === 'true' && process.env.E2E_TEST === 'true') { log('Development/test env. Getting cors') return { origin: '*', - methods: ['GET', 'POST'] + methods: ['GET', 'POST'], } } return {} @@ -38,7 +55,7 @@ export class DataServer extends EventEmitter { this.io = new SocketIO(this.server, { cors: this.cors, pingInterval: 1000_000, - pingTimeout: 1000_000 + pingTimeout: 1000_000, }) // Attach listeners here this.io.on(SocketActionTypes.CONNECTION, socket => { @@ -50,36 +67,21 @@ export class DataServer extends EventEmitter { socket.on(SocketActionTypes.CREATE_CHANNEL, async (payload: CreateChannelPayload) => { this.emit(SocketActionTypes.CREATE_CHANNEL, payload) }) - socket.on( - SocketActionTypes.SEND_MESSAGE, - async (payload: SendMessagePayload) => { - this.emit(SocketActionTypes.SEND_MESSAGE, payload) - } - ) - socket.on( - SocketActionTypes.UPLOAD_FILE, - async (payload: UploadFilePayload) => { - this.emit(SocketActionTypes.UPLOAD_FILE, payload.file) - } - ) - socket.on( - SocketActionTypes.DOWNLOAD_FILE, - async (payload: DownloadFilePayload) => { - this.emit(SocketActionTypes.DOWNLOAD_FILE, payload.metadata) - } - ) - socket.on( - SocketActionTypes.CANCEL_DOWNLOAD, - async (payload: CancelDownloadPayload) => { - this.emit(SocketActionTypes.CANCEL_DOWNLOAD, payload.mid) - } - ) + socket.on(SocketActionTypes.SEND_MESSAGE, async (payload: SendMessagePayload) => { + this.emit(SocketActionTypes.SEND_MESSAGE, payload) + }) + socket.on(SocketActionTypes.UPLOAD_FILE, async (payload: UploadFilePayload) => { + this.emit(SocketActionTypes.UPLOAD_FILE, payload.file) + }) + socket.on(SocketActionTypes.DOWNLOAD_FILE, async (payload: DownloadFilePayload) => { + this.emit(SocketActionTypes.DOWNLOAD_FILE, payload.metadata) + }) + socket.on(SocketActionTypes.CANCEL_DOWNLOAD, async (payload: CancelDownloadPayload) => { + this.emit(SocketActionTypes.CANCEL_DOWNLOAD, payload.mid) + }) socket.on( SocketActionTypes.INITIALIZE_CONVERSATION, - async ( - peerId: string, - { address, encryptedPhrase }: { address: string; encryptedPhrase: string } - ) => { + async (peerId: string, { address, encryptedPhrase }: { address: string; encryptedPhrase: string }) => { this.emit(SocketActionTypes.INITIALIZE_CONVERSATION, { address, encryptedPhrase }) } ) @@ -88,53 +90,39 @@ export class DataServer extends EventEmitter { }) socket.on( SocketActionTypes.SEND_DIRECT_MESSAGE, - async ( - peerId: string, - { channelId, message }: { channelId: string; message: string } - ) => { + async (peerId: string, { channelId, message }: { channelId: string; message: string }) => { this.emit(SocketActionTypes.SEND_DIRECT_MESSAGE, { channelId, message }) } ) - socket.on( - SocketActionTypes.SUBSCRIBE_FOR_DIRECT_MESSAGE_THREAD, - async (peerId: string, channelId: string) => { - this.emit(SocketActionTypes.SUBSCRIBE_FOR_DIRECT_MESSAGE_THREAD, { peerId, channelId }) - } - ) - socket.on( - SocketActionTypes.SUBSCRIBE_FOR_ALL_CONVERSATIONS, - async (peerId: string, conversations: string[]) => { - this.emit(SocketActionTypes.SUBSCRIBE_FOR_ALL_CONVERSATIONS, { peerId, conversations }) - } - ) + socket.on(SocketActionTypes.SUBSCRIBE_FOR_DIRECT_MESSAGE_THREAD, async (peerId: string, channelId: string) => { + this.emit(SocketActionTypes.SUBSCRIBE_FOR_DIRECT_MESSAGE_THREAD, { peerId, channelId }) + }) + socket.on(SocketActionTypes.SUBSCRIBE_FOR_ALL_CONVERSATIONS, async (peerId: string, conversations: string[]) => { + this.emit(SocketActionTypes.SUBSCRIBE_FOR_ALL_CONVERSATIONS, { peerId, conversations }) + }) socket.on(SocketActionTypes.ASK_FOR_MESSAGES, async (payload: AskForMessagesPayload) => { this.emit(SocketActionTypes.ASK_FOR_MESSAGES, payload) }) - socket.on( - SocketActionTypes.REGISTER_USER_CERTIFICATE, - async (payload: RegisterUserCertificatePayload) => { - log(`Registering user certificate (${payload.communityId}) on ${payload.serviceAddress}`) - this.emit(SocketActionTypes.REGISTER_USER_CERTIFICATE, payload) - await new Promise(resolve => setTimeout(() => { resolve() }, 2000)) - this.emit(SocketActionTypes.CONNECTION_PROCESS_INFO, ConnectionProcessInfo.REGISTERING_USER_CERTIFICATE) - } - ) - socket.on( - SocketActionTypes.REGISTER_OWNER_CERTIFICATE, - async (payload: RegisterOwnerCertificatePayload) => { - log(`Registering owner certificate (${payload.communityId})`) - this.emit(SocketActionTypes.REGISTER_OWNER_CERTIFICATE, payload) - this.emit(SocketActionTypes.CONNECTION_PROCESS_INFO, ConnectionProcessInfo.REGISTERING_OWNER_CERTIFICATE) - } - ) - socket.on( - SocketActionTypes.SAVE_OWNER_CERTIFICATE, - async (payload: SaveOwnerCertificatePayload) => { - log(`Saving owner certificate (${payload.peerId}), community: ${payload.id}`) - this.emit(SocketActionTypes.SAVED_OWNER_CERTIFICATE, payload) - } - ) + socket.on(SocketActionTypes.REGISTER_USER_CERTIFICATE, async (payload: RegisterUserCertificatePayload) => { + log(`Registering user certificate (${payload.communityId}) on ${payload.serviceAddress}`) + this.emit(SocketActionTypes.REGISTER_USER_CERTIFICATE, payload) + await new Promise(resolve => + setTimeout(() => { + resolve() + }, 2000) + ) + this.emit(SocketActionTypes.CONNECTION_PROCESS_INFO, ConnectionProcessInfo.REGISTERING_USER_CERTIFICATE) + }) + socket.on(SocketActionTypes.REGISTER_OWNER_CERTIFICATE, async (payload: RegisterOwnerCertificatePayload) => { + log(`Registering owner certificate (${payload.communityId})`) + this.emit(SocketActionTypes.REGISTER_OWNER_CERTIFICATE, payload) + this.emit(SocketActionTypes.CONNECTION_PROCESS_INFO, ConnectionProcessInfo.REGISTERING_OWNER_CERTIFICATE) + }) + socket.on(SocketActionTypes.SAVE_OWNER_CERTIFICATE, async (payload: SaveOwnerCertificatePayload) => { + log(`Saving owner certificate (${payload.peerId}), community: ${payload.id}`) + this.emit(SocketActionTypes.SAVED_OWNER_CERTIFICATE, payload) + }) socket.on(SocketActionTypes.CREATE_COMMUNITY, async (payload: InitCommunityPayload) => { log(`Creating community ${payload.id}`) this.emit(SocketActionTypes.CREATE_COMMUNITY, payload) @@ -180,7 +168,7 @@ export class DataServer extends EventEmitter { public close = async (): Promise => { log(`Closing data server on port ${this.PORT}`) await new Promise(resolve => { - this.server.close((err) => { + this.server.close(err => { if (err) throw new Error(err.message) resolve() }) diff --git a/packages/backend/src/storage/ipfsFileManager.test.ts b/packages/backend/src/storage/ipfsFileManager.test.ts index d97f95dcac..1d39da02ca 100644 --- a/packages/backend/src/storage/ipfsFileManager.test.ts +++ b/packages/backend/src/storage/ipfsFileManager.test.ts @@ -27,8 +27,7 @@ beforeEach(async () => { jest.clearAllMocks() tmpDir = createTmpDir() tmpAppDataPath = tmpQuietDirPath(tmpDir.name) - filePath = path.join( - dirname, '/testUtils/500kB-file.txt') + filePath = path.join(dirname, '/testUtils/500kB-file.txt') }) afterEach(async () => { @@ -56,8 +55,8 @@ describe('Ipfs file manager', () => { cid: 'uploading_id', message: { id: 'id', - channelId: 'channelId' - } + channelId: 'channelId', + }, } await fileManager.uploadFile(metadata) @@ -70,11 +69,27 @@ 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, ext: '.png', height: 44, message: { channelId: 'channelId', id: 'id' }, name: 'test-image', size: 15858, width: 824 }) + expect(eventSpy).toHaveBeenNthCalledWith( + 2, + StorageEvents.UPLOADED_FILE, + expect.objectContaining({ + cid, + ext: '.png', + height: 44, + message: { channelId: 'channelId', id: 'id' }, + name: 'test-image', + size: 15858, + width: 824, + }) ) }) await waitForExpect(() => { - expect(eventSpy).toHaveBeenNthCalledWith(3, StorageEvents.UPDATE_DOWNLOAD_PROGRESS, { cid, downloadProgress: undefined, downloadState: 'hosted', mid: 'id' }) + expect(eventSpy).toHaveBeenNthCalledWith(3, StorageEvents.UPDATE_DOWNLOAD_PROGRESS, { + cid, + downloadProgress: undefined, + downloadState: 'hosted', + mid: 'id', + }) }) }) @@ -93,8 +108,8 @@ describe('Ipfs file manager', () => { cid: 'uploading_id', message: { id: 'id', - channelId: 'channelId' - } + channelId: 'channelId', + }, } await fileManager.uploadFile(metadata) @@ -103,15 +118,41 @@ 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, ext: '.pdf', height: undefined, message: { channelId: 'channelId', id: 'id' }, name: 'test-file', size: 761991, width: undefined } - ) + expect(eventSpy).toHaveBeenNthCalledWith( + 2, + StorageEvents.UPLOADED_FILE, + expect.objectContaining({ + 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, downloadProgress: undefined, downloadState: 'hosted', mid: 'id' }) + expect(eventSpy).toHaveBeenNthCalledWith(3, StorageEvents.UPDATE_DOWNLOAD_PROGRESS, { + cid, + downloadProgress: undefined, + downloadState: 'hosted', + mid: 'id', + }) }) await waitForExpect(() => { - expect(eventSpy).toHaveBeenNthCalledWith(4, StorageEvents.UPDATE_MESSAGE_MEDIA, expect.objectContaining({ cid, ext: '.pdf', height: undefined, message: { channelId: 'channelId', id: 'id' }, name: 'test-file', size: 761991, width: undefined }) + expect(eventSpy).toHaveBeenNthCalledWith( + 4, + StorageEvents.UPDATE_MESSAGE_MEDIA, + expect.objectContaining({ + cid, + ext: '.pdf', + height: undefined, + message: { channelId: 'channelId', id: 'id' }, + name: 'test-file', + size: 761991, + width: undefined, + }) ) }) }) @@ -130,11 +171,11 @@ describe('Ipfs file manager', () => { cid: 'uploading_id', message: { id: 'id', - channelId: 'channelId' - } + channelId: 'channelId', + }, } - await waitForExpect(async () => { + await waitForExpect(async () => { await expect(fileManager.uploadFile(metadata)).rejects.toThrow() }) await waitForExpect(() => { @@ -157,8 +198,8 @@ describe('Ipfs file manager', () => { cid: 'uploading_id', message: { id: 'id', - channelId: 'channelId' - } + channelId: 'channelId', + }, } await fileManager.uploadFile(metadata) @@ -167,13 +208,42 @@ 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, ext: '.pdf', height: undefined, message: { channelId: 'channelId', id: 'id' }, name: 'test-file', size: 761991, width: undefined })) + expect(eventSpy).toHaveBeenNthCalledWith( + 2, + StorageEvents.UPLOADED_FILE, + expect.objectContaining({ + 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, downloadProgress: undefined, downloadState: 'hosted', mid: 'id' }) + expect(eventSpy).toHaveBeenNthCalledWith(3, StorageEvents.UPDATE_DOWNLOAD_PROGRESS, { + cid, + downloadProgress: undefined, + downloadState: 'hosted', + mid: 'id', + }) }) await waitForExpect(() => { - expect(eventSpy).toHaveBeenNthCalledWith(4, StorageEvents.UPDATE_MESSAGE_MEDIA, expect.objectContaining({ cid, ext: '.pdf', height: undefined, message: { channelId: 'channelId', id: 'id' }, name: 'test-file', size: 761991, width: undefined })) + expect(eventSpy).toHaveBeenNthCalledWith( + 4, + StorageEvents.UPDATE_MESSAGE_MEDIA, + expect.objectContaining({ + cid, + ext: '.pdf', + height: undefined, + message: { channelId: 'channelId', id: 'id' }, + name: 'test-file', + size: 761991, + width: undefined, + }) + ) }) // Downloading @@ -182,15 +252,23 @@ describe('Ipfs file manager', () => { fileManager.emit(IpfsFilesManagerEvents.DOWNLOAD_FILE, { ...uploadMetadata, - size: 20400 + size: 20400, }) await waitForExpect(() => { - expect(eventSpy).toHaveBeenNthCalledWith(5, IpfsFilesManagerEvents.DOWNLOAD_FILE, { ...uploadMetadata, size: 20400 }) + expect(eventSpy).toHaveBeenNthCalledWith(5, IpfsFilesManagerEvents.DOWNLOAD_FILE, { + ...uploadMetadata, + size: 20400, + }) }) await waitForExpect(() => { - expect(eventSpy).toHaveBeenNthCalledWith(6, StorageEvents.UPDATE_DOWNLOAD_PROGRESS, { cid, downloadProgress: undefined, downloadState: 'malicious', mid: 'id' }) + expect(eventSpy).toHaveBeenNthCalledWith(6, StorageEvents.UPDATE_DOWNLOAD_PROGRESS, { + cid, + downloadProgress: undefined, + downloadState: 'malicious', + mid: 'id', + }) }, 20000) expect(eventSpy).toBeCalledTimes(6) @@ -212,8 +290,8 @@ describe('Ipfs file manager', () => { cid, message: { id: 'id', - channelId: 'channelId' - } + channelId: 'channelId', + }, } await fileManager.uploadFile(metadata) @@ -223,15 +301,41 @@ 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, ext: '.pdf', height: undefined, message: { channelId: 'channelId', id: 'id' }, name: 'test-file', size: 761797, width: undefined }) + expect(eventSpy).toHaveBeenNthCalledWith( + 2, + StorageEvents.UPLOADED_FILE, + expect.objectContaining({ + cid, + ext: '.pdf', + height: undefined, + message: { channelId: 'channelId', id: 'id' }, + name: 'test-file', + size: 761797, + width: undefined, + }) ) }) await waitForExpect(() => { - expect(eventSpy).toHaveBeenNthCalledWith(3, StorageEvents.UPDATE_DOWNLOAD_PROGRESS, { cid, downloadProgress: undefined, downloadState: 'hosted', mid: 'id' } - ) + expect(eventSpy).toHaveBeenNthCalledWith(3, StorageEvents.UPDATE_DOWNLOAD_PROGRESS, { + cid, + downloadProgress: undefined, + downloadState: 'hosted', + mid: 'id', + }) }) await waitForExpect(() => { - expect(eventSpy).toHaveBeenNthCalledWith(4, StorageEvents.UPDATE_MESSAGE_MEDIA, expect.objectContaining({ cid, ext: '.pdf', height: undefined, message: { channelId: 'channelId', id: 'id' }, name: 'test-file', size: 761797, width: undefined }) + expect(eventSpy).toHaveBeenNthCalledWith( + 4, + StorageEvents.UPDATE_MESSAGE_MEDIA, + expect.objectContaining({ + cid, + ext: '.pdf', + height: undefined, + message: { channelId: 'channelId', id: 'id' }, + name: 'test-file', + size: 761797, + width: undefined, + }) ) }) @@ -251,8 +355,12 @@ describe('Ipfs file manager', () => { const uploadMetadata = eventSpy.mock.calls[1][1] await waitForExpect(() => { - expect(eventSpy).toHaveBeenNthCalledWith(1, StorageEvents.UPDATE_DOWNLOAD_PROGRESS, { cid, downloadProgress: { downloaded: 0, size: 761797, transferSpeed: 0 }, downloadState: 'canceled', mid: 'id' } - ) + expect(eventSpy).toHaveBeenNthCalledWith(1, StorageEvents.UPDATE_DOWNLOAD_PROGRESS, { + cid, + downloadProgress: { downloaded: 0, size: 761797, transferSpeed: 0 }, + downloadState: 'canceled', + mid: 'id', + }) }) expect(eventSpy).toBeCalledTimes(5) @@ -272,22 +380,47 @@ describe('Ipfs file manager', () => { cid: 'uploading_id', message: { id: 'id', - channelId: 'channelId' - } + channelId: 'channelId', + }, } await fileManager.uploadFile(metadata) const cid = 'QmSaK2joeTBYukh8L7besrvm56wSzMhn64nqLqtvxS3ths' - expect(eventSpy).toHaveBeenNthCalledWith(1, StorageEvents.REMOVE_DOWNLOAD_STATUS, { cid: 'uploading_id' } - ) - - expect(eventSpy).toHaveBeenNthCalledWith(2, StorageEvents.UPLOADED_FILE, expect.objectContaining({ cid, ext: '.png', height: 44, message: { channelId: 'channelId', id: 'id' }, name: 'test-image', size: 15858, width: 824 }) + expect(eventSpy).toHaveBeenNthCalledWith(1, StorageEvents.REMOVE_DOWNLOAD_STATUS, { cid: 'uploading_id' }) + + expect(eventSpy).toHaveBeenNthCalledWith( + 2, + StorageEvents.UPLOADED_FILE, + expect.objectContaining({ + 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, downloadProgress: undefined, downloadState: 'hosted', mid: 'id' } - ) + expect(eventSpy).toHaveBeenNthCalledWith(3, StorageEvents.UPDATE_DOWNLOAD_PROGRESS, { + cid, + downloadProgress: undefined, + downloadState: 'hosted', + mid: 'id', + }) - expect(eventSpy).toHaveBeenNthCalledWith(4, StorageEvents.UPDATE_MESSAGE_MEDIA, expect.objectContaining({ cid, ext: '.png', height: 44, message: { channelId: 'channelId', id: 'id' }, name: 'test-image', size: 15858, width: 824 }) + expect(eventSpy).toHaveBeenNthCalledWith( + 4, + StorageEvents.UPDATE_MESSAGE_MEDIA, + expect.objectContaining({ + cid, + ext: '.png', + height: 44, + message: { channelId: 'channelId', id: 'id' }, + name: 'test-image', + size: 15858, + width: 824, + }) ) // Downloading @@ -301,14 +434,33 @@ describe('Ipfs file manager', () => { }) await waitForExpect(() => { - expect(eventSpy).toHaveBeenNthCalledWith(6, StorageEvents.UPDATE_DOWNLOAD_PROGRESS, { cid, downloadProgress: { downloaded: 15855, size: 15858, transferSpeed: 0 }, downloadState: 'downloading', mid: 'id' } - ) + expect(eventSpy).toHaveBeenNthCalledWith(6, StorageEvents.UPDATE_DOWNLOAD_PROGRESS, { + cid, + downloadProgress: { downloaded: 15855, size: 15858, transferSpeed: 0 }, + downloadState: 'downloading', + mid: 'id', + }) }, 20000) await waitForExpect(() => { - expect(eventSpy).toHaveBeenNthCalledWith(7, StorageEvents.UPDATE_DOWNLOAD_PROGRESS, { cid, downloadProgress: { downloaded: 15855, size: 15858, transferSpeed: 0 }, downloadState: 'completed', mid: 'id' } - ) + expect(eventSpy).toHaveBeenNthCalledWith(7, StorageEvents.UPDATE_DOWNLOAD_PROGRESS, { + cid, + downloadProgress: { downloaded: 15855, size: 15858, transferSpeed: 0 }, + downloadState: 'completed', + mid: 'id', + }) }, 20000) - expect(eventSpy).toHaveBeenNthCalledWith(8, StorageEvents.UPDATE_MESSAGE_MEDIA, expect.objectContaining({ cid, ext: '.png', height: 44, message: { channelId: 'channelId', id: 'id' }, name: 'test-image', size: 15858, width: 824 }) + expect(eventSpy).toHaveBeenNthCalledWith( + 8, + StorageEvents.UPDATE_MESSAGE_MEDIA, + expect.objectContaining({ + 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,8 +478,8 @@ describe('Ipfs file manager', () => { cid: 'uploading_id', message: { id: 'id', - channelId: 'channelId' - } + channelId: 'channelId', + }, } await fileManager.uploadFile(metadata) @@ -370,8 +522,8 @@ describe('Ipfs file manager', () => { cid: 'uploading_id', message: { id: 'id', - channelId: 'channelId' - } + channelId: 'channelId', + }, } await fileManager.uploadFile(metadata) @@ -383,13 +535,12 @@ describe('Ipfs file manager', () => { const transferSpeeds: number[] = [] - eventSpy.mock.calls.map((call) => { + eventSpy.mock.calls.map(call => { if (call[0] === StorageEvents.UPDATE_DOWNLOAD_PROGRESS) { // @ts-ignore transferSpeeds.push(call[1].downloadProgress?.transferSpeed) } - } - ) + }) const unwantedValues = [undefined, null, Infinity] for (const value of unwantedValues) { await waitForExpect(() => { diff --git a/packages/backend/src/storage/ipfsFileManager.ts b/packages/backend/src/storage/ipfsFileManager.ts index 5ba75ecf64..a0429d0a97 100644 --- a/packages/backend/src/storage/ipfsFileManager.ts +++ b/packages/backend/src/storage/ipfsFileManager.ts @@ -19,30 +19,36 @@ import { CID } from 'multiformats/cid' import { sleep } from '../sleep' import logger from '../logger' -import { type DownloadProgress, DownloadState, type DownloadStatus, type FileMetadata, imagesExtensions } from '@quiet/types' +import { + type DownloadProgress, + DownloadState, + type DownloadStatus, + type FileMetadata, + imagesExtensions, +} from '@quiet/types' const log = logger('ipfsFiles') const sizeOfPromisified = promisify(sizeOf) const { createPaths, compare } = await import('../common/utils') export enum IpfsFilesManagerEvents { - // Incoming evetns - DOWNLOAD_FILE = 'downloadFile', - CANCEL_DOWNLOAD = 'cancelDownload', - UPLOAD_FILE = 'uploadFile', - DELETE_FILE = 'deleteFile', - // Outgoing evnets - UPDATE_MESSAGE_MEDIA = 'updateMessageMedia', - UPDATE_DOWNLOAD_PROGRESS = 'updateDownloadProgress' + // Incoming evetns + DOWNLOAD_FILE = 'downloadFile', + CANCEL_DOWNLOAD = 'cancelDownload', + UPLOAD_FILE = 'uploadFile', + DELETE_FILE = 'deleteFile', + // Outgoing evnets + UPDATE_MESSAGE_MEDIA = 'updateMessageMedia', + UPDATE_DOWNLOAD_PROGRESS = 'updateDownloadProgress', } interface FilesData { - size: number - downloadedBytes: number - transferSpeed: number - cid: string - message: { - id: string - } + size: number + downloadedBytes: number + transferSpeed: number + cid: string + message: { + id: string + } } const TRANSFER_SPEED_SPAN = 10 @@ -53,420 +59,433 @@ const QUEUE_CONCURRENCY = 40 const MAX_EVENT_LISTENERS = 300 export class IpfsFilesManager extends EventEmitter { - ipfs: IPFS - quietDir: string - // keep info about all in-progress downloads - files: Map - controllers: Map - - cancelledDownloads: Set - queue: PQueue - - constructor(ipfs: IPFS, quietDir: string) { - super() - this.ipfs = ipfs - this.quietDir = quietDir - this.files = new Map() - this.controllers = new Map() - this.cancelledDownloads = new Set() - this.queue = new PQueue({ concurrency: QUEUE_CONCURRENCY }) - this.attachIncomingEvents() + ipfs: IPFS + quietDir: string + // keep info about all in-progress downloads + files: Map + controllers: Map< + string, + { + controller: AbortController + } + > + + cancelledDownloads: Set + queue: PQueue + + constructor(ipfs: IPFS, quietDir: string) { + super() + this.ipfs = ipfs + this.quietDir = quietDir + this.files = new Map() + this.controllers = new Map() + this.cancelledDownloads = new Set() + this.queue = new PQueue({ concurrency: QUEUE_CONCURRENCY }) + this.attachIncomingEvents() + } + + private attachIncomingEvents = () => { + this.on(IpfsFilesManagerEvents.UPLOAD_FILE, async (fileMetadata: FileMetadata) => { + await this.uploadFile(fileMetadata) + }) + this.on(IpfsFilesManagerEvents.DOWNLOAD_FILE, async (fileMetadata: FileMetadata) => { + if (this.files.get(fileMetadata.cid)) return + this.files.set(fileMetadata.cid, { + size: fileMetadata.size || 0, + downloadedBytes: 0, + transferSpeed: 0, + cid: fileMetadata.cid, + message: fileMetadata.message, + }) + await this.downloadBlocks(fileMetadata) + }) + this.on(IpfsFilesManagerEvents.CANCEL_DOWNLOAD, async mid => { + const fileDownloaded = Array.from(this.files.values()).find(e => e.message.id === mid) + if (fileDownloaded) { + await this.cancelDownload(fileDownloaded.cid) + } else { + console.error(`downloading ${mid} has already been canceled or never started`) + } + }) + } + + public async deleteBlocks(fileMetadata: FileMetadata) { + const localBlocks = await this.getLocalBlocks() + const hasBlockBeenDownloaded = localBlocks.includes(`z${fileMetadata.cid.toString()}`) + if (!hasBlockBeenDownloaded) return + + try { + const result = await this.ipfs.pin.rm(fileMetadata.cid, { recursive: true }) + } catch (e) { + console.log('file removing error') + console.log(e) } - private attachIncomingEvents = () => { - this.on(IpfsFilesManagerEvents.UPLOAD_FILE, async (fileMetadata: FileMetadata) => { - await this.uploadFile(fileMetadata) - }) - this.on(IpfsFilesManagerEvents.DOWNLOAD_FILE, async (fileMetadata: FileMetadata) => { - if (this.files.get(fileMetadata.cid)) return - this.files.set(fileMetadata.cid, { - size: fileMetadata.size || 0, - downloadedBytes: 0, - transferSpeed: 0, - cid: fileMetadata.cid, - message: fileMetadata.message - }) - await this.downloadBlocks(fileMetadata) - }) - this.on(IpfsFilesManagerEvents.CANCEL_DOWNLOAD, async (mid) => { - const fileDownloaded = Array.from(this.files.values()).find((e) => e.message.id === mid) - if (fileDownloaded) { - await this.cancelDownload(fileDownloaded.cid) - } else { - console.error(`downloading ${mid} has already been canceled or never started`) - } - }) + const gcresult = this.ipfs.repo.gc() + for await (const res of gcresult) { + console.log('garbage collector result', res) } + } - public async deleteBlocks(fileMetadata: FileMetadata) { - const localBlocks = await this.getLocalBlocks() - const hasBlockBeenDownloaded = localBlocks.includes(`z${fileMetadata.cid.toString()}`) - if (!hasBlockBeenDownloaded) return + public async stop() { + for await (const cid of this.files.keys()) { + await this.cancelDownload(cid) + } + } + + public copyFile(originalFilePath: string, filename: string): string { + /** + * Copy file to a different directory and return the new path + */ + const uploadsDir = path.join(this.quietDir, 'uploads') + const newPath = path.join(uploadsDir, filename) + let filePath = originalFilePath + try { + if (!fs.existsSync(uploadsDir)) { + fs.mkdirSync(uploadsDir, { recursive: true }) + } + fs.copyFileSync(originalFilePath, newPath) + filePath = newPath + } catch (e) { + console.error(`Couldn't copy file ${originalFilePath} to ${newPath}. Error: ${e.message}`) + } + return filePath + } + + public async uploadFile(metadata: FileMetadata) { + let width: number | undefined + let height: number | undefined + if (!metadata.path) { + throw new Error(`File metadata (cid ${metadata.cid}) does not contain path`) + } + if (imagesExtensions.includes(metadata.ext)) { + let imageSize: { width: number | undefined; height: number | undefined } | undefined // ISizeCalculationResult + try { + imageSize = await sizeOfPromisified(metadata.path) + } catch (e) { + console.error(`Couldn't get image dimensions (${metadata.path}). Error: ${e.message}`) + throw new Error(`Couldn't get image dimensions (${metadata.path}). Error: ${e.message}`) + } + width = imageSize?.width + height = imageSize?.height + } - try { - const result = await this.ipfs.pin.rm(fileMetadata.cid, { recursive: true }) - } catch (e) { - console.log('file removing error') - console.log(e) + const stream = fs.createReadStream(metadata.path, { highWaterMark: 64 * 1024 * 10 }) + const uploadedFileStreamIterable = { + async* [Symbol.asyncIterator]() { // eslint-disable-line + for await (const data of stream) { + yield data } + }, + } - const gcresult = this.ipfs.repo.gc() - for await (const res of gcresult) { - console.log('garbage collector result', res) - } + // Create directory for file + const dirname = 'uploads' + await this.ipfs.files.mkdir(`/${dirname}`, { parents: true }) + + // Write file to IPFS + const uuid = `${Date.now()}_${Math.random().toString(36).substr(2.9)}` + const filename = `${uuid}_${metadata.name}${metadata.ext}` + + // Save copy to separate directory + const filePath = this.copyFile(metadata.path, filename) + console.time(`Writing ${filename} to ipfs`) + const newCid = await this.ipfs.add(uploadedFileStreamIterable) + + console.timeEnd(`Writing ${filename} to ipfs`) + + this.emit(StorageEvents.REMOVE_DOWNLOAD_STATUS, { cid: metadata.cid }) + const fileMetadata: FileMetadata = { + ...metadata, + path: filePath, + cid: newCid.cid.toString(), + size: newCid.size, + width, + height, } - public async stop() { - for await (const cid of this.files.keys()) { - await this.cancelDownload(cid) - } + this.emit(StorageEvents.UPLOADED_FILE, fileMetadata) + + const statusReady: DownloadStatus = { + mid: fileMetadata.message.id, + cid: fileMetadata.cid, + downloadState: DownloadState.Hosted, + downloadProgress: undefined, } - public copyFile(originalFilePath: string, filename: string): string { - /** - * Copy file to a different directory and return the new path - */ - const uploadsDir = path.join(this.quietDir, 'uploads') - const newPath = path.join(uploadsDir, filename) - let filePath = originalFilePath - try { - if (!fs.existsSync(uploadsDir)) { - fs.mkdirSync(uploadsDir, { recursive: true }) - } - fs.copyFileSync(originalFilePath, newPath) - filePath = newPath - } catch (e) { - console.error(`Couldn't copy file ${originalFilePath} to ${newPath}. Error: ${e.message}`) - } - return filePath + this.emit(StorageEvents.UPDATE_DOWNLOAD_PROGRESS, statusReady) + + if (metadata.path !== filePath) { + this.emit(StorageEvents.UPDATE_MESSAGE_MEDIA, fileMetadata) + } + } + + private cancelDownload = async (cid: string) => { + const queueController = this.controllers.get(cid) + const downloadInProgress = this.files.get(cid) + if (!downloadInProgress) return + // In case download is cancelled right after start and queue is not yet initialized. + if (!queueController) { + await sleep(1000) + await this.cancelDownload(cid) + } else { + const controller = queueController.controller + this.cancelledDownloads.add(cid) + controller.abort() } + } - public async uploadFile(metadata: FileMetadata) { - let width: number | undefined - let height: number | undefined - if (!metadata.path) { - throw new Error(`File metadata (cid ${metadata.cid}) does not contain path`) - } - if (imagesExtensions.includes(metadata.ext)) { - let imageSize: { width: number | undefined; height: number | undefined } | undefined // ISizeCalculationResult - try { - imageSize = await sizeOfPromisified(metadata.path) - } catch (e) { - console.error(`Couldn't get image dimensions (${metadata.path}). Error: ${e.message}`) - throw new Error(`Couldn't get image dimensions (${metadata.path}). Error: ${e.message}`) - } - width = imageSize?.width - height = imageSize?.height - } + private getLocalBlocks = async (): Promise => { + const blocks: string[] = [] - const stream = fs.createReadStream(metadata.path, { highWaterMark: 64 * 1024 * 10 }) - const uploadedFileStreamIterable = { - async* [Symbol.asyncIterator]() { - for await (const data of stream) { - yield data - } - } - } + const refs = this.ipfs.refs.local() - // Create directory for file - const dirname = 'uploads' - await this.ipfs.files.mkdir(`/${dirname}`, { parents: true }) - - // Write file to IPFS - const uuid = `${Date.now()}_${Math.random().toString(36).substr(2.9)}` - const filename = `${uuid}_${metadata.name}${metadata.ext}` - - // Save copy to separate directory - const filePath = this.copyFile(metadata.path, filename) - console.time(`Writing ${filename} to ipfs`) - const newCid = await this.ipfs.add(uploadedFileStreamIterable) - - console.timeEnd(`Writing ${filename} to ipfs`) - - this.emit(StorageEvents.REMOVE_DOWNLOAD_STATUS, { cid: metadata.cid }) - const fileMetadata: FileMetadata = { - ...metadata, - path: filePath, - cid: newCid.cid.toString(), - size: newCid.size, - width, - height - } + for await (const ref of refs) { + const cid = CID.parse(ref.ref) + const base58Encoded = base58.base58btc.encode(cid.multihash.bytes) + blocks.push(base58Encoded.toString()) + } + return blocks + } - this.emit(StorageEvents.UPLOADED_FILE, fileMetadata) + public downloadBlocks = async (fileMetadata: FileMetadata) => { + const block = CID.parse(fileMetadata.cid) - const statusReady: DownloadStatus = { - mid: fileMetadata.message.id, - cid: fileMetadata.cid, - downloadState: DownloadState.Hosted, - downloadProgress: undefined - } + const localBlocks = await this.getLocalBlocks() + const processedBlocks: PBNode[] = [] // TODO: Should it be CID or PBNode? - this.emit(StorageEvents.UPDATE_DOWNLOAD_PROGRESS, statusReady) + const controller = new AbortController() - if (metadata.path !== filePath) { - this.emit(StorageEvents.UPDATE_MESSAGE_MEDIA, fileMetadata) - } + setMaxListeners(MAX_EVENT_LISTENERS, controller.signal) + + this.controllers.set(fileMetadata.cid, { + controller, + }) + + // Add try catch and return downloadBlocks with timeout + const stat = await this.ipfs.files.stat(block) + if (fileMetadata.size && !compare(fileMetadata.size, stat.size, 0.05)) { + await this.updateStatus(fileMetadata.cid, DownloadState.Malicious) + return + } + + const addToQueue = async (link: CID) => { + try { + await this.queue.add(async () => { + try { + await processBlock(link, controller.signal) + } catch (e) { + if (!(e instanceof AbortError)) { + void addToQueue(link) + } + } + }) + } catch (e) { + /* empty */ + } } - private cancelDownload = async (cid: string) => { - const queueController = this.controllers.get(cid) - const downloadInProgress = this.files.get(cid) - if (!downloadInProgress) return - // In case download is cancelled right after start and queue is not yet initialized. - if (!queueController) { - await sleep(1000) - await this.cancelDownload(cid) + interface BlockStat { + fetchTime: number + byteLength: number + } + + // Transfer speed + const blocksStats: BlockStat[] = [] + + const updateTransferSpeed = setInterval(async () => { + const bytesDownloaded = blocksStats.reduce((previousValue, currentValue) => { + if (Math.floor(Date.now() / 1000) - currentValue.fetchTime < TRANSFER_SPEED_SPAN) + return previousValue + currentValue.byteLength + return 0 + }, 0) + const uniqueProcessedBlocks = [...new Set(processedBlocks)] + const totalBytesDownloaded = uniqueProcessedBlocks.reduce((prev, curr) => { + if (curr.Data) { + return prev + curr.Data.byteLength } else { - const controller = queueController.controller - this.cancelledDownloads.add(cid) - controller.abort() + return prev } - } + }, 0) + const transferSpeed = bytesDownloaded === 0 ? 0 : bytesDownloaded / TRANSFER_SPEED_SPAN + const fileState = this.files.get(fileMetadata.cid) + if (!fileState) { + log.error(`No saved data for file cid ${fileMetadata.cid}`) + return + } + this.files.set(fileMetadata.cid, { + ...fileState, + transferSpeed, + downloadedBytes: totalBytesDownloaded, + }) + await this.updateStatus(fileMetadata.cid) + }, UPDATE_STATUS_INTERVAL * 1000) + + const remainingBlocks = new Set() + remainingBlocks.add(block) + + const downloadCompletedOrCanceled = new Promise((resolve, reject) => { + const interval = setInterval(() => { + if (remainingBlocks.size === 0) { + clearInterval(interval) + resolve('No more blocks to fetch, download is completed or canceled') + } + }, 1000) + }) - private getLocalBlocks = async (): Promise => { - const blocks: string[] = [] + const processBlock = async (block: CID, signal: AbortSignal) => { + // eslint-disable-next-line + return await new Promise(async (resolve, reject) => { + const onAbort = () => { + remainingBlocks.delete(block) + reject(new AbortError('download cancelation')) + } - const refs = this.ipfs.refs.local() + if (signal.aborted) onAbort() + signal.addEventListener('abort', onAbort, { once: true }) - for await (const ref of refs) { - const cid = CID.parse(ref.ref) - const base58Encoded = base58.base58btc.encode(cid.multihash.bytes) - blocks.push(base58Encoded.toString()) + // @ts-ignore FIXME + if (processedBlocks.includes(block)) { + remainingBlocks.delete(block) + resolve(block) + return } - return blocks - } - public downloadBlocks = async (fileMetadata: FileMetadata) => { - const block = CID.parse(fileMetadata.cid) + const hasBlockBeenDownloaded = localBlocks.includes(`z${block.toString()}`) - const localBlocks = await this.getLocalBlocks() - const processedBlocks: PBNode[] = [] // TODO: Should it be CID or PBNode? + let fetchedBlock - const controller = new AbortController() + try { + fetchedBlock = await this.ipfs.block.get(block, { timeout: BLOCK_FETCH_TIMEOUT * 1000 }) + } catch (e) { + signal.removeEventListener('abort', onAbort) + reject(new Error("couldn't fetch block")) + return + } - setMaxListeners(MAX_EVENT_LISTENERS, controller.signal) + const decodedBlock: PBNode = decode(fetchedBlock) - this.controllers.set(fileMetadata.cid, { - controller - }) + const fileState = this.files.get(fileMetadata.cid) - // Add try catch and return downloadBlocks with timeout - const stat = await this.ipfs.files.stat(block) - if (fileMetadata.size && !compare(fileMetadata.size, stat.size, 0.05)) { - await this.updateStatus(fileMetadata.cid, DownloadState.Malicious) - return + if (!fileState) { + reject(new Error('Downloading has been cancelled')) + return } - const addToQueue = async (link: CID) => { - try { - await this.queue.add(async () => { - try { - await processBlock(link, controller.signal) - } catch (e) { - if (!(e instanceof AbortError)) { - void addToQueue(link) - } - } - }) - } catch (e) { - } - } + processedBlocks.push(decodedBlock) - interface BlockStat { - fetchTime: number - byteLength: number + if (!hasBlockBeenDownloaded) { + blocksStats.push({ + fetchTime: Math.floor(Date.now() / 1000), + byteLength: decodedBlock.Data?.byteLength || 0, + }) } - // Transfer speed - const blocksStats: BlockStat[] = [] - - const updateTransferSpeed = setInterval(async () => { - const bytesDownloaded = blocksStats.reduce((previousValue, currentValue) => { - if (Math.floor(Date.now() / 1000) - currentValue.fetchTime < TRANSFER_SPEED_SPAN) return previousValue + currentValue.byteLength - return 0 - }, 0) - const uniqueProcessedBlocks = [...new Set(processedBlocks)] - const totalBytesDownloaded = uniqueProcessedBlocks.reduce((prev, curr) => { - if (curr.Data) { - return prev + curr.Data.byteLength - } else { - return prev - } - }, 0) - const transferSpeed = bytesDownloaded === 0 ? 0 : bytesDownloaded / TRANSFER_SPEED_SPAN - const fileState = this.files.get(fileMetadata.cid) - if (!fileState) { - log.error(`No saved data for file cid ${fileMetadata.cid}`) - return - } - this.files.set(fileMetadata.cid, { - ...fileState, transferSpeed, downloadedBytes: totalBytesDownloaded - }) - await this.updateStatus(fileMetadata.cid) - }, UPDATE_STATUS_INTERVAL * 1000) - - const remainingBlocks = new Set() - remainingBlocks.add(block) - - const downloadCompletedOrCanceled = new Promise((resolve, reject) => { - const interval = setInterval(() => { - if (remainingBlocks.size === 0) { - clearInterval(interval) - resolve('No more blocks to fetch, download is completed or canceled') - } - }, 1000) - }) - - const processBlock = async (block: CID, signal: AbortSignal) => { - // eslint-disable-next-line - return await new Promise(async (resolve, reject) => { - const onAbort = () => { - remainingBlocks.delete(block) - reject(new AbortError('download cancelation')) - } - - if (signal.aborted) onAbort() - signal.addEventListener('abort', onAbort, { once: true }) - - // @ts-ignore FIXME - if (processedBlocks.includes(block)) { - remainingBlocks.delete(block) - resolve(block) - return - } - - const hasBlockBeenDownloaded = localBlocks.includes(`z${block.toString()}`) - - let fetchedBlock - - try { - fetchedBlock = await this.ipfs.block.get(block, { timeout: BLOCK_FETCH_TIMEOUT * 1000 }) - } catch (e) { - signal.removeEventListener('abort', onAbort) - reject(new Error("couldn't fetch block")) - return - } - - const decodedBlock: PBNode = decode(fetchedBlock) - - const fileState = this.files.get(fileMetadata.cid) - - if (!fileState) { - reject(new Error('Downloading has been cancelled')) - return - } - - processedBlocks.push(decodedBlock) - - if (!hasBlockBeenDownloaded) { - blocksStats.push({ - fetchTime: Math.floor(Date.now() / 1000), - byteLength: decodedBlock.Data?.byteLength || 0 - }) - } - - for (const link of decodedBlock.Links) { - // @ts-ignore - void addToQueue(link.Hash) - remainingBlocks.add(link.Hash) - } - - signal.removeEventListener('abort', onAbort) - remainingBlocks.delete(block) - resolve(fetchedBlock) - }) + for (const link of decodedBlock.Links) { + // @ts-ignore + void addToQueue(link.Hash) + remainingBlocks.add(link.Hash) } - void addToQueue(block) + signal.removeEventListener('abort', onAbort) + remainingBlocks.delete(block) + resolve(fetchedBlock) + }) + } - await downloadCompletedOrCanceled + void addToQueue(block) - clearInterval(updateTransferSpeed) + await downloadCompletedOrCanceled - const fileState = this.files.get(fileMetadata.cid) - if (!fileState) { - log.error(`No saved data for file cid ${fileMetadata.cid}`) - return - } + clearInterval(updateTransferSpeed) - if (this.cancelledDownloads.has(fileMetadata.cid)) { - this.files.set(fileMetadata.cid, { - ...fileState, downloadedBytes: 0, transferSpeed: 0 - }) - this.cancelledDownloads.delete(fileMetadata.cid) - this.controllers.delete(fileMetadata.cid) - await this.updateStatus(fileMetadata.cid, DownloadState.Canceled) - this.files.delete(fileMetadata.cid) - } else { - this.files.set(fileMetadata.cid, { - ...fileState, transferSpeed: 0 - }) - await this.ipfs.pin.add(block, { recursive: true }) - await this.assemblyFile(fileMetadata) - } + const fileState = this.files.get(fileMetadata.cid) + if (!fileState) { + log.error(`No saved data for file cid ${fileMetadata.cid}`) + return } - private assemblyFile = async (fileMetadata: FileMetadata) => { - const _CID = CID.parse(fileMetadata.cid) + if (this.cancelledDownloads.has(fileMetadata.cid)) { + this.files.set(fileMetadata.cid, { + ...fileState, + downloadedBytes: 0, + transferSpeed: 0, + }) + this.cancelledDownloads.delete(fileMetadata.cid) + this.controllers.delete(fileMetadata.cid) + await this.updateStatus(fileMetadata.cid, DownloadState.Canceled) + this.files.delete(fileMetadata.cid) + } else { + this.files.set(fileMetadata.cid, { + ...fileState, + transferSpeed: 0, + }) + await this.ipfs.pin.add(block, { recursive: true }) + await this.assemblyFile(fileMetadata) + } + } - const downloadDirectory = path.join(this.quietDir, 'downloads', fileMetadata.cid) - createPaths([downloadDirectory]) + private assemblyFile = async (fileMetadata: FileMetadata) => { + const _CID = CID.parse(fileMetadata.cid) - const fileName = fileMetadata.name + fileMetadata.ext - const filePath = `${path.join(downloadDirectory, fileName)}` + const downloadDirectory = path.join(this.quietDir, 'downloads', fileMetadata.cid) + createPaths([downloadDirectory]) - const writeStream = fs.createWriteStream(filePath) + const fileName = fileMetadata.name + fileMetadata.ext + const filePath = `${path.join(downloadDirectory, fileName)}` - const entries = this.ipfs.cat(_CID) + const writeStream = fs.createWriteStream(filePath) - for await (const entry of entries) { - await new Promise((resolve, reject) => { - writeStream.write(entry, err => { - if (err) { - console.error(`${fileMetadata.name} writing to file error: ${err}`) - reject(err) - } - }) - resolve() - }) - } + const entries = this.ipfs.cat(_CID) - writeStream.end() + for await (const entry of entries) { + await new Promise((resolve, reject) => { + writeStream.write(entry, err => { + if (err) { + console.error(`${fileMetadata.name} writing to file error: ${err}`) + reject(err) + } + }) + resolve() + }) + } - await this.updateStatus(fileMetadata.cid, DownloadState.Completed) - this.files.delete(fileMetadata.cid) - this.controllers.delete(fileMetadata.cid) + writeStream.end() - const messageMedia: FileMetadata = { - ...fileMetadata, - path: filePath - } + await this.updateStatus(fileMetadata.cid, DownloadState.Completed) + this.files.delete(fileMetadata.cid) + this.controllers.delete(fileMetadata.cid) - this.emit(IpfsFilesManagerEvents.UPDATE_MESSAGE_MEDIA, messageMedia) + const messageMedia: FileMetadata = { + ...fileMetadata, + path: filePath, } - private updateStatus = async (cid: string, downloadState = DownloadState.Downloading) => { - const metadata = this.files.get(cid) - if (!metadata) { - // TODO: emit error? - return - } - const progress: DownloadProgress | undefined = downloadState !== DownloadState.Malicious ? { + this.emit(IpfsFilesManagerEvents.UPDATE_MESSAGE_MEDIA, messageMedia) + } + + private updateStatus = async (cid: string, downloadState = DownloadState.Downloading) => { + const metadata = this.files.get(cid) + if (!metadata) { + // TODO: emit error? + return + } + const progress: DownloadProgress | undefined = + downloadState !== DownloadState.Malicious + ? { size: metadata.size, downloaded: metadata.downloadedBytes, - transferSpeed: metadata.transferSpeed - } : undefined - - const status: DownloadStatus = { - mid: metadata.message.id, - cid: metadata.cid, - downloadState, - downloadProgress: progress - } - - this.emit(IpfsFilesManagerEvents.UPDATE_DOWNLOAD_PROGRESS, status) + transferSpeed: metadata.transferSpeed, + } + : undefined + + const status: DownloadStatus = { + mid: metadata.message.id, + cid: metadata.cid, + downloadState, + downloadProgress: progress, } + + this.emit(IpfsFilesManagerEvents.UPDATE_DOWNLOAD_PROGRESS, status) + } } diff --git a/packages/backend/src/storage/large.js b/packages/backend/src/storage/large.js index 3feca786f9..1d4fa68ed4 100644 --- a/packages/backend/src/storage/large.js +++ b/packages/backend/src/storage/large.js @@ -2,7 +2,7 @@ const fs = require('fs') const path = require('path') const crypto = require('crypto') -function createLargeFile () { +function createLargeFile() { const stream = fs.createWriteStream(path.join(__dirname, '/testUtils/large-file.txt')) const max = 10000 let i = 0 diff --git a/packages/backend/src/storage/localDB.test.ts b/packages/backend/src/storage/localDB.test.ts index 2b835d2124..5358b8e03f 100644 --- a/packages/backend/src/storage/localDB.test.ts +++ b/packages/backend/src/storage/localDB.test.ts @@ -14,21 +14,23 @@ describe('LocalDB', () => { beforeAll(() => { dbPath = path.join(createTmpDir().name, 'testDB') - peer1Address = '/dns4/mxtsfs4kzxzuisrw4tumdmycbyerqwakx37kj6om6azcjdaasifxmoqd.onion/tcp/443/wss/p2p/QmaEvCkpUG7GxhgvMkk8wxurfi1ehjHhSUNRksWTmXN2ix' + peer1Address = + '/dns4/mxtsfs4kzxzuisrw4tumdmycbyerqwakx37kj6om6azcjdaasifxmoqd.onion/tcp/443/wss/p2p/QmaEvCkpUG7GxhgvMkk8wxurfi1ehjHhSUNRksWTmXN2ix' peer1Stats = { [peer1Address]: { - peerId: 'QmaEvCkpUG7GxhgvMkk8wxurfi1ehjHhSUNRksWTmXN2ix', - connectionTime: 50, - lastSeen: 1000 - } + peerId: 'QmaEvCkpUG7GxhgvMkk8wxurfi1ehjHhSUNRksWTmXN2ix', + connectionTime: 50, + lastSeen: 1000, + }, } - peer2Address = '/dns4/hxr74a76b4lerhov75a6ha6yprruvow3wfu4qmmeoc6ajs7m7323lyid.onion/tcp/443/wss/p2p/QmZB6pVafcvAQfy5R5LxvDXvB8xcDifD39Lp3XGDM9XDuQ' + peer2Address = + '/dns4/hxr74a76b4lerhov75a6ha6yprruvow3wfu4qmmeoc6ajs7m7323lyid.onion/tcp/443/wss/p2p/QmZB6pVafcvAQfy5R5LxvDXvB8xcDifD39Lp3XGDM9XDuQ' peer2Stats = { [peer2Address]: { - peerId: 'QmZB6pVafcvAQfy5R5LxvDXvB8xcDifD39Lp3XGDM9XDuQ', - connectionTime: 500, - lastSeen: 500 - } + peerId: 'QmZB6pVafcvAQfy5R5LxvDXvB8xcDifD39Lp3XGDM9XDuQ', + connectionTime: 500, + lastSeen: 500, + }, } }) @@ -60,17 +62,15 @@ describe('LocalDB', () => { }) it('get sorted peers', async () => { - const extraPeers = ['/dns4/zl37gnntp64dhnisddftypxbt5cqx6cum65vdv6oeaffrbqmemwc52ad.onion/tcp/443/wss/p2p/QmPGdGDUV1PXaJky4V53KSvFszdqEcM7KCoDpF2uFPf5w6'] + const extraPeers = [ + '/dns4/zl37gnntp64dhnisddftypxbt5cqx6cum65vdv6oeaffrbqmemwc52ad.onion/tcp/443/wss/p2p/QmPGdGDUV1PXaJky4V53KSvFszdqEcM7KCoDpF2uFPf5w6', + ] await db.put(LocalDBKeys.PEERS, { ...peer1Stats, - ...peer2Stats + ...peer2Stats, }) const sortedPeers = await db.getSortedPeers(extraPeers) - expect(sortedPeers).toEqual([ - peer1Address, - peer2Address, - extraPeers[0] - ]) + expect(sortedPeers).toEqual([peer1Address, peer2Address, extraPeers[0]]) }) it('updates nested object', async () => { @@ -80,23 +80,23 @@ describe('LocalDB', () => { const peersDBdata = await db.get(LocalDBKeys.PEERS) expect(peersDBdata).toEqual({ ...peer1Stats, - ...peer2Stats + ...peer2Stats, }) const peer2StatsUpdated: NetworkStats = { peerId: 'QmR7Qgd4tg2XrGD3kW647ZnYyazTwHQF3cqRBmSduhhusA', connectionTime: 777, - lastSeen: 678 + lastSeen: 678, } await db.update(LocalDBKeys.PEERS, { - [peer2Address]: peer2StatsUpdated + [peer2Address]: peer2StatsUpdated, }) const updatedPeersDBdata = await db.get(LocalDBKeys.PEERS) expect(updatedPeersDBdata).toEqual({ ...peer1Stats, - [peer2Address]: peer2StatsUpdated + [peer2Address]: peer2StatsUpdated, }) }) }) diff --git a/packages/backend/src/storage/localDB.ts b/packages/backend/src/storage/localDB.ts index fc4030ec28..c9b5f15a12 100644 --- a/packages/backend/src/storage/localDB.ts +++ b/packages/backend/src/storage/localDB.ts @@ -9,7 +9,7 @@ const log = logger('levelDB') export enum LocalDBKeys { COMMUNITY = 'community', REGISTRAR = 'registrar', - PEERS = 'peers' + PEERS = 'peers', } export class LocalDB { @@ -69,7 +69,7 @@ export class LocalDB { } public async getSortedPeers(peers: string[] = []): Promise { - const peersStats = await this.get(LocalDBKeys.PEERS) || {} + const peersStats = (await this.get(LocalDBKeys.PEERS)) || {} const peersAddresses: string[] = [...new Set(Object.keys(peersStats).concat(peers))] const stats: NetworkStats[] = Object.values(peersStats) const sortedPeers = sortPeers(peersAddresses, stats) diff --git a/packages/backend/src/storage/storage.test.ts b/packages/backend/src/storage/storage.test.ts index 1068bfcb30..972da29b05 100644 --- a/packages/backend/src/storage/storage.test.ts +++ b/packages/backend/src/storage/storage.test.ts @@ -5,17 +5,27 @@ import { Config } from '../constants' import { type FactoryGirl } from 'factory-girl' import waitForExpect from 'wait-for-expect' import { fileURLToPath } from 'url' -import { - createUserCert, - keyFromCertificate, - parseCertificate -} from '@quiet/identity' +import { createUserCert, keyFromCertificate, parseCertificate } from '@quiet/identity' import { jest, beforeEach, describe, it, expect, afterEach, beforeAll } from '@jest/globals' import { sleep } from '../sleep' import { StorageEvents } from './types' import type { Storage as StorageType } from './storage' -import { type ChannelMessage, type Community, type Identity, MessageType, type PublicChannel, type TestMessage } from '@quiet/types' -import { type Store, getFactory, prepareStore, publicChannels, generateMessageFactoryContentWithId, type FileMetadata } from '@quiet/state-manager' +import { + type ChannelMessage, + type Community, + type Identity, + MessageType, + type PublicChannel, + type TestMessage, +} from '@quiet/types' +import { + type Store, + getFactory, + prepareStore, + publicChannels, + generateMessageFactoryContentWithId, + type FileMetadata, +} from '@quiet/state-manager' const filename = fileURLToPath(import.meta.url) const dirname = path.dirname(filename) @@ -31,13 +41,15 @@ jest.unstable_mockModule('../common/utils', async () => { fs.mkdirSync(path, { recursive: true }) } } - }) - } + }), + } }) type ComonUtilsModuleType = typeof import('../common/utils') // eslint-disable-line @typescript-eslint/consistent-type-imports -const { createLibp2p, createTmpDir, tmpQuietDirPath, rootPermsData, createFile, createPeerId } = await import('../common/testUtils') +const { createLibp2p, createTmpDir, tmpQuietDirPath, rootPermsData, createFile, createPeerId } = await import( + '../common/testUtils' +) let tmpDir: DirResult let tmpAppDataPath: string @@ -71,27 +83,18 @@ beforeAll(async () => { description: channel.description, owner: channel.owner, timestamp: channel.timestamp, - id: channel.id + id: channel.id, } - alice = await factory.create( - 'Identity', - { id: community.id, nickname: 'alice' } - ) + alice = await factory.create('Identity', { id: community.id, nickname: 'alice' }) - john = await factory.create( - 'Identity', - { id: community.id, nickname: 'john' } - ) + john = await factory.create('Identity', { id: community.id, nickname: 'john' }) message = ( - await factory.create( - 'Message', - { - identity: alice, - message: generateMessageFactoryContentWithId(channel.id) - } - ) + await factory.create('Message', { + identity: alice, + message: generateMessageFactoryContentWithId(channel.id), + }) ).message }) @@ -104,8 +107,7 @@ beforeEach(async () => { Storage = (await import('./storage')).Storage utils = await import('../common/utils') storage = new Storage(tmpAppDataPath, 'communityId') - filePath = path.join( - dirname, '/testUtils/500kB-file.txt') + filePath = path.join(dirname, '/testUtils/500kB-file.txt') }) afterEach(async () => { @@ -177,7 +179,7 @@ describe('Channels', () => { const channelFromKeyValueStore = storage.channels.get(channelio.id) expect(channelFromKeyValueStore).toBeUndefined() expect(eventSpy).toBeCalledWith('channelDeletionResponse', { - channelId: channelio.id + channelId: channelio.id, }) }) @@ -199,7 +201,7 @@ describe('Channels', () => { const channelFromKeyValueStore = storage.channels.get(channelio.id) expect(channelFromKeyValueStore).toEqual(channelio) expect(eventSpy).toBeCalledWith('channelDeletionResponse', { - channelId: channelio.id + channelId: channelio.id, }) }) }) @@ -327,116 +329,123 @@ describe('Certificate', () => { storage.certificates.events.emit('replicated') expect(eventSpy).toBeCalledWith('loadCertificates', { - certificates: [ - - ] + certificates: [], }) expect(spyOnUpdatePeersList).toBeCalled() }) - it.each(['write', 'replicate.progress'])('The message is verified valid on "%s" db event', async (eventName: string) => { - const aliceMessage = await factory.create< - ReturnType['payload'] - >('Message', { - identity: alice, - message: generateMessageFactoryContentWithId(channel.id) - }) + it.each(['write', 'replicate.progress'])( + 'The message is verified valid on "%s" db event', + async (eventName: string) => { + const aliceMessage = await factory.create['payload']>( + 'Message', + { + identity: alice, + message: generateMessageFactoryContentWithId(channel.id), + } + ) - storage = new Storage(tmpAppDataPath, community.id, { createPaths: false }) + storage = new Storage(tmpAppDataPath, community.id, { createPaths: false }) - const peerId = await createPeerId() - const libp2p = await createLibp2p(peerId) + const peerId = await createPeerId() + const libp2p = await createLibp2p(peerId) - await storage.init(libp2p, peerId) - await storage.initDatabases() + await storage.init(libp2p, peerId) + await storage.initDatabases() - await storage.subscribeToChannel(channelio) + await storage.subscribeToChannel(channelio) - const eventSpy = jest.spyOn(storage, 'emit') - 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 - const messagePayload = { - payload: { - value: aliceMessage.message + const eventSpy = jest.spyOn(storage, 'emit') + 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 + const messagePayload = { + payload: { + value: aliceMessage.message, + }, + } + + switch (eventName) { + case 'write': + db.events.emit(eventName, 'address', messagePayload, []) + break + case 'replicate.progress': + db.events.emit(eventName, 'address', 'hash', messagePayload, 'progress', 'total', []) + break } - } - switch (eventName) { - case 'write': - db.events.emit(eventName, 'address', messagePayload, []) - break - case 'replicate.progress': - db.events.emit(eventName, 'address', 'hash', messagePayload, 'progress', 'total', []) - break + await waitForExpect(() => { + expect(eventSpy).toBeCalledWith('loadMessages', { isVerified: true, messages: [aliceMessage.message] }) + }) } + ) - await waitForExpect(() => { - expect(eventSpy).toBeCalledWith('loadMessages', { isVerified: true, messages: [aliceMessage.message] } + it.each([['write'], ['replicate.progress']])( + 'The message is verified not valid on "%s" db event', + async (eventName: string) => { + const aliceMessage = await factory.create['payload']>( + 'Message', + { + identity: alice, + message: generateMessageFactoryContentWithId(channel.id), + } ) - }) - }) - it.each([ - ['write'], - ['replicate.progress'] - ])('The message is verified not valid on "%s" db event', async (eventName: string) => { - const aliceMessage = await factory.create< - ReturnType['payload'] - >('Message', { - identity: alice, - message: generateMessageFactoryContentWithId(channel.id) - }) - - const johnMessage = await factory.create< - ReturnType['payload'] - >('Message', { - identity: john, - message: generateMessageFactoryContentWithId(channel.id) + const johnMessage = await factory.create['payload']>( + 'Message', + { + identity: john, + message: generateMessageFactoryContentWithId(channel.id), + } + ) - }) + const aliceMessageWithJohnsPublicKey: ChannelMessage = { + ...aliceMessage.message, + pubKey: johnMessage.message.pubKey, + } - const aliceMessageWithJohnsPublicKey: ChannelMessage = { - ...aliceMessage.message, - pubKey: johnMessage.message.pubKey - } + storage = new Storage(tmpAppDataPath, community.id, { createPaths: false }) - storage = new Storage(tmpAppDataPath, community.id, { createPaths: false }) + const peerId = await createPeerId() + const libp2p = await createLibp2p(peerId) - const peerId = await createPeerId() - const libp2p = await createLibp2p(peerId) + await storage.init(libp2p, peerId) + await storage.initDatabases() + await storage.subscribeToChannel(channelio) - await storage.init(libp2p, peerId) - await storage.initDatabases() - await storage.subscribeToChannel(channelio) + const spyOnEmit = jest.spyOn(storage, 'emit') + const publicChannelRepo = storage.publicChannelsRepos.get(message.channelId) + expect(publicChannelRepo).not.toBeUndefined() + // @ts-expect-error + const db = publicChannelRepo.db + const messagePayload = { + payload: { + value: aliceMessageWithJohnsPublicKey, + }, + } - const spyOnEmit = jest.spyOn(storage, 'emit') - const publicChannelRepo = storage.publicChannelsRepos.get(message.channelId) - expect(publicChannelRepo).not.toBeUndefined() - // @ts-expect-error - const db = publicChannelRepo.db - const messagePayload = { - payload: { - value: aliceMessageWithJohnsPublicKey + switch (eventName) { + case 'write': + db.events.emit(eventName, 'address', messagePayload, []) + break + case 'replicate.progress': + db.events.emit(eventName, 'address', 'hash', messagePayload, 'progress', 'total', []) + break } - } - switch (eventName) { - case 'write': - db.events.emit(eventName, 'address', messagePayload, []) - break - case 'replicate.progress': - db.events.emit(eventName, 'address', 'hash', messagePayload, 'progress', 'total', []) - break + await waitForExpect(() => { + expect(spyOnEmit).toBeCalledWith('loadMessages', { + isVerified: false, + messages: [aliceMessageWithJohnsPublicKey], + }) + }) } - - await waitForExpect(() => { - expect(spyOnEmit).toBeCalledWith('loadMessages', { isVerified: false, messages: [aliceMessageWithJohnsPublicKey] } - ) - }) - }) + ) it('Certificates and peers list are updated on write event', async () => { storage = new Storage(tmpAppDataPath, community.id, { createPaths: false }) @@ -453,9 +462,7 @@ describe('Certificate', () => { storage.certificates.events.emit('write', 'address', { payload: { value: 'something' } }, []) expect(eventSpy).toBeCalledWith(StorageEvents.LOAD_CERTIFICATES, { - certificates: [ - - ] + certificates: [], }) expect(spyOnUpdatePeersList).toBeCalled() }) @@ -481,7 +488,7 @@ describe('Message access controller', () => { const eventSpy = jest.spyOn(db, 'add') const messageCopy = { - ...message + ...message, } delete messageCopy.media @@ -496,12 +503,13 @@ describe('Message access controller', () => { }) it('is not saved to db if did not pass signature verification', async () => { - const aliceMessage = await factory.create< - ReturnType['payload'] - >('Message', { - identity: alice, - message: generateMessageFactoryContentWithId(channel.id) - }) + const aliceMessage = await factory.create['payload']>( + 'Message', + { + identity: alice, + message: generateMessageFactoryContentWithId(channel.id), + } + ) // @ts-expect-error userCertificate can be undefined const johnCertificate: string = john.userCertificate const johnPublicKey = keyFromCertificate(parseCertificate(johnCertificate)) @@ -509,7 +517,7 @@ describe('Message access controller', () => { const spoofedMessage = { ...aliceMessage.message, channelId: channelio.id, - pubKey: johnPublicKey + pubKey: johnPublicKey, } delete spoofedMessage.media // Media 'undefined' is not accepted by db.add @@ -547,7 +555,7 @@ describe('Users', () => { storage.getAllEventLogEntries = mockGetCertificates mockGetCertificates.mockReturnValue([ 'MIICWzCCAgGgAwIBAgIGAYKIVrmoMAoGCCqGSM49BAMCMA8xDTALBgNVBAMTBG1haW4wHhcNMjIwODEwMTUxOTIxWhcNMzAwMTMxMjMwMDAwWjBJMUcwRQYDVQQDEz5wM29xZHI1M2RrZ2czbjVudWV6bHp5YXdoeHZpdDVlZnh6bHVudnpwN243bG12YTZmajNpNDNhZC5vbmlvbjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCAjxbiV781WC8O5emEdavPaQfR0FD8CaqC+P3R3uRdL9xuzGeUu8f5NIplSJ6abBMnanGgcMs34u82buiFROHqjggENMIIBCTAJBgNVHRMEAjAAMAsGA1UdDwQEAwIAgDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwLwYJKoZIhvcNAQkMBCIEICSr5xj+pjBSb+YOZ7TMPQJHYs4KASfnc9TugSpKJUG/MBUGCisGAQQBg4wbAgEEBxMFZGV2dnYwPQYJKwYBAgEPAwEBBDATLlFtVlRrVWFkMkdxM01rQ2E4Z2YxMlIxZ3NXRGZrMnlpVEVxYjZZR1hERzJpUTMwSQYDVR0RBEIwQII+cDNvcWRyNTNka2dnM241bnVlemx6eWF3aHh2aXQ1ZWZ4emx1bnZ6cDduN2xtdmE2ZmozaTQzYWQub25pb24wCgYIKoZIzj0EAwIDSAAwRQIhAIXhkkgs3H6GcZ1GYrSL2qJYDRQcpZlmcbq7YjpJHaORAiBMfkwP75v08R/ud6BPWvdS36corT+596+HzpqFt6bffw==', - 'MIICYTCCAgegAwIBAgIGAYKIYnYuMAoGCCqGSM49BAMCMA8xDTALBgNVBAMTBG1haW4wHhcNMjIwODEwMTUzMjEwWhcNMzAwMTMxMjMwMDAwWjBJMUcwRQYDVQQDEz52bnl3dWl5bDdwN2lnMm11cmNzY2R5emtza281M2U0azNkcGRtMnlvb3B2dnUyNXA2d3dqcWJhZC5vbmlvbjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABM0cOt7jMJ6YhRvL9nhbDCh42QJPKDet/Zc2PJ9rm6CzYz1IXc5uRUCUNZSnNykVMZknogAavp0FjV+cFXzV8gGjggETMIIBDzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIAgDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwLwYJKoZIhvcNAQkMBCIEIIsBwPwIhLSltj9dnkgkMq3sOe3RVha9Mhukop6XOoISMBsGCisGAQQBg4wbAgEEDRMLZHNrZmpia3NmaWcwPQYJKwYBAgEPAwEBBDATLlFtZDJVbjlBeW5va1pyY1pHc011YXFndXBUdGlkSEdRblVrTlZmRkZBZWY5N0MwSQYDVR0RBEIwQII+dm55d3VpeWw3cDdpZzJtdXJjc2NkeXprc2tvNTNlNGszZHBkbTJ5b29wdnZ1MjVwNnd3anFiYWQub25pb24wCgYIKoZIzj0EAwIDSAAwRQIgAiCmGfUuSG010CxLEzu9mAQOgDq//SHI9LkXbmCxaAUCIQC9xzmkRBxq5HmNomYJ9ZAJXaY3J6+VqBYthaVnv0bhMw==' + 'MIICYTCCAgegAwIBAgIGAYKIYnYuMAoGCCqGSM49BAMCMA8xDTALBgNVBAMTBG1haW4wHhcNMjIwODEwMTUzMjEwWhcNMzAwMTMxMjMwMDAwWjBJMUcwRQYDVQQDEz52bnl3dWl5bDdwN2lnMm11cmNzY2R5emtza281M2U0azNkcGRtMnlvb3B2dnUyNXA2d3dqcWJhZC5vbmlvbjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABM0cOt7jMJ6YhRvL9nhbDCh42QJPKDet/Zc2PJ9rm6CzYz1IXc5uRUCUNZSnNykVMZknogAavp0FjV+cFXzV8gGjggETMIIBDzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIAgDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwLwYJKoZIhvcNAQkMBCIEIIsBwPwIhLSltj9dnkgkMq3sOe3RVha9Mhukop6XOoISMBsGCisGAQQBg4wbAgEEDRMLZHNrZmpia3NmaWcwPQYJKwYBAgEPAwEBBDATLlFtZDJVbjlBeW5va1pyY1pHc011YXFndXBUdGlkSEdRblVrTlZmRkZBZWY5N0MwSQYDVR0RBEIwQII+dm55d3VpeWw3cDdpZzJtdXJjc2NkeXprc2tvNTNlNGszZHBkbTJ5b29wdnZ1MjVwNnd3anFiYWQub25pb24wCgYIKoZIzj0EAwIDSAAwRQIgAiCmGfUuSG010CxLEzu9mAQOgDq//SHI9LkXbmCxaAUCIQC9xzmkRBxq5HmNomYJ9ZAJXaY3J6+VqBYthaVnv0bhMw==', ]) const allUsers = storage.getAllUsers() expect(allUsers).toStrictEqual([ @@ -555,26 +563,25 @@ describe('Users', () => { onionAddress: 'p3oqdr53dkgg3n5nuezlzyawhxvit5efxzlunvzp7n7lmva6fj3i43ad.onion', peerId: 'QmVTkUad2Gq3MkCa8gf12R1gsWDfk2yiTEqb6YGXDG2iQ3', dmPublicKey: '24abe718fea630526fe60e67b4cc3d024762ce0a0127e773d4ee812a4a2541bf', - username: 'devvv' + username: 'devvv', }, { onionAddress: 'vnywuiyl7p7ig2murcscdyzksko53e4k3dpdm2yoopvvu25p6wwjqbad.onion', peerId: 'Qmd2Un9AynokZrcZGsMuaqgupTtidHGQnUkNVfFFAef97C', dmPublicKey: '8b01c0fc0884b4a5b63f5d9e482432adec39edd15616bd321ba4a29e973a8212', - username: 'dskfjbksfig' - } + username: 'dskfjbksfig', + }, ]) }) }) describe('Files deletion', () => { - let realFilePath: string - let messages: { - messages: Record - } + let realFilePath: string + let messages: { + messages: Record + } beforeEach(async () => { - realFilePath = path.join( - dirname, '/testUtils/real-file.txt') + realFilePath = path.join(dirname, '/testUtils/real-file.txt') createFile(realFilePath, 2147483) storage = new Storage(tmpAppDataPath, 'communityId', { createPaths: false }) @@ -590,22 +597,23 @@ describe('Files deletion', () => { cid: 'uploading_id', message: { id: 'id', - channelId: channel.id - } + channelId: channel.id, + }, } - const aliceMessage = await factory.create< - ReturnType['payload'] - >('Message', { - identity: alice, - message: generateMessageFactoryContentWithId(channel.id, MessageType.File, metadata) - }) + const aliceMessage = await factory.create['payload']>( + 'Message', + { + identity: alice, + message: generateMessageFactoryContentWithId(channel.id, MessageType.File, metadata), + } + ) - messages = { - messages: { - [aliceMessage.message.id]: aliceMessage.message + messages = { + messages: { + [aliceMessage.message.id]: aliceMessage.message, + }, } - } }) afterEach(async () => { @@ -615,26 +623,22 @@ describe('Files deletion', () => { }) it('delete file correctly', async () => { - const isFileExist = await storage.checkIfFileExist(realFilePath) + const isFileExist = await storage.checkIfFileExist(realFilePath) expect(isFileExist).toBeTruthy() - await expect( - storage.deleteFilesFromChannel(messages) - ).resolves.not.toThrowError() + await expect(storage.deleteFilesFromChannel(messages)).resolves.not.toThrowError() - await waitForExpect(async() => { + await waitForExpect(async () => { expect(await storage.checkIfFileExist(realFilePath)).toBeFalsy() }, 2000) }) it('file dont exist - not throw error', async () => { fs.rmSync(realFilePath) - await waitForExpect(async() => { + await waitForExpect(async () => { expect(await storage.checkIfFileExist(realFilePath)).toBeFalsy() }, 2000) - await expect( - storage.deleteFilesFromChannel(messages) - ).resolves.not.toThrowError() + await expect(storage.deleteFilesFromChannel(messages)).resolves.not.toThrowError() }) }) diff --git a/packages/backend/src/storage/storage.ts b/packages/backend/src/storage/storage.ts index f922d4c423..6cb64e1e2b 100644 --- a/packages/backend/src/storage/storage.ts +++ b/packages/backend/src/storage/storage.ts @@ -5,7 +5,7 @@ import { keyObjectFromString, parseCertificate, verifySignature, - verifyUserCert + verifyUserCert, } from '@quiet/identity' import type { IPFS, create as createType } from 'ipfs-core' import { create } from 'ipfs-core' @@ -17,12 +17,7 @@ import path from 'path' import { EventEmitter } from 'events' import type PeerId from 'peer-id' import { getCrypto } from 'pkijs' -import { - type IMessageThread, - type DirectMessagesRepo, - type PublicChannelsRepo, - StorageOptions -} from '../common/types' +import { type IMessageThread, type DirectMessagesRepo, type PublicChannelsRepo, StorageOptions } from '../common/types' import { Config } from '../constants' import AccessControllers from 'orbit-db-access-controllers' import { MessagesAccessController } from './MessagesAccessController' @@ -35,7 +30,18 @@ import { StorageEvents } from './types' import { IpfsFilesManager, IpfsFilesManagerEvents } from './ipfsFileManager' import { CID } from 'multiformats/cid' -import { type ChannelMessage, ConnectionProcessInfo, type DeleteFilesFromChannelSocketPayload, type FileMetadata, NoCryptoEngineError, type PublicChannel, type PushNotificationPayload, type SaveCertificatePayload, SocketActionTypes, type User } from '@quiet/types' +import { + type ChannelMessage, + ConnectionProcessInfo, + type DeleteFilesFromChannelSocketPayload, + type FileMetadata, + NoCryptoEngineError, + type PublicChannel, + type PushNotificationPayload, + type SaveCertificatePayload, + SocketActionTypes, + type User, +} from '@quiet/types' import { isDefined } from '@quiet/common' import fs from 'fs' @@ -67,7 +73,7 @@ export class Storage extends EventEmitter { this.__communityId = communityId this.options = { ...new StorageOptions(), - ...options + ...options, } this.orbitDbDir = path.join(this.quietDir, this.options.orbitDbDir || Config.ORBIT_DB_DIR) this.ipfsRepoPath = path.join(this.quietDir, this.options.ipfsDir || Config.IPFS_REPO_PATH) @@ -97,7 +103,7 @@ export class Storage extends EventEmitter { id: peerID.toString(), directory: this.orbitDbDir, // @ts-ignore - AccessControllers + AccessControllers, }) this.emit(SocketActionTypes.CONNECTION_PROCESS_INFO, ConnectionProcessInfo.INITIALIZED_STORAGE) log('Initialized storage') @@ -164,11 +170,11 @@ export class Storage extends EventEmitter { preload: { enabled: false }, repo: this.ipfsRepoPath, EXPERIMENTAL: { - ipnsPubsub: true + ipnsPubsub: true, }, init: { - privateKey: peerID - } + privateKey: peerID, + }, }) } @@ -181,7 +187,7 @@ export class Storage extends EventEmitter { public async loadAllCertificates() { log('Getting all certificates') this.emit(StorageEvents.LOAD_CERTIFICATES, { - certificates: this.getAllEventLogEntries(this.certificates) + certificates: this.getAllEventLogEntries(this.certificates), }) } @@ -189,31 +195,30 @@ export class Storage extends EventEmitter { log('createDbForCertificates init') this.certificates = await this.orbitdb.log('certificates', { accessController: { - write: ['*'] - } + write: ['*'], + }, }) - this.certificates.events.on( - 'replicate.progress', - async (_address, _hash, entry, _progress, _total) => { - const certificate = entry.payload.value + this.certificates.events.on('replicate.progress', async (_address, _hash, entry, _progress, _total) => { + const certificate = entry.payload.value - const parsedCertificate = parseCertificate(certificate) - const key = keyFromCertificate(parsedCertificate) - - const username = getCertFieldValue(parsedCertificate, CertFieldsTypes.nickName) - if (!username) { - log.error(`Certificates replicate.progress: could not parse certificate for field type ${CertFieldsTypes.nickName}`) - return - } + const parsedCertificate = parseCertificate(certificate) + const key = keyFromCertificate(parsedCertificate) - this.userNamesMap.set(key, username) + const username = getCertFieldValue(parsedCertificate, CertFieldsTypes.nickName) + if (!username) { + log.error( + `Certificates replicate.progress: could not parse certificate for field type ${CertFieldsTypes.nickName}` + ) + return } - ) + + this.userNamesMap.set(key, username) + }) this.certificates.events.on('replicated', async () => { log('REPLICATED: Certificates') this.emit(SocketActionTypes.CONNECTION_PROCESS_INFO, ConnectionProcessInfo.CERTIFICATES_REPLICATED) this.emit(StorageEvents.LOAD_CERTIFICATES, { - certificates: this.getAllEventLogEntries(this.certificates) + certificates: this.getAllEventLogEntries(this.certificates), }) await this.updatePeersList() }) @@ -221,7 +226,7 @@ export class Storage extends EventEmitter { log('Saved certificate locally') log(entry.payload.value) this.emit(StorageEvents.LOAD_CERTIFICATES, { - certificates: this.getAllEventLogEntries(this.certificates) + certificates: this.getAllEventLogEntries(this.certificates), }) await this.updatePeersList() }) @@ -229,7 +234,7 @@ export class Storage extends EventEmitter { log('Loaded certificates to memory') this.emit(SocketActionTypes.CONNECTION_PROCESS_INFO, ConnectionProcessInfo.LOADED_CERTIFICATES) this.emit(StorageEvents.LOAD_CERTIFICATES, { - certificates: this.getAllEventLogEntries(this.certificates) + certificates: this.getAllEventLogEntries(this.certificates), }) }) @@ -245,7 +250,7 @@ export class Storage extends EventEmitter { // @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 Record + channels: this.channels.all as unknown as Record, }) } @@ -254,8 +259,8 @@ export class Storage extends EventEmitter { this.channels = await this.orbitdb.keyvalue('public-channels', { accessController: { // type: 'channelsaccess', - write: ['*'] - } + write: ['*'], + }, }) this.channels.events.on('write', async (_address, entry) => { @@ -279,7 +284,7 @@ export class Storage extends EventEmitter { }) this.emit(StorageEvents.LOAD_PUBLIC_CHANNELS, { - channels: keyValueChannels + channels: keyValueChannels, }) channels.forEach(async (channel: PublicChannel) => { @@ -301,8 +306,8 @@ export class Storage extends EventEmitter { private async createDbForMessageThreads() { this.messageThreads = await this.orbitdb.keyvalue('msg-threads', { accessController: { - write: ['*'] - } + write: ['*'], + }, }) this.messageThreads.events.on( 'replicated', @@ -323,7 +328,7 @@ export class Storage extends EventEmitter { async initAllChannels() { this.emit(StorageEvents.LOAD_PUBLIC_CHANNELS, { - channels: this.channels.all as unknown as Record + channels: this.channels.all as unknown as Record, }) } @@ -360,9 +365,7 @@ export class Storage extends EventEmitter { } protected getAllEventLogRawEntries(db: EventStore) { - return db - .iterator({ limit: -1 }) - .collect() + return db.iterator({ limit: -1 }).collect() } public async subscribeToChannel(channelData: PublicChannel): Promise { @@ -398,7 +401,7 @@ export class Storage extends EventEmitter { this.emit(StorageEvents.LOAD_MESSAGES, { messages: [entry.payload.value], - isVerified: verified + isVerified: verified, }) }) @@ -412,7 +415,7 @@ export class Storage extends EventEmitter { this.emit(StorageEvents.LOAD_MESSAGES, { messages: [message], - isVerified: verified + isVerified: verified, }) // Display push notifications on mobile @@ -431,7 +434,7 @@ export class Storage extends EventEmitter { const payload: PushNotificationPayload = { message: JSON.stringify(message), - username + username, } this.emit(StorageEvents.SEND_PUSH_NOTIFICATION, payload) @@ -443,7 +446,7 @@ export class Storage extends EventEmitter { this.emit(StorageEvents.SEND_MESSAGES_IDS, { ids, channelId: channelData.id, - communityId: this.communityId + communityId: this.communityId, }) }) db.events.on('ready', () => { @@ -451,7 +454,7 @@ export class Storage extends EventEmitter { this.emit(StorageEvents.SEND_MESSAGES_IDS, { ids, channelId: channelData.id, - communityId: this.communityId + communityId: this.communityId, }) }) await db.load() @@ -460,13 +463,13 @@ export class Storage extends EventEmitter { log(`Subscribed to channel ${channelData.id}`) this.emit(StorageEvents.SET_CHANNEL_SUBSCRIBED, { - channelId: channelData.id + channelId: channelData.id, }) } public transformMessages(msgs: ChannelMessage[]) { console.log('---------------- TRANSFORMING MESSAGES ----------------------') - const messages = msgs.map((msg) => { + const messages = msgs.map(msg => { console.log('processing message ', msg.id) // @ts-ignore if (msg.channelAddress) { @@ -508,7 +511,7 @@ export class Storage extends EventEmitter { filteredMessages = this.transformMessages(filteredMessages) this.emit(StorageEvents.LOAD_MESSAGES, { messages: filteredMessages, - isVerified: true + isVerified: true, }) this.emit(StorageEvents.CHECK_FOR_MISSING_FILES, this.communityId) } @@ -524,23 +527,20 @@ export class Storage extends EventEmitter { // @ts-ignore const channelId = data.id || data.address - const db: EventStore = await this.orbitdb.log( - `channels.${channelId}`, - { - accessController: { - type: 'messagesaccess', - write: ['*'] - } - } - ) + const db: EventStore = await this.orbitdb.log(`channels.${channelId}`, { + accessController: { + type: 'messagesaccess', + write: ['*'], + }, + }) const channel = this.channels.get(channelId) if (channel === undefined) { await this.channels.put(channelId, { - ...data + ...data, }) this.emit(StorageEvents.CREATED_CHANNEL, { - channel: data + channel: data, }) } @@ -564,28 +564,27 @@ export class Storage extends EventEmitter { } let repo = this.publicChannelsRepos.get(channelId) if (!repo) { - const db = await this.orbitdb.log( - `channels.${channelId}`, - { - accessController: { - type: 'messagesaccess', - write: ['*'] - } - } - ) + const db = await this.orbitdb.log(`channels.${channelId}`, { + accessController: { + type: 'messagesaccess', + write: ['*'], + }, + }) repo = { db, - eventsAttached: false + eventsAttached: false, } } await repo.db.load() const allEntries = this.getAllEventLogRawEntries(repo.db) await repo.db.close() await repo.db.drop() - const hashes = allEntries.map((e) => CID.parse(e.hash)) - const files = allEntries.map((e) => { - return e.payload.value.media - }).filter(isDefined) + const hashes = allEntries.map(e => CID.parse(e.hash)) + const files = allEntries + .map(e => { + return e.payload.value.media + }) + .filter(isDefined) // await this.deleteChannelFiles(files) // await this.deleteChannelMessages(hashes) this.publicChannelsRepos.delete(channelId) @@ -609,7 +608,6 @@ export class Storage extends EventEmitter { for await (const res of gcresult) { // @ts-ignore // const ccc = base58.base58btc.encode(res.cid?.multihash.bytes) - // console.log('base58btc encoded', ccc) // console.log('garbage collector result', res) } @@ -627,9 +625,7 @@ export class Storage extends EventEmitter { } const repo = this.publicChannelsRepos.get(message.channelId) if (!repo) { - log.error( - `Could not send message. No '${message.channelId}' channel in saved public channels` - ) + log.error(`Could not send message. No '${message.channelId}' channel in saved public channels`) return } try { @@ -640,22 +636,22 @@ export class Storage extends EventEmitter { } private attachFileManagerEvents = () => { - this.filesManager.on(IpfsFilesManagerEvents.UPDATE_DOWNLOAD_PROGRESS, (status) => { + this.filesManager.on(IpfsFilesManagerEvents.UPDATE_DOWNLOAD_PROGRESS, status => { this.emit(StorageEvents.UPDATE_DOWNLOAD_PROGRESS, status) }) - this.filesManager.on(IpfsFilesManagerEvents.UPDATE_MESSAGE_MEDIA, (messageMedia) => { + this.filesManager.on(IpfsFilesManagerEvents.UPDATE_MESSAGE_MEDIA, messageMedia => { this.emit(StorageEvents.UPDATE_MESSAGE_MEDIA, messageMedia) }) - this.filesManager.on(StorageEvents.REMOVE_DOWNLOAD_STATUS, (payload) => { + this.filesManager.on(StorageEvents.REMOVE_DOWNLOAD_STATUS, payload => { this.emit(StorageEvents.REMOVE_DOWNLOAD_STATUS, payload) }) - this.filesManager.on(StorageEvents.UPLOADED_FILE, (payload) => { + this.filesManager.on(StorageEvents.UPLOADED_FILE, payload => { this.emit(StorageEvents.UPLOADED_FILE, payload) }) - this.filesManager.on(StorageEvents.UPDATE_DOWNLOAD_PROGRESS, (payload) => { + this.filesManager.on(StorageEvents.UPDATE_DOWNLOAD_PROGRESS, payload => { this.emit(StorageEvents.UPDATE_DOWNLOAD_PROGRESS, payload) }) - this.filesManager.on(StorageEvents.UPDATE_MESSAGE_MEDIA, (payload) => { + this.filesManager.on(StorageEvents.UPDATE_MESSAGE_MEDIA, payload => { this.emit(StorageEvents.UPDATE_MESSAGE_MEDIA, payload) }) } @@ -679,8 +675,8 @@ export class Storage extends EventEmitter { } const db: EventStore = await this.orbitdb.log(`dms.${address}`, { accessController: { - write: ['*'] - } + write: ['*'], + }, }) this.directMessagesRepos.set(address, { db, eventsAttached: false }) @@ -715,20 +711,20 @@ export class Storage extends EventEmitter { log('Subscribing to direct messages thread ', channelId) this.emit(StorageEvents.LOAD_ALL_DIRECT_MESSAGES, { messages: this.getAllEventLogEntries(db), - channelId + channelId, }) db.events.on('write', (_address, _entry) => { log('Writing') this.emit(StorageEvents.LOAD_ALL_DIRECT_MESSAGES, { messages: this.getAllEventLogEntries(db), - channelId + channelId, }) }) db.events.on('replicated', () => { log('Message replicated') this.emit(StorageEvents.LOAD_ALL_DIRECT_MESSAGES, { messages: this.getAllEventLogEntries(db), - channelId + channelId, }) }) db.events.on('ready', () => { @@ -742,15 +738,15 @@ export class Storage extends EventEmitter { 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') + throw new Error("No channel ID, can't create channel") } log(`creatin direct message thread for ${channelId}`) const db: EventStore = await this.orbitdb.log(`dms.${channelId}`, { accessController: { - write: ['*'] - } + write: ['*'], + }, }) db.events.on('replicated', () => { log('replicated some messages') @@ -793,10 +789,7 @@ export class Storage extends EventEmitter { log('Certificate is either null or undefined, not saving to db') return false } - const verification = await verifyUserCert( - payload.rootPermsData.certificate, - payload.certificate - ) + const verification = await verifyUserCert(payload.rootPermsData.certificate, payload.certificate) if (verification.resultCode !== 0) { log.error('Certificate is not valid') log.error(verification.resultMessage) @@ -859,7 +852,7 @@ export class Storage extends EventEmitter { public async deleteFilesFromChannel(payload: DeleteFilesFromChannelSocketPayload) { const { messages } = payload - Object.keys(messages).map(async(key) => { + Object.keys(messages).map(async key => { const message = messages[key] if (message?.media?.path) { const mediaPath = message.media.path @@ -867,11 +860,11 @@ export class Storage extends EventEmitter { const isFileExist = await this.checkIfFileExist(mediaPath) log(`deleteFilesFromChannel : isFileExist- ${isFileExist}`) if (isFileExist) { - fs.unlink(mediaPath, unlinkError => { - if (unlinkError) { - log(`deleteFilesFromChannel : unlink error - ${unlinkError}`) - } - }) + fs.unlink(mediaPath, unlinkError => { + if (unlinkError) { + log(`deleteFilesFromChannel : unlink error - ${unlinkError}`) + } + }) } else { log(`deleteFilesFromChannel : file dont exist - ${mediaPath}`) } @@ -880,11 +873,11 @@ export class Storage extends EventEmitter { } public async checkIfFileExist(filepath: string): Promise { - return await new Promise((resolve) => { - fs.access(filepath, fs.constants.F_OK, error => { - resolve(!error) - }) + return await new Promise(resolve => { + fs.access(filepath, fs.constants.F_OK, error => { + resolve(!error) }) + }) } private async deleteFilesFromTemporaryDir() { diff --git a/packages/backend/src/storage/storageLargeFiles.test.ts b/packages/backend/src/storage/storageLargeFiles.test.ts index a99438397a..141589ff6f 100644 --- a/packages/backend/src/storage/storageLargeFiles.test.ts +++ b/packages/backend/src/storage/storageLargeFiles.test.ts @@ -47,8 +47,8 @@ describe('Storage', () => { cid: 'uploading_id', message: { id: 'id', - channelId: 'channelId' - } + channelId: 'channelId', + }, } await fileManager.uploadFile(metadata) @@ -63,27 +63,39 @@ describe('Storage', () => { expect(eventSpy).toHaveBeenNthCalledWith(1, StorageEvents.REMOVE_DOWNLOAD_STATUS, { cid: metadata.cid }) }) await waitForExpect(() => { - expect(eventSpy).toHaveBeenNthCalledWith(2, StorageEvents.UPLOADED_FILE, expect.objectContaining({ - ...metadata, - cid: expect.stringContaining('Qm'), - width: undefined, - height: undefined - })) + expect(eventSpy).toHaveBeenNthCalledWith( + 2, + StorageEvents.UPLOADED_FILE, + expect.objectContaining({ + ...metadata, + cid: expect.stringContaining('Qm'), + width: undefined, + height: undefined, + }) + ) }) await waitForExpect(() => { - expect(eventSpy).toHaveBeenNthCalledWith(3, StorageEvents.UPDATE_DOWNLOAD_PROGRESS, expect.objectContaining({ - cid: expect.stringContaining('Qm'), - downloadState: DownloadState.Hosted, - downloadProgress: undefined - })) + expect(eventSpy).toHaveBeenNthCalledWith( + 3, + StorageEvents.UPDATE_DOWNLOAD_PROGRESS, + expect.objectContaining({ + cid: expect.stringContaining('Qm'), + downloadState: DownloadState.Hosted, + downloadProgress: undefined, + }) + ) }) await waitForExpect(() => { - expect(eventSpy).toHaveBeenNthCalledWith(4, StorageEvents.UPDATE_MESSAGE_MEDIA, expect.objectContaining({ - ...metadata, - cid: expect.stringContaining('Qm'), - width: undefined, - height: undefined - })) + expect(eventSpy).toHaveBeenNthCalledWith( + 4, + StorageEvents.UPDATE_MESSAGE_MEDIA, + expect.objectContaining({ + ...metadata, + cid: expect.stringContaining('Qm'), + width: undefined, + height: undefined, + }) + ) }) await sleep(20_000) diff --git a/packages/backend/src/storage/testUtils/storageSnapshot.ts b/packages/backend/src/storage/testUtils/storageSnapshot.ts index a461da442c..3242695578 100644 --- a/packages/backend/src/storage/testUtils/storageSnapshot.ts +++ b/packages/backend/src/storage/testUtils/storageSnapshot.ts @@ -42,15 +42,11 @@ export class StorageTestSnapshot extends Storage { protected snapshotSaved: boolean protected msgReplCount: number - constructor( - quietDir: string, - communityId: string, - options?: Partial - ) { + constructor(quietDir: string, communityId: string, options?: Partial) { super(quietDir, communityId, options) this.options = { ...new StorageTestSnapshotOptions(), - ...options + ...options, } this.useSnapshot = options?.useSnapshot || process.env.USE_SNAPSHOT === 'true' // Actually use snapshot mechanizm this.messagesCount = options?.messagesCount || 0 // Quantity of messages that will be added to db @@ -83,8 +79,8 @@ export class StorageTestSnapshot extends Storage { } this.snapshotInfoDb = await this.orbitdb.log('092183012', { accessController: { - write: ['*'] - } + write: ['*'], + }, }) // eslint-disable-next-line @@ -108,8 +104,8 @@ export class StorageTestSnapshot extends Storage { log('createDbForMessages init') this.messages = await this.orbitdb.log('3479623913-test', { accessController: { - write: ['*'] - } + write: ['*'], + }, }) // Create snapshot and save to db for other peers to retrieve @@ -139,27 +135,26 @@ export class StorageTestSnapshot extends Storage { this.messages.events.on( 'replicate.progress', async (_address, _hash, _entry, progress, _total) => { - if (!this.replicationStartTime) { - console.time(`${this.name}; Replication time`) - this.replicationStartTime = new Date() - log('progress start', progress) - } - // log('---') - // log(`replicate.progress: ${address}`) - // log(`replicate.progress: ${hash}`) - // log(`${this.name}; replicate.progress: ${entry.payload.value}`) - // log(`replicate.progress: ${progress}`) - // log(`replicate.progress: ${total}`) - // await this.messages.load() - // log('Loaded entries replicate.progress:', this.getAllEventLogEntries(this.messages).length) - // fs.writeFileSync('allReplicatedMessages.json', JSON.stringify(this.getAllEventLogEntries(this.messages))) - if (progress === this.messagesCount) { - console.timeEnd(`${this.name}; Replication time`) - const diff = new Date().getTime() - this.replicationStartTime.getTime() - this.replicationTime = Number(diff / 1000) - } + if (!this.replicationStartTime) { + console.time(`${this.name}; Replication time`) + this.replicationStartTime = new Date() + log('progress start', progress) } - ) + // log('---') + // log(`replicate.progress: ${address}`) + // log(`replicate.progress: ${hash}`) + // log(`${this.name}; replicate.progress: ${entry.payload.value}`) + // log(`replicate.progress: ${progress}`) + // log(`replicate.progress: ${total}`) + // await this.messages.load() + // log('Loaded entries replicate.progress:', this.getAllEventLogEntries(this.messages).length) + // fs.writeFileSync('allReplicatedMessages.json', JSON.stringify(this.getAllEventLogEntries(this.messages))) + if (progress === this.messagesCount) { + console.timeEnd(`${this.name}; Replication time`) + const diff = new Date().getTime() - this.replicationStartTime.getTime() + this.replicationTime = Number(diff / 1000) + } + }) await this.messages.load() log(`${this.name}; Loaded entries:`, this.getAllEventLogEntries(this.messages).length) @@ -209,7 +204,7 @@ export class StorageTestSnapshot extends Storage { mode: snapshot.mode, hash: snapshot.hash, size: snapshot.size, - unfinished + unfinished, }) log('Saved snapshot info to DB') } @@ -225,13 +220,13 @@ export class StorageTestSnapshot extends Storage { size: snapshotInfo.size, mode: snapshotInfo.mode, mtime: undefined, - hash: snapshotInfo.hash + hash: snapshotInfo.hash, } return { queuePath: snapshotInfo.queuePath, snapshotPath: snapshotInfo.snapshotPath, snapshot, - unfinished: snapshotInfo.unfinished + unfinished: snapshotInfo.unfinished, } } @@ -246,7 +241,7 @@ export class StorageTestSnapshot extends Storage { heads: snapshotData.heads, size: snapshotData.values.length, values: snapshotData.values, - type: db.type + type: db.type, }) ) @@ -256,9 +251,7 @@ export class StorageTestSnapshot extends Storage { await db._cache.set(db.snapshotPath, snapshot) await db._cache.set(db.queuePath, unfinished) - console.debug( - `Saved snapshot: ${snapshot.hash as string}, queue length: ${unfinished.length as string}` - ) + console.debug(`Saved snapshot: ${snapshot.hash as string}, queue length: ${unfinished.length as string}`) await this.saveSnapshotInfoToDb( // Saving it to share with others db.queuePath, @@ -314,7 +307,7 @@ export class StorageTestSnapshot extends Storage { sortFn: db.options.sortFn, length: -1, timeout: 1000, - onProgressCallback: onProgress + onProgressCallback: onProgress, }) await db._oplog.join(log) await db._updateIndex() diff --git a/packages/backend/src/storage/types.ts b/packages/backend/src/storage/types.ts index 3265cc4e6b..9318fa8138 100644 --- a/packages/backend/src/storage/types.ts +++ b/packages/backend/src/storage/types.ts @@ -1,23 +1,23 @@ export enum StorageEvents { -// Peers -UPDATE_PEERS_LIST = 'updatePeersList', -LOAD_CERTIFICATES = 'loadCertificates', -// Public Channels -LOAD_PUBLIC_CHANNELS = 'loadPublicChannels', -LOAD_ALL_PRIVATE_CONVERSATIONS = 'loadAllPrivateConversations', -LOAD_MESSAGES = 'loadMessages', -SEND_MESSAGES_IDS = 'sendMessagesIds', -SET_CHANNEL_SUBSCRIBED = 'setChannelSubscribed', -CREATED_CHANNEL = 'createdChannel', -CHANNEL_DELETION_RESPONSE = 'channelDeletionResponse', -// Files -REMOVE_DOWNLOAD_STATUS = 'removeDownloadStatus', -UPLOADED_FILE = 'uploadedFile', -UPDATE_DOWNLOAD_PROGRESS = 'updateDownloadProgress', -UPDATE_MESSAGE_MEDIA = 'updateMessageMedia', -CHECK_FOR_MISSING_FILES = 'checkForMissingFiles', -// Direct Messages -LOAD_ALL_DIRECT_MESSAGES = 'loadAllDirectMessages', -// Misc -SEND_PUSH_NOTIFICATION = 'sendPushNotification' + // Peers + UPDATE_PEERS_LIST = 'updatePeersList', + LOAD_CERTIFICATES = 'loadCertificates', + // Public Channels + LOAD_PUBLIC_CHANNELS = 'loadPublicChannels', + LOAD_ALL_PRIVATE_CONVERSATIONS = 'loadAllPrivateConversations', + LOAD_MESSAGES = 'loadMessages', + SEND_MESSAGES_IDS = 'sendMessagesIds', + SET_CHANNEL_SUBSCRIBED = 'setChannelSubscribed', + CREATED_CHANNEL = 'createdChannel', + CHANNEL_DELETION_RESPONSE = 'channelDeletionResponse', + // Files + REMOVE_DOWNLOAD_STATUS = 'removeDownloadStatus', + UPLOADED_FILE = 'uploadedFile', + UPDATE_DOWNLOAD_PROGRESS = 'updateDownloadProgress', + UPDATE_MESSAGE_MEDIA = 'updateMessageMedia', + CHECK_FOR_MISSING_FILES = 'checkForMissingFiles', + // Direct Messages + LOAD_ALL_DIRECT_MESSAGES = 'loadAllDirectMessages', + // Misc + SEND_PUSH_NOTIFICATION = 'sendPushNotification', } diff --git a/packages/backend/src/torManager/TorControl.ts b/packages/backend/src/torManager/TorControl.ts index f5f400d89e..cc3736bb51 100644 --- a/packages/backend/src/torManager/TorControl.ts +++ b/packages/backend/src/torManager/TorControl.ts @@ -4,7 +4,7 @@ const log = logger('torControl') export enum TorControlAuthType { COOKIE = 'cookie', - PASSWORD = 'password' + PASSWORD = 'password', } interface IOpts { @@ -29,7 +29,7 @@ export class TorControl { constructor(opts: IOpts) { this.params = { port: opts.port, - family: 4 + family: 4, } if (opts.auth.type === TorControlAuthType.PASSWORD) { this.authString = 'AUTHENTICATE "' + opts.auth.value + '"\r\n' @@ -71,7 +71,11 @@ export class TorControl { this.connection = null } - private async _sendCommand(command: string, resolve: (value: {code: number; messages: string[]}) => void, reject: (reason?: any) => void) { + private async _sendCommand( + command: string, + resolve: (value: { code: number; messages: string[] }) => void, + reject: (reason?: any) => void + ) { await this.connect() const connectionTimeout = setTimeout(() => { reject('TOR: Send command timeout') diff --git a/packages/backend/src/torManager/torManager.tor.test.ts b/packages/backend/src/torManager/torManager.tor.test.ts index 90d0979031..f2ef51bef0 100644 --- a/packages/backend/src/torManager/torManager.tor.test.ts +++ b/packages/backend/src/torManager/torManager.tor.test.ts @@ -1,4 +1,3 @@ -/* eslint import/first: 0 */ import { Tor } from './torManager' import { torBinForPlatform, torDirForPlatform } from '../common/utils' import { createTmpDir, spawnTorProcess, tmpQuietDirPath } from '../common/testUtils' @@ -39,10 +38,10 @@ describe('Tor manager (using tor)', () => { options: { env: { LD_LIBRARY_PATH: libPath, - HOME: tmpAppDataPath + HOME: tmpAppDataPath, }, - detached: true - } + detached: true, + }, }) await tor.init() @@ -54,10 +53,10 @@ describe('Tor manager (using tor)', () => { options: { env: { LD_LIBRARY_PATH: libPath, - HOME: tmpAppDataPath + HOME: tmpAppDataPath, }, - detached: true - } + detached: true, + }, }) await torSecondInstance.init({}) await torSecondInstance.kill() @@ -76,7 +75,7 @@ describe('Tor manager (using tor)', () => { await tor.init() const hiddenServiceOnionAddress = await tor.spawnHiddenService({ targetPort: 4343, - privKey: 'ED25519-V3:uCr5t3EcOCwig4cu7pWY6996whV+evrRlI0iIIsjV3uCz4rx46sB3CPq8lXEWhjGl2jlyreomORirKcz9mmcdQ==' + privKey: 'ED25519-V3:uCr5t3EcOCwig4cu7pWY6996whV+evrRlI0iIIsjV3uCz4rx46sB3CPq8lXEWhjGl2jlyreomORirKcz9mmcdQ==', }) expect(hiddenServiceOnionAddress).toBe('u2rg2direy34dj77375h2fbhsc2tvxj752h4tlso64mjnlevcv54oaad.onion') await tor.kill() @@ -102,15 +101,13 @@ describe('Tor manager (using tor)', () => { options: { env: { LD_LIBRARY_PATH: libPath, - HOME: tmpAppDataPath + HOME: tmpAppDataPath, }, - detached: true - } + detached: true, + }, }) - await expect(tor.init({ repeat: 3, timeout: 1000 })) - .rejects - .toThrow('Failed to spawn tor 4 times') + await expect(tor.init({ repeat: 3, timeout: 1000 })).rejects.toThrow('Failed to spawn tor 4 times') await tor.kill() }) @@ -126,10 +123,10 @@ describe('Tor manager (using tor)', () => { options: { env: { LD_LIBRARY_PATH: libPath, - HOME: tmpAppDataPath + HOME: tmpAppDataPath, }, - detached: true - } + detached: true, + }, }) await tor.init({ repeat: 3, timeout: 40000 }) diff --git a/packages/backend/src/torManager/torManager.ts b/packages/backend/src/torManager/torManager.ts index c8aad74eb4..d09f5a022c 100644 --- a/packages/backend/src/torManager/torManager.ts +++ b/packages/backend/src/torManager/torManager.ts @@ -16,7 +16,7 @@ const log = logger('tor') export enum GetInfoTorSignal { CONFIG_TEXT = 'config-text', CIRCUT_STATUS = 'circuit-status', - ENTRY_GUARDS = 'entry-guards' + ENTRY_GUARDS = 'entry-guards', } export type TorParams = Record @@ -52,7 +52,7 @@ export class Tor extends EventEmitter { httpTunnelPort, extraTorProcessParams, controlPort, - authCookie + authCookie, }: IConstructor) { super() this.torPath = torPath ? path.normalize(torPath) : '' @@ -66,7 +66,7 @@ export class Tor extends EventEmitter { mergeDefaultTorParams = (params: TorParams = {}): TorParams => { const defaultParams = { - '--NumEntryGuards': '3' // See task #1295 + '--NumEntryGuards': '3', // See task #1295 } return { ...defaultParams, ...params } } @@ -132,21 +132,20 @@ export class Tor extends EventEmitter { } // eslint-disable-next-line tryToSpawnTor() - }) } public initTorControl = () => { if (!this.controlPort) { - throw new Error('Can\'t initialize TorControl - no control port') + throw new Error("Can't initialize TorControl - no control port") } this.torControl = new TorControl({ port: this.controlPort, host: 'localhost', auth: { value: this.torAuthCookie || this.torPassword, - type: this.torAuthCookie ? TorControlAuthType.COOKIE : TorControlAuthType.PASSWORD - } + type: this.torAuthCookie ? TorControlAuthType.COOKIE : TorControlAuthType.PASSWORD, + }, }) } @@ -155,7 +154,7 @@ export class Tor extends EventEmitter { android: `ps -p ${oldTorPid} -o comm=`, linux: `ps -p ${oldTorPid} -o comm=`, darwin: `ps -c -p ${oldTorPid} -o comm=`, - win32: `TASKLIST /FI "PID eq ${oldTorPid}"` + win32: `TASKLIST /FI "PID eq ${oldTorPid}"`, } return byPlatform[process.platform as SupportedPlatform] } @@ -168,7 +167,10 @@ export class Tor extends EventEmitter { android: `pgrep -af "${this.torDataDirectory}" | grep -v pgrep | awk '{print $1}'`, linux: `pgrep -af "${this.torDataDirectory}" | grep -v pgrep | awk '{print $1}'`, darwin: `ps -A | grep "${this.torDataDirectory}" | grep -v grep | awk '{print $1}'`, - win32: `powershell "Get-WmiObject Win32_process -Filter {commandline LIKE '%${this.torDataDirectory.replace(/\\/g, '\\\\')}%' and name = 'tor.exe'} | Format-Table ProcessId -HideTableHeaders"` + win32: `powershell "Get-WmiObject Win32_process -Filter {commandline LIKE '%${this.torDataDirectory.replace( + /\\/g, + '\\\\' + )}%' and name = 'tor.exe'} | Format-Table ProcessId -HideTableHeaders"`, } return byPlatform[process.platform as SupportedPlatform] } @@ -210,7 +212,7 @@ export class Tor extends EventEmitter { protected readonly spawnTor = async (timeoutMs: number): Promise => { await new Promise((resolve, reject) => { if (!this.controlPort) { - reject(new Error('Can\'t spawn tor - no control port')) + reject(new Error("Can't spawn tor - no control port")) return } this.process = child_process.spawn( @@ -228,7 +230,7 @@ export class Tor extends EventEmitter { this.torDataDirectory, '--HashedControlPassword', this.torHashedPassword, - ...this.torProcessParams + ...this.torProcessParams, ], this.options ) @@ -252,7 +254,7 @@ export class Tor extends EventEmitter { public async spawnHiddenService({ targetPort, privKey, - virtPort = 443 + virtPort = 443, }: { targetPort: number privKey: string @@ -260,38 +262,38 @@ export class Tor extends EventEmitter { }): Promise { const status = await this.torControl.sendCommand( `ADD_ONION ${privKey} Flags=Detach Port=${virtPort},127.0.0.1:${targetPort}` - ) - const onionAddress = status.messages[0].replace('250-ServiceID=', '') - return `${onionAddress}.onion` - } + ) + const onionAddress = status.messages[0].replace('250-ServiceID=', '') + return `${onionAddress}.onion` + } - public async destroyHiddenService(serviceId: string): Promise { - try { - await this.torControl.sendCommand(`DEL_ONION ${serviceId}`) - return true - } catch (err) { - log.error(`Couldn't destroy hidden service ${serviceId}`, err) - return false - } + public async destroyHiddenService(serviceId: string): Promise { + try { + await this.torControl.sendCommand(`DEL_ONION ${serviceId}`) + return true + } catch (err) { + log.error(`Couldn't destroy hidden service ${serviceId}`, err) + return false } + } - public async createNewHiddenService({ - targetPort, - virtPort = 443 - }: { - targetPort: number - virtPort?: number - }): Promise<{ onionAddress: string; privateKey: string }> { - const status = await this.torControl.sendCommand( - `ADD_ONION NEW:BEST Flags=Detach Port=${virtPort},127.0.0.1:${targetPort}` - ) - - const onionAddress = status.messages[0].replace('250-ServiceID=', '') - const privateKey = status.messages[1].replace('250-PrivateKey=', '') - - return { + public async createNewHiddenService({ + targetPort, + virtPort = 443, + }: { + targetPort: number + virtPort?: number + }): Promise<{ onionAddress: string; privateKey: string }> { + const status = await this.torControl.sendCommand( + `ADD_ONION NEW:BEST Flags=Detach Port=${virtPort},127.0.0.1:${targetPort}` + ) + + const onionAddress = status.messages[0].replace('250-ServiceID=', '') + const privateKey = status.messages[1].replace('250-PrivateKey=', '') + + return { onionAddress: `${onionAddress}.onion`, - privateKey + privateKey, } } @@ -316,10 +318,9 @@ export class Tor extends EventEmitter { public generateHashedPassword = () => { const password = crypto.randomBytes(16).toString('hex') - const hashedPassword = child_process.execSync( - `${this.torPath} --quiet --hash-password ${password}`, - { env: this.options?.env } - ) + const hashedPassword = child_process.execSync(`${this.torPath} --quiet --hash-password ${password}`, { + env: this.options?.env, + }) this.torPassword = password this.torHashedPassword = hashedPassword.toString().trim() } @@ -338,5 +339,5 @@ export class Tor extends EventEmitter { }) this.process?.kill() }) -} + } } diff --git a/packages/backend/src/torManager/torMesh.ts b/packages/backend/src/torManager/torMesh.ts index 416753130d..e9d5d791cf 100644 --- a/packages/backend/src/torManager/torMesh.ts +++ b/packages/backend/src/torManager/torMesh.ts @@ -3,11 +3,7 @@ import createHttpsProxyAgent from 'https-proxy-agent' import fetch, { type Response } from 'node-fetch' import { type Tor } from './torManager' -import { - createTmpDir, - spawnTorProcess, - tmpQuietDirPath -} from '../common/testUtils' +import { createTmpDir, spawnTorProcess, tmpQuietDirPath } from '../common/testUtils' import logger from '../logger' @@ -46,7 +42,7 @@ let finishedRequests = 0 const createServer = async (port: number, serverAddress: string) => { const app: express.Application = express() - // app.use(express.json()) + // app.use(express.json()) // eslint-disable-next-line app.post('/test', async (req, res) => { // eslint-disable-next-line @@ -64,17 +60,13 @@ const createAgent = async (httpTunnelPort: number) => { return createHttpsProxyAgent({ port: httpTunnelPort, host: 'localhost' }) } -const sendRequest = async ( - serviceAddress: string, - httpTunnelPort: number, - ownHs: string -): Promise => { +const sendRequest = async (serviceAddress: string, httpTunnelPort: number, ownHs: string): Promise => { const agent = await createAgent(httpTunnelPort) const options = { method: 'POST', body: JSON.stringify({ serviceAddress: ownHs }), headers: { 'Content-Type': 'application/json' }, - agent + agent, } try { return await fetch('http://' + serviceAddress + '.onion/test', options) @@ -102,9 +94,7 @@ const destroyHiddenServices = async () => { const hs = hiddenServices.get(key) if (!hs) continue await data.tor.destroyHiddenService(hs) - log( - `destroyed hidden service for instance ${key} with onion address ${hiddenServices.get(key)}` - ) + log(`destroyed hidden service for instance ${key} with onion address ${hiddenServices.get(key)}`) } } diff --git a/packages/backend/src/validation/validators.test.ts b/packages/backend/src/validation/validators.test.ts index ee0a5615c4..08211a4755 100644 --- a/packages/backend/src/validation/validators.test.ts +++ b/packages/backend/src/validation/validators.test.ts @@ -64,7 +64,8 @@ describe('Validators - Conversations', () => { }) describe('Validators - Messages', () => { - test('message is valid', () => { // message with media is valid + test('message is valid', () => { + // message with media is valid const msg = { id: 'fzxjdiasf8ashfisfd', type: 1, @@ -72,7 +73,7 @@ describe('Validators - Messages', () => { createdAt: 1234567, channelId: '123n23l234lk234', signature: 'asdfasdf', - pubKey: 'afsdf' + pubKey: 'afsdf', } expect(isMessage(msg)).toBeTruthy() }) @@ -89,12 +90,12 @@ describe('Validators - Messages', () => { cid: '123', message: { id: 'fzxjdiasf8ashfisfd', - channelId: '123n23l234lk234' + channelId: '123n23l234lk234', }, path: '/path/to/file', name: 'file', - ext: '.png' - } + ext: '.png', + }, } expect(isMessage(msg)).toBeTruthy() }) @@ -105,7 +106,7 @@ describe('Validators - Messages', () => { createdAt: 1234567, channelId: '123n23l234lk234', signature: 'asdfasdf', - pubKey: 'afsdf' + pubKey: 'afsdf', } expect(isMessage(msg as ChannelMessage)).toBeFalsy() }) @@ -117,9 +118,9 @@ describe('Validators - Messages', () => { createdAt: 1234567, channelId: '123n23l234lk234', signature: 'asdfasdf', - pubKey: 'afsdf' + pubKey: 'afsdf', } - expect(isMessage((msg as unknown) as ChannelMessage)).toBeFalsy() + expect(isMessage(msg as unknown as ChannelMessage)).toBeFalsy() }) test('message with invalid media property is invalid', () => { const msg = { @@ -133,14 +134,14 @@ describe('Validators - Messages', () => { media: { message: { id: 'fzxjdiasf8ashfisfd', - channelId: '123n23l234lk234' + channelId: '123n23l234lk234', }, path: '/path/to/file', name: 'file', - ext: '.png' - } + ext: '.png', + }, } - expect(isMessage((msg as unknown) as ChannelMessage)).toBeFalsy() + expect(isMessage(msg as unknown as ChannelMessage)).toBeFalsy() }) }) @@ -151,7 +152,7 @@ describe('Validators - Channels', () => { description: 'quiet', owner: 'szakalakakaaakaka', timestamp: 12341234, - id: 'sadfdasfsadfsdfsnfsdjfdsfsdfjsdf' + id: 'sadfdasfsadfsdfsnfsdjfdsfsdfjsdf', } expect(isChannel(channel)).toBeTruthy() }) @@ -160,7 +161,7 @@ describe('Validators - Channels', () => { name: 'quiet', description: 'quiet', owner: 'szakalakakaaakaka', - id: 'sadfdasfsadfsdfsnfsdjfdsfsdfjsdf' + id: 'sadfdasfsadfsdfsnfsdjfdsfsdfjsdf', } expect(isChannel(channel as unknown as PublicChannel)).toBeFalsy() }) @@ -170,9 +171,9 @@ describe('Validators - Channels', () => { description: 'quiet', owner: 'szakalakakaaakaka', timestamp: 'asfasdf', - id: 'sadfdasfsadfsdfsnfsdjfdsfsdfjsdf' + id: 'sadfdasfsadfsdfsnfsdjfdsfsdfjsdf', } - expect(isChannel((channel as unknown) as PublicChannel)).toBeFalsy() + 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 9324bc3dc4..d203c7a452 100644 --- a/packages/backend/src/validation/validators.ts +++ b/packages/backend/src/validation/validators.ts @@ -15,8 +15,8 @@ const messageMediaSchema = joi.object({ message: joi.object({ id: joi.string().required(), channelId: joi.string(), - channelAddress: joi.string() - }) + channelAddress: joi.string(), + }), }) const messageSchema = joi.object({ @@ -28,7 +28,7 @@ const messageSchema = joi.object({ channelId: joi.string(), channelAddress: joi.string(), signature: joi.string().required(), - pubKey: joi.string().required() + pubKey: joi.string().required(), }) const channelSchema = joi.object({ @@ -37,24 +37,15 @@ const channelSchema = joi.object({ owner: joi.string().required(), timestamp: joi.number().required(), id: joi.string(), - address: joi.string() + address: joi.string(), }) export const isUser = (publicKey: string, halfKey: string): boolean => { - return ( - publicKey.length === 66 && - halfKey.length === 64 && - _.isHexadecimal(publicKey) && - _.isHexadecimal(halfKey) - ) + return publicKey.length === 66 && halfKey.length === 64 && _.isHexadecimal(publicKey) && _.isHexadecimal(halfKey) } export const isConversation = (publicKey: string, encryptedPhrase: string): boolean => { - return ( - publicKey.length === 64 && - _.isHexadecimal(publicKey) && - _.isBase64(encryptedPhrase) - ) + return publicKey.length === 64 && _.isHexadecimal(publicKey) && _.isBase64(encryptedPhrase) } export const isDirectMessage = (msg: string): boolean => { @@ -77,5 +68,5 @@ export default { isMessage, isDirectMessage, isChannel, - isConversation + isConversation, } diff --git a/packages/common/src/channelAddress.ts b/packages/common/src/channelAddress.ts index 4e923e61fe..1a61aad440 100644 --- a/packages/common/src/channelAddress.ts +++ b/packages/common/src/channelAddress.ts @@ -1,7 +1,6 @@ import crypto from 'crypto' -export const generateChannelId = (channelName: string) => - `${channelName}_${crypto.randomBytes(16).toString('hex')}` +export const generateChannelId = (channelName: string) => `${channelName}_${crypto.randomBytes(16).toString('hex')}` export const getChannelNameFormChannelId = (channelId: string) => { const index = channelId.indexOf('_') diff --git a/packages/common/src/invitationCode.test.ts b/packages/common/src/invitationCode.test.ts index b3a10608e6..78bd1420c1 100644 --- a/packages/common/src/invitationCode.test.ts +++ b/packages/common/src/invitationCode.test.ts @@ -9,8 +9,7 @@ describe('Invitation code helper', () => { 'zbay://invalid', 'quiet://invalid', 'quiet://?param=invalid', - invitationDeepUrl('validCode') - + invitationDeepUrl('validCode'), ]) expect(result).toBe('validCode') }) diff --git a/packages/common/src/invitationCode.ts b/packages/common/src/invitationCode.ts index ed158d1bb2..6d17e38251 100644 --- a/packages/common/src/invitationCode.ts +++ b/packages/common/src/invitationCode.ts @@ -4,7 +4,7 @@ export const retrieveInvitationCode = (url: string): string => { /** * Extract invitation code from deep url. * Valid format: quiet://?code= - */ + */ let data: URL try { data = new URL(url) @@ -24,7 +24,7 @@ export const argvInvitationCode = (argv: string[]): string => { /** * Extract invitation code from deep url if url is present in argv */ - let invitationCode: string = '' + let invitationCode = '' for (const arg of argv) { invitationCode = retrieveInvitationCode(arg) if (invitationCode) { @@ -34,13 +34,13 @@ export const argvInvitationCode = (argv: string[]): string => { return invitationCode } -export const invitationDeepUrl = (code: string = ''): string => { +export const invitationDeepUrl = (code = ''): string => { const url = new URL('quiet://') url.searchParams.append(InvitationParams.CODE, code) return url.href } -export const invitationShareUrl = (code: string = ''): string => { +export const invitationShareUrl = (code = ''): string => { const url = new URL(`https://${Site.DOMAIN}/${Site.JOIN_PAGE}#${code}`) return url.href } diff --git a/packages/common/src/naming.test.ts b/packages/common/src/naming.test.ts index f902ee0fa9..cdd60fc85d 100644 --- a/packages/common/src/naming.test.ts +++ b/packages/common/src/naming.test.ts @@ -8,7 +8,7 @@ describe('Naming policy utility functions', () => { ['end-with-hyphen-', 'end-with-hyphen'], ['end-with-space ', 'end-with-space'], ['UpperCaseToLowerCase', 'uppercasetolowercase'], - ['spaces to hyphens', 'spaces-to-hyphens'] + ['spaces to hyphens', 'spaces-to-hyphens'], ])('name "%s" gets corrected to "%s"', async (name: string, corrected: string) => { expect(parseName(name)).toEqual(corrected) }) diff --git a/packages/common/src/process.ts b/packages/common/src/process.ts index f3cd9ca3f5..a5d85b6d8a 100644 --- a/packages/common/src/process.ts +++ b/packages/common/src/process.ts @@ -2,7 +2,7 @@ import { type SupportedPlatform } from '@quiet/types' export const hangingBackendProcessCommand = ({ backendBundlePath, - dataDir + dataDir, }: { backendBundlePath: string dataDir: string @@ -14,7 +14,13 @@ export const hangingBackendProcessCommand = ({ android: `pgrep -af "${backendBundlePath}" | grep -v pgrep | grep "${dataDir}" | awk '{print $1}'`, linux: `pgrep -af "${backendBundlePath}" | grep -v egrep | grep "${dataDir}" | awk '{print $1}'`, darwin: `ps -A | grep "${backendBundlePath}" | grep -v egrep | grep "${dataDir}" | awk '{print $1}'`, - win32: `powershell "Get-WmiObject Win32_process -Filter {commandline LIKE '%${backendBundlePath.replace(/\\/g, '\\\\')}%' and commandline LIKE '%${dataDir.replace(/\\/g, '\\\\')}%' and name = 'Quiet.exe'} | Format-Table ProcessId -HideTableHeaders"` + win32: `powershell "Get-WmiObject Win32_process -Filter {commandline LIKE '%${backendBundlePath.replace( + /\\/g, + '\\\\' + )}%' and commandline LIKE '%${dataDir.replace( + /\\/g, + '\\\\' + )}%' and name = 'Quiet.exe'} | Format-Table ProcessId -HideTableHeaders"`, } return byPlatform[process.platform as SupportedPlatform] } diff --git a/packages/common/src/sortPeers.ts b/packages/common/src/sortPeers.ts index 67c971b21f..33d98c06c7 100644 --- a/packages/common/src/sortPeers.ts +++ b/packages/common/src/sortPeers.ts @@ -31,8 +31,8 @@ export const sortPeers = (peersAddresses: string[], stats: NetworkStats[]): stri } } - const peerList = mostWantedPeers.map((peerId) => { - return peersAddresses.find((peerAddress) => { + const peerList = mostWantedPeers.map(peerId => { + return peersAddresses.find(peerAddress => { const id = peerAddress.split('/')[7] if (id === peerId.peerId) { peersAddresses.splice(peersAddresses.indexOf(peerAddress), 1) @@ -41,5 +41,8 @@ export const sortPeers = (peersAddresses: string[], stats: NetworkStats[]): stri }) }) - return peerList.concat(peersAddresses).filter(address => address !== null).filter(isDefined) + return peerList + .concat(peersAddresses) + .filter(address => address !== null) + .filter(isDefined) } diff --git a/packages/common/src/static.ts b/packages/common/src/static.ts index 8847a3e5fb..057018d0a7 100644 --- a/packages/common/src/static.ts +++ b/packages/common/src/static.ts @@ -1,11 +1,11 @@ export const ONION_ADDRESS_REGEX = /^[a-z0-9]{56}$/g export enum InvitationParams { - CODE = 'code' + CODE = 'code', } export enum Site { DOMAIN = 'tryquiet.org', MAIN_PAGE = 'https://tryquiet.org/', - JOIN_PAGE = 'join' + JOIN_PAGE = 'join', } diff --git a/packages/desktop/src/const/MenuNames.enum.ts b/packages/desktop/src/const/MenuNames.enum.ts index 6fd31205ba..20585f12d1 100644 --- a/packages/desktop/src/const/MenuNames.enum.ts +++ b/packages/desktop/src/const/MenuNames.enum.ts @@ -1,3 +1,3 @@ export enum MenuName { - Channel = 'ChannelContextMenu' + Channel = 'ChannelContextMenu', } diff --git a/packages/desktop/src/hooks/useContextMenu.ts b/packages/desktop/src/hooks/useContextMenu.ts index b196e7e439..e4883551b3 100644 --- a/packages/desktop/src/hooks/useContextMenu.ts +++ b/packages/desktop/src/hooks/useContextMenu.ts @@ -16,7 +16,7 @@ export const useContextMenu = (menu: MenuName dispatch( navigationActions.openMenu({ menu: menu, - args: args + args: args, }) ) @@ -26,6 +26,6 @@ export const useContextMenu = (menu: MenuName visible, handleOpen, handleClose, - ...props + ...props, } } diff --git a/packages/desktop/src/main/backendHelpers.ts b/packages/desktop/src/main/backendHelpers.ts index 75952ae817..912c5d6143 100644 --- a/packages/desktop/src/main/backendHelpers.ts +++ b/packages/desktop/src/main/backendHelpers.ts @@ -19,7 +19,7 @@ export const getPorts = async (): Promise<{ libp2pHiddenService, controlPort, httpTunnelPort, - dataServer + dataServer, } } diff --git a/packages/desktop/src/main/config.ts b/packages/desktop/src/main/config.ts index 897ff00933..606e3c3e85 100644 --- a/packages/desktop/src/main/config.ts +++ b/packages/desktop/src/main/config.ts @@ -1,8 +1,8 @@ const UPDATE_STATUSES = { PROCESSING_UPDATE: 'PROCESSING_UPDATE', - NO_UPDATE: 'NO_UPDATE' + NO_UPDATE: 'NO_UPDATE', } export default { - UPDATE_STATUSES + UPDATE_STATUSES, } diff --git a/packages/desktop/src/main/invitation.ts b/packages/desktop/src/main/invitation.ts index 168b623456..23f61c847e 100644 --- a/packages/desktop/src/main/invitation.ts +++ b/packages/desktop/src/main/invitation.ts @@ -7,7 +7,7 @@ import { BrowserWindow } from 'electron' export const processInvitationCode = (mainWindow: BrowserWindow, code: string) => { if (!code) return mainWindow.webContents.send('invitation', { - code + code, }) } @@ -34,7 +34,7 @@ export const updateDesktopFile = (isDev: boolean) => { try { execSync('xdg-settings set default-url-scheme-handler quiet quiet.desktop') } catch (e) { - console.error('Couldn\'t set default scheme handler', e.message) + console.error("Couldn't set default scheme handler", e.message) } } @@ -45,7 +45,7 @@ export const updateExecPath = (desktopFilePath: string) => { if (!desktopFile.includes(execInfo)) { // Replace old Exec with new Exec const lines = desktopFile.split('\n') - const newLines = lines.filter((line) => !line.includes('Exec=') && line !== '') + const newLines = lines.filter(line => !line.includes('Exec=') && line !== '') newLines.push(execInfo) fs.writeFileSync(desktopFilePath, newLines.join('\n')) } diff --git a/packages/desktop/src/main/loadMainEnvs.ts b/packages/desktop/src/main/loadMainEnvs.ts index a9eeb031f8..76f4835171 100644 --- a/packages/desktop/src/main/loadMainEnvs.ts +++ b/packages/desktop/src/main/loadMainEnvs.ts @@ -3,10 +3,7 @@ import path from 'path' if (process.env.NODE_ENV !== 'development') { try { - const pathProd = path.join.apply(null, [ - process.resourcesPath, - 'mainEnvs.json' - ]) + const pathProd = path.join.apply(null, [process.resourcesPath, 'mainEnvs.json']) const envsFile = fs.readFileSync(pathProd, { encoding: 'utf8' }) const envs = JSON.parse(envsFile) console.log('Read extra envs:', envs) diff --git a/packages/desktop/src/main/main.test.ts b/packages/desktop/src/main/main.test.ts index e5fc592ac4..faa8e913fc 100644 --- a/packages/desktop/src/main/main.test.ts +++ b/packages/desktop/src/main/main.test.ts @@ -28,20 +28,20 @@ jest.spyOn(path, 'join').mockReturnValue('path') jest.mock('electron-store', () => { return { - initRenderer: jest.fn() + initRenderer: jest.fn(), } }) jest.mock('@electron/remote/main', () => { return { initialize: jest.fn(), - enable: jest.fn() + enable: jest.fn(), } }) jest.mock('electron-localshortcut', () => { return { - register: jest.fn() + register: jest.fn(), } }) @@ -51,9 +51,9 @@ jest.mock('child_process', () => { fork: jest.fn().mockImplementation(() => { return { on: jest.fn(), - send: jest.fn() + send: jest.fn(), } - }) + }), } }) @@ -71,7 +71,7 @@ jest.mock('electron', () => { quit: jest.fn(), exit: jest.fn(), on: jest.fn(), - setAsDefaultProtocolClient: jest.fn() + setAsDefaultProtocolClient: jest.fn(), }, BrowserWindow: jest.fn().mockImplementation(() => { return { @@ -95,16 +95,16 @@ jest.mock('electron', () => { webContents: { on: mockwebContentsOn, once: mockwebContentsOnce, - send: mockWindowWebContentsSend - } + send: mockWindowWebContentsSend, + }, } }), Menu: { - setApplicationMenu: jest.fn() + setApplicationMenu: jest.fn(), }, ipcMain: { - on: jest.fn() - } + on: jest.fn(), + }, } }) @@ -112,8 +112,8 @@ jest.mock('electron-updater', () => { return { autoUpdater: { checkForUpdates: jest.fn(), - on: jest.fn() - } + on: jest.fn(), + }, } }) @@ -150,7 +150,7 @@ describe('electron app ready event', () => { 'libp2pHiddenService', 'controlPort', 'httpTunnelPort', - 'dataServer' + 'dataServer', ]) ) }) @@ -184,7 +184,7 @@ describe('electron app ready event', () => { expect(mockWindowOnce).toHaveBeenCalledTimes(2) expect(mockWindowOnceCalls[0][0]).toBe('close') - const event = { preventDefault: () => { } } + const event = { preventDefault: () => {} } mockWindowOnceCalls[0][1](event) expect(mockWindowWebContentsSend).toHaveBeenCalledWith('force-save-state') @@ -242,7 +242,7 @@ describe('Invitation code', () => { await mockAppOnCalls[2][1]() const code = 'invitationCode' expect(mockAppOnCalls[1][0]).toBe('open-url') - const event = { preventDefault: () => { } } + const event = { preventDefault: () => {} } mockAppOnCalls[1][1](event, invitationDeepUrl(code)) expect(mockWindowWebContentsSend).toHaveBeenCalledWith('invitation', { code: code }) }) @@ -250,12 +250,9 @@ describe('Invitation code', () => { it('process invitation code on second-instance event', async () => { const code = 'invitationCodeArgv' await mockAppOnCalls[2][1]() - const commandLine = [ - '/tmp/.mount_Quiet-TVQc6s/quiet', - invitationDeepUrl(code) - ] + const commandLine = ['/tmp/.mount_Quiet-TVQc6s/quiet', invitationDeepUrl(code)] expect(mockAppOnCalls[0][0]).toBe('second-instance') - const event = { preventDefault: () => { } } + const event = { preventDefault: () => {} } mockAppOnCalls[0][1](event, commandLine) expect(mockWindowWebContentsSend).toHaveBeenCalledWith('invitation', { code: code }) }) diff --git a/packages/desktop/src/main/main.ts b/packages/desktop/src/main/main.ts index beca2db83d..975ea624d7 100644 --- a/packages/desktop/src/main/main.ts +++ b/packages/desktop/src/main/main.ts @@ -23,7 +23,7 @@ remote.initialize() const log = logger('main') -let resetting: boolean = false +let resetting = false const updaterInterval = 15 * 60_000 @@ -90,7 +90,7 @@ console.log('electron main') const windowSize: IWindowSize = { width: 800, - height: 540 + height: 540, } setEngine( @@ -99,7 +99,7 @@ setEngine( new CryptoEngine({ name: '', crypto: webcrypto, - subtle: webcrypto.subtle + subtle: webcrypto.subtle, }) ) @@ -122,12 +122,12 @@ export const applyDevTools = async () => { const extensionsData = [ { name: REACT_DEVELOPER_TOOLS, - path: `${extensionsFolderPath}/${REACT_DEVELOPER_TOOLS.id}` + path: `${extensionsFolderPath}/${REACT_DEVELOPER_TOOLS.id}`, }, { name: REDUX_DEVTOOLS, - path: `${extensionsFolderPath}/${REDUX_DEVTOOLS.id}` - } + path: `${extensionsFolderPath}/${REDUX_DEVTOOLS.id}`, + }, ] await Promise.all( extensionsData.map(async extension => { @@ -141,7 +141,8 @@ export const applyDevTools = async () => { ) } -app.on('open-url', (event, url) => { // MacOS only +app.on('open-url', (event, url) => { + // MacOS only console.log('app.open-url', url) invitationUrl = url // If user opens invitation link with closed app open-url fires too early - before mainWindow is initialized event.preventDefault() @@ -165,9 +166,9 @@ export const createWindow = async () => { titleBarStyle, webPreferences: { nodeIntegration: true, - contextIsolation: false + contextIsolation: false, }, - autoHideMenuBar: true + autoHideMenuBar: true, }) remote.enable(mainWindow.webContents) @@ -179,10 +180,10 @@ export const createWindow = async () => { titleBarStyle, webPreferences: { nodeIntegration: true, - contextIsolation: false + contextIsolation: false, }, autoHideMenuBar: true, - alwaysOnTop: true + alwaysOnTop: true, }) remote.enable(splash.webContents) @@ -360,7 +361,7 @@ app.on('ready', async () => { '-r', `${process.resourcesPath}`, '-p', - 'desktop' + 'desktop', ] const backendBundlePath = path.normalize(require.resolve('backend-bundle')) @@ -413,7 +414,7 @@ app.on('ready', async () => { ipcMain.on('clear-community', () => { resetting = true - backendProcess?.on('message', (msg) => { + backendProcess?.on('message', msg => { if (msg === 'leftCommunity') { resetting = false } @@ -435,7 +436,7 @@ app.on('ready', async () => { event.reply('writeTempFileReply', { path: filePath, id: arg.fileName.split(arg.ext)[0], - ext: arg.ext + ext: arg.ext, }) }) @@ -449,7 +450,7 @@ app.on('ready', async () => { filesDialogResult = await dialog.showOpenDialog(mainWindow, { title: 'Upload files to Quiet', properties: ['openFile', 'openFile', 'multiSelections'], - filters: [] + filters: [], }) } catch (e) { mainWindow?.webContents.send('openedFilesError', e) diff --git a/packages/desktop/src/renderer/Root.tsx b/packages/desktop/src/renderer/Root.tsx index a4716dd87b..913bf1db54 100644 --- a/packages/desktop/src/renderer/Root.tsx +++ b/packages/desktop/src/renderer/Root.tsx @@ -45,7 +45,7 @@ export default () => { - + diff --git a/packages/desktop/src/renderer/components/Channel/Channel.stories.tsx b/packages/desktop/src/renderer/components/Channel/Channel.stories.tsx index c0c27944c3..a7aa3c303a 100644 --- a/packages/desktop/src/renderer/components/Channel/Channel.stories.tsx +++ b/packages/desktop/src/renderer/components/Channel/Channel.stories.tsx @@ -18,16 +18,16 @@ const args: Partial = { nickname: 'vader', hiddenService: { onionAddress: 'onionAddress', - privateKey: 'privateKey' + privateKey: 'privateKey', }, peerId: { id: 'id', privKey: 'privKey', - pubKey: 'pubKey' + pubKey: 'pubKey', }, dmKeys: { publicKey: 'publicKey', - privateKey: 'privateKey' + privateKey: 'privateKey', }, userCsr: { userCsr: 'userCsr', @@ -35,17 +35,17 @@ const args: Partial = { pkcs10: { publicKey: 'publicKey', privateKey: 'privateKey', - pkcs10: 'pkcs10' - } + pkcs10: 'pkcs10', + }, }, userCertificate: 'userCertificate', - joinTimestamp: null + joinTimestamp: null, }, uploadedFileModal: { open: false, handleOpen: function (_args?: any): any {}, handleClose: function (): any {}, - src: 'images/butterfly.jpeg' + src: 'images/butterfly.jpeg', }, messages: mock_messages(), newestMessage: { @@ -55,7 +55,7 @@ const args: Partial = { createdAt: 0, channelId: 'general', signature: 'signature', - pubKey: 'pubKey' + pubKey: 'pubKey', }, pendingMessages: {}, channelId: 'general', @@ -63,7 +63,7 @@ const args: Partial = { lazyLoading: function (_load: boolean): void {}, onInputChange: function (_value: string): void {}, onInputEnter: function (_message: string): void {}, - filesData: {} + filesData: {}, } const Template: ComponentStory = args => { @@ -86,9 +86,9 @@ Pending.args = { pendingMessages: { 33: { id: '33', - status: 0 - } - } + status: 0, + }, + }, } // Images @@ -102,9 +102,9 @@ ImagePreview.args = { file_id: { path: 'images/test-image.png', name: 'test-image', - ext: '.png' - } - } + ext: '.png', + }, + }, } ImagePlaceholder.args = { ...args, @@ -115,27 +115,27 @@ ImagePlaceholder.args = { cid: 'QmWUCSApiy76nW9DAk5M9QbH1nkW5XCYwxUHRSULjATyqs', message: { channelId: 'general', - id: 'wgtlstx3u7' + id: 'wgtlstx3u7', }, ext: '.png', name: 'test-image', width: 1200, height: 580, - path: null + path: null, }, message: '', createdAt: 0, date: '12:46', - nickname: 'vader' + nickname: 'vader', }), downloadStatuses: { 32: { mid: '', cid: 'QmWUCSApiy76nW9DAk5M9QbH1nkW5XCYwxUHRSULjATyqs', downloadState: DownloadState.None, - downloadProgress: undefined - } - } + downloadProgress: undefined, + }, + }, } SentImage.args = { ...args, @@ -146,27 +146,27 @@ SentImage.args = { cid: 'QmWUCSApiy76nW9DAk5M9QbH1nkW5XCYwxUHRSULjATyqs', message: { channelId: 'general', - id: 'wgtlstx3u7' + id: 'wgtlstx3u7', }, ext: '.png', name: 'test-image', width: 1200, height: 580, - path: 'images/test-image.png' + path: 'images/test-image.png', }, message: '', createdAt: 0, date: '12:46', - nickname: 'vader' + nickname: 'vader', }), downloadStatuses: { 32: { mid: '', cid: 'QmWUCSApiy76nW9DAk5M9QbH1nkW5XCYwxUHRSULjATyqs', downloadState: DownloadState.Completed, - downloadProgress: undefined - } - } + downloadProgress: undefined, + }, + }, } // Files @@ -187,9 +187,9 @@ FilePreview.args = { file_id: { path: 'files/my-file-name-goes-here-an-isnt-truncated.zip', name: 'my-file-name-goes-here-an-isnt-truncated', - ext: '.zip' - } - } + ext: '.zip', + }, + }, } MultipleMediaPreview.args = { ...args, @@ -197,14 +197,14 @@ MultipleMediaPreview.args = { file_id: { path: 'files/my-file-name-goes-here-an-isnt-truncated.zip', name: 'my-file-name-goes-here-an-isnt-truncated', - ext: '.zip' + ext: '.zip', }, image_id: { path: 'images/test-image.png', name: 'test-image', - ext: '.png' - } - } + ext: '.png', + }, + }, } UploadingFile.args = { ...args, @@ -215,27 +215,27 @@ UploadingFile.args = { cid: 'uploading_32', message: { channelId: 'general', - id: 'wgtlstx3u7' + id: 'wgtlstx3u7', }, ext: '.zip', name: 'my-file-name-goes-here-an-isnt-truncated', width: undefined, height: undefined, - path: null + path: null, }, message: '', createdAt: 0, date: '12:46', - nickname: 'vader' + nickname: 'vader', }), downloadStatuses: { 32: { cid: 'uploading_32', mid: 'mid', downloadState: DownloadState.Uploading, - downloadProgress: undefined - } - } + downloadProgress: undefined, + }, + }, } HostedFile.args = { ...args, @@ -246,28 +246,28 @@ HostedFile.args = { cid: 'QmWUCSApiy76nW9DAk5M9QbH1nkW5XCYwxUHRSULjATyqs', message: { channelId: 'general', - id: 'wgtlstx3u7' + id: 'wgtlstx3u7', }, ext: '.zip', name: 'my-file-name-goes-here-an-isnt-truncated', size: 2048, width: undefined, height: undefined, - path: 'files/my-file-name-goes-here-an-isnt-truncated.zip' + path: 'files/my-file-name-goes-here-an-isnt-truncated.zip', }, message: '', createdAt: 0, date: '12:46', - nickname: 'vader' + nickname: 'vader', }), downloadStatuses: { 32: { cid: 'QmWUCSApiy76nW9DAk5M9QbH1nkW5XCYwxUHRSULjATyqs', mid: 'mid', downloadState: DownloadState.Hosted, - downloadProgress: undefined - } - } + downloadProgress: undefined, + }, + }, } ReadyDownload.args = { ...args, @@ -278,28 +278,28 @@ ReadyDownload.args = { cid: 'QmWUCSApiy76nW9DAk5M9QbH1nkW5XCYwxUHRSULjATyqs', message: { channelId: 'general', - id: 'wgtlstx3u7' + id: 'wgtlstx3u7', }, ext: '.zip', name: 'my-file-name-goes-here-an-isnt-truncated', size: 2048, width: undefined, height: undefined, - path: 'files/my-file-name-goes-here-an-isnt-truncated.zip' + path: 'files/my-file-name-goes-here-an-isnt-truncated.zip', }, message: '', createdAt: 0, date: '12:46', - nickname: 'vader' + nickname: 'vader', }), downloadStatuses: { 32: { cid: 'QmWUCSApiy76nW9DAk5M9QbH1nkW5XCYwxUHRSULjATyqs', mid: 'mid', downloadState: DownloadState.Ready, - downloadProgress: undefined - } - } + downloadProgress: undefined, + }, + }, } Downloading.args = { ...args, @@ -310,19 +310,19 @@ Downloading.args = { cid: 'QmWUCSApiy76nW9DAk5M9QbH1nkW5XCYwxUHRSULjATyqs', message: { channelId: 'general', - id: 'wgtlstx3u7' + id: 'wgtlstx3u7', }, ext: '.zip', name: 'my-file-name-goes-here-an-isnt-truncated', size: 2048, width: undefined, height: undefined, - path: 'files/my-file-name-goes-here-an-isnt-truncated.zip' + path: 'files/my-file-name-goes-here-an-isnt-truncated.zip', }, message: '', createdAt: 0, date: '12:46', - nickname: 'vader' + nickname: 'vader', }), downloadStatuses: { 32: { @@ -332,10 +332,10 @@ Downloading.args = { downloadProgress: { size: 2048, downloaded: 256, - transferSpeed: 32 - } - } - } + transferSpeed: 32, + }, + }, + }, } CompletedDownload.args = { ...args, @@ -346,19 +346,19 @@ CompletedDownload.args = { cid: 'QmWUCSApiy76nW9DAk5M9QbH1nkW5XCYwxUHRSULjATyqs', message: { channelId: 'general', - id: 'wgtlstx3u7' + id: 'wgtlstx3u7', }, ext: '.zip', name: 'my-file-name-goes-here-an-isnt-truncated', size: 2048, width: undefined, height: undefined, - path: 'files/my-file-name-goes-here-an-isnt-truncated.zip' + path: 'files/my-file-name-goes-here-an-isnt-truncated.zip', }, message: '', createdAt: 0, date: '12:46', - nickname: 'vader' + nickname: 'vader', }), downloadStatuses: { 32: { @@ -368,10 +368,10 @@ CompletedDownload.args = { downloadProgress: { size: 2048, downloaded: 1024, - transferSpeed: 0 - } - } - } + transferSpeed: 0, + }, + }, + }, } CancelingDownload.args = { ...args, @@ -382,19 +382,19 @@ CancelingDownload.args = { cid: 'QmWUCSApiy76nW9DAk5M9QbH1nkW5XCYwxUHRSULjATyqs', message: { channelId: 'general', - id: 'wgtlstx3u7' + id: 'wgtlstx3u7', }, ext: '.zip', name: 'my-file-name-goes-here-an-isnt-truncated', size: 1024, width: undefined, height: undefined, - path: 'files/my-file-name-goes-here-an-isnt-truncated.zip' + path: 'files/my-file-name-goes-here-an-isnt-truncated.zip', }, message: '', createdAt: 0, date: '12:46', - nickname: 'vader' + nickname: 'vader', }), downloadStatuses: { 32: { @@ -404,10 +404,10 @@ CancelingDownload.args = { downloadProgress: { size: 2048, downloaded: 0, - transferSpeed: 0 - } - } - } + transferSpeed: 0, + }, + }, + }, } CanceledDownload.args = { ...args, @@ -418,28 +418,28 @@ CanceledDownload.args = { cid: 'QmWUCSApiy76nW9DAk5M9QbH1nkW5XCYwxUHRSULjATyqs', message: { channelId: 'general', - id: 'wgtlstx3u7' + id: 'wgtlstx3u7', }, ext: '.zip', name: 'my-file-name-goes-here-an-isnt-truncated', size: 1024, width: undefined, height: undefined, - path: 'files/my-file-name-goes-here-an-isnt-truncated.zip' + path: 'files/my-file-name-goes-here-an-isnt-truncated.zip', }, message: '', createdAt: 0, date: '12:46', - nickname: 'vader' + nickname: 'vader', }), downloadStatuses: { 32: { mid: 'mid', cid: 'QmWUCSApiy76nW9DAk5M9QbH1nkW5XCYwxUHRSULjATyqs', downloadState: DownloadState.Canceled, - downloadProgress: undefined - } - } + downloadProgress: undefined, + }, + }, } MaliciousDownload.args = { ...args, @@ -450,28 +450,28 @@ MaliciousDownload.args = { cid: 'QmWUCSApiy76nW9DAk5M9QbH1nkW5XCYwxUHRSULjATyqs', message: { channelId: 'general', - id: 'wgtlstx3u7' + id: 'wgtlstx3u7', }, ext: '.zip', name: 'my-file-name-goes-here-an-isnt-truncated', size: 1024, width: undefined, height: undefined, - path: 'files/my-file-name-goes-here-an-isnt-truncated.zip' + path: 'files/my-file-name-goes-here-an-isnt-truncated.zip', }, message: '', createdAt: 0, date: '12:46', - nickname: 'vader' + nickname: 'vader', }), downloadStatuses: { 32: { mid: 'mid', cid: 'QmWUCSApiy76nW9DAk5M9QbH1nkW5XCYwxUHRSULjATyqs', downloadState: DownloadState.Malicious, - downloadProgress: undefined - } - } + downloadProgress: undefined, + }, + }, } // Info @@ -486,8 +486,8 @@ NewUserMessage.args = { message: 'Hey, @the-emperor just joined!', createdAt: 0, date: '12:46', - nickname: 'vader' - }) + nickname: 'vader', + }), } // Link @@ -502,8 +502,8 @@ Link.args = { message: 'Hey, haye you seen this https://github.com/TryQuiet/monorepo awesome project?', createdAt: 0, date: '12:46', - nickname: 'vader' - }) + nickname: 'vader', + }), } // MathJax @@ -520,8 +520,8 @@ MathJaxMiddle.args = { message: String.raw`Check this out: $$\sum_{i=0}^n i = \frac{n(n+1)}{2}$$ This is the formula I told you about`, createdAt: 0, date: '12:46', - nickname: 'vader' - }) + nickname: 'vader', + }), } MathJaxPending.args = { ...args, @@ -532,14 +532,14 @@ MathJaxPending.args = { message: String.raw`Check this out: $$\sum_{i=0}^n i = \frac{n(n+1)}{2}$$ This is the formula I told you about`, createdAt: 0, date: '12:46', - nickname: 'vader' + nickname: 'vader', }), pendingMessages: { 32: { id: '32', - status: 0 - } - } + status: 0, + }, + }, } MathJaxBeginning.args = { ...args, @@ -550,14 +550,14 @@ MathJaxBeginning.args = { message: String.raw`$$a^2 +b^2=c^2$$`, createdAt: 0, date: '12:46', - nickname: 'vader' - }) + nickname: 'vader', + }), } const component: ComponentMeta = { title: 'Components/ChannelComponent', decorators: [withTheme], - component: ChannelComponent + component: ChannelComponent, } export default component diff --git a/packages/desktop/src/renderer/components/Channel/Channel.tsx b/packages/desktop/src/renderer/components/Channel/Channel.tsx index ca8b64be6a..5f2256a4eb 100644 --- a/packages/desktop/src/renderer/components/Channel/Channel.tsx +++ b/packages/desktop/src/renderer/components/Channel/Channel.tsx @@ -3,19 +3,8 @@ import React, { useCallback, useEffect } from 'react' import { shell, ipcRenderer } from 'electron' import { useDispatch, useSelector } from 'react-redux' -import { - identity, - messages, - publicChannels, - communities, - files, - network -} from '@quiet/state-manager' -import { - FileMetadata, - CancelDownload, - FileContent -} from '@quiet/types' +import { identity, messages, publicChannels, communities, files, network } from '@quiet/state-manager' +import { FileMetadata, CancelDownload, FileContent } from '@quiet/types' import ChannelComponent, { ChannelComponentProps } from './ChannelComponent' @@ -37,17 +26,11 @@ const Channel = () => { const currentChannelId = useSelector(publicChannels.selectors.currentChannelId) const currentChannelName = useSelector(publicChannels.selectors.currentChannelName) - const currentChannelMessagesCount = useSelector( - publicChannels.selectors.currentChannelMessagesCount - ) + const currentChannelMessagesCount = useSelector(publicChannels.selectors.currentChannelMessagesCount) - const currentChannelDisplayableMessages = useSelector( - publicChannels.selectors.currentChannelMessagesMergedBySender - ) + const currentChannelDisplayableMessages = useSelector(publicChannels.selectors.currentChannelMessagesMergedBySender) - const newestCurrentChannelMessage = useSelector( - publicChannels.selectors.newestCurrentChannelMessage - ) + const newestCurrentChannelMessage = useSelector(publicChannels.selectors.newestCurrentChannelMessage) const downloadStatusesMapping = useSelector(files.selectors.downloadStatuses) @@ -56,16 +39,14 @@ const Channel = () => { const initializedCommunities = useSelector(network.selectors.initializedCommunities) const isCommunityInitialized = Boolean(community && initializedCommunities[community.id]) - const pendingGeneralChannelRecreationSelector = useSelector( - publicChannels.selectors.pendingGeneralChannelRecreation - ) + const pendingGeneralChannelRecreationSelector = useSelector(publicChannels.selectors.pendingGeneralChannelRecreation) const pendingGeneralChannelRecreation = pendingGeneralChannelRecreationSelector && (currentChannelName === 'general' || currentChannelName === '') && currentChannelMessagesCount === 0 - let enableContextMenu: boolean = false + let enableContextMenu = false if (community) { // Enable only for community owner enableContextMenu = Boolean(community.CA) @@ -146,7 +127,7 @@ const Channel = () => { ipcRenderer.send('writeTempFile', { fileName: `${id}${ext}`, fileBuffer: new Uint8Array(imageBuffer), - ext: ext + ext: ext, }) } @@ -158,8 +139,8 @@ const Channel = () => { [arg.id]: { ext: arg.ext, name: arg.id, - path: arg.path - } + path: arg.path, + }, } return updatedFiles @@ -216,7 +197,7 @@ const Channel = () => { channelName: currentChannelName, messages: { count: currentChannelMessagesCount, - groups: currentChannelDisplayableMessages + groups: currentChannelDisplayableMessages, }, newestMessage: newestCurrentChannelMessage, pendingMessages: pendingMessages, @@ -232,18 +213,18 @@ const Channel = () => { uploadedFileModal: uploadedFileModal, openContextMenu: openContextMenu, enableContextMenu: enableContextMenu, - pendingGeneralChannelRecreation: pendingGeneralChannelRecreation + pendingGeneralChannelRecreation: pendingGeneralChannelRecreation, } const uploadFilesPreviewProps: UploadFilesPreviewsProps = { filesData: uploadingFiles, - removeFile: removeFilePreview + removeFile: removeFilePreview, } const fileActionsProps: FileActionsProps = { openContainingFolder: openContainingFolder, downloadFile: downloadFile, - cancelDownload: cancelDownload + cancelDownload: cancelDownload, } return ( diff --git a/packages/desktop/src/renderer/components/Channel/ChannelComponent.tsx b/packages/desktop/src/renderer/components/Channel/ChannelComponent.tsx index 132cdf4cd1..6cc17b0079 100644 --- a/packages/desktop/src/renderer/components/Channel/ChannelComponent.tsx +++ b/packages/desktop/src/renderer/components/Channel/ChannelComponent.tsx @@ -12,13 +12,7 @@ import ChannelInputComponent from '../widgets/channels/ChannelInput' import { INPUT_STATE } from '../widgets/channels/ChannelInput/InputState.enum' -import { - ChannelMessage, - DownloadStatus, - Identity, - MessagesDailyGroups, - MessageSendingStatus -} from '@quiet/types' +import { ChannelMessage, DownloadStatus, Identity, MessagesDailyGroups, MessageSendingStatus } from '@quiet/types' import { useResizeDetector } from 'react-resize-detector' import { Dictionary } from '@reduxjs/toolkit' @@ -35,7 +29,7 @@ import { UseModalType } from '../../containers/hooks' const ChannelMessagesWrapperStyled = styled(Grid)(({ theme }) => ({ position: 'relative', height: 0, - backgroundColor: theme.palette.colors.white + backgroundColor: theme.palette.colors.white, })) export interface ChannelComponentProps { @@ -68,12 +62,10 @@ export interface ChannelComponentProps { const enum ScrollPosition { TOP = 0, MIDDLE = -1, - BOTTOM = 1 + BOTTOM = 1, } -export const ChannelComponent: React.FC< - ChannelComponentProps & UploadFilesPreviewsProps & FileActionsProps -> = ({ +export const ChannelComponent: React.FC = ({ user, channelId, channelName, @@ -97,7 +89,7 @@ export const ChannelComponent: React.FC< cancelDownload, openContextMenu, enableContextMenu = false, - pendingGeneralChannelRecreation + pendingGeneralChannelRecreation, }) => { const [lastSeenMessage, setLastSeenMessage] = useState() const [newMessagesInfo, setNewMessagesInfo] = useState(false) @@ -132,7 +124,7 @@ export const ChannelComponent: React.FC< memoizedScrollHeight.current = 0 scrollbarRef.current.scrollTo({ behavior: 'auto', - top: Math.abs(scrollbarRef.current.clientHeight - scrollbarRef.current.scrollHeight) + top: Math.abs(scrollbarRef.current.clientHeight - scrollbarRef.current.scrollHeight), }) } @@ -258,12 +250,11 @@ export const ChannelComponent: React.FC< infoClass={infoClass} setInfoClass={setInfoClass} inputState={ - isCommunityInitialized && Boolean(messages.count) - ? INPUT_STATE.AVAILABLE - : INPUT_STATE.NOT_CONNECTED + isCommunityInitialized && Boolean(messages.count) ? INPUT_STATE.AVAILABLE : INPUT_STATE.NOT_CONNECTED } handleClipboardFiles={handleClipboardFiles} - handleOpenFiles={handleFileDrop}> + handleOpenFiles={handleFileDrop} + > removeFile(id)} /> diff --git a/packages/desktop/src/renderer/components/Channel/CreateChannel/CreateChannel.stories.tsx b/packages/desktop/src/renderer/components/Channel/CreateChannel/CreateChannel.stories.tsx index e196785bb0..a99460279d 100644 --- a/packages/desktop/src/renderer/components/Channel/CreateChannel/CreateChannel.stories.tsx +++ b/packages/desktop/src/renderer/components/Channel/CreateChannel/CreateChannel.stories.tsx @@ -17,7 +17,7 @@ const args: CreateChannelProps = { console.log('creating channel: ', name) }, handleClose: function (): void {}, - clearErrorsDispatch: function (): void {} + clearErrorsDispatch: function (): void {}, } Component.args = args @@ -25,7 +25,7 @@ Component.args = args const component: ComponentMeta = { title: 'Components/CreateChannel', decorators: [withTheme], - component: CreateChannelComponent + component: CreateChannelComponent, } export default component diff --git a/packages/desktop/src/renderer/components/Channel/CreateChannel/CreateChannel.test.tsx b/packages/desktop/src/renderer/components/Channel/CreateChannel/CreateChannel.test.tsx index 4582e543bd..328dd2dd63 100644 --- a/packages/desktop/src/renderer/components/Channel/CreateChannel/CreateChannel.test.tsx +++ b/packages/desktop/src/renderer/components/Channel/CreateChannel/CreateChannel.test.tsx @@ -35,10 +35,9 @@ describe('Add new channel', () => { const factory = await getFactory(store) - await factory.create['payload']>( - 'Identity', - { nickname: 'alice' } - ) + await factory.create['payload']>('Identity', { + nickname: 'alice', + }) renderComponent(, store) @@ -94,53 +93,47 @@ describe('Add new channel', () => { ['end-with-space ', 'end-with-space'], ['UpperCaseToLowerCase', 'uppercasetolowercase'], ['spaces to hyphens', 'spaces-to-hyphens'], - ['!@#start-with-exclaim-at-hash', 'start-with-exclaim-at-hash'] - ])( - 'user inserting wrong channel name "%s" gets corrected "%s"', - async (name: string, corrected: string) => { - renderComponent( - {}} - handleClose={() => {}} - clearErrorsDispatch={() => {}} - /> - ) - - const input = screen.getByPlaceholderText('Enter a channel name') - - await userEvent.type(input, name) - expect(screen.getByTestId('createChannelNameWarning')).toHaveTextContent( - `Your channel will be created as #${corrected}` - ) - } - ) + ['!@#start-with-exclaim-at-hash', 'start-with-exclaim-at-hash'], + ])('user inserting wrong channel name "%s" gets corrected "%s"', async (name: string, corrected: string) => { + renderComponent( + {}} + handleClose={() => {}} + clearErrorsDispatch={() => {}} + /> + ) + + const input = screen.getByPlaceholderText('Enter a channel name') + + await userEvent.type(input, name) + expect(screen.getByTestId('createChannelNameWarning')).toHaveTextContent( + `Your channel will be created as #${corrected}` + ) + }) it.each([ [' whitespaces', FieldErrors.Whitespaces], - ['----hyphens', FieldErrors.Whitespaces] - ])( - 'user inserting invalid channel name "%s" should see "%s" error', - async (name: string, error: string) => { - const createChannel = jest.fn() - - renderComponent( - {}} - clearErrorsDispatch={() => {}} - /> - ) - - const input = screen.getByPlaceholderText('Enter a channel name') - const button = screen.getByText('Create Channel') - - await userEvent.type(input, name) - await userEvent.click(button) - - const message = await screen.findByText(error) - expect(message).toBeVisible() - } - ) + ['----hyphens', FieldErrors.Whitespaces], + ])('user inserting invalid channel name "%s" should see "%s" error', async (name: string, error: string) => { + const createChannel = jest.fn() + + renderComponent( + {}} + clearErrorsDispatch={() => {}} + /> + ) + + const input = screen.getByPlaceholderText('Enter a channel name') + const button = screen.getByText('Create Channel') + + await userEvent.type(input, name) + await userEvent.click(button) + + const message = await screen.findByText(error) + expect(message).toBeVisible() + }) }) diff --git a/packages/desktop/src/renderer/components/Channel/CreateChannel/CreateChannel.tsx b/packages/desktop/src/renderer/components/Channel/CreateChannel/CreateChannel.tsx index 91ea173ace..4a462287df 100644 --- a/packages/desktop/src/renderer/components/Channel/CreateChannel/CreateChannel.tsx +++ b/packages/desktop/src/renderer/components/Channel/CreateChannel/CreateChannel.tsx @@ -1,18 +1,8 @@ import React, { useEffect, useState } from 'react' import { useDispatch, useSelector } from 'react-redux' import CreateChannelComponent from './CreateChannelComponent' -import { - communities, - errors, - identity, - publicChannels -} from '@quiet/state-manager' -import { - ErrorCodes, - ErrorMessages, - PublicChannel, - SocketActionTypes -} from '@quiet/types' +import { communities, errors, identity, publicChannels } from '@quiet/state-manager' +import { ErrorCodes, ErrorMessages, PublicChannel, SocketActionTypes } from '@quiet/types' import { DateTime } from 'luxon' import { useModal } from '../../../containers/hooks' import { ModalName } from '../../../sagas/modals/modals.types' @@ -35,13 +25,10 @@ export const CreateChannel = () => { useEffect(() => { if (!newChannel) return - if ( - createChannelModal.open && - channels.filter(channel => channel.name === newChannel?.name).length > 0 - ) { + if (createChannelModal.open && channels.filter(channel => channel.name === newChannel?.name).length > 0) { dispatch( publicChannels.actions.setCurrentChannel({ - channelId: newChannel.id + channelId: newChannel.id, }) ) setNewChannel(null) @@ -65,7 +52,7 @@ export const CreateChannel = () => { type: SocketActionTypes.CREATED_CHANNEL, code: ErrorCodes.NOT_FOUND, message: ErrorMessages.GENERAL, - community: community + community: community, }) ) return @@ -77,7 +64,7 @@ export const CreateChannel = () => { type: SocketActionTypes.CREATED_CHANNEL, code: ErrorCodes.FORBIDDEN, message: ErrorMessages.CHANNEL_NAME_TAKEN, - community: community + community: community, }) ) return @@ -89,7 +76,7 @@ export const CreateChannel = () => { description: `Welcome to #${name}`, owner: user.nickname, id: generateChannelId(name), - timestamp: DateTime.utc().valueOf() + timestamp: DateTime.utc().valueOf(), } flushSync(() => { // TODO: maybe add a better fix. React 18 does not perform rerenders inside callback functions @@ -97,7 +84,7 @@ export const CreateChannel = () => { }) dispatch( publicChannels.actions.createChannel({ - channel: channel + channel: channel, }) ) } diff --git a/packages/desktop/src/renderer/components/Channel/CreateChannel/CreateChannelComponent.tsx b/packages/desktop/src/renderer/components/Channel/CreateChannel/CreateChannelComponent.tsx index 7d9337843b..cd576d6583 100644 --- a/packages/desktop/src/renderer/components/Channel/CreateChannel/CreateChannelComponent.tsx +++ b/packages/desktop/src/renderer/components/Channel/CreateChannel/CreateChannelComponent.tsx @@ -26,7 +26,7 @@ const classes = { warrningMessage: `${PREFIX}warrningMessage`, rootBar: `${PREFIX}rootBar`, progressBar: `${PREFIX}progressBar`, - info: `${PREFIX}info` + info: `${PREFIX}info`, } const StyledModalContent = styled(Grid)(({ theme }) => ({ @@ -35,12 +35,12 @@ const StyledModalContent = styled(Grid)(({ theme }) => ({ [`& .${classes.fullContainer}`]: { width: '100%', - height: '100%' + height: '100%', }, [`& .${classes.gutter}`]: { marginTop: 8, - marginBottom: 24 + marginBottom: 24, }, [`& .${classes.button}`]: { @@ -48,49 +48,49 @@ const StyledModalContent = styled(Grid)(({ theme }) => ({ backgroundColor: theme.palette.colors.quietBlue, color: theme.palette.colors.white, '&:hover': { - backgroundColor: theme.palette.colors.quietBlue + backgroundColor: theme.palette.colors.quietBlue, }, textTransform: 'none', height: 48, - fontWeight: 'normal' + fontWeight: 'normal', }, [`& .${classes.title}`]: { - marginBottom: 24 + marginBottom: 24, }, [`& .${classes.iconDiv}`]: { width: 24, height: 28, - marginRight: 8 + marginRight: 8, }, [`& .${classes.warrningIcon}`]: { - color: '#FFCC00' + color: '#FFCC00', }, [`& .${classes.warrningMessage}`]: { - wordBreak: 'break-word' + wordBreak: 'break-word', }, [`& .${classes.rootBar}`]: { width: 350, marginTop: 32, - marginBottom: 16 + marginBottom: 16, }, [`& .${classes.progressBar}`]: { - backgroundColor: theme.palette.colors.linkBlue + backgroundColor: theme.palette.colors.linkBlue, }, [`& .${classes.info}`]: { lineHeight: '19px', - color: theme.palette.colors.darkGray - } + color: theme.palette.colors.darkGray, + }, })) const createChannelFields = { - channelName: channelNameField() + channelName: channelNameField(), } interface CreateChannelFormValues { @@ -110,7 +110,7 @@ export const CreateChannelComponent: React.FC = ({ channelCreationError, createChannel, handleClose, - clearErrorsDispatch + clearErrorsDispatch, }) => { const [channelName, setChannelName] = useState('') const [parsedNameDiffers, setParsedNameDiffers] = useState(false) @@ -121,9 +121,9 @@ export const CreateChannelComponent: React.FC = ({ setValue, setError, clearErrors, - control + control, } = useForm<{ channelName: string }>({ - mode: 'onSubmit' + mode: 'onSubmit', }) const onSubmit = (values: CreateChannelFormValues) => { @@ -160,11 +160,7 @@ export const CreateChannelComponent: React.FC = ({
- + Create a new public channel @@ -208,7 +204,8 @@ export const CreateChannelComponent: React.FC = ({ + data-testid={'createChannelNameWarning'} + > Your channel will be created as {`#${channelName}`} diff --git a/packages/desktop/src/renderer/components/Channel/DeleteChannel/DeleteChannel.stories.tsx b/packages/desktop/src/renderer/components/Channel/DeleteChannel/DeleteChannel.stories.tsx index 0b76358b61..bd498ae0d9 100644 --- a/packages/desktop/src/renderer/components/Channel/DeleteChannel/DeleteChannel.stories.tsx +++ b/packages/desktop/src/renderer/components/Channel/DeleteChannel/DeleteChannel.stories.tsx @@ -21,7 +21,7 @@ const args: ReturnType & DeleteChannelProps = { // @ts-expect-error handleOpen: () => {}, // @ts-expect-error - handleClose: () => {} + handleClose: () => {}, } Component.args = args @@ -29,7 +29,7 @@ Component.args = args const component: ComponentMeta = { title: 'Components/DeleteChannel', decorators: [withTheme], - component: DeleteChannelComponent + component: DeleteChannelComponent, } export default component diff --git a/packages/desktop/src/renderer/components/Channel/DeleteChannel/DeleteChannelComponent.tsx b/packages/desktop/src/renderer/components/Channel/DeleteChannel/DeleteChannelComponent.tsx index 9c024bc139..649107da3f 100644 --- a/packages/desktop/src/renderer/components/Channel/DeleteChannel/DeleteChannelComponent.tsx +++ b/packages/desktop/src/renderer/components/Channel/DeleteChannel/DeleteChannelComponent.tsx @@ -19,7 +19,7 @@ const classes = { buttonContainer: `${PREFIX}buttonContainer`, button: `${PREFIX}button`, secondaryButtonContainer: `${PREFIX}secondaryButtonContainer`, - secondaryButton: `${PREFIX}secondaryButton` + secondaryButton: `${PREFIX}secondaryButton`, } const StyledGrid = styled(Grid)(({ theme }) => ({ @@ -28,22 +28,22 @@ const StyledGrid = styled(Grid)(({ theme }) => ({ [`& .${classes.root}`]: {}, [`& .${classes.titleContainer}`]: { - marginTop: 16 + marginTop: 16, }, [`& .${classes.descContainer}`]: { marginTop: 8, marginLeft: 32, marginRight: 32, - width: 100 + width: 100, }, [`& .${classes.iconContainer}`]: { - marginTop: 0 + marginTop: 0, }, [`& .${classes.buttonContainer}`]: { - marginTop: 25 + marginTop: 25, }, [`& .${classes.button}`]: { @@ -53,16 +53,16 @@ const StyledGrid = styled(Grid)(({ theme }) => ({ backgroundColor: theme.palette.colors.purple, padding: theme.spacing(2), '&:hover': { - backgroundColor: theme.palette.colors.darkPurple + backgroundColor: theme.palette.colors.darkPurple, }, '&:disabled': { - backgroundColor: theme.palette.colors.gray - } + backgroundColor: theme.palette.colors.gray, + }, }, [`& .${classes.secondaryButtonContainer}`]: { marginTop: 16, - marginBottom: 32 + marginBottom: 32, }, [`& .${classes.secondaryButton}`]: { @@ -74,9 +74,9 @@ const StyledGrid = styled(Grid)(({ theme }) => ({ '&:hover': { boxShadow: 'none', cursor: 'pointer', - backgroundColor: theme.palette.colors.white - } - } + backgroundColor: theme.palette.colors.white, + }, + }, })) export interface DeleteChannelProps { @@ -88,30 +88,17 @@ export const DeleteChannelComponent: React.FC & Dele open, handleClose, channelName, - deleteChannel + deleteChannel, }) => { return ( - + Are you sure? - + - Delete{' '} - #{channelName}? This cannot be undone. + Delete #{channelName}? This cannot be undone. @@ -121,7 +108,8 @@ export const DeleteChannelComponent: React.FC & Dele size='small' fullWidth className={classes.button} - data-testid="deleteChannelButton"> + data-testid='deleteChannelButton' + > Delete channel @@ -131,13 +119,9 @@ export const DeleteChannelComponent: React.FC & Dele className={classes.secondaryButtonContainer} xs={12} direction='row' - justifyContent='center'> - diff --git a/packages/desktop/src/renderer/components/Channel/DropZone/DropZone.test.tsx b/packages/desktop/src/renderer/components/Channel/DropZone/DropZone.test.tsx index 0c095847c1..b0de37855f 100644 --- a/packages/desktop/src/renderer/components/Channel/DropZone/DropZone.test.tsx +++ b/packages/desktop/src/renderer/components/Channel/DropZone/DropZone.test.tsx @@ -4,9 +4,7 @@ import { DropZoneComponent } from './DropZoneComponent' describe('Drop zone component', () => { it('renders component', () => { - const result = renderComponent( - - ) + const result = renderComponent() expect(result.baseElement).toMatchInlineSnapshot(`
diff --git a/packages/desktop/src/renderer/components/Channel/DropZone/DropZoneComponent.tsx b/packages/desktop/src/renderer/components/Channel/DropZone/DropZoneComponent.tsx index e100af3d48..0773af35d9 100644 --- a/packages/desktop/src/renderer/components/Channel/DropZone/DropZoneComponent.tsx +++ b/packages/desktop/src/renderer/components/Channel/DropZone/DropZoneComponent.tsx @@ -9,7 +9,7 @@ import { DropTargetMonitor, useDrop } from 'react-dnd' import { NativeTypes } from 'react-dnd-html5-backend' const StyledDropZoneComponent = styled(Grid)(() => ({ - position: 'relative' + position: 'relative', })) const StyledActiveDropZoneComponent = styled('div')(({ theme }) => ({ @@ -23,7 +23,7 @@ const StyledActiveDropZoneComponent = styled('div')(({ theme }) => ({ display: 'flex', flexDirection: 'column', alignItems: 'center', - justifyContent: 'center' + justifyContent: 'center', })) interface DropZoneComponentProps { @@ -34,9 +34,7 @@ interface DropZoneComponentProps { export const ActiveDropZoneComponent: React.FC<{ channelName: string -}> = ({ - channelName -}) => { +}> = ({ channelName }) => { return ( @@ -45,11 +43,7 @@ export const ActiveDropZoneComponent: React.FC<{ ) } -export const DropZoneComponent: React.FC = ({ - children, - channelName, - handleFileDrop -}) => { +export const DropZoneComponent: React.FC = ({ children, channelName, handleFileDrop }) => { const [{ canDrop, isOver }, drop] = useDrop( () => ({ accept: [NativeTypes.FILE], @@ -78,16 +72,16 @@ export const DropZoneComponent: React.FC = ({ return { isOver: monitor.isOver(), - canDrop: monitor.canDrop() + canDrop: monitor.canDrop(), } - } + }, }), [handleFileDrop] ) const dropIsActive = canDrop && isOver return ( - {dropIsActive && } + {dropIsActive && } {children} ) 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 6008037fa2..3ae10b3fe5 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 @@ -34,26 +34,26 @@ const args: FileComponentProps = { cid: cid, message: { channelId: 'general', - id: 'wgtlstx3u7' + id: 'wgtlstx3u7', }, ext: '.zip', name: 'my-file-name-goes-here-an-isnt-truncated', size: 1024, width: undefined, height: undefined, - path: 'files/my-file-name-goes-here-an-isnt-truncated.zip' + path: 'files/my-file-name-goes-here-an-isnt-truncated.zip', }, message: '', createdAt: 0, date: '12:46', - nickname: 'vader' + nickname: 'vader', }, downloadStatus: { mid: mid, cid: cid, downloadState: DownloadState.Ready, - downloadProgress: undefined - } + downloadProgress: undefined, + }, } Uploading.args = { @@ -63,15 +63,15 @@ Uploading.args = { // @ts-expect-error media: { ...args.message.media, - size: undefined - } + size: undefined, + }, }, downloadStatus: { mid: mid, cid: cid, downloadState: DownloadState.Uploading, - downloadProgress: undefined - } + downloadProgress: undefined, + }, } Hosted.args = { ...args, @@ -79,8 +79,8 @@ Hosted.args = { mid: mid, cid: cid, downloadState: DownloadState.Hosted, - downloadProgress: undefined - } + downloadProgress: undefined, + }, } Queued.args = { ...args, @@ -91,14 +91,18 @@ Queued.args = { downloadProgress: { size: 2048, downloaded: 0, - transferSpeed: 0 - } + transferSpeed: 0, + }, + }, + cancelDownload: () => { + console.log('cancel download') }, - cancelDownload: () => { console.log('cancel download') } } Ready.args = { ...args, - downloadFile: () => { console.log('download file') } + downloadFile: () => { + console.log('download file') + }, } Downloading.args = { ...args, @@ -109,26 +113,28 @@ Downloading.args = { downloadProgress: { size: 1024, downloaded: 256, - transferSpeed: 32 - } + transferSpeed: 32, + }, + }, + cancelDownload: () => { + console.log('cancel download') }, - cancelDownload: () => { console.log('cancel download') } } Canceling.args = { ...args, downloadStatus: { mid: mid, cid: cid, - downloadState: DownloadState.Canceling - } + downloadState: DownloadState.Canceling, + }, } Canceled.args = { ...args, downloadStatus: { mid: mid, cid: cid, - downloadState: DownloadState.Canceled - } + downloadState: DownloadState.Canceled, + }, } Completed.args = { ...args, @@ -139,10 +145,12 @@ Completed.args = { downloadProgress: { size: 1024, downloaded: 1024, - transferSpeed: 0 - } + transferSpeed: 0, + }, + }, + openContainingFolder: () => { + console.log('show in folder') }, - openContainingFolder: () => { console.log('show in folder') } } Malicious.args = { ...args, @@ -150,14 +158,14 @@ Malicious.args = { mid: mid, cid: cid, downloadState: DownloadState.Malicious, - downloadProgress: undefined - } + downloadProgress: undefined, + }, } const component: ComponentMeta = { title: 'Components/FileComponent', decorators: [withTheme], - component: FileComponent + component: FileComponent, } export default component 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 89c946e7ec..c223a4e182 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 @@ -15,25 +15,25 @@ describe('FileComponent', () => { cid: 'QmWUCSApiy76nW9DAk5M9QbH1nkW5XCYwxUHRSULjATyqs', message: { channelId: 'general', - id: 'wgtlstx3u7' + id: 'wgtlstx3u7', }, ext: '.zip', name: 'my-file-name-goes-here-an-isnt-truncated', size: 2048, width: 1200, height: 580, - path: 'files/my-file-name-goes-here-an-isnt-truncated.zip' + path: 'files/my-file-name-goes-here-an-isnt-truncated.zip', }, message: '', createdAt: 0, date: '12:46', - nickname: 'vader' + nickname: 'vader', }} downloadStatus={{ mid: '32', cid: 'QmWUCSApiy76nW9DAk5M9QbH1nkW5XCYwxUHRSULjATyqs', downloadState: DownloadState.Ready, - downloadProgress: undefined + downloadProgress: undefined, }} /> ) diff --git a/packages/desktop/src/renderer/components/Channel/File/FileComponent/FileComponent.tsx b/packages/desktop/src/renderer/components/Channel/File/FileComponent/FileComponent.tsx index 4763c164e3..e55dd313b7 100644 --- a/packages/desktop/src/renderer/components/Channel/File/FileComponent/FileComponent.tsx +++ b/packages/desktop/src/renderer/components/Channel/File/FileComponent/FileComponent.tsx @@ -1,13 +1,7 @@ import React, { useState } from 'react' import { styled } from '@mui/material/styles' import { CircularProgress, Typography } from '@mui/material' -import { - DisplayableMessage, - DownloadState, - DownloadStatus, - FileMetadata, - CancelDownload -} from '@quiet/types' +import { DisplayableMessage, DownloadState, DownloadStatus, FileMetadata, CancelDownload } from '@quiet/types' import { formatBytes } from '@quiet/state-manager' import theme from '../../../../theme' import Icon from '../../../ui/Icon/Icon' @@ -28,7 +22,7 @@ const classes = { icon: `${PREFIX}icon`, fileIcon: `${PREFIX}fileIcon`, filename: `${PREFIX}filename`, - actionIcon: `${PREFIX}actionIcon` + actionIcon: `${PREFIX}actionIcon`, } const FileComponentStyled = styled('div')(({ theme }) => ({ @@ -47,17 +41,17 @@ const FileComponentStyled = styled('div')(({ theme }) => ({ backgroundColor: '#F0F0F0', display: 'flex', alignItems: 'center', - justifyContent: 'center' + justifyContent: 'center', }, [`& .${classes.fileIcon}`]: { width: '16px', - height: '20px' + height: '20px', }, [`& .${classes.filename}`]: { - marginLeft: '16px' - } + marginLeft: '16px', + }, })) const ActionIndicatorStyled = styled('div')(() => ({ @@ -65,8 +59,8 @@ const ActionIndicatorStyled = styled('div')(() => ({ width: 'fit-content', [`& .${classes.actionIcon}`]: { - width: '15px' - } + width: '15px', + }, })) interface ActionIndicatorMode { @@ -122,7 +116,8 @@ const ActionIndicator: React.FC<{ onClick={action} onMouseOver={onMouseOver} onMouseOut={onMouseOut} - style={{ cursor: hover ? 'pointer' : 'default' }}> + style={{ cursor: hover ? 'pointer' : 'default' }} + > {renderIndicator()}
) @@ -144,7 +139,7 @@ export const FileComponent: React.FC = ({ downloadStatus, openContainingFolder, downloadFile, - cancelDownload + cancelDownload, }) => { if (!message.media) return null const { cid, path, name, ext } = message.media @@ -201,7 +196,7 @@ export const FileComponent: React.FC = ({ if (cancelDownload) { cancelDownload({ mid: message.id, - cid: cid + cid: cid, }) } } @@ -214,7 +209,7 @@ export const FileComponent: React.FC = ({ regular={{ label: 'Uploading...', color: theme.palette.colors.darkGray, - icon: downloadIconGray + icon: downloadIconGray, }} /> ) @@ -224,14 +219,14 @@ export const FileComponent: React.FC = ({ regular={{ label: 'Show in folder', color: theme.palette.colors.darkGray, - icon: folderIconGray + icon: folderIconGray, }} hover={{ label: 'Show in folder', color: theme.palette.colors.lushSky, - icon: folderIcon + icon: folderIcon, }} - action={(_openContainingFolder)} + action={_openContainingFolder} /> ) case DownloadState.Ready: @@ -240,12 +235,12 @@ export const FileComponent: React.FC = ({ regular={{ label: 'Download file', color: theme.palette.colors.lushSky, - icon: downloadIcon + icon: downloadIcon, }} hover={{ label: 'Download file', color: theme.palette.colors.lushSky, - icon: downloadIcon + icon: downloadIcon, }} action={_downloadFile} /> @@ -253,11 +248,11 @@ export const FileComponent: React.FC = ({ case DownloadState.Queued: return ( ) case DownloadState.Downloading: @@ -266,12 +261,12 @@ export const FileComponent: React.FC = ({ regular={{ label: 'Downloading...', color: theme.palette.colors.darkGray, - icon: downloadIconGray + icon: downloadIconGray, }} hover={{ label: 'Cancel download', color: theme.palette.colors.hotRed, - icon: cancelIconRed + icon: cancelIconRed, }} action={_cancelDownload} /> @@ -282,7 +277,7 @@ export const FileComponent: React.FC = ({ regular={{ label: 'Canceling...', color: theme.palette.colors.darkGray, - icon: pauseIconGray + icon: pauseIconGray, }} /> ) @@ -292,12 +287,12 @@ export const FileComponent: React.FC = ({ regular={{ label: 'Canceled', color: theme.palette.colors.darkGray, - icon: cancelIconGray + icon: cancelIconGray, }} hover={{ label: 'Download file', color: theme.palette.colors.lushSky, - icon: downloadIcon + icon: downloadIcon, }} action={_downloadFile} /> @@ -308,12 +303,12 @@ export const FileComponent: React.FC = ({ regular={{ label: 'Show in folder', color: theme.palette.colors.darkGray, - icon: folderIconGray + icon: folderIconGray, }} hover={{ label: 'Show in folder', color: theme.palette.colors.lushSky, - icon: folderIcon + icon: folderIcon, }} action={_openContainingFolder} /> @@ -324,7 +319,7 @@ export const FileComponent: React.FC = ({ regular={{ label: 'File not valid. Download canceled.', color: theme.palette.colors.hotRed, - icon: cancelIconRed + icon: cancelIconRed, }} /> ) @@ -341,10 +336,13 @@ export const FileComponent: React.FC = ({ downloadProgress && downloadProgress.size && downloadProgress?.transferSpeed !== -1 - ? `(${Math.floor(downloadProgress.downloaded / downloadProgress.size * 100)}%) ${formatBytes(downloadProgress.transferSpeed)}ps` + ? `(${Math.floor((downloadProgress.downloaded / downloadProgress.size) * 100)}%) ${formatBytes( + downloadProgress.transferSpeed + )}ps` : '' } - placement='top'> + placement='top' + >
{renderIcon()}
@@ -352,9 +350,7 @@ export const FileComponent: React.FC = ({ {name} {ext} - + {message.media?.size ? formatBytes(message.media?.size) : 'Calculating size...'}
@@ -364,8 +360,9 @@ export const FileComponent: React.FC = ({ style={{ paddingTop: '16px', width: 'fit-content', - display: downloadState ? 'block' : 'none' - }}> + display: downloadState ? 'block' : 'none', + }} + > {renderActionIndicator()}
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 b59b9c13ad..19341fbb98 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 @@ -1,9 +1,4 @@ -import { - DisplayableMessage, - DownloadState, - DownloadStatus, - MessageType -} from '@quiet/types' +import { DisplayableMessage, DownloadState, DownloadStatus, MessageType } from '@quiet/types' import React from 'react' import { renderComponent } from '../../../../testUtils/renderComponent' @@ -30,15 +25,15 @@ describe('UploadedFile', () => { height: 600, message: { id: 'string', - channelId: 'general' - } - } + channelId: 'general', + }, + }, } downloadStatus = { mid: 'string', cid: 'abcd1234', - downloadState: DownloadState.Completed + downloadState: DownloadState.Completed, } }) @@ -118,7 +113,7 @@ describe('UploadedFile', () => { // @ts-expect-error message.media.message = { id: 'string', - channelId: 'general' + channelId: 'general', } const result = renderComponent( ({ [`& .${classes.image}`]: { maxWidth: '100%', - display: 'block' + display: 'block', }, [`& .${classes.container}`]: { maxWidth: '400px', - cursor: 'pointer' - } + cursor: 'pointer', + }, })) export interface UploadedImageProps { media: FileMetadata - uploadedFileModal?: - UseModalType<{ - src: string - }> + uploadedFileModal?: UseModalType<{ + src: string + }> downloadStatus?: DownloadStatus } @@ -50,7 +49,7 @@ export const UploadedImage: React.FC = ({ media, uploadedFil useEffect(() => { if (showImage && path) { uploadedFileModal?.handleOpen({ - src: path + src: path, }) } }, [showImage]) @@ -59,14 +58,15 @@ export const UploadedImage: React.FC = ({ media, uploadedFil const width = imageWidth >= 400 ? 400 : imageWidth return ( - ( + {path ? ( <>
{ setShowImage(true) - }}> + }} + >
= ({ media, uploadedFil downloadStatus={downloadStatus} /> )} - ) + ) } diff --git a/packages/desktop/src/renderer/components/Channel/File/UploadedImage/UploadedImagePreview.tsx b/packages/desktop/src/renderer/components/Channel/File/UploadedImage/UploadedImagePreview.tsx index f117f22cbc..cf44000985 100644 --- a/packages/desktop/src/renderer/components/Channel/File/UploadedImage/UploadedImagePreview.tsx +++ b/packages/desktop/src/renderer/components/Channel/File/UploadedImage/UploadedImagePreview.tsx @@ -7,7 +7,7 @@ import Modal from '../../../ui/Modal/Modal' const PREFIX = 'UploadedImagePreviewComponent' const classes = { - image: `${PREFIX}image` + image: `${PREFIX}image`, } const StyledModalContent = styled('div')(() => ({ @@ -24,8 +24,8 @@ const StyledModalContent = styled('div')(() => ({ top: '50%', transform: 'translate(-50%, -50%)', maxWidth: '90vw', - maxHeight: '90vh' - } + maxHeight: '90vh', + }, })) interface UploadedImagePreviewProps { @@ -39,7 +39,7 @@ interface UploadedImagePreviewProps { const UploadedImagePreviewComponent: React.FC = ({ open, handleClose, - uploadedFileModal + uploadedFileModal, }) => { return ( diff --git a/packages/desktop/src/renderer/components/Channel/File/UploadedImagePlaceholder/UploadedImagePlaceholder.stories.tsx b/packages/desktop/src/renderer/components/Channel/File/UploadedImagePlaceholder/UploadedImagePlaceholder.stories.tsx index a3265540f0..7fccc7253f 100644 --- a/packages/desktop/src/renderer/components/Channel/File/UploadedImagePlaceholder/UploadedImagePlaceholder.stories.tsx +++ b/packages/desktop/src/renderer/components/Channel/File/UploadedImagePlaceholder/UploadedImagePlaceholder.stories.tsx @@ -6,9 +6,7 @@ import UploadedImagePlaceholder, { UploadedImagePlaceholderProps } from './Uploa import { withTheme } from '../../../../storybook/decorators' const Template: ComponentStory = args => { - return ( - - ) + return } export const Component = Template.bind({}) @@ -16,7 +14,7 @@ export const Component = Template.bind({}) const downloadStatus = { mid: 'test', cid: 'hvb45FGa', - downloadState: DownloadState.Completed + downloadState: DownloadState.Completed, } const args: UploadedImagePlaceholderProps = { @@ -25,7 +23,7 @@ const args: UploadedImagePlaceholderProps = { imageHeight: 200, name: 'image', ext: '.png', - downloadStatus: downloadStatus + downloadStatus: downloadStatus, } Component.args = args @@ -33,7 +31,7 @@ Component.args = args const component: ComponentMeta = { title: 'Components/UploadedImagePlaceholder', decorators: [withTheme], - component: UploadedImagePlaceholder + component: UploadedImagePlaceholder, } export default component diff --git a/packages/desktop/src/renderer/components/Channel/File/UploadedImagePlaceholder/UploadedImagePlaceholder.test.tsx b/packages/desktop/src/renderer/components/Channel/File/UploadedImagePlaceholder/UploadedImagePlaceholder.test.tsx index dd105591ac..82b49d7b6e 100644 --- a/packages/desktop/src/renderer/components/Channel/File/UploadedImagePlaceholder/UploadedImagePlaceholder.test.tsx +++ b/packages/desktop/src/renderer/components/Channel/File/UploadedImagePlaceholder/UploadedImagePlaceholder.test.tsx @@ -11,7 +11,7 @@ describe('UploadedImagePlaceholder', () => { downloadStatus = { mid: 'test', cid: 'hvb45FGa', - downloadState: DownloadState.Completed + downloadState: DownloadState.Completed, } const result = renderComponent( { downloadProgress: { size: 12345, downloaded: 1234, - transferSpeed: 123 - } + transferSpeed: 123, + }, } const result = renderComponent( ({ @@ -29,11 +29,11 @@ const Root = styled('div')(() => ({ alignItems: 'center', minWidth: '50px', minHeight: '50px', - backgroundColor: '#e0e0e0' + backgroundColor: '#e0e0e0', }, [`& .${classes.placeholderIcon}`]: { - marginRight: '0.5em' + marginRight: '0.5em', }, [`& .${classes.icon}`]: { @@ -44,29 +44,21 @@ const Root = styled('div')(() => ({ backgroundColor: '#F0F0F0', display: 'flex', alignItems: 'center', - justifyContent: 'center' + justifyContent: 'center', }, })) -const StyledUploadedFilename = styled('p')(( - { - theme - } -) => ({ +const StyledUploadedFilename = styled('p')(({ theme }) => ({ color: theme.palette.colors.darkGray, - margin: 0 + margin: 0, })) interface UploadedFilenameProps { fileName: string } -export const UploadedFilename: React.FC = ({ - fileName -}) => { - return ( - {fileName} - ) +export const UploadedFilename: React.FC = ({ fileName }) => { + return {fileName} } export interface UploadedImagePlaceholderProps { @@ -84,7 +76,7 @@ export const UploadedImagePlaceholder: React.FC = imageHeight, name, ext, - downloadStatus + downloadStatus, }) => { const width = imageWidth >= 400 ? 400 : imageWidth @@ -128,17 +120,20 @@ export const UploadedImagePlaceholder: React.FC = -
+
+ placement='top' + >
{renderIcon()}
diff --git a/packages/desktop/src/renderer/components/Channel/File/UploadingPreview.tsx b/packages/desktop/src/renderer/components/Channel/File/UploadingPreview.tsx index 8cc015ea33..c3ba6a755a 100644 --- a/packages/desktop/src/renderer/components/Channel/File/UploadingPreview.tsx +++ b/packages/desktop/src/renderer/components/Channel/File/UploadingPreview.tsx @@ -19,7 +19,7 @@ const classes = { closeIconContainer: `${PREFIX}closeIconContainer`, closeIcon: `${PREFIX}closeIcon`, imageContainer: `${PREFIX}imageContainer`, - tooltip: `${PREFIX}tooltip` + tooltip: `${PREFIX}tooltip`, } const StyledFilePreviewComponent = styled('div')(() => ({ @@ -30,19 +30,19 @@ const StyledFilePreviewComponent = styled('div')(() => ({ [`& .${classes.wrapper}`]: { margin: '0 0 10px 10px', width: '64px', - height: '64px' + height: '64px', }, [`& .${classes.image}`]: { width: '64px', height: '64px', borderRadius: '15%', - objectFit: 'cover' + objectFit: 'cover', }, [`& .${classes.fileIcon}`]: { width: '32px', - height: '40px' + height: '40px', }, [`& .${classes.fileIconContainer}`]: { @@ -52,7 +52,7 @@ const StyledFilePreviewComponent = styled('div')(() => ({ backgroundColor: '#F0F0F0', display: 'flex', alignItems: 'center', - justifyContent: 'center' + justifyContent: 'center', }, [`& .${classes.closeIconContainer}`]: { @@ -65,8 +65,8 @@ const StyledFilePreviewComponent = styled('div')(() => ({ height: '22px', transform: 'translate(50%, -50%)', '&:hover': { - backgroundColor: '#dddddd' - } + backgroundColor: '#dddddd', + }, }, [`& .${classes.closeIcon}`]: { @@ -76,14 +76,14 @@ const StyledFilePreviewComponent = styled('div')(() => ({ color: '#444444', transform: 'translate(-50%, -50%)', '&:hover': { - color: '#000000' + color: '#000000', }, - width: '17px' + width: '17px', }, [`& .${classes.tooltip}`]: { - marginTop: '8px' - } + marginTop: '8px', + }, })) const StyledUploadFilesPreviewsComponent = styled('div')(() => ({ @@ -92,7 +92,7 @@ const StyledUploadFilesPreviewsComponent = styled('div')(() => ({ justifyContent: 'flexStart', alignItems: 'baseline', alignContent: 'stretch', - paddingRight: '50px' + paddingRight: '50px', })) export interface FilePreviewData { @@ -115,7 +115,8 @@ const FilePreviewComponent: React.FC = ({ fileData, o }} onMouseOver={() => { setShowClose(true) - }}> + }} + > {showClose && (
@@ -123,11 +124,11 @@ const FilePreviewComponent: React.FC = ({ fileData, o )}
- { imageType && fileData.path ? ( + {imageType && fileData.path ? ( {fileData.name} ) : (
- +
)}
@@ -141,10 +142,7 @@ export interface UploadFilesPreviewsProps { removeFile: (id: string) => void } -const UploadFilesPreviewsComponent: React.FC = ({ - filesData, - removeFile -}) => { +const UploadFilesPreviewsComponent: React.FC = ({ filesData, removeFile }) => { return ( {Object.entries(filesData).map(fileData => ( diff --git a/packages/desktop/src/renderer/components/Channel/NewMessagesInfo/NewMessagesInfoComponent.stories.tsx b/packages/desktop/src/renderer/components/Channel/NewMessagesInfo/NewMessagesInfoComponent.stories.tsx index 618b98734e..05d6ff02b6 100644 --- a/packages/desktop/src/renderer/components/Channel/NewMessagesInfo/NewMessagesInfoComponent.stories.tsx +++ b/packages/desktop/src/renderer/components/Channel/NewMessagesInfo/NewMessagesInfoComponent.stories.tsx @@ -13,7 +13,7 @@ export const Component = Template.bind({}) const args: NewMessagesInfoComponentProps = { scrollBottom: () => {}, - show: true + show: true, } Component.args = args @@ -21,7 +21,7 @@ Component.args = args const component: ComponentMeta = { title: 'Components/NewMessagesInfo', decorators: [withTheme], - component: NewMessagesInfoComponent + component: NewMessagesInfoComponent, } export default component diff --git a/packages/desktop/src/renderer/components/Channel/NewMessagesInfo/NewMessagesInfoComponent.tsx b/packages/desktop/src/renderer/components/Channel/NewMessagesInfo/NewMessagesInfoComponent.tsx index 74d42c9d39..418ae4b0d7 100644 --- a/packages/desktop/src/renderer/components/Channel/NewMessagesInfo/NewMessagesInfoComponent.tsx +++ b/packages/desktop/src/renderer/components/Channel/NewMessagesInfo/NewMessagesInfoComponent.tsx @@ -10,19 +10,15 @@ const classes = { wrapper: `${PREFIX}wrapper`, indicator: `${PREFIX}indicator`, label: `${PREFIX}label`, - icon: `${PREFIX}icon` + icon: `${PREFIX}icon`, } -const Root = styled('div')(( - { - theme - } -) => ({ +const Root = styled('div')(({ theme }) => ({ [`&.${classes.wrapper}`]: { width: '100%', position: 'absolute', bottom: 20, - zIndex: 2 + zIndex: 2, }, [`& .${classes.indicator}`]: { @@ -34,21 +30,21 @@ const Root = styled('div')(( borderRadius: 50, justifyContent: 'center', alignItems: 'center', - cursor: 'pointer' + cursor: 'pointer', }, [`& .${classes.label}`]: { color: theme.palette.colors.white, fontSize: '0.855rem', whiteSpace: 'pre-line', - lineHeight: '21px' + lineHeight: '21px', }, [`& .${classes.icon}`]: { width: 16, height: 16, - margin: '0px 0px 0px 8px' - } + margin: '0px 0px 0px 8px', + }, })) export interface NewMessagesInfoComponentProps { @@ -56,10 +52,7 @@ export interface NewMessagesInfoComponentProps { show: boolean } -export const NewMessagesInfoComponent: React.FC = ({ - scrollBottom, - show -}) => { +export const NewMessagesInfoComponent: React.FC = ({ scrollBottom, show }) => { return (
diff --git a/packages/desktop/src/renderer/components/ChannelCreationModal/ChannelCreationModal.component.tsx b/packages/desktop/src/renderer/components/ChannelCreationModal/ChannelCreationModal.component.tsx index 592d9e81d7..1c34a14e2d 100644 --- a/packages/desktop/src/renderer/components/ChannelCreationModal/ChannelCreationModal.component.tsx +++ b/packages/desktop/src/renderer/components/ChannelCreationModal/ChannelCreationModal.component.tsx @@ -7,11 +7,11 @@ import { styled } from '@mui/material/styles' const PREFIX = 'ChannelCreationModalComponent' const classes = { - wrapper: `${PREFIX}wrapper` + wrapper: `${PREFIX}wrapper`, } const StyledGrid = styled(Grid)(() => ({ - [`&.${classes.wrapper}`]: {} + [`&.${classes.wrapper}`]: {}, })) export interface ChannelCreationModalComponentProps { @@ -19,10 +19,7 @@ export interface ChannelCreationModalComponentProps { handleClose: () => void } -const ChannelCreationModalComponent: React.FC = ({ - open, - handleClose -}) => { +const ChannelCreationModalComponent: React.FC = ({ open, handleClose }) => { return ( diff --git a/packages/desktop/src/renderer/components/ChannelCreationModal/ChannelCreationModal.stories.tsx b/packages/desktop/src/renderer/components/ChannelCreationModal/ChannelCreationModal.stories.tsx index 1f560a23ac..d814840a88 100644 --- a/packages/desktop/src/renderer/components/ChannelCreationModal/ChannelCreationModal.stories.tsx +++ b/packages/desktop/src/renderer/components/ChannelCreationModal/ChannelCreationModal.stories.tsx @@ -1,9 +1,7 @@ import React from 'react' import { ComponentStory, ComponentMeta } from '@storybook/react' import { withTheme } from '../../storybook/decorators' -import ChannelCreationModalComponent, { - ChannelCreationModalComponentProps -} from './ChannelCreationModal.component' +import ChannelCreationModalComponent, { ChannelCreationModalComponentProps } from './ChannelCreationModal.component' const ChannelCreationModalTemplate: ComponentStory = args => { return @@ -13,7 +11,7 @@ export const ChannelCreationModal = ChannelCreationModalTemplate.bind({}) const ChannelCreationModalArgs: ChannelCreationModalComponentProps = { open: true, - handleClose: function (): void {} + handleClose: function (): void {}, } ChannelCreationModal.args = ChannelCreationModalArgs @@ -21,7 +19,7 @@ ChannelCreationModal.args = ChannelCreationModalArgs const component: ComponentMeta = { title: 'Components/ChannelCreationModal', decorators: [withTheme], - component: ChannelCreationModalComponent + component: ChannelCreationModalComponent, } export default component diff --git a/packages/desktop/src/renderer/components/ChannelCreationModal/ChannelCreationModal.test.tsx b/packages/desktop/src/renderer/components/ChannelCreationModal/ChannelCreationModal.test.tsx index f588c79b1e..081720d7cc 100644 --- a/packages/desktop/src/renderer/components/ChannelCreationModal/ChannelCreationModal.test.tsx +++ b/packages/desktop/src/renderer/components/ChannelCreationModal/ChannelCreationModal.test.tsx @@ -4,9 +4,7 @@ import ChannelCreationModalComponent from './ChannelCreationModal.component' describe('Create ChannelCreationModalComponent', () => { it('renders component', () => { - const result = renderComponent( - - ) + const result = renderComponent() expect(result.baseElement).toMatchInlineSnapshot(` = ({ - visible, - handleClose, - title, - items, - hint, -}) => { +export const ContextMenu: FC = ({ visible, handleClose, title, items, hint }) => { const ref = useRef(null) useEffect(() => { @@ -42,8 +36,9 @@ export const ContextMenu: FC = ({ width: '100%', height: '100%', zIndex: 9001, - pointerEvents: 'none' - }}> + pointerEvents: 'none', + }} + > = ({ // }, // elevation: 12, maxWidth: '375px', - pointerEvents: 'auto' + pointerEvents: 'auto', }} - data-testid={'contextMenu'}> + data-testid={'contextMenu'} + > = ({ textAlign: 'center', marginBottom: '-10px', height: 60, - width: '100%' - }}> + width: '100%', + }} + > + onClick={handleClose} + > @@ -101,8 +99,9 @@ export const ContextMenu: FC = ({ width: '100%', padding: 16, borderTopWidth: 1, - borderColor: '#F0F0F0' - }}> + borderColor: '#F0F0F0', + }} + > {hint} @@ -116,9 +115,10 @@ export const ContextMenu: FC = ({ cursor: 'pointer', borderTop: '1px solid', borderColor: '#F0F0F0', - borderBottomWidth: index === items.length - 1 ? '1px solid' : 0 + borderBottomWidth: index === items.length - 1 ? '1px solid' : 0, }} - key={index}> + key={index} + > ) @@ -139,14 +139,16 @@ export const ContextMenuItem: FC = ({ title, action }) => paddingLeft: 20, paddingRight: 20, height: 48, - width: '100%' + width: '100%', }} onClick={action} - data-testid={`contextMenuItem${title}`}> + data-testid={`contextMenuItem${title}`} + > + flex: 8, + }} + > {title} diff --git a/packages/desktop/src/renderer/components/ContextMenu/ContextMenu.stories.tsx b/packages/desktop/src/renderer/components/ContextMenu/ContextMenu.stories.tsx index e88344f739..931663de5c 100644 --- a/packages/desktop/src/renderer/components/ContextMenu/ContextMenu.stories.tsx +++ b/packages/desktop/src/renderer/components/ContextMenu/ContextMenu.stories.tsx @@ -17,8 +17,8 @@ const channel_items: ContextMenuItemProps[] = [ title: 'Delete', action: () => { console.log('clicked on delete channel') - } - } + }, + }, ] const args: ContextMenuProps = { @@ -27,7 +27,7 @@ const args: ContextMenuProps = { visible: true, handleClose: () => { console.log('closing menu') - } + }, } Component.args = args @@ -35,7 +35,7 @@ Component.args = args const component: ComponentMeta = { title: 'Components/ContextMenu', decorators: [withTheme], - component: ContextMenu + component: ContextMenu, } export default component diff --git a/packages/desktop/src/renderer/components/ContextMenu/menus/ChannelContextMenu.container.tsx b/packages/desktop/src/renderer/components/ContextMenu/menus/ChannelContextMenu.container.tsx index a2ce4e83b5..d0ff7d555d 100644 --- a/packages/desktop/src/renderer/components/ContextMenu/menus/ChannelContextMenu.container.tsx +++ b/packages/desktop/src/renderer/components/ContextMenu/menus/ChannelContextMenu.container.tsx @@ -34,8 +34,8 @@ export const ChannelContextMenu: FC = () => { action: () => { channelContextMenu.handleClose() // Dismiss context menu before displaying modal deleteChannelModal.handleOpen() - } - } + }, + }, ] } diff --git a/packages/desktop/src/renderer/components/CreateJoinCommunity/CreateCommunity/CreateCommunity.stories.tsx b/packages/desktop/src/renderer/components/CreateJoinCommunity/CreateCommunity/CreateCommunity.stories.tsx index f432e1cd8c..01188395c6 100644 --- a/packages/desktop/src/renderer/components/CreateJoinCommunity/CreateCommunity/CreateCommunity.stories.tsx +++ b/packages/desktop/src/renderer/components/CreateJoinCommunity/CreateCommunity/CreateCommunity.stories.tsx @@ -21,9 +21,9 @@ const args: PerformCommunityActionProps = { handleRedirection: function (): void { console.log('Redirected to join community') }, - handleClose: function (): void { }, + handleClose: function (): void {}, isCloseDisabled: false, - hasReceivedResponse: false + hasReceivedResponse: false, } Component.args = args @@ -31,7 +31,7 @@ Component.args = args const component: ComponentMeta = { title: 'Components/CreateCommunity', decorators: [withTheme], - component: PerformCommunityActionComponent + component: PerformCommunityActionComponent, } export default component diff --git a/packages/desktop/src/renderer/components/CreateJoinCommunity/CreateCommunity/CreateCommunity.test.tsx b/packages/desktop/src/renderer/components/CreateJoinCommunity/CreateCommunity/CreateCommunity.test.tsx index 01a04e1ce9..69a46114ce 100644 --- a/packages/desktop/src/renderer/components/CreateJoinCommunity/CreateCommunity/CreateCommunity.test.tsx +++ b/packages/desktop/src/renderer/components/CreateJoinCommunity/CreateCommunity/CreateCommunity.test.tsx @@ -24,12 +24,12 @@ describe('Create community', () => { const { store } = await prepareStore({ [StoreKeys.Socket]: { ...new SocketState(), - isConnected: true + isConnected: true, }, [StoreKeys.Modals]: { ...new ModalsInitialState(), - [ModalName.createCommunityModal]: { open: true } - } + [ModalName.createCommunityModal]: { open: true }, + }, }) renderComponent( @@ -59,18 +59,18 @@ describe('Create community', () => { const { store } = await prepareStore({ [StoreKeys.Socket]: { ...new SocketState(), - isConnected: true + isConnected: true, }, [StoreKeys.Modals]: { ...new ModalsInitialState(), - [ModalName.createCommunityModal]: { open: true } + [ModalName.createCommunityModal]: { open: true }, }, [StateManagerStoreKeys.Communities]: { - ...new communities.State() + ...new communities.State(), }, [StateManagerStoreKeys.Identity]: { - ...new identity.State() - } + ...new identity.State(), + }, }) renderComponent( @@ -104,16 +104,18 @@ describe('Create community', () => { it('creates community on submit if connection is ready', async () => { const handleCommunityAction = jest.fn() - const component = { }} - communityOwnership={CommunityOwnership.Owner} - handleCommunityAction={handleCommunityAction} - handleRedirection={() => { }} - isConnectionReady={true} - isCloseDisabled={true} - hasReceivedResponse={false} - /> + const component = ( + {}} + communityOwnership={CommunityOwnership.Owner} + handleCommunityAction={handleCommunityAction} + handleRedirection={() => {}} + isConnectionReady={true} + isCloseDisabled={true} + hasReceivedResponse={false} + /> + ) const result = renderComponent(component) const communityName = 'communityname' const textInput = result.queryByPlaceholderText('Community name') @@ -135,15 +137,15 @@ describe('Create community', () => { ['end-with-hyphen-', 'end-with-hyphen'], ['end-with-space ', 'end-with-space'], ['UpperCaseToLowerCase', 'uppercasetolowercase'], - ['spaces to hyphens', 'spaces-to-hyphens'] + ['spaces to hyphens', 'spaces-to-hyphens'], ])('user inserting wrong community name "%s" gets corrected "%s"', async (name: string, corrected: string) => { renderComponent( { }} + handleClose={() => {}} communityOwnership={CommunityOwnership.Owner} - handleCommunityAction={() => { }} - handleRedirection={() => { }} + handleCommunityAction={() => {}} + handleRedirection={() => {}} isConnectionReady={true} isCloseDisabled={true} hasReceivedResponse={false} @@ -153,27 +155,31 @@ describe('Create community', () => { const input = screen.getByPlaceholderText('Community name') await userEvent.type(input, name) - expect(screen.getByTestId('createCommunityNameWarning')).toHaveTextContent(`Your community will be created as #${corrected}`) + expect(screen.getByTestId('createCommunityNameWarning')).toHaveTextContent( + `Your community will be created as #${corrected}` + ) }) it.each([ [' whitespaces', FieldErrors.Whitespaces], ['----hyphens', FieldErrors.Whitespaces], ['!@#', CommunityNameErrors.WrongCharacter], - ['too-long-community-name', CommunityNameErrors.NameTooLong] + ['too-long-community-name', CommunityNameErrors.NameTooLong], ])('user inserting invalid community name "%s" should see "%s" error', async (name: string, error: string) => { const handleCommunityAction = jest.fn() - renderComponent( { }} - communityOwnership={CommunityOwnership.Owner} - handleCommunityAction={handleCommunityAction} - handleRedirection={() => { }} - isConnectionReady={true} - isCloseDisabled={true} - hasReceivedResponse={false} - />) + renderComponent( + {}} + communityOwnership={CommunityOwnership.Owner} + handleCommunityAction={handleCommunityAction} + handleRedirection={() => {}} + isConnectionReady={true} + isCloseDisabled={true} + hasReceivedResponse={false} + /> + ) const input = screen.getByPlaceholderText('Community name') const button = screen.getByText('Continue') @@ -190,16 +196,18 @@ describe('Create community', () => { it('blocks submit button if connection is not ready', async () => { const handleCommunityAction = jest.fn() - const component = { }} - communityOwnership={CommunityOwnership.Owner} - handleCommunityAction={handleCommunityAction} - handleRedirection={() => { }} - isConnectionReady={false} - isCloseDisabled={true} - hasReceivedResponse={false} - /> + const component = ( + {}} + communityOwnership={CommunityOwnership.Owner} + handleCommunityAction={handleCommunityAction} + handleRedirection={() => {}} + isConnectionReady={false} + isCloseDisabled={true} + hasReceivedResponse={false} + /> + ) const result = renderComponent(component) @@ -209,16 +217,18 @@ describe('Create community', () => { }) it('shows loading spinner on submit button while waiting for the response', async () => { - const { rerender } = renderComponent( { }} - communityOwnership={CommunityOwnership.Owner} - handleCommunityAction={() => { }} - handleRedirection={() => { }} - isConnectionReady={true} - isCloseDisabled={true} - hasReceivedResponse={false} - />) + const { rerender } = renderComponent( + {}} + communityOwnership={CommunityOwnership.Owner} + handleCommunityAction={() => {}} + handleRedirection={() => {}} + isConnectionReady={true} + isCloseDisabled={true} + hasReceivedResponse={false} + /> + ) const textInput = screen.getByPlaceholderText(communityNameField().fieldProps.placeholder) await userEvent.type(textInput, 'rockets') @@ -227,21 +237,23 @@ describe('Create community', () => { expect(submitButton).toBeEnabled() await userEvent.click(submitButton) - await act(async () => { }) + await act(async () => {}) expect(screen.queryByTestId('loading-button-progress')).toBeVisible() // Rerender component to verify circular progress has dissapeared - rerender( { }} - communityOwnership={CommunityOwnership.Owner} - handleCommunityAction={() => { }} - handleRedirection={() => { }} - isConnectionReady={true} - isCloseDisabled={true} - hasReceivedResponse={true} - />) + rerender( + {}} + communityOwnership={CommunityOwnership.Owner} + handleCommunityAction={() => {}} + handleRedirection={() => {}} + isConnectionReady={true} + isCloseDisabled={true} + hasReceivedResponse={true} + /> + ) expect(screen.queryByTestId('loading-button-progress')).toBeNull() }) @@ -250,16 +262,18 @@ describe('Create community', () => { const handleRedirection = jest.fn() const handleCommunityAction = jest.fn() - const component = { }} - communityOwnership={CommunityOwnership.Owner} - handleCommunityAction={handleCommunityAction} - handleRedirection={handleRedirection} - isConnectionReady={true} - isCloseDisabled={true} - hasReceivedResponse={false} - /> + const component = ( + {}} + communityOwnership={CommunityOwnership.Owner} + handleCommunityAction={handleCommunityAction} + handleRedirection={handleRedirection} + isConnectionReady={true} + isCloseDisabled={true} + hasReceivedResponse={false} + /> + ) const result = renderComponent(component) diff --git a/packages/desktop/src/renderer/components/CreateJoinCommunity/CreateCommunity/CreateCommunity.tsx b/packages/desktop/src/renderer/components/CreateJoinCommunity/CreateCommunity/CreateCommunity.tsx index c43de28874..272f631775 100644 --- a/packages/desktop/src/renderer/components/CreateJoinCommunity/CreateCommunity/CreateCommunity.tsx +++ b/packages/desktop/src/renderer/components/CreateJoinCommunity/CreateCommunity/CreateCommunity.tsx @@ -27,7 +27,7 @@ const CreateCommunity = () => { const handleCommunityAction = (name: string) => { const payload: CreateNetworkPayload = { ownership: CommunityOwnership.Owner, - name: name + name: name, } dispatch(communities.actions.createNetwork(payload)) } diff --git a/packages/desktop/src/renderer/components/CreateJoinCommunity/JoinCommunity/JoinCommunity.stories.tsx b/packages/desktop/src/renderer/components/CreateJoinCommunity/JoinCommunity/JoinCommunity.stories.tsx index 6f5eb97ba8..0b4dd62fb1 100644 --- a/packages/desktop/src/renderer/components/CreateJoinCommunity/JoinCommunity/JoinCommunity.stories.tsx +++ b/packages/desktop/src/renderer/components/CreateJoinCommunity/JoinCommunity/JoinCommunity.stories.tsx @@ -21,11 +21,11 @@ const args: PerformCommunityActionProps = { handleRedirection: function (): void { console.log('Redirected to create community') }, - handleClose: function (): void { }, + handleClose: function (): void {}, isCloseDisabled: false, hasReceivedResponse: false, handleClickInputReveal: function (): void {}, - revealInputValue: false + revealInputValue: false, } Component.args = args @@ -33,7 +33,7 @@ Component.args = args const component: ComponentMeta = { title: 'Components/JoinCommunity', decorators: [withTheme], - component: PerformCommunityActionComponent + component: PerformCommunityActionComponent, } export default component diff --git a/packages/desktop/src/renderer/components/CreateJoinCommunity/JoinCommunity/JoinCommunity.test.tsx b/packages/desktop/src/renderer/components/CreateJoinCommunity/JoinCommunity/JoinCommunity.test.tsx index 812741942d..3985a2845f 100644 --- a/packages/desktop/src/renderer/components/CreateJoinCommunity/JoinCommunity/JoinCommunity.test.tsx +++ b/packages/desktop/src/renderer/components/CreateJoinCommunity/JoinCommunity/JoinCommunity.test.tsx @@ -26,12 +26,12 @@ describe('join community', () => { const { store } = await prepareStore({ [StoreKeys.Socket]: { ...new SocketState(), - isConnected: true + isConnected: true, }, [StoreKeys.Modals]: { ...new ModalsInitialState(), - [ModalName.joinCommunityModal]: { open: true } - } + [ModalName.joinCommunityModal]: { open: true }, + }, }) renderComponent( @@ -61,12 +61,12 @@ describe('join community', () => { const { store } = await prepareStore({ [StoreKeys.Socket]: { ...new SocketState(), - isConnected: true + isConnected: true, }, [StoreKeys.Modals]: { ...new ModalsInitialState(), - [ModalName.joinCommunityModal]: { open: true } - } + [ModalName.joinCommunityModal]: { open: true }, + }, }) renderComponent( @@ -103,16 +103,18 @@ describe('join community', () => { const handleCommunityAction = jest.fn() - const component = { }} - communityOwnership={CommunityOwnership.User} - handleCommunityAction={handleCommunityAction} - handleRedirection={() => { }} - isConnectionReady={true} - isCloseDisabled={true} - hasReceivedResponse={false} - /> + const component = ( + {}} + communityOwnership={CommunityOwnership.User} + handleCommunityAction={handleCommunityAction} + handleRedirection={() => {}} + isConnectionReady={true} + isCloseDisabled={true} + hasReceivedResponse={false} + /> + ) const result = renderComponent(component) @@ -129,54 +131,61 @@ describe('join community', () => { }) it.each([ - [`https://${Site.DOMAIN}/${Site.JOIN_PAGE}#${validCode}`], - [`https://${Site.DOMAIN}/${Site.JOIN_PAGE}/#${validCode}`], - [`https://${Site.DOMAIN}/${Site.JOIN_PAGE}?code=${validCode}`] // Old link format - ])('joins community on submit if connection is ready and invitation code is a correct invitation url (%s)', async (invitationLink: string) => { - const registrarUrl = new URL(invitationLink) - - const handleCommunityAction = jest.fn() - - const component = { }} - communityOwnership={CommunityOwnership.User} - handleCommunityAction={handleCommunityAction} - handleRedirection={() => { }} - isConnectionReady={true} - isCloseDisabled={true} - hasReceivedResponse={false} - /> - - const result = renderComponent(component) - - const textInput = result.queryByPlaceholderText(inviteLinkField().fieldProps.placeholder) - expect(textInput).not.toBeNull() - // @ts-expect-error - await userEvent.type(textInput, registrarUrl.href) - - const submitButton = result.getByText('Continue') - expect(submitButton).toBeEnabled() - await userEvent.click(submitButton) - - await waitFor(() => expect(handleCommunityAction).toBeCalledWith(validCode)) - }) + [`https://${Site.DOMAIN}/${Site.JOIN_PAGE}#${validCode}`], + [`https://${Site.DOMAIN}/${Site.JOIN_PAGE}/#${validCode}`], + [`https://${Site.DOMAIN}/${Site.JOIN_PAGE}?code=${validCode}`], // Old link format + ])( + 'joins community on submit if connection is ready and invitation code is a correct invitation url (%s)', + async (invitationLink: string) => { + const registrarUrl = new URL(invitationLink) + + const handleCommunityAction = jest.fn() + + const component = ( + {}} + communityOwnership={CommunityOwnership.User} + handleCommunityAction={handleCommunityAction} + handleRedirection={() => {}} + isConnectionReady={true} + isCloseDisabled={true} + hasReceivedResponse={false} + /> + ) + + const result = renderComponent(component) + + const textInput = result.queryByPlaceholderText(inviteLinkField().fieldProps.placeholder) + expect(textInput).not.toBeNull() + // @ts-expect-error + await userEvent.type(textInput, registrarUrl.href) + + const submitButton = result.getByText('Continue') + expect(submitButton).toBeEnabled() + await userEvent.click(submitButton) + + await waitFor(() => expect(handleCommunityAction).toBeCalledWith(validCode)) + } + ) it('trims whitespaces from registrar url', async () => { const registrarUrl = 'nqnw4kc4c77fb47lk52m5l57h4tcxceo7ymxekfn7yh5m66t4jv2olad ' const handleCommunityAction = jest.fn() - const component = { }} - communityOwnership={CommunityOwnership.User} - handleCommunityAction={handleCommunityAction} - handleRedirection={() => { }} - isConnectionReady={true} - isCloseDisabled={true} - hasReceivedResponse={false} - /> + const component = ( + {}} + communityOwnership={CommunityOwnership.User} + handleCommunityAction={handleCommunityAction} + handleRedirection={() => {}} + isConnectionReady={true} + isCloseDisabled={true} + hasReceivedResponse={false} + /> + ) const result = renderComponent(component) @@ -199,22 +208,33 @@ describe('join community', () => { ['nqnw4kc4c77fb47lk52m5l57h4tcxceo7ymxekfn7yh5m66t4jv2ola ', InviteLinkErrors.InvalidCode], ['nqnw4kc4c77fb47lk52m5l57h4tc', InviteLinkErrors.InvalidCode], [`https://${Site.DOMAIN}/${Site.JOIN_PAGE}?${InvitationParams.CODE}=invalidcode`, InviteLinkErrors.InvalidCode], - [`https://otherwebsite.com/${Site.JOIN_PAGE}?${InvitationParams.CODE}=nqnw4kc4c77fb47lk52m5l57h4tcxceo7ymxekfn7yh5m66t4jv2olad`, InviteLinkErrors.InvalidCode], - [`https://${Site.DOMAIN}/${Site.JOIN_PAGE}?param=nqnw4kc4c77fb47lk52m5l57h4tcxceo7ymxekfn7yh5m66t4jv2olad`, InviteLinkErrors.InvalidCode], - [`https://${Site.DOMAIN}/share?${InvitationParams.CODE}=nqnw4kc4c77fb47lk52m5l57h4tcxceo7ymxekfn7yh5m66t4jv2olad`, InviteLinkErrors.InvalidCode], + [ + `https://otherwebsite.com/${Site.JOIN_PAGE}?${InvitationParams.CODE}=nqnw4kc4c77fb47lk52m5l57h4tcxceo7ymxekfn7yh5m66t4jv2olad`, + InviteLinkErrors.InvalidCode, + ], + [ + `https://${Site.DOMAIN}/${Site.JOIN_PAGE}?param=nqnw4kc4c77fb47lk52m5l57h4tcxceo7ymxekfn7yh5m66t4jv2olad`, + InviteLinkErrors.InvalidCode, + ], + [ + `https://${Site.DOMAIN}/share?${InvitationParams.CODE}=nqnw4kc4c77fb47lk52m5l57h4tcxceo7ymxekfn7yh5m66t4jv2olad`, + InviteLinkErrors.InvalidCode, + ], ])('user inserting invalid url %s should see "%s" error', async (url: string, error: string) => { const handleCommunityAction = jest.fn() - renderComponent( { }} - communityOwnership={CommunityOwnership.User} - handleCommunityAction={handleCommunityAction} - handleRedirection={() => { }} - isConnectionReady={true} - isCloseDisabled={true} - hasReceivedResponse={false} - />) + renderComponent( + {}} + communityOwnership={CommunityOwnership.User} + handleCommunityAction={handleCommunityAction} + handleRedirection={() => {}} + isConnectionReady={true} + isCloseDisabled={true} + hasReceivedResponse={false} + /> + ) const input = screen.getByPlaceholderText('Invite code') const button = screen.getByText('Continue') @@ -231,16 +251,18 @@ describe('join community', () => { it('blocks submit button if connection is not ready', async () => { const handleCommunityAction = jest.fn() - const component = { }} - communityOwnership={CommunityOwnership.User} - handleCommunityAction={handleCommunityAction} - handleRedirection={() => { }} - isConnectionReady={false} - isCloseDisabled={true} - hasReceivedResponse={false} - /> + const component = ( + {}} + communityOwnership={CommunityOwnership.User} + handleCommunityAction={handleCommunityAction} + handleRedirection={() => {}} + isConnectionReady={false} + isCloseDisabled={true} + hasReceivedResponse={false} + /> + ) const result = renderComponent(component) @@ -257,16 +279,18 @@ describe('join community', () => { }) it('shows loading spinner on submit button while waiting for the response', async () => { - const { rerender } = renderComponent( { }} - communityOwnership={CommunityOwnership.User} - handleCommunityAction={() => { }} - handleRedirection={() => { }} - isConnectionReady={true} - isCloseDisabled={true} - hasReceivedResponse={false} - />) + const { rerender } = renderComponent( + {}} + communityOwnership={CommunityOwnership.User} + handleCommunityAction={() => {}} + handleRedirection={() => {}} + isConnectionReady={true} + isCloseDisabled={true} + hasReceivedResponse={false} + /> + ) const textInput = screen.getByPlaceholderText(inviteLinkField().fieldProps.placeholder) await userEvent.type(textInput, 'nqnw4kc4c77fb47lk52m5l57h4tcxceo7ymxekfn7yh5m66t4jv2olad') @@ -280,16 +304,18 @@ describe('join community', () => { expect(screen.queryByTestId('loading-button-progress')).toBeVisible() // Rerender component to verify circular progress has dissapeared - rerender( { }} - communityOwnership={CommunityOwnership.User} - handleCommunityAction={() => { }} - handleRedirection={() => { }} - isConnectionReady={true} - isCloseDisabled={true} - hasReceivedResponse={true} - />) + rerender( + {}} + communityOwnership={CommunityOwnership.User} + handleCommunityAction={() => {}} + handleRedirection={() => {}} + isConnectionReady={true} + isCloseDisabled={true} + hasReceivedResponse={true} + /> + ) expect(screen.queryByTestId('loading-button-progress')).toBeNull() }) @@ -297,16 +323,18 @@ describe('join community', () => { it('handles redirection to create community page if user clicks on the link', async () => { const handleRedirection = jest.fn() - const component = { }} - communityOwnership={CommunityOwnership.User} - handleCommunityAction={() => { }} - handleRedirection={handleRedirection} - isConnectionReady={true} - isCloseDisabled={true} - hasReceivedResponse={false} - /> + const component = ( + {}} + communityOwnership={CommunityOwnership.User} + handleCommunityAction={() => {}} + handleRedirection={handleRedirection} + isConnectionReady={true} + isCloseDisabled={true} + hasReceivedResponse={false} + /> + ) const result = renderComponent(component) diff --git a/packages/desktop/src/renderer/components/CreateJoinCommunity/JoinCommunity/JoinCommunity.tsx b/packages/desktop/src/renderer/components/CreateJoinCommunity/JoinCommunity/JoinCommunity.tsx index d2c27f9943..04310859d2 100644 --- a/packages/desktop/src/renderer/components/CreateJoinCommunity/JoinCommunity/JoinCommunity.tsx +++ b/packages/desktop/src/renderer/components/CreateJoinCommunity/JoinCommunity/JoinCommunity.tsx @@ -1,16 +1,8 @@ import React, { useEffect, useState } from 'react' import { useDispatch, useSelector } from 'react-redux' import { socketSelectors } from '../../../sagas/socket/socket.selectors' -import { - CommunityOwnership, - CreateNetworkPayload, - TOR_BOOTSTRAP_COMPLETE -} from '@quiet/types' -import { - communities, - identity, - connection -} from '@quiet/state-manager' +import { CommunityOwnership, CreateNetworkPayload, TOR_BOOTSTRAP_COMPLETE } from '@quiet/types' +import { communities, identity, connection } from '@quiet/state-manager' import PerformCommunityActionComponent from '../../../components/CreateJoinCommunity/PerformCommunityActionComponent' import { ModalName } from '../../../sagas/modals/modals.types' import { useModal } from '../../../containers/hooks' @@ -52,7 +44,7 @@ const JoinCommunity = () => { const handleCommunityAction = (address: string) => { const payload: CreateNetworkPayload = { ownership: CommunityOwnership.User, - registrar: address + registrar: address, } dispatch(communities.actions.createNetwork(payload)) } diff --git a/packages/desktop/src/renderer/components/CreateJoinCommunity/PerformCommunityActionComponent.tsx b/packages/desktop/src/renderer/components/CreateJoinCommunity/PerformCommunityActionComponent.tsx index ca483982f6..e0442790ae 100644 --- a/packages/desktop/src/renderer/components/CreateJoinCommunity/PerformCommunityActionComponent.tsx +++ b/packages/desktop/src/renderer/components/CreateJoinCommunity/PerformCommunityActionComponent.tsx @@ -10,10 +10,7 @@ import WarningIcon from '@mui/icons-material/Warning' import Modal from '../ui/Modal/Modal' import { LoadingButton } from '../ui/LoadingButton/LoadingButton' -import { - CreateCommunityDictionary, - JoinCommunityDictionary -} from '../CreateJoinCommunity/community.dictionary' +import { CreateCommunityDictionary, JoinCommunityDictionary } from '../CreateJoinCommunity/community.dictionary' import { CommunityOwnership } from '@quiet/types' @@ -41,46 +38,42 @@ const classes = { warrningMessage: `${PREFIX}warrningMessage`, rootBar: `${PREFIX}rootBar`, progressBar: `${PREFIX}progressBar`, - info: `${PREFIX}info` + info: `${PREFIX}info`, } -const StyledModalContent = styled(Grid)(( - { - theme - } -) => ({ - backgroundColor: theme.palette.colors.white, - padding: '0px 32px', +const StyledModalContent = styled(Grid)(({ theme }) => ({ + backgroundColor: theme.palette.colors.white, + padding: '0px 32px', [`& .${classes.focus}`]: { '& .MuiOutlinedInput-root': { '&.Mui-focused fieldset': { - borderColor: theme.palette.colors.linkBlue - } - } + borderColor: theme.palette.colors.linkBlue, + }, + }, }, [`& .${classes.margin}`]: { '& .MuiFormHelperText-contained': { - margin: '5px 0px' - } + margin: '5px 0px', + }, }, [`& .${classes.error}`]: { '& .MuiOutlinedInput-root': { '&.Mui-focused fieldset': { - borderColor: theme.palette.colors.red - } - } + borderColor: theme.palette.colors.red, + }, + }, }, [`& .${classes.fullContainer}`]: { - width: '100%' + width: '100%', }, [`& .${classes.gutter}`]: { marginTop: 8, - marginBottom: 24 + marginBottom: 24, }, [`& .${classes.button}`]: { @@ -88,45 +81,45 @@ const StyledModalContent = styled(Grid)(( backgroundColor: theme.palette.colors.quietBlue, color: theme.palette.colors.white, '&:hover': { - backgroundColor: theme.palette.colors.quietBlue + backgroundColor: theme.palette.colors.quietBlue, }, textTransform: 'none', height: 48, - fontWeight: 'normal' + fontWeight: 'normal', }, [`& .${classes.title}`]: { - marginBottom: 24 + marginBottom: 24, }, [`& .${classes.iconDiv}`]: { width: 24, height: 28, - marginRight: 8 + marginRight: 8, }, [`& .${classes.warrningIcon}`]: { - color: '#FFCC00' + color: '#FFCC00', }, [`& .${classes.warrningMessage}`]: { - wordBreak: 'break-word' + wordBreak: 'break-word', }, [`& .${classes.rootBar}`]: { width: 350, marginTop: 32, - marginBottom: 16 + marginBottom: 16, }, [`& .${classes.progressBar}`]: { - backgroundColor: theme.palette.colors.linkBlue + backgroundColor: theme.palette.colors.linkBlue, }, [`& .${classes.info}`]: { lineHeight: '19px', - color: theme.palette.colors.darkGray - } + color: theme.palette.colors.darkGray, + }, })) interface PerformCommunityActionFormValues { @@ -158,7 +151,7 @@ export const PerformCommunityActionComponent: React.FC { const [formSent, setFormSent] = useState(false) @@ -177,21 +170,19 @@ export const PerformCommunityActionComponent: React.FC({ - mode: 'onTouched' + mode: 'onTouched', }) - const onSubmit = (values: PerformCommunityActionFormValues) => - submitForm(handleCommunityAction, values, setFormSent) + const onSubmit = (values: PerformCommunityActionFormValues) => submitForm(handleCommunityAction, values, setFormSent) const submitForm = ( handleSubmit: (value: string) => void, values: PerformCommunityActionFormValues, setFormSent: (value: boolean) => void ) => { - let submitValue = - communityOwnership === CommunityOwnership.Owner ? parseName(values.name) : values.name.trim() + let submitValue = communityOwnership === CommunityOwnership.Owner ? parseName(values.name) : values.name.trim() if (communityOwnership === CommunityOwnership.User) { submitValue = getInvitationCode(submitValue) @@ -233,11 +224,7 @@ export const PerformCommunityActionComponent: React.FC <> - + {dictionary.header} @@ -254,7 +241,7 @@ export const PerformCommunityActionComponent: React.FC {}} - InputProps={communityOwnership === CommunityOwnership.User - ? { - endAdornment: ( - - - {!revealInputValue ? ( - - ) : ( - - )} - - - ) - } : <>} + InputProps={ + communityOwnership === CommunityOwnership.User ? ( + { + endAdornment: ( + + + {!revealInputValue ? ( + + ) : ( + + )} + + + ), + } + ) : ( + <> + ) + } type={revealInputValue ? 'text' : 'password'} value={communityOwnership === CommunityOwnership.User ? field.value.trim() : field.value} /> @@ -300,7 +289,8 @@ export const PerformCommunityActionComponent: React.FC + data-testid={'createCommunityNameWarning'} + > Your community will be created as {`#${communityName}`} diff --git a/packages/desktop/src/renderer/components/CreateJoinCommunity/community.dictionary.tsx b/packages/desktop/src/renderer/components/CreateJoinCommunity/community.dictionary.tsx index cceeb5beeb..50f3d29424 100644 --- a/packages/desktop/src/renderer/components/CreateJoinCommunity/community.dictionary.tsx +++ b/packages/desktop/src/renderer/components/CreateJoinCommunity/community.dictionary.tsx @@ -14,9 +14,7 @@ export interface PerformCommunityActionDictionary { id: string } -export const CreateCommunityDictionary = ( - handleRedirection?: () => void -): PerformCommunityActionDictionary => { +export const CreateCommunityDictionary = (handleRedirection?: () => void): PerformCommunityActionDictionary => { let link: ReactElement | undefined if (handleRedirection) { link = ( @@ -26,8 +24,8 @@ export const CreateCommunityDictionary = ( { tag: 'link', label: 'join a community', - action: handleRedirection - } + action: handleRedirection, + }, ]} testIdPrefix={'CreateCommunity'} /> @@ -41,7 +39,7 @@ export const CreateCommunityDictionary = ( button: 'Continue', field: communityNameField(), redirection: link, - id: 'createCommunity' + id: 'createCommunity', } } @@ -55,8 +53,8 @@ export const JoinCommunityDictionary = (handleRedirection?: () => void): Perform { tag: 'link', label: 'create a new community', - action: handleRedirection - } + action: handleRedirection, + }, ]} testIdPrefix={'JoinCommunity'} /> @@ -70,6 +68,6 @@ export const JoinCommunityDictionary = (handleRedirection?: () => void): Perform button: 'Continue', field: inviteLinkField(), redirection: link, - id: 'joinCommunity' + id: 'joinCommunity', } } diff --git a/packages/desktop/src/renderer/components/CreateUsername/CreateUsername.stories.tsx b/packages/desktop/src/renderer/components/CreateUsername/CreateUsername.stories.tsx index 0abc53397b..e298655534 100644 --- a/packages/desktop/src/renderer/components/CreateUsername/CreateUsername.stories.tsx +++ b/packages/desktop/src/renderer/components/CreateUsername/CreateUsername.stories.tsx @@ -14,9 +14,9 @@ export const Component = Template.bind({}) const args: CreateUsernameComponentProps = { open: true, handleClose: function (): void {}, - registerUsername: function(nickname: string): void { + registerUsername: function (nickname: string): void { console.log('Registering username: ', nickname) - } + }, } Component.args = args @@ -24,7 +24,7 @@ Component.args = args const component: ComponentMeta = { title: 'Components/CreateUsername', decorators: [withTheme], - component: CreateUsernameComponent + component: CreateUsernameComponent, } export default component diff --git a/packages/desktop/src/renderer/components/CreateUsername/CreateUsername.test.tsx b/packages/desktop/src/renderer/components/CreateUsername/CreateUsername.test.tsx index 6c03ae4e4f..0f8656a6c4 100644 --- a/packages/desktop/src/renderer/components/CreateUsername/CreateUsername.test.tsx +++ b/packages/desktop/src/renderer/components/CreateUsername/CreateUsername.test.tsx @@ -16,27 +16,25 @@ describe('Create username', () => { ['end-with-space ', 'end-with-space'], ['UpperCaseToLowerCase', 'uppercasetolowercase'], ['spaces to hyphens', 'spaces-to-hyphens'], - ['----hyphens', 'hyphens'] + ['----hyphens', 'hyphens'], ])('user inserting wrong name "%s" gets corrected "%s"', async (name: string, corrected: string) => { - renderComponent( - {}} handleClose={() => {}} /> - ) + renderComponent( {}} handleClose={() => {}} />) const input = screen.getByPlaceholderText('Enter a username') await userEvent.type(input, name) - expect(screen.getByTestId('createUserNameWarning')).toHaveTextContent(`Your username will be registered as @${corrected}`) + expect(screen.getByTestId('createUserNameWarning')).toHaveTextContent( + `Your username will be registered as @${corrected}` + ) }) it.each([ [' whitespaces', FieldErrors.Whitespaces], - ['!@#', UsernameErrors.WrongCharacter] + ['!@#', UsernameErrors.WrongCharacter], ])('user inserting invalid name "%s" should see "%s" error', async (name: string, error: string) => { const registerUsername = jest.fn() - renderComponent( - {}} /> - ) + renderComponent( {}} />) const input = screen.getByPlaceholderText('Enter a username') const button = screen.getByText('Register') diff --git a/packages/desktop/src/renderer/components/CreateUsername/CreateUsernameComponent.tsx b/packages/desktop/src/renderer/components/CreateUsername/CreateUsernameComponent.tsx index 12c74bbab5..b26d4b49a5 100644 --- a/packages/desktop/src/renderer/components/CreateUsername/CreateUsernameComponent.tsx +++ b/packages/desktop/src/renderer/components/CreateUsername/CreateUsernameComponent.tsx @@ -30,47 +30,43 @@ const classes = { warrningMessage: `${PREFIX}warrningMessage`, rootBar: `${PREFIX}rootBar`, progressBar: `${PREFIX}progressBar`, - info: `${PREFIX}info` + info: `${PREFIX}info`, } -const StyledModalContent = styled(Grid)(( - { - theme - } -) => ({ +const StyledModalContent = styled(Grid)(({ theme }) => ({ backgroundColor: theme.palette.colors.white, padding: '0px 32px', [`& .${classes.focus}`]: { '& .MuiOutlinedInput-root': { '&.Mui-focused fieldset': { - borderColor: theme.palette.colors.linkBlue - } - } + borderColor: theme.palette.colors.linkBlue, + }, + }, }, [`& .${classes.margin}`]: { '& .MuiFormHelperText-contained': { - margin: '5px 0px' - } + margin: '5px 0px', + }, }, [`& .${classes.error}`]: { '& .MuiOutlinedInput-root': { '&.Mui-focused fieldset': { - borderColor: theme.palette.colors.red - } - } + borderColor: theme.palette.colors.red, + }, + }, }, [`& .${classes.fullContainer}`]: { width: '100%', - height: '100%' + height: '100%', }, [`& .${classes.gutter}`]: { marginTop: 8, - marginBottom: 24 + marginBottom: 24, }, [`& .${classes.button}`]: { @@ -78,49 +74,49 @@ const StyledModalContent = styled(Grid)(( backgroundColor: theme.palette.colors.quietBlue, color: theme.palette.colors.white, '&:hover': { - backgroundColor: theme.palette.colors.quietBlue + backgroundColor: theme.palette.colors.quietBlue, }, textTransform: 'none', height: 48, - fontWeight: 'normal' + fontWeight: 'normal', }, [`& .${classes.title}`]: { - marginBottom: 24 + marginBottom: 24, }, [`& .${classes.iconDiv}`]: { width: 24, height: 28, - marginRight: 8 + marginRight: 8, }, [`& .${classes.warrningIcon}`]: { - color: '#FFCC00' + color: '#FFCC00', }, [`& .${classes.warrningMessage}`]: { - wordBreak: 'break-word' + wordBreak: 'break-word', }, [`& .${classes.rootBar}`]: { width: 350, marginTop: 32, - marginBottom: 16 + marginBottom: 16, }, [`& .${classes.progressBar}`]: { - backgroundColor: theme.palette.colors.linkBlue + backgroundColor: theme.palette.colors.linkBlue, }, [`& .${classes.info}`]: { lineHeight: '19px', - color: theme.palette.colors.darkGray - } + color: theme.palette.colors.darkGray, + }, })) const userFields = { - userName: userNameField() + userName: userNameField(), } interface CreateUserValues { @@ -140,7 +136,7 @@ export const CreateUsernameComponent: React.FC = ( registerUsername, certificateRegistrationError, certificate, - handleClose + handleClose, }) => { const [formSent, setFormSent] = useState(false) const [userName, setUserName] = useState('') @@ -154,9 +150,9 @@ export const CreateUsernameComponent: React.FC = ( formState: { errors }, setValue, setError, - control + control, } = useForm({ - mode: 'onTouched' + mode: 'onTouched', }) const onSubmit = (values: CreateUserValues) => { @@ -194,78 +190,75 @@ export const CreateUsernameComponent: React.FC = ( return ( - <> - - - - Register a username - - Choose your favorite username - ( - e.preventDefault()} - variant='outlined' - onchange={event => { - event.persist() - const value = event.target.value - onChange(value) - // Call default - field.onChange(event) - }} - onblur={() => { - field.onBlur() - }} - value={field.value} - /> - )} - /> -
- {!errors.userName && userName.length > 0 && parsedNameDiffers && ( - - - - - - - Your username will be registered as {`@${userName}`} - - + <> + + + + Register a username + + Choose your favorite username + ( + e.preventDefault()} + variant='outlined' + onchange={event => { + event.persist() + const value = event.target.value + onChange(value) + // Call default + field.onChange(event) + }} + onblur={() => { + field.onBlur() + }} + value={field.value} + /> + )} + /> +
+ {!errors.userName && userName.length > 0 && parsedNameDiffers && ( + + + + + + + Your username will be registered as {`@${userName}`} + - )} -
- -
- - +
+ )} +
+ +
+ +
) diff --git a/packages/desktop/src/renderer/components/CreateUsername/UsernameCreated/UsernameCreated.test.tsx b/packages/desktop/src/renderer/components/CreateUsername/UsernameCreated/UsernameCreated.test.tsx index 3df5e5f3a3..be91805216 100644 --- a/packages/desktop/src/renderer/components/CreateUsername/UsernameCreated/UsernameCreated.test.tsx +++ b/packages/desktop/src/renderer/components/CreateUsername/UsernameCreated/UsernameCreated.test.tsx @@ -1,4 +1,3 @@ -/* eslint import/first: 0 */ import React from 'react' import { renderComponent } from '../../../testUtils/renderComponent' @@ -6,9 +5,7 @@ import { UsernameCreated } from './UsernameCreated' describe('UsernameCreated', () => { it('renders component', () => { - const result = renderComponent( - - ) + const result = renderComponent() expect(result.baseElement).toMatchInlineSnapshot(`
diff --git a/packages/desktop/src/renderer/components/CreateUsername/UsernameCreated/UsernameCreated.tsx b/packages/desktop/src/renderer/components/CreateUsername/UsernameCreated/UsernameCreated.tsx index 32d8e01302..1fa9fff5c3 100644 --- a/packages/desktop/src/renderer/components/CreateUsername/UsernameCreated/UsernameCreated.tsx +++ b/packages/desktop/src/renderer/components/CreateUsername/UsernameCreated/UsernameCreated.tsx @@ -18,37 +18,33 @@ const classes = { descConatainer: `${PREFIX}descConatainer`, usernameIcon: `${PREFIX}usernameIcon`, buttonContainer: `${PREFIX}buttonContainer`, - button: `${PREFIX}button` + button: `${PREFIX}button`, } -const StyledGrid = styled(Grid)(( - { - theme - } -) => ({ +const StyledGrid = styled(Grid)(({ theme }) => ({ [`& .${classes.root}`]: {}, [`& .${classes.usernameConatainer}`]: { - marginTop: 24 + marginTop: 24, }, [`& .${classes.infoConatainer}`]: { - marginTop: 24 + marginTop: 24, }, [`& .${classes.descConatainer}`]: { - marginTop: 8 + marginTop: 8, }, [`& .${classes.usernameIcon}`]: { width: 118, height: 118, - justifyContent: 'center' + justifyContent: 'center', }, [`& .${classes.buttonContainer}`]: { marginTop: 23, - paddingBottom: 63 + paddingBottom: 63, }, [`& .${classes.button}`]: { @@ -58,12 +54,12 @@ const StyledGrid = styled(Grid)(( backgroundColor: theme.palette.colors.purple, padding: theme.spacing(2), '&:hover': { - backgroundColor: theme.palette.colors.darkPurple + backgroundColor: theme.palette.colors.darkPurple, }, '&:disabled': { - backgroundColor: theme.palette.colors.gray - } - } + backgroundColor: theme.palette.colors.gray, + }, + }, })) const handleModalClose = (handleClose: () => void, setFormSent: (value: boolean) => void) => { @@ -80,24 +76,10 @@ export const UsernameCreated: React.FC = ({ handleClose, s setFormSent(false) return ( - + - + You created a username diff --git a/packages/desktop/src/renderer/components/Jdenticon/Jdenticon.tsx b/packages/desktop/src/renderer/components/Jdenticon/Jdenticon.tsx index fe9811a7c1..6f43de9849 100644 --- a/packages/desktop/src/renderer/components/Jdenticon/Jdenticon.tsx +++ b/packages/desktop/src/renderer/components/Jdenticon/Jdenticon.tsx @@ -1,4 +1,3 @@ - import React from 'react' import PropTypes from 'prop-types' import * as jdenticon from 'jdenticon/browser' @@ -20,7 +19,7 @@ const Jdenticon = ({ value = 'test', size = '100%' }) => { Jdenticon.propTypes = { size: PropTypes.string, - value: PropTypes.string.isRequired + value: PropTypes.string.isRequired, } export default Jdenticon diff --git a/packages/desktop/src/renderer/components/LoadingPanel/JoiningPanelComponent.tsx b/packages/desktop/src/renderer/components/LoadingPanel/JoiningPanelComponent.tsx index 3d1303a0b1..74119966bf 100644 --- a/packages/desktop/src/renderer/components/LoadingPanel/JoiningPanelComponent.tsx +++ b/packages/desktop/src/renderer/components/LoadingPanel/JoiningPanelComponent.tsx @@ -18,20 +18,20 @@ const classes = { text: `${PREFIX}text`, progressBar: `${PREFIX}progressBar`, progress: `${PREFIX}progress`, - progressBarWrapper: `${PREFIX}progressBarWrapper` + progressBarWrapper: `${PREFIX}progressBarWrapper`, } const StyledGrid = styled(Grid)(({ theme, width }) => ({ [`&.${classes.root}`]: { textAlign: 'center', - width: '100%' + width: '100%', }, [`& .${classes.contentWrapper}`]: { - maxWidth: '450px' + maxWidth: '450px', }, '@keyframes rotate': { from: { transform: 'rotate(0deg)' }, - to: { transform: 'rotate(360deg)' } + to: { transform: 'rotate(360deg)' }, }, [`& .${classes.animatedImage}`]: { width: '120px', @@ -40,23 +40,23 @@ const StyledGrid = styled(Grid)(({ theme, width }) => ({ animationDuration: '8s', animationTimingFunction: 'linear', animationIterationCount: 'infinite', - transition: '2s all' + transition: '2s all', }, [`& .${classes.image}`]: { width: '120px', - height: '115px' + height: '115px', }, [`& .${classes.heading2}`]: { fontSize: '18px', - marginTop: '12px' + marginTop: '12px', }, [`& .${classes.link}`]: { color: theme.palette.colors.blue, cursor: 'pointer', - marginTop: '16px' + marginTop: '16px', }, [`& .${classes.text}`]: { - color: theme.palette.colors.black30 + color: theme.palette.colors.black30, }, [`& .${classes.progressBar}`]: { backgroundColor: theme.palette.colors.veryLightGray, @@ -65,18 +65,18 @@ const StyledGrid = styled(Grid)(({ theme, width }) => ({ position: 'relative', borderRadius: '100px', overflow: 'hidden', - marginBottom: '8px' + marginBottom: '8px', }, [`& .${classes.progress}`]: { backgroundColor: theme.palette.colors.lushSky, width: width, height: '4px', - position: 'relative' + position: 'relative', }, [`& .${classes.progressBarWrapper}`]: { // margin: '16px 0 40px' - margin: '16px 0 20px' - } + margin: '16px 0 20px', + }, })) export interface JoiningPanelComponentProps { @@ -92,32 +92,25 @@ const JoiningPanelComponent: React.FC = ({ handleClose, openUrl, torConnectionInfo, - isOwner + isOwner, }) => { return ( - + + data-testid='joiningPanelComponent' + > Joining now!
- +
{torConnectionInfo.text} @@ -129,9 +122,9 @@ const JoiningPanelComponent: React.FC = ({ This first time might take 30 seconds, 10 minutes, or even longer.

- There's a good reason why it's slow: Quiet stores data on your community’s devices (not - Big Tech’s servers!) and uses the battle-tested privacy tool Tor to protect your - information. Tor is fast once connected, but can take a long time to connect at first. + There's a good reason why it's slow: Quiet stores data on your community’s devices (not Big Tech’s servers!) + and uses the battle-tested privacy tool Tor to protect your information. Tor is fast once connected, but can + take a long time to connect at first. openUrl(Site.MAIN_PAGE)}> diff --git a/packages/desktop/src/renderer/components/LoadingPanel/LoadingPanel.stories.tsx b/packages/desktop/src/renderer/components/LoadingPanel/LoadingPanel.stories.tsx index 9e16101730..2e961c4af4 100644 --- a/packages/desktop/src/renderer/components/LoadingPanel/LoadingPanel.stories.tsx +++ b/packages/desktop/src/renderer/components/LoadingPanel/LoadingPanel.stories.tsx @@ -19,13 +19,13 @@ const JoiningPanelArgs: JoiningPanelComponentProps = { handleClose: function (): void {}, openUrl: () => console.log('OpenURL'), torConnectionInfo: { number: 10, text: 'Fetching' }, - isOwner: false + isOwner: false, } const StartingPanelArgs: StartingPanelComponentProps = { open: true, handleClose: function (): void {}, message: 'Starting Quiet', - torBootstrapInfo: 'Bootstrapped 100% (done)' + torBootstrapInfo: 'Bootstrapped 100% (done)', } JoiningPanel.args = JoiningPanelArgs @@ -34,7 +34,7 @@ StartingPanel.args = StartingPanelArgs const component: ComponentMeta = { title: 'Components/LoadingPanel', decorators: [withTheme], - component: JoiningPanelComponent + component: JoiningPanelComponent, } export default component diff --git a/packages/desktop/src/renderer/components/LoadingPanel/LoadingPanel.tsx b/packages/desktop/src/renderer/components/LoadingPanel/LoadingPanel.tsx index c228f33ae4..39ffc62cd1 100644 --- a/packages/desktop/src/renderer/components/LoadingPanel/LoadingPanel.tsx +++ b/packages/desktop/src/renderer/components/LoadingPanel/LoadingPanel.tsx @@ -3,14 +3,7 @@ import { useDispatch, useSelector } from 'react-redux' import { useModal } from '../../containers/hooks' import { ModalName } from '../../sagas/modals/modals.types' import { socketSelectors } from '../../sagas/socket/socket.selectors' -import { - communities, - publicChannels, - users, - identity, - connection, - network -} from '@quiet/state-manager' +import { communities, publicChannels, users, identity, connection, network } from '@quiet/state-manager' import { modalsActions } from '../../sagas/modals/modals.slice' import { shell } from 'electron' import JoiningPanelComponent from './JoiningPanelComponent' @@ -25,13 +18,9 @@ const LoadingPanel = () => { const isConnected = useSelector(socketSelectors.isConnected) const currentCommunity = useSelector(communities.selectors.currentCommunity) - const isChannelReplicated = Boolean( - useSelector(publicChannels.selectors.publicChannels)?.length > 0 - ) + const isChannelReplicated = Boolean(useSelector(publicChannels.selectors.publicChannels)?.length > 0) - const currentChannelDisplayableMessages = useSelector( - publicChannels.selectors.currentChannelMessagesMergedBySender - ) + const currentChannelDisplayableMessages = useSelector(publicChannels.selectors.currentChannelMessagesMergedBySender) const community = useSelector(communities.selectors.currentCommunity) const owner = Boolean(community?.CA) @@ -65,7 +54,7 @@ const LoadingPanel = () => { const notification = new Notification('Community created!', { body: 'Visit Settings for an invite code you can share.', icon: '../../build' + '/icon.png', - silent: true + silent: true, }) notification.onclick = () => { @@ -82,11 +71,7 @@ const LoadingPanel = () => { if (message === LoadingPanelType.StartingApplication) { return ( - + ) } else { return ( diff --git a/packages/desktop/src/renderer/components/LoadingPanel/StartingPanelComponent.tsx b/packages/desktop/src/renderer/components/LoadingPanel/StartingPanelComponent.tsx index 708db6d85b..902faba10e 100644 --- a/packages/desktop/src/renderer/components/LoadingPanel/StartingPanelComponent.tsx +++ b/packages/desktop/src/renderer/components/LoadingPanel/StartingPanelComponent.tsx @@ -15,34 +15,34 @@ const classes = { link: `${PREFIX}link`, text: `${PREFIX}text`, progressBar: `${PREFIX}progressBar`, - progress: `${PREFIX}progress` + progress: `${PREFIX}progress`, } const StyledGrid = styled(Grid)(({ theme, width }) => ({ [`&.${classes.root}`]: { textAlign: 'center', marginTop: '24px', - width: '100%' + width: '100%', }, [`& .${classes.contentWrapper}`]: { - maxWidth: '320px' + maxWidth: '320px', }, [`& .${classes.image}`]: { width: '95px', height: '95px', - marginBottom: '58px' + marginBottom: '58px', }, [`& .${classes.heading2}`]: { fontSize: '18px', - marginTop: '12px' + marginTop: '12px', }, [`& .${classes.link}`]: { color: theme.palette.colors.blue, cursor: 'pointer', - marginTop: '16px' + marginTop: '16px', }, [`& .${classes.text}`]: { - color: theme.palette.colors.black30 + color: theme.palette.colors.black30, }, [`& .${classes.progressBar}`]: { backgroundColor: theme.palette.colors.veryLightGray, @@ -51,14 +51,14 @@ const StyledGrid = styled(Grid)(({ theme, width }) => ({ position: 'relative', borderRadius: '100px', overflow: 'hidden', - marginBottom: '16px' + marginBottom: '16px', }, [`& .${classes.progress}`]: { backgroundColor: theme.palette.colors.lushSky, width: width, height: '4px', - position: 'relative' - } + position: 'relative', + }, })) export interface StartingPanelComponentProps { @@ -72,31 +72,29 @@ const StartingPanelComponent: React.FC = ({ open, handleClose, message, - torBootstrapInfo + torBootstrapInfo, }) => { const progressNumber = Number(torBootstrapInfo.replace(/\D/g, '')) return ( - - + + - +
{message} - {`Tor ${torBootstrapInfo}`} + {`Tor ${torBootstrapInfo}`}
diff --git a/packages/desktop/src/renderer/components/MathMessage/MathMessageComponent.test.tsx b/packages/desktop/src/renderer/components/MathMessage/MathMessageComponent.test.tsx index 30d1bb2cb6..579358a1bf 100644 --- a/packages/desktop/src/renderer/components/MathMessage/MathMessageComponent.test.tsx +++ b/packages/desktop/src/renderer/components/MathMessage/MathMessageComponent.test.tsx @@ -6,12 +6,7 @@ import { MathMessageComponent } from './MathMessageComponent' describe('MathMessageComponent', () => { it('renders tex', async () => { const result = renderComponent( - {}} - /> + {}} /> ) await act(async () => {}) expect(result.baseElement).toMatchInlineSnapshot(` diff --git a/packages/desktop/src/renderer/components/MathMessage/MathMessageComponent.tsx b/packages/desktop/src/renderer/components/MathMessage/MathMessageComponent.tsx index dee2415c4f..6e9fc3f3dc 100644 --- a/packages/desktop/src/renderer/components/MathMessage/MathMessageComponent.tsx +++ b/packages/desktop/src/renderer/components/MathMessage/MathMessageComponent.tsx @@ -12,24 +12,24 @@ const classes = { pending: `${PREFIX}pending`, message: `${PREFIX}message`, beginning: `${PREFIX}beginning`, - middle: `${PREFIX}middle` + middle: `${PREFIX}middle`, } const StyledMath = styled('span')(() => ({ [`&.${classes.message}`]: { - marginLeft: '20px' + marginLeft: '20px', }, [`&.${classes.pending}`]: { - color: theme.palette.colors.lightGray + color: theme.palette.colors.lightGray, }, [`&.${classes.middle}`]: { - margin: '0 5px 0 5px' + margin: '0 5px 0 5px', }, [`&.${classes.beginning}`]: { - margin: '0 5px 0 0' + margin: '0 5px 0 0', }, })) @@ -47,7 +47,7 @@ const MathComponent: React.FC = ({ messageId, pending, openUrl, - index + index, }) => { const [renderedHTML, setRenderedHTML] = React.useState(null) const [node, setNode] = React.useState(null) @@ -83,17 +83,19 @@ const MathComponent: React.FC = ({ [classes.message]: true, [classes.pending]: pending, [classes.beginning]: index === 0, - [classes.middle]: index !== 0 + [classes.middle]: index !== 0, } return React.createElement(StyledMath, { className: classNames(className), ...props }) } - return + return ( + + ) } interface MathMessageProps { @@ -107,7 +109,7 @@ export const MathMessageComponent: React.FC { // Split message into regular text and math parts let texMessageSplit: string[] @@ -119,7 +121,8 @@ export const MathMessageComponent: React.FC{texMessageSplit.map((partialMessage, index) => + <> + {texMessageSplit.map((partialMessage, index) => ( - )} + ))} ) } diff --git a/packages/desktop/src/renderer/components/MathMessage/customMathJax.ts b/packages/desktop/src/renderer/components/MathMessage/customMathJax.ts index 315bf1e0cb..165e901796 100644 --- a/packages/desktop/src/renderer/components/MathMessage/customMathJax.ts +++ b/packages/desktop/src/renderer/components/MathMessage/customMathJax.ts @@ -9,7 +9,7 @@ import { STATE } from 'mathjax-full/js/core/MathItem' export const enum SourceLang { MathML = 'MathML', - Tex = 'TeX' + Tex = 'TeX', } // TODO import from mathjax-full @@ -48,9 +48,7 @@ function onError(math: any) { const { root, typesetRoot } = math if (root.toString().substr(0, 14) === 'math([merror([') { const merror = root.childNodes[0].childNodes[0] - const text = - merror.attributes.get('data-mjx-error') || - merror.childNodes[0].childNodes[0].getText() + const text = merror.attributes.get('data-mjx-error') || merror.childNodes[0].childNodes[0].getText() adaptor.setAttribute(typesetRoot, 'data-mjx-error', text) } } @@ -69,11 +67,7 @@ function updateCSS(nodeID: string, text: string) { * Does a single convert call to MathJax. Tex from inputText is converted and * options are the MathJax options */ -export function convert( - srcSpec: SourceSpecification, - node: HTMLElement, - display: boolean -): string { +export function convert(srcSpec: SourceSpecification, node: HTMLElement, display: boolean): string { const { src, lang } = srcSpec let html = texHtml if (lang === 'MathML') html = mathmlHtml @@ -127,7 +121,7 @@ export function convertPromise( } return adaptor.outerHTML(dom) }) - .catch((err) => { + .catch(err => { if (!(err instanceof CancelationException)) { throw err } else { @@ -135,5 +129,5 @@ export function convertPromise( return null } }) - return { promise: res.then((v) => (v || '')), cancel } + return { promise: res.then(v => v || ''), cancel } } diff --git a/packages/desktop/src/renderer/components/SaveState/SaveStateComponent.tsx b/packages/desktop/src/renderer/components/SaveState/SaveStateComponent.tsx index b9418d9fd4..70493c9cd3 100644 --- a/packages/desktop/src/renderer/components/SaveState/SaveStateComponent.tsx +++ b/packages/desktop/src/renderer/components/SaveState/SaveStateComponent.tsx @@ -7,9 +7,16 @@ interface SaveStateComponentProps { // Only for testing purposes export const SaveStateComponent: React.FC = ({ persistor }) => { - return
{ - await persistor.flush() - const element = document.getElementById('save-state-button') - element?.setAttribute('data-is-saved', 'true') - }}/> + return ( +
{ + await persistor.flush() + const element = document.getElementById('save-state-button') + element?.setAttribute('data-is-saved', 'true') + }} + /> + ) } diff --git a/packages/desktop/src/renderer/components/SearchModal/ChannelItem.tsx b/packages/desktop/src/renderer/components/SearchModal/ChannelItem.tsx index ac305a4566..45807f76e9 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 = ({ className, classNameSelected, onClickHandler, - channelInput + channelInput, }: ChannelItemProps) => { const [_initialRender, setInitialRender] = useState(false) @@ -37,12 +37,13 @@ const ChannelItem = ({
{ 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 0cbe13c397..775fea5284 100644 --- a/packages/desktop/src/renderer/components/SearchModal/SearchModal.stories.tsx +++ b/packages/desktop/src/renderer/components/SearchModal/SearchModal.stories.tsx @@ -13,20 +13,55 @@ const args: SearchModalComponentProps = { open: true, dynamicSearchedChannelsSelector: [ { name: 'fun', id: 'fun', messages: { ids: [], entities: {} }, description: '', owner: '', timestamp: 123123 }, - { name: 'mobile', id: 'mobile', messages: { ids: [], entities: {} }, description: '', owner: '', timestamp: 123123 }, - { name: 'new-york-plans', id: 'new-york-plans', messages: { ids: [], entities: {} }, description: '', owner: '', timestamp: 123123 } + { + name: 'mobile', + id: 'mobile', + messages: { ids: [], entities: {} }, + description: '', + owner: '', + timestamp: 123123, + }, + { + name: 'new-york-plans', + id: 'new-york-plans', + messages: { ids: [], entities: {} }, + description: '', + owner: '', + timestamp: 123123, + }, ], publicChannelsSelector: [ { name: 'fun', id: 'fun', messages: { ids: [], entities: {} }, description: '', owner: '', timestamp: 123123 }, - { name: 'mobile', id: 'mobile', messages: { ids: [], entities: {} }, description: '', owner: '', timestamp: 123123 }, - { name: 'new-york-plans', id: 'new-york-plans', messages: { ids: [], entities: {} }, description: '', owner: '', timestamp: 123123 }, - { name: 'general', id: 'general', messages: { ids: [], entities: {} }, description: '', owner: '', timestamp: 123123 } + { + name: 'mobile', + id: 'mobile', + messages: { ids: [], entities: {} }, + description: '', + owner: '', + timestamp: 123123, + }, + { + name: 'new-york-plans', + id: 'new-york-plans', + messages: { ids: [], entities: {} }, + description: '', + owner: '', + timestamp: 123123, + }, + { + name: 'general', + id: 'general', + messages: { ids: [], entities: {} }, + description: '', + owner: '', + timestamp: 123123, + }, ], unreadChannelsSelector: [], channelInput: '', handleClose: () => {}, setCurrentChannel: () => {}, - setChannelInput: () => {} + setChannelInput: () => {}, } Component.args = args @@ -34,7 +69,7 @@ Component.args = args const component: ComponentMeta = { title: 'Components/SearchModal', decorators: [withTheme], - component: SearchModalComponent + component: SearchModalComponent, } export default component diff --git a/packages/desktop/src/renderer/components/SearchModal/SearchModal.test.tsx b/packages/desktop/src/renderer/components/SearchModal/SearchModal.test.tsx index 27c981ea37..2b46f0c64d 100644 --- a/packages/desktop/src/renderer/components/SearchModal/SearchModal.test.tsx +++ b/packages/desktop/src/renderer/components/SearchModal/SearchModal.test.tsx @@ -24,39 +24,35 @@ describe('Search Modal', () => { const factory = await getFactory(store) - const community = await factory.create< - ReturnType['payload'] - >('Community') + const community = await factory.create['payload']>( + 'Community' + ) - const alice = await factory.create< - ReturnType['payload'] - >('Identity', { id: community.id, nickname: 'alice' }) + const alice = await factory.create['payload']>('Identity', { + id: community.id, + nickname: 'alice', + }) const channelsMocks = [ { name: 'fun', timestamp: 1673857606990 }, { name: 'random', timestamp: 1673854900410 }, { name: 'test', timestamp: 1673623514097 }, - { name: 'general', timestamp: 1673623514 } + { name: 'general', timestamp: 1673623514 }, ] for (const channelMock of channelsMocks) { - await factory.create['payload']>( - 'PublicChannel', - { - channel: { - name: channelMock.name, - description: `Welcome to #${channelMock.name}`, - timestamp: channelMock.timestamp, - owner: alice.nickname, - id: generateChannelId(channelMock.name) - } - } - ) + await factory.create['payload']>('PublicChannel', { + channel: { + name: channelMock.name, + description: `Welcome to #${channelMock.name}`, + timestamp: channelMock.timestamp, + owner: alice.nickname, + id: generateChannelId(channelMock.name), + }, + }) } - const dynamicSearchedChannels = publicChannels.selectors.dynamicSearchedChannels('')( - store.getState() - ) + const dynamicSearchedChannels = publicChannels.selectors.dynamicSearchedChannels('')(store.getState()) const publicChannelsSelector = publicChannels.selectors.publicChannels(store.getState()) const result = renderComponent( diff --git a/packages/desktop/src/renderer/components/SearchModal/SearchModal.tsx b/packages/desktop/src/renderer/components/SearchModal/SearchModal.tsx index aa9410cfbe..906752382c 100644 --- a/packages/desktop/src/renderer/components/SearchModal/SearchModal.tsx +++ b/packages/desktop/src/renderer/components/SearchModal/SearchModal.tsx @@ -12,9 +12,7 @@ const SearchModal = () => { const searchChannelModal = useModal(ModalName.searchChannelModal) - const dynamicSearchedChannelsSelector = useSelector( - publicChannels.selectors.dynamicSearchedChannels(channelInput) - ) + const dynamicSearchedChannelsSelector = useSelector(publicChannels.selectors.dynamicSearchedChannels(channelInput)) const unreadChannelsSelector = useSelector(publicChannels.selectors.unreadChannels) @@ -24,7 +22,7 @@ const SearchModal = () => { (id: string) => { dispatch( publicChannels.actions.setCurrentChannel({ - channelId: id + channelId: id, }) ) searchChannelModal.handleClose() @@ -32,22 +30,25 @@ const SearchModal = () => { [dispatch] ) - const handleKeyDown = useCallback<(evt: KeyboardEvent) => void>(evt => { - if ((evt.metaKey || evt.ctrlKey) && evt.key === 'k') { - evt.preventDefault() - dispatch(navigationActions.closeAllMenus()) - searchChannelModal.handleOpen() - } - if ((evt.metaKey || evt.ctrlKey) && evt.key === 't') { - evt.preventDefault() - dispatch(navigationActions.closeAllMenus()) - searchChannelModal.handleOpen() - } - if (evt.key === 'Escape') { - evt.preventDefault() - searchChannelModal.handleClose() - } - }, [dispatch]) + const handleKeyDown = useCallback<(evt: KeyboardEvent) => void>( + evt => { + if ((evt.metaKey || evt.ctrlKey) && evt.key === 'k') { + evt.preventDefault() + dispatch(navigationActions.closeAllMenus()) + searchChannelModal.handleOpen() + } + if ((evt.metaKey || evt.ctrlKey) && evt.key === 't') { + evt.preventDefault() + dispatch(navigationActions.closeAllMenus()) + searchChannelModal.handleOpen() + } + if (evt.key === 'Escape') { + evt.preventDefault() + searchChannelModal.handleClose() + } + }, + [dispatch] + ) useEffect(() => { document.addEventListener('keydown', handleKeyDown, false) diff --git a/packages/desktop/src/renderer/components/SearchModal/SearchModelComponent.tsx b/packages/desktop/src/renderer/components/SearchModal/SearchModelComponent.tsx index bdea7d29bb..66999b1c9b 100644 --- a/packages/desktop/src/renderer/components/SearchModal/SearchModelComponent.tsx +++ b/packages/desktop/src/renderer/components/SearchModal/SearchModelComponent.tsx @@ -30,14 +30,14 @@ const classes = { wrapperRecent: `${PREFIX}wrapperRecent`, channelWrapper: `${PREFIX}channelWrapper`, channelWrapperSelected: `${PREFIX}channelWrapperSelected`, - scrollContainer: `${PREFIX}scrollContainer` + scrollContainer: `${PREFIX}scrollContainer`, } const StyledModalContent = styled(Grid)(({ theme }) => ({ [`& .${classes.root}`]: {}, [`& .${classes.overlay}`]: { width: '100%', - height: '100%' + height: '100%', }, [`& .${classes.modalContainer}`]: { backgroundColor: '#FFFFFF', @@ -45,30 +45,30 @@ const StyledModalContent = styled(Grid)(({ theme }) => ({ borderRadius: '8px', width: '60%', overflow: 'hidden', - minHeight: '255px' + minHeight: '255px', }, [`& .${classes.wrapper}`]: { - padding: '24px' + padding: '24px', }, [`& .${classes.wrapperRecent}`]: { - padding: '16px 24px 8px' + padding: '16px 24px 8px', }, [`& .${classes.magnifyingGlassIcon}`]: { width: 18, heigth: 18, justifyContent: 'center', - marginRight: '16px' + marginRight: '16px', }, [`& .${classes.closeIcon}`]: { width: 14, heigth: 14, justifyContent: 'center', - cursor: 'pointer' + cursor: 'pointer', }, [`& .${classes.line}`]: { width: '100%', height: '1px', - backgroundColor: theme.palette.colors.veryLightGray + backgroundColor: theme.palette.colors.veryLightGray, }, [`& .${classes.channel}`]: {}, [`& .${classes.channelWrapper}`]: { @@ -77,28 +77,28 @@ const StyledModalContent = styled(Grid)(({ theme }) => ({ padding: '8px 24px', '&:hover': { backgroundColor: theme.palette.colors.lushSky, - color: 'white' + color: 'white', }, '&:focus': { backgroundColor: theme.palette.colors.lushSky, - color: 'white' + color: 'white', }, '&:focus-visible': { - outline: '0' - } + outline: '0', + }, }, [`& .${classes.channelWrapperSelected}`]: { backgroundColor: theme.palette.colors.lushSky, color: 'white', '&:focus-visible': { - outline: '0' - } + outline: '0', + }, }, [`& .${classes.recentChannels}`]: { - color: '#7F7F7F' + color: '#7F7F7F', }, [`& .${classes.inputWrapper}`]: { - display: 'flex' + display: 'flex', }, [`& .${classes.scrollContainer}`]: { // I will back to this idea @@ -112,21 +112,21 @@ const StyledModalContent = styled(Grid)(({ theme }) => ({ '&:hover': { border: 'none', '&::before': { - border: 'none' - } + border: 'none', + }, }, '&::before': { - border: 'none' + border: 'none', }, '&::after': { - border: 'none' - } - } - } + border: 'none', + }, + }, + }, })) const searchChannelFields = { - searchChannel: searchChannelField() + searchChannel: searchChannelField(), } export interface SearchModalComponentProps { @@ -148,22 +148,19 @@ const SearchModalComponent: React.FC = ({ dynamicSearchedChannelsSelector, unreadChannelsSelector, publicChannelsSelector, - channelInput + channelInput, }) => { const { - formState: { errors } + formState: { errors }, } = useForm<{ searchChannel: string }>({ - mode: 'onChange' + mode: 'onChange', }) - const unreadChannels = publicChannelsSelector.filter(channel => - unreadChannelsSelector.includes(channel.name) - ) + const unreadChannels = publicChannelsSelector.filter(channel => unreadChannelsSelector.includes(channel.name)) const unread = unreadChannels.length > 0 - const channelList = - unread && channelInput.length === 0 ? unreadChannels : dynamicSearchedChannelsSelector + const channelList = unread && channelInput.length === 0 ? unreadChannels : dynamicSearchedChannelsSelector const onChannelClickHandler = (id: string) => { setChannelInput('') @@ -171,11 +168,7 @@ const SearchModalComponent: React.FC = ({ setCurrentChannel(id) } - const [focusedIndex, setCurrentFocus] = useCyclingFocus( - channelList.length, - Variant.ARROWS_KEYS, - 0 - ) + const [focusedIndex, setCurrentFocus] = useCyclingFocus(channelList.length, Variant.ARROWS_KEYS, 0) const onChange = (value: string) => { setChannelInput(value) @@ -193,25 +186,13 @@ const SearchModalComponent: React.FC = ({ handleClose={closeHandler} data-testid={'searchChannelModal'} contentWidth={'100wh'} - isTransparent={true}> + isTransparent={true} + > - - - + + + = ({ /> - + @@ -252,9 +228,7 @@ const SearchModalComponent: React.FC = ({ )} - 3 ? classes.scrollContainer : ''}> + 3 ? classes.scrollContainer : ''}> {channelList.length > 0 && channelList.map((item, index) => { return ( diff --git a/packages/desktop/src/renderer/components/Settings/Settings.stories.tsx b/packages/desktop/src/renderer/components/Settings/Settings.stories.tsx index 1149e70295..8995370b1e 100644 --- a/packages/desktop/src/renderer/components/Settings/Settings.stories.tsx +++ b/packages/desktop/src/renderer/components/Settings/Settings.stories.tsx @@ -37,9 +37,7 @@ const Invite: FC = () => { const [revealInputValue, setRevealInputValue] = useState(false) return ( { setRevealInputValue(!revealInputValue) @@ -49,9 +47,7 @@ const Invite: FC = () => { } const QRCode: FC = () => { - return ( - - ) + return } const args: SettingsComponentProps = { @@ -63,25 +59,25 @@ const args: SettingsComponentProps = { notifications: Dummy, invite: Invite, leave: Leave, - qrcode: QRCode + qrcode: QRCode, }, leaveCommunityModal: { open: false, handleOpen: function (_args?: any): any {}, - handleClose: function (): any {} - } + handleClose: function (): any {}, + }, } Component.args = args WindowsComponent.args = { ...args, - isWindows: true + isWindows: true, } const component: ComponentMeta = { title: 'Components/Settings', decorators: [withTheme], - component: SettingsComponent + component: SettingsComponent, } export default component diff --git a/packages/desktop/src/renderer/components/Settings/Settings.tsx b/packages/desktop/src/renderer/components/Settings/Settings.tsx index 468ca1cfbc..e21de55790 100644 --- a/packages/desktop/src/renderer/components/Settings/Settings.tsx +++ b/packages/desktop/src/renderer/components/Settings/Settings.tsx @@ -24,7 +24,7 @@ const Settings = () => { about: About, notifications: Notifications, invite: Invite, - qrcode: QRCode + qrcode: QRCode, } const leaveCommunityModal = useModal(ModalName.leaveCommunity) diff --git a/packages/desktop/src/renderer/components/Settings/SettingsComponent.tsx b/packages/desktop/src/renderer/components/Settings/SettingsComponent.tsx index 1e6164b0b2..63b376467e 100644 --- a/packages/desktop/src/renderer/components/Settings/SettingsComponent.tsx +++ b/packages/desktop/src/renderer/components/Settings/SettingsComponent.tsx @@ -16,30 +16,30 @@ const PREFIX = 'SettingsModal' const classes = { indicator: `${PREFIX}indicator`, - leaveComunity: `${PREFIX}leaveCommunity` + leaveComunity: `${PREFIX}leaveCommunity`, } const StyledModalContent = styled(Grid)(() => ({ zIndex: 9002, paddingLeft: 20, paddingTop: 32, - paddingRight: 32 + paddingRight: 32, })) const StyledTabsWrapper = styled(Grid)(() => ({ - width: 168 + width: 168, })) const StyledAppBar = styled(AppBar, { label: 'xxxxx' })(() => ({ backgroundColor: '#fff', - boxShadow: '0px 1px 3px rgba(0, 0, 0, 0.0)' + boxShadow: '0px 1px 3px rgba(0, 0, 0, 0.0)', })) const StyledTabs = styled(Tabs)(({ theme }) => ({ color: theme.palette.colors.trueBlack, [`& .${classes.indicator}`]: { - height: '0 !important' + height: '0 !important', }, [`& .${classes.leaveComunity}`]: { @@ -51,12 +51,12 @@ const StyledTabs = styled(Tabs)(({ theme }) => ({ alignItems: 'flex-start', textTransform: 'none', lineHeight: '21px', - minHeight: '0px' - } + minHeight: '0px', + }, })) const TabComponentWrapper = styled(Grid)(() => ({ - marginLeft: 32 + marginLeft: 32, })) export interface SettingsComponentProps { @@ -74,7 +74,7 @@ export const SettingsComponent: React.FC = ({ isOwner, tabs, leaveCommunityModal, - isWindows + isWindows, }) => { const [contentRef, setContentRef] = React.useState(null) @@ -113,7 +113,8 @@ export const SettingsComponent: React.FC = ({ testIdPrefix='settings' isBold addBorder - contentWidth='100%'> + contentWidth='100%' + > { if (ref) { @@ -121,7 +122,8 @@ export const SettingsComponent: React.FC = ({ } }} container - direction='row'> + direction='row' + > = ({ }} orientation='vertical' textColor='inherit' - classes={{ indicator: classes.indicator }}> + classes={{ indicator: classes.indicator }} + > - + {!isWindows && ( @@ -146,7 +145,8 @@ export const SettingsComponent: React.FC = ({ + onClick={leaveCommunityModal.handleOpen} + > Leave community @@ -162,7 +162,8 @@ export const SettingsComponent: React.FC = ({ + style={{ width: maxWidth + offset, height: height }} + > diff --git a/packages/desktop/src/renderer/components/Settings/Tabs/Invite/Invite.component.test.tsx b/packages/desktop/src/renderer/components/Settings/Tabs/Invite/Invite.component.test.tsx index 41014ec314..6c04f3f0a2 100644 --- a/packages/desktop/src/renderer/components/Settings/Tabs/Invite/Invite.component.test.tsx +++ b/packages/desktop/src/renderer/components/Settings/Tabs/Invite/Invite.component.test.tsx @@ -7,9 +7,7 @@ describe('CopyLink', () => { it('renderComponent- long link', () => { const result = renderComponent( @@ -113,9 +111,7 @@ describe('CopyLink', () => { it('renderComponent - short link', () => { const result = renderComponent( diff --git a/packages/desktop/src/renderer/components/Settings/Tabs/Invite/Invite.component.tsx b/packages/desktop/src/renderer/components/Settings/Tabs/Invite/Invite.component.tsx index 99cd8a3b75..4eef2bb460 100644 --- a/packages/desktop/src/renderer/components/Settings/Tabs/Invite/Invite.component.tsx +++ b/packages/desktop/src/renderer/components/Settings/Tabs/Invite/Invite.component.tsx @@ -18,23 +18,23 @@ const classes = { bold: `${PREFIX}bold`, linkContainer: `${PREFIX}linkContainer`, eyeIcon: `${PREFIX}eyeIcon`, - wrapper: `${PREFIX}wrapper` + wrapper: `${PREFIX}wrapper`, } const StyledGrid = styled(Grid)(({ theme }) => ({ [`& .${classes.title}`]: {}, [`& .${classes.wrapper}`]: { - maxWidth: '485px' + maxWidth: '485px', }, [`& .${classes.titleDiv}`]: { - marginBottom: 24 + marginBottom: 24, }, [`& .${classes.link}`]: { marginTop: '16px', fontSize: '13px', letterSpacing: '-0.4px', overflowWrap: 'break-word', - inlineSize: '430px' + inlineSize: '430px', }, [`& .${classes.button}`]: { marginTop: 24, @@ -45,11 +45,11 @@ const StyledGrid = styled(Grid)(({ theme }) => ({ backgroundColor: theme.palette.colors.quietBlue, '&:hover': { opacity: 0.7, - backgroundColor: theme.palette.colors.quietBlue - } + backgroundColor: theme.palette.colors.quietBlue, + }, }, [`& .${classes.bold}`]: { - fontWeight: 'bold' + fontWeight: 'bold', }, [`& .${classes.linkContainer}`]: { @@ -61,15 +61,15 @@ const StyledGrid = styled(Grid)(({ theme }) => ({ alignContent: 'stretch', maxWidth: '485px', height: '48px', - position: 'relative' + position: 'relative', }, [`& .${classes.eyeIcon}`]: { margin: '5px', top: '8px', position: 'absolute', - right: '0' - } + right: '0', + }, })) export interface InviteComponentProps { @@ -81,16 +81,11 @@ export interface InviteComponentProps { export const InviteComponent: FC = ({ invitationLink, revealInputValue, - handleClickInputReveal + handleClickInputReveal, }) => { return ( - + Invite a friend @@ -114,7 +109,8 @@ export const InviteComponent: FC = ({ data-testid='show-invitation-link' size='small' onClick={handleClickInputReveal} - className={classes.eyeIcon}> + className={classes.eyeIcon} + > {!revealInputValue ? ( ) : ( diff --git a/packages/desktop/src/renderer/components/Settings/Tabs/Invite/Invite.stories.tsx b/packages/desktop/src/renderer/components/Settings/Tabs/Invite/Invite.stories.tsx index ec873914b7..df11ce8e22 100644 --- a/packages/desktop/src/renderer/components/Settings/Tabs/Invite/Invite.stories.tsx +++ b/packages/desktop/src/renderer/components/Settings/Tabs/Invite/Invite.stories.tsx @@ -10,12 +10,11 @@ const Template: ComponentStory = args => { export const Component = Template.bind({}) let revealInputValue = true const args: InviteComponentProps = { - invitationLink: - 'https://tryquiet.org/join#p7lrosb6fvtt7t3fhmuh5uj5twxirpngeipemdm5d32shgz46cbd3bad', + invitationLink: 'https://tryquiet.org/join#p7lrosb6fvtt7t3fhmuh5uj5twxirpngeipemdm5d32shgz46cbd3bad', revealInputValue: revealInputValue, handleClickInputReveal: () => { revealInputValue = !revealInputValue - } + }, } Component.args = args @@ -23,7 +22,7 @@ Component.args = args const component: ComponentMeta = { title: 'Components/InviteFriend', decorators: [withTheme], - component: InviteComponent + component: InviteComponent, } export default component diff --git a/packages/desktop/src/renderer/components/Settings/Tabs/LeaveCommunity/LeaveCommunity.stories.tsx b/packages/desktop/src/renderer/components/Settings/Tabs/LeaveCommunity/LeaveCommunity.stories.tsx index 425db5b118..4319d4a937 100644 --- a/packages/desktop/src/renderer/components/Settings/Tabs/LeaveCommunity/LeaveCommunity.stories.tsx +++ b/packages/desktop/src/renderer/components/Settings/Tabs/LeaveCommunity/LeaveCommunity.stories.tsx @@ -12,9 +12,9 @@ export const Component = Template.bind({}) const args: LeaveCommunityProps = { communityName: 'Rockets', - leaveCommunity: function (): void { }, + leaveCommunity: function (): void {}, open: true, - handleClose: function (): void { } + handleClose: function (): void {}, } Component.args = args @@ -22,7 +22,7 @@ Component.args = args const component: ComponentMeta = { title: 'Components/LeaveCommunity', decorators: [withTheme], - component: LeaveCommunityComponent + component: LeaveCommunityComponent, } export default component diff --git a/packages/desktop/src/renderer/components/Settings/Tabs/LeaveCommunity/LeaveCommunityComponent.tsx b/packages/desktop/src/renderer/components/Settings/Tabs/LeaveCommunity/LeaveCommunityComponent.tsx index c3c1cd9400..ecd62b4931 100644 --- a/packages/desktop/src/renderer/components/Settings/Tabs/LeaveCommunity/LeaveCommunityComponent.tsx +++ b/packages/desktop/src/renderer/components/Settings/Tabs/LeaveCommunity/LeaveCommunityComponent.tsx @@ -18,7 +18,7 @@ const classes = { buttonContainer: `${PREFIX}buttonContainer`, button: `${PREFIX}button`, secondaryButtonContainer: `${PREFIX}secondaryButtonContainer`, - secondaryButton: `${PREFIX}secondaryButton` + secondaryButton: `${PREFIX}secondaryButton`, } const StyledGrid = styled(Grid)(({ theme }) => ({ @@ -27,22 +27,22 @@ const StyledGrid = styled(Grid)(({ theme }) => ({ [`& .${classes.root}`]: {}, [`& .${classes.titleContainer}`]: { - marginTop: 16 + marginTop: 16, }, [`& .${classes.descContainer}`]: { marginTop: 8, marginLeft: 32, marginRight: 32, - width: 100 + width: 100, }, [`& .${classes.iconContainer}`]: { - marginTop: 0 + marginTop: 0, }, [`& .${classes.buttonContainer}`]: { - marginTop: 25 + marginTop: 25, }, [`& .${classes.button}`]: { @@ -52,16 +52,16 @@ const StyledGrid = styled(Grid)(({ theme }) => ({ backgroundColor: theme.palette.colors.purple, padding: theme.spacing(2), '&:hover': { - backgroundColor: theme.palette.colors.darkPurple + backgroundColor: theme.palette.colors.darkPurple, }, '&:disabled': { - backgroundColor: theme.palette.colors.gray - } + backgroundColor: theme.palette.colors.gray, + }, }, [`& .${classes.secondaryButtonContainer}`]: { marginTop: 16, - marginBottom: 32 + marginBottom: 32, }, [`& .${classes.secondaryButton}`]: { @@ -73,9 +73,9 @@ const StyledGrid = styled(Grid)(({ theme }) => ({ '&:hover': { boxShadow: 'none', cursor: 'pointer', - backgroundColor: theme.palette.colors.white - } - } + backgroundColor: theme.palette.colors.white, + }, + }, })) export interface LeaveCommunityProps { @@ -89,40 +89,22 @@ export const LeaveCommunityComponent: FC = ({ communityName, leaveCommunity, open, - handleClose + handleClose, }) => { return ( - + Are you sure you want to leave? - + - Your account, messages, and all data for{' '} - {communityName} will be deleted from this - device. This cannot be undone. + Your account, messages, and all data for {communityName} will be + deleted from this device. This cannot be undone. - @@ -132,13 +114,9 @@ export const LeaveCommunityComponent: FC = ({ className={classes.secondaryButtonContainer} xs={12} direction='row' - justifyContent='center'> - diff --git a/packages/desktop/src/renderer/components/Settings/Tabs/Notifications/Notifications.test.tsx b/packages/desktop/src/renderer/components/Settings/Tabs/Notifications/Notifications.test.tsx index b94506d527..5f9915e0f3 100644 --- a/packages/desktop/src/renderer/components/Settings/Tabs/Notifications/Notifications.test.tsx +++ b/packages/desktop/src/renderer/components/Settings/Tabs/Notifications/Notifications.test.tsx @@ -11,7 +11,7 @@ describe('Notifications', () => { notificationsOption: NotificationsOptions.notifyForEveryMessage, notificationsSound: NotificationsSounds.bang, setNotificationsOption: jest.fn(), - setNotificationsSound: jest.fn() + setNotificationsSound: jest.fn(), } const result = renderComponent() expect(result.baseElement).toMatchInlineSnapshot(` @@ -318,12 +318,10 @@ describe('Notifications', () => { notificationsOption: NotificationsOptions.notifyForEveryMessage, notificationsSound: NotificationsSounds.bang, setNotificationsOption: jest.fn(), - setNotificationsSound: jest.fn() + setNotificationsSound: jest.fn(), } renderComponent() - const sounds = Object.values(NotificationsSounds).filter( - sound => sound !== NotificationsSounds.none - ) + const sounds = Object.values(NotificationsSounds).filter(sound => sound !== NotificationsSounds.none) for (const sound of sounds) { const soundRadioButton = screen.getByTestId(`sound-${sound}-radio`) await userEvent.click(soundRadioButton) diff --git a/packages/desktop/src/renderer/components/Settings/Tabs/Notifications/Notifications.tsx b/packages/desktop/src/renderer/components/Settings/Tabs/Notifications/Notifications.tsx index e65582adc0..84f4ee7e71 100644 --- a/packages/desktop/src/renderer/components/Settings/Tabs/Notifications/Notifications.tsx +++ b/packages/desktop/src/renderer/components/Settings/Tabs/Notifications/Notifications.tsx @@ -13,29 +13,40 @@ interface useNotificationsDataReturnType { export const useNotificationsData = (): useNotificationsDataReturnType => { const data = { notificationsOption: useSelector(settings.selectors.getNotificationsOption), - notificationsSound: useSelector(settings.selectors.getNotificationsSound) + notificationsSound: useSelector(settings.selectors.getNotificationsSound), } return data } -export const useNotificationsActions = (notificationsOption: NotificationsOptions, notificationsSound: NotificationsSounds) => { +export const useNotificationsActions = ( + notificationsOption: NotificationsOptions, + notificationsSound: NotificationsSounds +) => { const dispatch = useDispatch() - const setNotificationsOption = useCallback((option: NotificationsOptions) => { - dispatch(settings.actions.setNotificationsOption(option)) - }, [dispatch, notificationsOption]) + const setNotificationsOption = useCallback( + (option: NotificationsOptions) => { + dispatch(settings.actions.setNotificationsOption(option)) + }, + [dispatch, notificationsOption] + ) - const setNotificationsSound = useCallback((sound: NotificationsSounds) => { - dispatch(settings.actions.setNotificationsSound(sound)) - }, [dispatch, notificationsSound]) + const setNotificationsSound = useCallback( + (sound: NotificationsSounds) => { + dispatch(settings.actions.setNotificationsSound(sound)) + }, + [dispatch, notificationsSound] + ) return { setNotificationsOption, setNotificationsSound } } export const Notifications: FC = () => { const { notificationsOption, notificationsSound } = useNotificationsData() - const { setNotificationsOption, setNotificationsSound } = - useNotificationsActions(notificationsOption, notificationsSound) + const { setNotificationsOption, setNotificationsSound } = useNotificationsActions( + notificationsOption, + notificationsSound + ) return ( ({ +const StyledGrid = styled(Grid)(({ theme }) => ({ [`& .${classes.title}`]: {}, [`& .${classes.titleDiv}`]: { - marginBottom: 24 + marginBottom: 24, }, [`& .${classes.subtitle}`]: { fontSize: 18, - lineHeight: '27px' + lineHeight: '27px', }, [`& .${classes.radioDiv}`]: { - marginLeft: 4 + marginLeft: 4, }, [`& .${classes.radioSoundDiv}`]: {}, @@ -59,55 +55,55 @@ const StyledGrid = styled(Grid)(( '& .MuiCheckbox-root': { backgroundColor: 'transparent', '&:hover': { - backgroundColor: 'transparent' + backgroundColor: 'transparent', }, - display: 'block' + display: 'block', }, '& .MuiIconButton-colorSecondary': { - color: theme.palette.colors.quietBlue + color: theme.palette.colors.quietBlue, }, '& .MuiTypography-body1': { fontSize: '14px', - lineHeight: '25px' - } + lineHeight: '25px', + }, }, [`& .${classes.bold}`]: { - fontWeight: 500 + fontWeight: 500, }, [`& .${classes.offset}`]: { - marginTop: 5 + marginTop: 5, }, [`& .${classes.spacing}`]: { - marginTop: 16 + marginTop: 16, }, [`& .${classes.radioSound}`]: { '& .MuiCheckbox-root': { backgroundColor: 'transparent', '&:hover': { - backgroundColor: 'transparent' + backgroundColor: 'transparent', }, - display: 'block' + display: 'block', }, marginLeft: 23, - height: 24 + height: 24, }, [`& .${classes.subtitleSoundDiv}`]: { - marginTop: 40 + marginTop: 40, }, [`& .${classes.label}`]: { marginTop: 1, - fontWeight: 500 + fontWeight: 500, }, [`& .${classes.spacingSound}`]: { - marginTop: 8 - } + marginTop: 8, + }, })) interface NotificationsProps { @@ -121,22 +117,16 @@ export const NotificationsComponent: React.FC = ({ notificationsOption, notificationsSound, setNotificationsOption, - setNotificationsSound + setNotificationsSound, }) => { return ( - + Notifications - + Notify me about... @@ -152,9 +142,7 @@ export const NotificationsComponent: React.FC = ({ checked={NotificationsOptions.notifyForEveryMessage === notificationsOption} /> } - onChange={() => - setNotificationsOption(NotificationsOptions.notifyForEveryMessage) - } + onChange={() => setNotificationsOption(NotificationsOptions.notifyForEveryMessage)} label={ @@ -184,9 +172,7 @@ export const NotificationsComponent: React.FC = ({ Nothing - - You won’t receive notifications from Quiet. - + You won’t receive notifications from Quiet. } @@ -197,12 +183,7 @@ export const NotificationsComponent: React.FC = ({ Sound when receiving a notification - + ({ [`& .${classes.codeWrapper}`]: { - marginTop: 16 + marginTop: 16, }, [`& .${classes.textWrapper}`]: { marginTop: 16, - width: 340 - } + width: 340, + }, })) export interface QRCodeProps { @@ -42,8 +42,8 @@ export const QRCodeComponent: FC = ({ value }) => { - This community QR code is private. If it is shared with someone, they can scan it with - their camera to join this community. + This community QR code is private. If it is shared with someone, they can scan it with their camera to + join this community. diff --git a/packages/desktop/src/renderer/components/Settings/Tabs/QRCode/QRCode.stories.tsx b/packages/desktop/src/renderer/components/Settings/Tabs/QRCode/QRCode.stories.tsx index dcf40af281..8268756941 100644 --- a/packages/desktop/src/renderer/components/Settings/Tabs/QRCode/QRCode.stories.tsx +++ b/packages/desktop/src/renderer/components/Settings/Tabs/QRCode/QRCode.stories.tsx @@ -11,7 +11,7 @@ const Template: ComponentStory = args => { export const Component = Template.bind({}) const args: QRCodeProps = { - value: 'https://tryquiet.org/join#ytzoaxku26gobduqogx6ydhezgf6aumpcted27qx7tz6z77lzj2zb6ad' + value: 'https://tryquiet.org/join#ytzoaxku26gobduqogx6ydhezgf6aumpcted27qx7tz6z77lzj2zb6ad', } Component.args = args @@ -19,7 +19,7 @@ Component.args = args const component: ComponentMeta = { title: 'Components/QRCode', decorators: [withTheme], - component: QRCodeComponent + component: QRCodeComponent, } export default component diff --git a/packages/desktop/src/renderer/components/Settings/Tabs/QRCode/QRCode.test.tsx b/packages/desktop/src/renderer/components/Settings/Tabs/QRCode/QRCode.test.tsx index 234d0cdb84..a44c6f92d8 100644 --- a/packages/desktop/src/renderer/components/Settings/Tabs/QRCode/QRCode.test.tsx +++ b/packages/desktop/src/renderer/components/Settings/Tabs/QRCode/QRCode.test.tsx @@ -7,9 +7,7 @@ import QRCodeComponent from './QRCode.component' describe('LeaveCommunity', () => { it('renders component', () => { const result = renderComponent( - + ) expect(result.baseElement).toMatchInlineSnapshot(` diff --git a/packages/desktop/src/renderer/components/Sidebar/ChannelsPanel/ChannelsListItem.tsx b/packages/desktop/src/renderer/components/Sidebar/ChannelsPanel/ChannelsListItem.tsx index 75c2751572..a70902b9ce 100644 --- a/packages/desktop/src/renderer/components/Sidebar/ChannelsPanel/ChannelsListItem.tsx +++ b/packages/desktop/src/renderer/components/Sidebar/ChannelsPanel/ChannelsListItem.tsx @@ -16,23 +16,23 @@ const classes = { connectedIcon: `${PREFIX}connectedIcon`, notConnectedIcon: `${PREFIX}notConnectedIcon`, itemText: `${PREFIX}itemText`, - disabled: `${PREFIX}disabled` + disabled: `${PREFIX}disabled`, } const StyledListItemButton = styled(ListItemButton)(({ theme }) => ({ [`&.${classes.root}`]: { - padding: 0 + padding: 0, }, [`&.${classes.selected}`]: { backgroundColor: theme.palette.colors.lushSky, '&:hover': { - backgroundColor: theme.palette.colors.lushSky - } + backgroundColor: theme.palette.colors.lushSky, + }, }, [`& .${classes.primary}`]: { - display: 'flex' + display: 'flex', }, [`& .${classes.title}`]: { @@ -44,19 +44,19 @@ const StyledListItemButton = styled(ListItemButton)(({ theme }) => ({ textOverflow: 'ellipsis', maxWidth: 215, whiteSpace: 'nowrap', - textTransform: 'lowercase' + textTransform: 'lowercase', }, [`& .${classes.newMessages}`]: { opacity: 1, - fontWeight: 600 + fontWeight: 600, }, [`& .${classes.connectedIcon}`]: { marginLeft: 16, marginRight: -8, width: 11, - height: 11 + height: 11, }, [`& .${classes.notConnectedIcon}`]: { @@ -64,17 +64,17 @@ const StyledListItemButton = styled(ListItemButton)(({ theme }) => ({ marginRight: -8, width: 11, height: 11, - opacity: 0.5 + opacity: 0.5, }, [`& .${classes.itemText}`]: { - margin: 0 + margin: 0, }, [`&.${classes.disabled}`]: { opacity: '0.3', pointerEvents: 'none', - cursor: 'not-allowed' - } + cursor: 'not-allowed', + }, })) export interface ChannelsListItemProps { @@ -90,7 +90,7 @@ export const ChannelsListItem: React.FC = ({ unread, selected, setCurrentChannel, - disabled = false + disabled = false, }) => { const ref = useRef(null) @@ -103,9 +103,10 @@ export const ChannelsListItem: React.FC = ({ }} className={classNames(classes.root, { [classes.selected]: selected, - [classes.disabled]: disabled + [classes.disabled]: disabled, })} - data-testid={`${channel.name}-link`}> + data-testid={`${channel.name}-link`} + > @@ -113,16 +114,17 @@ export const ChannelsListItem: React.FC = ({ + data-testid={`${channel.name}-link-text`} + > {`# ${channel.name}`} } classes={{ - primary: classes.primary + primary: classes.primary, }} className={classes.itemText} /> 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 c59a6b9bc4..fc9f91133b 100644 --- a/packages/desktop/src/renderer/components/Sidebar/ChannelsPanel/ChannelsPanel.test.tsx +++ b/packages/desktop/src/renderer/components/Sidebar/ChannelsPanel/ChannelsPanel.test.tsx @@ -24,31 +24,29 @@ describe('Channels panel', () => { const factory = await getFactory(store) - const community = await factory.create< - ReturnType['payload'] - >('Community') + const community = await factory.create['payload']>( + 'Community' + ) const generalChannel = publicChannels.selectors.generalChannel(store.getState()) expect(generalChannel).not.toBeUndefined() - const alice = await factory.create< - ReturnType['payload'] - >('Identity', { id: community.id, nickname: 'alice' }) + const alice = await factory.create['payload']>('Identity', { + id: community.id, + nickname: 'alice', + }) // 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, - id: generateChannelId(name) - } - } - ) + await factory.create['payload']>('PublicChannel', { + channel: { + name: name, + description: `Welcome to #${name}`, + timestamp: DateTime.utc().valueOf(), + owner: alice.nickname, + id: generateChannelId(name), + }, + }) } const channels = publicChannels.selectors.publicChannels(store.getState()) @@ -63,7 +61,7 @@ describe('Channels panel', () => { createChannelModal={{ open: false, handleOpen: function (_args?: any): any {}, - handleClose: function (): any {} + handleClose: function (): any {}, }} /> ) diff --git a/packages/desktop/src/renderer/components/Sidebar/ChannelsPanel/ChannelsPanel.tsx b/packages/desktop/src/renderer/components/Sidebar/ChannelsPanel/ChannelsPanel.tsx index 5b1809616e..d0b7c27801 100644 --- a/packages/desktop/src/renderer/components/Sidebar/ChannelsPanel/ChannelsPanel.tsx +++ b/packages/desktop/src/renderer/components/Sidebar/ChannelsPanel/ChannelsPanel.tsx @@ -19,7 +19,7 @@ const ChannelsPanel: React.FC = ({ unreadChannels, setCurrentChannel, currentChannelId, - createChannelModal + createChannelModal, }) => { return ( diff --git a/packages/desktop/src/renderer/components/Sidebar/IdentityPanel/IdentityPanel.test.tsx b/packages/desktop/src/renderer/components/Sidebar/IdentityPanel/IdentityPanel.test.tsx index 3379a1e508..10afcc0fcb 100644 --- a/packages/desktop/src/renderer/components/Sidebar/IdentityPanel/IdentityPanel.test.tsx +++ b/packages/desktop/src/renderer/components/Sidebar/IdentityPanel/IdentityPanel.test.tsx @@ -21,7 +21,7 @@ describe('IdentityPanel', () => { accountSettingsModal={{ open: false, handleOpen: function (_args?: any): any {}, - handleClose: function (): any {} + handleClose: function (): any {}, }} /> ) @@ -71,7 +71,7 @@ describe('IdentityPanel', () => { accountSettingsModal={{ open: false, handleOpen: function (_args?: any): any {}, - handleClose: function (): any {} + handleClose: function (): any {}, }} /> ) @@ -116,9 +116,7 @@ describe('IdentityPanel', () => { const factory = await getFactory(store) - const community: Community = ( - await factory.build('Community') - ).payload + const community: Community = (await factory.build('Community')).payload community.name = undefined @@ -128,7 +126,7 @@ describe('IdentityPanel', () => { accountSettingsModal={{ open: false, handleOpen: function (_args?: any): any {}, - handleClose: function (): any {} + handleClose: function (): any {}, }} /> ) diff --git a/packages/desktop/src/renderer/components/Sidebar/IdentityPanel/IdentityPanel.tsx b/packages/desktop/src/renderer/components/Sidebar/IdentityPanel/IdentityPanel.tsx index 9de8286ed9..6cbe2dd540 100644 --- a/packages/desktop/src/renderer/components/Sidebar/IdentityPanel/IdentityPanel.tsx +++ b/packages/desktop/src/renderer/components/Sidebar/IdentityPanel/IdentityPanel.tsx @@ -11,14 +11,10 @@ const PREFIX = 'IdentityPanel' const classes = { root: `${PREFIX}root`, button: `${PREFIX}button`, - nickname: `${PREFIX}nickname` + nickname: `${PREFIX}nickname`, } -const IdentityPanelButtonStyled = styled('div')(( - { - theme - } -) => ({ +const IdentityPanelButtonStyled = styled('div')(({ theme }) => ({ marginTop: theme.spacing(1), paddingLeft: 16, paddingRight: 16, @@ -32,16 +28,16 @@ const IdentityPanelButtonStyled = styled('div')(( textTransform: 'capitalize', '&:hover': { opacity: 1, - backgroundColor: 'inherit' - } + backgroundColor: 'inherit', + }, }, [`& .${classes.nickname}`]: { overflow: 'hidden', textOverflow: 'ellipsis', maxWidth: 175, - whiteSpace: 'nowrap' - } + whiteSpace: 'nowrap', + }, })) export interface IdentityPanelProps { @@ -49,10 +45,7 @@ export interface IdentityPanelProps { accountSettingsModal: ReturnType } -export const IdentityPanel: React.FC = ({ - currentCommunity, - accountSettingsModal -}) => { +export const IdentityPanel: React.FC = ({ currentCommunity, accountSettingsModal }) => { const communityName = currentCommunity?.name || '' return ( @@ -63,7 +56,8 @@ export const IdentityPanel: React.FC = ({ }} component='span' classes={{ root: classes.button }} - data-testid={'settings-panel-button'}> + data-testid={'settings-panel-button'} + > {communityName} diff --git a/packages/desktop/src/renderer/components/Sidebar/Sidebar.stories.tsx b/packages/desktop/src/renderer/components/Sidebar/Sidebar.stories.tsx index dc0385c3c0..974b2ef37d 100644 --- a/packages/desktop/src/renderer/components/Sidebar/Sidebar.stories.tsx +++ b/packages/desktop/src/renderer/components/Sidebar/Sidebar.stories.tsx @@ -22,8 +22,9 @@ const Template: ComponentStory = args => { minHeight: '100vh', minWidth: '100vw', overflow: 'hidden', - position: 'relative' - }}> + position: 'relative', + }} + > @@ -35,29 +36,29 @@ const Template: ComponentStory = args => { const args: IdentityPanelProps & ChannelsPanelProps = { // @ts-expect-error currentCommunity: { - name: 'rockets' + name: 'rockets', }, accountSettingsModal: { open: false, handleOpen: function (_args?: any): any {}, - handleClose: function (): any {} + handleClose: function (): any {}, }, channels: [ // @ts-expect-error { id: 'general', - name: 'general' + name: 'general', }, // @ts-expect-error { id: 'spooky', - name: 'spooky' + name: 'spooky', }, // @ts-expect-error { id: 'kalkan', - name: 'kalkan' - } + name: 'kalkan', + }, ], unreadChannels: ['spooky'], setCurrentChannel: function (_id: string): void {}, @@ -66,8 +67,8 @@ const args: IdentityPanelProps & ChannelsPanelProps = { createChannelModal: { open: false, handleOpen: function (_args?: any): any {}, - handleClose: function (): any {} - } + handleClose: function (): any {}, + }, } export const Component = Template.bind({}) @@ -80,7 +81,7 @@ const component: ComponentMeta = { title: 'Components/SidebarComponent', decorators: [withTheme], component: SidebarComponent, - excludeStories: ['Reusable'] + excludeStories: ['Reusable'], } export default component diff --git a/packages/desktop/src/renderer/components/Sidebar/Sidebar.tsx b/packages/desktop/src/renderer/components/Sidebar/Sidebar.tsx index 4f26f56013..a2ffc0e9a3 100644 --- a/packages/desktop/src/renderer/components/Sidebar/Sidebar.tsx +++ b/packages/desktop/src/renderer/components/Sidebar/Sidebar.tsx @@ -26,7 +26,7 @@ const Sidebar = () => { const setCurrentChannel = (id: string) => { dispatch( publicChannels.actions.setCurrentChannel({ - channelId: id + channelId: id, }) ) } @@ -37,7 +37,7 @@ const Sidebar = () => { const identityPanelProps: IdentityPanelProps = { currentCommunity: currentCommunity, - accountSettingsModal: accountSettingsModal + accountSettingsModal: accountSettingsModal, } const channelsPanelProps: ChannelsPanelProps = { @@ -45,7 +45,7 @@ const Sidebar = () => { unreadChannels: unreadChannels, setCurrentChannel: setCurrentChannel, currentChannelId: currentChannelId, - createChannelModal: createChannelModal + createChannelModal: createChannelModal, } return diff --git a/packages/desktop/src/renderer/components/Sidebar/SidebarComponent.tsx b/packages/desktop/src/renderer/components/Sidebar/SidebarComponent.tsx index 4bba5e6efd..b4d5277b85 100644 --- a/packages/desktop/src/renderer/components/Sidebar/SidebarComponent.tsx +++ b/packages/desktop/src/renderer/components/Sidebar/SidebarComponent.tsx @@ -13,47 +13,41 @@ const classes = { padding: `${PREFIX}padding`, content: `${PREFIX}content`, gutterBottom: `${PREFIX}gutterBottom`, - walletInfo: `${PREFIX}walletInfo` + walletInfo: `${PREFIX}walletInfo`, } -const StyledGrid = styled(Grid)(( - { - theme - } -) => ({ +const StyledGrid = styled(Grid)(({ theme }) => ({ [`&.${classes.root}`]: { paddingTop: '30px', minHeight: '100%', width: '220px', position: 'relative', backgroundImage: 'linear-gradient(290.29deg, #521576 18.61%, #E42656 96.07%)', - color: theme.palette.colors.white + color: theme.palette.colors.white, }, [`& .${classes.padding}`]: { - padding: 0 + padding: 0, }, [`& .${classes.content}`]: { - height: '100%' + height: '100%', }, [`& .${classes.gutterBottom}`]: { - marginBottom: theme.spacing(4) + marginBottom: theme.spacing(4), }, [`& .${classes.walletInfo}`]: { - backgroundColor: 'rgb(0,0,0,0.1)' - } + backgroundColor: 'rgb(0,0,0,0.1)', + }, })) -const SidebarComponent: React.FC = ({ - ...props -}) => { +const SidebarComponent: React.FC = ({ ...props }) => { return ( - + diff --git a/packages/desktop/src/renderer/components/Titlebar.tsx b/packages/desktop/src/renderer/components/Titlebar.tsx index 807b00bca6..66e4b680c6 100644 --- a/packages/desktop/src/renderer/components/Titlebar.tsx +++ b/packages/desktop/src/renderer/components/Titlebar.tsx @@ -5,7 +5,7 @@ export const addTitlebar = () => { // eslint-disable-next-line const titlebar = new Titlebar({ backgroundColor: Color.fromHex('#521c74'), - overflow: 'hidden' + overflow: 'hidden', }) }, 0) } diff --git a/packages/desktop/src/renderer/components/ui/Elipsis/Elipsis.tsx b/packages/desktop/src/renderer/components/ui/Elipsis/Elipsis.tsx index b9bcccc304..507bbe9cba 100644 --- a/packages/desktop/src/renderer/components/ui/Elipsis/Elipsis.tsx +++ b/packages/desktop/src/renderer/components/ui/Elipsis/Elipsis.tsx @@ -7,11 +7,11 @@ import Typography from '@mui/material/Typography' const PREFIX = 'Elipsis' const classes = { - content: `${PREFIX}content` + content: `${PREFIX}content`, } const StyledTooltip = styled(Tooltip)({ - [`& .${classes.content}`]: {} + [`& .${classes.content}`]: {}, }) interface ElipsisProps { @@ -25,14 +25,15 @@ export const Elipsis: React.FC = ({ content, length = 40, tooltipPlacement = 'bottom-start', - interactive = false + interactive = false, }) => { return ( + disableHoverListener={content.length < length} + > {R.concat(content.substring(0, length), content.length > length ? '...' : '')} diff --git a/packages/desktop/src/renderer/components/ui/ErrorModal/ErrorModal.tsx b/packages/desktop/src/renderer/components/ui/ErrorModal/ErrorModal.tsx index 96f2483a70..55ba849941 100644 --- a/packages/desktop/src/renderer/components/ui/ErrorModal/ErrorModal.tsx +++ b/packages/desktop/src/renderer/components/ui/ErrorModal/ErrorModal.tsx @@ -16,7 +16,16 @@ export const ErrorModal = () => { const testMode = Boolean(process.env.TEST_MODE) - return + return ( + + ) } export default ErrorModal diff --git a/packages/desktop/src/renderer/components/ui/ErrorModal/ErrorModalComponent.tsx b/packages/desktop/src/renderer/components/ui/ErrorModal/ErrorModalComponent.tsx index f3fb02d94c..a86644a198 100644 --- a/packages/desktop/src/renderer/components/ui/ErrorModal/ErrorModalComponent.tsx +++ b/packages/desktop/src/renderer/components/ui/ErrorModal/ErrorModalComponent.tsx @@ -23,50 +23,46 @@ const classes = { info: `${PREFIX}info`, textfield: `${PREFIX}textfield`, cssDisabled: `${PREFIX}cssDisabled`, - button: `${PREFIX}button` + button: `${PREFIX}button`, } -const StyledModalContent = styled(Grid)(( - { - theme - } -) => ({ +const StyledModalContent = styled(Grid)(({ theme }) => ({ padding: theme.spacing(4), [`& .${classes.icon}`]: { fontSize: '10rem', color: red[500], width: 80, - height: 70 + height: 70, }, [`& .${classes.stackTrace}`]: { fontSize: '14px', - wordBreak: 'break-all' + wordBreak: 'break-all', }, [`& .${classes.message}`]: { wordBreak: 'break-all', marginTop: 20, - fontWeight: 500 + fontWeight: 500, }, [`& .${classes.info}`]: { - textAlign: 'center' + textAlign: 'center', }, [`& .${classes.textfield}`]: {}, [`& .${classes.cssDisabled}`]: { backgroundColor: theme.palette.colors.inputGray, - color: theme.palette.colors.red + color: theme.palette.colors.red, }, [`& .${classes.button}`]: { textTransform: 'none', width: 150, - height: 60 - } + height: 60, + }, })) interface ErrorModalProps { @@ -84,7 +80,7 @@ export const ErrorModalComponent: React.FC = ({ traceback, handleClose, restartApp, - testMode + testMode, }) => { return ( @@ -116,17 +112,13 @@ export const ErrorModalComponent: React.FC = ({ classes: { root: classes.textfield, multiline: classes.stackTrace, - disabled: classes.cssDisabled - } + disabled: classes.cssDisabled, + }, }} /> - + diff --git a/packages/desktop/src/renderer/components/ui/Icon/Icon.tsx b/packages/desktop/src/renderer/components/ui/Icon/Icon.tsx index 1b948c6b1b..c571daf47d 100644 --- a/packages/desktop/src/renderer/components/ui/Icon/Icon.tsx +++ b/packages/desktop/src/renderer/components/ui/Icon/Icon.tsx @@ -6,7 +6,7 @@ export const Icon: React.FC = ({ src, onClickHandler, onMouseEnterHandler, - onMouseLeaveHandler + onMouseLeaveHandler, }) => { return ( ({ +const StyledIconButtonMui = styled(IconButtonMui)(({ theme }) => ({ [`& .${classes.root}`]: { padding: 6, - color: theme.typography.body1.color - } + color: theme.typography.body1.color, + }, })) -export const IconButton: React.FC = ({ - children, - onClick -}) => { +export const IconButton: React.FC = ({ children, onClick }) => { return ( {children} diff --git a/packages/desktop/src/renderer/components/ui/Icon/IconCopy.tsx b/packages/desktop/src/renderer/components/ui/Icon/IconCopy.tsx index c9a64adc8c..4718af6237 100644 --- a/packages/desktop/src/renderer/components/ui/Icon/IconCopy.tsx +++ b/packages/desktop/src/renderer/components/ui/Icon/IconCopy.tsx @@ -10,36 +10,36 @@ const classes = { squareTop: `${PREFIX}squareTop`, gradient: `${PREFIX}gradient`, squareFill: `${PREFIX}squareFill`, - squareBottom: `${PREFIX}squareBottom` + squareBottom: `${PREFIX}squareBottom`, } const Root = styled('div')({ [`& .${classes.root}`]: {}, [`& .${classes.main}`]: { padding: 0, - margin: 0 + margin: 0, }, [`& .${classes.squareTop}`]: { position: 'absolute', left: 4, - top: 7 + top: 7, }, [`& .${classes.gradient}`]: { maxWidth: 50, padding: 2, position: 'relative', - backgroundImage: 'linear-gradient(315deg, #521576, #e42656)' + backgroundImage: 'linear-gradient(315deg, #521576, #e42656)', }, [`& .${classes.squareFill}`]: { background: 'white', color: 'white', - padding: 5 + padding: 5, }, [`& .${classes.squareBottom}`]: { position: 'absolute', left: 9, - top: 2 - } + top: 2, + }, }) export const IconCopy: React.FC = () => { diff --git a/packages/desktop/src/renderer/components/ui/LoadingButton/LoadingButton.tsx b/packages/desktop/src/renderer/components/ui/LoadingButton/LoadingButton.tsx index 5ea275c760..116fc921bf 100644 --- a/packages/desktop/src/renderer/components/ui/LoadingButton/LoadingButton.tsx +++ b/packages/desktop/src/renderer/components/ui/LoadingButton/LoadingButton.tsx @@ -11,14 +11,10 @@ const PREFIX = 'LoadingButton' const classes = { button: `${PREFIX}button`, inProgress: `${PREFIX}inProgress`, - progress: `${PREFIX}progress` + progress: `${PREFIX}progress`, } -const StyledButton = styled(Button)(( - { - theme - } -) => ({ +const StyledButton = styled(Button)(({ theme }) => ({ [`&.${classes.button}`]: { maxWidth: 286, minWidth: 100, @@ -26,23 +22,23 @@ const StyledButton = styled(Button)(( backgroundColor: theme.palette.colors.quietBlue, color: theme.palette.colors.white, '&:hover': { - backgroundColor: theme.palette.colors.quietBlue + backgroundColor: theme.palette.colors.quietBlue, }, '&:disabled': { - opacity: 0.7 - } + opacity: 0.7, + }, }, [`&.${classes.inProgress}`]: { '&:disabled': { backgroundColor: theme.palette.colors.quietBlue, - opacity: 1 - } + opacity: 1, + }, }, [`& .${classes.progress}`]: { - color: theme.palette.colors.white - } + color: theme.palette.colors.white, + }, })) interface LoadingButtonClasses extends ButtonClasses { @@ -63,12 +59,19 @@ export const LoadingButton: React.FC = ({ }) => { const mergedClasses = { ...classes, - ...customClasses + ...customClasses, } return ( - - {inProgress ? : text } + + {inProgress ? ( + + ) : ( + text + )} ) } diff --git a/packages/desktop/src/renderer/components/ui/MenuAction/MenuAction.tsx b/packages/desktop/src/renderer/components/ui/MenuAction/MenuAction.tsx index ff863bcd18..aabeffaeed 100644 --- a/packages/desktop/src/renderer/components/ui/MenuAction/MenuAction.tsx +++ b/packages/desktop/src/renderer/components/ui/MenuAction/MenuAction.tsx @@ -15,7 +15,7 @@ const PREFIX = 'MenuAction' const classes = { menuList: `${PREFIX}menuList`, icon: `${PREFIX}icon`, - button: `${PREFIX}button` + button: `${PREFIX}button`, } // TODO jss-to-styled codemod: The Fragment root was replaced by div. Change the tag if needed. @@ -24,19 +24,19 @@ const Root = styled('div')(() => ({ paddingTop: 24, paddingBottom: 24, minWidth: 136, - borderRadius: 8 + borderRadius: 8, }, [`& .${classes.icon}`]: {}, - [`& .${classes.button}`]: {} + [`& .${classes.button}`]: {}, })) -const RefIconButton = React.forwardRef>( - (props, ref) => -) +const RefIconButton = React.forwardRef>((props, ref) => ( + +)) interface MenuActionProps { - icon: string | ExtendButtonBase> + icon: string | ExtendButtonBase> iconHover: string children?: any offset: string | number @@ -52,7 +52,7 @@ export const MenuAction: React.FC = ({ offset, disabled = false, onClick, - placement + placement, }) => { const [open, setOpen] = useState(false) const [hover, setHover] = useState(false) @@ -81,7 +81,8 @@ export const MenuAction: React.FC = ({ disabled={disabled} disableRipple onMouseEnter={toggleHover} - onMouseLeave={toggleHover}> + onMouseLeave={toggleHover} + > diff --git a/packages/desktop/src/renderer/components/ui/MenuAction/MenuActionItem.tsx b/packages/desktop/src/renderer/components/ui/MenuAction/MenuActionItem.tsx index b959cad32e..0466fbe4a5 100644 --- a/packages/desktop/src/renderer/components/ui/MenuAction/MenuActionItem.tsx +++ b/packages/desktop/src/renderer/components/ui/MenuAction/MenuActionItem.tsx @@ -7,7 +7,7 @@ import MuiMenuItem from '@mui/material/MenuItem' const PREFIX = 'MenuActionItem' const classes = { - root: `${PREFIX}root` + root: `${PREFIX}root`, } const StyledMuiMenuItem = styled(MuiMenuItem)(() => ({ @@ -17,8 +17,8 @@ const StyledMuiMenuItem = styled(MuiMenuItem)(() => ({ fontSize: 14, letterSpacing: 0.4, paddingTop: 5, - paddingBottom: 5 - } + paddingBottom: 5, + }, })) interface MenuActionItemProps { @@ -28,12 +28,7 @@ interface MenuActionItemProps { closeAfterAction?: boolean } -export const MenuActionItem: React.FC = ({ - onClick, - title, - close, - closeAfterAction = true -}) => { +export const MenuActionItem: React.FC = ({ onClick, title, close, closeAfterAction = true }) => { return ( { diff --git a/packages/desktop/src/renderer/components/ui/Modal/Modal.tsx b/packages/desktop/src/renderer/components/ui/Modal/Modal.tsx index d1148d1015..dd34e8b60f 100644 --- a/packages/desktop/src/renderer/components/ui/Modal/Modal.tsx +++ b/packages/desktop/src/renderer/components/ui/Modal/Modal.tsx @@ -30,12 +30,12 @@ const classes = { bold: `${PREFIX}bold`, none: `${PREFIX}none`, transparent: `${PREFIX}transparent`, - withoutHeader: `${PREFIX}withoutHeader` + withoutHeader: `${PREFIX}withoutHeader`, } const StyledMaterialModal = styled(MaterialModal)(({ theme }) => ({ [`& .${classes.root}`]: { - padding: '0 15%' + padding: '0 15%', }, [`& .${classes.windowed}`]: { @@ -43,7 +43,7 @@ const StyledMaterialModal = styled(MaterialModal)(({ theme }) => ({ width: '50vw', position: 'fixed', marginTop: '25vh', - marginLeft: '25vw' + marginLeft: '25vw', }, [`& .${classes.title}`]: { @@ -51,40 +51,40 @@ const StyledMaterialModal = styled(MaterialModal)(({ theme }) => ({ color: theme.palette.colors.trueBlack, lineHeight: '18px', fontStyle: 'normal', - fontWeight: 'normal' + fontWeight: 'normal', }, [`& .${classes.header}`]: { background: theme.palette.colors.white, - height: constants.headerHeight + height: constants.headerHeight, }, [`& .${classes.headerBorder}`]: { - borderBottom: `1px solid ${theme.palette.colors.contentGray}` + borderBottom: `1px solid ${theme.palette.colors.contentGray}`, }, [`& .${classes.actions}`]: { paddingLeft: 10, - paddingRight: 10 + paddingRight: 10, }, [`& .${classes.content}`]: { - background: theme.palette.colors.white + background: theme.palette.colors.white, }, [`& .${classes.fullPage}`]: { width: '100%', - height: `calc(100vh - ${constants.headerHeight}px)` + height: `calc(100vh - ${constants.headerHeight}px)`, }, [`& .${classes.withoutHeader}`]: { width: '100%', - height: '100vh' + height: '100vh', }, [`& .${classes.notFullPage}`]: { height: '100%', - width: '100%' + width: '100%', }, [`& .${classes.centered}`]: { @@ -94,7 +94,7 @@ const StyledMaterialModal = styled(MaterialModal)(({ theme }) => ({ top: '50%', left: '50%', transform: 'translate(-50%, -50%)', - outline: 0 + outline: 0, }, [`& .${classes.window}`]: { @@ -104,24 +104,24 @@ const StyledMaterialModal = styled(MaterialModal)(({ theme }) => ({ top: '50%', left: '50%', transform: 'translate(-50%, -50%)', - outline: 0 + outline: 0, }, [`& .${classes.bold}`]: { fontSize: 16, lineHeight: '26px', - fontWeight: 500 + fontWeight: 500, }, [`& .${classes.none}`]: { - display: 'none' + display: 'none', }, [`& .${classes.transparent}`]: { - backgroundColor: 'transparent' - } + backgroundColor: 'transparent', + }, })) const constants = { - headerHeight: 60 + headerHeight: 60, } export const Modal: React.FC = ({ @@ -142,7 +142,7 @@ export const Modal: React.FC = ({ windowed, fullPage = true, isTransparent = false, - withoutHeader = false + withoutHeader = false, }) => { return ( = ({ className={classNames({ [classes.windowed]: windowed, [classes.root]: !windowed, - [classes.transparent]: isTransparent - })}> + [classes.transparent]: isTransparent, + })} + > = ({ className={classNames({ [classes.centered]: fullPage, [classes.window]: !fullPage, - [classes.transparent]: isTransparent - })}> + [classes.transparent]: isTransparent, + })} + > + alignItems='center' + > + alignItems='center' + > + align='center' + > {title} @@ -197,14 +202,16 @@ export const Modal: React.FC = ({ item justifyContent={alignCloseLeft ? 'flex-start' : 'flex-end'} className={classes.actions} - data-testid={`${testIdPrefix}ModalActions`}> + data-testid={`${testIdPrefix}ModalActions`} + > {canGoBack ? ( { if (setStep && step) { return setStep(step - 1) } - }}> + }} + > ) : ( @@ -214,7 +221,8 @@ export const Modal: React.FC = ({ if (handleClose) { return handleClose({}, 'backdropClick') } - }}> + }} + > ) @@ -232,16 +240,18 @@ export const Modal: React.FC = ({ [classes.fullPage]: fullPage, [classes.notFullPage]: !fullPage, [classes.withoutHeader]: isTransparent || withoutHeader, - [classes.transparent]: isTransparent - })}> + [classes.transparent]: isTransparent, + })} + > + style={{ width: contentWidth, height: contentHeight }} + > {children} @@ -256,7 +266,7 @@ Modal.defaultProps = { alignCloseLeft: false, contentWidth: 600, isCloseDisabled: false, - addBorder: false + addBorder: false, } export default Modal diff --git a/packages/desktop/src/renderer/components/ui/OpenlinkModal/OpenlinkModal.tsx b/packages/desktop/src/renderer/components/ui/OpenlinkModal/OpenlinkModal.tsx index e082154aae..2fec9632fb 100644 --- a/packages/desktop/src/renderer/components/ui/OpenlinkModal/OpenlinkModal.tsx +++ b/packages/desktop/src/renderer/components/ui/OpenlinkModal/OpenlinkModal.tsx @@ -26,46 +26,42 @@ const classes = { checkboxLabel: `${PREFIX}checkboxLabel`, checkboxes: `${PREFIX}checkboxes`, buttonBack: `${PREFIX}buttonBack`, - buttons: `${PREFIX}buttons` + buttons: `${PREFIX}buttons`, } -const StyledModalContent = styled(Grid)(( - { - theme - } -) => ({ +const StyledModalContent = styled(Grid)(({ theme }) => ({ padding: theme.spacing(4), [`& .${classes.icon}`]: { fontSize: '10rem', color: red[500], width: 80, - height: 70 + height: 70, }, [`& .${classes.title}`]: { marginTop: 36, - marginBottom: 24 + marginBottom: 24, }, [`& .${classes.message}`]: { wordBreak: 'break-word', marginTop: 16, - fontWeight: 500 + fontWeight: 500, }, [`& .${classes.bold}`]: { - fontWeight: 600 + fontWeight: 600, }, [`& .${classes.checkboxLabel}`]: { fontSize: 14, lineHeight: '24px', - wordBreak: 'break-word' + wordBreak: 'break-word', }, [`& .${classes.checkboxes}`]: { - marginTop: 32 + marginTop: 32, }, [`& .${classes.buttonBack}`]: { @@ -74,13 +70,13 @@ const StyledModalContent = styled(Grid)(( backgroundColor: theme.palette.colors.quietBlue, color: theme.palette.colors.white, '&:hover': { - backgroundColor: theme.palette.colors.quietBlue - } + backgroundColor: theme.palette.colors.quietBlue, + }, }, [`& .${classes.buttons}`]: { - marginTop: 24 - } + marginTop: 24, + }, })) interface OpenLinkModalProps { @@ -100,7 +96,7 @@ export const OpenlinkModal: React.FC = ({ url = 'https://www.zbay.app/', addToWhitelist, setWhitelistAll, - isImage = false + isImage = false, }) => { const [allowThisLink, setAllowThisLink] = React.useState(false) const [allowAllLink, setAllowAllLink] = React.useState(false) @@ -123,51 +119,16 @@ export const OpenlinkModal: React.FC = ({ - Opening link posted in Quiet reveals data about you to your goverment, your - Internet provider, the site you are visiting and, potentially, to whoever posted - the link. Only open links from people you trust. If you are using Quiet to - protect your anonymity, never open links. + Opening link posted in Quiet reveals data about you to your goverment, your Internet provider, the + site you are visiting and, potentially, to whoever posted the link. Only open links from people you + trust. If you are using Quiet to protect your anonymity, never open links. {' '} - {isImage - ? ( - <> - - - setAllowThisLink(e.target.checked)} - color='primary' - /> - - - {'Automatically load images from '} - {uri.hostname} - { - "- I trust them with my data and I'm not using Quiet for anonymity protection. " - } - - - - - setDontAutoload(e.target.checked)} - color='primary' - /> - - - {"Don't warn me about "} - {uri.hostname}{' '} - {"again, but don't auto-load images."} - - - - ) - : ( + {isImage ? ( + <> = ({ color='primary' /> + + {'Automatically load images from '} + {uri.hostname} + {"- I trust them with my data and I'm not using Quiet for anonymity protection. "} + + + + + setDontAutoload(e.target.checked)} + color='primary' + /> + {"Don't warn me about "} - {uri.hostname} {'again'} + {uri.hostname} {"again, but don't auto-load images."} - )} + + ) : ( + + + setAllowThisLink(e.target.checked)} + color='primary' + /> + + + {"Don't warn me about "} + {uri.hostname} {'again'} + + + )} = ({ size='large' onClick={() => { handleClose() - }}> + }} + > Back to safety @@ -212,7 +203,7 @@ export const OpenlinkModal: React.FC = ({ style={{ color: '#67BFD3', textDecoration: 'none', - wordBreak: 'break-all' + wordBreak: 'break-all', }} onClick={e => { e.preventDefault() @@ -223,10 +214,9 @@ export const OpenlinkModal: React.FC = ({ setWhitelistAll(allowAllLink) handleClose() }} - href={''}> - {isImage - ? `Load image from site ${uri.hostname}` - : `Continue to ${uri.hostname}`} + href={''} + > + {isImage ? `Load image from site ${uri.hostname}` : `Continue to ${uri.hostname}`}
diff --git a/packages/desktop/src/renderer/components/ui/Page/PageHeader.tsx b/packages/desktop/src/renderer/components/ui/Page/PageHeader.tsx index 5ee35b1d60..bc8ca2c08b 100644 --- a/packages/desktop/src/renderer/components/ui/Page/PageHeader.tsx +++ b/packages/desktop/src/renderer/components/ui/Page/PageHeader.tsx @@ -7,19 +7,15 @@ import Grid from '@mui/material/Grid' const PREFIX = 'PageHeader' const classes = { - root: `${PREFIX}root` + root: `${PREFIX}root`, } -const StyledGrid = styled(Grid)(( - { - theme - } -) => ({ +const StyledGrid = styled(Grid)(({ theme }) => ({ [`&.${classes.root}`]: { background: theme.palette.colors.white, order: -1, - zIndex: 9 - } + zIndex: 9, + }, })) interface PageHeaderProps { diff --git a/packages/desktop/src/renderer/components/ui/PopupMenu/PopupMenu.tsx b/packages/desktop/src/renderer/components/ui/PopupMenu/PopupMenu.tsx index 5a89c77091..67fffd57e0 100644 --- a/packages/desktop/src/renderer/components/ui/PopupMenu/PopupMenu.tsx +++ b/packages/desktop/src/renderer/components/ui/PopupMenu/PopupMenu.tsx @@ -14,20 +14,16 @@ const classes = { arrow: `${PREFIX}arrow`, bottom: `${PREFIX}bottom`, top: `${PREFIX}top`, - popper: `${PREFIX}popper` + popper: `${PREFIX}popper`, } -const StyledPopper = styled(Popper)(( - { - theme - } -) => ({ +const StyledPopper = styled(Popper)(({ theme }) => ({ [`& .${classes.wrapper}`]: {}, [`& .${classes.paper}`]: { background: theme.palette.background.default, boxShadow: '0px 2px 25px rgba(0, 0, 0, 0.2)', - borderRadius: 8 + borderRadius: 8, }, [`& .${classes.arrow}`]: { @@ -41,8 +37,8 @@ const StyledPopper = styled(Popper)(( display: 'block', width: 0, height: 0, - borderStyle: 'solid' - } + borderStyle: 'solid', + }, }, [`& .${classes.bottom}`]: { @@ -50,8 +46,8 @@ const StyledPopper = styled(Popper)(( marginTop: `-${constants.arrowSize}px`, '&::before': { borderWidth: `0 ${constants.arrowSize}px ${constants.arrowSize}px ${constants.arrowSize}px`, - borderColor: `transparent transparent ${theme.palette.background.default} transparent` - } + borderColor: `transparent transparent ${theme.palette.background.default} transparent`, + }, }, [`& .${classes.top}`]: { @@ -59,17 +55,17 @@ const StyledPopper = styled(Popper)(( marginBottom: `-${2 * constants.arrowSize}px`, '&::before': { borderWidth: `${constants.arrowSize}px ${constants.arrowSize}px 0 ${constants.arrowSize}px`, - borderColor: `${theme.palette.background.default} transparent transparent transparent` - } + borderColor: `${theme.palette.background.default} transparent transparent transparent`, + }, }, [`&.${classes.popper}`]: { - zIndex: 100 - } + zIndex: 100, + }, })) const constants = { - arrowSize: 10 + arrowSize: 10, } export const PopupMenu: React.FC = ({ @@ -78,7 +74,7 @@ export const PopupMenu: React.FC = ({ children, className = '', offset = 0, - placement = 'bottom-end' + placement = 'bottom-end', }) => { const arrowRef = useRef(null) return ( @@ -91,31 +87,36 @@ export const PopupMenu: React.FC = ({ className={classes.popper} modifiers={[ { name: 'arrow', enabled: Boolean(arrowRef.current), options: { element: arrowRef.current } }, - { name: 'offset', options: { offset } } + { name: 'offset', options: { offset } }, ]} > {({ TransitionProps, placement }) => { - const splitPlacement: keyof typeof classes = placement.split('-')[0] as 'wrapper' | 'paper' | 'bottom' | 'top' | 'arrow' | 'popper' + const splitPlacement: keyof typeof classes = placement.split('-')[0] as + | 'wrapper' + | 'paper' + | 'bottom' + | 'top' + | 'arrow' + | 'popper' return (
{children} diff --git a/packages/desktop/src/renderer/components/ui/ProgressFab/ProgressFab.tsx b/packages/desktop/src/renderer/components/ui/ProgressFab/ProgressFab.tsx index 96d32803b3..b5b2d327cd 100644 --- a/packages/desktop/src/renderer/components/ui/ProgressFab/ProgressFab.tsx +++ b/packages/desktop/src/renderer/components/ui/ProgressFab/ProgressFab.tsx @@ -13,7 +13,7 @@ const classes = { root: `${PREFIX}root`, fabProgress: `${PREFIX}fabProgress`, wrapper: `${PREFIX}wrapper`, - buttonSuccess: `${PREFIX}buttonSuccess` + buttonSuccess: `${PREFIX}buttonSuccess`, } const Root = styled('div')(() => ({ @@ -21,8 +21,8 @@ const Root = styled('div')(() => ({ backgroundColor: '#8d8d8d', color: '#fff', '&:hover': { - backgroundColor: '#737373' - } + backgroundColor: '#737373', + }, }, [`& .${classes.fabProgress}`]: { @@ -30,19 +30,19 @@ const Root = styled('div')(() => ({ position: 'absolute', left: -6, top: -6, - zIndex: 1 + zIndex: 1, }, [`&.${classes.wrapper}`]: { - position: 'relative' + position: 'relative', }, [`& .${classes.buttonSuccess}`]: { '&:disabled': { backgroundColor: green[500], - color: '#fff' - } - } + color: '#fff', + }, + }, })) interface ProgressFabProps { @@ -67,18 +67,20 @@ export const ProgressFab: React.FC & ProgressFa + [className]: className, + })} + > + {...props} + > {success ? : children} {loading && } diff --git a/packages/desktop/src/renderer/components/ui/QuickActionLayout/QuickActionLayout.tsx b/packages/desktop/src/renderer/components/ui/QuickActionLayout/QuickActionLayout.tsx index dab0b3284f..4d0b460503 100644 --- a/packages/desktop/src/renderer/components/ui/QuickActionLayout/QuickActionLayout.tsx +++ b/packages/desktop/src/renderer/components/ui/QuickActionLayout/QuickActionLayout.tsx @@ -8,6 +8,7 @@ import Button from '@mui/material/Button' import Grid from '@mui/material/Grid' import IconButton from '../Icon/IconButton' +import { IconButtonProps } from '@mui/material/IconButton' const PREFIX = 'QuickActionLayout' @@ -19,16 +20,12 @@ const classes = { closeIcon: `${PREFIX}closeIcon`, info: `${PREFIX}info`, infoDiv: `${PREFIX}infoDiv`, - avatar: `${PREFIX}avatar` + avatar: `${PREFIX}avatar`, } -const StyledGrid = styled(Grid)(( - { - theme - } -) => ({ +const StyledGrid = styled(Grid)(({ theme }) => ({ [`& .${classes.alignAvatarPopover}`]: { - marginTop: theme.spacing(2) + marginTop: theme.spacing(2), }, [`& .${classes.button}`]: { @@ -42,45 +39,45 @@ const StyledGrid = styled(Grid)(( color: theme.palette.colors.white, fontSize: '0.9rem', backgroundColor: theme.palette.colors.purple, - textTransform: 'none' + textTransform: 'none', }, [`&.${classes.container}`]: { height: 400, - width: 320 + width: 320, }, [`& .${classes.usernamePopover}`]: { marginTop: theme.spacing(1), fontSize: '1.2rem', - fontWeight: 'bold' + fontWeight: 'bold', }, [`& .${classes.closeIcon}`]: { - margin: theme.spacing(2) + margin: theme.spacing(2), }, [`& .${classes.info}`]: { - color: theme.palette.colors.quietBlue + color: theme.palette.colors.quietBlue, }, [`& .${classes.infoDiv}`]: { textAlign: 'center', marginTop: theme.spacing(1.2), marginLeft: theme.spacing(4), - marginRight: theme.spacing(4) + marginRight: theme.spacing(4), }, [`& .${classes.avatar}`]: { - marginTop: theme.spacing(2) - } + marginTop: theme.spacing(2), + }, })) interface QuickActionLayoutProps { main: string info?: string children?: ReactElement - handleClose: (event?: {}, reason?: 'backdropClick' | 'escapeKeyDown') => void + handleClose: IconButtonProps['onClick'] buttonName?: string warning?: string onClick?: () => void @@ -93,7 +90,7 @@ export const QuickActionLayout: React.FC = ({ handleClose, buttonName, warning, - onClick + onClick, }) => { return ( = ({ className={classes.container} direction='column' justifyContent='flex-start' - alignItems='center'> + alignItems='center' + > @@ -121,11 +119,7 @@ export const QuickActionLayout: React.FC = ({ - diff --git a/packages/desktop/src/renderer/components/ui/QuitApp/QuitAppDialog.test.tsx b/packages/desktop/src/renderer/components/ui/QuitApp/QuitAppDialog.test.tsx index f614816051..9d5527d5ee 100644 --- a/packages/desktop/src/renderer/components/ui/QuitApp/QuitAppDialog.test.tsx +++ b/packages/desktop/src/renderer/components/ui/QuitApp/QuitAppDialog.test.tsx @@ -5,9 +5,7 @@ import { QuitAppDialog } from './QuitAppDialog' describe('QuitAppDialog', () => { it('renders component', () => { - const result = renderComponent( - - ) + const result = renderComponent() expect(result.baseElement).toMatchInlineSnapshot(` ({ +const StyledDialog = styled(Dialog)(({ theme }) => ({ [`& .${classes.root}`]: {}, [`& .${classes.info}`]: { - fontWeight: 500 + fontWeight: 500, }, [`& .${classes.dialogContent}`]: { - borderBottom: `1px solid ${theme.palette.colors.veryLightGray}` + borderBottom: `1px solid ${theme.palette.colors.veryLightGray}`, }, [`& .${classes.buttonNo}`]: { borderRight: `1px solid ${theme.palette.colors.veryLightGray}`, cursor: 'pointer', - padding: 10 + padding: 10, }, [`& .${classes.buttonYes}`]: { cursor: 'pointer', padding: 10, - color: theme.palette.colors.lushSky + color: theme.palette.colors.lushSky, }, [`& .${classes.dialogActions}`]: { - padding: 0 + padding: 0, }, [`& .${classes.typography}`]: { - textAlign: 'center' - } + textAlign: 'center', + }, })) interface QuitAppDialogProps { diff --git a/packages/desktop/src/renderer/components/ui/Sidebar/SidebarHeader.test.tsx b/packages/desktop/src/renderer/components/ui/Sidebar/SidebarHeader.test.tsx index 1b5501b694..8a248e77bc 100644 --- a/packages/desktop/src/renderer/components/ui/Sidebar/SidebarHeader.test.tsx +++ b/packages/desktop/src/renderer/components/ui/Sidebar/SidebarHeader.test.tsx @@ -6,12 +6,7 @@ import { SidebarHeader } from './SidebarHeader' describe('SidebarHeader', () => { it('renders component', () => { const result = renderComponent( - + ) expect(result.baseElement).toMatchInlineSnapshot(` diff --git a/packages/desktop/src/renderer/components/ui/Sidebar/SidebarHeader.tsx b/packages/desktop/src/renderer/components/ui/Sidebar/SidebarHeader.tsx index 66c14eed42..1890130025 100644 --- a/packages/desktop/src/renderer/components/ui/Sidebar/SidebarHeader.tsx +++ b/packages/desktop/src/renderer/components/ui/Sidebar/SidebarHeader.tsx @@ -14,7 +14,7 @@ const classes = { title: `${PREFIX}title`, clickable: `${PREFIX}clickable`, iconButton: `${PREFIX}iconButton`, - tooltip: `${PREFIX}tooltip` + tooltip: `${PREFIX}tooltip`, } const StyledGrid = styled(Grid)(() => ({ @@ -22,34 +22,34 @@ const StyledGrid = styled(Grid)(() => ({ marginTop: 25, height: 32, paddingLeft: 16, - paddingRight: 16 + paddingRight: 16, }, [`& .${classes.title}`]: { opacity: 0.7, - fontWeight: 500 + fontWeight: 500, }, [`& .${classes.clickable}`]: { '&:hover': { backgroundColor: 'inherit', - opacity: 1 + opacity: 1, }, - cursor: 'pointer' + cursor: 'pointer', }, [`& .${classes.iconButton}`]: { opacity: 0.7, '&:hover': { backgroundColor: 'inherit', - opacity: 1 - } + opacity: 1, + }, }, [`& .${classes.tooltip}`]: { marginTop: -1, - backgroundColor: 'blue' - } + backgroundColor: 'blue', + }, })) interface SidebarHeaderProps { @@ -59,34 +59,19 @@ interface SidebarHeaderProps { tooltipText: string } -export const SidebarHeader: React.FC = ({ - title, - action, - actionTitle, - tooltipText -}) => { +export const SidebarHeader: React.FC = ({ title, action, actionTitle, tooltipText }) => { return ( - + - {actionTitle - ? ( - - {title} - - ) - : ( - - {title} - - )} + {actionTitle ? ( + + {title} + + ) : ( + + {title} + + )} @@ -98,7 +83,8 @@ export const SidebarHeader: React.FC = ({ }} edge='end' data-testid={'addChannelButton'} - size="large"> + size='large' + > diff --git a/packages/desktop/src/renderer/components/ui/Slider/Slider.tsx b/packages/desktop/src/renderer/components/ui/Slider/Slider.tsx index a887b06e18..60b1660d51 100644 --- a/packages/desktop/src/renderer/components/ui/Slider/Slider.tsx +++ b/packages/desktop/src/renderer/components/ui/Slider/Slider.tsx @@ -19,79 +19,63 @@ const classes = { iconWrapper: `${PREFIX}iconWrapper`, track: `${PREFIX}track`, thumb: `${PREFIX}thumb`, - activated: `${PREFIX}activated` + activated: `${PREFIX}activated`, } -const StyledGrid = styled(Grid)(( - { - theme - } -) => ({ +const StyledGrid = styled(Grid)(({ theme }) => ({ [`& .${classes.sliderContainer}`]: { width: 105, - padding: '5px 10px' + padding: '5px 10px', }, [`& .${classes.sliderRoot}`]: { - paddingTop: 4 + paddingTop: 4, }, [`& .${classes.label}`]: { - fontSize: '0.83rem' + fontSize: '0.83rem', }, [`& .${classes.title}`]: { color: theme.typography.body2.color, - marginBottom: 8 + marginBottom: 8, }, [`& .${classes.iconWrapper}`]: { width: 18, - height: 18 + height: 18, }, [`& .${classes.track}`]: { backgroundColor: '#979797', height: 0.5, - opacity: 1 + opacity: 1, }, [`& .${classes.thumb}`]: { '&:hover': { - boxShadow: 'none' + boxShadow: 'none', }, '&$activated': { - boxShadow: 'none' - } + boxShadow: 'none', + }, }, [`& .${classes.activated}`]: { - boxShadow: 'none' - } + boxShadow: 'none', + }, })) -export const Slider: React.FC = ({ - value, - handleOnChange, - title, - minLabel, - maxLabel, - min, - max -}) => { +export const Slider: React.FC = ({ value, handleOnChange, title, minLabel, maxLabel, min, max }) => { return ( - - + + {title} - + - + {minLabel} @@ -103,7 +87,7 @@ export const Slider: React.FC = ({ classes={{ root: classes.sliderRoot, track: classes.track, - thumb: classes.thumb + thumb: classes.thumb, }} components={{ Thumb: SliderThumb, @@ -112,11 +96,7 @@ export const Slider: React.FC = ({ /> - + {maxLabel} @@ -130,7 +110,7 @@ Slider.defaultProps = { minLabel: '$0', maxLabel: '$max', max: 100, - min: 0 + min: 0, } export default Slider diff --git a/packages/desktop/src/renderer/components/ui/Slider/SliderThumb.tsx b/packages/desktop/src/renderer/components/ui/Slider/SliderThumb.tsx index 487550b114..3b85df3685 100644 --- a/packages/desktop/src/renderer/components/ui/Slider/SliderThumb.tsx +++ b/packages/desktop/src/renderer/components/ui/Slider/SliderThumb.tsx @@ -4,7 +4,7 @@ import { styled } from '@mui/material/styles' const PREFIX = 'SliderThumb' const classes = { - root: `${PREFIX}root` + root: `${PREFIX}root`, } const Root = styled('div')({ @@ -15,8 +15,8 @@ const Root = styled('div')({ borderColor: '#979797', borderWidth: 1, borderStyle: 'solid', - borderRadius: '50%' - } + borderRadius: '50%', + }, }) export const SliderThumb: React.FC = () => { diff --git a/packages/desktop/src/renderer/components/ui/Spinner/SpinnerLoader.test.tsx b/packages/desktop/src/renderer/components/ui/Spinner/SpinnerLoader.test.tsx index 94bbda6fee..73c75b85ce 100644 --- a/packages/desktop/src/renderer/components/ui/Spinner/SpinnerLoader.test.tsx +++ b/packages/desktop/src/renderer/components/ui/Spinner/SpinnerLoader.test.tsx @@ -5,9 +5,7 @@ import { SpinnerLoader } from './SpinnerLoader' describe('SpinnerLoader', () => { it('renders component', () => { - const result = renderComponent( - - ) + const result = renderComponent() expect(result.baseElement).toMatchInlineSnapshot(`
diff --git a/packages/desktop/src/renderer/components/ui/Spinner/SpinnerLoader.tsx b/packages/desktop/src/renderer/components/ui/Spinner/SpinnerLoader.tsx index 460eac860f..dcb917480a 100644 --- a/packages/desktop/src/renderer/components/ui/Spinner/SpinnerLoader.tsx +++ b/packages/desktop/src/renderer/components/ui/Spinner/SpinnerLoader.tsx @@ -10,22 +10,18 @@ const PREFIX = 'SpinnerLoader' const classes = { message: `${PREFIX}message`, - spinner: `${PREFIX}spinner` + spinner: `${PREFIX}spinner`, } -const StyledGrid = styled(Grid)(( - { - theme - } -) => ({ +const StyledGrid = styled(Grid)(({ theme }) => ({ [`& .${classes.message}`]: { marginTop: theme.spacing(2), - color: theme.palette.primary.main + color: theme.palette.primary.main, }, [`& .${classes.spinner}`]: (props: any) => ({ - color: props.color ? props.color : theme.palette.colors.white - }) + color: props.color ? props.color : theme.palette.colors.white, + }), })) interface SpinnerLoaderProps { @@ -39,21 +35,19 @@ interface SpinnerLoaderStylesProps { color?: string } -export const SpinnerLoader: React.FC = ({ - size = 40, - message, - color, - className -}) => { +export const SpinnerLoader: React.FC = ({ size = 40, message, color, className }) => { const stylesProps: SpinnerLoaderStylesProps = { color: color } return ( - + - + {message} diff --git a/packages/desktop/src/renderer/components/ui/Tab/Tab.tsx b/packages/desktop/src/renderer/components/ui/Tab/Tab.tsx index 3387c2b853..c148b94fda 100644 --- a/packages/desktop/src/renderer/components/ui/Tab/Tab.tsx +++ b/packages/desktop/src/renderer/components/ui/Tab/Tab.tsx @@ -7,7 +7,7 @@ const PREFIX = 'Tab' const classes = { tabRoot: `${PREFIX}tabRoot`, textColorPrimary: `${PREFIX}textColorPrimary`, - selected: `${PREFIX}selected` + selected: `${PREFIX}selected`, } const StyledMuiTab = styled(MuiTab)(({ theme }) => ({ @@ -19,14 +19,14 @@ const StyledMuiTab = styled(MuiTab)(({ theme }) => ({ alignItems: 'flex-start', textTransform: 'none', lineHeight: '21px', - minHeight: '0px' + minHeight: '0px', }, [`&.${classes.selected}`]: { backgroundColor: theme.palette.colors.lushSky, borderRadius: 5, - color: `${theme.palette.colors.white} !important` - } + color: `${theme.palette.colors.white} !important`, + }, })) export const Tab: React.FC> = props => { @@ -35,7 +35,7 @@ export const Tab: React.FC> = props => { classes={{ root: classes.tabRoot, textColorPrimary: classes.textColorPrimary, - selected: classes.selected + selected: classes.selected, }} {...props} /> diff --git a/packages/desktop/src/renderer/components/ui/TextWithLink/TextWithLink.stories.tsx b/packages/desktop/src/renderer/components/ui/TextWithLink/TextWithLink.stories.tsx index 1ab8fd5dcf..1654d78466 100644 --- a/packages/desktop/src/renderer/components/ui/TextWithLink/TextWithLink.stories.tsx +++ b/packages/desktop/src/renderer/components/ui/TextWithLink/TextWithLink.stories.tsx @@ -19,9 +19,9 @@ const args: TextWithLinkProps = { label: 'linked', action: () => { console.log('link clicked') - } - } - ] + }, + }, + ], } Component.args = args @@ -29,7 +29,7 @@ Component.args = args const component: ComponentMeta = { title: 'Components/TextWithLink', decorators: [withTheme], - component: TextWithLink + component: TextWithLink, } export default component diff --git a/packages/desktop/src/renderer/components/ui/TextWithLink/TextWithLink.test.tsx b/packages/desktop/src/renderer/components/ui/TextWithLink/TextWithLink.test.tsx index 26fbed9969..6ac48c706c 100644 --- a/packages/desktop/src/renderer/components/ui/TextWithLink/TextWithLink.test.tsx +++ b/packages/desktop/src/renderer/components/ui/TextWithLink/TextWithLink.test.tsx @@ -14,8 +14,8 @@ describe('TextWithLink', () => { label: 'simple', action: () => { console.log('linked clicked') - } - } + }, + }, ]} /> ) diff --git a/packages/desktop/src/renderer/components/ui/TextWithLink/TextWithLink.tsx b/packages/desktop/src/renderer/components/ui/TextWithLink/TextWithLink.tsx index 01c1b7a0be..410a897db9 100644 --- a/packages/desktop/src/renderer/components/ui/TextWithLink/TextWithLink.tsx +++ b/packages/desktop/src/renderer/components/ui/TextWithLink/TextWithLink.tsx @@ -7,18 +7,14 @@ import { Typography, TypographyProps } from '@mui/material' const PREFIX = 'TextWithLink' const classes = { - link: `${PREFIX}link` + link: `${PREFIX}link`, } -const StyledTypography = styled(Typography)(( - { - theme - } -) => ({ +const StyledTypography = styled(Typography)(({ theme }) => ({ [`& .${classes.link}`]: { textDecoration: 'none', - color: theme.palette.colors.linkBlue - } + color: theme.palette.colors.linkBlue, + }, })) export interface TextWithLinkProps { @@ -50,7 +46,8 @@ export const TextWithLink: React.FC = ({ e.preventDefault() action() }} - data-testid={`${testIdPrefix}Link`}> + data-testid={`${testIdPrefix}Link`} + > {label} ) diff --git a/packages/desktop/src/renderer/components/ui/Tooltip/Tooltip.tsx b/packages/desktop/src/renderer/components/ui/Tooltip/Tooltip.tsx index 20743725a2..fd555c48a0 100644 --- a/packages/desktop/src/renderer/components/ui/Tooltip/Tooltip.tsx +++ b/packages/desktop/src/renderer/components/ui/Tooltip/Tooltip.tsx @@ -5,9 +5,10 @@ import classNames from 'classnames' import MuiTooltip, { TooltipProps } from '@mui/material/Tooltip' // Styling mui tooltip requires workaround: https://mui.com/material-ui/guides/interoperability/#portals -const StyledTooltip = styled(({ className, ...props }: TooltipProps) => ( - -), {})(({ theme }) => ({ +const StyledTooltip = styled( + ({ className, ...props }: TooltipProps) => , + {} +)(({ theme }) => ({ '& .MuiTooltip-tooltip': { marginBottom: 5, background: theme.palette.colors.trueBlack, @@ -22,14 +23,14 @@ const StyledTooltip = styled(({ className, ...props }: TooltipProps) => ( '&:first-letter': { textTransform: 'capitalize', - } + }, }, '& .MuiTooltip-arrow': { '&:before': { - border: `1px solid ${theme.palette.colors.trueBlack}` + border: `1px solid ${theme.palette.colors.trueBlack}`, }, - color: theme.palette.colors.trueBlack - } + color: theme.palette.colors.trueBlack, + }, })) interface CustomTooltipProps { @@ -57,20 +58,10 @@ export const Tooltip: React.FC & CustomT - {titleHTML || ( - {title} - )} - - ) - } + title={title?.length === 0 ? '' : {titleHTML || {title}}} placement={placement} arrow - > + > {children} diff --git a/packages/desktop/src/renderer/components/ui/WindowWrapper/WindowWrapper.tsx b/packages/desktop/src/renderer/components/ui/WindowWrapper/WindowWrapper.tsx index acf9e9dead..54d69aef55 100644 --- a/packages/desktop/src/renderer/components/ui/WindowWrapper/WindowWrapper.tsx +++ b/packages/desktop/src/renderer/components/ui/WindowWrapper/WindowWrapper.tsx @@ -6,15 +6,15 @@ const PREFIX = 'WindowWrapper' const classes = { root: `${PREFIX}root`, - wrapper: `${PREFIX}wrapper` + wrapper: `${PREFIX}wrapper`, } const Root = styled('div')(() => ({ [`& .${classes.root}`]: {}, [`&.${classes.wrapper}`]: { - minHeight: '100vh' - } + minHeight: '100vh', + }, })) interface WindowWrapperProps { @@ -27,8 +27,9 @@ export const WindowWrapper: React.FC = ({ children, classNam + [className]: className, + })} + > {children} ) diff --git a/packages/desktop/src/renderer/components/widgets/MessagesDivider.test.tsx b/packages/desktop/src/renderer/components/widgets/MessagesDivider.test.tsx index ba9b110c90..e4893962cc 100644 --- a/packages/desktop/src/renderer/components/widgets/MessagesDivider.test.tsx +++ b/packages/desktop/src/renderer/components/widgets/MessagesDivider.test.tsx @@ -1,4 +1,3 @@ -/* eslint import/first: 0 */ import React from 'react' import { renderComponent } from '../../testUtils/renderComponent' diff --git a/packages/desktop/src/renderer/components/widgets/MessagesDivider.tsx b/packages/desktop/src/renderer/components/widgets/MessagesDivider.tsx index 5accce6bc6..d2e8792d38 100644 --- a/packages/desktop/src/renderer/components/widgets/MessagesDivider.tsx +++ b/packages/desktop/src/renderer/components/widgets/MessagesDivider.tsx @@ -9,27 +9,23 @@ const PREFIX = 'MessagesDivider' const classes = { root: `${PREFIX}root`, divider: `${PREFIX}divider`, - titleDiv: `${PREFIX}titleDiv` + titleDiv: `${PREFIX}titleDiv`, } -const StyledGrid = styled(Grid)(( - { - theme - } -) => ({ +const StyledGrid = styled(Grid)(({ theme }) => ({ [`& .${classes.root}`]: { - padding: 0 + padding: 0, }, [`& .${classes.divider}`]: { height: 1, - backgroundColor: theme.palette.colors.veryLightGray + backgroundColor: theme.palette.colors.veryLightGray, }, [`& .${classes.titleDiv}`]: { paddingLeft: 12, - paddingRight: 12 - } + paddingRight: 12, + }, })) interface MessagesDividerProps { diff --git a/packages/desktop/src/renderer/components/widgets/Settings/About.tsx b/packages/desktop/src/renderer/components/widgets/Settings/About.tsx index ce07b826ff..901cbc311e 100644 --- a/packages/desktop/src/renderer/components/widgets/Settings/About.tsx +++ b/packages/desktop/src/renderer/components/widgets/Settings/About.tsx @@ -9,27 +9,22 @@ const PREFIX = 'About' const classes = { title: `${PREFIX}title`, - titleDiv: `${PREFIX}titleDiv` + titleDiv: `${PREFIX}titleDiv`, } const StyledGrid = styled(Grid)(() => ({ [`& .${classes.title}`]: {}, [`& .${classes.titleDiv}`]: { - marginBottom: 24 - } + marginBottom: 24, + }, })) export const About: FC = () => { const version = app.getVersion() return ( - + About Quiet diff --git a/packages/desktop/src/renderer/components/widgets/WarningModal/WarningModal.stories.tsx b/packages/desktop/src/renderer/components/widgets/WarningModal/WarningModal.stories.tsx index d2113c0b1c..ec618f4f40 100644 --- a/packages/desktop/src/renderer/components/widgets/WarningModal/WarningModal.stories.tsx +++ b/packages/desktop/src/renderer/components/widgets/WarningModal/WarningModal.stories.tsx @@ -16,7 +16,7 @@ const args: WarningModalComponentProps = { console.log('Closed modal') }, title: 'Warning title', - subtitle: 'Warning description' + subtitle: 'Warning description', } Component.args = args @@ -24,7 +24,7 @@ Component.args = args const component: ComponentMeta = { title: 'Components/WarningModal', decorators: [withTheme], - component: WarningModalComponent + component: WarningModalComponent, } export default component diff --git a/packages/desktop/src/renderer/components/widgets/WarningModal/WarningModal.tsx b/packages/desktop/src/renderer/components/widgets/WarningModal/WarningModal.tsx index 471b8c93d6..0d83151cba 100644 --- a/packages/desktop/src/renderer/components/widgets/WarningModal/WarningModal.tsx +++ b/packages/desktop/src/renderer/components/widgets/WarningModal/WarningModal.tsx @@ -17,40 +17,36 @@ const classes = { button: `${PREFIX}button`, updateIcon: `${PREFIX}updateIcon`, title: `${PREFIX}title`, - subTitle: `${PREFIX}subTitle` + subTitle: `${PREFIX}subTitle`, } -const StyledModalContent = styled(Grid)(( - { - theme - } -) => ({ +const StyledModalContent = styled(Grid)(({ theme }) => ({ backgroundColor: theme.palette.colors.white, border: 'none', [`& .${classes.info}`]: { - marginTop: 38 + marginTop: 38, }, [`& .${classes.button}`]: { height: 55, fontSize: '0.9rem', - backgroundColor: theme.palette.colors.quietBlue + backgroundColor: theme.palette.colors.quietBlue, }, [`& .${classes.updateIcon}`]: { width: 102, - height: 102 + height: 102, }, [`& .${classes.title}`]: { marginTop: 24, - marginBottom: 16 + marginBottom: 16, }, [`& .${classes.subTitle}`]: { - marginBottom: 32 - } + marginBottom: 32, + }, })) export interface WarningModalComponentProps { @@ -71,12 +67,20 @@ export const WarningModalComponent: React.FC = ({ op - {title && {title}} + {title && ( + + {title} + + )} - {subtitle && {subtitle}} + {subtitle && ( + + {subtitle} + + )} diff --git a/packages/desktop/src/renderer/components/widgets/channels/BasicMessage.tsx b/packages/desktop/src/renderer/components/widgets/channels/BasicMessage.tsx index d820270109..853813f324 100644 --- a/packages/desktop/src/renderer/components/widgets/channels/BasicMessage.tsx +++ b/packages/desktop/src/renderer/components/widgets/channels/BasicMessage.tsx @@ -42,56 +42,52 @@ const classes = { iconBox: `${PREFIX}iconBox`, pending: `${PREFIX}pending`, info: `${PREFIX}info`, - infoIcon: `${PREFIX}infoIcon` + infoIcon: `${PREFIX}infoIcon`, } -const StyledListItem = styled(ListItem)(( - { - theme - } -) => ({ +const StyledListItem = styled(ListItem)(({ theme }) => ({ [`& .${classes.messageCard}`]: { - padding: '0 4px' + padding: '0 4px', }, [`&.${classes.wrapper}`]: { backgroundColor: theme.palette.colors.white, '&:hover': { - backgroundColor: theme.palette.colors.gray03 - } + backgroundColor: theme.palette.colors.gray03, + }, }, [`& .${classes.infoWrapper}`]: { - backgroundColor: `${theme.palette.colors.blue} !important` + backgroundColor: `${theme.palette.colors.blue} !important`, }, [`& .${classes.clickable}`]: { - cursor: 'pointer' + cursor: 'pointer', }, [`& .${classes.wrapperPending}`]: { - background: theme.palette.colors.white + background: theme.palette.colors.white, }, [`& .${classes.username}`]: { fontSize: 16, fontWeight: 500, marginTop: -4, - marginRight: 5 + marginRight: 5, }, [`& .${classes.statusIcon}`]: { color: theme.palette.colors.lightGray, fontSize: 21, - marginLeft: theme.spacing(1) + marginLeft: theme.spacing(1), }, [`& .${classes.broadcasted}`]: { - color: theme.palette.colors.lightGray + color: theme.palette.colors.lightGray, }, [`& .${classes.failed}`]: { - color: red[500] + color: red[500], }, [`& .${classes.avatar}`]: { @@ -100,43 +96,43 @@ const StyledListItem = styled(ListItem)(( marginRight: 10, marginBottom: 4, borderRadius: 4, - backgroundColor: theme.palette.colors.grayBackgroud + backgroundColor: theme.palette.colors.grayBackgroud, }, [`& .${classes.alignAvatar}`]: { marginTop: 2, marginLeft: 2, width: 32, - height: 32 + height: 32, }, [`& .${classes.moderation}`]: { cursor: 'pointer', - marginRight: 10 + marginRight: 10, }, [`& .${classes.time}`]: { color: theme.palette.colors.lightGray, fontSize: 14, marginTop: -4, - marginRight: 5 + marginRight: 5, }, [`& .${classes.iconBox}`]: { - marginTop: -4 + marginTop: -4, }, [`& .${classes.pending}`]: { - color: theme.palette.colors.lightGray + color: theme.palette.colors.lightGray, }, [`& .${classes.info}`]: { - color: theme.palette.colors.white + color: theme.palette.colors.white, }, [`& .${classes.infoIcon}`]: { - width: 32 - } + width: 32, + }, })) export const getTimeFormat = () => { @@ -167,7 +163,7 @@ export const BasicMessageComponent: React.FC { const messageDisplayData = messages[0] @@ -179,23 +175,17 @@ export const BasicMessageComponent: React.FC {}} - onMouseLeave={() => {}}> + onMouseLeave={() => {}} + > - + +
{infoMessage ? ( @@ -212,8 +202,9 @@ export const BasicMessageComponent: React.FC + [classes.pending]: pending, + })} + > {infoMessage ? 'Quiet' : messageDisplayData.nickname} @@ -221,8 +212,9 @@ export const BasicMessageComponent: React.FC + [classes.time]: true, + })} + > {messageDisplayData.date} @@ -233,7 +225,8 @@ export const BasicMessageComponent: React.FC + data-testid={`userMessages-${messageDisplayData.nickname}-${messageDisplayData.id}`} + > {messages.map((message, index) => { const pending = pendingMessages[message.id] !== undefined const downloadStatus = downloadStatuses[message.id] diff --git a/packages/desktop/src/renderer/components/widgets/channels/ChannelHeader.test.tsx b/packages/desktop/src/renderer/components/widgets/channels/ChannelHeader.test.tsx index 7dcde41876..ca8ea8b5f6 100644 --- a/packages/desktop/src/renderer/components/widgets/channels/ChannelHeader.test.tsx +++ b/packages/desktop/src/renderer/components/widgets/channels/ChannelHeader.test.tsx @@ -5,9 +5,7 @@ import { ChannelHeaderComponent } from './ChannelHeader' describe('ChannelHeader', () => { it('hides context menu', () => { - const result = renderComponent( - - ) + const result = renderComponent() expect(result.baseElement).toMatchInlineSnapshot(`
@@ -46,9 +44,7 @@ describe('ChannelHeader', () => { `) }) it('reveals context menu', () => { - const result = renderComponent( - - ) + const result = renderComponent() expect(result.baseElement).toMatchInlineSnapshot(`
diff --git a/packages/desktop/src/renderer/components/widgets/channels/ChannelHeader.tsx b/packages/desktop/src/renderer/components/widgets/channels/ChannelHeader.tsx index 1868e8e464..0d065ecd10 100644 --- a/packages/desktop/src/renderer/components/widgets/channels/ChannelHeader.tsx +++ b/packages/desktop/src/renderer/components/widgets/channels/ChannelHeader.tsx @@ -28,7 +28,7 @@ const classes = { iconDiv: `${PREFIX}iconDiv`, iconButton: `${PREFIX}iconButton`, bold: `${PREFIX}bold`, - menu: `${PREFIX}menu` + menu: `${PREFIX}menu`, } const Root = styled('div')(({ theme }) => ({ @@ -36,20 +36,20 @@ const Root = styled('div')(({ theme }) => ({ height: '75px', paddingLeft: 20, paddingRight: 24, - borderBottom: `1px solid ${theme.palette.colors.veryLightGray}` + borderBottom: `1px solid ${theme.palette.colors.veryLightGray}`, }, [`& .${classes.title}`]: { fontSize: '1rem', - lineHeight: '1.66' + lineHeight: '1.66', }, [`& .${classes.subtitle}`]: { - fontSize: '0.8rem' + fontSize: '0.8rem', }, [`& .${classes.spendButton}`]: { - fontSize: 13 + fontSize: 13, }, [`& .${classes.actions}`]: {}, @@ -59,7 +59,7 @@ const Root = styled('div')(({ theme }) => ({ marginRight: 18, borderRadius: 4, borderStyle: 'solid', - borderColor: theme.palette.colors.gray03 + borderColor: theme.palette.colors.gray03, }, [`& .${classes.tab}`]: { @@ -72,47 +72,47 @@ const Root = styled('div')(({ theme }) => ({ textTransform: 'none', backgroundColor: theme.palette.colors.gray03, color: theme.palette.colors.gray40, - fontWeight: 'normal' + fontWeight: 'normal', }, [`& .${classes.tabs}`]: { - minHeight: 0 + minHeight: 0, }, [`& .${classes.selected}`]: { color: theme.palette.colors.trueBlack, - backgroundColor: theme.palette.colors.white + backgroundColor: theme.palette.colors.white, }, [`& .${classes.indicator}`]: { - maxHeight: 0 + maxHeight: 0, }, [`& .${classes.descriptionDiv}`]: { top: 75, padding: '12px 25px 12px 20px', backgroundColor: theme.palette.colors.white, - boxShadow: `0px 1px 0px ${theme.palette.colors.veryLightGray}` + boxShadow: `0px 1px 0px ${theme.palette.colors.veryLightGray}`, }, [`&.${classes.wrapper}`]: {}, [`& .${classes.iconDiv}`]: { - marginLeft: 12 + marginLeft: 12, }, [`& .${classes.iconButton}`]: { - padding: 0 + padding: 0, }, [`& .${classes.bold}`]: { - fontWeight: 500 + fontWeight: 500, }, [`& .${classes.menu}`]: { padding: '20px', - cursor: 'pointer' - } + cursor: 'pointer', + }, })) export interface ChannelHeaderProps { @@ -124,7 +124,7 @@ export interface ChannelHeaderProps { export const ChannelHeaderComponent: React.FC = ({ channelName, openContextMenu, - enableContextMenu + enableContextMenu, }) => { const debounce = (fn: () => void, ms: number) => { let timer: ReturnType | null @@ -157,12 +157,7 @@ export const ChannelHeaderComponent: React.FC = ({ return ( - + @@ -172,9 +167,10 @@ export const ChannelHeaderComponent: React.FC = ({ variant='subtitle1' className={classNames({ [classes.title]: true, - [classes.bold]: true + [classes.bold]: true, })} - data-testid={'channelTitle'}> + data-testid={'channelTitle'} + > {`#${channelName?.substring(0, 20)}`} @@ -187,7 +183,8 @@ export const ChannelHeaderComponent: React.FC = ({ className={classes.actions} justifyContent='flex-end' alignContent='center' - alignItems='center'> + alignItems='center' + > {enableContextMenu && ( 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 8ec9f22460..bd8d8ea4d9 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 @@ -21,31 +21,31 @@ const args: ChannelInputProps = { channelId: 'channelId', channelParticipants: [{ nickname: 'john' }, { nickname: 'emily' }], inputPlaceholder: '#general as @alice', - onChange: function (_arg: string): void { }, + onChange: function (_arg: string): void {}, onKeyPress: function (input: string): void { console.log('send message', input) }, infoClass: '', - setInfoClass: function (_arg: string): void { }, - openFilesDialog: () => { }, - handleOpenFiles: (_arg) => { }, - handleClipboardFiles: function (arg: ArrayBuffer, ext: string, name: string): void {} + setInfoClass: function (_arg: string): void {}, + openFilesDialog: () => {}, + handleOpenFiles: _arg => {}, + handleClipboardFiles: function (arg: ArrayBuffer, ext: string, name: string): void {}, } const argsDisabledInput: ChannelInputProps = { channelId: 'channelId', channelParticipants: [{ nickname: 'john' }, { nickname: 'emily' }], inputPlaceholder: '#general as @alice', - onChange: function (_arg: string): void { }, + onChange: function (_arg: string): void {}, onKeyPress: function (input: string): void { console.log('send message', input) }, infoClass: '', - setInfoClass: function (_arg: string): void { }, + setInfoClass: function (_arg: string): void {}, inputState: INPUT_STATE.NOT_CONNECTED, - openFilesDialog: () => { }, - handleOpenFiles: (_arg) => { }, - handleClipboardFiles: function (arg: ArrayBuffer, ext: string, name: string): void {} + openFilesDialog: () => {}, + handleOpenFiles: _arg => {}, + handleClipboardFiles: function (arg: ArrayBuffer, ext: string, name: string): void {}, } Component.args = args @@ -54,7 +54,7 @@ Disabled.args = argsDisabledInput const component: ComponentMeta = { title: 'Components/ChannelInput', decorators: [withTheme], - component: ChannelInputComponent + component: ChannelInputComponent, } export default component 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 457117f6ae..817696294e 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 @@ -20,8 +20,9 @@ describe('ChannelInput', () => { infoClass={''} setInfoClass={jest.fn()} openFilesDialog={jest.fn()} - handleOpenFiles={jest.fn()} handleClipboardFiles={function (arg: ArrayBuffer, ext: string, name: string): void {}} - /> + handleOpenFiles={jest.fn()} + handleClipboardFiles={function (arg: ArrayBuffer, ext: string, name: string): void {}} + /> ) expect(result.baseElement).toMatchInlineSnapshot(` @@ -112,8 +113,9 @@ describe('ChannelInput', () => { setInfoClass={jest.fn()} inputState={INPUT_STATE.NOT_CONNECTED} openFilesDialog={jest.fn()} - handleOpenFiles={jest.fn()} handleClipboardFiles={function (arg: ArrayBuffer, ext: string, name: string): void { - } } /> + handleOpenFiles={jest.fn()} + handleClipboardFiles={function (arg: ArrayBuffer, ext: string, name: string): void {}} + /> ) expect(result.baseElement).toMatchInlineSnapshot(` @@ -203,7 +205,7 @@ describe('ChannelInput', () => { window.ResizeObserver = jest.fn().mockImplementation(() => ({ observe: jest.fn(), unobserve: jest.fn(), - disconnect: jest.fn() + disconnect: jest.fn(), })) window.HTMLElement.prototype.scrollTo = jest.fn() document.execCommand = jest.fn() @@ -222,20 +224,19 @@ describe('ChannelInput', () => { const { store } = await prepareStore() const factory = await getFactory(store) - await factory.create['payload']>( - 'Identity', - { nickname: 'alice' } - ) + await factory.create['payload']>('Identity', { + nickname: 'alice', + }) const fileContent: FileContent = { path: `data:image/jpeg;base64,${base64Image}`, name: 'imageName', - ext: '.png' + ext: '.png', } const fileData = { ...fileContent, - arrayBuffer: bytes.buffer // need for test + arrayBuffer: bytes.buffer, // need for test } renderComponent( @@ -270,41 +271,34 @@ describe('ChannelInput', () => { size: 4135, type: 'image/png', webkitRelativePath: '', - arrayBuffer: () => fileData.arrayBuffer - } - ] - } + arrayBuffer: () => fileData.arrayBuffer, + }, + ], + }, }) await fireEvent(input, paste) expect(mockHandleClipboardFiles).toHaveBeenCalled() - expect(mockHandleClipboardFiles).toHaveBeenCalledWith( - fileData.arrayBuffer, - fileData.ext, - fileData.name - ) + expect(mockHandleClipboardFiles).toHaveBeenCalledWith(fileData.arrayBuffer, fileData.ext, fileData.name) const filesData = { 1: { path: fileData.path, name: fileData.name, - ext: fileData.ext - } + ext: fileData.ext, + }, } const filesDataWithUnsuportedFile = { 1: { path: fileData.path, name: fileData.name, - ext: '.unsupported' - } + ext: '.unsupported', + }, } - renderComponent( - , - store - ) + renderComponent(, store) // image with data from onPaste event appear const image: HTMLImageElement = screen.getByAltText(fileData.name) @@ -314,10 +308,7 @@ describe('ChannelInput', () => { expect(mockUnsupportedModalHandleOpen).not.toHaveBeenCalled() renderComponent( - , + , store ) diff --git a/packages/desktop/src/renderer/components/widgets/channels/ChannelInput/ChannelInput.tsx b/packages/desktop/src/renderer/components/widgets/channels/ChannelInput/ChannelInput.tsx index 476fbe7b88..321c00dd74 100644 --- a/packages/desktop/src/renderer/components/widgets/channels/ChannelInput/ChannelInput.tsx +++ b/packages/desktop/src/renderer/components/widgets/channels/ChannelInput/ChannelInput.tsx @@ -40,27 +40,23 @@ const classes = { linkBlue: `${PREFIX}linkBlue`, notAllowed: `${PREFIX}notAllowed`, inputFiles: `${PREFIX}inputFiles`, - icons: `${PREFIX}icons` + icons: `${PREFIX}icons`, } -const StyledChannelInput = styled(Grid)(( - { - theme - } -) => ({ +const StyledChannelInput = styled(Grid)(({ theme }) => ({ [`&.${classes.root}`]: { background: '#fff', height: '100%', - width: '100%' + width: '100%', }, [`& .${classes.rootContent}`]: { background: '#fff', height: '100%', - width: '100%' + width: '100%', }, '@keyframes blinker': { from: { opacity: 0 }, - to: { opacity: 1 } + to: { opacity: 1 }, }, [`& .${classes.input}`]: { whiteSpace: 'pre-line', @@ -73,12 +69,12 @@ const StyledChannelInput = styled(Grid)(( '&:before': { content: 'attr(placeholder)', display: 'block', - color: '#aaa' - } + color: '#aaa', + }, }, wordBreak: 'break-word', position: 'relative', - paddingRight: '60px' + paddingRight: '60px', }, [`& .${classes.textfield}`]: { border: `1px solid ${theme.palette.colors.veryLightGray}`, @@ -86,7 +82,7 @@ const StyledChannelInput = styled(Grid)(( overflowY: 'auto', borderRadius: 4, '&:hover': { - borderColor: theme.palette.colors.trueBlack + borderColor: theme.palette.colors.trueBlack, }, display: 'flex', flexDirection: 'column', @@ -95,26 +91,26 @@ const StyledChannelInput = styled(Grid)(( alignItems: 'stretch', alignContent: 'stretch', width: '100%', - position: 'relative' + position: 'relative', }, [`& .${classes.inputsDiv}`]: { paddingLeft: '20px', paddingRight: '20px', width: '100%', margin: '0px', - position: 'relative' + position: 'relative', }, [`& .${classes.disabledBottomMargin}`]: { - marginBottom: 0 + marginBottom: 0, }, [`& .${classes.warningIcon}`]: { - color: orange[500] + color: orange[500], }, [`& .${classes.blinkAnimation}`]: { animationName: '$blinker', animationDuration: '1s', animationTimingFunction: 'linear', - animationIterationCount: 1 + animationIterationCount: 1, }, [`& .${classes.backdrop}`]: { height: 'auto', @@ -122,10 +118,10 @@ const StyledChannelInput = styled(Grid)(( backgroundColor: 'rgba(0, 0, 0, 0.75)', WebkitTapHighlightColor: 'transparent', pointerEvents: 'none', - touchAction: 'none' + touchAction: 'none', }, [`& .${classes.focused}`]: { - borderColor: theme.palette.colors.trueBlack + borderColor: theme.palette.colors.trueBlack, }, [`& .${classes.iconButton}`]: { cursor: 'pointer', @@ -134,61 +130,60 @@ const StyledChannelInput = styled(Grid)(( color: '#808080', '&:hover': { color: 'black', - border: '1px solid black' - + border: '1px solid black', }, border: '1px solid #808080', // boxShadow: '-.75px -.75px 1px #808080', borderRadius: '100%', width: '23px', - height: '23px' + height: '23px', }, [`& .${classes.emoji}`]: { cursor: 'pointer', position: 'relative', - float: 'right' + float: 'right', }, [`& .${classes.highlight}`]: { color: theme.palette.colors.lushSky, backgroundColor: theme.palette.colors.lushSky12, padding: 5, - borderRadius: 4 + borderRadius: 4, }, [`& .${classes.actions}`]: { postion: 'relative', float: 'right', - padding: '5px' + padding: '5px', }, [`& .${classes.picker}`]: { position: 'fixed', bottom: 60, - right: 15 + right: 15, }, [`& .${classes.errorIcon}`]: { display: 'flex', justify: 'center', alignItems: 'center', marginLeft: 20, - marginRight: 5 + marginRight: 5, }, [`& .${classes.errorText}`]: { - color: theme.palette.colors.trueBlack + color: theme.palette.colors.trueBlack, }, [`& .${classes.errorBox}`]: { - marginTop: 5 + marginTop: 5, }, [`& .${classes.linkBlue}`]: { fontWeight: 'normal', fontStyle: 'normal', cursor: 'pointer', - color: theme.palette.colors.linkBlue + color: theme.palette.colors.linkBlue, }, [`& .${classes.notAllowed}`]: { - cursor: 'not-allowed' + cursor: 'not-allowed', }, [`& .${classes.inputFiles}`]: { position: 'relative', - float: 'left' + float: 'left', }, [`& .${classes.icons}`]: { position: 'absolute', @@ -200,8 +195,8 @@ const StyledChannelInput = styled(Grid)(( flexWrap: 'wrap', justifyContent: 'flexStart', alignItems: 'center', - alignCntent: 'stretch' - } + alignCntent: 'stretch', + }, })) export interface ChannelInputProps { @@ -218,7 +213,7 @@ export interface ChannelInputProps { children?: ReactElement openFilesDialog: () => void handleClipboardFiles: (arg: ArrayBuffer, ext: string, name: string) => void - handleOpenFiles: (arg: {files: any[]}) => void + handleOpenFiles: (arg: { files: any[] }) => void } export const ChannelInputComponent: React.FC = ({ @@ -234,12 +229,14 @@ export const ChannelInputComponent: React.FC = ({ children, openFilesDialog, handleClipboardFiles, - handleOpenFiles + handleOpenFiles, }) => { const [_anchorEl, setAnchorEl] = React.useState() - const [mentionsToSelect, setMentionsToSelect] = React.useState>([]) + const [mentionsToSelect, setMentionsToSelect] = React.useState< + Array<{ + nickname: string + }> + >([]) const messageRef = React.useRef() const refSelected = React.useRef() const isFirstRenderRef = React.useRef(true) @@ -316,11 +313,12 @@ export const ChannelInputComponent: React.FC = ({ } nickname = nickname ?? '' - const possibleMentions = channelParticipants.filter( - user => - user.nickname.startsWith(nickname) && - !channelParticipants.find(user => user.nickname === nickname) - ).filter(isDefined) + const possibleMentions = channelParticipants + .filter( + user => + user.nickname.startsWith(nickname) && !channelParticipants.find(user => user.nickname === nickname) + ) + .filter(isDefined) if (JSON.stringify(mentionsToSelect) !== JSON.stringify(possibleMentions)) { setMentionsToSelect(possibleMentions) @@ -350,9 +348,7 @@ export const ChannelInputComponent: React.FC = ({ // or the end of the focus line, whichever is shorter range.setStart( focusLine, - anchorLinePosition < focusLine.nodeValue.length - ? anchorLinePosition - : focusLine.nodeValue.length + anchorLinePosition < focusLine.nodeValue.length ? anchorLinePosition : focusLine.nodeValue.length ) // Remove the range from the anchor line const selection = window.getSelection() @@ -439,10 +435,7 @@ export const ChannelInputComponent: React.FC = ({ } // If we're on the bottom line, go to the end if (selection.anchorNode?.nodeValue) { - caretLineTraversal( - selection.anchorNode, - selection.anchorNode.nodeValue.length - ) + caretLineTraversal(selection.anchorNode, selection.anchorNode.nodeValue.length) } } if (e.key === 'ArrowUp') { @@ -489,7 +482,7 @@ export const ChannelInputComponent: React.FC = ({ setHtmlMessage, infoClass, setInfoClass, - setSelected + setSelected, ] ) @@ -503,16 +496,17 @@ export const ChannelInputComponent: React.FC = ({ + justifyContent='center' + > {/* {mentionsToSelect.map((target, index) => ( = ({ justifyContent='center' spacing={0} className={classNames({ - [classes.inputsDiv]: true - })}> + [classes.inputsDiv]: true, + })} + > { setFocused(false) - }}> + }} + > + alignItems='center' + > = ({ html={sanitizedHtml} onChange={onChangeCb} onKeyDown={onKeyDownCb} - onPaste={async (e) => { + onPaste={async e => { e.preventDefault() const files = e.clipboardData.files for (let i = 0; i < files.length; i++) { @@ -603,7 +600,9 @@ export const ChannelInputComponent: React.FC = ({ type='file' onChange={handleFileInput} // Value needs to be cleared otherwise one can't upload same image twice - onClick={(e) => { (e.target as HTMLInputElement).value = '' }} // TODO: check + onClick={e => { + ;(e.target as HTMLInputElement).value = '' + }} // TODO: check accept='*' multiple hidden @@ -625,13 +624,13 @@ export const ChannelInputComponent: React.FC = ({ setEmojiHovered(false) }} /> - {openEmoji && ( { setOpenEmoji(false) - }}> + }} + >
{ @@ -651,9 +650,7 @@ export const ChannelInputComponent: React.FC = ({ - + ) diff --git a/packages/desktop/src/renderer/components/widgets/channels/ChannelInput/ChannelInputInfoMessage.tsx b/packages/desktop/src/renderer/components/widgets/channels/ChannelInput/ChannelInputInfoMessage.tsx index 7a59e622fb..883978a034 100644 --- a/packages/desktop/src/renderer/components/widgets/channels/ChannelInput/ChannelInputInfoMessage.tsx +++ b/packages/desktop/src/renderer/components/widgets/channels/ChannelInput/ChannelInputInfoMessage.tsx @@ -7,29 +7,25 @@ const PREFIX = 'ChannelInputInfoMessage' const classes = { info: `${PREFIX}info`, bold: `${PREFIX}bold`, - boot: `${PREFIX}boot` + boot: `${PREFIX}boot`, } -const StyledGrid = styled(Grid)(( - { - theme - } -) => ({ +const StyledGrid = styled(Grid)(({ theme }) => ({ [`& .${classes.info}`]: { color: theme.palette.colors.trueBlack, width: '100px', - letterSpacing: '0.4px' + letterSpacing: '0.4px', }, [`& .${classes.bold}`]: { - fontWeight: 'bold' + fontWeight: 'bold', }, [`&.${classes.boot}`]: { height: '24px', width: '100%', - padding: '0px 20px' - } + padding: '0px 20px', + }, })) interface ChannelInputInfoMessageProps { @@ -42,7 +38,7 @@ const ChannelInputInfoMessage: React.FC = ({ showI {showInfoMessage && ( - Initializing community. This may take a few minutes... + Initializing community. This may take a few minutes... )} diff --git a/packages/desktop/src/renderer/components/widgets/channels/ChannelInput/InputState.enum.ts b/packages/desktop/src/renderer/components/widgets/channels/ChannelInput/InputState.enum.ts index bc85131ecc..494ebca3bf 100644 --- a/packages/desktop/src/renderer/components/widgets/channels/ChannelInput/InputState.enum.ts +++ b/packages/desktop/src/renderer/components/widgets/channels/ChannelInput/InputState.enum.ts @@ -1,4 +1,4 @@ export enum INPUT_STATE { NOT_CONNECTED = 0, - AVAILABLE = 1 + AVAILABLE = 1, } diff --git a/packages/desktop/src/renderer/components/widgets/channels/ChannelInput/MentionElement.stories.tsx b/packages/desktop/src/renderer/components/widgets/channels/ChannelInput/MentionElement.stories.tsx index 11de157ecf..a19ef13b5b 100644 --- a/packages/desktop/src/renderer/components/widgets/channels/ChannelInput/MentionElement.stories.tsx +++ b/packages/desktop/src/renderer/components/widgets/channels/ChannelInput/MentionElement.stories.tsx @@ -16,7 +16,7 @@ const args: MentionElementProps = { onMouseEnter: function (): void {}, onClick: function (): void {}, participant: true, - highlight: false + highlight: false, } Component.args = args @@ -24,7 +24,7 @@ Component.args = args const component: ComponentMeta = { title: 'Components/MentionElement', decorators: [withTheme], - component: MentionElement + component: MentionElement, } export default component diff --git a/packages/desktop/src/renderer/components/widgets/channels/ChannelInput/MentionElement.tsx b/packages/desktop/src/renderer/components/widgets/channels/ChannelInput/MentionElement.tsx index 7be2940b53..7d97ae2030 100644 --- a/packages/desktop/src/renderer/components/widgets/channels/ChannelInput/MentionElement.tsx +++ b/packages/desktop/src/renderer/components/widgets/channels/ChannelInput/MentionElement.tsx @@ -15,56 +15,52 @@ const classes = { highlight: `${PREFIX}highlight`, name: `${PREFIX}name`, caption: `${PREFIX}caption`, - captionHighlight: `${PREFIX}captionHighlight` + captionHighlight: `${PREFIX}captionHighlight`, } -const StyledGrid = styled(Grid)(( - { - theme - } -) => ({ +const StyledGrid = styled(Grid)(({ theme }) => ({ [`&.${classes.root}`]: { paddingTop: 10, - paddingLeft: 16 + paddingLeft: 16, }, [`& .${classes.avatarDiv}`]: { maxHeight: 18, maxWidth: 18, borderRadius: 4, - backgroundColor: theme.palette.colors.grayBackgroud + backgroundColor: theme.palette.colors.grayBackgroud, }, [`& .${classes.alignAvatar}`]: { width: 17, height: 17, marginLeft: 1, - marginTop: 1 + marginTop: 1, }, [`& .${classes.data}`]: { - marginLeft: 9 + marginLeft: 9, }, [`&.${classes.highlight}`]: { backgroundColor: theme.palette.colors.lushSky, - color: theme.palette.colors.white + color: theme.palette.colors.white, }, [`& .${classes.name}`]: { - marginTop: -4 + marginTop: -4, }, [`& .${classes.caption}`]: { lineHeight: '18px', fontSize: 12, letterSpacing: 0.4, - color: 'rgba(0,0,0,0.6)' + color: 'rgba(0,0,0,0.6)', }, [`& .${classes.captionHighlight}`]: { - color: 'rgba(255,255,255,0.6)' - } + color: 'rgba(255,255,255,0.6)', + }, })) export interface MentionElementProps { @@ -82,14 +78,14 @@ export const MentionElement: React.FC = ({ participant = false, highlight = false, onMouseEnter, - onClick + onClick, }) => { return ( onClick(e)} @@ -108,7 +104,7 @@ export const MentionElement: React.FC = ({ variant='body2' className={classNames({ [classes.caption]: true, - [classes.captionHighlight]: highlight + [classes.captionHighlight]: highlight, })} >{`Participant in ${channelName}`} )} diff --git a/packages/desktop/src/renderer/components/widgets/channels/ChannelInput/MentionPoper.tsx b/packages/desktop/src/renderer/components/widgets/channels/ChannelInput/MentionPoper.tsx index d3e0be5fc1..3150523ff0 100644 --- a/packages/desktop/src/renderer/components/widgets/channels/ChannelInput/MentionPoper.tsx +++ b/packages/desktop/src/renderer/components/widgets/channels/ChannelInput/MentionPoper.tsx @@ -11,7 +11,7 @@ const PREFIX = 'MentionPoper' const classes = { root: `${PREFIX}root`, thumb: `${PREFIX}thumb`, - divider: `${PREFIX}divider` + divider: `${PREFIX}divider`, } const maxHeight = 230 @@ -23,19 +23,19 @@ const StyledPopper = styled(Popper)({ borderRadius: 8, overflow: 'hidden', boxShadow: '0px 2px 25px rgba(0,0,0,0.2)', - marginBottom: 10 + marginBottom: 10, }, [`& .${classes.thumb}`]: { backgroundColor: 'rgba(0,0,0,0.46)', borderRadius: 100, marginLeft: -3, - width: 8 + width: 8, }, [`& .${classes.divider}`]: { width: 14, borderLeft: '1px solid', - borderColor: 'rgba(0,0,0,0.08)' - } + borderColor: 'rgba(0,0,0,0.08)', + }, }) function isDivElement(element: Element | undefined): element is HTMLDivElement { @@ -61,7 +61,7 @@ export const MentionPoper: React.FC = ({ anchorEl, children, React.useEffect(() => { if (anchorEl && popperRef.current) { if (children.length) { - const popperContainer = (popperRef.current as unknown) as HTMLDivElement + const popperContainer = popperRef.current as unknown as HTMLDivElement setPositionY(anchorEl.offsetTop - popperContainer.clientHeight) setPositionX(anchorEl.offsetLeft) } else { @@ -86,10 +86,7 @@ export const MentionPoper: React.FC = ({ anchorEl, children, React.useEffect(() => { const element = anchor.current?.children[selected] if (isDivElement(element) && scrollbarRef?.current) { - if ( - element.offsetTop > - scrollbarRef.current.getScrollTop() + maxHeight - element.clientHeight - ) { + if (element.offsetTop > scrollbarRef.current.getScrollTop() + maxHeight - element.clientHeight) { scrollbarRef.current.scrollTop(element.offsetTop + element.clientHeight - maxHeight) } if (element.offsetTop < scrollbarRef.current.getScrollTop()) { @@ -104,16 +101,18 @@ export const MentionPoper: React.FC = ({ anchorEl, children, className={classes.root} style={{ transform: `translate3d(${positionX}px,${positionY}px,0px`, - zIndex: positionX && positionY ? 0 : -1 + zIndex: positionX && positionY ? 0 : -1, }} // @ts-expect-error - ref={popperRef}> + ref={popperRef} + >
}> + renderThumbVertical={() =>
} + > diff --git a/packages/desktop/src/renderer/components/widgets/channels/ChannelInputAction.tsx b/packages/desktop/src/renderer/components/widgets/channels/ChannelInputAction.tsx index f555477cfc..337979f548 100644 --- a/packages/desktop/src/renderer/components/widgets/channels/ChannelInputAction.tsx +++ b/packages/desktop/src/renderer/components/widgets/channels/ChannelInputAction.tsx @@ -9,21 +9,10 @@ interface ChannelInputActionProps { disabled?: boolean } -export const ChannelInputAction: React.FC = ({ - disabled = false -}) => { +export const ChannelInputAction: React.FC = ({ disabled = false }) => { return ( - - {}} - title='Send money' - /> + + {}} title='Send money' /> ) } diff --git a/packages/desktop/src/renderer/components/widgets/channels/ChannelMessageActions.tsx b/packages/desktop/src/renderer/components/widgets/channels/ChannelMessageActions.tsx index d25fe4690b..5e29342c20 100644 --- a/packages/desktop/src/renderer/components/widgets/channels/ChannelMessageActions.tsx +++ b/packages/desktop/src/renderer/components/widgets/channels/ChannelMessageActions.tsx @@ -12,18 +12,14 @@ const PREFIX = 'ChannelMessageActions' const classes = { warrning: `${PREFIX}warrning`, tryAgain: `${PREFIX}tryAgain`, - pointer: `${PREFIX}pointer` + pointer: `${PREFIX}pointer`, } -const StyledGrid = styled(Grid)(( - { - theme - } -) => ({ +const StyledGrid = styled(Grid)(({ theme }) => ({ [`& .${classes.warrning}`]: { marginLeft: 8, letterSpacing: 0.4, - color: theme.palette.colors.error + color: theme.palette.colors.error, }, [`& .${classes.tryAgain}`]: { @@ -31,13 +27,13 @@ const StyledGrid = styled(Grid)(( letterSpacing: 0.4, color: theme.palette.colors.linkBlue, '&:hover': { - color: theme.palette.colors.blue - } + color: theme.palette.colors.blue, + }, }, [`& .${classes.pointer}`]: { - cursor: 'pointer' - } + cursor: 'pointer', + }, })) interface ChannelMessageActionsProps { diff --git a/packages/desktop/src/renderer/components/widgets/channels/ChannelMessages.test.tsx b/packages/desktop/src/renderer/components/widgets/channels/ChannelMessages.test.tsx index b84738d7ed..1face89542 100644 --- a/packages/desktop/src/renderer/components/widgets/channels/ChannelMessages.test.tsx +++ b/packages/desktop/src/renderer/components/widgets/channels/ChannelMessages.test.tsx @@ -8,7 +8,7 @@ describe('ChannelMessages', () => { window.ResizeObserver = jest.fn().mockImplementation(() => ({ observe: jest.fn(), unobserve: jest.fn(), - disconnect: jest.fn() + disconnect: jest.fn(), })) }) @@ -19,13 +19,13 @@ describe('ChannelMessages', () => { message: 'string', createdAt: 1636995488.44, date: 'string', - nickname: 'string' + nickname: 'string', } jest.spyOn(DateTime, 'utc').mockImplementationOnce(() => DateTime.utc(2019, 3, 7, 13, 3, 48)) const messages = { - Today: [[message]] + Today: [[message]], } const result = renderComponent( diff --git a/packages/desktop/src/renderer/components/widgets/channels/ChannelMessages.tsx b/packages/desktop/src/renderer/components/widgets/channels/ChannelMessages.tsx index bb785093e8..70ed09fc8e 100644 --- a/packages/desktop/src/renderer/components/widgets/channels/ChannelMessages.tsx +++ b/packages/desktop/src/renderer/components/widgets/channels/ChannelMessages.tsx @@ -23,50 +23,46 @@ const classes = { link: `${PREFIX}link`, info: `${PREFIX}info`, item: `${PREFIX}item`, - bold: `${PREFIX}bold` + bold: `${PREFIX}bold`, } -const Root = styled('div')(( - { - theme - } -) => ({ +const Root = styled('div')(({ theme }) => ({ [`& .${classes.spinner}`]: { top: '50%', textAlign: 'center', position: 'relative', - transform: 'translate(0, -50%)' + transform: 'translate(0, -50%)', }, [`&.${classes.scroll}`]: { overflow: 'scroll', overflowX: 'hidden', - height: '100%' + height: '100%', }, [`& .${classes.list}`]: { backgroundColor: theme.palette.colors.white, - width: '100%' + width: '100%', }, [`& .${classes.link}`]: { color: theme.palette.colors.lushSky, - cursor: 'pointer' + cursor: 'pointer', }, [`& .${classes.info}`]: { color: theme.palette.colors.trueBlack, - letterSpacing: '0.4px' + letterSpacing: '0.4px', }, [`& .${classes.item}`]: { backgroundColor: theme.palette.colors.gray03, - padding: '9px 16px' + padding: '9px 16px', }, [`& .${classes.bold}`]: { - fontWeight: 'bold' - } + fontWeight: 'bold', + }, })) export const fetchingChannelMessagesText = 'Fetching channel messages...' @@ -99,7 +95,7 @@ export const ChannelMessagesComponent: React.FC { const spinnerMessage = pendingGeneralChannelRecreation ? deletingChannelMessage : fetchingChannelMessagesText const listRef = useRef(null) @@ -121,7 +117,7 @@ export const ChannelMessagesComponent: React.FC { @@ -133,18 +129,9 @@ export const ChannelMessagesComponent: React.FC + {Object.values(messages).length < 1 && ( - + )} {Object.keys(messages).map(day => { 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 3f4a6de0a5..459aebc262 100644 --- a/packages/desktop/src/renderer/components/widgets/channels/NestedMessageContent.test.tsx +++ b/packages/desktop/src/renderer/components/widgets/channels/NestedMessageContent.test.tsx @@ -1,9 +1,6 @@ import React from 'react' import '@testing-library/jest-dom/extend-expect' -import { - DownloadState, - DownloadStatus -} from '@quiet/types' +import { DownloadState, DownloadStatus } from '@quiet/types' import { AUTODOWNLOAD_SIZE_LIMIT } from '@quiet/state-manager' import { generateMessages, renderComponent } from '../../../testUtils' import { FileActionsProps } from '../../Channel/File/FileComponent/FileComponent' @@ -14,9 +11,7 @@ import NestedMessageContent, { NestedMessageContentProps } from './NestedMessage describe('NestedMessageContent', () => { it('renders message', () => { const messages = generateMessages() - const result = renderComponent( - - ) + const result = renderComponent() expect(result.baseElement).toMatchInlineSnapshot(`
@@ -37,9 +32,7 @@ describe('NestedMessageContent', () => { it('renders pending message', () => { const messages = generateMessages() - const result = renderComponent( - - ) + const result = renderComponent() expect(result.baseElement).toMatchInlineSnapshot(`
@@ -74,9 +67,9 @@ describe('NestedMessageContent', () => { size: AUTODOWNLOAD_SIZE_LIMIT - 2048, message: { id: 'string', - channelId: 'general' - } - } + channelId: 'general', + }, + }, } const downloadStatus: DownloadStatus = { mid: message.id, @@ -85,25 +78,18 @@ describe('NestedMessageContent', () => { downloadProgress: { size: 10000, downloaded: 10000, - transferSpeed: 500 - } + transferSpeed: 500, + }, } const result = renderComponent( - + ) expect(await screen.findByText('File not valid. Download canceled.')).toBeVisible() }) it('renders info message', () => { const messages = generateMessages({ type: 3 }) - const result = renderComponent( - - ) + const result = renderComponent() expect(result.baseElement).toMatchInlineSnapshot(`
@@ -137,13 +123,11 @@ describe('NestedMessageContent', () => { size: AUTODOWNLOAD_SIZE_LIMIT - 2048, message: { id: 'string', - channelId: 'general' - } - } + channelId: 'general', + }, + }, } - const result = renderComponent( - - ) + const result = renderComponent() expect(result.baseElement).toMatchInlineSnapshot(`
@@ -199,9 +183,9 @@ describe('NestedMessageContent', () => { size: AUTODOWNLOAD_SIZE_LIMIT + 2048, message: { id: 'string', - channelId: 'general' - } - } + channelId: 'general', + }, + }, } const fileComponentProps: NestedMessageContentProps & FileActionsProps = { @@ -212,15 +196,15 @@ describe('NestedMessageContent', () => { downloadProgress: { size: AUTODOWNLOAD_SIZE_LIMIT + 2048, downloaded: AUTODOWNLOAD_SIZE_LIMIT / 2, - transferSpeed: 1000 - } + transferSpeed: 1000, + }, }, openUrl: jest.fn(), openContainingFolder: jest.fn(), downloadFile: jest.fn(), cancelDownload: jest.fn(), message: message, - pending: false + pending: false, } const result = renderComponent() diff --git a/packages/desktop/src/renderer/components/widgets/channels/NestedMessageContent.tsx b/packages/desktop/src/renderer/components/widgets/channels/NestedMessageContent.tsx index f7b0f08399..fdb02adddd 100644 --- a/packages/desktop/src/renderer/components/widgets/channels/NestedMessageContent.tsx +++ b/packages/desktop/src/renderer/components/widgets/channels/NestedMessageContent.tsx @@ -17,7 +17,7 @@ const PREFIX = 'NestedMessageContent' const classes = { message: `${PREFIX}message`, pending: `${PREFIX}pending`, - info: `${PREFIX}info` + info: `${PREFIX}info`, } const StyledGrid = styled(Grid)(() => ({ @@ -25,16 +25,16 @@ const StyledGrid = styled(Grid)(() => ({ fontSize: '0.855rem', whiteSpace: 'pre-line', lineHeight: '21px', - overflowWrap: 'anywhere' + overflowWrap: 'anywhere', }, [`& .${classes.pending}`]: { - color: theme.palette.colors.lightGray + color: theme.palette.colors.lightGray, }, [`& .${classes.info}`]: { - color: theme.palette.colors.white - } + color: theme.palette.colors.white, + }, })) export interface NestedMessageContentProps { @@ -57,7 +57,7 @@ export const NestedMessageContent: React.FC { const renderMessage = () => { const isMalicious = downloadStatus?.downloadState === DownloadState.Malicious @@ -70,13 +70,24 @@ export const NestedMessageContent: React.FC + data-testid={`messagesGroupContent-${message.id}`} + > {fileDisplay && message.media ? ( - + ) : ( - + )}
) @@ -85,15 +96,30 @@ export const NestedMessageContent: React.FC - + data-testid={`messagesGroupContent-${message.id}`} + > +
) default: - if (!displayMathRegex.test(message.message)) { // Regular text message - return + if (!displayMathRegex.test(message.message)) { + // Regular text message + return ( + + ) } return ( diff --git a/packages/desktop/src/renderer/components/widgets/channels/TextMessage.tsx b/packages/desktop/src/renderer/components/widgets/channels/TextMessage.tsx index dba2de47f5..5c2fc9675c 100644 --- a/packages/desktop/src/renderer/components/widgets/channels/TextMessage.tsx +++ b/packages/desktop/src/renderer/components/widgets/channels/TextMessage.tsx @@ -9,7 +9,7 @@ const PREFIX = 'TextMessage' const classes = { message: `${PREFIX}message`, pending: `${PREFIX}pending`, - link: `${PREFIX}link` + link: `${PREFIX}link`, } const StyledTypography = styled(Typography)(() => ({ @@ -17,20 +17,20 @@ const StyledTypography = styled(Typography)(() => ({ fontSize: '0.855rem', whiteSpace: 'pre-line', lineHeight: '21px', - overflowWrap: 'anywhere' + overflowWrap: 'anywhere', }, [`&.${classes.pending}`]: { - color: theme.palette.colors.lightGray + color: theme.palette.colors.lightGray, }, [`& .${classes.link}`]: { color: theme.palette.colors.lushSky, cursor: 'pointer', '&:hover': { - textDecoration: 'underline' - } - } + textDecoration: 'underline', + }, + }, })) as typeof Typography export interface TextMessageComponentProps { @@ -40,15 +40,16 @@ export interface TextMessageComponentProps { openUrl: (url: string) => void } -export const TextMessageComponent: React.FC = ({ - message, - messageId, - pending, - openUrl -}) => { +export const TextMessageComponent: React.FC = ({ message, messageId, pending, openUrl }) => { const componentDecorator = (decoratedHref: string, decoratedText: string, key: number): ReactNode => { return ( - { openUrl(decoratedHref) }} className={classNames({ [classes.link]: true })} key={key}> + { + openUrl(decoratedHref) + }} + className={classNames({ [classes.link]: true })} + key={key} + > {decoratedText} ) @@ -59,9 +60,10 @@ export const TextMessageComponent: React.FC = ({ component={'span' as any} className={classNames({ [classes.message]: true, - [classes.pending]: pending + [classes.pending]: pending, })} - data-testid={`messagesGroupContent-${messageId}`}> + data-testid={`messagesGroupContent-${messageId}`} + > {message} ) diff --git a/packages/desktop/src/renderer/components/widgets/channels/WelcomeMessage.tsx b/packages/desktop/src/renderer/components/widgets/channels/WelcomeMessage.tsx index da269a260e..6abe9cf642 100644 --- a/packages/desktop/src/renderer/components/widgets/channels/WelcomeMessage.tsx +++ b/packages/desktop/src/renderer/components/widgets/channels/WelcomeMessage.tsx @@ -22,88 +22,75 @@ const classes = { message: `${PREFIX}message`, messageInput: `${PREFIX}messageInput`, icon: `${PREFIX}icon`, - time: `${PREFIX}time` + time: `${PREFIX}time`, } -const StyledListItem = styled(ListItem)(( - { - theme - } -) => ({ +const StyledListItem = styled(ListItem)(({ theme }) => ({ [`& .${classes.messageCard}`]: { - padding: 0 + padding: 0, }, [`&.${classes.wrapper}`]: { - backgroundColor: theme.palette.colors.white + backgroundColor: theme.palette.colors.white, }, [`& .${classes.username}`]: { fontSize: 16, fontWeight: 500, marginTop: -4, - marginRight: 5 + marginRight: 5, }, [`& .${classes.avatar}`]: { - marginRight: 10 + marginRight: 10, }, [`& .${classes.message}`]: { marginTop: 14, marginLeft: -4, whiteSpace: 'pre-line', - wordBreak: 'break-word' + wordBreak: 'break-word', }, [`& .${classes.messageInput}`]: { marginTop: -35, - marginLeft: 50 + marginLeft: 50, }, [`& .${classes.icon}`]: { width: 36, height: 36, - borderRadius: 4 + borderRadius: 4, }, [`& .${classes.time}`]: { color: theme.palette.colors.lightGray, fontSize: 14, marginTop: -4, - marginRight: 5 - } + marginRight: 5, + }, })) -export const WelcomeMessage: React.FC = ({ - message, - timestamp -}) => { +export const WelcomeMessage: React.FC = ({ message, timestamp }) => { const username = 'Quiet' return ( + - - + + - + {username} @@ -118,7 +105,7 @@ export const WelcomeMessage: React.FC = ({ } secondary={ - + {message} @@ -129,7 +116,7 @@ export const WelcomeMessage: React.FC = ({ } WelcomeMessage.defaultProps = { - timestamp: '0' + timestamp: '0', } export default WelcomeMessage diff --git a/packages/desktop/src/renderer/components/widgets/sentryWarning/SentryWarning.stories.tsx b/packages/desktop/src/renderer/components/widgets/sentryWarning/SentryWarning.stories.tsx index 089cf61c34..06a9c55027 100644 --- a/packages/desktop/src/renderer/components/widgets/sentryWarning/SentryWarning.stories.tsx +++ b/packages/desktop/src/renderer/components/widgets/sentryWarning/SentryWarning.stories.tsx @@ -15,7 +15,7 @@ const args: SentryWarningProps = { open: true, handleClose: function (): void { console.log('Closed modal') - } + }, } Component.args = args @@ -23,7 +23,7 @@ Component.args = args const component: ComponentMeta = { title: 'Components/SentryWarning', decorators: [withTheme], - component: SentryWarningComponent + component: SentryWarningComponent, } export default component diff --git a/packages/desktop/src/renderer/components/widgets/sentryWarning/SentryWarningComponent.tsx b/packages/desktop/src/renderer/components/widgets/sentryWarning/SentryWarningComponent.tsx index 384d1b7937..c27f133f98 100644 --- a/packages/desktop/src/renderer/components/widgets/sentryWarning/SentryWarningComponent.tsx +++ b/packages/desktop/src/renderer/components/widgets/sentryWarning/SentryWarningComponent.tsx @@ -9,23 +9,19 @@ const PREFIX = 'SentryWarningComponent' const classes = { title: `${PREFIX}title`, fullWidth: `${PREFIX}fullWidth`, - button: `${PREFIX}button` + button: `${PREFIX}button`, } -const StyledModalContent = styled(Grid)(( - { - theme - } -) => ({ +const StyledModalContent = styled(Grid)(({ theme }) => ({ backgroundColor: theme.palette.colors.white, padding: '0px 32px', [`& .${classes.title}`]: { - marginTop: 24 + marginTop: 24, }, [`& .${classes.fullWidth}`]: { - paddingBottom: 25 + paddingBottom: 25, }, [`& .${classes.button}`]: { @@ -34,13 +30,13 @@ const StyledModalContent = styled(Grid)(( backgroundColor: theme.palette.colors.purple, padding: theme.spacing(2), '&:hover': { - backgroundColor: theme.palette.colors.darkPurple + backgroundColor: theme.palette.colors.darkPurple, }, '&:disabled': { backgroundColor: theme.palette.colors.lightGray, - color: 'rgba(255,255,255,0.6)' - } - } + color: 'rgba(255,255,255,0.6)', + }, + }, })) export interface SentryWarningProps { diff --git a/packages/desktop/src/renderer/components/widgets/sidebar/MoreButton.tsx b/packages/desktop/src/renderer/components/widgets/sidebar/MoreButton.tsx index bd2a7504ad..50b7ab0b50 100644 --- a/packages/desktop/src/renderer/components/widgets/sidebar/MoreButton.tsx +++ b/packages/desktop/src/renderer/components/widgets/sidebar/MoreButton.tsx @@ -12,14 +12,10 @@ const PREFIX = 'MoreButton' const classes = { button: `${PREFIX}button`, - tooltip: `${PREFIX}tooltip` + tooltip: `${PREFIX}tooltip`, } -const StyledTooltip = styled(Tooltip)(( - { - theme - } -) => ({ +const StyledTooltip = styled(Tooltip)(({ theme }) => ({ [`& .${classes.button}`]: { padding: 0, paddingLeft: 16, @@ -28,15 +24,15 @@ const StyledTooltip = styled(Tooltip)(( justifyContent: 'start', '&:hover': { backgroundColor: 'rgba(0,0,0,0.08)', - opacity: 1 + opacity: 1, }, opacity: 0.7, - color: theme.palette.colors.white + color: theme.palette.colors.white, }, [`&.${classes.tooltip}`]: { - marginTop: 5 - } + marginTop: 5, + }, })) interface MoreButtonProps { @@ -47,12 +43,7 @@ interface MoreButtonProps { export const MoreButton: React.FC = ({ tooltipText, action }) => { return ( - diff --git a/packages/desktop/src/renderer/components/widgets/sidebar/QuickActionButton.tsx b/packages/desktop/src/renderer/components/widgets/sidebar/QuickActionButton.tsx index 99ce72acf2..8446effb76 100644 --- a/packages/desktop/src/renderer/components/widgets/sidebar/QuickActionButton.tsx +++ b/packages/desktop/src/renderer/components/widgets/sidebar/QuickActionButton.tsx @@ -12,14 +12,10 @@ const PREFIX = 'QuickActionButton' const classes = { button: `${PREFIX}button`, icon: `${PREFIX}icon`, - iconDiv: `${PREFIX}iconDiv` + iconDiv: `${PREFIX}iconDiv`, } -const StyledButton = styled(Button)(( - { - theme - } -) => ({ +const StyledButton = styled(Button)(({ theme }) => ({ [`&.${classes.button}`]: { marginTop: 8, padding: 0, @@ -27,23 +23,23 @@ const StyledButton = styled(Button)(( textTransform: 'none', '&:hover': { backgroundColor: 'inherit', - opacity: 1 + opacity: 1, }, opacity: 0.7, - color: theme.palette.colors.white + color: theme.palette.colors.white, }, [`& .${classes.icon}`]: { fontSize: 12, marginRight: 2, marginLeft: -2, - marginBottom: 2 + marginBottom: 2, }, [`& .${classes.iconDiv}`]: { marginRight: 5, - marginBottom: 2 - } + marginBottom: 2, + }, })) interface QuickActionButtonProps { @@ -55,13 +51,7 @@ interface QuickActionButtonProps { export const QuickActionButton: React.FC = ({ text, action, icon }) => { return ( - {icon - ? ( -
{icon}
- ) - : ( - - )} + {icon ?
{icon}
: } {text}
) diff --git a/packages/desktop/src/renderer/components/widgets/update/UpdateModal.test.tsx b/packages/desktop/src/renderer/components/widgets/update/UpdateModal.test.tsx index c89200c297..6994299c5b 100644 --- a/packages/desktop/src/renderer/components/widgets/update/UpdateModal.test.tsx +++ b/packages/desktop/src/renderer/components/widgets/update/UpdateModal.test.tsx @@ -1,4 +1,3 @@ -/* eslint import/first: 0 */ import React from 'react' import { UpdateModal } from './UpdateModal' @@ -6,9 +5,7 @@ import { renderComponent } from '../../../testUtils/renderComponent' describe('UpdateModal', () => { it('renders component', () => { - const result = renderComponent( - - ) + const result = renderComponent() expect(result.baseElement).toMatchInlineSnapshot(` ({ +const StyledModalContent = styled(Grid)(({ theme }) => ({ backgroundColor: theme.palette.colors.white, border: 'none', [`& .${classes.info}`]: { - marginTop: 38 + marginTop: 38, }, [`& .${classes.button}`]: { height: 55, fontSize: '0.9rem', - backgroundColor: theme.palette.colors.quietBlue + backgroundColor: theme.palette.colors.quietBlue, }, [`& .${classes.updateIcon}`]: { width: 102, - height: 102 + height: 102, }, [`& .${classes.title}`]: { marginTop: 24, - marginBottom: 16 + marginBottom: 16, }, [`& .${classes.subTitle}`]: { - marginBottom: 32 - } + marginBottom: 32, + }, })) interface UpdateModalProps { diff --git a/packages/desktop/src/renderer/components/windows/Index.test.tsx b/packages/desktop/src/renderer/components/windows/Index.test.tsx index 108c79a9d8..660a935ace 100644 --- a/packages/desktop/src/renderer/components/windows/Index.test.tsx +++ b/packages/desktop/src/renderer/components/windows/Index.test.tsx @@ -1,4 +1,3 @@ -/* eslint import/first: 0 */ import React from 'react' import { renderComponent } from '../../testUtils/renderComponent' diff --git a/packages/desktop/src/renderer/components/windows/Index.tsx b/packages/desktop/src/renderer/components/windows/Index.tsx index 24d5de3862..398e824b54 100644 --- a/packages/desktop/src/renderer/components/windows/Index.tsx +++ b/packages/desktop/src/renderer/components/windows/Index.tsx @@ -8,7 +8,7 @@ import Loading from './Loading' const PREFIX = 'Index' const classes = { - root: `${PREFIX}root` + root: `${PREFIX}root`, } const StyledWindowWrapper = styled(WindowWrapper)(() => ({ @@ -16,8 +16,8 @@ const StyledWindowWrapper = styled(WindowWrapper)(() => ({ display: 'flex', justifyContent: 'center', alignItems: 'center', - WebkitAppRegion: process.platform === 'win32' ? 'no-drag' : 'drag' - } + WebkitAppRegion: process.platform === 'win32' ? 'no-drag' : 'drag', + }, })) interface IndexProps { diff --git a/packages/desktop/src/renderer/components/windows/Loading.tsx b/packages/desktop/src/renderer/components/windows/Loading.tsx index c66275cc5f..2c21f51b32 100644 --- a/packages/desktop/src/renderer/components/windows/Loading.tsx +++ b/packages/desktop/src/renderer/components/windows/Loading.tsx @@ -20,50 +20,46 @@ const classes = { progressBar: `${PREFIX}progressBar`, carouselContainer: `${PREFIX}carouselContainer`, messageContainer: `${PREFIX}messageContainer`, - message: `${PREFIX}message` + message: `${PREFIX}message`, } -const StyledGrid = styled(Grid)(( - { - theme - } -) => ({ +const StyledGrid = styled(Grid)(({ theme }) => ({ [`&.${classes.root}`]: { width: '100vw', height: '100vh', - WebkitAppRegion: process.platform === 'win32' ? 'no-drag' : 'drag' + WebkitAppRegion: process.platform === 'win32' ? 'no-drag' : 'drag', }, [`& .${classes.icon}`]: { width: 285, - height: 67 + height: 67, }, [`& .${classes.svg}`]: { width: 100, - height: 100 + height: 100, }, [`& .${classes.progressBarContainer}`]: { - width: 254 + width: 254, }, [`& .${classes.progressBar}`]: { - backgroundColor: theme.palette.colors.lushSky + backgroundColor: theme.palette.colors.lushSky, }, [`& .${classes.carouselContainer}`]: { - marginTop: theme.spacing(5) + marginTop: theme.spacing(5), }, [`& .${classes.messageContainer}`]: { - marginTop: 16 + marginTop: 16, }, [`& .${classes.message}`]: { color: theme.palette.colors.darkGray, - fontSize: 16 - } + fontSize: 16, + }, })) interface LoadingProps { diff --git a/packages/desktop/src/renderer/components/windows/Main.stories.tsx b/packages/desktop/src/renderer/components/windows/Main.stories.tsx index 3bb652655b..ae5ea59137 100644 --- a/packages/desktop/src/renderer/components/windows/Main.stories.tsx +++ b/packages/desktop/src/renderer/components/windows/Main.stories.tsx @@ -20,9 +20,10 @@ const Template: ComponentStory = () => { minHeight: '100vh', minWidth: '100vw', overflow: 'hidden', - position: 'relative' + position: 'relative', }} - wrap='nowrap'> + wrap='nowrap' + > {/* @ts-ignore */} @@ -41,7 +42,7 @@ Component.args = {} const component: ComponentMeta = { title: 'Components/Main', decorators: [withTheme], - component: SidebarComponent + component: SidebarComponent, } export default component diff --git a/packages/desktop/src/renderer/components/windows/Main.test.tsx b/packages/desktop/src/renderer/components/windows/Main.test.tsx index cbdfd59440..ba9c983eed 100644 --- a/packages/desktop/src/renderer/components/windows/Main.test.tsx +++ b/packages/desktop/src/renderer/components/windows/Main.test.tsx @@ -1,4 +1,3 @@ -/* eslint import/first: 0 */ import React from 'react' import { renderComponent } from '../../testUtils/renderComponent' import { HashRouter } from 'react-router-dom' @@ -9,14 +8,11 @@ import { prepareStore } from '../../testUtils' describe('Main', () => { it('renders component', async () => { - const store = ( - await prepareStore() - ).store + const store = (await prepareStore()).store const factory = await getFactory(store) - await factory.create['payload']>( - 'Community', - { rootCa: 'rootCa' } - ) + await factory.create['payload']>('Community', { + rootCa: 'rootCa', + }) const result = renderComponent( diff --git a/packages/desktop/src/renderer/components/windows/Main.tsx b/packages/desktop/src/renderer/components/windows/Main.tsx index 468038d8a8..a55f2aec9d 100644 --- a/packages/desktop/src/renderer/components/windows/Main.tsx +++ b/packages/desktop/src/renderer/components/windows/Main.tsx @@ -12,7 +12,7 @@ const MainGridStyled = styled(Grid)(() => ({ minHeight: '100vh', minWidth: '100vw', overflow: 'hidden', - position: 'relative' + position: 'relative', })) export const Main: React.FC = () => { @@ -31,14 +31,14 @@ export const Main: React.FC = () => { const [_dimensions, setDimensions] = React.useState({ height: window.innerHeight, - width: window.innerWidth + width: window.innerWidth, }) useEffect(() => { const debouncedHandleResize = debounce(function handleResize() { setDimensions({ height: window.innerHeight, - width: window.innerWidth + width: window.innerWidth, }) }, 1000) @@ -50,20 +50,20 @@ export const Main: React.FC = () => { }) return ( - (
+
- - } /> - + + } /> + -
) +
) } diff --git a/packages/desktop/src/renderer/containers/hooks.ts b/packages/desktop/src/renderer/containers/hooks.ts index 8c7dc15f52..0921c1a31f 100644 --- a/packages/desktop/src/renderer/containers/hooks.ts +++ b/packages/desktop/src/renderer/containers/hooks.ts @@ -17,7 +17,7 @@ export const useModal = (name: ModalName) => dispatch( modalsActions.openModal({ name: name, - args: args + args: args, }) ) @@ -27,13 +27,13 @@ export const useModal = (name: ModalName) => open, handleOpen, handleClose, - ...props + ...props, } } export enum Variant { ARROWS_KEYS = 'arrows-keys', - PAGES_KEYS = 'pages-keys' + PAGES_KEYS = 'pages-keys', } export const useCyclingFocus = ( diff --git a/packages/desktop/src/renderer/containers/widgets/WarningModal/WarningModal.tsx b/packages/desktop/src/renderer/containers/widgets/WarningModal/WarningModal.tsx index 1fe00c5bb3..534c1262f2 100644 --- a/packages/desktop/src/renderer/containers/widgets/WarningModal/WarningModal.tsx +++ b/packages/desktop/src/renderer/containers/widgets/WarningModal/WarningModal.tsx @@ -5,7 +5,7 @@ import { useModal } from '../../hooks' const Warning = () => { const modal = useModal(ModalName.warningModal) - return + return } export default Warning diff --git a/packages/desktop/src/renderer/containers/widgets/sentryWarning/sentryWarning.basic.test.tsx b/packages/desktop/src/renderer/containers/widgets/sentryWarning/sentryWarning.basic.test.tsx index de8f11e64d..1c2a40e514 100644 --- a/packages/desktop/src/renderer/containers/widgets/sentryWarning/sentryWarning.basic.test.tsx +++ b/packages/desktop/src/renderer/containers/widgets/sentryWarning/sentryWarning.basic.test.tsx @@ -25,8 +25,8 @@ describe('Sentry warning modal', () => { const { store } = await prepareStore({ [StoreKeys.Socket]: { ...new SocketState(), - isConnected: true - } + isConnected: true, + }, }) renderComponent( @@ -43,8 +43,8 @@ describe('Sentry warning modal', () => { const { store } = await prepareStore({ [StoreKeys.Socket]: { ...new SocketState(), - isConnected: true - } + isConnected: true, + }, }) renderComponent( diff --git a/packages/desktop/src/renderer/containers/widgets/update/UpdateModal.tsx b/packages/desktop/src/renderer/containers/widgets/update/UpdateModal.tsx index cf08d91dda..0a66f3ca0f 100644 --- a/packages/desktop/src/renderer/containers/widgets/update/UpdateModal.tsx +++ b/packages/desktop/src/renderer/containers/widgets/update/UpdateModal.tsx @@ -11,7 +11,7 @@ export const mapDispatchToProps = (dispatch: Dispatch) => bindActionCreators( { handleUpdate: updateHandlers.epics.startApplicationUpdate, - rejectUpdate: updateHandlers.epics.declineUpdate + rejectUpdate: updateHandlers.epics.declineUpdate, }, dispatch ) diff --git a/packages/desktop/src/renderer/containers/windows/Index.tsx b/packages/desktop/src/renderer/containers/windows/Index.tsx index 9f2d2bb87a..2ce6446ac9 100644 --- a/packages/desktop/src/renderer/containers/windows/Index.tsx +++ b/packages/desktop/src/renderer/containers/windows/Index.tsx @@ -20,7 +20,7 @@ export const Index = () => { useEffect(() => { loadVersion() }, []) - return + return } export default Index diff --git a/packages/desktop/src/renderer/expanded-theme.ts b/packages/desktop/src/renderer/expanded-theme.ts index 8c187ad13e..503278c261 100644 --- a/packages/desktop/src/renderer/expanded-theme.ts +++ b/packages/desktop/src/renderer/expanded-theme.ts @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-unused-vars-experimental */ +/* eslint-disable @typescript-eslint/no-unused-vars */ import { Typography, TypographyOptions } from '@mui/material/styles/createTypography' import { Palette, PaletteOptions } from '@mui/material/styles/createPalette' diff --git a/packages/desktop/src/renderer/forms/components/textInput.tsx b/packages/desktop/src/renderer/forms/components/textInput.tsx index 86a3326804..ef6d8759fc 100644 --- a/packages/desktop/src/renderer/forms/components/textInput.tsx +++ b/packages/desktop/src/renderer/forms/components/textInput.tsx @@ -10,7 +10,15 @@ export type TextInputProps = TextFieldProps & { onblur: Noop } -export const TextInput: React.FC = ({ errors, defaultValue, classes, onchange, onblur, name = '', ...props }) => { +export const TextInput: React.FC = ({ + errors, + defaultValue, + classes, + onchange, + onblur, + name = '', + ...props +}) => { const hasError = Boolean(errors?.[name]) return ( @@ -28,7 +36,7 @@ export const TextInput: React.FC = ({ errors, defaultValue, clas {errors?.[name]?.message || ''} - + ) } diff --git a/packages/desktop/src/renderer/forms/fields/communityFields.ts b/packages/desktop/src/renderer/forms/fields/communityFields.ts index 08859e5ccb..bda6aa21a4 100644 --- a/packages/desktop/src/renderer/forms/fields/communityFields.ts +++ b/packages/desktop/src/renderer/forms/fields/communityFields.ts @@ -7,22 +7,22 @@ export const communityNameField = (name = 'name'): FieldData => { label: '', name, type: 'text', - placeholder: 'Community name' + placeholder: 'Community name', }, validation: { required: FieldErrors.Required, maxLength: { value: 20, - message: CommunityNameErrors.NameTooLong + message: CommunityNameErrors.NameTooLong, }, pattern: { value: /^[-a-zA-Z0-9 ]+$/g, - message: CommunityNameErrors.WrongCharacter + message: CommunityNameErrors.WrongCharacter, }, validate: { - whitespaces: (value) => /^(?![\s-])[\w\s-]+$/.test(value) || FieldErrors.Whitespaces - } - } + whitespaces: value => /^(?![\s-])[\w\s-]+$/.test(value) || FieldErrors.Whitespaces, + }, + }, } } @@ -32,14 +32,14 @@ export const inviteLinkField = (name = 'name'): FieldData => { label: '', name, type: 'password', - placeholder: 'Invite code' + placeholder: 'Invite code', }, validation: { required: FieldErrors.Required, pattern: { value: /^[a-z0-9:/.#?= ]+$/g, - message: InviteLinkErrors.InvalidCode - } - } + message: InviteLinkErrors.InvalidCode, + }, + }, } } diff --git a/packages/desktop/src/renderer/forms/fields/createChannelFields.ts b/packages/desktop/src/renderer/forms/fields/createChannelFields.ts index 240ede7386..1a56c83b22 100644 --- a/packages/desktop/src/renderer/forms/fields/createChannelFields.ts +++ b/packages/desktop/src/renderer/forms/fields/createChannelFields.ts @@ -7,13 +7,13 @@ export const channelNameField = (name = 'channelName'): FieldData => { label: '', name, type: 'text', - placeholder: 'Type name' + placeholder: 'Type name', }, validation: { required: FieldErrors.Required, maxLength: { value: 20, - message: ChannelNameErrors.NameTooLong + message: ChannelNameErrors.NameTooLong, }, /* eslint-disable */ validate: { diff --git a/packages/desktop/src/renderer/forms/fields/createUserFields.ts b/packages/desktop/src/renderer/forms/fields/createUserFields.ts index e91e504106..a3cb334cfa 100644 --- a/packages/desktop/src/renderer/forms/fields/createUserFields.ts +++ b/packages/desktop/src/renderer/forms/fields/createUserFields.ts @@ -7,21 +7,21 @@ export const userNameField = (name = 'userName'): FieldData => { label: '', name, type: 'text', - placeholder: 'Type name' + placeholder: 'Type name', }, validation: { required: FieldErrors.Required, maxLength: { value: 20, - message: UsernameErrors.NameTooLong + message: UsernameErrors.NameTooLong, }, pattern: { value: /^[-a-zA-Z0-9 ]+$/g, - message: UsernameErrors.WrongCharacter + message: UsernameErrors.WrongCharacter, }, validate: { - whitespaces: (value) => /^(?![\s])[\w\s-]+$/.test(value) || FieldErrors.Whitespaces - } - } + whitespaces: value => /^(?![\s])[\w\s-]+$/.test(value) || FieldErrors.Whitespaces, + }, + }, } } diff --git a/packages/desktop/src/renderer/forms/fields/searchChannelField.ts b/packages/desktop/src/renderer/forms/fields/searchChannelField.ts index bc9b986991..54639e3ade 100644 --- a/packages/desktop/src/renderer/forms/fields/searchChannelField.ts +++ b/packages/desktop/src/renderer/forms/fields/searchChannelField.ts @@ -7,10 +7,10 @@ export const searchChannelField = (name = 'searchChannel'): FieldData => { label: '', name, type: 'text', - placeholder: 'Type name' + placeholder: 'Type name', }, validation: { - required: FieldErrors.Required - } + required: FieldErrors.Required, + }, } } diff --git a/packages/desktop/src/renderer/forms/fieldsErrors.ts b/packages/desktop/src/renderer/forms/fieldsErrors.ts index 35863b3e7e..4dca02d69e 100644 --- a/packages/desktop/src/renderer/forms/fieldsErrors.ts +++ b/packages/desktop/src/renderer/forms/fieldsErrors.ts @@ -1,23 +1,23 @@ export enum FieldErrors { Required = 'Required field', - Whitespaces = 'Name cannot begin with whitespaces or dashes' + Whitespaces = 'Name cannot begin with whitespaces or dashes', } export enum UsernameErrors { NameTooLong = 'Username must have less than 20 characters', - WrongCharacter = 'Username must be lowercase and cannot contain any special characters' + WrongCharacter = 'Username must be lowercase and cannot contain any special characters', } export enum CommunityNameErrors { NameTooLong = 'Community name must have less than 20 characters', - WrongCharacter = 'Community name must be lowercase and cannot contain any special characters' + WrongCharacter = 'Community name must be lowercase and cannot contain any special characters', } export enum InviteLinkErrors { - InvalidCode = 'Please check your invitation code and try again' + InvalidCode = 'Please check your invitation code and try again', } export enum ChannelNameErrors { NameTooLong = 'Channel name must have less than 20 characters', - WrongCharacter = 'Channel name cannot contain any special characters' + WrongCharacter = 'Channel name cannot contain any special characters', } diff --git a/packages/desktop/src/renderer/sagas/index.saga.ts b/packages/desktop/src/renderer/sagas/index.saga.ts index 33d8e28fb4..35d0dbc985 100644 --- a/packages/desktop/src/renderer/sagas/index.saga.ts +++ b/packages/desktop/src/renderer/sagas/index.saga.ts @@ -10,8 +10,8 @@ export default function* root(): Generator { takeEvery(communities.actions.handleInvitationCode.type, handleInvitationCodeSaga), startConnectionSaga( socketActions.startConnection({ - dataPort: parseInt(dataPort) + dataPort: parseInt(dataPort), }) - ) + ), ]) } diff --git a/packages/desktop/src/renderer/sagas/invitation/handleInvitationCode.saga.test.ts b/packages/desktop/src/renderer/sagas/invitation/handleInvitationCode.saga.test.ts index 5eb0ca8e20..470a0ae044 100644 --- a/packages/desktop/src/renderer/sagas/invitation/handleInvitationCode.saga.test.ts +++ b/packages/desktop/src/renderer/sagas/invitation/handleInvitationCode.saga.test.ts @@ -16,12 +16,14 @@ describe('Handle invitation code', () => { let validInvitationCode: string beforeEach(async () => { - store = (await prepareStore({ - [StoreKeys.Socket]: { - ...new SocketState(), - isConnected: true - } - })).store + store = ( + await prepareStore({ + [StoreKeys.Socket]: { + ...new SocketState(), + isConnected: true, + }, + }) + ).store factory = await getFactory(store) validInvitationCode = 'bb5wacaftixjl3yhq2cp3ls2ife2e5wlwct3hjlb4lyk4iniypmgozyd' @@ -30,61 +32,56 @@ describe('Handle invitation code', () => { it('creates network if code is valid', async () => { const payload: CreateNetworkPayload = { ownership: CommunityOwnership.User, - registrar: validInvitationCode + registrar: validInvitationCode, } - await expectSaga( - handleInvitationCodeSaga, - communities.actions.handleInvitationCode(validInvitationCode) - ) - .withState(store.getState()) - .put(communities.actions.createNetwork(payload)) - .run() + await expectSaga(handleInvitationCodeSaga, communities.actions.handleInvitationCode(validInvitationCode)) + .withState(store.getState()) + .put(communities.actions.createNetwork(payload)) + .run() }) it('does not try to create network if user is already in community', async () => { community = await factory.create['payload']>('Community') const payload: CreateNetworkPayload = { ownership: CommunityOwnership.User, - registrar: validInvitationCode + registrar: validInvitationCode, } - await expectSaga( - handleInvitationCodeSaga, - communities.actions.handleInvitationCode(validInvitationCode) - ) - .withState(store.getState()) - .put(modalsActions.openModal({ - name: ModalName.warningModal, - args: { - title: 'You already belong to a community', - subtitle: "We're sorry but for now you can only be a member of a single community at a time." - } - })) - .not.put(communities.actions.createNetwork(payload)) - .run() + await expectSaga(handleInvitationCodeSaga, communities.actions.handleInvitationCode(validInvitationCode)) + .withState(store.getState()) + .put( + modalsActions.openModal({ + name: ModalName.warningModal, + args: { + title: 'You already belong to a community', + subtitle: "We're sorry but for now you can only be a member of a single community at a time.", + }, + }) + ) + .not.put(communities.actions.createNetwork(payload)) + .run() }) it('does not try to create network if code is invalid', async () => { const code = 'invalid' const payload: CreateNetworkPayload = { ownership: CommunityOwnership.User, - registrar: code + registrar: code, } - await expectSaga( - handleInvitationCodeSaga, - communities.actions.handleInvitationCode(code) - ) - .withState(store.getState()) - .put(communities.actions.clearInvitationCode()) - .put(modalsActions.openModal({ - name: ModalName.warningModal, - args: { - title: 'Invalid link', - subtitle: 'The invite link you received is not valid. Please check it and try again.' - } - })) - .not.put(communities.actions.createNetwork(payload)) - .run() + await expectSaga(handleInvitationCodeSaga, communities.actions.handleInvitationCode(code)) + .withState(store.getState()) + .put(communities.actions.clearInvitationCode()) + .put( + modalsActions.openModal({ + name: ModalName.warningModal, + args: { + title: 'Invalid link', + subtitle: 'The invite link you received is not valid. Please check it and try again.', + }, + }) + ) + .not.put(communities.actions.createNetwork(payload)) + .run() }) }) diff --git a/packages/desktop/src/renderer/sagas/invitation/handleInvitationCode.saga.ts b/packages/desktop/src/renderer/sagas/invitation/handleInvitationCode.saga.ts index a09151447a..5fab99fb8d 100644 --- a/packages/desktop/src/renderer/sagas/invitation/handleInvitationCode.saga.ts +++ b/packages/desktop/src/renderer/sagas/invitation/handleInvitationCode.saga.ts @@ -8,46 +8,50 @@ import { ModalName } from '../modals/modals.types' import { modalsActions } from '../modals/modals.slice' export function* handleInvitationCodeSaga( - action: PayloadAction['payload']> - ): Generator { - while (true) { - const connected = yield* select(socketSelectors.isConnected) - if (connected) { - break - } - yield* delay(500) + action: PayloadAction['payload']> +): Generator { + while (true) { + const connected = yield* select(socketSelectors.isConnected) + if (connected) { + break } + yield* delay(500) + } - const currentCommunityId = yield* select(communities.selectors.currentCommunityId) - if (currentCommunityId) { - yield* put(modalsActions.openModal({ - name: ModalName.warningModal, - args: { - title: 'You already belong to a community', - subtitle: "We're sorry but for now you can only be a member of a single community at a time." - } - })) - return - } + const currentCommunityId = yield* select(communities.selectors.currentCommunityId) + if (currentCommunityId) { + yield* put( + modalsActions.openModal({ + name: ModalName.warningModal, + args: { + title: 'You already belong to a community', + subtitle: "We're sorry but for now you can only be a member of a single community at a time.", + }, + }) + ) + return + } - const code = action.payload.trim() + const code = action.payload.trim() - if (code.match(ONION_ADDRESS_REGEX)) { - const payload: CreateNetworkPayload = { - ownership: CommunityOwnership.User, - registrar: code - } - yield* put(communities.actions.createNetwork(payload)) - return + if (code.match(ONION_ADDRESS_REGEX)) { + const payload: CreateNetworkPayload = { + ownership: CommunityOwnership.User, + registrar: code, } + yield* put(communities.actions.createNetwork(payload)) + return + } - yield* put(communities.actions.clearInvitationCode()) + yield* put(communities.actions.clearInvitationCode()) - yield* put(modalsActions.openModal({ + yield* put( + modalsActions.openModal({ name: ModalName.warningModal, args: { title: 'Invalid link', - subtitle: 'The invite link you received is not valid. Please check it and try again.' - } - })) + subtitle: 'The invite link you received is not valid. Please check it and try again.', + }, + }) + ) } diff --git a/packages/desktop/src/renderer/sagas/modals/modals.selectors.test.ts b/packages/desktop/src/renderer/sagas/modals/modals.selectors.test.ts index 1af338a35c..74446fc9c6 100644 --- a/packages/desktop/src/renderer/sagas/modals/modals.selectors.test.ts +++ b/packages/desktop/src/renderer/sagas/modals/modals.selectors.test.ts @@ -8,12 +8,12 @@ describe('modalsSelectors', () => { it('returns false for closed modal', () => { store = createStore( combineReducers({ - Modals: modalsReducer + Modals: modalsReducer, }), { Modals: { - ...new ModalsInitialState() - } + ...new ModalsInitialState(), + }, } ) const channelInfo = modalsSelectors.open(ModalName.channelInfo)(store.getState()) @@ -23,13 +23,13 @@ describe('modalsSelectors', () => { it('returns true for open modal', () => { store = createStore( combineReducers({ - Modals: modalsReducer + Modals: modalsReducer, }), { Modals: { ...new ModalsInitialState(), - [ModalName.channelInfo]: { open: true, args: {} } - } + [ModalName.channelInfo]: { open: true, args: {} }, + }, } ) const channelInfo = modalsSelectors.open(ModalName.channelInfo)(store.getState()) diff --git a/packages/desktop/src/renderer/sagas/modals/modals.selectors.ts b/packages/desktop/src/renderer/sagas/modals/modals.selectors.ts index 59576e8e0b..9a78ef8dac 100644 --- a/packages/desktop/src/renderer/sagas/modals/modals.selectors.ts +++ b/packages/desktop/src/renderer/sagas/modals/modals.selectors.ts @@ -4,8 +4,7 @@ import { CreatedSelectors, StoreState } from '../store.types' import { StoreKeys } from '../../store/store.keys' import { ModalState, modalsReducer } from './modals.slice' -const ModalsSlice: CreatedSelectors[StoreKeys.Modals] = (state: StoreState) => - state[StoreKeys.Modals] +const ModalsSlice: CreatedSelectors[StoreKeys.Modals] = (state: StoreState) => state[StoreKeys.Modals] export const open = (modal: ModalName) => createSelector(ModalsSlice, (reducerState: ReturnType) => { @@ -21,5 +20,5 @@ export const props = (modal: ModalName) => export const modalsSelectors = { open, - props + props, } diff --git a/packages/desktop/src/renderer/sagas/modals/modals.slice.ts b/packages/desktop/src/renderer/sagas/modals/modals.slice.ts index de187306bd..b29e606394 100644 --- a/packages/desktop/src/renderer/sagas/modals/modals.slice.ts +++ b/packages/desktop/src/renderer/sagas/modals/modals.slice.ts @@ -3,12 +3,12 @@ import { ModalName } from './modals.types' export interface OpenModalPayload { name: ModalName - args?: {} + args?: Record } export interface ModalState { open: boolean - args?: {} + args?: Record } export class ModalsInitialState { @@ -49,8 +49,8 @@ export const modalsSlice = createSlice({ closeModal: (state, action: PayloadAction) => { state[action.payload].open = false state[action.payload].args = {} - } - } + }, + }, }) export const modalsActions = modalsSlice.actions diff --git a/packages/desktop/src/renderer/sagas/modals/modals.types.ts b/packages/desktop/src/renderer/sagas/modals/modals.types.ts index f1ee798c5a..ab48c13f80 100644 --- a/packages/desktop/src/renderer/sagas/modals/modals.types.ts +++ b/packages/desktop/src/renderer/sagas/modals/modals.types.ts @@ -20,5 +20,5 @@ export enum ModalName { leaveCommunity = 'leaveCommunityModal', searchChannelModal = 'searchChannelModal', warningModal = 'warningModal', - channelCreationModal = 'channelCreationModal' + channelCreationModal = 'channelCreationModal', } 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 037b27fdc8..231fff0642 100644 --- a/packages/desktop/src/renderer/sagas/notifications/notifications.click.test.ts +++ b/packages/desktop/src/renderer/sagas/notifications/notifications.click.test.ts @@ -4,17 +4,8 @@ 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 { - createNotification, - handleNotificationActions, - NotificationData -} from './notifications.saga' +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(() => { @@ -28,16 +19,16 @@ jest.mock('../../../shared/sounds', () => ({ ...jest.requireActual('../../../shared/sounds'), soundTypeToAudio: { pow: { - play: jest.fn() - } - } + play: jest.fn(), + }, + }, })) jest.mock('electron', () => { return { shell: { - showItemInFolder: jest.fn() - } + showItemInFolder: jest.fn(), + }, } }) @@ -59,7 +50,7 @@ describe('clicking in notification', () => { label: 'label', body: 'body', channel: sailingId, - sound: NotificationsSounds.splat + sound: NotificationsSounds.splat, } store.dispatch(publicChannels.actions.setCurrentChannel({ channelId: generalId })) @@ -94,15 +85,15 @@ describe('clicking in notification', () => { path: 'path/file.ext', message: { id: 'id', - channelId: sailingId - } + channelId: sailingId, + }, } const notificationData: NotificationData = { label: 'label', body: 'body', channel: sailingId, - sound: NotificationsSounds.splat + sound: NotificationsSounds.splat, } const spy = jest.spyOn(shell, 'showItemInFolder') diff --git a/packages/desktop/src/renderer/sagas/notifications/notifications.saga.ts b/packages/desktop/src/renderer/sagas/notifications/notifications.saga.ts index c2b465916b..8ed916673d 100644 --- a/packages/desktop/src/renderer/sagas/notifications/notifications.saga.ts +++ b/packages/desktop/src/renderer/sagas/notifications/notifications.saga.ts @@ -13,11 +13,7 @@ import { NotificationsSounds, files, } from '@quiet/state-manager' -import { - MessageType, - FileMetadata, - DownloadState -} from '@quiet/types' +import { MessageType, FileMetadata, DownloadState } from '@quiet/types' import { soundTypeToAudio } from '../../../shared/sounds' import { eventChannel } from 'redux-saga' import { takeEvery } from 'redux-saga/effects' @@ -52,7 +48,7 @@ export function* displayMessageNotificationSaga( for (const message of incomingMessages) { const focused = yield* call(isWindowFocused) - const channelName = publicChannelsSelector.find((channel) => channel.id === message.channelId)?.name + 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.channelId === currentChannelId) return @@ -100,7 +96,7 @@ export function* displayMessageNotificationSaga( label: label, body: body, channel: channel, - sound: notificationsSound + sound: notificationsSound, } const notification = yield* call(createNotification, notificationData) @@ -128,7 +124,7 @@ export const createNotification = (notificationData: NotificationData): Notifica return new Notification(label, { body: body, icon: '../../build' + '/icon.png', - silent: true + silent: true, }) } diff --git a/packages/desktop/src/renderer/sagas/notifications/notifications.test.ts b/packages/desktop/src/renderer/sagas/notifications/notifications.test.ts index 962e737067..d8a9ef7ae3 100644 --- a/packages/desktop/src/renderer/sagas/notifications/notifications.test.ts +++ b/packages/desktop/src/renderer/sagas/notifications/notifications.test.ts @@ -15,19 +15,8 @@ import { publicChannels, settings, } from '@quiet/state-manager' -import { - Community, - Identity, - ChannelMessage, - IncomingMessages, - PublicChannel, - MessageType -} from '@quiet/types' -import { - createNotification, - displayMessageNotificationSaga, - isWindowFocused -} from './notifications.saga' +import { Community, Identity, ChannelMessage, IncomingMessages, PublicChannel, MessageType } from '@quiet/types' +import { createNotification, displayMessageNotificationSaga, isWindowFocused } from './notifications.saga' import { soundTypeToAudio } from '../../../shared/sounds' const originalNotification = window.Notification @@ -49,11 +38,11 @@ jest.mock('@electron/remote', () => { getAllWindows: () => { return [ { - show: mockShow - } + show: mockShow, + }, ] - } - } + }, + }, } }) @@ -61,18 +50,18 @@ jest.mock('../../../shared/sounds', () => ({ ...jest.requireActual('../../../shared/sounds'), soundTypeToAudio: { librarianShhh: { - play: jest.fn() + play: jest.fn(), }, pow: { - play: jest.fn() + play: jest.fn(), }, bang: { - play: jest.fn() + play: jest.fn(), }, splat: { - play: jest.fn() - } - } + play: jest.fn(), + }, + }, })) let store: Store @@ -96,32 +85,30 @@ beforeAll(async () => { const factory = await getFactory(store) - community = await factory.create< - ReturnType['payload'] - >('Community') + community = await factory.create['payload']>('Community') const generalChannel = publicChannels.selectors.generalChannel(store.getState()) expect(generalChannel).not.toBeUndefined() - store.dispatch(publicChannels.actions.setCurrentChannel({ - // @ts-expect-error - channelId: generalChannel.id - })) + store.dispatch( + publicChannels.actions.setCurrentChannel({ + // @ts-expect-error + channelId: generalChannel.id, + }) + ) sailingChannel = ( - await factory.create['payload']>( - 'PublicChannel' - ) + await factory.create['payload']>('PublicChannel') ).channel - alice = await factory.create['payload']>( - 'Identity', - { id: community.id, nickname: 'alice' } - ) + alice = await factory.create['payload']>('Identity', { + id: community.id, + nickname: 'alice', + }) store.dispatch(connection.actions.setLastConnectedTime(lastConnectedTime)) bob = ( await factory.build('Identity', { id: community.id, - nickname: 'bob' + nickname: 'bob', }) ).payload @@ -135,8 +122,8 @@ beforeAll(async () => { createdAt: lastConnectedTime + 1, channelId: sailingChannel.id, signature: '', - pubKey: '' - } + pubKey: '', + }, }) ).payload.message @@ -150,8 +137,8 @@ beforeAll(async () => { createdAt: lastConnectedTime + 1, channelId: sailingChannel.id, signature: '', - pubKey: '' - } + pubKey: '', + }, }) ).payload.message }) @@ -166,9 +153,7 @@ afterEach(() => { jest.resetAllMocks() // Reenable notification in settings - store.dispatch( - settings.actions.setNotificationsOption(NotificationsOptions.notifyForEveryMessage) - ) + store.dispatch(settings.actions.setNotificationsOption(NotificationsOptions.notifyForEveryMessage)) // Reenable notification sound in settings store.dispatch(settings.actions.setNotificationsSound(NotificationsSounds.librarianShhh)) @@ -181,7 +166,7 @@ describe('displayNotificationsSaga', () => { displayMessageNotificationSaga, messages.actions.incomingMessages({ messages: [message], - isVerified: true + isVerified: true, }) ) .withReducer(reducer) @@ -191,14 +176,14 @@ describe('displayNotificationsSaga', () => { label: `New message from @${bob.nickname} in #${sailingChannel.name}`, body: message.message, channel: sailingChannel.id, - sound: NotificationsSounds.pow + sound: NotificationsSounds.pow, }) .run() expect(notification).toBeCalledWith(`New message from @${bob.nickname} in #${sailingChannel.name}`, { body: message.message, icon: '../../build/icon.png', - silent: true + silent: true, }) }) @@ -208,7 +193,7 @@ describe('displayNotificationsSaga', () => { displayMessageNotificationSaga, messages.actions.incomingMessages({ messages: [message], - isVerified: false + isVerified: false, }) ) .withReducer(reducer) @@ -217,7 +202,7 @@ describe('displayNotificationsSaga', () => { .not.call(createNotification) .run() - expect(notification).not.toHaveBeenCalled() + expect(notification).not.toHaveBeenCalled() }) test('clicking in notification foregrounds the app', async () => { @@ -226,7 +211,7 @@ describe('displayNotificationsSaga', () => { displayMessageNotificationSaga, messages.actions.incomingMessages({ messages: [message], - isVerified: true + isVerified: true, }) ) .withReducer(reducer) @@ -247,7 +232,7 @@ describe('displayNotificationsSaga', () => { displayMessageNotificationSaga, messages.actions.incomingMessages({ messages: [message], - isVerified: true + isVerified: true, }) ) .withReducer(reducer) @@ -259,16 +244,14 @@ describe('displayNotificationsSaga', () => { }) test('do not display notification when the user is on the active channel', async () => { - store.dispatch( - publicChannels.actions.setCurrentChannel({ channelId: sailingChannel.id }) - ) + store.dispatch(publicChannels.actions.setCurrentChannel({ channelId: sailingChannel.id })) const reducer = combineReducers(reducers) await expectSaga( displayMessageNotificationSaga, messages.actions.incomingMessages({ messages: [message], - isVerified: true + isVerified: true, }) ) .withReducer(reducer) @@ -281,16 +264,14 @@ describe('displayNotificationsSaga', () => { }) test('notification shows for message in current channel when app window does not have focus', async () => { - store.dispatch( - publicChannels.actions.setCurrentChannel({ channelId: sailingChannel.id }) - ) + store.dispatch(publicChannels.actions.setCurrentChannel({ channelId: sailingChannel.id })) const reducer = combineReducers(reducers) await expectSaga( displayMessageNotificationSaga, messages.actions.incomingMessages({ messages: [message], - isVerified: true + isVerified: true, }) ) .withReducer(reducer) @@ -300,28 +281,26 @@ describe('displayNotificationsSaga', () => { label: `New message from @${bob.nickname} in #${sailingChannel.name}`, body: message.message, channel: sailingChannel.id, - sound: NotificationsSounds.librarianShhh + sound: NotificationsSounds.librarianShhh, }) .run() expect(notification).toBeCalledWith(`New message from @${bob.nickname} in #${sailingChannel.name}`, { body: message.message, icon: '../../build/icon.png', - silent: true + silent: true, }) }) test('notification shows for message in non-active channel when app window has focus', async () => { - store.dispatch( - publicChannels.actions.setCurrentChannel({ channelId: 'general' }) - ) + store.dispatch(publicChannels.actions.setCurrentChannel({ channelId: 'general' })) const reducer = combineReducers(reducers) await expectSaga( displayMessageNotificationSaga, messages.actions.incomingMessages({ messages: [message], - isVerified: true + isVerified: true, }) ) .withReducer(reducer) @@ -331,14 +310,14 @@ describe('displayNotificationsSaga', () => { label: `New message from @${bob.nickname} in #${sailingChannel.name}`, body: message.message, channel: sailingChannel.id, - sound: NotificationsSounds.librarianShhh + sound: NotificationsSounds.librarianShhh, }) .run() expect(notification).toBeCalledWith(`New message from @${bob.nickname} in #${sailingChannel.name}`, { body: message.message, icon: '../../build/icon.png', - silent: true + silent: true, }) }) @@ -348,10 +327,10 @@ describe('displayNotificationsSaga', () => { messages: [ { ...message, - createdAt: lastConnectedTime - 1 - } + createdAt: lastConnectedTime - 1, + }, ], - isVerified: true + isVerified: true, } const reducer = combineReducers(reducers) @@ -371,10 +350,10 @@ describe('displayNotificationsSaga', () => { messages: [ { ...message, - pubKey: 'fake' - } + pubKey: 'fake', + }, ], - isVerified: true + isVerified: true, } const reducer = combineReducers(reducers) @@ -391,7 +370,7 @@ describe('displayNotificationsSaga', () => { test('do not display notification for own messages', async () => { const payload: IncomingMessages = { messages: [aliceMessage], - isVerified: true + isVerified: true, } const reducer = combineReducers(reducers) @@ -413,7 +392,7 @@ describe('displayNotificationsSaga', () => { displayMessageNotificationSaga, messages.actions.incomingMessages({ messages: [message], - isVerified: true + isVerified: true, }) ) .withReducer(reducer) @@ -423,7 +402,7 @@ describe('displayNotificationsSaga', () => { label: `New message from @${bob.nickname} in #${sailingChannel.name}`, body: message.message, channel: sailingChannel.id, - sound: NotificationsSounds.none + sound: NotificationsSounds.none, }) .run() @@ -434,16 +413,14 @@ describe('displayNotificationsSaga', () => { }) test('do not display notifications if turned off in settings', async () => { - store.dispatch( - settings.actions.setNotificationsOption(NotificationsOptions.doNotNotifyOfAnyMessages) - ) + store.dispatch(settings.actions.setNotificationsOption(NotificationsOptions.doNotNotifyOfAnyMessages)) const reducer = combineReducers(reducers) await expectSaga( displayMessageNotificationSaga, messages.actions.incomingMessages({ messages: [message], - isVerified: true + isVerified: true, }) ) .withReducer(reducer) @@ -468,12 +445,12 @@ describe('displayNotificationsSaga', () => { ext: '.png', message: { id: message.id, - channelId: message.channelId - } - } - } + channelId: message.channelId, + }, + }, + }, ], - isVerified: true + isVerified: true, } const reducer = combineReducers(reducers) @@ -485,14 +462,14 @@ describe('displayNotificationsSaga', () => { label: `@${bob.nickname} sent an image in #${sailingChannel.name}`, body: undefined, channel: sailingChannel.id, - sound: NotificationsSounds.librarianShhh + sound: NotificationsSounds.librarianShhh, }) .run() expect(notification).toBeCalledWith(`@${bob.nickname} sent an image in #${sailingChannel.name}`, { body: undefined, icon: '../../build/icon.png', - silent: true + silent: true, }) }) @@ -509,12 +486,12 @@ describe('displayNotificationsSaga', () => { ext: '.ext', message: { id: message.id, - channelId: message.channelId - } - } - } + channelId: message.channelId, + }, + }, + }, ], - isVerified: true + isVerified: true, } const reducer = combineReducers(reducers) @@ -526,14 +503,14 @@ describe('displayNotificationsSaga', () => { label: `@${bob.nickname} sends file in #${sailingChannel.name}`, body: undefined, channel: sailingChannel.id, - sound: NotificationsSounds.librarianShhh + sound: NotificationsSounds.librarianShhh, }) .run() expect(notification).toBeCalledWith(`@${bob.nickname} sends file in #${sailingChannel.name}`, { body: undefined, icon: '../../build/icon.png', - silent: true + silent: true, }) }) }) diff --git a/packages/desktop/src/renderer/sagas/socket/socket.saga.ts b/packages/desktop/src/renderer/sagas/socket/socket.saga.ts index d69b693ecc..33beaa1a9c 100644 --- a/packages/desktop/src/renderer/sagas/socket/socket.saga.ts +++ b/packages/desktop/src/renderer/sagas/socket/socket.saga.ts @@ -29,7 +29,7 @@ function* setConnectedSaga(socket: Socket): Generator { // Handle suspending current connection yield all([ takeEvery(socketActions.suspendConnection, cancelRootSaga, root), - takeEvery(socketActions.suspendConnection, cancelObservers, observers) + takeEvery(socketActions.suspendConnection, cancelObservers, observers), ]) } @@ -42,8 +42,7 @@ function* handleSocketLifecycleActions(socket: Socket): Generator { function subscribeSocketLifecycle(socket?: Socket) { return eventChannel< - ReturnType | - ReturnType + ReturnType | ReturnType >(emit => { socket?.on('connect', async () => { console.log('websocket connected') @@ -68,7 +67,5 @@ function* cancelObservers(task: FixedTask): Generator { } function* initObservers(): Generator { - yield all([ - takeEvery(messages.actions.incomingMessages.type, displayMessageNotificationSaga) - ]) + yield all([takeEvery(messages.actions.incomingMessages.type, displayMessageNotificationSaga)]) } diff --git a/packages/desktop/src/renderer/sagas/socket/socket.selectors.ts b/packages/desktop/src/renderer/sagas/socket/socket.selectors.ts index be4d321f63..197fed13c9 100644 --- a/packages/desktop/src/renderer/sagas/socket/socket.selectors.ts +++ b/packages/desktop/src/renderer/sagas/socket/socket.selectors.ts @@ -11,5 +11,5 @@ export const isConnected = createSelector( ) export const socketSelectors = { - isConnected + isConnected, } diff --git a/packages/desktop/src/renderer/sagas/socket/socket.slice.ts b/packages/desktop/src/renderer/sagas/socket/socket.slice.ts index f76d9e69b1..aff4529096 100644 --- a/packages/desktop/src/renderer/sagas/socket/socket.slice.ts +++ b/packages/desktop/src/renderer/sagas/socket/socket.slice.ts @@ -4,7 +4,7 @@ import { Socket } from 'socket.io-client' import { FixedTask } from 'typed-redux-saga' export class SocketState { - public isConnected: boolean = false + public isConnected = false } export interface WebsocketConnectionPayload { @@ -29,8 +29,8 @@ export const socketSlice = createSlice({ }, setConnected: state => { state.isConnected = true - } - } + }, + }, }) export const socketActions = socketSlice.actions diff --git a/packages/desktop/src/renderer/sagas/store.types.ts b/packages/desktop/src/renderer/sagas/store.types.ts index 37372412d1..7f21501ad0 100644 --- a/packages/desktop/src/renderer/sagas/store.types.ts +++ b/packages/desktop/src/renderer/sagas/store.types.ts @@ -8,7 +8,7 @@ export type StoreState = ReturnType export type StoreDispatch = typeof store.dispatch export type CreatedSelectors = { - [Key in keyof SelectedState]: (state: SelectedState) => SelectedState[Key]; + [Key in keyof SelectedState]: (state: SelectedState) => SelectedState[Key] } export type StoreModuleStateClass = new () => object diff --git a/packages/desktop/src/renderer/sagas/store.utils.ts b/packages/desktop/src/renderer/sagas/store.utils.ts index 0c340719d0..d2e6aa0727 100644 --- a/packages/desktop/src/renderer/sagas/store.utils.ts +++ b/packages/desktop/src/renderer/sagas/store.utils.ts @@ -3,21 +3,14 @@ import { createSelector } from '@reduxjs/toolkit' import { StoreKeys } from '../store/store.keys' import { AnyObject } from '../../utils/types/AnyObject.interface' -import { - CreatedSelectors, - StoreModuleStateClass, - StoreState -} from './store.types' +import { CreatedSelectors, StoreModuleStateClass, StoreState } from './store.types' export const selectorsFactory = ( storeKey: StoreKey, ReducerState: StoreModuleStateClass ): CreatedSelectors => { - const reducerSelector = (store: AnyObject): StoreState[StoreKey] => - store[storeKey] - const reducerKeys = Object.keys( - new ReducerState() - ) as Array + const reducerSelector = (store: AnyObject): StoreState[StoreKey] => store[storeKey] + const reducerKeys = Object.keys(new ReducerState()) as Array // @ts-expect-error const createdSelectors: CreatedSelectors = {} @@ -26,10 +19,7 @@ export const selectorsFactory = ( accumulator: CreatedSelectors, key: keyof StoreState[StoreKey] ): CreatedSelectors => { - accumulator[key] = createSelector( - reducerSelector, - (state: StoreState[StoreKey]) => state[key] - ) + accumulator[key] = createSelector(reducerSelector, (state: StoreState[StoreKey]) => state[key]) return accumulator }, @@ -37,9 +27,7 @@ export const selectorsFactory = ( ) } -export const selectReducer = ( - storeKey: StoreKey -) => { +export const selectReducer = (storeKey: StoreKey) => { return (store: StoreState): StoreState[StoreKey] => { return store[storeKey] } diff --git a/packages/desktop/src/renderer/store/create.ts b/packages/desktop/src/renderer/store/create.ts index 48a7320f1b..a5c7d55279 100644 --- a/packages/desktop/src/renderer/store/create.ts +++ b/packages/desktop/src/renderer/store/create.ts @@ -15,7 +15,7 @@ const testMode = process.env.TEST_MODE if (testMode) { Sentry.init({ - dsn: 'https://1ca88607c3d14e15b36cb2cfd5f16d68@o1060867.ingest.sentry.io/6050774' + dsn: 'https://1ca88607c3d14e15b36cb2cfd5f16d68@o1060867.ingest.sentry.io/6050774', }) } @@ -25,16 +25,14 @@ const sagaMiddleware = createSagaMiddleware({ if (testMode) { Sentry.captureException(err) } - } + }, }) export default (initialState = {}): Store => { const store = createStore( reducers, initialState, - composeWithDevTools( - applyMiddleware(...[errorsMiddleware, createDebounce(), sagaMiddleware, thunk, promise()]) - ) + composeWithDevTools(applyMiddleware(...[errorsMiddleware, createDebounce(), sagaMiddleware, thunk, promise()])) ) sagaMiddleware.run(rootSaga) return store diff --git a/packages/desktop/src/renderer/store/handlers/app.test.ts b/packages/desktop/src/renderer/store/handlers/app.test.ts index 721db64901..bed7294e7a 100644 --- a/packages/desktop/src/renderer/store/handlers/app.test.ts +++ b/packages/desktop/src/renderer/store/handlers/app.test.ts @@ -1,4 +1,3 @@ -/* eslint import/first: 0 */ jest.mock('electron', () => { // @ts-expect-error const ipcRenderer = jest.mock() @@ -35,8 +34,8 @@ describe('criticalError reducer', () => { beforeEach(() => { store = create({ initialState: { - app: {} - } + app: {}, + }, }) jest.clearAllMocks() }) diff --git a/packages/desktop/src/renderer/store/handlers/app.ts b/packages/desktop/src/renderer/store/handlers/app.ts index 5da7a4b148..3615ade723 100644 --- a/packages/desktop/src/renderer/store/handlers/app.ts +++ b/packages/desktop/src/renderer/store/handlers/app.ts @@ -16,14 +16,14 @@ export class App { export const initialState: App = { ...new App({ - version: null - }) + version: null, + }), } const loadVersion = createAction(actionTypes.SET_APP_VERSION, () => 1.0) export const actions = { - loadVersion + loadVersion, } export type AppActions = ActionsType @@ -33,12 +33,12 @@ export const reducer = handleActions>( [loadVersion.toString()]: (state, { payload: version }: AppActions['loadVersion']) => produce(state, draft => { draft.version = version - }) + }), }, initialState ) export default { actions, - reducer + reducer, } diff --git a/packages/desktop/src/renderer/store/handlers/types.ts b/packages/desktop/src/renderer/store/handlers/types.ts index 30931d4262..025a36deb3 100644 --- a/packages/desktop/src/renderer/store/handlers/types.ts +++ b/packages/desktop/src/renderer/store/handlers/types.ts @@ -6,7 +6,7 @@ export interface ActionsBasicType { } export type ActionsType = { - [k in keyof actions]: ReturnType; + [k in keyof actions]: ReturnType } export interface ActionsCreatorsBasicType { @@ -25,6 +25,4 @@ export type StoreType = { [k in keyof reducers]: ReturnType } -export type PayloadType< - actions extends ActionsType -> = actions[keyof actions]['payload'] +export type PayloadType> = actions[keyof actions]['payload'] diff --git a/packages/desktop/src/renderer/store/handlers/update.ts b/packages/desktop/src/renderer/store/handlers/update.ts index 4f3c79969f..5f6ec83c1f 100644 --- a/packages/desktop/src/renderer/store/handlers/update.ts +++ b/packages/desktop/src/renderer/store/handlers/update.ts @@ -19,9 +19,9 @@ export const declineUpdate = () => async (dispatch: Dispatch) => { export const epics = { checkForUpdate, startApplicationUpdate, - declineUpdate + declineUpdate, } export default { - epics + epics, } diff --git a/packages/desktop/src/renderer/store/middlewares.ts b/packages/desktop/src/renderer/store/middlewares.ts index ea62b94df1..d5500b73c2 100644 --- a/packages/desktop/src/renderer/store/middlewares.ts +++ b/packages/desktop/src/renderer/store/middlewares.ts @@ -2,18 +2,19 @@ import { ModalName } from '../sagas/modals/modals.types' import { modalsActions } from '../sagas/modals/modals.slice' import { Store } from '../sagas/store.types' -const isPromise = (value: any) => - value !== null && typeof value === 'object' && typeof value.then === 'function' +const isPromise = (value: any) => value !== null && typeof value === 'object' && typeof value.then === 'function' const _dispatchError = (store: Store, err: Error) => { const criticalError = { message: err.message, - traceback: err.stack + traceback: err.stack, } - store.dispatch(modalsActions.openModal({ - name: ModalName.criticalError, - args: criticalError - })) + store.dispatch( + modalsActions.openModal({ + name: ModalName.criticalError, + args: criticalError, + }) + ) } export const errorsMiddleware = (store: any) => (next: (action: any) => Promise) => (action: any) => { diff --git a/packages/desktop/src/renderer/store/navigation/navigation.selectors.ts b/packages/desktop/src/renderer/store/navigation/navigation.selectors.ts index 3e13387ef5..6a8b2ef7fa 100644 --- a/packages/desktop/src/renderer/store/navigation/navigation.selectors.ts +++ b/packages/desktop/src/renderer/store/navigation/navigation.selectors.ts @@ -19,5 +19,5 @@ export const contextMenuProps = (menu: MenuName) => export const navigationSelectors = { contextMenuVisibility, - contextMenuProps + contextMenuProps, } diff --git a/packages/desktop/src/renderer/store/navigation/navigation.slice.ts b/packages/desktop/src/renderer/store/navigation/navigation.slice.ts index ba79cb0197..1ba0a8036f 100644 --- a/packages/desktop/src/renderer/store/navigation/navigation.slice.ts +++ b/packages/desktop/src/renderer/store/navigation/navigation.slice.ts @@ -8,7 +8,7 @@ export class NavigationState { export interface OpenMenuPayload { menu: MenuName - args?: {} + args?: Record } export interface ToggleConfirmationBoxPayload { @@ -34,18 +34,18 @@ export const navigationSlice = createSlice({ const menu = action.payload state[menu] = { open: false, - args: {} + args: {}, } }, closeAllMenus: state => { Object.values(MenuName).forEach(menu => { state[menu] = { open: false, - args: {} + args: {}, } }) - } - } + }, + }, }) export const navigationActions = navigationSlice.actions diff --git a/packages/desktop/src/renderer/store/reducers.ts b/packages/desktop/src/renderer/store/reducers.ts index 0be258ee21..db36a00d09 100644 --- a/packages/desktop/src/renderer/store/reducers.ts +++ b/packages/desktop/src/renderer/store/reducers.ts @@ -12,7 +12,7 @@ import stateManagerReducers, { FilesTransform, communities, ConnectionTransform, - resetStateAndSaveTorConnectionData + resetStateAndSaveTorConnectionData, } from '@quiet/state-manager' import { StoreType } from './handlers/types' @@ -29,15 +29,12 @@ import { Store } from '../sagas/store.types' const dataPath = process.env.APPDATA || - (process.platform === 'darwin' - ? process.env.HOME + '/Library/Application Support' - : process.env.HOME + '/.config') -const appPath = - process.env.DATA_DIR || (process.env.NODE_ENV === 'development' ? DEV_DATA_DIR : 'Quiet') + (process.platform === 'darwin' ? process.env.HOME + '/Library/Application Support' : process.env.HOME + '/.config') +const appPath = process.env.DATA_DIR || (process.env.NODE_ENV === 'development' ? DEV_DATA_DIR : 'Quiet') const options = { projectName: 'quiet', - cwd: path.join(dataPath, appPath) + cwd: path.join(dataPath, appPath), } const store = new ElectronStore(options) @@ -57,15 +54,9 @@ const persistConfig = { StateManagerStoreKeys.Settings, StateManagerStoreKeys.Users, StateManagerStoreKeys.Connection, - StoreKeys.App + StoreKeys.App, ], - transforms: [ - CommunitiesTransform, - PublicChannelsTransform, - MessagesTransform, - FilesTransform, - ConnectionTransform - ] + transforms: [CommunitiesTransform, PublicChannelsTransform, MessagesTransform, FilesTransform, ConnectionTransform], } export const reducers = { @@ -73,12 +64,13 @@ export const reducers = { [StoreKeys.App]: appHandlers.reducer, [StoreKeys.Socket]: socketReducer, [StoreKeys.Modals]: modalsReducer, - [StoreKeys.Navigation]: navigationReducer + [StoreKeys.Navigation]: navigationReducer, } const allReducers = combineReducers(reducers) -export const rootReducer = (state: any, action: AnyAction) => { // TODO: what is state? +export const rootReducer = (state: any, action: AnyAction) => { + // TODO: what is state? if (action.type === communities.actions.resetApp.type) { state = resetStateAndSaveTorConnectionData() } diff --git a/packages/desktop/src/renderer/store/selectors/app.test.ts b/packages/desktop/src/renderer/store/selectors/app.test.ts index 70e58014a4..86c369c6af 100644 --- a/packages/desktop/src/renderer/store/selectors/app.test.ts +++ b/packages/desktop/src/renderer/store/selectors/app.test.ts @@ -1,4 +1,3 @@ -/* eslint import/first: 0 */ import selectors from './app' import { initialState as AppState } from '../handlers/app' @@ -16,8 +15,8 @@ describe('app -', () => { transfers: {}, modalTabToOpen: 'addFunds', allTransfersCount: 12, - newTransfersCounter: 2 - } + newTransfersCounter: 2, + }, }) }) diff --git a/packages/desktop/src/renderer/store/selectors/app.ts b/packages/desktop/src/renderer/store/selectors/app.ts index e276835ecb..5b2a7c4888 100644 --- a/packages/desktop/src/renderer/store/selectors/app.ts +++ b/packages/desktop/src/renderer/store/selectors/app.ts @@ -3,8 +3,8 @@ import { StoreState } from '../../sagas/store.types' const app = (s: StoreState) => s.app -const version = createSelector(app, (a) => a.version) +const version = createSelector(app, a => a.version) export default { - version + version, } diff --git a/packages/desktop/src/renderer/store/store.keys.ts b/packages/desktop/src/renderer/store/store.keys.ts index d1d82ff3d4..c6c0ab164a 100644 --- a/packages/desktop/src/renderer/store/store.keys.ts +++ b/packages/desktop/src/renderer/store/store.keys.ts @@ -2,5 +2,5 @@ export enum StoreKeys { App = 'app', Socket = 'socket', Modals = 'Modals', - Navigation = 'Navigation' + Navigation = 'Navigation', } diff --git a/packages/desktop/src/renderer/storybook/decorators.tsx b/packages/desktop/src/renderer/storybook/decorators.tsx index c6cfdd7bc4..35167fa545 100644 --- a/packages/desktop/src/renderer/storybook/decorators.tsx +++ b/packages/desktop/src/renderer/storybook/decorators.tsx @@ -5,11 +5,12 @@ import { ThemeProvider, StyledEngineProvider } from '@mui/material/styles' import theme from '../theme' import { Store } from '../sagas/store.types' -export const withStore = (store: Store) => (Story: React.FC) => ( - - - -) +export const withStore = (store: Store) => (Story: React.FC) => + ( + + + + ) export const withTheme = (Story: React.FC) => ( diff --git a/packages/desktop/src/renderer/storybook/utils.ts b/packages/desktop/src/renderer/storybook/utils.ts index f71905e96f..61791cb38b 100644 --- a/packages/desktop/src/renderer/storybook/utils.ts +++ b/packages/desktop/src/renderer/storybook/utils.ts @@ -8,7 +8,7 @@ export const mock_messages = (message: DisplayableMessage | null = null) => { message: '*heavy breathing*', createdAt: 0, date: '12:46', - nickname: 'vader' + nickname: 'vader', } if (message !== null) { @@ -29,8 +29,8 @@ export const mock_messages = (message: DisplayableMessage | null = null) => { message: 'Messages more there should be', createdAt: 0, date: '12:46', - nickname: 'yoda' - } + nickname: 'yoda', + }, ], [ { @@ -39,7 +39,7 @@ export const mock_messages = (message: DisplayableMessage | null = null) => { message: 'I Agree', createdAt: 0, date: '12:46', - nickname: 'obi' + nickname: 'obi', }, { id: '3', @@ -47,8 +47,8 @@ export const mock_messages = (message: DisplayableMessage | null = null) => { message: 'Of course, I Agree', createdAt: 0, date: '12:46', - nickname: 'obi' - } + nickname: 'obi', + }, ], [ { @@ -57,8 +57,8 @@ export const mock_messages = (message: DisplayableMessage | null = null) => { message: 'Wrough!', createdAt: 0, date: '12:46', - nickname: 'wookie' - } + nickname: 'wookie', + }, ], [ { @@ -67,8 +67,8 @@ export const mock_messages = (message: DisplayableMessage | null = null) => { message: 'Yeah!', createdAt: 0, date: '12:46', - nickname: 'leah' - } + nickname: 'leah', + }, ], [ { @@ -77,8 +77,8 @@ export const mock_messages = (message: DisplayableMessage | null = null) => { message: 'The more messages the better', createdAt: 0, date: '12:46', - nickname: 'luke' - } + nickname: 'luke', + }, ], [ { @@ -87,8 +87,8 @@ export const mock_messages = (message: DisplayableMessage | null = null) => { message: 'We cannot grant you the rank of messager', createdAt: 0, date: '12:46', - nickname: 'windoo' - } + nickname: 'windoo', + }, ], [ { @@ -98,9 +98,9 @@ export const mock_messages = (message: DisplayableMessage | null = null) => { 'deathhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhstarrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrdeathstartttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr', createdAt: 0, date: '12:46', - nickname: 'vader' - } - ] + nickname: 'vader', + }, + ], ], '27 Oct': [ [ @@ -110,7 +110,7 @@ export const mock_messages = (message: DisplayableMessage | null = null) => { message: 'Luck, I am your father!', createdAt: 0, date: '12:40', - nickname: 'chad' + nickname: 'chad', }, { id: '10', @@ -118,7 +118,7 @@ export const mock_messages = (message: DisplayableMessage | null = null) => { message: "That's impossible!", createdAt: 0, date: '12:41', - nickname: 'chad' + nickname: 'chad', }, { id: '11', @@ -126,8 +126,8 @@ export const mock_messages = (message: DisplayableMessage | null = null) => { message: 'Nooo!', createdAt: 0, date: '12:45', - nickname: 'chad' - } + nickname: 'chad', + }, ], [ { @@ -136,8 +136,8 @@ export const mock_messages = (message: DisplayableMessage | null = null) => { message: 'Uhuhu!', createdAt: 0, date: '12:46', - nickname: 'anakin' - } + nickname: 'anakin', + }, ], [ { @@ -146,8 +146,8 @@ export const mock_messages = (message: DisplayableMessage | null = null) => { message: 'Why?', createdAt: 0, date: '12:46', - nickname: 'anakin' - } + nickname: 'anakin', + }, ], [ { @@ -156,8 +156,8 @@ export const mock_messages = (message: DisplayableMessage | null = null) => { message: 'Messages more there should be', createdAt: 0, date: '12:46', - nickname: 'yoda' - } + nickname: 'yoda', + }, ], [ { @@ -166,7 +166,7 @@ export const mock_messages = (message: DisplayableMessage | null = null) => { message: 'I Agree', createdAt: 0, date: '12:46', - nickname: 'obi' + nickname: 'obi', }, { id: '16', @@ -174,8 +174,8 @@ export const mock_messages = (message: DisplayableMessage | null = null) => { message: 'Of course, I Agree', createdAt: 0, date: '12:46', - nickname: 'obi' - } + nickname: 'obi', + }, ], [ { @@ -184,8 +184,8 @@ export const mock_messages = (message: DisplayableMessage | null = null) => { message: 'Wrough!', createdAt: 0, date: '12:46', - nickname: 'wookie' - } + nickname: 'wookie', + }, ], [ { @@ -194,8 +194,8 @@ export const mock_messages = (message: DisplayableMessage | null = null) => { message: 'Yeah!', createdAt: 0, date: '12:46', - nickname: 'leah' - } + nickname: 'leah', + }, ], [ { @@ -204,8 +204,8 @@ export const mock_messages = (message: DisplayableMessage | null = null) => { message: 'The more messages the better', createdAt: 0, date: '12:46', - nickname: 'luke' - } + nickname: 'luke', + }, ], [ { @@ -214,8 +214,8 @@ export const mock_messages = (message: DisplayableMessage | null = null) => { message: 'We cannot grant you the rank of messager', createdAt: 0, date: '12:46', - nickname: 'windoo' - } + nickname: 'windoo', + }, ], [ { @@ -225,9 +225,9 @@ export const mock_messages = (message: DisplayableMessage | null = null) => { 'deathhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhstarrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrdeathstartttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr', createdAt: 0, date: '12:46', - nickname: 'vader' - } - ] + nickname: 'vader', + }, + ], ], '28 Oct': [ [ @@ -237,7 +237,7 @@ export const mock_messages = (message: DisplayableMessage | null = null) => { message: 'Hello', createdAt: 0, date: '28 Oct, 10:00', - nickname: 'alice' + nickname: 'alice', }, { id: '23', @@ -246,8 +246,8 @@ export const mock_messages = (message: DisplayableMessage | null = null) => { "How are you? My day was awesome. I removed a lot of unused props from container and I simplified code a lot. I like coding, coding is like building things with LEGO. I could admit it's a little bit harder and there's a lot that can go wrong but I like it anyway.", createdAt: 0, date: '28 Oct, 10:01', - nickname: 'alice' - } + nickname: 'alice', + }, ], [ { @@ -256,9 +256,9 @@ export const mock_messages = (message: DisplayableMessage | null = null) => { message: 'Great, thanks!', createdAt: 0, date: '28 Oct, 10:02', - nickname: 'john' - } - ] + nickname: 'john', + }, + ], ], Today: [ [ @@ -268,7 +268,7 @@ export const mock_messages = (message: DisplayableMessage | null = null) => { message: 'Luck, I am your father!', createdAt: 0, date: '12:40', - nickname: 'chad' + nickname: 'chad', }, { id: '26', @@ -276,7 +276,7 @@ export const mock_messages = (message: DisplayableMessage | null = null) => { message: "That's impossible!", createdAt: 0, date: '12:41', - nickname: 'chad' + nickname: 'chad', }, { id: '27', @@ -284,8 +284,8 @@ export const mock_messages = (message: DisplayableMessage | null = null) => { message: 'Nooo!', createdAt: 0, date: '12:45', - nickname: 'chad' - } + nickname: 'chad', + }, ], [ { @@ -294,8 +294,8 @@ export const mock_messages = (message: DisplayableMessage | null = null) => { message: 'Uhuhu!', createdAt: 0, date: '12:46', - nickname: 'anakin' - } + nickname: 'anakin', + }, ], [ { @@ -304,8 +304,8 @@ export const mock_messages = (message: DisplayableMessage | null = null) => { message: 'Why?', createdAt: 0, date: '12:46', - nickname: 'anakin' - } + nickname: 'anakin', + }, ], [ { @@ -314,8 +314,8 @@ export const mock_messages = (message: DisplayableMessage | null = null) => { message: 'Messages more there should be', createdAt: 0, date: '12:46', - nickname: 'yoda' - } + nickname: 'yoda', + }, ], [ { @@ -324,8 +324,8 @@ export const mock_messages = (message: DisplayableMessage | null = null) => { message: 'I Agree', createdAt: 0, date: '12:46', - nickname: 'obi' - } + nickname: 'obi', + }, ], [placeholder], [ @@ -335,11 +335,11 @@ export const mock_messages = (message: DisplayableMessage | null = null) => { message: 'Use the force, look!', createdAt: 0, date: '12:46', - nickname: 'vader' - } - ] - ] - } + nickname: 'vader', + }, + ], + ], + }, } return messages diff --git a/packages/desktop/src/renderer/testUtils/generateMessages.tsx b/packages/desktop/src/renderer/testUtils/generateMessages.tsx index b3cdb3ba9c..b38191cd5a 100644 --- a/packages/desktop/src/renderer/testUtils/generateMessages.tsx +++ b/packages/desktop/src/renderer/testUtils/generateMessages.tsx @@ -1,6 +1,11 @@ import { DisplayableMessage } from '@quiet/state-manager' -interface IGenerateMessages {amount?: number; type?: number; message?: string; nickname?: string} +interface IGenerateMessages { + amount?: number + type?: number + message?: string + nickname?: string +} const defaults = { amount: 1, type: 1, message: 'message', nickname: 'gringo' } @@ -18,7 +23,7 @@ export const generateMessages = (options: IGenerateMessages = defaults) => { message: `${message}${i}`, createdAt: 0, date: 'string', - nickname + nickname, }) } diff --git a/packages/desktop/src/renderer/testUtils/prepareStore.ts b/packages/desktop/src/renderer/testUtils/prepareStore.ts index 2a3f6a8103..6a8f15d65c 100644 --- a/packages/desktop/src/renderer/testUtils/prepareStore.ts +++ b/packages/desktop/src/renderer/testUtils/prepareStore.ts @@ -10,7 +10,7 @@ import { files, StoreKeys as StateManagerStoreKeys, Store, - network + network, } from '@quiet/state-manager' import { StoreKeys } from '../store/store.keys' import { combineReducers, createStore, applyMiddleware } from 'redux' @@ -38,7 +38,7 @@ export const reducers = { [StoreKeys.App]: appReducer, [StoreKeys.Socket]: socketReducer, [StoreKeys.Modals]: modalsReducer, - [StoreKeys.Navigation]: navigationReducer + [StoreKeys.Navigation]: navigationReducer, } interface Options { @@ -58,8 +58,7 @@ export interface PrepareStore { class SagaMonitor { effectsTriggeredArray effectsResolvedArray - constructor( - ) { + constructor() { this.effectsTriggeredArray = new Map() this.effectsResolvedArray = new Map() } @@ -75,13 +74,13 @@ class SagaMonitor { } public isEffectResolved = (effectName: string) => { - const parentEffect = Array.from(this.effectsResolvedArray).filter((effect) => { + const parentEffect = Array.from(this.effectsResolvedArray).filter(effect => { return effect[1].result?.meta?.name === effectName }) - const childrenEffects = Array.from(this.effectsResolvedArray).filter((effect) => { + const childrenEffects = Array.from(this.effectsResolvedArray).filter(effect => { return effect[1].parentEffectId === parentEffect[0][0] }) - return childrenEffects.filter((effect) => { + return childrenEffects.filter(effect => { return effect[1].result === '@@redux-saga/TERMINATE' }).length } @@ -95,14 +94,10 @@ export const prepareStore = async ( const sagaMonitor = new SagaMonitor() const sagaMiddleware = createSagaMiddleware({ - sagaMonitor + sagaMonitor, }) - const store = createStore( - combinedReducers, - mockedState, - applyMiddleware(...[sagaMiddleware, thunk]) - ) + const store = createStore(combinedReducers, mockedState, applyMiddleware(...[sagaMiddleware, thunk])) // Fork State manager's sagas (require mocked socket.io-client) if (mockedSocket) { sagaMiddleware.run(rootSaga) @@ -113,7 +108,7 @@ export const prepareStore = async ( return { store, runSaga: sagaMiddleware.run, - sagaMonitor + sagaMonitor, } } diff --git a/packages/desktop/src/renderer/testUtils/renderComponent.tsx b/packages/desktop/src/renderer/testUtils/renderComponent.tsx index 3a6fa7ee51..b341fac9ff 100644 --- a/packages/desktop/src/renderer/testUtils/renderComponent.tsx +++ b/packages/desktop/src/renderer/testUtils/renderComponent.tsx @@ -19,9 +19,7 @@ export const renderComponent = (ui: ReactElement, storeState: Store = store): Re - - {children} - + {children} diff --git a/packages/desktop/src/renderer/theme.ts b/packages/desktop/src/renderer/theme.ts index 62083a512d..46bcafd117 100644 --- a/packages/desktop/src/renderer/theme.ts +++ b/packages/desktop/src/renderer/theme.ts @@ -12,60 +12,60 @@ export default createTheme({ overline: { fontSize: 10, lineHeight: '16px', - fontWeight: 500 + fontWeight: 500, }, caption: { fontSize: 12, lineHeight: '20px', - color: '#b2b2b2' + color: '#b2b2b2', }, body1: { fontSize: 16, - lineHeight: '26px' + lineHeight: '26px', }, body2: { fontSize: 14, - lineHeight: '24px' + lineHeight: '24px', }, subtitle1: { fontSize: 16, - lineHeight: '26px' + lineHeight: '26px', }, subtitle2: { fontSize: 14, - lineHeight: '23px' + lineHeight: '23px', }, h1: { fontWeight: 500, fontSize: 48, - lineHeight: '40px' + lineHeight: '40px', }, h2: { fontWeight: 500, fontSize: 34, - lineHeight: '40px' + lineHeight: '40px', }, h3: { fontWeight: 500, fontSize: 28, - lineHeight: '34px' + lineHeight: '34px', }, h4: { fontWeight: 500, fontSize: 18, - lineHeight: '27px' + lineHeight: '27px', }, h5: { fontSize: 16, lineHeight: '26px', - fontWeight: 500 - } + fontWeight: 500, + }, }, palette: { primary: { light: '#e9e9e9', main: '#8d8d8d', - dark: '#4a4a4a' + dark: '#4a4a4a', }, colors: { contentGray: '#D2D2D2', @@ -106,8 +106,8 @@ export default createTheme({ logsTabWhite: '#A9A9A9', logsScrollBar: '#3D3D3D', logsScrollBarThumb: ' #787878', - yellow: '#E6BB46' - } + yellow: '#E6BB46', + }, }, components: { // Body font size changed in mui v5: https://mui.com/material-ui/migration/v5-component-changes/#update-body-font-size @@ -116,16 +116,16 @@ export default createTheme({ body: { fontSize: '14px', lineHeight: '24px', - letterSpacing: '0.01071em' + letterSpacing: '0.01071em', }, }, }, MuiSnackbarContent: { styleOverrides: { root: { - wordBreak: 'break-all' - } - } + wordBreak: 'break-all', + }, + }, }, MuiButton: { styleOverrides: { @@ -137,8 +137,8 @@ export default createTheme({ fontWeight: 400, fontSize: '14px', '&:active': { - boxShadow: 'none' - } + boxShadow: 'none', + }, }, sizeLarge: { textTransform: 'none', @@ -148,23 +148,22 @@ export default createTheme({ paddingBottom: 12, fontSize: 14, '&:active': { - boxShadow: 'none' - } - } - } - + boxShadow: 'none', + }, + }, + }, }, MuiOutlinedInput: { styleOverrides: { - input: {} - } + input: {}, + }, }, MuiPopover: { styleOverrides: { paper: { - borderRadius: 8 - } - } - } - } + borderRadius: 8, + }, + }, + }, + }, }) diff --git a/packages/desktop/src/rtl-tests/app.restart.test.tsx b/packages/desktop/src/rtl-tests/app.restart.test.tsx index 9cd2236bc9..9e1ea0518f 100644 --- a/packages/desktop/src/rtl-tests/app.restart.test.tsx +++ b/packages/desktop/src/rtl-tests/app.restart.test.tsx @@ -9,16 +9,11 @@ import CreateCommunity from '../renderer/components/CreateJoinCommunity/CreateCo import Channel from '../renderer/components/Channel/Channel' import { CreateCommunityDictionary, - JoinCommunityDictionary + JoinCommunityDictionary, } from '../renderer/components/CreateJoinCommunity/community.dictionary' import MockedSocket from 'socket.io-mock' import { ioMock } from '../shared/setupTests' -import { - communities, - getFactory, - network, - publicChannels -} from '@quiet/state-manager' +import { communities, getFactory, network, publicChannels } from '@quiet/state-manager' import { act } from 'react-dom/test-utils' import { identityActions } from 'packages/state-manager/src/sagas/identity/identity.slice' import { LoadingPanelType } from '@quiet/types' @@ -34,7 +29,7 @@ describe('Restart app works correctly', () => { window.ResizeObserver = jest.fn().mockImplementation(() => ({ observe: jest.fn(), unobserve: jest.fn(), - disconnect: jest.fn() + disconnect: jest.fn(), })) }) @@ -46,16 +41,16 @@ describe('Restart app works correctly', () => { const factory = await getFactory(store) - const community = await factory.create< - ReturnType['payload'] - >('Community') + const community = await factory.create['payload']>( + 'Community' + ) await factory.create['payload']>('Identity', { - id: community.id + id: community.id, }) await factory.create['payload']>('Identity', { - id: community.id + id: community.id, }) window.HTMLElement.prototype.scrollTo = jest.fn() @@ -81,7 +76,7 @@ describe('Restart app works correctly', () => { store.dispatch( publicChannels.actions.sendInitialChannelMessage({ channelId: generalId, - channelName: 'general' + 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 8e5c0e55fe..3fdfc97081 100644 --- a/packages/desktop/src/rtl-tests/channel.add.test.tsx +++ b/packages/desktop/src/rtl-tests/channel.add.test.tsx @@ -15,18 +15,14 @@ import CreateChannel from '../renderer/components/Channel/CreateChannel/CreateCh import Channel from '../renderer/components/Channel/Channel' import Sidebar from '../renderer/components/Sidebar/Sidebar' -import { - getFactory, - identity, - publicChannels -} from '@quiet/state-manager' +import { getFactory, identity, publicChannels } from '@quiet/state-manager' import { ChannelsReplicatedPayload, CreateChannelPayload, ErrorMessages, IncomingMessages, SendMessagePayload, - SocketActionTypes + SocketActionTypes, } from '@quiet/types' import { modalsActions, ModalsInitialState } from '../renderer/sagas/modals/modals.slice' @@ -44,7 +40,7 @@ describe('Add new channel', () => { window.ResizeObserver = jest.fn().mockImplementation(() => ({ observe: jest.fn(), unobserve: jest.fn(), - disconnect: jest.fn() + disconnect: jest.fn(), })) }) @@ -56,10 +52,9 @@ describe('Add new channel', () => { const factory = await getFactory(store) - await factory.create['payload']>( - 'Identity', - { nickname: 'alice' } - ) + await factory.create['payload']>('Identity', { + nickname: 'alice', + }) renderComponent( <> @@ -81,44 +76,42 @@ describe('Add new channel', () => { { [StoreKeys.Modals]: { ...new ModalsInitialState(), - [ModalName.createChannel]: { open: true } - } + [ModalName.createChannel]: { open: true }, + }, }, socket // Fork state manager's sagas ) const factory = await getFactory(store) - const alice = await factory.create< - ReturnType['payload'] - >('Identity', { nickname: 'alice' }) + const alice = await factory.create['payload']>('Identity', { + nickname: 'alice', + }) const channelName = { input: 'my-Super Channel ', output: 'my-super-channel' } - jest - .spyOn(socket, 'emit') - .mockImplementation(async (...input: [SocketActionTypes, ...socketEventData<[any]>]) => { - const action = input[0] - if (action === SocketActionTypes.CREATE_CHANNEL) { - const payload = input[1] as CreateChannelPayload - expect(payload.channel.owner).toEqual(alice.nickname) - expect(payload.channel.name).toEqual(channelName.output) - const channels = store.getState().PublicChannels.channels.entities - return socket.socketClient.emit(SocketActionTypes.CHANNELS_REPLICATED, { - channels: { - ...channels, - [payload.channel.name]: payload.channel - } - }) - } - if (action === SocketActionTypes.SEND_MESSAGE) { - const data = input[1] as SendMessagePayload - const { message } = data - expect(message.channelId).toEqual(channelName.output) - expect(message.message).toEqual(`Created #${channelName.output}`) - return socket.socketClient.emit(SocketActionTypes.INCOMING_MESSAGES, { - messages: [message] - }) - } - }) + jest.spyOn(socket, 'emit').mockImplementation(async (...input: [SocketActionTypes, ...socketEventData<[any]>]) => { + const action = input[0] + if (action === SocketActionTypes.CREATE_CHANNEL) { + const payload = input[1] as CreateChannelPayload + expect(payload.channel.owner).toEqual(alice.nickname) + expect(payload.channel.name).toEqual(channelName.output) + const channels = store.getState().PublicChannels.channels.entities + return socket.socketClient.emit(SocketActionTypes.CHANNELS_REPLICATED, { + channels: { + ...channels, + [payload.channel.name]: payload.channel, + }, + }) + } + if (action === SocketActionTypes.SEND_MESSAGE) { + const data = input[1] as SendMessagePayload + const { message } = data + expect(message.channelId).toEqual(channelName.output) + expect(message.message).toEqual(`Created #${channelName.output}`) + return socket.socketClient.emit(SocketActionTypes.INCOMING_MESSAGES, { + messages: [message], + }) + } + }) window.HTMLElement.prototype.scrollTo = jest.fn() @@ -173,9 +166,9 @@ describe('Add new channel', () => { ) const factory = await getFactory(store) - const channel = await factory.create< - ReturnType['payload'] - >('PublicChannel') + const channel = await factory.create['payload']>( + 'PublicChannel' + ) renderComponent(, store) @@ -200,10 +193,9 @@ describe('Add new channel', () => { const factory = await getFactory(store) - await factory.create['payload']>( - 'Identity', - { nickname: 'alice' } - ) + await factory.create['payload']>('Identity', { + nickname: 'alice', + }) renderComponent( <> @@ -247,10 +239,9 @@ describe('Add new channel', () => { const factory = await getFactory(store) - await factory.create['payload']>( - 'Identity', - { nickname: 'alice' } - ) + await factory.create['payload']>('Identity', { + nickname: 'alice', + }) renderComponent( <> @@ -303,37 +294,35 @@ describe('Add new channel', () => { const factory = await getFactory(store) - const alice = await factory.create< - ReturnType['payload'] - >('Identity', { nickname: 'alice' }) + const alice = await factory.create['payload']>('Identity', { + nickname: 'alice', + }) - jest - .spyOn(socket, 'emit') - .mockImplementation(async (...input: [SocketActionTypes, ...socketEventData<[any]>]) => { - const action = input[0] - if (action === SocketActionTypes.CREATE_CHANNEL) { - const payload = input[1] as CreateChannelPayload - // const payload = data[0] - expect(payload.channel.owner).toEqual(alice.nickname) - expect(payload.channel.name).toEqual(channelName) - const channels = store.getState().PublicChannels.channels.entities - return socket.socketClient.emit(SocketActionTypes.CHANNELS_REPLICATED, { - channels: { - ...channels, - [payload.channel.name]: payload.channel - } - }) - } - if (action === SocketActionTypes.SEND_MESSAGE) { - const data = input[1] as SendMessagePayload - const { message } = data - expect(message.channelId).toEqual(channelName) - expect(message.message).toEqual(`Created #${channelName}`) - return socket.socketClient.emit(SocketActionTypes.INCOMING_MESSAGES, { - messages: [message] - }) - } - }) + jest.spyOn(socket, 'emit').mockImplementation(async (...input: [SocketActionTypes, ...socketEventData<[any]>]) => { + const action = input[0] + if (action === SocketActionTypes.CREATE_CHANNEL) { + const payload = input[1] as CreateChannelPayload + // const payload = data[0] + expect(payload.channel.owner).toEqual(alice.nickname) + expect(payload.channel.name).toEqual(channelName) + const channels = store.getState().PublicChannels.channels.entities + return socket.socketClient.emit(SocketActionTypes.CHANNELS_REPLICATED, { + channels: { + ...channels, + [payload.channel.name]: payload.channel, + }, + }) + } + if (action === SocketActionTypes.SEND_MESSAGE) { + const data = input[1] as SendMessagePayload + const { message } = data + expect(message.channelId).toEqual(channelName) + expect(message.message).toEqual(`Created #${channelName}`) + return socket.socketClient.emit(SocketActionTypes.INCOMING_MESSAGES, { + messages: [message], + }) + } + }) renderComponent( <> @@ -391,16 +380,16 @@ describe('Add new channel', () => { { [StoreKeys.Modals]: { ...new ModalsInitialState(), - [ModalName.createChannel]: { open: true } - } + [ModalName.createChannel]: { open: true }, + }, }, socket // Fork state manager's sagas ) const factory = await getFactory(store) - const alice = await factory.create< - ReturnType['payload'] - >('Identity', { nickname: 'alice' }) + const alice = await factory.create['payload']>('Identity', { + nickname: 'alice', + }) const channels = ['zzz', 'abc', '12a'] @@ -418,7 +407,7 @@ describe('Add new channel', () => { channels: { ...channels, [payload.channel.name]: payload.channel, - } + }, }) } }) diff --git a/packages/desktop/src/rtl-tests/channel.main.test.tsx b/packages/desktop/src/rtl-tests/channel.main.test.tsx index c77b4f315a..88b4f1e4f9 100644 --- a/packages/desktop/src/rtl-tests/channel.main.test.tsx +++ b/packages/desktop/src/rtl-tests/channel.main.test.tsx @@ -22,7 +22,7 @@ import { AUTODOWNLOAD_SIZE_LIMIT, network, connection, - generateMessageFactoryContentWithId + generateMessageFactoryContentWithId, } from '@quiet/state-manager' import { SocketActionTypes, @@ -39,8 +39,8 @@ import { MessageVerificationStatus, DownloadStatus, IncomingMessages, - ResponseLaunchCommunityPayload -, Community + ResponseLaunchCommunityPayload, + Community, } from '@quiet/types' import { keyFromCertificate, parseCertificate } from '@quiet/identity' @@ -64,12 +64,12 @@ jest.mock('electron', () => { return [ { show: jest.fn(), - isFocused: jest.fn() - } + isFocused: jest.fn(), + }, ] - } - } - } + }, + }, + }, } }) @@ -77,9 +77,9 @@ jest.mock('../shared/sounds', () => ({ ...jest.requireActual('../shared/sounds'), soundTypeToAudio: { pow: { - play: jest.fn() - } - } + play: jest.fn(), + }, + }, })) describe('Channel', () => { @@ -91,7 +91,7 @@ describe('Channel', () => { window.ResizeObserver = jest.fn().mockImplementation(() => ({ observe: jest.fn(), unobserve: jest.fn(), - disconnect: jest.fn() + disconnect: jest.fn(), })) }) @@ -123,9 +123,9 @@ describe('Channel', () => { // ReturnType['payload'] // >('Community') - const alice = await factory.create< - ReturnType['payload'] - >('Identity', { nickname: 'alice' }) + const alice = await factory.create['payload']>('Identity', { + nickname: 'alice', + }) window.HTMLElement.prototype.scrollTo = jest.fn() @@ -151,9 +151,9 @@ describe('Channel', () => { const factory = await getFactory(store) - const community = await factory.create< - ReturnType['payload'] - >('Community') + const community = await factory.create['payload']>( + 'Community' + ) const entities = store.getState().PublicChannels.channels.entities @@ -161,19 +161,23 @@ describe('Channel', () => { expect(generalId).not.toBeUndefined() await act(async () => { - store.dispatch(publicChannels.actions.setCurrentChannel({ - // @ts-expect-error - channelId: generalId - })) + store.dispatch( + publicChannels.actions.setCurrentChannel({ + // @ts-expect-error + channelId: generalId, + }) + ) }) - const alice = await factory.create< - ReturnType['payload'] - >('Identity', { id: community.id, nickname: 'alice' }) + const alice = await factory.create['payload']>('Identity', { + id: community.id, + nickname: 'alice', + }) - const john = await factory.create< - ReturnType['payload'] - >('Identity', { id: community.id, nickname: 'john' }) + const john = await factory.create['payload']>('Identity', { + id: community.id, + nickname: 'john', + }) expect(john.userCertificate).not.toBeNull() // @ts-expect-error const johnPublicKey = keyFromCertificate(parseCertificate(john.userCertificate)) @@ -189,10 +193,10 @@ describe('Channel', () => { createdAt: DateTime.utc().valueOf(), channelId: generalId, signature: '', - pubKey: '' - } + pubKey: '', + }, }) - ).payload.message + ).payload.message, } const spoofedMessage: ChannelMessage = { @@ -206,10 +210,10 @@ describe('Channel', () => { createdAt: DateTime.utc().valueOf(), channelId: generalId, signature: '', - pubKey: johnPublicKey - } + pubKey: johnPublicKey, + }, }) - ).payload.message + ).payload.message, } window.HTMLElement.prototype.scrollTo = jest.fn() @@ -238,16 +242,16 @@ describe('Channel', () => { { messages: [authenticMessage], communityId: community.id, - isVerified: true - } + isVerified: true, + }, ]) yield* apply(socket.socketClient, socket.socketClient.emit, [ SocketActionTypes.INCOMING_MESSAGES, { messages: [spoofedMessage], communityId: community.id, - isVerified: false - } + isVerified: false, + }, ]) } }) @@ -260,17 +264,18 @@ describe('Channel', () => { const factory = await getFactory(store) - const community = await factory.create< - ReturnType['payload'] - >('Community') + const community = await factory.create['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 alice = await factory.create['payload']>('Identity', { + id: community.id, + nickname: 'alice', + }) const aliceMessage = ( await factory.build('Message', { @@ -282,8 +287,8 @@ describe('Channel', () => { createdAt: DateTime.utc().valueOf(), channelId: generalId, signature: '', - pubKey: '' - } + pubKey: '', + }, }) ).payload.message @@ -310,8 +315,8 @@ describe('Channel', () => { { messages: [aliceMessage], communityId: community.id, - isVerified: true - } + isVerified: true, + }, ]) } }) @@ -327,9 +332,10 @@ describe('Channel', () => { const community = await factory.create['payload']>( 'Community' ) - await factory.create['payload']>( - 'Identity', { id: community.id, nickname: 'john' } - ) + await factory.create['payload']>('Identity', { + id: community.id, + nickname: 'john', + }) renderComponent( <> @@ -355,28 +361,26 @@ describe('Channel', () => { const factory = await getFactory(store) - const community = await factory.create< - ReturnType['payload'] - >('Community') + const community = await factory.create['payload']>( + 'Community' + ) const entities = store.getState().PublicChannels.channels.entities const generalId = Object.keys(entities).find(key => entities[key]?.name === 'general') expect(generalId).not.toBeUndefined() - const alice = await factory.create< - ReturnType['payload'] - >('Identity', { id: community.id, nickname: 'alice' }) + const alice = await factory.create['payload']>('Identity', { + id: community.id, + nickname: 'alice', + }) - await factory.create['payload']>( - 'Message', - { - identity: alice, - // @ts-expect-error - message: generateMessageFactoryContentWithId(generalId), - verifyAutomatically: true - } - ) + await factory.create['payload']>('Message', { + identity: alice, + // @ts-expect-error + message: generateMessageFactoryContentWithId(generalId), + verifyAutomatically: true, + }) window.HTMLElement.prototype.scrollTo = jest.fn() @@ -390,10 +394,9 @@ describe('Channel', () => { await act(async () => {}) // Confirm there are messages to display - expect( - Object.values(publicChannels.selectors.currentChannelMessagesMergedBySender(store.getState())) - .length - ).toBe(1) + expect(Object.values(publicChannels.selectors.currentChannelMessagesMergedBySender(store.getState())).length).toBe( + 1 + ) // Verify loading spinner is not visible const spinner = await screen.queryByText(fetchingChannelMessagesText) @@ -408,13 +411,14 @@ describe('Channel', () => { const factory = await getFactory(store) - const community = await factory.create< - ReturnType['payload'] - >('Community') + const community = await factory.create['payload']>( + 'Community' + ) - const alice = await factory.create< - ReturnType['payload'] - >('Identity', { id: community.id, nickname: 'alice' }) + const alice = await factory.create['payload']>('Identity', { + id: community.id, + nickname: 'alice', + }) window.HTMLElement.prototype.scrollTo = jest.fn() @@ -435,9 +439,7 @@ describe('Channel', () => { const sentMessage = publicChannels.selectors.currentChannelMessages(store.getState())[0] // Confirm message has been stored immediately - const displayableMessages = publicChannels.selectors.currentChannelMessagesMergedBySender( - store.getState() - ) + const displayableMessages = publicChannels.selectors.currentChannelMessagesMergedBySender(store.getState()) expect(Object.values(displayableMessages).length).toBe(1) // Verify message status is 'pending' @@ -451,7 +453,7 @@ describe('Channel', () => { // Update message sending status store.dispatch( messages.actions.incomingMessages({ - messages: [sentMessage] + messages: [sentMessage], }) ) @@ -464,15 +466,13 @@ describe('Channel', () => { SocketActionTypes.INCOMING_MESSAGES, { messages: [sentMessage], - communityId: community.id - } + communityId: community.id, + }, ]) } // Confirm 'pending' message status has been removed - expect(messages.selectors.messagesSendingStatus(store.getState())[sentMessage.id]).toBe( - undefined - ) + expect(messages.selectors.messagesSendingStatus(store.getState())[sentMessage.id]).toBe(undefined) // Confirm message is no longer greyed out expect(await screen.findByText(messageText)).toBeVisible() @@ -487,17 +487,18 @@ describe('Channel', () => { const factory = await getFactory(store) - const community = await factory.create< - ReturnType['payload'] - >('Community') + const community = await factory.create['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 alice = await factory.create['payload']>('Identity', { + id: community.id, + nickname: 'alice', + }) window.HTMLElement.prototype.scrollTo = jest.fn() @@ -522,8 +523,8 @@ describe('Channel', () => { createdAt: messagesText.indexOf(msg) + 1, channelId: generalId, signature: '', - pubKey: '' - } + pubKey: '', + }, }) ).payload.message messages.push(message) @@ -541,24 +542,24 @@ describe('Channel', () => { { messages: [message1], communityId: community.id, - isVerified: true - } + isVerified: true, + }, ]) yield* apply(socket.socketClient, socket.socketClient.emit, [ SocketActionTypes.INCOMING_MESSAGES, { messages: [message3], communityId: community.id, - isVerified: true - } + isVerified: true, + }, ]) yield* apply(socket.socketClient, socket.socketClient.emit, [ SocketActionTypes.INCOMING_MESSAGES, { messages: [message2], communityId: community.id, - isVerified: true - } + isVerified: true, + }, ]) } @@ -576,13 +577,14 @@ describe('Channel', () => { const factory = await getFactory(store) - const community = await factory.create< - ReturnType['payload'] - >('Community') + const community = await factory.create['payload']>( + 'Community' + ) - const alice = await factory.create< - ReturnType['payload'] - >('Identity', { id: community.id, nickname: 'alice' }) + const alice = await factory.create['payload']>('Identity', { + id: community.id, + nickname: 'alice', + }) window.HTMLElement.prototype.scrollTo = jest.fn() @@ -644,10 +646,7 @@ describe('Channel', () => { const messageInput = screen.getByTestId('messageInput') // Why does the first letter not get entered? - await userEvent.type( - messageInput, - 'mmulti-line{Shift>}{Enter}{/Shift}message{Shift>}{Enter}{/Shift}hello' - ) + await userEvent.type(messageInput, 'mmulti-line{Shift>}{Enter}{/Shift}message{Shift>}{Enter}{/Shift}hello') expect(messageInput.textContent).toBe('multi-line\nmessage\nhello') }) @@ -673,10 +672,7 @@ describe('Channel', () => { // TODO Why does the first letter not get entered? // Test where the starting caret is - await userEvent.type( - messageInput, - 'mmulti-line{Shift>}{Enter}{/Shift}message{Shift>}{Enter}{/Shift}hello' - ) + await userEvent.type(messageInput, 'mmulti-line{Shift>}{Enter}{/Shift}message{Shift>}{Enter}{/Shift}hello') expect(window?.getSelection()?.anchorNode?.nodeValue).toBe('hello') expect(window?.getSelection()?.anchorOffset).toBe(5) @@ -715,13 +711,14 @@ describe('Channel', () => { const factory = await getFactory(store) - const community = await factory.create< - ReturnType['payload'] - >('Community') + const community = await factory.create['payload']>( + 'Community' + ) - const alice = await factory.create< - ReturnType['payload'] - >('Identity', { id: community.id, nickname: 'alice' }) + const alice = await factory.create['payload']>('Identity', { + id: community.id, + nickname: 'alice', + }) window.HTMLElement.prototype.scrollTo = jest.fn() @@ -762,61 +759,60 @@ describe('Channel', () => { const factory = await getFactory(initialState) - const community = await factory.create< - ReturnType['payload'] - >('Community') + const community = await factory.create['payload']>( + 'Community' + ) - await factory.create< - ReturnType['payload'] - >('Identity', { id: community.id, nickname: 'alice' }) + await factory.create['payload']>('Identity', { + id: community.id, + nickname: 'alice', + }) - let cid: string = '' + let cid = '' const uploadingDelay = 100 - jest - .spyOn(socket, 'emit') - .mockImplementation(async (...input: [SocketActionTypes, ...socketEventData<[any]>]) => { - const action = input[0] - if (action === SocketActionTypes.LAUNCH_COMMUNITY) { - const data = input[1] as InitCommunityPayload - const payload = data - return socket.socketClient.emit(SocketActionTypes.COMMUNITY, { - id: payload.id - }) - } - if (action === SocketActionTypes.UPLOAD_FILE) { - const data = input[1] as UploadFilePayload - const payload = data - - cid = `uploading_${payload.file.message.id}` - - await new Promise(resolve => { - setTimeout(resolve, uploadingDelay) - }) - - socket.socketClient.emit(SocketActionTypes.UPLOADED_FILE, { - ...payload.file, - cid: cid, - path: null, - width: 100, - height: 100, - size: AUTODOWNLOAD_SIZE_LIMIT - 2048 - }) - return socket.socketClient.emit(SocketActionTypes.DOWNLOAD_PROGRESS, { - mid: payload.file.message.id, - cid: cid, - downloadState: DownloadState.Hosted - }) - } - if (action === SocketActionTypes.SEND_MESSAGE) { - const data = input[1] as SendMessagePayload - const payload = data - return socket.socketClient.emit(SocketActionTypes.INCOMING_MESSAGES, { - messages: [payload.message] - }) - } - }) + jest.spyOn(socket, 'emit').mockImplementation(async (...input: [SocketActionTypes, ...socketEventData<[any]>]) => { + const action = input[0] + if (action === SocketActionTypes.LAUNCH_COMMUNITY) { + const data = input[1] as InitCommunityPayload + const payload = data + return socket.socketClient.emit(SocketActionTypes.COMMUNITY, { + id: payload.id, + }) + } + if (action === SocketActionTypes.UPLOAD_FILE) { + const data = input[1] as UploadFilePayload + const payload = data + + cid = `uploading_${payload.file.message.id}` + + await new Promise(resolve => { + setTimeout(resolve, uploadingDelay) + }) + + socket.socketClient.emit(SocketActionTypes.UPLOADED_FILE, { + ...payload.file, + cid: cid, + path: null, + width: 100, + height: 100, + size: AUTODOWNLOAD_SIZE_LIMIT - 2048, + }) + return socket.socketClient.emit(SocketActionTypes.DOWNLOAD_PROGRESS, { + mid: payload.file.message.id, + cid: cid, + downloadState: DownloadState.Hosted, + }) + } + if (action === SocketActionTypes.SEND_MESSAGE) { + const data = input[1] as SendMessagePayload + const payload = data + return socket.socketClient.emit(SocketActionTypes.INCOMING_MESSAGES, { + messages: [payload.message], + }) + } + }) const { store, runSaga } = await prepareStore( initialState.getState(), @@ -826,7 +822,7 @@ describe('Channel', () => { const fileContent: FileContent = { path: 'path/to/image.png', name: 'image', - ext: '.png' + ext: '.png', } // Log all the dispatched actions in order @@ -899,9 +895,10 @@ describe('Channel', () => { ReturnType['payload'] >('Community', { rootCa: 'rootCa', privateKey: 'privateKey' }) - const alice = await factory.create< - ReturnType['payload'] - >('Identity', { id: community.id, nickname: 'alice' }) + const alice = await factory.create['payload']>('Identity', { + id: community.id, + nickname: 'alice', + }) const message = Math.random().toString(36).substr(2.9) @@ -919,28 +916,25 @@ describe('Channel', () => { message: { id: message, // @ts-expect-error - channelId: generalId + channelId: generalId, }, - size: AUTODOWNLOAD_SIZE_LIMIT - 2048 + size: AUTODOWNLOAD_SIZE_LIMIT - 2048, } - await factory.create['payload']>( - 'Message', - { - identity: alice, - message: { - id: message, - type: MessageType.Image, - message: '', - createdAt: DateTime.utc().valueOf(), - // @ts-expect-error - channelId: generalId, - signature: '', - pubKey: '', - media: missingFile - } - } - ) + await factory.create['payload']>('Message', { + identity: alice, + message: { + id: message, + type: MessageType.Image, + message: '', + createdAt: DateTime.utc().valueOf(), + // @ts-expect-error + channelId: generalId, + signature: '', + pubKey: '', + media: missingFile, + }, + }) initialState.dispatch( files.actions.updateDownloadStatus({ @@ -950,33 +944,31 @@ describe('Channel', () => { downloadProgress: { downloaded: AUTODOWNLOAD_SIZE_LIMIT / 2, size: AUTODOWNLOAD_SIZE_LIMIT - 2048, - transferSpeed: 1024 - } + transferSpeed: 1024, + }, }) ) - jest - .spyOn(socket, 'emit') - .mockImplementation(async (...input: [SocketActionTypes, ...socketEventData<[any]>]) => { - const action = input[0] - if (action === SocketActionTypes.LAUNCH_COMMUNITY) { - const data = input[1] as InitCommunityPayload - const payload = data - return socket.socketClient.emit(SocketActionTypes.COMMUNITY, { - id: payload.id - }) - } - if (action === SocketActionTypes.DOWNLOAD_FILE) { - const data = input[1] as DownloadFilePayload - const payload = data - expect(payload.metadata.cid).toEqual(missingFile.cid) - await new Promise(resolve => setTimeout(resolve, 1000)) - return socket.socketClient.emit(SocketActionTypes.UPDATE_MESSAGE_MEDIA, { - ...missingFile, - path: `${__dirname}/test-image.jpeg` - }) - } - }) + jest.spyOn(socket, 'emit').mockImplementation(async (...input: [SocketActionTypes, ...socketEventData<[any]>]) => { + const action = input[0] + if (action === SocketActionTypes.LAUNCH_COMMUNITY) { + const data = input[1] as InitCommunityPayload + const payload = data + return socket.socketClient.emit(SocketActionTypes.COMMUNITY, { + id: payload.id, + }) + } + if (action === SocketActionTypes.DOWNLOAD_FILE) { + const data = input[1] as DownloadFilePayload + const payload = data + expect(payload.metadata.cid).toEqual(missingFile.cid) + await new Promise(resolve => setTimeout(resolve, 1000)) + return socket.socketClient.emit(SocketActionTypes.UPDATE_MESSAGE_MEDIA, { + ...missingFile, + path: `${__dirname}/test-image.jpeg`, + }) + } + }) const { store, runSaga } = await prepareStore( initialState.getState(), @@ -1028,40 +1020,38 @@ describe('Channel', () => { const factory = await getFactory(initialState) - const community = await factory.create< - ReturnType['payload'] - >('Community') - - await factory.create['payload']>( - 'Identity', - { id: community.id, nickname: 'alice' } + const community = await factory.create['payload']>( + 'Community' ) - jest - .spyOn(socket, 'emit') - .mockImplementation(async (...input: [SocketActionTypes, ...socketEventData<[any]>]) => { - const action = input[0] - if (action === SocketActionTypes.LAUNCH_COMMUNITY) { - const data = input[1] as InitCommunityPayload - const payload = data - return socket.socketClient.emit(SocketActionTypes.COMMUNITY, { - id: payload.id - }) - } - if (action === SocketActionTypes.UPLOAD_FILE) { - const data = input[1] as UploadFilePayload - const payload = data - socket.socketClient.emit(SocketActionTypes.UPLOADED_FILE, { - ...payload.file, - size: 1024 - }) - return socket.socketClient.emit(SocketActionTypes.DOWNLOAD_PROGRESS, { - mid: payload.file.message.id, - cid: `uploading_${payload.file.message.id}`, - downloadState: DownloadState.Hosted - }) - } - }) + await factory.create['payload']>('Identity', { + id: community.id, + nickname: 'alice', + }) + + jest.spyOn(socket, 'emit').mockImplementation(async (...input: [SocketActionTypes, ...socketEventData<[any]>]) => { + const action = input[0] + if (action === SocketActionTypes.LAUNCH_COMMUNITY) { + const data = input[1] as InitCommunityPayload + const payload = data + return socket.socketClient.emit(SocketActionTypes.COMMUNITY, { + id: payload.id, + }) + } + if (action === SocketActionTypes.UPLOAD_FILE) { + const data = input[1] as UploadFilePayload + const payload = data + socket.socketClient.emit(SocketActionTypes.UPLOADED_FILE, { + ...payload.file, + size: 1024, + }) + return socket.socketClient.emit(SocketActionTypes.DOWNLOAD_PROGRESS, { + mid: payload.file.message.id, + cid: `uploading_${payload.file.message.id}`, + downloadState: DownloadState.Hosted, + }) + } + }) const { store, runSaga } = await prepareStore( initialState.getState(), @@ -1071,7 +1061,7 @@ describe('Channel', () => { const fileContent: FileContent = { path: 'path/to/file.ext', name: 'file', - ext: '.ext' + ext: '.ext', } // Log all the dispatched actions in order @@ -1126,13 +1116,14 @@ describe('Channel', () => { const factory = await getFactory(initialState) - const community = await factory.create< - ReturnType['payload'] - >('Community') + const community = await factory.create['payload']>( + 'Community' + ) - const alice = await factory.create< - ReturnType['payload'] - >('Identity', { id: community.id, nickname: 'alice' }) + const alice = await factory.create['payload']>('Identity', { + id: community.id, + nickname: 'alice', + }) const messageId = Math.random().toString(36).substr(2.9) const entities = initialState.getState().PublicChannels.channels.entities @@ -1147,8 +1138,8 @@ describe('Channel', () => { message: { id: messageId, // @ts-expect-error - channelId: generalId - } + channelId: generalId, + }, } const message = ( @@ -1162,23 +1153,21 @@ describe('Channel', () => { channelId: generalId, signature: '', pubKey: '', - media: media - } + media: media, + }, }) ).payload.message - jest - .spyOn(socket, 'emit') - .mockImplementation(async (...input: [SocketActionTypes, ...socketEventData<[any]>]) => { - const action = input[0] - if (action === SocketActionTypes.LAUNCH_COMMUNITY) { - const data = input[1] as InitCommunityPayload - const payload = data - return socket.socketClient.emit(SocketActionTypes.COMMUNITY, { - id: payload.id - }) - } - }) + jest.spyOn(socket, 'emit').mockImplementation(async (...input: [SocketActionTypes, ...socketEventData<[any]>]) => { + const action = input[0] + if (action === SocketActionTypes.LAUNCH_COMMUNITY) { + const data = input[1] as InitCommunityPayload + const payload = data + return socket.socketClient.emit(SocketActionTypes.COMMUNITY, { + id: payload.id, + }) + } + }) const { store, runSaga } = await prepareStore( initialState.getState(), @@ -1213,8 +1202,8 @@ describe('Channel', () => { { messages: [message], communityId: community.id, - isVerified: true - } + isVerified: true, + }, ]) } @@ -1243,13 +1232,14 @@ describe('Channel', () => { const factory = await getFactory(initialState) - const community = await factory.create< - ReturnType['payload'] - >('Community') + const community = await factory.create['payload']>( + 'Community' + ) - const alice = await factory.create< - ReturnType['payload'] - >('Identity', { id: community.id, nickname: 'alice' }) + const alice = await factory.create['payload']>('Identity', { + id: community.id, + nickname: 'alice', + }) const messageId = Math.random().toString(36).substr(2.9) const entities = initialState.getState().PublicChannels.channels.entities @@ -1264,8 +1254,8 @@ describe('Channel', () => { message: { id: messageId, // @ts-expect-error - channelId: generalId - } + channelId: generalId, + }, } const message = ( @@ -1279,23 +1269,21 @@ describe('Channel', () => { channelId: generalId, signature: '', pubKey: '', - media: media - } + media: media, + }, }) ).payload.message - jest - .spyOn(socket, 'emit') - .mockImplementation(async (...input: [SocketActionTypes, ...socketEventData<[any]>]) => { - const action = input[0] - if (action === SocketActionTypes.LAUNCH_COMMUNITY) { - const data = input[1] as InitCommunityPayload - const payload = data - return socket.socketClient.emit(SocketActionTypes.COMMUNITY, { - id: payload.id - }) - } - }) + jest.spyOn(socket, 'emit').mockImplementation(async (...input: [SocketActionTypes, ...socketEventData<[any]>]) => { + const action = input[0] + if (action === SocketActionTypes.LAUNCH_COMMUNITY) { + const data = input[1] as InitCommunityPayload + const payload = data + return socket.socketClient.emit(SocketActionTypes.COMMUNITY, { + id: payload.id, + }) + } + }) const { store, runSaga } = await prepareStore( initialState.getState(), @@ -1330,8 +1318,8 @@ describe('Channel', () => { { messages: [message], communityId: community.id, - isVerified: true - } + isVerified: true, + }, ]) } @@ -1362,13 +1350,14 @@ describe('Channel', () => { const factory = await getFactory(initialState) - const community = await factory.create< - ReturnType['payload'] - >('Community') + const community = await factory.create['payload']>( + 'Community' + ) - const alice = await factory.create< - ReturnType['payload'] - >('Identity', { id: community.id, nickname: 'alice' }) + const alice = await factory.create['payload']>('Identity', { + id: community.id, + nickname: 'alice', + }) const messageId = Math.random().toString(36).substr(2.9) const entities = initialState.getState().PublicChannels.channels.entities @@ -1383,8 +1372,8 @@ describe('Channel', () => { message: { id: messageId, // @ts-expect-error - channelId: generalId - } + channelId: generalId, + }, } const message = ( @@ -1398,23 +1387,21 @@ describe('Channel', () => { channelId: generalId, signature: '', pubKey: '', - media: media - } + media: media, + }, }) ).payload.message - jest - .spyOn(socket, 'emit') - .mockImplementation(async (...input: [SocketActionTypes, ...socketEventData<[any]>]) => { - const action = input[0] - if (action === SocketActionTypes.LAUNCH_COMMUNITY) { - const data = input[1] as InitCommunityPayload - const payload = data - return socket.socketClient.emit(SocketActionTypes.COMMUNITY, { - id: payload.id - }) - } - }) + jest.spyOn(socket, 'emit').mockImplementation(async (...input: [SocketActionTypes, ...socketEventData<[any]>]) => { + const action = input[0] + if (action === SocketActionTypes.LAUNCH_COMMUNITY) { + const data = input[1] as InitCommunityPayload + const payload = data + return socket.socketClient.emit(SocketActionTypes.COMMUNITY, { + id: payload.id, + }) + } + }) const { store, runSaga } = await prepareStore( initialState.getState(), @@ -1446,7 +1433,7 @@ describe('Channel', () => { const verificationStatus: MessageVerificationStatus = { publicKey: message.pubKey, signature: message.signature, - isVerified: true + isVerified: true, } store.dispatch(messages.actions.addMessageVerificationStatus(verificationStatus)) @@ -1457,8 +1444,8 @@ describe('Channel', () => { { messages: [message], communityId: community.id, - isVerfied: true - } + isVerfied: true, + }, ]) } @@ -1474,7 +1461,7 @@ describe('Channel', () => { expect(downloadSpy).toHaveBeenCalledWith(SocketActionTypes.DOWNLOAD_FILE, { peerId: alice.peerId.id, - metadata: media + metadata: media, }) expect(actions).toMatchInlineSnapshot(` diff --git a/packages/desktop/src/rtl-tests/channel.menu.test.tsx b/packages/desktop/src/rtl-tests/channel.menu.test.tsx index 04eeb04da3..96b3b84048 100644 --- a/packages/desktop/src/rtl-tests/channel.menu.test.tsx +++ b/packages/desktop/src/rtl-tests/channel.menu.test.tsx @@ -25,7 +25,7 @@ describe('Channel menu', () => { window.ResizeObserver = jest.fn().mockImplementation(() => ({ observe: jest.fn(), unobserve: jest.fn(), - disconnect: jest.fn() + disconnect: jest.fn(), })) }) @@ -37,17 +37,14 @@ describe('Channel menu', () => { const factory = await getFactory(store) - await factory.create['payload']>( - 'Community', - { - id: '0', - name: 'community', - CA: null, - registrarUrl: 'http://ugmx77q2tnm5fliyfxfeen5hsuzjtbsz44tsldui2ju7vl5xj4d447yd.onion', - rootCa: '', - peerList: [] - } - ) + await factory.create['payload']>('Community', { + id: '0', + name: 'community', + CA: null, + registrarUrl: 'http://ugmx77q2tnm5fliyfxfeen5hsuzjtbsz44tsldui2ju7vl5xj4d447yd.onion', + rootCa: '', + peerList: [], + }) /* Context menu is not visible to non-owners at all, for now */ store.dispatch(navigationActions.openMenu({ menu: MenuName.Channel })) @@ -84,15 +81,15 @@ describe('Channel menu', () => { const factory = await getFactory(store) - const community = await factory.create< - ReturnType['payload'] - >('Community') - - await factory.create['payload']>( - 'Identity', - { id: community.id, nickname: 'alice' } + const community = await factory.create['payload']>( + 'Community' ) + await factory.create['payload']>('Identity', { + id: community.id, + nickname: 'alice', + }) + window.HTMLElement.prototype.scrollTo = jest.fn() renderComponent( diff --git a/packages/desktop/src/rtl-tests/channel.switch.test.tsx b/packages/desktop/src/rtl-tests/channel.switch.test.tsx index 4ce1e82b3c..77e840774a 100644 --- a/packages/desktop/src/rtl-tests/channel.switch.test.tsx +++ b/packages/desktop/src/rtl-tests/channel.switch.test.tsx @@ -26,7 +26,7 @@ import { MessageType, ChannelMessage, messages, - generateMessageFactoryContentWithId + generateMessageFactoryContentWithId, } from '@quiet/state-manager' import { DateTime } from 'luxon' @@ -43,12 +43,12 @@ jest.mock('electron', () => { return [ { show: jest.fn(), - isFocused: jest.fn() - } + isFocused: jest.fn(), + }, ] - } - } - } + }, + }, + }, } }) @@ -71,20 +71,18 @@ describe('Switch channels', () => { window.ResizeObserver = jest.fn().mockImplementation(() => ({ observe: jest.fn(), unobserve: jest.fn(), - disconnect: jest.fn() + disconnect: jest.fn(), })) redux = await prepareStore({}, socket) factory = await getFactory(redux.store) - community = await factory.create< - ReturnType['payload'] - >('Community') + community = await factory.create['payload']>('Community') - alice = await factory.create['payload']>( - 'Identity', - { id: community.id, nickname: 'alice' } - ) + 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') expect(generalId).not.toBeUndefined() @@ -92,18 +90,15 @@ describe('Switch channels', () => { 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, - id: name - } - } - ) + await factory.create['payload']>('PublicChannel', { + channel: { + name: name, + description: `Welcome to #${name}`, + timestamp: DateTime.utc().valueOf(), + owner: alice.nickname, + id: name, + }, + }) } }) @@ -114,7 +109,7 @@ describe('Switch channels', () => { identity: alice, // @ts-expect-error message: generateMessageFactoryContentWithId(generalId), - verifyAutomatically: true + verifyAutomatically: true, }) window.HTMLElement.prototype.scrollTo = jest.fn() @@ -169,9 +164,9 @@ describe('Switch channels', () => { createdAt: DateTime.utc().valueOf(), channelId: id, signature: '', - pubKey: '' + pubKey: '', }, - verifyAutomatically: true + verifyAutomatically: true, }) ).payload.message messages.push(message) @@ -187,7 +182,7 @@ describe('Switch channels', () => { // Set 'general' as active channel store.dispatch( publicChannels.actions.setCurrentChannel({ - channelId: 'general' + channelId: 'general', }) ) @@ -217,8 +212,8 @@ describe('Switch channels', () => { SocketActionTypes.INCOMING_MESSAGES, { messages: messages, - communityId: community.id - } + communityId: community.id, + }, ]) } }) @@ -234,9 +229,9 @@ describe('Switch channels', () => { createdAt: DateTime.utc().valueOf(), channelId: 'general', signature: '', - pubKey: '' + pubKey: '', }, - verifyAutomatically: true + verifyAutomatically: true, }) ).payload.message @@ -267,8 +262,8 @@ describe('Switch channels', () => { SocketActionTypes.INCOMING_MESSAGES, { messages: [message], - communityId: community.id - } + communityId: community.id, + }, ]) } }) @@ -284,9 +279,9 @@ describe('Switch channels', () => { createdAt: DateTime.utc().valueOf(), channelId: 'travels', signature: '', - pubKey: '' + pubKey: '', }, - verifyAutomatically: true + verifyAutomatically: true, }) ).payload.message @@ -303,7 +298,7 @@ describe('Switch channels', () => { // Set 'general' as active channel store.dispatch( publicChannels.actions.setCurrentChannel({ - channelId: 'general' + channelId: 'general', }) ) @@ -323,13 +318,11 @@ 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() @@ -340,8 +333,8 @@ describe('Switch channels', () => { { messages: [message], communityId: community.id, - isVerified: true - } + isVerified: true, + }, ]) } }) diff --git a/packages/desktop/src/rtl-tests/community.create.test.tsx b/packages/desktop/src/rtl-tests/community.create.test.tsx index ab2f5972b3..c8444336c7 100644 --- a/packages/desktop/src/rtl-tests/community.create.test.tsx +++ b/packages/desktop/src/rtl-tests/community.create.test.tsx @@ -26,7 +26,7 @@ import { ResponseLaunchCommunityPayload, ResponseRegistrarPayload, SaveOwnerCertificatePayload, - SocketActionTypes + SocketActionTypes, } from '@quiet/state-manager' import Channel from '../renderer/components/Channel/Channel' import LoadingPanel from '../renderer/components/LoadingPanel/LoadingPanel' @@ -46,7 +46,7 @@ describe('User', () => { window.ResizeObserver = jest.fn().mockImplementation(() => ({ observe: jest.fn(), unobserve: jest.fn(), - disconnect: jest.fn() + disconnect: jest.fn(), })) }) @@ -84,12 +84,12 @@ describe('User', () => { network: { hiddenService: { onionAddress: 'onionAddress', - privateKey: 'privKey' + privateKey: 'privKey', }, peerId: { - id: 'peerId' - } - } + id: 'peerId', + }, + }, }) } if (action === SocketActionTypes.REGISTER_OWNER_CERTIFICATE) { @@ -98,17 +98,17 @@ describe('User', () => { communityId: payload.communityId, network: { certificate: payload.permsData.certificate, - peers: [] - } + peers: [], + }, }) } if (action === SocketActionTypes.CREATE_COMMUNITY) { const payload = input[1] as InitCommunityPayload socket.socketClient.emit(SocketActionTypes.COMMUNITY, { - id: payload.id + id: payload.id, }) socket.socketClient.emit(SocketActionTypes.NEW_COMMUNITY, { - id: payload.id + id: payload.id, }) socket.socketClient.emit(SocketActionTypes.CHANNELS_REPLICATED, { @@ -118,9 +118,9 @@ describe('User', () => { description: 'string', owner: 'owner', timestamp: 0, - id: generalId - } - } + id: generalId, + }, + }, }) } if (action === SocketActionTypes.LAUNCH_REGISTRAR) { @@ -130,8 +130,8 @@ describe('User', () => { payload: { privateKey: 'privateKey', onionAddress: 'onionAddress', - port: 7909 - } + port: 7909, + }, }) } }) diff --git a/packages/desktop/src/rtl-tests/community.join.test.tsx b/packages/desktop/src/rtl-tests/community.join.test.tsx index bc8b37afd0..8061a1c5a6 100644 --- a/packages/desktop/src/rtl-tests/community.join.test.tsx +++ b/packages/desktop/src/rtl-tests/community.join.test.tsx @@ -25,13 +25,19 @@ import { ErrorMessages, getFactory, errors, - ResponseCreateNetworkPayload + ResponseCreateNetworkPayload, } from '@quiet/state-manager' import Channel from '../renderer/components/Channel/Channel' import LoadingPanel from '../renderer/components/LoadingPanel/LoadingPanel' import { createUserCertificateTestHelper } from '@quiet/identity' import { AnyAction } from 'redux' -import { ChannelsReplicatedPayload, ErrorPayload, ResponseLaunchCommunityPayload, SendOwnerCertificatePayload, SendUserCertificatePayload } from '@quiet/types' +import { + ChannelsReplicatedPayload, + ErrorPayload, + ResponseLaunchCommunityPayload, + SendOwnerCertificatePayload, + SendUserCertificatePayload, +} from '@quiet/types' jest.setTimeout(20_000) @@ -44,7 +50,7 @@ describe('User', () => { window.ResizeObserver = jest.fn().mockImplementation(() => ({ observe: jest.fn(), unobserve: jest.fn(), - disconnect: jest.fn() + disconnect: jest.fn(), })) }) @@ -70,81 +76,78 @@ describe('User', () => { const factory = await getFactory(store) - jest - .spyOn(socket, 'emit') - .mockImplementation(async (...input: [SocketActionTypes, ...socketEventData<[any]>]) => { - const action = input[0] - if (action === SocketActionTypes.CREATE_NETWORK) { - const payload = input[1] as Community - return socket.socketClient.emit(SocketActionTypes.NETWORK, { - community: payload, - network: { - hiddenService: { - onionAddress: 'onionAddress', - privateKey: 'privKey' - }, - peerId: { - id: 'peerId' - } - } - }) - } - if (action === SocketActionTypes.REGISTER_USER_CERTIFICATE) { - const payload = input[1] as RegisterUserCertificatePayload - const user = identity.selectors.currentIdentity(store.getState()) - expect(user).not.toBeUndefined() - // This community serves only as a mocked object for generating valid crytpo data (certificate, rootCA) - const communityHelper: ReturnType['payload'] = - ( - await factory.build('Community', { - id: payload.communityId - }) - ).payload - const certificateHelper = await createUserCertificateTestHelper( - { - // @ts-expect-error - nickname: user.nickname, - // @ts-expect-error - commonName: communityHelper.registrarUrl, - // @ts-expect-error - peerId: user.peerId.id, - // @ts-expect-error - dmPublicKey: user.dmKeys.publicKey + jest.spyOn(socket, 'emit').mockImplementation(async (...input: [SocketActionTypes, ...socketEventData<[any]>]) => { + const action = input[0] + if (action === SocketActionTypes.CREATE_NETWORK) { + const payload = input[1] as Community + return socket.socketClient.emit(SocketActionTypes.NETWORK, { + community: payload, + network: { + hiddenService: { + onionAddress: 'onionAddress', + privateKey: 'privKey', }, - communityHelper.CA - ) - const certificate = certificateHelper.userCert.userCertString - const rootCa = communityHelper.CA?.rootCertString - return socket.socketClient.emit(SocketActionTypes.SEND_USER_CERTIFICATE, { - communityId: payload.communityId, - payload: { - certificate: certificate, - // @ts-expect-error - rootCa: rootCa, - peers: [] - } - }) - } - if (action === SocketActionTypes.LAUNCH_COMMUNITY) { - const payload = input[1] as InitCommunityPayload - const community = communities.selectors.currentCommunity(store.getState()) - expect(payload.id).toEqual(community?.id) - socket.socketClient.emit(SocketActionTypes.COMMUNITY, { - id: payload.id - }) - socket.socketClient.emit(SocketActionTypes.CHANNELS_REPLICATED, { - channels: { - general: { - name: 'general', - description: 'string', - owner: 'owner', - timestamp: 0, - id: 'general' - } - } + peerId: { + id: 'peerId', + }, + }, + }) + } + if (action === SocketActionTypes.REGISTER_USER_CERTIFICATE) { + const payload = input[1] as RegisterUserCertificatePayload + const user = identity.selectors.currentIdentity(store.getState()) + expect(user).not.toBeUndefined() + // This community serves only as a mocked object for generating valid crytpo data (certificate, rootCA) + const communityHelper: ReturnType['payload'] = ( + await factory.build('Community', { + id: payload.communityId, }) - } - }) + ).payload + const certificateHelper = await createUserCertificateTestHelper( + { + // @ts-expect-error + nickname: user.nickname, + // @ts-expect-error + commonName: communityHelper.registrarUrl, + // @ts-expect-error + peerId: user.peerId.id, + // @ts-expect-error + dmPublicKey: user.dmKeys.publicKey, + }, + communityHelper.CA + ) + const certificate = certificateHelper.userCert.userCertString + const rootCa = communityHelper.CA?.rootCertString + return socket.socketClient.emit(SocketActionTypes.SEND_USER_CERTIFICATE, { + communityId: payload.communityId, + payload: { + certificate: certificate, + // @ts-expect-error + rootCa: rootCa, + peers: [], + }, + }) + } + if (action === SocketActionTypes.LAUNCH_COMMUNITY) { + const payload = input[1] as InitCommunityPayload + const community = communities.selectors.currentCommunity(store.getState()) + expect(payload.id).toEqual(community?.id) + socket.socketClient.emit(SocketActionTypes.COMMUNITY, { + id: payload.id, + }) + socket.socketClient.emit(SocketActionTypes.CHANNELS_REPLICATED, { + channels: { + general: { + name: 'general', + description: 'string', + owner: 'owner', + timestamp: 0, + id: 'general', + }, + }, + }) + } + }) // Log all the dispatched actions in order const actions: AnyAction[] = [] @@ -163,10 +166,7 @@ describe('User', () => { // Enter community address and hit button const joinCommunityInput = screen.getByPlaceholderText(dictionary.placeholder) const joinCommunityButton = screen.getByText(dictionary.button) - await userEvent.type( - joinCommunityInput, - '3lyn5yjwwb74he5olv43eej7knt34folvrgrfsw6vzitvkxmc5wpe4yd' - ) + await userEvent.type(joinCommunityInput, '3lyn5yjwwb74he5olv43eej7knt34folvrgrfsw6vzitvkxmc5wpe4yd') await userEvent.click(joinCommunityButton) // Confirm user is being redirected to username registration @@ -242,37 +242,35 @@ describe('User', () => { store ) - jest - .spyOn(socket, 'emit') - .mockImplementation(async (...input: [SocketActionTypes, ...socketEventData<[any]>]) => { - const action = input[0] - if (action === SocketActionTypes.CREATE_NETWORK) { - const payload = input[1] as Community - return socket.socketClient.emit(SocketActionTypes.NETWORK, { - community: payload, - network: { - hiddenService: { - onionAddress: 'onionAddress', - privateKey: 'privKey' - }, - peerId: { - id: 'peerId' - } - } - }) - } - if (action === SocketActionTypes.REGISTER_USER_CERTIFICATE) { - const payload = input[1] as RegisterUserCertificatePayload - const community = communities.selectors.currentCommunity(store.getState()) - expect(payload.communityId).toEqual(community?.id) - socket.socketClient.emit(SocketActionTypes.ERROR, { - type: SocketActionTypes.REGISTRAR, - code: ErrorCodes.FORBIDDEN, - message: ErrorMessages.USERNAME_TAKEN, - community: community?.id - }) - } - }) + jest.spyOn(socket, 'emit').mockImplementation(async (...input: [SocketActionTypes, ...socketEventData<[any]>]) => { + const action = input[0] + if (action === SocketActionTypes.CREATE_NETWORK) { + const payload = input[1] as Community + return socket.socketClient.emit(SocketActionTypes.NETWORK, { + community: payload, + network: { + hiddenService: { + onionAddress: 'onionAddress', + privateKey: 'privKey', + }, + peerId: { + id: 'peerId', + }, + }, + }) + } + if (action === SocketActionTypes.REGISTER_USER_CERTIFICATE) { + const payload = input[1] as RegisterUserCertificatePayload + const community = communities.selectors.currentCommunity(store.getState()) + expect(payload.communityId).toEqual(community?.id) + socket.socketClient.emit(SocketActionTypes.ERROR, { + type: SocketActionTypes.REGISTRAR, + code: ErrorCodes.FORBIDDEN, + message: ErrorMessages.USERNAME_TAKEN, + community: community?.id, + }) + } + }) // Log all the dispatched actions in order const actions: AnyAction[] = [] @@ -291,10 +289,7 @@ describe('User', () => { // Enter community address and hit button const joinCommunityInput = screen.getByPlaceholderText(dictionary.placeholder) const joinCommunityButton = screen.getByText(dictionary.button) - await userEvent.type( - joinCommunityInput, - '3lyn5yjwwb74he5olv43eej7knt34folvrgrfsw6vzitvkxmc5wpe4yd' - ) + await userEvent.type(joinCommunityInput, '3lyn5yjwwb74he5olv43eej7knt34folvrgrfsw6vzitvkxmc5wpe4yd') await userEvent.click(joinCommunityButton) // Confirm user is being redirected to username registration @@ -353,26 +348,24 @@ describe('User', () => { store ) - jest - .spyOn(socket, 'emit') - .mockImplementation(async (...input: [SocketActionTypes, ...socketEventData<[any]>]) => { - const action = input[0] - if (action === SocketActionTypes.CREATE_NETWORK) { - const payload = input[1] as Community - return socket.socketClient.emit(SocketActionTypes.NETWORK, { - community: payload, - network: { - hiddenService: { - onionAddress: 'onionAddress', - privateKey: 'privKey' - }, - peerId: { - id: 'peerId' - } - } - }) - } - }) + jest.spyOn(socket, 'emit').mockImplementation(async (...input: [SocketActionTypes, ...socketEventData<[any]>]) => { + const action = input[0] + if (action === SocketActionTypes.CREATE_NETWORK) { + const payload = input[1] as Community + return socket.socketClient.emit(SocketActionTypes.NETWORK, { + community: payload, + network: { + hiddenService: { + onionAddress: 'onionAddress', + privateKey: 'privKey', + }, + peerId: { + id: 'peerId', + }, + }, + }) + } + }) // Log all the dispatched actions in order const actions: AnyAction[] = [] @@ -391,10 +384,7 @@ describe('User', () => { // Enter community address and hit button const joinCommunityInput = screen.getByPlaceholderText(dictionary.placeholder) const joinCommunityButton = screen.getByText(dictionary.button) - await userEvent.type( - joinCommunityInput, - '3lyn5yjwwb74he5olv43eej7knt34folvrgrfsw6vzitvkxmc5wpe4yd' - ) + await userEvent.type(joinCommunityInput, '3lyn5yjwwb74he5olv43eej7knt34folvrgrfsw6vzitvkxmc5wpe4yd') await userEvent.click(joinCommunityButton) // Confirm user is being redirected to username registration @@ -408,7 +398,7 @@ describe('User', () => { type: SocketActionTypes.REGISTRAR, code: ErrorCodes.FORBIDDEN, message: ErrorMessages.USERNAME_TAKEN, - community: community?.id + community: community?.id, }) ) }) diff --git a/packages/desktop/src/rtl-tests/customProtocol.test.tsx b/packages/desktop/src/rtl-tests/customProtocol.test.tsx index 33d5657678..3a17bb1e2c 100644 --- a/packages/desktop/src/rtl-tests/customProtocol.test.tsx +++ b/packages/desktop/src/rtl-tests/customProtocol.test.tsx @@ -23,20 +23,20 @@ describe('Opening app through custom protocol', () => { name: '', CA: { rootCertString: '', - rootKeyString: '' + rootKeyString: '', }, rootCa: '', peerList: [], registrar: { privateKey: '', - address: '' + address: '', }, registrarUrl: 'https://bidrmzr3ee6qa2vvrlcnqvvvsk2gmjktcqkunba326parszr44gibwyd.onion', onionAddress: '', privateKey: '', port: 0, registrationAttempts: 0, - ownerCertificate: '' + ownerCertificate: '', } const _identity: Partial = { @@ -44,7 +44,7 @@ describe('Opening app through custom protocol', () => { nickname: '', userCsr: null, userCertificate: null, - joinTimestamp: 0 + joinTimestamp: 0, } beforeEach(() => { @@ -54,7 +54,7 @@ describe('Opening app through custom protocol', () => { window.ResizeObserver = jest.fn().mockImplementation(() => ({ observe: jest.fn(), unobserve: jest.fn(), - disconnect: jest.fn() + disconnect: jest.fn(), })) }) diff --git a/packages/desktop/src/rtl-tests/generalChannel.create.test.tsx b/packages/desktop/src/rtl-tests/generalChannel.create.test.tsx index 12c40dade7..cbb8a8860f 100644 --- a/packages/desktop/src/rtl-tests/generalChannel.create.test.tsx +++ b/packages/desktop/src/rtl-tests/generalChannel.create.test.tsx @@ -13,7 +13,7 @@ import { publicChannels, getFactory, SocketActionTypes, - ChannelsReplicatedPayload + ChannelsReplicatedPayload, } from '@quiet/state-manager' import Channel from '../renderer/components/Channel/Channel' import { waitFor } from '@testing-library/dom' @@ -30,7 +30,7 @@ describe('General channel', () => { window.ResizeObserver = jest.fn().mockImplementation(() => ({ observe: jest.fn(), unobserve: jest.fn(), - disconnect: jest.fn() + disconnect: jest.fn(), })) }) @@ -51,20 +51,17 @@ describe('General channel', () => { const factory = await getFactory(store) - await factory.create['payload']>( - 'Identity', - { nickname: 'alice' } - ) + await factory.create['payload']>('Identity', { + nickname: 'alice', + }) - jest - .spyOn(socket, 'emit') - .mockImplementation(async (...input: [SocketActionTypes, ...socketEventData<[any]>]) => { - const action = input[0] - if (action === SocketActionTypes.CREATE_CHANNEL) { - const payload = input[1] as ChannelsReplicatedPayload - expect(payload.channels.channel?.name).toEqual('general') - } - }) + jest.spyOn(socket, 'emit').mockImplementation(async (...input: [SocketActionTypes, ...socketEventData<[any]>]) => { + const action = input[0] + if (action === SocketActionTypes.CREATE_CHANNEL) { + const payload = input[1] as ChannelsReplicatedPayload + expect(payload.channels.channel?.name).toEqual('general') + } + }) // Log all the dispatched actions in order const actions: AnyAction[] = [] @@ -83,8 +80,8 @@ describe('General channel', () => { yield* apply(socket.socketClient, socket.socketClient.emit, [ SocketActionTypes.NEW_COMMUNITY, { - id: communityId - } + id: communityId, + }, ]) } diff --git a/packages/desktop/src/rtl-tests/loadingPanel.test.tsx b/packages/desktop/src/rtl-tests/loadingPanel.test.tsx index 0e242e9fda..e73c428759 100644 --- a/packages/desktop/src/rtl-tests/loadingPanel.test.tsx +++ b/packages/desktop/src/rtl-tests/loadingPanel.test.tsx @@ -16,7 +16,7 @@ import { publicChannels, network, LoadingPanelType, - connection + connection, } from '@quiet/state-manager' import { DateTime } from 'luxon' import { act } from 'react-dom/test-utils' @@ -40,7 +40,7 @@ describe('Loading panel', () => { window.ResizeObserver = jest.fn().mockImplementation(() => ({ observe: jest.fn(), unobserve: jest.fn(), - disconnect: jest.fn() + disconnect: jest.fn(), })) }) @@ -50,8 +50,8 @@ describe('Loading panel', () => { const { store } = await prepareStore({ [StoreKeys.Socket]: { ...new SocketState(), - isConnected: false - } + isConnected: false, + }, }) renderComponent( @@ -84,8 +84,7 @@ describe('Loading panel', () => { const factory = await getFactory(store) - const community = (await factory.build('Community')) - .payload + const community = (await factory.build('Community')).payload store.dispatch(communities.actions.addNewCommunity(community)) store.dispatch(communities.actions.setCurrentCommunity(community.id)) @@ -98,15 +97,15 @@ describe('Loading panel', () => { description: 'Welcome to #general', timestamp: DateTime.utc().valueOf(), owner: 'owner', - id: 'general' - } + id: 'general', + }, }) ).payload - await factory.create['payload']>( - 'Identity', - { id: community.id, nickname: 'alice' } - ) + await factory.create['payload']>('Identity', { + id: community.id, + nickname: 'alice', + }) store.dispatch(communities.actions.addNewCommunity(community)) store.dispatch(communities.actions.setCurrentCommunity(community.id)) @@ -142,19 +141,17 @@ describe('Loading panel', () => { const factory = await getFactory(store) - const community = (await factory.build('Community')) - .payload + const community = (await factory.build('Community')).payload store.dispatch(communities.actions.addNewCommunity(community)) store.dispatch(communities.actions.setCurrentCommunity(community.id)) - await factory.create['payload']>( - 'Identity', - { id: community.id, nickname: 'alice' } - ) + await factory.create['payload']>('Identity', { + id: community.id, + nickname: 'alice', + }) - const aliceCertificate = - store.getState().Identity.identities.entities[community.id]?.userCertificate + const aliceCertificate = store.getState().Identity.identities.entities[community.id]?.userCertificate expect(aliceCertificate).not.toBeUndefined() expect(aliceCertificate).not.toBeNull() @@ -163,7 +160,7 @@ describe('Loading panel', () => { identity.actions.storeUserCertificate({ communityId: community.id, // @ts-expect-error - userCertificate: null + userCertificate: null, }) ) @@ -184,7 +181,7 @@ describe('Loading panel', () => { identity.actions.storeUserCertificate({ communityId: community.id, // @ts-expect-error - userCertificate: aliceCertificate + userCertificate: aliceCertificate, }) ) await waitFor(() => expect(screen.queryByTestId('createUsernameModalActions')).toBeNull()) @@ -222,28 +219,20 @@ describe('Loading panel', () => { ) ) await act(async () => {}) - const bootstrapped50text = screen.getByText( - 'Tor Bootstrapped 50% (loading_descriptors)' - ) + const bootstrapped50text = screen.getByText('Tor Bootstrapped 50% (loading_descriptors)') expect(bootstrapped50text).toBeVisible() // 95% store.dispatch( - connection.actions.setTorBootstrapProcess( - 'Bootstrapped 95% (circuit_create): Establishing a Tor circuit' - ) + connection.actions.setTorBootstrapProcess('Bootstrapped 95% (circuit_create): Establishing a Tor circuit') ) await act(async () => {}) - const bootstrapped95text = screen.getByText( - 'Tor Bootstrapped 95% (circuit_create)' - ) + const bootstrapped95text = screen.getByText('Tor Bootstrapped 95% (circuit_create)') expect(bootstrapped95text).toBeVisible() // 100% store.dispatch( - connection.actions.setTorBootstrapProcess( - 'Apr 05 17:36:10.000 [notice] Bootstrapped 100% (done): Done' - ) + connection.actions.setTorBootstrapProcess('Apr 05 17:36:10.000 [notice] Bootstrapped 100% (done): Done') ) await act(async () => {}) const bootstrapped100text = screen.getByText('Tor Bootstrapped 100% (done)') diff --git a/packages/desktop/src/rtl-tests/searchModal.test.tsx b/packages/desktop/src/rtl-tests/searchModal.test.tsx index c7d70b5ef2..09ded622b7 100644 --- a/packages/desktop/src/rtl-tests/searchModal.test.tsx +++ b/packages/desktop/src/rtl-tests/searchModal.test.tsx @@ -20,7 +20,7 @@ import { Store, MessageType, ChannelMessage, - SocketActionTypes + SocketActionTypes, } from '@quiet/state-manager' import { FactoryGirl } from 'factory-girl' @@ -40,12 +40,12 @@ jest.mock('electron', () => { return [ { show: jest.fn(), - isFocused: jest.fn() - } + isFocused: jest.fn(), + }, ] - } - } - } + }, + }, + }, } }) @@ -65,7 +65,7 @@ describe('Switch channels', () => { const channelsMocks = [ channelFun, { name: 'random', timestamp: 1673854900410 }, - { name: 'test', timestamp: 1673623514097 } + { name: 'test', timestamp: 1673623514097 }, ] beforeEach(async () => { @@ -74,35 +74,30 @@ describe('Switch channels', () => { window.ResizeObserver = jest.fn().mockImplementation(() => ({ observe: jest.fn(), unobserve: jest.fn(), - disconnect: jest.fn() + disconnect: jest.fn(), })) redux = await prepareStore({}, socket) factory = await getFactory(redux.store) - community = await factory.create< - ReturnType['payload'] - >('Community') + community = await factory.create['payload']>('Community') - alice = await factory.create['payload']>( - 'Identity', - { id: community.id, nickname: 'alice' } - ) + alice = await factory.create['payload']>('Identity', { + id: community.id, + nickname: 'alice', + }) // Automatically create channels for (const channelMock of channelsMocks) { - await factory.create['payload']>( - 'PublicChannel', - { - channel: { - name: channelMock.name, - description: `Welcome to #${channelMock.name}`, - timestamp: channelMock.timestamp, - owner: alice.nickname, - id: channelMock.name - } - } - ) + await factory.create['payload']>('PublicChannel', { + channel: { + name: channelMock.name, + description: `Welcome to #${channelMock.name}`, + timestamp: channelMock.timestamp, + owner: alice.nickname, + id: channelMock.name, + }, + }) } }) @@ -198,9 +193,9 @@ describe('Switch channels', () => { createdAt: DateTime.utc().valueOf(), channelId: 'fun', signature: '', - pubKey: '' + pubKey: '', }, - verifyAutomatically: true + verifyAutomatically: true, }) ).payload.message messages.push(message) @@ -236,8 +231,8 @@ describe('Switch channels', () => { { messages: [message], communityId: community.id, - isVerified: true - } + isVerified: true, + }, ]) } }) diff --git a/packages/desktop/src/shared/sentryConfig.ts b/packages/desktop/src/shared/sentryConfig.ts index 1830560b60..f77eeb7424 100644 --- a/packages/desktop/src/shared/sentryConfig.ts +++ b/packages/desktop/src/shared/sentryConfig.ts @@ -1,4 +1,3 @@ - import * as Sentry from '@sentry/electron' export const initSentry = () => { @@ -9,7 +8,7 @@ export const initSentry = () => { // Set tracesSampleRate to 1.0 to capture 100% // of transactions for performance monitoring. // We recommend adjusting this value in production - tracesSampleRate: 1.0 + tracesSampleRate: 1.0, }) } } diff --git a/packages/desktop/src/shared/setupTests.ts b/packages/desktop/src/shared/setupTests.ts index 4360b07c20..de304c2675 100644 --- a/packages/desktop/src/shared/setupTests.ts +++ b/packages/desktop/src/shared/setupTests.ts @@ -10,7 +10,7 @@ setEngine( new CryptoEngine({ name: '', crypto: webcrypto, - subtle: webcrypto.subtle + subtle: webcrypto.subtle, }) ) // @ts-ignore @@ -20,7 +20,7 @@ global.crypto = webcrypto process._linkedBinding = name => name jest.mock('socket.io-client', () => ({ - io: jest.fn() + io: jest.fn(), })) export const ioMock = io as jest.Mock @@ -28,7 +28,7 @@ export const ioMock = io as jest.Mock jest.mock('electron-store-webpack-wrapper') jest.mock('electron', () => { - return { ipcRenderer: { on: () => { }, send: jest.fn(), sendSync: jest.fn() } } + return { ipcRenderer: { on: () => {}, send: jest.fn(), sendSync: jest.fn() } } }) jest.mock('electron-store', () => { @@ -45,11 +45,11 @@ jest.mock('@electron/remote', () => { return [ { isFocused: () => true, - show: jest.fn() - } + show: jest.fn(), + }, ] - } - } + }, + }, } // @ts-expect-error - expression of type 'unique symbol' can't be used to index type diff --git a/packages/desktop/src/shared/static.ts b/packages/desktop/src/shared/static.ts index 82468b60c1..007634973e 100644 --- a/packages/desktop/src/shared/static.ts +++ b/packages/desktop/src/shared/static.ts @@ -4,18 +4,18 @@ export const DEV_DATA_DIR = 'Quietdev' export const DATA_DIR = 'Quiet' export const actionTypes = mirrorKey({ - SET_APP_VERSION: undefined + SET_APP_VERSION: undefined, }) export const notificationFilterType = { ALL_MESSAGES: 1, MENTIONS: 2, NONE: 3, - MUTE: 4 + MUTE: 4, } export const soundType = { NONE: 0, POW: 1, BANG: 2, - SPLAT: 3 + SPLAT: 3, } diff --git a/packages/desktop/src/utils/functions/fileData.ts b/packages/desktop/src/utils/functions/fileData.ts index 08581dc713..7f1756f958 100644 --- a/packages/desktop/src/utils/functions/fileData.ts +++ b/packages/desktop/src/utils/functions/fileData.ts @@ -9,7 +9,7 @@ export const getFileData = (filePath: string): FilePreviewData => { const fileContent: FileContent = { path: filePath, name: path.basename(filePath, path.extname(filePath)), - ext: path.extname(filePath).toLowerCase() + ext: path.extname(filePath).toLowerCase(), } const id = `${Date.now()}_${Math.random().toString(36).substring(0, 20)}` return { [id]: fileContent } diff --git a/packages/desktop/src/utils/functions/splitByTex.test.ts b/packages/desktop/src/utils/functions/splitByTex.test.ts index 823a4fd832..0f33340a4b 100644 --- a/packages/desktop/src/utils/functions/splitByTex.test.ts +++ b/packages/desktop/src/utils/functions/splitByTex.test.ts @@ -2,12 +2,15 @@ import { displayMathRegex, splitByTex } from './splitByTex' describe('splitByTex', () => { it('splits math and text', () => { - const result = splitByTex(String.raw`Hello! Does in-line LaTeX work? $$\sum_{i=0}^n i = \frac{n(n+1)}{2}$$ $$R(t)= A \left(\frac{E_0}{\rho_0}\right)^{1/5}t^{2/5}$$ Text at the end`, displayMathRegex) + const result = splitByTex( + String.raw`Hello! Does in-line LaTeX work? $$\sum_{i=0}^n i = \frac{n(n+1)}{2}$$ $$R(t)= A \left(\frac{E_0}{\rho_0}\right)^{1/5}t^{2/5}$$ Text at the end`, + displayMathRegex + ) expect(result).toStrictEqual([ 'Hello! Does in-line LaTeX work?', String.raw`$$\sum_{i=0}^n i = \frac{n(n+1)}{2}$$`, String.raw`$$R(t)= A \left(\frac{E_0}{\rho_0}\right)^{1/5}t^{2/5}$$`, - 'Text at the end' + 'Text at the end', ]) }) }) diff --git a/packages/desktop/src/utils/functions/splitByTex.ts b/packages/desktop/src/utils/functions/splitByTex.ts index 710513eca7..a09086654d 100644 --- a/packages/desktop/src/utils/functions/splitByTex.ts +++ b/packages/desktop/src/utils/functions/splitByTex.ts @@ -14,5 +14,5 @@ export const splitByTex = (text: string, regex: RegExp): string[] => { matchResult = msgWIP.match(regex) } results.push(msgWIP) // Add remaining text - return results.filter((r) => r !== '') + return results.filter(r => r !== '') } diff --git a/packages/e2e-tests/src/selectors.ts b/packages/e2e-tests/src/selectors.ts index 1d1c9ec499..1b8a50de00 100644 --- a/packages/e2e-tests/src/selectors.ts +++ b/packages/e2e-tests/src/selectors.ts @@ -32,9 +32,7 @@ export class App { } get saveStateButton() { - return this.driver.wait( - until.elementLocated(By.xpath('//div[@data-testid="save-state-button"]')) - ) + return this.driver.wait(until.elementLocated(By.xpath('//div[@data-testid="save-state-button"]'))) } async saveState() { @@ -43,9 +41,7 @@ export class App { } async waitForSavedState() { - const dataSaved = this.driver.wait( - until.elementLocated(By.xpath('//div[@data-is-saved="true"]')) - ) + const dataSaved = this.driver.wait(until.elementLocated(By.xpath('//div[@data-is-saved="true"]'))) return await dataSaved } } @@ -57,9 +53,7 @@ export class StartingLoadingPanel { } get element() { - return this.driver.wait( - until.elementLocated(By.xpath('//div[@data-testid="startingPanelComponent"]')) - ) + return this.driver.wait(until.elementLocated(By.xpath('//div[@data-testid="startingPanelComponent"]'))) } // get element() { // return this.driver.wait(until.elementLocated(By.xpath(`//span[text()="${this.text}"]`))) @@ -77,15 +71,11 @@ export class WarningModal { } get titleElement() { - return this.driver.wait( - until.elementLocated(By.xpath('//h3[@data-testid="warningModalTitle"]')) - ) + return this.driver.wait(until.elementLocated(By.xpath('//h3[@data-testid="warningModalTitle"]'))) } async close() { - const submitButton = await this.driver.findElement( - By.xpath('//button[@data-testid="warningModalSubmit"]') - ) + const submitButton = await this.driver.findElement(By.xpath('//button[@data-testid="warningModalSubmit"]')) await submitButton.click() } } @@ -97,9 +87,7 @@ export class JoiningLoadingPanel { } get element() { - return this.driver.wait( - until.elementLocated(By.xpath('//div[@data-testid="joiningPanelComponent"]')) - ) + return this.driver.wait(until.elementLocated(By.xpath('//div[@data-testid="joiningPanelComponent"]'))) } } @@ -110,25 +98,23 @@ export class ChannelContextMenu { } async openMenu() { - const menu = this.driver.wait( - until.elementLocated(By.xpath('//div[@data-testid="channelContextMenuButton"]')) - ) + const menu = this.driver.wait(until.elementLocated(By.xpath('//div[@data-testid="channelContextMenuButton"]'))) await menu.click() } async openDeletionChannelModal() { - const tab = this.driver.wait( - until.elementLocated(By.xpath('//div[@data-testid="contextMenuItemDelete"]')) - ) + const tab = this.driver.wait(until.elementLocated(By.xpath('//div[@data-testid="contextMenuItemDelete"]'))) await tab.click() } async deleteChannel() { - const button = this.driver.wait( - until.elementLocated(By.xpath('//button[@data-testid="deleteChannelButton"]')) - ) + const button = this.driver.wait(until.elementLocated(By.xpath('//button[@data-testid="deleteChannelButton"]'))) await button.click() - await new Promise(resolve => setTimeout(() => { resolve(); }, 5000)) + await new Promise(resolve => + setTimeout(() => { + resolve() + }, 5000) + ) } } export class RegisterUsernameModal { @@ -167,16 +153,12 @@ export class JoinCommunityModal { } async typeCommunityCode(code: string) { - const communityNameInput = await this.driver.findElement( - By.xpath('//input[@placeholder="Invite code"]') - ) + const communityNameInput = await this.driver.findElement(By.xpath('//input[@placeholder="Invite code"]')) await communityNameInput.sendKeys(code) } async submit() { - const continueButton = await this.driver.findElement( - By.xpath('//button[@data-testid="continue-joinCommunity"]') - ) + const continueButton = await this.driver.findElement(By.xpath('//button[@data-testid="continue-joinCommunity"]')) await continueButton.click() } } @@ -191,16 +173,12 @@ export class CreateCommunityModal { } async typeCommunityName(name: string) { - const communityNameInput = await this.driver.findElement( - By.xpath('//input[@placeholder="Community name"]') - ) + const communityNameInput = await this.driver.findElement(By.xpath('//input[@placeholder="Community name"]')) await communityNameInput.sendKeys(name) } async submit() { - const continueButton = await this.driver.findElement( - By.xpath('//button[@data-testid="continue-createCommunity"]') - ) + const continueButton = await this.driver.findElement(By.xpath('//button[@data-testid="continue-createCommunity"]')) await continueButton.click() } } @@ -231,9 +209,7 @@ export class Channel { } get getAllMessages() { - return this.driver.wait( - until.elementsLocated(By.xpath('//*[contains(@data-testid, "userMessages-")]')) - ) + return this.driver.wait(until.elementsLocated(By.xpath('//*[contains(@data-testid, "userMessages-")]'))) } get element() { @@ -248,7 +224,11 @@ export class Channel { const communityNameInput = await this.messageInput await communityNameInput.sendKeys(message) await communityNameInput.sendKeys(Key.ENTER) - await new Promise(resolve => setTimeout(() => { resolve(); }, 5000)) + await new Promise(resolve => + setTimeout(() => { + resolve() + }, 5000) + ) } async getUserMessages(username: string) { @@ -258,9 +238,7 @@ export class Channel { } async getMessage(text: string) { - return await this.driver.wait( - until.elementLocated(By.xpath(`//span[contains(text(),"${text}")]`)) - ) + return await this.driver.wait(until.elementLocated(By.xpath(`//span[contains(text(),"${text}")]`))) } } export class Sidebar { @@ -270,37 +248,27 @@ export class Sidebar { } async getChannelList() { - const channels = await this.driver.findElements( - By.xpath('//*[contains(@data-testid, "link-text")]') - ) + const channels = await this.driver.findElements(By.xpath('//*[contains(@data-testid, "link-text")]')) return channels } async openSettings() { - const button = await this.driver.findElement( - By.xpath('//span[@data-testid="settings-panel-button"]') - ) + const button = await this.driver.findElement(By.xpath('//span[@data-testid="settings-panel-button"]')) await button.click() return new Settings(this.driver) } async switchChannel(name: string) { - const channelLink = await this.driver.wait( - until.elementLocated(By.xpath(`//div[@data-testid="${name}-link"]`)) - ) + const channelLink = await this.driver.wait(until.elementLocated(By.xpath(`//div[@data-testid="${name}-link"]`))) await channelLink.click() } async addNewChannel(name: string) { - const button = await this.driver.findElement( - By.xpath('//button[@data-testid="addChannelButton"]') - ) + const button = await this.driver.findElement(By.xpath('//button[@data-testid="addChannelButton"]')) await button.click() const channelNameInput = await this.driver.findElement(By.xpath('//input[@name="channelName"]')) await channelNameInput.sendKeys(name) - const channelNameButton = await this.driver.findElement( - By.xpath('//button[@data-testid="channelNameSubmit"]') - ) + const channelNameButton = await this.driver.findElement(By.xpath('//button[@data-testid="channelNameSubmit"]')) await channelNameButton.click() return new Channel(this.driver, name) } @@ -316,30 +284,22 @@ export class Settings { } async openLeaveCommunityModal() { - const tab = await this.driver.wait( - until.elementLocated(By.xpath('//p[@data-testid="leave-community-tab"]')) - ) + const tab = await this.driver.wait(until.elementLocated(By.xpath('//p[@data-testid="leave-community-tab"]'))) await tab.click() } async leaveCommunityButton() { - const button = await this.driver.wait( - until.elementLocated(By.xpath('//button[text()="Leave community"]')) - ) + const button = await this.driver.wait(until.elementLocated(By.xpath('//button[text()="Leave community"]'))) await button.click() } async switchTab(name: string) { - const tab = await this.driver.findElement( - By.xpath(`//button[@data-testid='${name}-settings-tab']`) - ) + const tab = await this.driver.findElement(By.xpath(`//button[@data-testid='${name}-settings-tab']`)) await tab.click() } async invitationCode() { - const unlockButton = await this.driver.findElement( - By.xpath('//button[@data-testid="show-invitation-link"]') - ) + const unlockButton = await this.driver.findElement(By.xpath('//button[@data-testid="show-invitation-link"]')) await unlockButton.click() @@ -361,9 +321,7 @@ export class DebugModeModal { } get element() { - return this.driver.wait( - until.elementLocated(By.xpath("//h3[text()='App is running in debug mode']")) - ) + return this.driver.wait(until.elementLocated(By.xpath("//h3[text()='App is running in debug mode']"))) } get button() { diff --git a/packages/e2e-tests/src/tests/invitationLink.test.ts b/packages/e2e-tests/src/tests/invitationLink.test.ts index da179a17ce..799e944a4e 100644 --- a/packages/e2e-tests/src/tests/invitationLink.test.ts +++ b/packages/e2e-tests/src/tests/invitationLink.test.ts @@ -8,7 +8,7 @@ import { App, Sidebar, StartingLoadingPanel, - WarningModal + WarningModal, } from '../selectors' import { capitalizeFirstLetter, invitationDeepUrl } from '@quiet/common' import { execSync } from 'child_process' @@ -147,14 +147,10 @@ describe('New user joins using invitation link while having app opened', () => { const command = { linux: 'xdg-open', darwin: 'open', - win32: 'start' + win32: 'start', } - execSync( - `${command[process.platform as SupportedPlatformDesktop]} ${invitationDeepUrl( - url.hash.substring(1) - )}` - ) + execSync(`${command[process.platform as SupportedPlatformDesktop]} ${invitationDeepUrl(url.hash.substring(1))}`) console.log('Guest opened invitation link') }) diff --git a/packages/e2e-tests/src/tests/oneClient.test.ts b/packages/e2e-tests/src/tests/oneClient.test.ts index 312bfa09f8..3dc0c0fcc7 100644 --- a/packages/e2e-tests/src/tests/oneClient.test.ts +++ b/packages/e2e-tests/src/tests/oneClient.test.ts @@ -6,7 +6,7 @@ import { JoinCommunityModal, JoiningLoadingPanel, RegisterUsernameModal, - StartingLoadingPanel + StartingLoadingPanel, } from '../selectors' import getPort from 'get-port' import { fork } from 'child_process' @@ -105,7 +105,7 @@ describe('One Client', () => { '-r', `${resourcesPath}`, '-p', - 'desktop' + 'desktop', ] const backendBundlePath = path.normalize(require.resolve('backend-bundle')) console.log('Spawning backend', backendBundlePath, 'with argvs:', forkArgvs) diff --git a/packages/e2e-tests/src/tests/twoClients.test.ts b/packages/e2e-tests/src/tests/twoClients.test.ts index 651c888ce5..45f9ff5e44 100644 --- a/packages/e2e-tests/src/tests/twoClients.test.ts +++ b/packages/e2e-tests/src/tests/twoClients.test.ts @@ -8,7 +8,7 @@ import { JoiningLoadingPanel, RegisterUsernameModal, Sidebar, - StartingLoadingPanel + StartingLoadingPanel, } from '../selectors' import logger from '../logger' const log = logger('Two Clients:') @@ -165,7 +165,11 @@ describe('Two Clients', () => { const isMessageInput2 = await generalChannel2.messageInput.isDisplayed() expect(isMessageInput2).toBeTruthy() console.log('FETCHING CHANNEL MESSAGES!') - await new Promise(resolve => setTimeout(() => { resolve(); }, 5000)) + await new Promise(resolve => + setTimeout(() => { + resolve() + }, 5000) + ) await generalChannel2.sendMessage(joiningUserMessages[0]) }) it('Sent message is visible in a channel', async () => { @@ -192,7 +196,11 @@ describe('Two Clients', () => { sidebar2 = new Sidebar(guestApp.driver) await sidebar2.switchChannel(newChannelName) secondChannel2 = new Channel(guestApp.driver, newChannelName) - await new Promise(resolve => setTimeout(() => { resolve(); }, 2000)) + await new Promise(resolve => + setTimeout(() => { + resolve() + }, 2000) + ) const messages = await secondChannel2.getUserMessages(ownerUsername) const text = await messages[1].getText() expect(text).toEqual(ownerMessages[1]) @@ -215,7 +223,11 @@ describe('Two Clients', () => { await sidebar2.switchChannel(newChannelName) const messages = await secondChannel2.getUserMessages(joiningUserUsername) expect(messages.length).toEqual(1) - await new Promise(resolve => setTimeout(() => { resolve(); }, 2000)) + await new Promise(resolve => + setTimeout(() => { + resolve() + }, 2000) + ) const channels = await sidebar.getChannelList() expect(channels.length).toEqual(2) }) @@ -235,7 +247,11 @@ describe('Two Clients', () => { } // Delete general channel while guest is absent it('Channel deletion - Owner recreate general channel', async () => { - await new Promise(resolve => setTimeout(() => { resolve(); }, 2000)) + await new Promise(resolve => + setTimeout(() => { + resolve() + }, 2000) + ) const isGeneralChannel = await generalChannel.messageInput.isDisplayed() expect(isGeneralChannel).toBeTruthy() await channelContextMenu.openMenu() @@ -264,7 +280,11 @@ describe('Two Clients', () => { 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)) + 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() @@ -278,7 +298,11 @@ describe('Two Clients', () => { await generalChannel2.element.isDisplayed() const isMessageInput2 = await generalChannel2.messageInput.isDisplayed() expect(isMessageInput2).toBeTruthy() - await new Promise(resolve => setTimeout(() => { resolve(); }, 5000)) + await new Promise(resolve => + setTimeout(() => { + resolve() + }, 5000) + ) await generalChannel2.sendMessage(joiningUserMessages[1]) }) it('Leave community - Sent message is visible in a channel', async () => { diff --git a/packages/e2e-tests/src/utils.ts b/packages/e2e-tests/src/utils.ts index 562637abc9..5b541e3f92 100644 --- a/packages/e2e-tests/src/utils.ts +++ b/packages/e2e-tests/src/utils.ts @@ -53,23 +53,27 @@ export class BuildSetup { await this.initPorts() const env = { DATA_DIR: this.dataDir || 'Quiet', - DEBUG: 'backend*' + DEBUG: 'backend*', } if (process.platform === 'win32') { console.log('!WINDOWS!') this.child = spawn(`cd node_modules/.bin & chromedriver.cmd --port=${this.port}`, [], { shell: true, - env: Object.assign(process.env, env) + env: Object.assign(process.env, env), }) } else { this.child = spawn(`node_modules/.bin/chromedriver --port=${this.port}`, [], { shell: true, detached: false, - env: Object.assign(process.env, env) + env: Object.assign(process.env, env), }) } // Extra time for chromedriver to setup - await new Promise(resolve => setTimeout(() => { resolve(); }, 2000)) + await new Promise(resolve => + setTimeout(() => { + resolve() + }, 2000) + ) const killNine = () => { exec(`kill -9 $(lsof -t -i:${this.port})`) @@ -91,8 +95,12 @@ export class BuildSetup { killNine() }) - this.child.on('message', data => { console.log('message', data); }) - this.child.on('error', data => { console.log('error', data); }) + this.child.on('message', data => { + console.log('message', data) + }) + this.child.on('error', data => { + console.log('error', data) + }) this.child.stdout.on('data', data => { console.log(`stdout:\n${data}`) @@ -132,8 +140,8 @@ export class BuildSetup { .withCapabilities({ 'goog:chromeOptions': { binary, - args: [`--remote-debugging-port=${this.debugPort}`, '--enable-logging'] - } + args: [`--remote-debugging-port=${this.debugPort}`, '--enable-logging'], + }, }) .forBrowser(Browser.CHROME) .build() @@ -155,7 +163,11 @@ export class BuildSetup { public async killChromeDriver() { console.log(`Killing driver (DATA_DIR=${this.dataDir})`) this.child?.kill() - await new Promise(resolve => setTimeout(() => { resolve(); }, 2000)) + await new Promise(resolve => + setTimeout(() => { + resolve() + }, 2000) + ) } public async closeDriver() { @@ -164,8 +176,8 @@ export class BuildSetup { } public getProcessData = () => { - let dataDirPath: string = '' - let resourcesPath: string = '' + let dataDirPath = '' + let resourcesPath = '' const backendBundlePath = path.normalize('backend-bundle/bundle.cjs') const byPlatform = { linux: `pgrep -af "${backendBundlePath}" | grep -v egrep | grep "${this.dataDir}"`, @@ -175,7 +187,7 @@ export class BuildSetup { '\\\\' )}%' and commandline LIKE '%${ this.dataDir - }%' and name = 'Quiet.exe'} | Format-Table CommandLine -HideTableHeaders -Wrap -Autosize"` + }%' and name = 'Quiet.exe'} | Format-Table CommandLine -HideTableHeaders -Wrap -Autosize"`, } const command = byPlatform[process.platform as SupportedPlatformDesktop] @@ -199,7 +211,7 @@ export class BuildSetup { console.log('Extracted dataDirPath:', dataDirPath, 'resourcesPath:', resourcesPath) return { dataDirPath, - resourcesPath + resourcesPath, } } } diff --git a/packages/eslint-config-custom/.prettierrc.js b/packages/eslint-config-custom/.prettierrc.js deleted file mode 100644 index 414a7059c8..0000000000 --- a/packages/eslint-config-custom/.prettierrc.js +++ /dev/null @@ -1,10 +0,0 @@ -const modifiedConfig = { - singleQuote: true, - jsxSingleQuote: true, - spaceBeforeFunctionParen: true, - printWidth: 100, - parser: 'typescript', - semi: false -} - -module.exports = modifiedConfig diff --git a/packages/eslint-config-custom/index.js b/packages/eslint-config-custom/index.js index ecf9bf26da..c9850b47a9 100644 --- a/packages/eslint-config-custom/index.js +++ b/packages/eslint-config-custom/index.js @@ -1,6 +1,10 @@ module.exports = { env: { - "jest/globals": true + "jest/globals": true, + "node": true, + "commonjs": true, + "browser": true, + "es6": true }, extends: [ 'eslint:recommended', @@ -21,6 +25,8 @@ module.exports = { 'no-irregular-whitespace': 'off', 'array-callback-return': 'off', 'comma-dangle': 'off', + "no-empty-function": "off", + "@typescript-eslint/no-empty-function": "off", '@typescript-eslint/naming-convention': 'off', '@typescript-eslint/no-var-requires': 'off', '@typescript-eslint/indent': 'off', @@ -66,10 +72,8 @@ module.exports = { // "error", { // semi: false, // singleQuote: true - // }, - // { - // "usePrettierrc": false - // }], + // } + // ], }, overrides: [ { diff --git a/packages/eslint-config-custom/new.json b/packages/eslint-config-custom/new.json new file mode 100644 index 0000000000..bc8a5c6a67 --- /dev/null +++ b/packages/eslint-config-custom/new.json @@ -0,0 +1,13 @@ +{ + "arrowParens": "always", + "bracketSpacing": true, + "jsxBracketSameLine": false, + "jsxSingleQuote": true, + "quoteProps": "as-needed", + "singleQuote": true, + "semi": false, + "printWidth": 140, + "useTabs": false, + "tabWidth": 2, + "trailingComma": "es5" + } \ No newline at end of file diff --git a/packages/identity/src/common.ts b/packages/identity/src/common.ts index 06e4fe0894..ab64be1285 100644 --- a/packages/identity/src/common.ts +++ b/packages/identity/src/common.ts @@ -1,6 +1,14 @@ import { NoCryptoEngineError } from '@quiet/types' import { fromBER, type ObjectIdentifier } from 'asn1js' -import { getAlgorithmParameters, getCrypto, CertificationRequest, Certificate, TSTInfo, ECNamedCurves, type AttributeTypeAndValue } from 'pkijs' +import { + getAlgorithmParameters, + getCrypto, + CertificationRequest, + Certificate, + TSTInfo, + ECNamedCurves, + type AttributeTypeAndValue, +} from 'pkijs' import { stringToArrayBuffer, fromBase64 } from 'pvutils' export enum CertFieldsTypes { @@ -8,13 +16,13 @@ export enum CertFieldsTypes { subjectAltName = '2.5.29.17', nickName = '1.3.6.1.4.1.50715.2.1', peerId = '1.3.6.1.2.1.15.3.1.1', - dmPublicKey = '1.2.840.113549.1.9.12' + dmPublicKey = '1.2.840.113549.1.9.12', } export enum ExtensionsTypes { basicConstr = '2.5.29.19', keyUsage = '2.5.29.15', - extKeyUsage = '2.5.29.37' + extKeyUsage = '2.5.29.37', } export function hexStringToArrayBuffer(str: string): ArrayBuffer { @@ -48,11 +56,7 @@ export const generateKeyPair = async ({ signAlg }: { signAlg: string }): Promise const crypto = getCrypto() if (!crypto) throw new NoCryptoEngineError() - const keyPair = await crypto.generateKey( - algorithm.algorithm as EcKeyGenParams, - true, - algorithm.usages - ) + const keyPair = await crypto.generateKey(algorithm.algorithm as EcKeyGenParams, true, algorithm.usages) return keyPair } @@ -82,13 +86,7 @@ export const loadPrivateKey = async (rootKey: string, signAlg: string): Promise< const crypto = getCrypto() if (!crypto) throw new NoCryptoEngineError() - return await crypto.importKey( - 'pkcs8', - keyBuffer, - (algorithm.algorithm as Algorithm), - true, - algorithm.usages - ) + return await crypto.importKey('pkcs8', keyBuffer, algorithm.algorithm as Algorithm, true, algorithm.usages) } export const loadCSR = async (csr: string): Promise => { @@ -97,10 +95,7 @@ export const loadCSR = async (csr: string): Promise => { return new CertificationRequest({ schema: asn1.result }) } -export const getCertFieldValue = ( - cert: Certificate, - fieldType: CertFieldsTypes | ObjectIdentifier -): string | null => { +export const getCertFieldValue = (cert: Certificate, fieldType: CertFieldsTypes | ObjectIdentifier): string | null => { if (fieldType === CertFieldsTypes.commonName) { const block = cert.subject.typesAndValues.find((tav: AttributeTypeAndValue) => tav.type === fieldType) diff --git a/packages/identity/src/config.ts b/packages/identity/src/config.ts index fdd0a95e26..bdadc5a5cb 100644 --- a/packages/identity/src/config.ts +++ b/packages/identity/src/config.ts @@ -1,4 +1,4 @@ export default { signAlg: 'ECDSA', - hashAlg: 'sha-256' + hashAlg: 'sha-256', } diff --git a/packages/identity/src/extractPubKey.ts b/packages/identity/src/extractPubKey.ts index 5c448e9b5b..e7615df59f 100644 --- a/packages/identity/src/extractPubKey.ts +++ b/packages/identity/src/extractPubKey.ts @@ -19,28 +19,18 @@ export const parseCertificationRequest = (pem: string): CertificationRequest => } export const keyFromCertificate = (certificate: Certificate | CertificationRequest): string => { - return Buffer.from( - certificate.subjectPublicKeyInfo.subjectPublicKey.valueBlock.valueHex - ).toString('base64') + return Buffer.from(certificate.subjectPublicKeyInfo.subjectPublicKey.valueBlock.valueHex).toString('base64') } -export const keyObjectFromString = async ( - pubKeyString: string, - crypto: SubtleCrypto | null -): Promise => { +export const keyObjectFromString = async (pubKeyString: string, crypto: SubtleCrypto | null): Promise => { if (!crypto) throw new NoCryptoEngineError() let keyArray = new ArrayBuffer(0) keyArray = stringToArrayBuffer(fromBase64(pubKeyString)) const algorithm = getAlgorithmParameters(config.signAlg, 'generateKey') - return await crypto.importKey('raw', keyArray, (algorithm.algorithm as Algorithm), true, [ - 'verify' - ]) + return await crypto.importKey('raw', keyArray, algorithm.algorithm as Algorithm, true, ['verify']) } -export const extractPubKey = async ( - pem: string, - crypto: SubtleCrypto | null -): Promise => { +export const extractPubKey = async (pem: string, crypto: SubtleCrypto | null): Promise => { const pubKeyString = extractPubKeyString(pem) return await keyObjectFromString(pubKeyString, crypto) } diff --git a/packages/identity/src/generateRootCA.ts b/packages/identity/src/generateRootCA.ts index d0d5c78bb7..bce1cbdd00 100644 --- a/packages/identity/src/generateRootCA.ts +++ b/packages/identity/src/generateRootCA.ts @@ -9,7 +9,7 @@ import { ExtKeyUsage, Extension, AttributeTypeAndValue, - getCrypto + getCrypto, } from 'pkijs' import { NoCryptoEngineError } from '@quiet/types' @@ -37,18 +37,18 @@ export const createRootCA = async ( commonName, ...config, notBeforeDate, - notAfterDate + notAfterDate, }) const rootData = { rootCert: rootCA.certificate.toSchema(true).toBER(false), - rootKey: await crypto.exportKey('pkcs8', rootCA.privateKey) + rootKey: await crypto.exportKey('pkcs8', rootCA.privateKey), } return { rootObject: rootCA, rootCertString: Buffer.from(rootData.rootCert).toString('base64'), - rootKeyString: Buffer.from(rootData.rootKey).toString('base64') + rootKeyString: Buffer.from(rootData.rootKey).toString('base64'), } } @@ -57,7 +57,7 @@ async function generateRootCA({ signAlg = config.signAlg, hashAlg = config.hashAlg, notBeforeDate, - notAfterDate + notAfterDate, }: { commonName: string signAlg: string @@ -70,8 +70,8 @@ async function generateRootCA({ const extKeyUsage = new ExtKeyUsage({ keyPurposes: [ '1.3.6.1.5.5.7.3.2', // id-kp-clientAuth - '1.3.6.1.5.5.7.3.1' // id-kp-serverAuth - ] + '1.3.6.1.5.5.7.3.1', // id-kp-serverAuth + ], }) const certificate = new Certificate({ serialNumber: new Integer({ value: 1 }), @@ -81,34 +81,34 @@ async function generateRootCA({ extnID: ExtensionsTypes.basicConstr, critical: false, extnValue: basicConstr.toSchema().toBER(false), - parsedValue: basicConstr // Parsed value for well-known extensions + parsedValue: basicConstr, // Parsed value for well-known extensions }), new Extension({ extnID: ExtensionsTypes.keyUsage, critical: false, extnValue: keyUsage.toBER(false), - parsedValue: keyUsage // Parsed value for well-known extensions + parsedValue: keyUsage, // Parsed value for well-known extensions }), new Extension({ extnID: ExtensionsTypes.extKeyUsage, critical: false, extnValue: extKeyUsage.toSchema().toBER(false), - parsedValue: extKeyUsage // Parsed value for well-known extensions - }) + parsedValue: extKeyUsage, // Parsed value for well-known extensions + }), ], notBefore: notBeforeDate, - notAfter: notAfterDate + notAfter: notAfterDate, }) certificate.issuer.typesAndValues.push( new AttributeTypeAndValue({ type: CertFieldsTypes.commonName, - value: new PrintableString({ value: commonName }) + value: new PrintableString({ value: commonName }), }) ) certificate.subject.typesAndValues.push( new AttributeTypeAndValue({ type: CertFieldsTypes.commonName, - value: new PrintableString({ value: commonName }) + value: new PrintableString({ value: commonName }), }) ) const keyPair = await generateKeyPair({ signAlg }) diff --git a/packages/identity/src/generateUserCertificate.ts b/packages/identity/src/generateUserCertificate.ts index c81b30c313..a9e8034cb3 100644 --- a/packages/identity/src/generateUserCertificate.ts +++ b/packages/identity/src/generateUserCertificate.ts @@ -3,7 +3,14 @@ import { Integer, BitString, OctetString, PrintableString } from 'asn1js' import config from './config' import { loadCertificate, loadPrivateKey, loadCSR, ExtensionsTypes, CertFieldsTypes } from './common' import { - Certificate, Extension, ExtKeyUsage, BasicConstraints, type CertificationRequest, GeneralName, GeneralNames, type Attribute + Certificate, + Extension, + ExtKeyUsage, + BasicConstraints, + type CertificationRequest, + GeneralName, + GeneralNames, + type Attribute, } from 'pkijs' export interface UserCert { @@ -28,13 +35,13 @@ export const createUserCert = async ( pkcs10: await loadCSR(userCsr), hashAlg, notBeforeDate, - notAfterDate + notAfterDate, }) const userCert = userCertificate.certificate.toSchema(true).toBER(false) return { userCertObject: userCertificate, - userCertString: Buffer.from(userCert).toString('base64') + userCertString: Buffer.from(userCert).toString('base64'), } } @@ -44,7 +51,7 @@ async function generateuserCertificate({ pkcs10, hashAlg = config.hashAlg, notBeforeDate, - notAfterDate + notAfterDate, }: { issuerCert: Certificate issuerKey: CryptoKey @@ -58,8 +65,8 @@ async function generateuserCertificate({ const extKeyUsage = new ExtKeyUsage({ keyPurposes: [ '1.3.6.1.5.5.7.3.2', // id-kp-clientAuth - '1.3.6.1.5.5.7.3.1' // id-kp-serverAuth - ] + '1.3.6.1.5.5.7.3.1', // id-kp-serverAuth + ], }) const attr: Attribute[] | undefined = pkcs10.attributes let dmPubKey = null @@ -77,9 +84,9 @@ async function generateuserCertificate({ names: [ new GeneralName({ type: 2, // dNSName - value: `${onionAddress}` - }) - ] + value: `${onionAddress}`, + }), + ], }) } catch (err) { throw new Error('Cannot get certificate request extension') @@ -93,44 +100,44 @@ async function generateuserCertificate({ extnID: ExtensionsTypes.basicConstr, critical: false, extnValue: basicConstr.toSchema().toBER(false), - parsedValue: basicConstr // Parsed value for well-known extensions + parsedValue: basicConstr, // Parsed value for well-known extensions }), new Extension({ extnID: ExtensionsTypes.keyUsage, critical: false, extnValue: keyUsage.toBER(false), - parsedValue: keyUsage // Parsed value for well-known extensions + parsedValue: keyUsage, // Parsed value for well-known extensions }), new Extension({ extnID: ExtensionsTypes.extKeyUsage, critical: false, extnValue: extKeyUsage.toSchema().toBER(false), - parsedValue: extKeyUsage // Parsed value for well-known extensions + parsedValue: extKeyUsage, // Parsed value for well-known extensions }), new Extension({ extnID: CertFieldsTypes.dmPublicKey, critical: false, - extnValue: new OctetString({ valueHex: dmPubKey }).toBER(false) + extnValue: new OctetString({ valueHex: dmPubKey }).toBER(false), }), new Extension({ extnID: CertFieldsTypes.nickName, critical: false, - extnValue: new PrintableString({ value: nickname }).toBER(false) + extnValue: new PrintableString({ value: nickname }).toBER(false), }), new Extension({ extnID: CertFieldsTypes.peerId, critical: false, - extnValue: new PrintableString({ value: peerId }).toBER(false) + extnValue: new PrintableString({ value: peerId }).toBER(false), }), new Extension({ extnID: CertFieldsTypes.subjectAltName, critical: false, - extnValue: altNames.toSchema().toBER(false) - }) + extnValue: altNames.toSchema().toBER(false), + }), ], issuer: issuerCert.subject, subject: pkcs10.subject, - subjectPublicKeyInfo: pkcs10.subjectPublicKeyInfo + subjectPublicKeyInfo: pkcs10.subjectPublicKeyInfo, }) certificate.notBefore.value = notBeforeDate certificate.notAfter.value = notAfterDate diff --git a/packages/identity/src/index.ts b/packages/identity/src/index.ts index c610615b29..0e2139b2fb 100644 --- a/packages/identity/src/index.ts +++ b/packages/identity/src/index.ts @@ -4,7 +4,7 @@ import { parseCertificate, keyFromCertificate, keyObjectFromString, - extractPubKeyString + extractPubKeyString, } from './extractPubKey' import { verifyUserCert } from './verifyUserCertificate' import { verifySignature } from './verification' @@ -21,7 +21,7 @@ import { CertFieldsTypes, hexStringToArrayBuffer, arrayBufferToHexString, - getReqFieldValue + getReqFieldValue, } from './common' import configCrypto from './config' import { @@ -31,7 +31,7 @@ import { createTestUserCsr, userData, createRootCertificateTestHelper, - createUserCertificateTestHelper + createUserCertificateTestHelper, } from './test/helpers' export { createRootCA } @@ -51,9 +51,16 @@ export { getCertFieldValue, CertFieldsTypes, hexStringToArrayBuffer, - arrayBufferToHexString + arrayBufferToHexString, } export { configCrypto } export { setupCrypto } -export { createTestRootCA, createTestUserCert, createTestUserCsr, userData, createRootCertificateTestHelper, createUserCertificateTestHelper } +export { + createTestRootCA, + createTestUserCert, + createTestUserCsr, + userData, + createRootCertificateTestHelper, + createUserCertificateTestHelper, +} export { getReqFieldValue } diff --git a/packages/identity/src/requestCertificate.ts b/packages/identity/src/requestCertificate.ts index 2bb585e245..bd991e697e 100644 --- a/packages/identity/src/requestCertificate.ts +++ b/packages/identity/src/requestCertificate.ts @@ -2,14 +2,7 @@ import { PrintableString, OctetString } from 'asn1js' import { NoCryptoEngineError } from '@quiet/types' import config from './config' import { generateKeyPair, CertFieldsTypes, hexStringToArrayBuffer } from './common' -import { - getCrypto, - CertificationRequest, - AttributeTypeAndValue, - Attribute, - Extensions, - Extension -} from 'pkijs' +import { getCrypto, CertificationRequest, AttributeTypeAndValue, Attribute, Extensions, Extension } from 'pkijs' interface CertData { publicKey: CryptoKey @@ -27,7 +20,7 @@ export const createUserCsr = async ({ nickname, commonName, peerId, - dmPublicKey + dmPublicKey, }: { nickname: string commonName: string @@ -41,20 +34,20 @@ export const createUserCsr = async ({ commonName, peerId, dmPublicKey, - ...config + ...config, }) const crypto = getCrypto() if (!crypto) throw new NoCryptoEngineError() const userData = { userCsr: pkcs10.pkcs10.toSchema().toBER(false), - userKey: await crypto.exportKey('pkcs8', pkcs10.privateKey) + userKey: await crypto.exportKey('pkcs8', pkcs10.privateKey), } return { userCsr: Buffer.from(userData.userCsr).toString('base64'), userKey: Buffer.from(userData.userKey).toString('base64'), - pkcs10 + pkcs10, } } @@ -64,7 +57,7 @@ async function requestCertificate({ peerId, dmPublicKey, signAlg = config.signAlg, - hashAlg = config.hashAlg + hashAlg = config.hashAlg, }: { nickname: string commonName: string @@ -79,12 +72,12 @@ async function requestCertificate({ const pkcs10 = new CertificationRequest({ version: 0, - attributes: [] + attributes: [], }) pkcs10.subject.typesAndValues.push( new AttributeTypeAndValue({ type: CertFieldsTypes.commonName, - value: new PrintableString({ value: commonName }) + value: new PrintableString({ value: commonName }), }) ) @@ -105,27 +98,27 @@ async function requestCertificate({ new Extension({ extnID: '2.5.29.14', critical: false, - extnValue: new OctetString({ valueHex: hashedPublicKey }).toBER(false) - }) - ] - }).toSchema() - ] + extnValue: new OctetString({ valueHex: hashedPublicKey }).toBER(false), + }), + ], + }).toSchema(), + ], }), new Attribute({ type: CertFieldsTypes.dmPublicKey, - values: [new OctetString({ valueHex: arrayBufferDmPubKey })] + values: [new OctetString({ valueHex: arrayBufferDmPubKey })], }), new Attribute({ type: CertFieldsTypes.nickName, - values: [new PrintableString({ value: nickname })] + values: [new PrintableString({ value: nickname })], }), new Attribute({ type: CertFieldsTypes.peerId, - values: [new PrintableString({ value: peerId })] + values: [new PrintableString({ value: peerId })], }), new Attribute({ type: CertFieldsTypes.subjectAltName, - values: [new PrintableString({ value: commonName })] + values: [new PrintableString({ value: commonName })], }) ) await pkcs10.sign(keyPair.privateKey, hashAlg) diff --git a/packages/identity/src/sign.ts b/packages/identity/src/sign.ts index d42d711bd1..0e4939d619 100644 --- a/packages/identity/src/sign.ts +++ b/packages/identity/src/sign.ts @@ -9,5 +9,5 @@ export const sign = async (message: string, privKey: CryptoKey): Promise { - return await createRootCA(new Time({ type: 1, value: notBeforeDate }), new Time({ type: 1, value: notAfterDate }), commonName) +export async function createTestRootCA(commonName?: string): Promise { + return await createRootCA( + new Time({ type: 1, value: notBeforeDate }), + new Time({ type: 1, value: notAfterDate }), + commonName + ) } -export async function createTestUserCsr (): Promise { +export async function createTestUserCsr(): Promise { return await createUserCsr(userData) } -export async function createTestUserCert (rootCert?: RootCA, userCsr?: UserCsr): Promise { - const rootC = rootCert || await createTestRootCA() - const user = userCsr || await createTestUserCsr() +export async function createTestUserCert(rootCert?: RootCA, userCsr?: UserCsr): Promise { + const rootC = rootCert || (await createTestRootCA()) + const user = userCsr || (await createTestUserCsr()) return await createUserCert(rootC.rootCertString, rootC.rootKeyString, user.userCsr, notBeforeDate, notAfterDate) } -export function setupCrypto () { +export function setupCrypto() { const webcrypto = new Crypto() setEngine( 'newEngine', @@ -39,7 +43,7 @@ export function setupCrypto () { new CryptoEngine({ name: '', crypto: webcrypto, - subtle: webcrypto.subtle + subtle: webcrypto.subtle, }) ) global.crypto = webcrypto @@ -71,7 +75,7 @@ export const createUserCertificateTestHelper = async ( peerId: user.peerId, dmPublicKey: user.dmPublicKey, signAlg: config.signAlg, - hashAlg: config.hashAlg + hashAlg: config.hashAlg, }) const userCert = await createUserCert( rootCA.rootCertString, @@ -82,6 +86,6 @@ export const createUserCertificateTestHelper = async ( ) return { userCsr, - userCert + userCert, } } diff --git a/packages/identity/src/test/index.test.ts b/packages/identity/src/test/index.test.ts index f6da0a0e9a..8332c728ce 100644 --- a/packages/identity/src/test/index.test.ts +++ b/packages/identity/src/test/index.test.ts @@ -11,10 +11,13 @@ describe('Message signature verification', () => { let crypto: SubtleCrypto | null beforeAll(() => { const webcrypto = new Crypto() - setEngine('newEngine', new CryptoEngine({ - name: 'newEngine', - crypto: webcrypto, - })) + setEngine( + 'newEngine', + new CryptoEngine({ + name: 'newEngine', + crypto: webcrypto, + }) + ) crypto = getCrypto() }) @@ -28,14 +31,10 @@ describe('Message signature verification', () => { const data = { message, userPubKey: await extractPubKey(userCert.userCertString, crypto), - signature: await sign(message, userCsr.pkcs10.privateKey) + signature: await sign(message, userCsr.pkcs10.privateKey), } - const result = await verifySignature( - data.signature, - data.message, - data.userPubKey - ) + const result = await verifySignature(data.signature, data.message, data.userPubKey) expect(result).toBe(true) }) @@ -52,14 +51,10 @@ describe('Message signature verification', () => { const data = { message, userPubKey: await extractPubKey(spoofedUserCert.userCertString, crypto), - signature: await sign(message, userCsr.pkcs10.privateKey) + signature: await sign(message, userCsr.pkcs10.privateKey), } - const result = await verifySignature( - data.signature, - data.message, - data.userPubKey - ) + const result = await verifySignature(data.signature, data.message, data.userPubKey) expect(result).toBe(false) }) @@ -92,7 +87,7 @@ describe('Certificate', () => { [CertFieldsTypes.commonName]: userData.commonName, [CertFieldsTypes.nickName]: userData.nickname, [CertFieldsTypes.peerId]: userData.peerId, - [CertFieldsTypes.dmPublicKey]: userData.dmPublicKey + [CertFieldsTypes.dmPublicKey]: userData.dmPublicKey, } type CertFieldsTypesKeys = keyof typeof certTypeData @@ -112,7 +107,7 @@ describe('Certificate', () => { [CertFieldsTypes.commonName]: userData.commonName, [CertFieldsTypes.nickName]: userData.nickname, [CertFieldsTypes.peerId]: userData.peerId, - [CertFieldsTypes.dmPublicKey]: userData.dmPublicKey + [CertFieldsTypes.dmPublicKey]: userData.dmPublicKey, } type CertFieldsTypesKeys = keyof typeof certTypeData @@ -127,7 +122,9 @@ describe('Certificate', () => { }) it('old certificate cannot contains proper data', async () => { - const parsedCert = parseCertificate('MIIB7TCCAZMCBgF641h5xzAKBggqhkjOPQQDAjASMRAwDgYDVQQDEwdaYmF5IENBMB4XDTIxMDcyNjE1MDQyNFoXDTMwMDIwMTAwMDAwMFowgc4xgcswHAYKKwYBBAGDjBsCARMOZGV2OTlkZXY5OXlvZGEwPwYDVQQDEzgzNWNzNmZramJoZmJiMnppYnIzNm5rdXY0cWlld2x2NXBmcGprbHh2N2xtcGphM2hydTN3NDdpZDA7BgkrBgECAQ8DAQETLlFtVmIxbUZ2Z1hKZXRKS0o1NmRtR1Q2Rkd1TnJtM0VhVFZ6V3VHaGtxcjZodjUwLQYJKoZIhvcNAQkMBCCf3wijnripB3ZADnDgT1ZIr1zUGjHVZI2K4kt6Yb7CazBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDKw/zcoE2Vahw3q9CdRQsCXikFU8PhEIN/y65hrB6yAxWH4Ut9QBKMMAnaG8JlzvEeaScQiu5Jyyx0O0xAadQ+jHTAbMAwGA1UdEwQFMAMCAQMwCwYDVR0PBAQDAgAGMAoGCCqGSM49BAMCA0gAMEUCIQCRz+6W3K3SI7Q7uYDVVIJXnWud/DGvpqHCuLJ+gnJLMgIgBmS1D8s1xnGOQpARx40vus4b/f49LQeG2YxPCSHVQOM=') + const parsedCert = parseCertificate( + 'MIIB7TCCAZMCBgF641h5xzAKBggqhkjOPQQDAjASMRAwDgYDVQQDEwdaYmF5IENBMB4XDTIxMDcyNjE1MDQyNFoXDTMwMDIwMTAwMDAwMFowgc4xgcswHAYKKwYBBAGDjBsCARMOZGV2OTlkZXY5OXlvZGEwPwYDVQQDEzgzNWNzNmZramJoZmJiMnppYnIzNm5rdXY0cWlld2x2NXBmcGprbHh2N2xtcGphM2hydTN3NDdpZDA7BgkrBgECAQ8DAQETLlFtVmIxbUZ2Z1hKZXRKS0o1NmRtR1Q2Rkd1TnJtM0VhVFZ6V3VHaGtxcjZodjUwLQYJKoZIhvcNAQkMBCCf3wijnripB3ZADnDgT1ZIr1zUGjHVZI2K4kt6Yb7CazBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDKw/zcoE2Vahw3q9CdRQsCXikFU8PhEIN/y65hrB6yAxWH4Ut9QBKMMAnaG8JlzvEeaScQiu5Jyyx0O0xAadQ+jHTAbMAwGA1UdEwQFMAMCAQMwCwYDVR0PBAQDAgAGMAoGCCqGSM49BAMCA0gAMEUCIQCRz+6W3K3SI7Q7uYDVVIJXnWud/DGvpqHCuLJ+gnJLMgIgBmS1D8s1xnGOQpARx40vus4b/f49LQeG2YxPCSHVQOM=' + ) expect(getCertFieldValue(parsedCert, CertFieldsTypes.dmPublicKey)).toEqual(null) expect(getCertFieldValue(parsedCert, CertFieldsTypes.subjectAltName)).toEqual(null) diff --git a/packages/identity/src/test/setupTests.ts b/packages/identity/src/test/setupTests.ts index 38cab734a0..94a8039ce9 100644 --- a/packages/identity/src/test/setupTests.ts +++ b/packages/identity/src/test/setupTests.ts @@ -8,7 +8,7 @@ setEngine( new CryptoEngine({ name: '', crypto: webcrypto, - subtle: webcrypto.subtle + subtle: webcrypto.subtle, }) ) diff --git a/packages/identity/src/verification.ts b/packages/identity/src/verification.ts index e5488223e7..4cf04a7531 100644 --- a/packages/identity/src/verification.ts +++ b/packages/identity/src/verification.ts @@ -10,10 +10,5 @@ export const verifySignature = async ( const crypto = getCrypto() if (!crypto) throw new NoCryptoEngineError() const algorithm = getAlgorithmParameters(config.signAlg, 'verify') - return await crypto.verify( - (algorithm.algorithm as Algorithm), - publicKey, - signature, - Buffer.from(message) - ) + return await crypto.verify(algorithm.algorithm as Algorithm, publicKey, signature, Buffer.from(message)) } diff --git a/packages/identity/src/verifyUserCertificate.ts b/packages/identity/src/verifyUserCertificate.ts index 1261f66717..15acfe3c9b 100644 --- a/packages/identity/src/verifyUserCertificate.ts +++ b/packages/identity/src/verifyUserCertificate.ts @@ -1,7 +1,7 @@ import { CertificateChainValidationEngine, type CertificateChainValidationEngineVerifyResult, - type CertificateRevocationList + type CertificateRevocationList, } from 'pkijs' import { loadCertificate } from './common' @@ -16,7 +16,7 @@ export const verifyUserCert = async ( const certChainVerificationEngine = new CertificateChainValidationEngine({ trustedCerts, certs: certificates, - crls + crls, }) return await certChainVerificationEngine.verify() } diff --git a/packages/integration-tests/src/bot/bot.ts b/packages/integration-tests/src/bot/bot.ts index 8785df2189..e2a4cb3c6e 100644 --- a/packages/integration-tests/src/bot/bot.ts +++ b/packages/integration-tests/src/bot/bot.ts @@ -25,7 +25,7 @@ setEngine( new CryptoEngine({ name: '', crypto: webcrypto, - subtle: webcrypto.subtle + subtle: webcrypto.subtle, }) ) @@ -36,7 +36,10 @@ program .option('-u, --activeUsers ', 'Number of spamming users (bots)', '3') .option('-s, --silentUsers ', 'Number of extra peers (bots)', '0') .option('-i, --intensity ', 'Number of messages per minute') - .option('-std, --standby ', 'Amount of time (ms) during which the peers will remain connected after sending all the messages') + .option( + '-std, --standby ', + 'Amount of time (ms) during which the peers will remain connected after sending all the messages' + ) .option('-e, --endless', 'Make the bot run endlessly') program.parse() @@ -45,8 +48,8 @@ const options = program.opts() const lorem = new LoremIpsum({ wordsPerSentence: { max: 16, - min: 1 - } + min: 1, + }, }) const apps: Map> = new Map() @@ -90,7 +93,7 @@ const registerBots = async () => { registrarAddress, userName: username, registrarPort: null, - store + store, } log(`Registering ${username}`) await registerUsername(payload) @@ -98,7 +101,10 @@ const registerBots = async () => { const communityId = store.getState().Communities.communities.ids[0] await waitForExpect(() => { - assert.ok(store.getState().Identity.identities.entities[communityId].userCertificate, `User ${username} did not receive certificate`) + assert.ok( + store.getState().Identity.identities.entities[communityId].userCertificate, + `User ${username} did not receive certificate` + ) }, timeout) await assertReceivedChannel(username, channelName, timeout, store) await switchChannel({ channelName, store }) @@ -139,7 +145,11 @@ const sendMessages = async () => { continue } - await sendMessageWithLatency(currentUsername, apps.get(currentUsername).store, `(${endless ? 'endless' : messagesLeft}) ${lorem.generateSentences(1)}`) + await sendMessageWithLatency( + currentUsername, + apps.get(currentUsername).store, + `(${endless ? 'endless' : messagesLeft}) ${lorem.generateSentences(1)}` + ) messagesToSend.set(currentUsername, messagesLeft) } } @@ -148,22 +158,18 @@ const sendMessages = async () => { await sleep(10_000) } -const sendMessageWithLatency = async ( - username: string, - store: TestStore, - message: string -) => { +const sendMessageWithLatency = async (username: string, store: TestStore, message: string) => { const latency = typingLatency || getRandomInt(300, 550) log(`${username} is waiting ${latency}ms to send a message`) await sleep(latency) await sendMessage({ message, channelName, - store + store, }) } -const closeAll = async (force: boolean = false) => { +const closeAll = async (force = false) => { if (!force && standby) { log(`Waiting ${standby}ms before peers goes offline`) await sleep(standby) @@ -175,7 +181,7 @@ const closeAll = async (force: boolean = false) => { } const run = async () => { - process.on('unhandledRejection', async (error) => { + process.on('unhandledRejection', async error => { console.error(error) await closeAll(true) }) @@ -190,6 +196,8 @@ const run = async () => { await closeAll() } -run().then(() => { - console.log('FINISHED') -}).catch((e) => console.error(e)) +run() + .then(() => { + console.log('FINISHED') + }) + .catch(e => console.error(e)) diff --git a/packages/integration-tests/src/integrationTests/appActions.ts b/packages/integration-tests/src/integrationTests/appActions.ts index 5b5e4638c3..1addb1a517 100644 --- a/packages/integration-tests/src/integrationTests/appActions.ts +++ b/packages/integration-tests/src/integrationTests/appActions.ts @@ -1,5 +1,19 @@ import waitForExpect from 'wait-for-expect' -import { identity, communities, messages, files, connection, publicChannels, RegisterCertificatePayload, CreateNetworkPayload, CommunityOwnership, TestStore, ChannelMessage, FileContent, network } from '@quiet/state-manager' +import { + identity, + communities, + messages, + files, + connection, + publicChannels, + RegisterCertificatePayload, + CreateNetworkPayload, + CommunityOwnership, + TestStore, + ChannelMessage, + FileContent, + network, +} from '@quiet/state-manager' import { MAIN_CHANNEL } from '../testUtils/constants' import { AsyncReturnType } from '../types/AsyncReturnType.interface' import { createApp } from '../utils' @@ -64,7 +78,7 @@ export async function createCommunity({ userName, store }: CreateCommunity) { const createNetworkPayload: CreateNetworkPayload = { ownership: CommunityOwnership.Owner, - name: communityName + name: communityName, } store.dispatch(communities.actions.createNetwork(createNetworkPayload)) @@ -79,60 +93,38 @@ export async function createCommunity({ userName, store }: CreateCommunity) { const communityId = store.getState().Communities.communities.ids[0] await waitForExpect(() => { - expect( - store.getState().Identity.identities.entities[communityId].hiddenService - .onionAddress - ).toBeTruthy() + expect(store.getState().Identity.identities.entities[communityId].hiddenService.onionAddress).toBeTruthy() }, timeout) await waitForExpect(() => { - expect( - store.getState().Identity.identities.entities[communityId].peerId.id - ).toHaveLength(46) + expect(store.getState().Identity.identities.entities[communityId].peerId.id).toHaveLength(46) }, timeout) store.dispatch(identity.actions.registerUsername(userName)) await waitForExpect(() => { - expect( - store.getState().Identity.identities.entities[communityId].userCertificate - ).toBeTruthy() + expect(store.getState().Identity.identities.entities[communityId].userCertificate).toBeTruthy() }, timeout) await waitForExpect(() => { - expect( - store.getState().Communities.communities.entities[communityId].CA - ).toHaveProperty('rootObject') + expect(store.getState().Communities.communities.entities[communityId].CA).toHaveProperty('rootObject') }, timeout) await waitForExpect(() => { - expect( - store.getState().Communities.communities.entities[communityId] - .onionAddress - ).toBeTruthy() + expect(store.getState().Communities.communities.entities[communityId].onionAddress).toBeTruthy() }, timeout) - log(store.getState().Communities.communities.entities[communityId] - .onionAddress) + log(store.getState().Communities.communities.entities[communityId].onionAddress) await waitForExpect(() => { expect(store.getState().Users.certificates.ids).toHaveLength(1) }, timeout) await waitForExpect(() => { - expect( - store.getState().Network.initializedCommunities[communityId] - ).toBeTruthy() + expect(store.getState().Network.initializedCommunities[communityId]).toBeTruthy() }, timeout) log('initializedCommunity', store.getState().Network.initializedCommunities[communityId]) await waitForExpect(() => { - expect( - store.getState().Network.initializedRegistrars[communityId] - ).toBeTruthy() + expect(store.getState().Network.initializedRegistrars[communityId]).toBeTruthy() }, timeout) } export async function registerUsername(payload: Register) { - const { - registrarAddress, - userName, - registrarPort, - store - } = payload + const { registrarAddress, userName, registrarPort, store } = payload // Give it a huge timeout, it should never fail, but sometimes takes more time, depending on tor. const timeout = 600_000 @@ -146,7 +138,7 @@ export async function registerUsername(payload: Register) { const createNetworkPayload: CreateNetworkPayload = { ownership: CommunityOwnership.User, - registrar: address + registrar: address, } store.dispatch(communities.actions.createNetwork(createNetworkPayload)) @@ -161,15 +153,10 @@ export async function registerUsername(payload: Register) { const communityId = store.getState().Communities.communities.ids[0] await waitForExpect(() => { - expect( - store.getState().Identity.identities.entities[communityId].hiddenService - .onionAddress - ).toBeTruthy() + expect(store.getState().Identity.identities.entities[communityId].hiddenService.onionAddress).toBeTruthy() }, timeout) await waitForExpect(() => { - expect( - store.getState().Identity.identities.entities[communityId].peerId.id - ).toHaveLength(46) + expect(store.getState().Identity.identities.entities[communityId].peerId.id).toHaveLength(46) }, timeout) store.dispatch(identity.actions.registerUsername(userName)) @@ -183,49 +170,35 @@ export async function sendCsr(store: Store) { const csr: RegisterCertificatePayload = { communityId, nickname: nickname, - userCsr + userCsr, } store.dispatch(identity.actions.registerCertificate(csr)) } export async function joinCommunity(payload: JoinCommunity) { - const { - ownerPeerId, - ownerRootCA, - expectedPeersCount, - store - } = payload + const { ownerPeerId, ownerRootCA, expectedPeersCount, store } = payload const timeout = 600_000 await registerUsername(payload) const communityId = store.getState().Communities.communities.ids[0] - const userPeerId = - store.getState().Identity.identities.entities[communityId].peerId.id + const userPeerId = store.getState().Identity.identities.entities[communityId].peerId.id await waitForExpect(() => { - expect( - store.getState().Identity.identities.entities[communityId].userCertificate - ).toBeTruthy() + expect(store.getState().Identity.identities.entities[communityId].userCertificate).toBeTruthy() }, timeout) await waitForExpect(() => { - expect( - store.getState().Communities.communities.entities[communityId].rootCa - ).toEqual(ownerRootCA) + expect(store.getState().Communities.communities.entities[communityId].rootCa).toEqual(ownerRootCA) }, timeout) await waitForExpect(() => { - expect( - store.getState().Communities.communities.entities[communityId].peerList - .length - ).toEqual(expectedPeersCount) + expect(store.getState().Communities.communities.entities[communityId].peerList.length).toEqual(expectedPeersCount) }, timeout) - const peerList = - store.getState().Communities.communities.entities[communityId].peerList + const peerList = store.getState().Communities.communities.entities[communityId].peerList await waitForExpect(() => { expect(peerList[0]).toMatch(new RegExp(ownerPeerId)) @@ -236,14 +209,8 @@ export async function joinCommunity(payload: JoinCommunity) { }, timeout) } -export async function sendMessage( - payload: SendMessage -): Promise { - const { - message, - channelName, - store - } = payload +export async function sendMessage(payload: SendMessage): Promise { + const { message, channelName, store } = payload log(message, 'sendMessage') const communityId = store.getState().Communities.communities.ids[0] @@ -251,7 +218,7 @@ export async function sendMessage( if (channelName) { store.dispatch( publicChannels.actions.setCurrentChannel({ - channelId: channelName + channelId: channelName, }) ) } @@ -262,22 +229,19 @@ export async function sendMessage( expect(store.getState().LastAction.includes('Messages/addMessageVerificationStatus')) }, 5000) - const entities = Array.from(Object.values(store.getState().Messages.publicChannelsMessagesBase.entities[MAIN_CHANNEL].messages.entities)) + const entities = Array.from( + Object.values(store.getState().Messages.publicChannelsMessagesBase.entities[MAIN_CHANNEL].messages.entities) + ) - const newMessage = entities.filter((m) => { + const newMessage = entities.filter(m => { return m.message === message }) return newMessage[0] } -export async function sendImage( - payload: SendImage -) { - const { - file, - store - } = payload +export async function sendImage(payload: SendImage) { + const { file, store } = payload log(JSON.stringify(payload), 'sendImage') @@ -291,21 +255,15 @@ export async function sendImage( export const getCommunityOwnerData = (ownerStore: Store) => { const ownerStoreState = ownerStore.getState() - const community = - ownerStoreState.Communities.communities.entities[ - ownerStoreState.Communities.currentCommunity - ] + const community = ownerStoreState.Communities.communities.entities[ownerStoreState.Communities.currentCommunity] const registrarAddress = community.onionAddress const ownerIdentityState = ownerStore.getState().Identity return { registrarAddress, communityId: community.id, - ownerPeerId: - ownerIdentityState.identities.entities[ - ownerIdentityState.identities.ids[0] - ].peerId.id, + ownerPeerId: ownerIdentityState.identities.entities[ownerIdentityState.identities.ids[0]].peerId.id, ownerRootCA: community.rootCa, - registrarPort: community.port + registrarPort: community.port, } } @@ -314,9 +272,7 @@ export const clearInitializedCommunitiesAndRegistrars = (store: Store) => { store.dispatch(network.actions.removeInitializedRegistrars) } -export const sendRegistrationRequest = async ( - payload: SendRegistrationRequest -) => { +export const sendRegistrationRequest = async (payload: SendRegistrationRequest) => { const { registrarAddress, userName, registrarPort, store } = payload const timeout = 600_000 @@ -330,7 +286,7 @@ export const sendRegistrationRequest = async ( const createNetworkPayload: CreateNetworkPayload = { ownership: CommunityOwnership.User, - registrar: address + registrar: address, } store.dispatch(communities.actions.createNetwork(createNetworkPayload)) @@ -345,15 +301,10 @@ export const sendRegistrationRequest = async ( const communityId = store.getState().Communities.communities.ids[0] await waitForExpect(() => { - expect( - store.getState().Identity.identities.entities[communityId].hiddenService - .onionAddress - ).toBeTruthy() + expect(store.getState().Identity.identities.entities[communityId].hiddenService.onionAddress).toBeTruthy() }, timeout) await waitForExpect(() => { - expect( - store.getState().Identity.identities.entities[communityId].peerId.id - ).toHaveLength(46) + expect(store.getState().Identity.identities.entities[communityId].peerId.id).toHaveLength(46) }, timeout) store.dispatch(identity.actions.registerUsername(userName)) diff --git a/packages/integration-tests/src/integrationTests/assertions.ts b/packages/integration-tests/src/integrationTests/assertions.ts index b10dfaa6b1..3693b86aaf 100644 --- a/packages/integration-tests/src/integrationTests/assertions.ts +++ b/packages/integration-tests/src/integrationTests/assertions.ts @@ -9,7 +9,7 @@ const log = logger('assertions') export async function assertReceivedCertificates( userName: string, expectedCount: number, - maxTime: number = 60000, + maxTime = 60000, store: TestStore ) { log(`User ${userName} starts waiting ${maxTime}ms for certificates`) @@ -18,53 +18,46 @@ export async function assertReceivedCertificates( expect(store.getState().Users.certificates.ids).toHaveLength(expectedCount) }, maxTime) - log( - `User ${userName} received ${store.getState().Users.certificates.ids.length - } certificates` - ) + log(`User ${userName} received ${store.getState().Users.certificates.ids.length} certificates`) } export async function assertReceivedChannelsAndSubscribe( userName: string, expectedCount: number, - maxTime: number = 60000, + maxTime = 60000, store: TestStore ) { log(`User ${userName} starts waiting ${maxTime}ms for channels`) await waitForExpect(() => { - expect( - store.getState().PublicChannels.channels.ids - ).toHaveLength(expectedCount) + expect(store.getState().PublicChannels.channels.ids).toHaveLength(expectedCount) }, maxTime) store.dispatch( publicChannels.actions.setCurrentChannel({ - channelId: store.getState().PublicChannels.channels.ids[0] as string + channelId: store.getState().PublicChannels.channels.ids[0] as string, }) ) - log( - `User ${userName} received ${store.getState().PublicChannels.channels.ids.length - } channels` - ) + log(`User ${userName} received ${store.getState().PublicChannels.channels.ids.length} channels`) } export async function assertReceivedMessages( userName: string, expectedCount: number, - maxTime: number = 60000, + maxTime = 60000, store: TestStore ) { log(`User ${userName} starts waiting ${maxTime}ms for messages`) await waitForExpect(() => { - expect( - store.getState().Messages.publicChannelsMessagesBase.entities[MAIN_CHANNEL].messages.ids - ).toHaveLength(expectedCount) + expect(store.getState().Messages.publicChannelsMessagesBase.entities[MAIN_CHANNEL].messages.ids).toHaveLength( + expectedCount + ) }, maxTime) log( - `User ${userName} received ${store.getState().Messages.publicChannelsMessagesBase.entities[MAIN_CHANNEL].messages.ids.length + `User ${userName} received ${ + store.getState().Messages.publicChannelsMessagesBase.entities[MAIN_CHANNEL].messages.ids.length } messages` ) } @@ -72,7 +65,7 @@ export async function assertReceivedMessages( export const assertReceivedMessagesAreValid = async ( userName: string, messages: any[], - maxTime: number = 60000, + maxTime = 60000, store: TestStore ) => { log(`User ${userName} checks is messages are valid`) @@ -84,9 +77,7 @@ export const assertReceivedMessagesAreValid = async ( const validMessages = [] for (const receivedMessage of receivedMessages) { - const msg = messages.filter( - (message) => message.signature === receivedMessage.signature - ) + const msg = messages.filter(message => message.signature === receivedMessage.signature) if (msg[0]) { validMessages.push(msg[0]) } @@ -97,12 +88,7 @@ export const assertReceivedMessagesAreValid = async ( }, maxTime) } -export async function assertReceivedImages( - userName: string, - expectedCount: number, - maxTime: number = 60000, - store: TestStore -) { +export async function assertReceivedImages(userName: string, expectedCount: number, maxTime = 60000, store: TestStore) { log(`User ${userName} starts waiting ${maxTime}ms for image`) await waitForExpect(() => { expect( @@ -123,7 +109,7 @@ export async function assertReceivedImages( export async function assertDownloadedImage( userName: string, expectedImage: string, // filename.ext - maxTime: number = 60000, + maxTime = 60000, store: TestStore ) { log(`User ${userName} starts waiting ${maxTime}ms for downloading ${expectedImage}`) @@ -137,25 +123,17 @@ export async function assertDownloadedImage( expect(filename).toBe(expectedImage) }, maxTime) - log( - `User ${userName} downloaded ${expectedImage}` - ) + log(`User ${userName} downloaded ${expectedImage}`) } -export const assertInitializedExistingCommunitiesAndRegistrars = async ( - store: TestStore -) => { +export const assertInitializedExistingCommunitiesAndRegistrars = async (store: TestStore) => { const communityId = store.getState().Communities.communities.ids[0] await waitForExpect(() => { - expect( - store.getState().Network.initializedCommunities[communityId] - ).toBeTruthy() + expect(store.getState().Network.initializedCommunities[communityId]).toBeTruthy() }) await waitForExpect(() => { - expect( - store.getState().Network.initializedRegistrars[communityId] - ).toBeTruthy() + expect(store.getState().Network.initializedRegistrars[communityId]).toBeTruthy() }) } @@ -170,7 +148,7 @@ export const assertReceivedRegistrationError = async (store: TestStore, error?: } } -export const assertNoRegistrationError = async(store: TestStore) => { +export const assertNoRegistrationError = async (store: TestStore) => { await waitForExpect(() => { expect(store.getState().Errors.errors?.ids.includes('registrar')).toBe(false) }, 20_000) @@ -179,21 +157,14 @@ export const assertNoRegistrationError = async(store: TestStore) => { export const assertReceivedCertificate = async (store: TestStore) => { const communityId = store.getState().Communities.communities.ids[0] await waitForExpect(() => { - expect( - store.getState().Identity.identities.entities[communityId].userCertificate - ).toBeTruthy() + expect(store.getState().Identity.identities.entities[communityId].userCertificate).toBeTruthy() }, 150_000) } -export const assertConnectedToPeers = async ( - store: TestStore, - count: number -) => { +export const assertConnectedToPeers = async (store: TestStore, count: number) => { await sleep(10_000) await waitForExpect(() => { - expect( - store.getState().Network.connectedPeers.ids.length - ).toEqual(count) + expect(store.getState().Network.connectedPeers.ids.length).toEqual(count) }, 20_000) } @@ -202,8 +173,8 @@ export const assertStoreStatesAreEqual = async (oldState, currentState) => { ...oldState, Connection: { ...oldState.Connection, - lastConnectedTime: currentState.Connection.lastConnectedTime - } + lastConnectedTime: currentState.Connection.lastConnectedTime, + }, } expect(currentState).toMatchObject(oldStateWithLastConnectedTimeFromCurrentState) diff --git a/packages/integration-tests/src/integrationTests/createJoinCommunity.test.ts b/packages/integration-tests/src/integrationTests/createJoinCommunity.test.ts index ebabe2da86..d5f2e2cd97 100644 --- a/packages/integration-tests/src/integrationTests/createJoinCommunity.test.ts +++ b/packages/integration-tests/src/integrationTests/createJoinCommunity.test.ts @@ -1,9 +1,7 @@ import { Crypto } from '@peculiar/webcrypto' import { AsyncReturnType } from '../types/AsyncReturnType.interface' import { createApp, sleep } from '../utils' -import { - createCommunity, getCommunityOwnerData, joinCommunity -} from './appActions' +import { createCommunity, getCommunityOwnerData, joinCommunity } from './appActions' import { assertConnectedToPeers, assertReceivedCertificates } from './assertions' const crypto = new Crypto() @@ -54,14 +52,14 @@ describe('owner creates community and two users join', () => { ...ownerData, store: userOne.store, userName: 'username1', - expectedPeersCount: 2 + expectedPeersCount: 2, }) await joinCommunity({ ...ownerData, store: userTwo.store, userName: 'username2', - expectedPeersCount: 3 + expectedPeersCount: 3, }) }) diff --git a/packages/integration-tests/src/integrationTests/registrar.test.ts b/packages/integration-tests/src/integrationTests/registrar.test.ts index f73251c9c8..2e6849bc98 100644 --- a/packages/integration-tests/src/integrationTests/registrar.test.ts +++ b/packages/integration-tests/src/integrationTests/registrar.test.ts @@ -1,6 +1,20 @@ import { Crypto } from '@peculiar/webcrypto' -import { createCommunity, getCommunityOwnerData, registerUsername, sendRegistrationRequest, sendCsr, OwnerData } from './appActions' -import { assertReceivedCertificate, assertReceivedRegistrationError, assertReceivedCertificates, assertNoRegistrationError, assertInitializedCommunity, assertRegistrationRequestSent } from './assertions' +import { + createCommunity, + getCommunityOwnerData, + registerUsername, + sendRegistrationRequest, + sendCsr, + OwnerData, +} from './appActions' +import { + assertReceivedCertificate, + assertReceivedRegistrationError, + assertReceivedCertificates, + assertNoRegistrationError, + assertInitializedCommunity, + assertRegistrationRequestSent, +} from './assertions' import { createApp, sleep, storePersistor } from '../utils' import { AsyncReturnType } from '../types/AsyncReturnType.interface' import { ErrorPayload, SocketActionTypes, ErrorCodes, ErrorMessages } from '@quiet/state-manager' @@ -24,7 +38,7 @@ describe('offline registrar, user tries to join', () => { await sendRegistrationRequest({ registrarAddress: '76gan734wqm4hy7ahj33pnfub7qobdhhkdnd3rbma7o4dq4hce3ncxad', userName: 'waclaw', - store: user.store + store: user.store, }) }) @@ -54,8 +68,7 @@ describe('registrar is offline, user tries to join, then registrar goes online', await createCommunity({ userName: 'placek', store: owner.store }) await assertInitializedCommunity(owner.store) const communityId = owner.store.getState().Communities.currentCommunity - registrarAddress = - owner.store.getState().Communities.communities.entities[communityId].onionAddress + registrarAddress = owner.store.getState().Communities.communities.entities[communityId].onionAddress ownerOldState = storePersistor(owner.store.getState()) ownerDataPath = owner.appPath }) @@ -68,7 +81,7 @@ describe('registrar is offline, user tries to join, then registrar goes online', await sendRegistrationRequest({ userName: 'wacek', store: user.store, - registrarAddress + registrarAddress, }) // User should keep sending requests for 10 seconds. await assertRegistrationRequestSent(user.store, 2) @@ -115,7 +128,7 @@ describe('User tries to register existing username', () => { await registerUsername({ ...ownerData, store: user.store, - userName + userName, }) }) @@ -150,7 +163,7 @@ xdescribe('Certificate already exists in db, user asks for certificate providing await registerUsername({ ...ownerData, store: user.store, - userName + userName, }) }) diff --git a/packages/integration-tests/src/integrationTests/restartApp.test.ts b/packages/integration-tests/src/integrationTests/restartApp.test.ts index 56e5c6d493..c60265a468 100644 --- a/packages/integration-tests/src/integrationTests/restartApp.test.ts +++ b/packages/integration-tests/src/integrationTests/restartApp.test.ts @@ -1,11 +1,12 @@ import { Crypto } from '@peculiar/webcrypto' -import { - createCommunity, - clearInitializedCommunitiesAndRegistrars -} from './appActions' +import { createCommunity, clearInitializedCommunitiesAndRegistrars } from './appActions' import { createApp, sleep, storePersistor } from '../utils' import { AsyncReturnType } from '../types/AsyncReturnType.interface' -import { assertInitializedExistingCommunitiesAndRegistrars, assertStoreStatesAreEqual, assertInitializedCommunity } from './assertions' +import { + assertInitializedExistingCommunitiesAndRegistrars, + assertStoreStatesAreEqual, + assertInitializedCommunity, +} from './assertions' const crypto = new Crypto() @@ -25,8 +26,7 @@ describe('restart app without doing anything', () => { await owner.manager.closeAllServices() }) - test('Owner creates community', async () => { - }) + test('Owner creates community', async () => {}) test('Owner successfully closes app', async () => { store = owner.store diff --git a/packages/integration-tests/src/integrationTests/sendFiles.test.ts b/packages/integration-tests/src/integrationTests/sendFiles.test.ts index 353fdc4809..fdbf8ee482 100644 --- a/packages/integration-tests/src/integrationTests/sendFiles.test.ts +++ b/packages/integration-tests/src/integrationTests/sendFiles.test.ts @@ -3,7 +3,7 @@ import { assertDownloadedImage, assertReceivedCertificates, assertReceivedChannelsAndSubscribe, - assertReceivedImages + assertReceivedImages, } from './assertions' import { createCommunity, joinCommunity, getCommunityOwnerData, sendImage, SendImage } from './appActions' import { createApp, storePersistor } from '../utils' @@ -26,7 +26,7 @@ describe('send message - users are online', () => { const image: FileContent = { path: `${__dirname}/assets/test-image.jpeg`, name: 'test-image', - ext: '.jpeg' + ext: '.jpeg', } beforeAll(async () => { @@ -50,7 +50,7 @@ describe('send message - users are online', () => { ...ownerData, store: userOne.store, userName: 'username1', - expectedPeersCount: 2 + expectedPeersCount: 2, }) }) @@ -68,7 +68,7 @@ describe('send message - users are online', () => { log(`Image ${JSON.stringify(image)}`) const payload: SendImage = { file: image, - store: owner.store + store: owner.store, } await sendImage(payload) }) @@ -96,7 +96,7 @@ describe('send files - image is being redistributed (users going offline)', () = const image: FileContent = { path: `${__dirname}/assets/test-image.jpeg`, name: 'test-image', - ext: '.jpeg' + ext: '.jpeg', } beforeAll(async () => { @@ -121,14 +121,14 @@ describe('send files - image is being redistributed (users going offline)', () = ...ownerData, store: userOne.store, userName: 'username1', - expectedPeersCount: 2 + expectedPeersCount: 2, }) await joinCommunity({ ...ownerData, store: userTwo.store, userName: 'username2', - expectedPeersCount: 3 + expectedPeersCount: 3, }) }) @@ -153,7 +153,7 @@ describe('send files - image is being redistributed (users going offline)', () = test('Owner sends image, while UserTwo is offline', async () => { await sendImage({ file: image, - store: owner.store + store: owner.store, }) }) diff --git a/packages/integration-tests/src/integrationTests/sendMessages.test.ts b/packages/integration-tests/src/integrationTests/sendMessages.test.ts index c949a6279d..5908759be3 100644 --- a/packages/integration-tests/src/integrationTests/sendMessages.test.ts +++ b/packages/integration-tests/src/integrationTests/sendMessages.test.ts @@ -3,7 +3,7 @@ import { assertReceivedCertificates, assertReceivedChannelsAndSubscribe, assertReceivedMessages, - assertReceivedMessagesAreValid + assertReceivedMessagesAreValid, } from './assertions' import { createCommunity, joinCommunity, getCommunityOwnerData, sendMessage } from './appActions' import { createApp, createAppWithoutTor, sleep, storePersistor } from '../utils' @@ -49,14 +49,14 @@ describe('send message - users go offline and online', () => { ...ownerData, store: userOne.store, userName: 'username1', - expectedPeersCount: 2 + expectedPeersCount: 2, }) await joinCommunity({ ...ownerData, store: userTwo.store, userName: 'username2', - expectedPeersCount: 3 + expectedPeersCount: 3, }) }) @@ -99,9 +99,8 @@ describe('send message - users go offline and online', () => { test('Owner sends messages, while users are offline', async () => { const ownerMessage = await sendMessage({ message: 'Hi folks, how u doin? Does Wacek still has covid?', - store: owner.store - } - ) + store: owner.store, + }) ownerMessagesData.push(ownerMessage) }) @@ -161,14 +160,14 @@ describe.only('send message - users are online', () => { ...ownerData, store: userOne.store, userName: 'username1', - expectedPeersCount: 2 + expectedPeersCount: 2, }) await joinCommunity({ ...ownerData, store: userTwo.store, userName: 'username2', - expectedPeersCount: 3 + expectedPeersCount: 3, }) }) @@ -256,14 +255,14 @@ xdescribe('send message - without tor', () => { ...ownerData, store: userOne.store, userName: 'username1', - expectedPeersCount: 2 + expectedPeersCount: 2, }) await joinCommunity({ ...ownerData, store: userTwo.store, userName: 'username2', - expectedPeersCount: 3 + expectedPeersCount: 3, }) }) diff --git a/packages/integration-tests/src/setupTests.ts b/packages/integration-tests/src/setupTests.ts index 38cab734a0..94a8039ce9 100644 --- a/packages/integration-tests/src/setupTests.ts +++ b/packages/integration-tests/src/setupTests.ts @@ -8,7 +8,7 @@ setEngine( new CryptoEngine({ name: '', crypto: webcrypto, - subtle: webcrypto.subtle + subtle: webcrypto.subtle, }) ) diff --git a/packages/integration-tests/src/testUtils/actions.ts b/packages/integration-tests/src/testUtils/actions.ts index 641d028bf6..4c4d94677e 100644 --- a/packages/integration-tests/src/testUtils/actions.ts +++ b/packages/integration-tests/src/testUtils/actions.ts @@ -1,5 +1,13 @@ - -import { ChannelMessage, communities, CommunityOwnership, CreateNetworkPayload, identity, publicChannels, messages, files } from '@quiet/state-manager' +import { + ChannelMessage, + communities, + CommunityOwnership, + CreateNetworkPayload, + identity, + publicChannels, + messages, + files, +} from '@quiet/state-manager' import assert from 'assert' import { Register, SendImage, SendMessage } from '../integrationTests/appActions' import logger from '../logger' @@ -9,15 +17,11 @@ const log = logger('actions') const timeout = 120_000 export async function registerUsername(payload: Register) { - const { - registrarAddress, - userName, - store - } = payload + const { registrarAddress, userName, store } = payload const createNetworkPayload: CreateNetworkPayload = { ownership: CommunityOwnership.User, - registrar: registrarAddress + registrar: registrarAddress, } log(`User ${userName} starts creating network`) store.dispatch(communities.actions.createNetwork(createNetworkPayload)) @@ -45,7 +49,7 @@ export async function registerUsername(payload: Register) { export const createCommunity = async ({ username, communityName, store }): Promise => { const createNetworkPayload: CreateNetworkPayload = { ownership: CommunityOwnership.Owner, - name: communityName + name: communityName, } store.dispatch(communities.actions.createNetwork(createNetworkPayload)) @@ -59,23 +63,16 @@ export const createCommunity = async ({ username, communityName, store }): Promi const communityId = store.getState().Communities.communities.ids[0] await waitForExpect(() => { - assert.ok( - store.getState().Identity.identities.entities[communityId].hiddenService - .onionAddress - ) + assert.ok(store.getState().Identity.identities.entities[communityId].hiddenService.onionAddress) }, timeout) await waitForExpect(() => { - assert.strictEqual( - store.getState().Identity.identities.entities[communityId].peerId.id.length, 46 - ) + assert.strictEqual(store.getState().Identity.identities.entities[communityId].peerId.id.length, 46) }, timeout) store.dispatch(identity.actions.registerUsername(username)) await waitForExpect(() => { - assert.ok( - store.getState().Identity.identities.entities[communityId].userCertificate - ) + assert.ok(store.getState().Identity.identities.entities[communityId].userCertificate) }, timeout) // await waitForExpect(() => { // expect( @@ -83,38 +80,25 @@ export const createCommunity = async ({ username, communityName, store }): Promi // ).toHaveProperty('rootObject') // }, timeout) await waitForExpect(() => { - assert.ok( - store.getState().Communities.communities.entities[communityId] - .onionAddress - ) + assert.ok(store.getState().Communities.communities.entities[communityId].onionAddress) }, timeout) log(store.getState().Communities.communities.entities[communityId].onionAddress) await waitForExpect(() => { assert.strictEqual(store.getState().Users.certificates.ids.length, 1) }, timeout) await waitForExpect(() => { - assert.ok( - store.getState().Connection.initializedCommunities[communityId] - ) + assert.ok(store.getState().Connection.initializedCommunities[communityId]) }, timeout) log('initializedCommunity', store.getState().Connection.initializedCommunities[communityId]) await waitForExpect(() => { - assert.ok( - store.getState().Connection.initializedRegistrars[communityId] - ) + assert.ok(store.getState().Connection.initializedRegistrars[communityId]) }, timeout) return store.getState().Communities.communities.entities[communityId].onionAddress } -export async function sendMessage( - payload: SendMessage -): Promise { - const { - message, - channelName, - store - } = payload +export async function sendMessage(payload: SendMessage): Promise { + const { message, channelName, store } = payload log(message, 'sendMessage') @@ -124,22 +108,19 @@ export async function sendMessage( assert.ok(store.getState().LastAction.includes('Messages/addMessageVerificationStatus')) }, 5000) - const entities = Array.from(Object.values(store.getState().Messages.publicChannelsMessagesBase.entities[channelName].messages.entities)) + const entities = Array.from( + Object.values(store.getState().Messages.publicChannelsMessagesBase.entities[channelName].messages.entities) + ) - const newMessage = entities.filter((m) => { + const newMessage = entities.filter(m => { return m.message === message }) return newMessage[0] } -export async function sendImage( - payload: SendImage -) { - const { - file, - store - } = payload +export async function sendImage(payload: SendImage) { + const { file, store } = payload log(file.path, 'sendImage') @@ -158,15 +139,11 @@ export async function joinCommunity({ registrarAddress, userName, expectedPeersC const userPeerId = store.getState().Identity.identities.entities[communityId].peerId.id await waitForExpect(() => { - assert.ok( - store.getState().Identity.identities.entities[communityId].userCertificate - ) + assert.ok(store.getState().Identity.identities.entities[communityId].userCertificate) }, timeout) await waitForExpect(() => { - assert.equal( - store.getState().Communities.communities.entities[communityId].peerList.length, expectedPeersCount - ) + assert.equal(store.getState().Communities.communities.entities[communityId].peerList.length, expectedPeersCount) }, timeout) const peerList = store.getState().Communities.communities.entities[communityId].peerList @@ -180,7 +157,7 @@ export const switchChannel = async ({ channelName, store }) => { const communityId = store.getState().Communities.communities.ids[0] store.dispatch( publicChannels.actions.setCurrentChannel({ - channelId: channelName + channelId: channelName, }) ) } diff --git a/packages/integration-tests/src/testUtils/assertions.ts b/packages/integration-tests/src/testUtils/assertions.ts index e8d93aa841..84b97ac394 100644 --- a/packages/integration-tests/src/testUtils/assertions.ts +++ b/packages/integration-tests/src/testUtils/assertions.ts @@ -35,14 +35,11 @@ export async function assertReceivedChannel( store.dispatch( publicChannels.actions.setCurrentChannel({ - channelId: store.getState().PublicChannels.channels.ids[0] as string + channelId: store.getState().PublicChannels.channels.ids[0] as string, }) ) - log( - `User ${userName} received ${store.getState().PublicChannels.channels.ids.length - } channels` - ) + log(`User ${userName} received ${store.getState().PublicChannels.channels.ids.length} channels`) } export async function assertReceivedMessages( @@ -55,21 +52,19 @@ export async function assertReceivedMessages( await waitForExpect(() => { assert.strictEqual( - store.getState().Messages.publicChannelsMessagesBase.entities[MAIN_CHANNEL].messages.ids.length, expectedCount + store.getState().Messages.publicChannelsMessagesBase.entities[MAIN_CHANNEL].messages.ids.length, + expectedCount ) }, maxTime) log( - `User ${userName} received ${store.getState().Messages.publicChannelsMessagesBase.entities[MAIN_CHANNEL].messages.ids.length + `User ${userName} received ${ + store.getState().Messages.publicChannelsMessagesBase.entities[MAIN_CHANNEL].messages.ids.length } messages` ) } -export const assertReceivedMessagesMatch = ( - userName: string, - messages: string[], - store: Store -) => { +export const assertReceivedMessagesMatch = (userName: string, messages: string[], store: Store) => { const receivedMessagesEntities = Object.values( store.getState().Messages.publicChannelsMessagesBase.entities[MAIN_CHANNEL].messages.entities ) @@ -84,14 +79,13 @@ export const assertReceivedMessagesMatch = ( } assert.strictEqual( - matchingMessages.length, messages.length, `Messages for ${userName} don't match. Was looking for ${messages}, found ${receivedMessages}` + matchingMessages.length, + messages.length, + `Messages for ${userName} don't match. Was looking for ${messages}, found ${receivedMessages}` ) } -export const assertConnectedToPeers = async ( - store: Store, - count: number -) => { +export const assertConnectedToPeers = async (store: Store, count: number) => { await waitForExpect(() => { assert.strictEqual(store.getState().Network.connectedPeers.ids.length, count) }, timeout) diff --git a/packages/integration-tests/src/testUtils/waitForExpect.ts b/packages/integration-tests/src/testUtils/waitForExpect.ts index 521bebbca3..d14683a2d1 100644 --- a/packages/integration-tests/src/testUtils/waitForExpect.ts +++ b/packages/integration-tests/src/testUtils/waitForExpect.ts @@ -1,6 +1,6 @@ const defaults = { timeout: 4500, - interval: 50 + interval: 50, } /** @@ -35,7 +35,7 @@ export const waitForExpect = async function waitForExpect( tries += 1 try { Promise.resolve(expectation()) - // @ts-expect-error + // @ts-expect-error .then(() => resolve()) .catch(rejectOrRerun) } catch (error) { diff --git a/packages/integration-tests/src/types/AsyncReturnType.interface.ts b/packages/integration-tests/src/types/AsyncReturnType.interface.ts index 3c198283e8..26fbc0fc21 100644 --- a/packages/integration-tests/src/types/AsyncReturnType.interface.ts +++ b/packages/integration-tests/src/types/AsyncReturnType.interface.ts @@ -1,7 +1,5 @@ -export type AsyncReturnType any> = T extends ( - ...args: any -) => Promise +export type AsyncReturnType any> = T extends (...args: any) => Promise ? U : T extends (...args: any) => infer U - ? U - : any + ? U + : any diff --git a/packages/integration-tests/src/utils.ts b/packages/integration-tests/src/utils.ts index 3be6baed81..5af12e3c6b 100644 --- a/packages/integration-tests/src/utils.ts +++ b/packages/integration-tests/src/utils.ts @@ -34,7 +34,10 @@ const connectToDataport = (url: string, name: string): Socket => { return socket } -export const createApp = async (mockedState?: { [key in StoreKeys]?: any }, appDataPath?: string): Promise<{ +export const createApp = async ( + mockedState?: { [key in StoreKeys]?: any }, + appDataPath?: string +): Promise<{ store: TestStore runSaga: >(saga: S, ...args: Parameters) => Task rootTask: Task @@ -58,10 +61,10 @@ export const createApp = async (mockedState?: { [key in StoreKeys]?: any }, appD const manager = new backend.ConnectionsManager({ options: { env: { - appDataPath: appDataPath || appPath + appDataPath: appDataPath || appPath, }, }, - socketIOPort: dataServerPort1 + socketIOPort: dataServerPort1, }) await manager.init() @@ -76,15 +79,18 @@ export const createApp = async (mockedState?: { [key in StoreKeys]?: any }, appD return { store, runSaga, rootTask, manager, appPath } } -export const createAppWithoutTor = async (mockedState?: { - [key in StoreKeys]?: any -}, appDataPath?: string): Promise<{ - store: TestStore - runSaga: >(saga: S, ...args: Parameters) => Task - rootTask: Task - manager: ConnectionsManager - appPath: string - }> => { +export const createAppWithoutTor = async ( + mockedState?: { + [key in StoreKeys]?: any + }, + appDataPath?: string +): Promise<{ + store: TestStore + runSaga: >(saga: S, ...args: Parameters) => Task + rootTask: Task + manager: ConnectionsManager + appPath: string +}> => { /** * Configure and initialize ConnectionsManager from backend, * configure redux store @@ -103,10 +109,10 @@ export const createAppWithoutTor = async (mockedState?: { const manager = new backend.ConnectionsManager({ options: { env: { - appDataPath: appDataPath || appPath + appDataPath: appDataPath || appPath, }, }, - socketIOPort + socketIOPort, }) function* root(): Generator { diff --git a/packages/logger/src/index.ts b/packages/logger/src/index.ts index 8c11305c1e..5459a0ff1c 100644 --- a/packages/logger/src/index.ts +++ b/packages/logger/src/index.ts @@ -1,18 +1,20 @@ import debug from 'debug' export type Logger = debug.Debugger & { - error: debug.Debugger + error: debug.Debugger } -export const consoleLogger = (packageName: string) => (module: string): Logger => { - debug('quiet')('Initializing debug logger') - const logger = Object.assign(debug(`${packageName}:${module}`), { - error: debug(`${packageName}:${module}:err`) - }) - return logger -} +export const consoleLogger = + (packageName: string) => + (module: string): Logger => { + debug('quiet')('Initializing debug logger') + const logger = Object.assign(debug(`${packageName}:${module}`), { + error: debug(`${packageName}:${module}:err`), + }) + return logger + } -export const logger = (packageName: string): (arg: string) => Logger => { +export const logger = (packageName: string): ((arg: string) => Logger) => { return consoleLogger(packageName) } diff --git a/packages/mobile/src/App.tsx b/packages/mobile/src/App.tsx index 571758b959..be5941d19f 100644 --- a/packages/mobile/src/App.tsx +++ b/packages/mobile/src/App.tsx @@ -55,9 +55,9 @@ const linking = { prefixes: ['quiet://'], config: { screens: { - SplashScreen: '' - } - } + SplashScreen: '', + }, + }, } function App(): JSX.Element { @@ -73,30 +73,23 @@ function App(): JSX.Element { linking={linking} onReady={() => { dispatch(navigationActions.redirection()) - }}> + }} + > - + - + headerShown: false, + }} + > + - + diff --git a/packages/mobile/src/RootNavigation.ts b/packages/mobile/src/RootNavigation.ts index fc631d97a6..e0a512d014 100644 --- a/packages/mobile/src/RootNavigation.ts +++ b/packages/mobile/src/RootNavigation.ts @@ -4,24 +4,16 @@ import { ScreenNames } from './const/ScreenNames.enum' export const navigationRef = createNavigationContainerRef() -export const navigate = ( - screen: ScreenNames, - params?: Params -): void => { +export const navigate = >(screen: ScreenNames, params?: Params): void => { if (navigationRef.isReady()) { // @ts-ignore navigationRef.navigate(screen, params) } } -export const replaceScreen = ( - screen: ScreenNames, - params?: Params -): void => { +export const replaceScreen = >(screen: ScreenNames, params?: Params): void => { if (navigationRef.isReady()) { // @ts-ignore - navigationRef.dispatch( - StackActions.replace(screen, params) - ) + navigationRef.dispatch(StackActions.replace(screen, params)) } } diff --git a/packages/mobile/src/assets.ts b/packages/mobile/src/assets.ts index 1746acb9dd..cab9b09d71 100644 --- a/packages/mobile/src/assets.ts +++ b/packages/mobile/src/assets.ts @@ -30,7 +30,7 @@ export const appImages = { icon_warning, icon_close, file_document, - dots + dots, } /** diff --git a/packages/mobile/src/components/Appbar/Appbar.component.tsx b/packages/mobile/src/components/Appbar/Appbar.component.tsx index f43603a2f1..04fa07e061 100644 --- a/packages/mobile/src/components/Appbar/Appbar.component.tsx +++ b/packages/mobile/src/components/Appbar/Appbar.component.tsx @@ -18,7 +18,8 @@ export const Appbar: FC = ({ title, prefix, position, style, back, onPress={() => { if (back) back() }} - testID={'appbar_action_item'}> + testID={'appbar_action_item'} + > {back ? ( = ({ title, prefix, position, style, back, resizeMethod='resize' style={{ width: 16, - height: 16 + height: 16, }} /> ) : ( @@ -38,8 +39,9 @@ export const Appbar: FC = ({ title, prefix, position, style, back, alignItems: 'center', justifyContent: 'center', borderRadius: 4, - backgroundColor: defaultTheme.palette.background.lushSky - }}> + backgroundColor: defaultTheme.palette.background.lushSky, + }} + > {prefix} {title?.slice(0, 2).toLowerCase()} @@ -61,7 +63,8 @@ export const Appbar: FC = ({ title, prefix, position, style, back, event.persist() contextMenu.handleOpen() }} - testID={'open_menu'}> + testID={'open_menu'} + > = ({ title, prefix, position, style, back, resizeMethod='resize' style={{ width: 16, - height: 16 + height: 16, }} /> diff --git a/packages/mobile/src/components/Appbar/Appbar.stories.tsx b/packages/mobile/src/components/Appbar/Appbar.stories.tsx index 201936c7b7..cbf7d390ce 100644 --- a/packages/mobile/src/components/Appbar/Appbar.stories.tsx +++ b/packages/mobile/src/components/Appbar/Appbar.stories.tsx @@ -1,4 +1,3 @@ - import React from 'react' import { storiesOf } from '@storybook/react-native' @@ -9,9 +8,17 @@ import { Appbar } from './Appbar.component' const contextMenu: ReturnType = { visible: false, handleOpen: function (_args?: any): any {}, - handleClose: function (_args?: any): any {} + handleClose: function (_args?: any): any {}, } storiesOf('Appbar', module) - .add('Channel', () => { console.log('back') }} />) + .add('Channel', () => ( + { + console.log('back') + }} + /> + )) .add('Community', () => ) diff --git a/packages/mobile/src/components/Appbar/Appbar.styles.ts b/packages/mobile/src/components/Appbar/Appbar.styles.ts index 7174d54b14..89f344bffc 100644 --- a/packages/mobile/src/components/Appbar/Appbar.styles.ts +++ b/packages/mobile/src/components/Appbar/Appbar.styles.ts @@ -1,4 +1,3 @@ - import { View } from 'react-native' import styled, { css } from 'styled-components/native' import { defaultTheme } from '../../styles/themes/default.theme' diff --git a/packages/mobile/src/components/Appbar/Appbar.test.tsx b/packages/mobile/src/components/Appbar/Appbar.test.tsx index b28224824e..763fe3a7d1 100644 --- a/packages/mobile/src/components/Appbar/Appbar.test.tsx +++ b/packages/mobile/src/components/Appbar/Appbar.test.tsx @@ -123,12 +123,10 @@ describe('Appbar component', () => { const contextMenu: ReturnType = { visible: false, handleOpen: function (_args?: any): any {}, - handleClose: function (): any {} + handleClose: function (): any {}, } - const { toJSON } = renderComponent( - - ) + const { toJSON } = renderComponent() expect(toJSON()).toMatchInlineSnapshot(` = ({ onPress, title, width, loading, negati event.persist() if (!disabled) onPress() }} - testID={'button'}> + testID={'button'} + > = ({ onPress, title, width, loading, negati justifyContent: 'center', alignItems: 'center', minHeight: 45, - width - }}> + width, + }} + > {!loading ? ( {title} diff --git a/packages/mobile/src/components/Button/Button.stories.tsx b/packages/mobile/src/components/Button/Button.stories.tsx index 6a6575fe0b..3c90500a1d 100644 --- a/packages/mobile/src/components/Button/Button.stories.tsx +++ b/packages/mobile/src/components/Button/Button.stories.tsx @@ -5,20 +5,6 @@ import { storybookLog } from '../../utils/functions/storybookLog/storybookLog.fu import { Button } from './Button.component' storiesOf('Button', module) - .add('Default', () => ( -