From 53f1ec91da07efeb7861e504c4936728ff01062c Mon Sep 17 00:00:00 2001 From: breadhunter <6099829+EmiM@users.noreply.github.com> Date: Mon, 5 Feb 2024 17:51:29 +0100 Subject: [PATCH] fix: make sure local peer's address in in invitation link (#2268) * fix: make sure local peer's address is in the invitation link * fix: running e2e user profile test on ci --- .github/workflows/e2e-win.yml | 1 + CHANGELOG.md | 6 +++ .../connections-manager.service.spec.ts | 17 ++++--- .../backend/src/nest/libp2p/libp2p.service.ts | 8 ++-- .../src/nest/local-db/local-db.service.ts | 15 ++++-- packages/common/src/libp2p.test.ts | 14 +++++- packages/common/src/sortPeers.ts | 17 +++++-- .../Settings/Tabs/Invite/Invite.tsx | 4 +- .../Settings/Tabs/QRCode/QRCode.tsx | 4 +- .../menus/InvitationContextMenu.container.tsx | 4 +- .../src/screens/QRCode/QRCode.screen.tsx | 4 +- .../connection.selectors.test.ts | 48 ++++++++++++++++++- .../appConnection/connection.selectors.ts | 43 ++++++++++++----- .../communities/communities.selectors.test.ts | 43 ----------------- .../communities/communities.selectors.ts | 16 ------- .../src/sagas/identity/identity.selectors.ts | 7 +++ 16 files changed, 150 insertions(+), 101 deletions(-) diff --git a/.github/workflows/e2e-win.yml b/.github/workflows/e2e-win.yml index 92aca8f2a8..4ca8bd6c77 100644 --- a/.github/workflows/e2e-win.yml +++ b/.github/workflows/e2e-win.yml @@ -105,6 +105,7 @@ jobs: with: timeout_minutes: 25 max_attempts: 3 + shell: bash command: cd packages/e2e-tests && npm run test userProfile.test.ts - name: Run invitation link test - Includes 2 separate application clients diff --git a/CHANGELOG.md b/CHANGELOG.md index 95d77f053f..275a24db69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +[unreleased] + +# Fixes: + +* Make sure address of the inviting peer is in the invitation link + [2.1.1] # Fixes: diff --git a/packages/backend/src/nest/connections-manager/connections-manager.service.spec.ts b/packages/backend/src/nest/connections-manager/connections-manager.service.spec.ts index 0736326698..9f4d842a1f 100644 --- a/packages/backend/src/nest/connections-manager/connections-manager.service.spec.ts +++ b/packages/backend/src/nest/connections-manager/connections-manager.service.spec.ts @@ -91,6 +91,10 @@ describe('ConnectionsManagerService', () => { }) it('launches community on init if its data exists in local db', async () => { + const remotePeer = createLibp2pAddress( + 'y7yczmugl2tekami7sbdz5pfaemvx7bahwthrdvcbzw5vex2crsr26qd', + 'QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLsbKSE' + ) const launchCommunityPayload: InitCommunityPayload = { id: community.id, peerId: userIdentity.peerId, @@ -102,12 +106,7 @@ describe('ConnectionsManagerService', () => { key: userIdentity.userCsr?.userKey, CA: [communityRootCa], }, - peers: [ - createLibp2pAddress( - 'y7yczmugl2tekami7sbdz5pfaemvx7bahwthrdvcbzw5vex2crsr26qd', - 'QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLsbKSE' - ), - ], + peers: [remotePeer], } await localDbService.put(LocalDBKeys.COMMUNITY, launchCommunityPayload) @@ -116,7 +115,11 @@ describe('ConnectionsManagerService', () => { const launchCommunitySpy = jest.spyOn(connectionsManagerService, 'launchCommunity').mockResolvedValue() await connectionsManagerService.init() - expect(launchCommunitySpy).toHaveBeenCalledWith(launchCommunityPayload) + + const localPeerAddress = createLibp2pAddress(userIdentity.hiddenService.onionAddress, userIdentity.peerId.id) + const updatedLaunchCommunityPayload = { ...launchCommunityPayload, peers: [localPeerAddress, remotePeer] } + + expect(launchCommunitySpy).toHaveBeenCalledWith(updatedLaunchCommunityPayload) }) it('does not launch community on init if its data does not exist in local db', async () => { diff --git a/packages/backend/src/nest/libp2p/libp2p.service.ts b/packages/backend/src/nest/libp2p/libp2p.service.ts index d427a1d2b6..c4129e9888 100644 --- a/packages/backend/src/nest/libp2p/libp2p.service.ts +++ b/packages/backend/src/nest/libp2p/libp2p.service.ts @@ -5,7 +5,7 @@ import { mplex } from '@libp2p/mplex' import { multiaddr } from '@multiformats/multiaddr' import { Inject, Injectable } from '@nestjs/common' import { createLibp2pAddress, createLibp2pListenAddress } from '@quiet/common' -import { ConnectionProcessInfo, PeerId, SocketActionTypes } from '@quiet/types' +import { ConnectionProcessInfo, type NetworkDataPayload, PeerId, SocketActionTypes } from '@quiet/types' import crypto from 'crypto' import { EventEmitter } from 'events' import { Agent } from 'https' @@ -188,12 +188,12 @@ export class Libp2pService extends EventEmitter { this.connectedPeers.delete(remotePeerId) this.logger(`${localPeerId} is connected to ${this.connectedPeers.size} peers`) - - this.emit(Libp2pEvents.PEER_DISCONNECTED, { + const peerStat: NetworkDataPayload = { peer: remotePeerId, connectionDuration, lastSeen: connectionEndTime, - }) + } + this.emit(Libp2pEvents.PEER_DISCONNECTED, peerStat) }) await this.processInChunksService.process() diff --git a/packages/backend/src/nest/local-db/local-db.service.ts b/packages/backend/src/nest/local-db/local-db.service.ts index e537224e06..327a7b43f1 100644 --- a/packages/backend/src/nest/local-db/local-db.service.ts +++ b/packages/backend/src/nest/local-db/local-db.service.ts @@ -1,10 +1,11 @@ import { Inject, Injectable } from '@nestjs/common' import { Level } from 'level' -import { NetworkStats } from '@quiet/types' -import { filterAndSortPeers } from '@quiet/common' +import { InitCommunityPayload, NetworkStats } from '@quiet/types' +import { createLibp2pAddress, filterAndSortPeers } from '@quiet/common' import { LEVEL_DB } from '../const' import { LocalDBKeys, LocalDbStatus } from './local-db.types' import Logger from '../common/logger' +import { create } from 'mock-fs/lib/filesystem' @Injectable() export class LocalDbService { @@ -35,7 +36,7 @@ export class LocalDbService { try { data = await this.db.get(key) } catch (e) { - this.logger.error(`Getting '${key}'`, e) + this.logger(`Getting '${key}'`, e) return null } return data @@ -74,7 +75,13 @@ export class LocalDbService { public async getSortedPeers(peers: string[] = []): Promise { const peersStats = (await this.get(LocalDBKeys.PEERS)) || {} const stats: NetworkStats[] = Object.values(peersStats) - return filterAndSortPeers(peers, stats) + const community: InitCommunityPayload = await this.get(LocalDBKeys.COMMUNITY) + if (!community) { + return filterAndSortPeers(peers, stats) + } + const localPeerAddress = createLibp2pAddress(community.hiddenService.onionAddress, community.peerId.id) + this.logger('Local peer', localPeerAddress) + return filterAndSortPeers(peers, stats, localPeerAddress) } public async putOwnerOrbitDbIdentity(id: string): Promise { diff --git a/packages/common/src/libp2p.test.ts b/packages/common/src/libp2p.test.ts index 5a623aadb7..db7eee547b 100644 --- a/packages/common/src/libp2p.test.ts +++ b/packages/common/src/libp2p.test.ts @@ -2,6 +2,8 @@ import { filterAndSortPeers } from './sortPeers' describe('filterValidAddresses', () => { it('filters out invalid addresses', () => { + const localAddress = + '/dns4/f3lupwnhaqplbn4djaut5rtipwmlotlb57flfvjzgexek2yezlpjddid.onion/tcp/443/ws/p2p/Qmd35TsAvtskei8zWY3A65ifNWcY4x4SdqkQDHMkH5xPF9' const valid = [ '/dns4/gloao6h5plwjy4tdlze24zzgcxll6upq2ex2fmu2ohhyu4gtys4nrjad.onion/tcp/443/ws/p2p/QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLsbKSE', '/dns4/gloao6h5plwjy4tdlze24zzgcxll6upq2ex2fmu2ohhyu4gtys4nrjad.onion/tcp/80/ws/p2p/QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLsbKSE', @@ -15,6 +17,16 @@ describe('filterValidAddresses', () => { '/dns4/gloao6h5plwjy4tdlze24zzgcxll6upq2ex2fmu2ohhyu4gtys4nrj.onion/tcp/443/ws/p2p/QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLsbKSE', 'QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLsbK', ] - expect(filterAndSortPeers(addresses, [])).toEqual(valid) + expect(filterAndSortPeers(addresses, [], localAddress)).toEqual([localAddress, ...valid]) + }) + + it('sets local address as first without duplicating it', () => { + const localAddress = + '/dns4/gloao6h5plwjy4tdlze24zzgcxll6upq2ex2fmu2ohhyu4gtys4nrjad.onion/tcp/80/ws/p2p/QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLsbKSE' + const addresses = [ + localAddress, + '/dns4/gloao6h5plwjy4tdlze24zzgcxll6upq2ex2fmu2ohhyu4gtys4nrjad.onion/tcp/443/ws/p2p/QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLsbKSE', + ] + expect(filterAndSortPeers(addresses, [], localAddress)).toEqual([localAddress, addresses[1]]) }) }) diff --git a/packages/common/src/sortPeers.ts b/packages/common/src/sortPeers.ts index 2b92cd1ea8..682111c1f2 100644 --- a/packages/common/src/sortPeers.ts +++ b/packages/common/src/sortPeers.ts @@ -9,8 +9,13 @@ This is the very simple algorithm for evaluating the most wanted peers. 2. Two sorted arrays are created - one sorted by last seen and other by most uptime shared. 3. Arrays are merged taking one element from list one and one element from the second list. Duplicates are ommited 4. We end up with mix of last seen and most uptime descending array of peers, the it is enchanced to libp2p address. +5. Prioritize local peer if given */ -export const filterAndSortPeers = (peersAddresses: string[], stats: NetworkStats[]): string[] => { +export const filterAndSortPeers = ( + peersAddresses: string[], + stats: NetworkStats[], + localPeerAddress?: string +): string[] => { peersAddresses = filterValidAddresses(peersAddresses) const lastSeenSorted = [...stats].sort((a, b) => { return b.lastSeen - a.lastSeen @@ -44,8 +49,12 @@ export const filterAndSortPeers = (peersAddresses: string[], stats: NetworkStats }) }) - return peerList - .concat(peersAddresses) - .filter(address => address !== null) + return [ + ...new Set([ + localPeerAddress, // Set local peer as first + ...peerList.concat(peersAddresses), + ]), + ] + .filter(address => address !== null && address !== '') .filter(isDefined) } diff --git a/packages/desktop/src/renderer/components/Settings/Tabs/Invite/Invite.tsx b/packages/desktop/src/renderer/components/Settings/Tabs/Invite/Invite.tsx index fd5ab5f1b3..c9bc95c8d4 100644 --- a/packages/desktop/src/renderer/components/Settings/Tabs/Invite/Invite.tsx +++ b/packages/desktop/src/renderer/components/Settings/Tabs/Invite/Invite.tsx @@ -1,10 +1,10 @@ import React, { FC, useState } from 'react' import { useSelector } from 'react-redux' -import { communities } from '@quiet/state-manager' +import { connection } from '@quiet/state-manager' import { InviteComponent } from './Invite.component' export const Invite: FC = () => { - const invitationLink = useSelector(communities.selectors.invitationUrl) + const invitationLink = useSelector(connection.selectors.invitationUrl) const [revealInputValue, setRevealInputValue] = useState(false) diff --git a/packages/desktop/src/renderer/components/Settings/Tabs/QRCode/QRCode.tsx b/packages/desktop/src/renderer/components/Settings/Tabs/QRCode/QRCode.tsx index 8cbcea0850..de578f5cd9 100644 --- a/packages/desktop/src/renderer/components/Settings/Tabs/QRCode/QRCode.tsx +++ b/packages/desktop/src/renderer/components/Settings/Tabs/QRCode/QRCode.tsx @@ -1,11 +1,11 @@ import React from 'react' import { useSelector } from 'react-redux' -import { communities } from '@quiet/state-manager' +import { connection } from '@quiet/state-manager' import { QRCodeComponent } from './QRCode.component' import { Site } from '@quiet/common' export const QRCode: React.FC = () => { - const invitationLink = useSelector(communities.selectors.invitationUrl) || Site.MAIN_PAGE + const invitationLink = useSelector(connection.selectors.invitationUrl) || Site.MAIN_PAGE return } diff --git a/packages/mobile/src/components/ContextMenu/menus/InvitationContextMenu.container.tsx b/packages/mobile/src/components/ContextMenu/menus/InvitationContextMenu.container.tsx index c092fbc895..ba25067d75 100644 --- a/packages/mobile/src/components/ContextMenu/menus/InvitationContextMenu.container.tsx +++ b/packages/mobile/src/components/ContextMenu/menus/InvitationContextMenu.container.tsx @@ -4,7 +4,7 @@ import { Share } from 'react-native' import Clipboard from '@react-native-clipboard/clipboard' -import { communities } from '@quiet/state-manager' +import { connection } from '@quiet/state-manager' import { navigationSelectors } from '../../../store/navigation/navigation.selectors' @@ -21,7 +21,7 @@ export const InvitationContextMenu: FC = () => { const dispatch = useDispatch() const screen = useSelector(navigationSelectors.currentScreen) - const invitationLink = useSelector(communities.selectors.invitationUrl) + const invitationLink = useSelector(connection.selectors.invitationUrl) const invitationContextMenu = useContextMenu(MenuName.Invitation) diff --git a/packages/mobile/src/screens/QRCode/QRCode.screen.tsx b/packages/mobile/src/screens/QRCode/QRCode.screen.tsx index c4f614d5b0..c29ccfced2 100644 --- a/packages/mobile/src/screens/QRCode/QRCode.screen.tsx +++ b/packages/mobile/src/screens/QRCode/QRCode.screen.tsx @@ -2,7 +2,7 @@ import React, { FC, useCallback, useRef } from 'react' import { useDispatch, useSelector } from 'react-redux' import Share from 'react-native-share' import SVG from 'react-native-svg' -import { communities } from '@quiet/state-manager' +import { connection } from '@quiet/state-manager' import { navigationActions } from '../../store/navigation/navigation.slice' import { ScreenNames } from '../../const/ScreenNames.enum' @@ -14,7 +14,7 @@ export const QRCodeScreen: FC = () => { const svgRef = useRef() - const invitationLink = useSelector(communities.selectors.invitationUrl) || Site.MAIN_PAGE + const invitationLink = useSelector(connection.selectors.invitationUrl) || Site.MAIN_PAGE const handleBackButton = useCallback(() => { dispatch( diff --git a/packages/state-manager/src/sagas/appConnection/connection.selectors.test.ts b/packages/state-manager/src/sagas/appConnection/connection.selectors.test.ts index e48a3896ac..3f298c92ea 100644 --- a/packages/state-manager/src/sagas/appConnection/connection.selectors.test.ts +++ b/packages/state-manager/src/sagas/appConnection/connection.selectors.test.ts @@ -3,10 +3,11 @@ import { type Store } from '@reduxjs/toolkit' import { getFactory } from '../../utils/tests/factories' import { prepareStore } from '../../utils/tests/prepareStore' import { connectionSelectors } from './connection.selectors' -import { type communitiesActions } from '../communities/communities.slice' +import { communitiesActions } from '../communities/communities.slice' import { connectionActions } from './connection.slice' import { type FactoryGirl } from 'factory-girl' import { type Community } from '@quiet/types' +import { createLibp2pAddress, invitationShareUrl } from '@quiet/common' describe('communitiesSelectors', () => { setupCrypto() @@ -31,7 +32,7 @@ describe('communitiesSelectors', () => { ], }) - // This peer should be first in the list as it is the most revently seen one. + // This peer should be first in the list as it is the most recently seen one. store.dispatch( connectionActions.updateNetworkData({ peer: 'Qmd35TsAvtskei8zWY3A65ifNWcY4x4SdqkQDHMkH5xPF9', @@ -91,4 +92,47 @@ describe('communitiesSelectors', () => { expect(socketIOSecret2).toEqual(secret) }) + + it('invitationUrl selector does not break if there is no community', () => { + const { store } = prepareStore() + const invitationUrl = connectionSelectors.invitationUrl(store.getState()) + expect(invitationUrl).toEqual('') + }) + + it('invitationUrl selector returns proper url', async () => { + const { store } = prepareStore() + const factory = await getFactory(store) + const peerList = [ + createLibp2pAddress( + 'gloao6h5plwjy4tdlze24zzgcxll6upq2ex2fmu2ohhyu4gtys4nrjad', + 'QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLsbKSA' + ), + ] + const psk = '12345' + const ownerOrbitDbIdentity = 'testOwnerOrbitDbIdentity' + await factory.create['payload']>('Community', { + peerList, + ownerOrbitDbIdentity, + }) + store.dispatch(communitiesActions.savePSK(psk)) + const selectorInvitationUrl = connectionSelectors.invitationUrl(store.getState()) + const expectedUrl = invitationShareUrl(peerList, psk, ownerOrbitDbIdentity) + expect(expectedUrl).not.toEqual('') + expect(selectorInvitationUrl).toEqual(expectedUrl) + }) + + it('invitationUrl selector returns empty string if state lacks psk', async () => { + const { store } = prepareStore() + const factory = await getFactory(store) + await factory.create['payload']>('Community', { + peerList: [ + createLibp2pAddress( + 'gloao6h5plwjy4tdlze24zzgcxll6upq2ex2fmu2ohhyu4gtys4nrjad', + 'QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLsbKSA' + ), + ], + }) + const invitationUrl = connectionSelectors.invitationUrl(store.getState()) + expect(invitationUrl).toEqual('') + }) }) diff --git a/packages/state-manager/src/sagas/appConnection/connection.selectors.ts b/packages/state-manager/src/sagas/appConnection/connection.selectors.ts index a1ef6a371b..1bce105c4c 100644 --- a/packages/state-manager/src/sagas/appConnection/connection.selectors.ts +++ b/packages/state-manager/src/sagas/appConnection/connection.selectors.ts @@ -2,13 +2,14 @@ import { StoreKeys } from '../store.keys' import { createSelector } from 'reselect' import { type CreatedSelectors, type StoreState } from '../store.types' import { allUsers, areCertificatesLoaded } from '../users/users.selectors' -import { communitiesSelectors } from '../communities/communities.selectors' import { peersStatsAdapter } from './connection.adapter' import { connectedPeers, isCurrentCommunityInitialized } from '../network/network.selectors' import { type NetworkStats } from './connection.types' import { type User } from '../users/users.types' -import { filterAndSortPeers } from '@quiet/common' +import { filterAndSortPeers, invitationShareUrl } from '@quiet/common' import { areMessagesLoaded, areChannelsLoaded } from '../publicChannels/publicChannels.selectors' +import { identitySelectors } from '../identity/identity.selectors' +import { communitiesSelectors } from '../communities/communities.selectors' const connectionSlice: CreatedSelectors[StoreKeys.Connection] = (state: StoreState) => state[StoreKeys.Connection] @@ -22,21 +23,38 @@ export const connectionProcess = createSelector(connectionSlice, reducerState => export const socketIOSecret = createSelector(connectionSlice, reducerState => reducerState.socketIOSecret) +const peerStats = createSelector(connectionSlice, reducerState => { + let stats: NetworkStats[] + if (reducerState.peersStats === undefined) { + stats = [] + } else { + stats = peersStatsAdapter.getSelectors().selectAll(reducerState.peersStats) + } + return stats +}) + export const peerList = createSelector( - connectionSlice, communitiesSelectors.currentCommunity, - (reducerState, community) => { + identitySelectors.currentPeerAddress, + peerStats, + (community, localPeerAddress, stats) => { if (!community) return [] - const arr = [...(community.peerList || [])] - let stats: NetworkStats[] - if (reducerState.peersStats === undefined) { - stats = [] - } else { - stats = peersStatsAdapter.getSelectors().selectAll(reducerState.peersStats) - } + const arr = [...(community.peerList || [])] + return filterAndSortPeers(arr, stats, localPeerAddress) + } +) - return filterAndSortPeers(arr, stats) +export const invitationUrl = createSelector( + communitiesSelectors.psk, + communitiesSelectors.ownerOrbitDbIdentity, + peerList, + (communityPsk, ownerOrbitDbIdentity, sortedPeerList) => { + if (!sortedPeerList || sortedPeerList?.length === 0) return '' + if (!communityPsk) return '' + if (!ownerOrbitDbIdentity) return '' + const initialPeers = sortedPeerList.slice(0, 3) + return invitationShareUrl(initialPeers, communityPsk, ownerOrbitDbIdentity) } ) @@ -70,6 +88,7 @@ export const connectionSelectors = { lastConnectedTime, connectedPeersMapping, peerList, + invitationUrl, torBootstrapProcess, connectionProcess, isTorInitialized, diff --git a/packages/state-manager/src/sagas/communities/communities.selectors.test.ts b/packages/state-manager/src/sagas/communities/communities.selectors.test.ts index 53eba357b2..1cccbaa515 100644 --- a/packages/state-manager/src/sagas/communities/communities.selectors.test.ts +++ b/packages/state-manager/src/sagas/communities/communities.selectors.test.ts @@ -44,49 +44,6 @@ describe('communitiesSelectors', () => { expect(community).toEqual({ ...communityAlpha }) }) - it('invitationUrl selector does not break if there is no community', () => { - const { store } = prepareStore() - const invitationUrl = communitiesSelectors.invitationUrl(store.getState()) - expect(invitationUrl).toEqual('') - }) - - it('invitationUrl selector returns proper url', async () => { - const { store } = prepareStore() - const factory = await getFactory(store) - const peerList = [ - createLibp2pAddress( - 'gloao6h5plwjy4tdlze24zzgcxll6upq2ex2fmu2ohhyu4gtys4nrjad', - 'QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLsbKSA' - ), - ] - const psk = '12345' - const ownerOrbitDbIdentity = 'testOwnerOrbitDbIdentity' - await factory.create['payload']>('Community', { - peerList, - ownerOrbitDbIdentity, - }) - store.dispatch(communitiesActions.savePSK(psk)) - const selectorInvitationUrl = communitiesSelectors.invitationUrl(store.getState()) - const expectedUrl = invitationShareUrl(peerList, psk, ownerOrbitDbIdentity) - expect(expectedUrl).not.toEqual('') - expect(selectorInvitationUrl).toEqual(expectedUrl) - }) - - it('invitationUrl selector returns empty string if state lacks psk', async () => { - const { store } = prepareStore() - const factory = await getFactory(store) - await factory.create['payload']>('Community', { - peerList: [ - createLibp2pAddress( - 'gloao6h5plwjy4tdlze24zzgcxll6upq2ex2fmu2ohhyu4gtys4nrjad', - 'QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLsbKSA' - ), - ], - }) - const invitationUrl = communitiesSelectors.invitationUrl(store.getState()) - expect(invitationUrl).toEqual('') - }) - it('returns proper ownerNickname - ownerCertificate exist', async () => { const { store } = prepareStore() const factory = await getFactory(store) diff --git a/packages/state-manager/src/sagas/communities/communities.selectors.ts b/packages/state-manager/src/sagas/communities/communities.selectors.ts index cbf766d453..2c324ce148 100644 --- a/packages/state-manager/src/sagas/communities/communities.selectors.ts +++ b/packages/state-manager/src/sagas/communities/communities.selectors.ts @@ -2,7 +2,6 @@ import { StoreKeys } from '../store.keys' import { createSelector } from 'reselect' import { communitiesAdapter } from './communities.adapter' import { type CreatedSelectors, type StoreState } from '../store.types' -import { invitationShareUrl } from '@quiet/common' import { CertFieldsTypes, getCertFieldValue, parseCertificate } from '@quiet/identity' // Workaround for "The inferred type of 'communitiesSelectors' cannot be named without a reference to @@ -49,20 +48,6 @@ export const ownerOrbitDbIdentity = createSelector(currentCommunity, currentComm return currentCommunity?.ownerOrbitDbIdentity }) -export const invitationUrl = createSelector( - currentCommunity, - psk, - ownerOrbitDbIdentity, - (community, communityPsk, ownerOrbitDbIdentity) => { - const peerList = community?.peerList - if (!peerList || peerList?.length === 0) return '' - if (!communityPsk) return '' - if (!ownerOrbitDbIdentity) return '' - const initialPeers = peerList.slice(0, 3) - return invitationShareUrl(initialPeers, communityPsk, ownerOrbitDbIdentity) - } -) - export const ownerNickname = createSelector(ownerCertificate, ownerCertificate => { if (!ownerCertificate) return undefined @@ -84,7 +69,6 @@ export const communitiesSelectors = { currentCommunity, currentCommunityId, invitationCodes, - invitationUrl, ownerOrbitDbIdentity, ownerCertificate, ownerNickname, diff --git a/packages/state-manager/src/sagas/identity/identity.selectors.ts b/packages/state-manager/src/sagas/identity/identity.selectors.ts index 8e9b48cc98..0c8e7a1cdf 100644 --- a/packages/state-manager/src/sagas/identity/identity.selectors.ts +++ b/packages/state-manager/src/sagas/identity/identity.selectors.ts @@ -4,6 +4,7 @@ import { identityAdapter } from './identity.adapter' import { type CreatedSelectors, type StoreState } from '../store.types' import { communitiesSelectors, selectCommunities, currentCommunity } from '../communities/communities.selectors' import { certificatesMapping } from '../users/users.selectors' +import { createLibp2pAddress } from '@quiet/common' const identitySlice: CreatedSelectors[StoreKeys.Identity] = (state: StoreState) => state[StoreKeys.Identity] @@ -22,6 +23,11 @@ export const currentIdentity = createSelector( } ) +export const currentPeerAddress = createSelector(currentIdentity, identity => { + if (!identity) return '' + return createLibp2pAddress(identity?.hiddenService.onionAddress, identity?.peerId.id) +}) + export const communityMembership = createSelector(currentIdentity, currentCommunity, (identity, community) => { return Boolean(identity?.userCsr && community?.name) }) @@ -61,6 +67,7 @@ export const identitySelectors = { selectById, selectEntities, currentIdentity, + currentPeerAddress, communityMembership, joinedCommunities, joinTimestamp,