From 52ebcfdbab836965077bfc1de0cab4868f5e2cca Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Wed, 17 Apr 2024 14:56:07 +1000 Subject: [PATCH 1/9] fix: randomly pick a snode topollfrom until we build a better way --- ts/session/apis/snode_api/retrieveRequest.ts | 19 ++++++++---- ts/session/apis/snode_api/swarmPolling.ts | 27 +++++++---------- .../job_runners/jobs/ConfigurationSyncJob.ts | 30 +++++++++++++++++-- 3 files changed, 51 insertions(+), 25 deletions(-) diff --git a/ts/session/apis/snode_api/retrieveRequest.ts b/ts/session/apis/snode_api/retrieveRequest.ts index f8c9d9bdeb..e5ed42639d 100644 --- a/ts/session/apis/snode_api/retrieveRequest.ts +++ b/ts/session/apis/snode_api/retrieveRequest.ts @@ -1,4 +1,4 @@ -import { isArray, omit } from 'lodash'; +import { isArray, omit, sortBy } from 'lodash'; import { Snode } from '../../../data/data'; import { updateIsOnline } from '../../../state/ducks/onion'; import { doSnodeBatchRequest } from './batchRequest'; @@ -167,11 +167,18 @@ async function retrieveNextMessages( GetNetworkTime.handleTimestampOffsetFromNetwork('retrieve', bodyFirstResult.t); // merge results with their corresponding namespaces - return results.map((result, index) => ({ - code: result.code, - messages: result.body as RetrieveMessagesResultsContent, - namespace: namespaces[index], - })); + return results.map((result, index) => { + const messages = result.body as RetrieveMessagesResultsContent; + // Not sure if that makes sense, but we probably want those messages sorted. + const sortedMessages = sortBy(messages.messages, m => m.timestamp); + messages.messages = sortedMessages; + + return { + code: result.code, + messages, + namespace: namespaces[index], + }; + }); } catch (e) { window?.log?.warn('exception while parsing json of nextMessage:', e); if (!window.inboxStore?.getState().onionPaths.isOnline) { diff --git a/ts/session/apis/snode_api/swarmPolling.ts b/ts/session/apis/snode_api/swarmPolling.ts index e8ddcc092f..9b8f0ee7b1 100644 --- a/ts/session/apis/snode_api/swarmPolling.ts +++ b/ts/session/apis/snode_api/swarmPolling.ts @@ -1,7 +1,7 @@ /* eslint-disable no-await-in-loop */ /* eslint-disable more/no-then */ /* eslint-disable @typescript-eslint/no-misused-promises */ -import { compact, concat, difference, flatten, last, sample, toNumber, uniqBy } from 'lodash'; +import { compact, concat, flatten, last, sample, toNumber, uniqBy } from 'lodash'; import { Data, Snode } from '../../../data/data'; import { SignalService } from '../../../protobuf'; import * as Receiver from '../../../receiver/receiver'; @@ -24,7 +24,6 @@ import { getConversationController } from '../../conversations'; import { IncomingMessage } from '../../messages/incoming/IncomingMessage'; import { ed25519Str } from '../../onions/onionPath'; import { StringUtils, UserUtils } from '../../utils'; -import { perfEnd, perfStart } from '../../utils/Performance'; import { LibSessionUtil } from '../../utils/libsession/libsession_utils'; import { SnodeNamespace, SnodeNamespaces } from './namespaces'; import { SnodeAPIRetrieve } from './retrieveRequest'; @@ -228,21 +227,16 @@ export class SwarmPolling { namespaces: Array ) { const polledPubkey = pubkey.key; + let resultsFromAllNamespaces: RetrieveMessagesResultsBatched | null; const swarmSnodes = await snodePool.getSwarmFor(polledPubkey); - - // Select nodes for which we already have lastHashes - const alreadyPolled = swarmSnodes.filter((n: Snode) => this.lastHashes[n.pubkey_ed25519]); - let toPollFrom = alreadyPolled.length ? alreadyPolled[0] : null; - - // If we need more nodes, select randomly from the remaining nodes: - if (!toPollFrom) { - const notPolled = difference(swarmSnodes, alreadyPolled); - toPollFrom = sample(notPolled) as Snode; - } - - let resultsFromAllNamespaces: RetrieveMessagesResultsBatched | null; + let toPollFrom: Snode | undefined; try { + toPollFrom = sample(swarmSnodes); + + if (!toPollFrom) { + throw new Error(`pollOnceForKey: no snode in swarm for ${ed25519Str(polledPubkey)}`); + } // Note: always print something so we know if the polling is hanging window.log.info( `about to pollNodeForKey of ${ed25519Str(pubkey.key)} from snode: ${ed25519Str(toPollFrom.pubkey_ed25519)} namespaces: ${namespaces} ` @@ -337,9 +331,10 @@ export class SwarmPolling { }); } - perfStart(`handleSeenMessages-${polledPubkey}`); const newMessages = await this.handleSeenMessages(messages); - perfEnd(`handleSeenMessages-${polledPubkey}`, 'handleSeenMessages'); + window.log.info( + `handleSeenMessages: ${newMessages.length} out of ${messages.length} are not seen yet. snode: ${toPollFrom ? ed25519Str(toPollFrom.pubkey_ed25519) : 'undefined'}` + ); // don't handle incoming messages from group swarms when using the userconfig and the group is not one of the tracked group const isUserConfigReleaseLive = await ReleasedFeatures.checkIsUserConfigFeatureReleased(); diff --git a/ts/session/utils/job_runners/jobs/ConfigurationSyncJob.ts b/ts/session/utils/job_runners/jobs/ConfigurationSyncJob.ts index aaae284d15..0e94040c08 100644 --- a/ts/session/utils/job_runners/jobs/ConfigurationSyncJob.ts +++ b/ts/session/utils/job_runners/jobs/ConfigurationSyncJob.ts @@ -1,14 +1,18 @@ /* eslint-disable no-await-in-loop */ +import { to_hex } from 'libsodium-wrappers-sumo'; import { compact, isArray, isEmpty, isNumber, isString } from 'lodash'; import { v4 } from 'uuid'; import { UserUtils } from '../..'; import { ConfigDumpData } from '../../../../data/configDump/configDump'; import { ConfigurationSyncJobDone } from '../../../../shims/events'; +import { ReleasedFeatures } from '../../../../util/releaseFeature'; +import { isSignInByLinking } from '../../../../util/storage'; import { GenericWrapperActions } from '../../../../webworker/workers/browser/libsession_worker_interface'; import { NotEmptyArrayOfBatchResults } from '../../../apis/snode_api/SnodeRequestTypes'; import { getConversationController } from '../../../conversations'; import { SharedConfigMessage } from '../../../messages/outgoing/controlMessage/SharedConfigMessage'; import { MessageSender } from '../../../sending/MessageSender'; +import { allowOnlyOneAtATime } from '../../Promise'; import { LibSessionUtil, OutgoingConfResult } from '../../libsession/libsession_utils'; import { runners } from '../JobRunner'; import { @@ -17,9 +21,6 @@ import { PersistedJob, RunJobResult, } from '../PersistedJob'; -import { ReleasedFeatures } from '../../../../util/releaseFeature'; -import { allowOnlyOneAtATime } from '../../Promise'; -import { isSignInByLinking } from '../../../../util/storage'; const defaultMsBetweenRetries = 15000; // a long time between retries, to avoid running multiple jobs at the same time, when one was postponed at the same time as one already planned (5s) const defaultMaxAttempts = 2; @@ -208,6 +209,29 @@ class ConfigurationSyncJob extends PersistedJob }; }); + if (window.sessionFeatureFlags.debug.debugLibsessionDumps) { + for (let index = 0; index < LibSessionUtil.requiredUserVariants.length; index++) { + const variant = LibSessionUtil.requiredUserVariants[index]; + + window.log.info( + `ConfigurationSyncJob: current dumps: ${variant}:`, + to_hex(await GenericWrapperActions.dump(variant)) + ); + } + window.log.info( + 'ConfigurationSyncJob: About to push changes: ', + msgs.map(m => { + return { + ...m, + message: { + ...m.message, + data: to_hex(m.message.data), + }, + }; + }) + ); + } + const result = await MessageSender.sendMessagesToSnode( msgs, thisJobDestination, From 87fb1701d4e9720cbd8ff9d0076292d82ea9196b Mon Sep 17 00:00:00 2001 From: Ryan Miller Date: Mon, 22 Apr 2024 14:47:07 +1000 Subject: [PATCH 2/9] chore: rename fetch functions --- ts/components/DebugLogView.tsx | 4 ++-- ts/node/logging.ts | 4 ++-- ts/util/logging.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ts/components/DebugLogView.tsx b/ts/components/DebugLogView.tsx index 3caa50b485..5ffd340130 100644 --- a/ts/components/DebugLogView.tsx +++ b/ts/components/DebugLogView.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react'; import styled from 'styled-components'; import { switchThemeTo } from '../themes/switchTheme'; import { SessionTheme } from '../themes/SessionTheme'; -import { fetch } from '../util/logging'; +import { fetchNodeLog } from '../util/logging'; import { SessionButton, SessionButtonType } from './basic/SessionButton'; import { SessionIconButton } from './icon'; @@ -78,7 +78,7 @@ const DebugLogViewAndSave = () => { const commitHashInfo = window.getCommitHash() ? `Commit Hash: ${window.getCommitHash()}` : ''; // eslint-disable-next-line more/no-then - fetch() + fetchNodeLog() .then((text: any) => { const debugLogWithSystemInfo = `${operatingSystemInfo} ${commitHashInfo} ${text}`; setContent(debugLogWithSystemInfo); diff --git a/ts/node/logging.ts b/ts/node/logging.ts index f256d4f82d..1f141bedba 100644 --- a/ts/node/logging.ts +++ b/ts/node/logging.ts @@ -63,7 +63,7 @@ export async function initializeLogger() { fs.mkdirSync(logPath, { recursive: true }); console.info('fetching logs from logPath'); - fetch(logPath).then( + fetchLogFile(logPath).then( data => { event.sender.send('fetched-log', data); }, @@ -218,7 +218,7 @@ async function fetchLog(logFile: string) { }); } -export async function fetch(logPath: string) { +export async function fetchLogFile(logPath: string) { // Check that the file exists locally if (!fs.existsSync(logPath)) { (console as ConsoleCustom)._log( diff --git a/ts/util/logging.ts b/ts/util/logging.ts index 83c4945808..b82cfd8263 100644 --- a/ts/util/logging.ts +++ b/ts/util/logging.ts @@ -94,7 +94,7 @@ function format(entries: Array) { return redactAll(entries.map(formatLine).join('\n')); } -export async function fetch() { +export async function fetchNodeLog() { return new Promise(resolve => { ipc.on('fetched-log', (_event, text) => { const result = `${getHeader()}\n${format(text)}`; From 7f7f0fe26cf5f779ed0db62cf9a4969f75d13ae3 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Wed, 24 Apr 2024 13:57:47 +1000 Subject: [PATCH 3/9] fix: clear swarms from snodes not in pool on full fetch --- .gitignore | 2 ++ package.json | 12 ++++---- ts/components/calling/IncomingCallDialog.tsx | 2 +- ts/components/dialog/DeleteAccountModal.tsx | 3 +- ts/data/data.ts | 5 ++++ ts/data/dataInit.ts | 1 + .../conversations/unsendingInteractions.ts | 2 +- ts/models/conversation.ts | 3 +- ts/node/sql.ts | 29 +++++++++++++++++++ ts/session/apis/snode_api/SNodeAPI.ts | 3 +- ts/session/apis/snode_api/onions.ts | 4 +-- ts/session/apis/snode_api/retrieveRequest.ts | 2 +- ts/session/apis/snode_api/snodePool.ts | 15 +++++++++- ts/session/apis/snode_api/swarmPolling.ts | 2 +- ts/session/onions/onionPath.ts | 3 +- ts/session/sending/MessageSender.ts | 3 +- ts/session/utils/String.ts | 2 ++ ts/session/utils/calling/CallManager.ts | 8 +++-- 18 files changed, 77 insertions(+), 24 deletions(-) diff --git a/.gitignore b/.gitignore index 27cf464dbe..46770cfbbc 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,5 @@ stylesheets/dist/ *.LICENSE.txt ts/webworker/workers/node/**/*.node +.yarn/**/*.mjs +.yarn/**/*.cjs diff --git a/package.json b/package.json index 7c68497006..acb4abb618 100644 --- a/package.json +++ b/package.json @@ -216,11 +216,13 @@ "afterSign": "build/notarize.js", "afterPack": "build/afterPackHook.js", "artifactName": "${name}-${os}-${arch}-${version}.${ext}", - "extraResources": [{ - "from": "./build/launcher-script.sh", - "to": "./launcher-script.sh" - }, - "mmdb/GeoLite2-Country.mmdb"], + "extraResources": [ + { + "from": "./build/launcher-script.sh", + "to": "./launcher-script.sh" + }, + "mmdb/GeoLite2-Country.mmdb" + ], "mac": { "category": "public.app-category.social-networking", "icon": "build/icon-mac.icns", diff --git a/ts/components/calling/IncomingCallDialog.tsx b/ts/components/calling/IncomingCallDialog.tsx index ccaba5c465..a3ec501d58 100644 --- a/ts/components/calling/IncomingCallDialog.tsx +++ b/ts/components/calling/IncomingCallDialog.tsx @@ -3,13 +3,13 @@ import { useSelector } from 'react-redux'; import styled from 'styled-components'; import { useConversationUsername } from '../../hooks/useParamSelector'; -import { ed25519Str } from '../../session/onions/onionPath'; import { CallManager } from '../../session/utils'; import { callTimeoutMs } from '../../session/utils/calling/CallManager'; import { getHasIncomingCall, getHasIncomingCallFrom } from '../../state/selectors/call'; import { Avatar, AvatarSize } from '../avatar/Avatar'; import { SessionButton, SessionButtonColor, SessionButtonType } from '../basic/SessionButton'; import { SessionWrapperModal } from '../SessionWrapperModal'; +import { ed25519Str } from '../../session/utils/String'; export const CallWindow = styled.div` position: absolute; diff --git a/ts/components/dialog/DeleteAccountModal.tsx b/ts/components/dialog/DeleteAccountModal.tsx index b11dd35168..3f394850ca 100644 --- a/ts/components/dialog/DeleteAccountModal.tsx +++ b/ts/components/dialog/DeleteAccountModal.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useState } from 'react'; import { useDispatch } from 'react-redux'; import { SnodeAPI } from '../../session/apis/snode_api/SNodeAPI'; -import { ed25519Str } from '../../session/onions/onionPath'; + import { forceSyncConfigurationNowIfNeeded } from '../../session/utils/sync/syncUtils'; import { updateConfirmModal, updateDeleteAccountModal } from '../../state/ducks/modalDialog'; import { SessionWrapperModal } from '../SessionWrapperModal'; @@ -14,6 +14,7 @@ import { deleteAllLogs } from '../../node/logs'; import { clearInbox } from '../../session/apis/open_group_api/sogsv3/sogsV3ClearInbox'; import { getAllValidOpenGroupV2ConversationRoomInfos } from '../../session/apis/open_group_api/utils/OpenGroupUtils'; import { SessionRadioGroup } from '../basic/SessionRadioGroup'; +import { ed25519Str } from '../../session/utils/String'; const deleteDbLocally = async () => { window?.log?.info('last message sent successfully. Deleting everything'); diff --git a/ts/data/data.ts b/ts/data/data.ts index e5190e9970..ed585f7f37 100644 --- a/ts/data/data.ts +++ b/ts/data/data.ts @@ -109,6 +109,10 @@ async function updateSwarmNodesForPubkey( await channels.updateSwarmNodesForPubkey(pubkey, snodeEdKeys); } +async function clearOutAllSnodesNotInPool(edKeysOfSnodePool: Array): Promise { + await channels.clearOutAllSnodesNotInPool(edKeysOfSnodePool); +} + // Closed group /** @@ -802,6 +806,7 @@ export const Data = { generateAttachmentKeyIfEmpty, getSwarmNodesForPubkey, updateSwarmNodesForPubkey, + clearOutAllSnodesNotInPool, getAllEncryptionKeyPairsForGroup, getLatestClosedGroupEncryptionKeyPair, addClosedGroupEncryptionKeyPair, diff --git a/ts/data/dataInit.ts b/ts/data/dataInit.ts index abf1c3f43e..3577c9f548 100644 --- a/ts/data/dataInit.ts +++ b/ts/data/dataInit.ts @@ -24,6 +24,7 @@ const channelsToMake = new Set([ 'removeItemById', 'getSwarmNodesForPubkey', 'updateSwarmNodesForPubkey', + 'clearOutAllSnodesNotInPool', 'saveConversation', 'fetchConvoMemoryDetails', 'getConversationById', diff --git a/ts/interactions/conversations/unsendingInteractions.ts b/ts/interactions/conversations/unsendingInteractions.ts index 828c059032..fae67ea3ab 100644 --- a/ts/interactions/conversations/unsendingInteractions.ts +++ b/ts/interactions/conversations/unsendingInteractions.ts @@ -9,12 +9,12 @@ import { SnodeAPI } from '../../session/apis/snode_api/SNodeAPI'; import { SnodeNamespaces } from '../../session/apis/snode_api/namespaces'; import { getConversationController } from '../../session/conversations'; import { UnsendMessage } from '../../session/messages/outgoing/controlMessage/UnsendMessage'; -import { ed25519Str } from '../../session/onions/onionPath'; import { PubKey } from '../../session/types'; import { ToastUtils, UserUtils } from '../../session/utils'; import { closeRightPanel, resetSelectedMessageIds } from '../../state/ducks/conversations'; import { updateConfirmModal } from '../../state/ducks/modalDialog'; import { resetRightOverlayMode } from '../../state/ducks/section'; +import { ed25519Str } from '../../session/utils/String'; /** * Deletes messages for everyone in a 1-1 or everyone in a closed group conversation. diff --git a/ts/models/conversation.ts b/ts/models/conversation.ts index 096fd86d40..794a8c67e5 100644 --- a/ts/models/conversation.ts +++ b/ts/models/conversation.ts @@ -42,7 +42,7 @@ import { VisibleMessageParams, } from '../session/messages/outgoing/visibleMessage/VisibleMessage'; import { perfEnd, perfStart } from '../session/utils/Performance'; -import { toHex } from '../session/utils/String'; +import { ed25519Str, toHex } from '../session/utils/String'; import { createTaskWithTimeout } from '../session/utils/TaskWithTimeout'; import { actions as conversationActions, @@ -74,7 +74,6 @@ import { MessageRequestResponse, MessageRequestResponseParams, } from '../session/messages/outgoing/controlMessage/MessageRequestResponse'; -import { ed25519Str } from '../session/onions/onionPath'; import { ConfigurationSync } from '../session/utils/job_runners/jobs/ConfigurationSyncJob'; import { SessionUtilContact } from '../session/utils/libsession/libsession_utils_contacts'; import { SessionUtilConvoInfoVolatile } from '../session/utils/libsession/libsession_utils_convo_info_volatile'; diff --git a/ts/node/sql.ts b/ts/node/sql.ts index 52323b03a8..80fcecbfd7 100644 --- a/ts/node/sql.ts +++ b/ts/node/sql.ts @@ -12,6 +12,7 @@ import { differenceBy, forEach, fromPairs, + intersection, isArray, isEmpty, isNumber, @@ -78,6 +79,7 @@ import { initDbInstanceWith, isInstanceInitialized, } from './sqlInstance'; +import { ed25519Str } from '../session/utils/String'; // eslint:disable: function-name non-literal-fs-path @@ -398,6 +400,32 @@ function updateSwarmNodesForPubkey(pubkey: string, snodeEdKeys: Array) { }); } +function clearOutAllSnodesNotInPool(edKeysOfSnodePool: Array) { + const allSwarms = assertGlobalInstance() + .prepare(`SELECT * FROM ${NODES_FOR_PUBKEY_TABLE};`) + .all(); + + allSwarms.forEach(swarm => { + try { + const json = JSON.parse(swarm.json); + if (isArray(json)) { + const intersect = intersection(json, edKeysOfSnodePool); + if (intersect.length !== json.length) { + updateSwarmNodesForPubkey(swarm.pubkey, intersect); + console.info( + `clearOutAllSnodesNotInPool: updating swarm of ${ed25519Str(swarm.pubkey)} to `, + intersect + ); + } + } + } catch (e) { + console.warn( + `Failed to parse swarm while iterating in clearOutAllSnodesNotInPool for pk: ${ed25519Str(swarm?.pubkey)}` + ); + } + }); +} + function getConversationCount() { const row = assertGlobalInstance().prepare(`SELECT count(*) from ${CONVERSATIONS_TABLE};`).get(); if (!row) { @@ -2449,6 +2477,7 @@ export const sqlNode = { getSwarmNodesForPubkey, updateSwarmNodesForPubkey, + clearOutAllSnodesNotInPool, getGuardNodes, updateGuardNodes, diff --git a/ts/session/apis/snode_api/SNodeAPI.ts b/ts/session/apis/snode_api/SNodeAPI.ts index 2eeefde012..91f214e7c9 100644 --- a/ts/session/apis/snode_api/SNodeAPI.ts +++ b/ts/session/apis/snode_api/SNodeAPI.ts @@ -4,9 +4,8 @@ import { compact, sample } from 'lodash'; import pRetry from 'p-retry'; import { Snode } from '../../../data/data'; import { getSodiumRenderer } from '../../crypto'; -import { ed25519Str } from '../../onions/onionPath'; import { StringUtils, UserUtils } from '../../utils'; -import { fromBase64ToArray, fromHexToArray } from '../../utils/String'; +import { ed25519Str, fromBase64ToArray, fromHexToArray } from '../../utils/String'; import { doSnodeBatchRequest } from './batchRequest'; import { getSwarmFor } from './snodePool'; import { SnodeSignature } from './snodeSignatures'; diff --git a/ts/session/apis/snode_api/onions.ts b/ts/session/apis/snode_api/onions.ts index 1478bce85f..4f2c1750a0 100644 --- a/ts/session/apis/snode_api/onions.ts +++ b/ts/session/apis/snode_api/onions.ts @@ -11,8 +11,8 @@ import { AbortSignal as AbortSignalNode } from 'node-fetch/externals'; import { dropSnodeFromSnodePool, dropSnodeFromSwarmIfNeeded, updateSwarmFor } from './snodePool'; import { OnionPaths } from '../../onions'; -import { ed25519Str, incrementBadPathCountOrDrop } from '../../onions/onionPath'; -import { toHex } from '../../utils/String'; +import { incrementBadPathCountOrDrop } from '../../onions/onionPath'; +import { ed25519Str, toHex } from '../../utils/String'; import { Snode } from '../../../data/data'; import { callUtilsWorker } from '../../../webworker/workers/browser/util_worker_interface'; diff --git a/ts/session/apis/snode_api/retrieveRequest.ts b/ts/session/apis/snode_api/retrieveRequest.ts index e5ed42639d..5dfcaf007c 100644 --- a/ts/session/apis/snode_api/retrieveRequest.ts +++ b/ts/session/apis/snode_api/retrieveRequest.ts @@ -124,7 +124,7 @@ async function retrieveNextMessages( ); // let exceptions bubble up // no retry for this one as this a call we do every few seconds while polling for messages - const timeOutMs = 4 * 1000; + const timeOutMs = 10 * 1000; // yes this is a long timeout for just messages, but 4s timeout way to often... const timeoutPromise = async () => sleepFor(timeOutMs); const fetchPromise = async () => doSnodeBatchRequest(retrieveRequestsParams, targetNode, timeOutMs, associatedWith); diff --git a/ts/session/apis/snode_api/snodePool.ts b/ts/session/apis/snode_api/snodePool.ts index f13a27e218..20a029d2ee 100644 --- a/ts/session/apis/snode_api/snodePool.ts +++ b/ts/session/apis/snode_api/snodePool.ts @@ -3,12 +3,12 @@ import pRetry from 'p-retry'; import { Data, Snode } from '../../../data/data'; -import { ed25519Str } from '../../onions/onionPath'; import { OnionPaths } from '../../onions'; import { Onions, SnodePool } from '.'; import { SeedNodeAPI } from '../seed_node_api'; import { requestSnodesForPubkeyFromNetwork } from './getSwarmFor'; import { ServiceNodesList } from './getServiceNodesList'; +import { ed25519Str } from '../../utils/String'; /** * If we get less than this snode in a swarm, we fetch new snodes for this pubkey @@ -204,6 +204,18 @@ export async function TEST_fetchFromSeedWithRetriesAndWriteToDb() { } } +async function clearOutAllSnodesNotInPool(snodePool: Array) { + if (snodePool.length <= 10) { + return; + } + const edKeysOfSnodePool = snodePool.map(m => m.pubkey_ed25519); + + await Data.clearOutAllSnodesNotInPool(edKeysOfSnodePool); + + // just remove all the cached entries, we will refetch them as needed from the DB + swarmCache.clear(); +} + /** * This function retries a few times to get a consensus between 3 snodes of at least 24 snodes in the snode pool. * @@ -230,6 +242,7 @@ async function tryToGetConsensusWithSnodesWithRetries() { ); randomSnodePool = commonNodes; await Data.updateSnodePoolOnDb(JSON.stringify(randomSnodePool)); + await clearOutAllSnodesNotInPool(randomSnodePool); OnionPaths.resetPathFailureCount(); Onions.resetSnodeFailureCount(); diff --git a/ts/session/apis/snode_api/swarmPolling.ts b/ts/session/apis/snode_api/swarmPolling.ts index 9b8f0ee7b1..cedebc9378 100644 --- a/ts/session/apis/snode_api/swarmPolling.ts +++ b/ts/session/apis/snode_api/swarmPolling.ts @@ -22,12 +22,12 @@ import { import { DURATION, SWARM_POLLING_TIMEOUT } from '../../constants'; import { getConversationController } from '../../conversations'; import { IncomingMessage } from '../../messages/incoming/IncomingMessage'; -import { ed25519Str } from '../../onions/onionPath'; import { StringUtils, UserUtils } from '../../utils'; import { LibSessionUtil } from '../../utils/libsession/libsession_utils'; import { SnodeNamespace, SnodeNamespaces } from './namespaces'; import { SnodeAPIRetrieve } from './retrieveRequest'; import { RetrieveMessageItem, RetrieveMessagesResultsBatched } from './types'; +import { ed25519Str } from '../../utils/String'; export function extractWebSocketContent( message: string, diff --git a/ts/session/onions/onionPath.ts b/ts/session/onions/onionPath.ts index d05229976d..84b9d14a18 100644 --- a/ts/session/onions/onionPath.ts +++ b/ts/session/onions/onionPath.ts @@ -14,6 +14,7 @@ import { updateOnionPaths } from '../../state/ducks/onion'; import { ERROR_CODE_NO_CONNECT } from '../apis/snode_api/SNodeAPI'; import { OnionPaths } from '.'; import { APPLICATION_JSON } from '../../types/MIME'; +import { ed25519Str } from '../utils/String'; const desiredGuardCount = 3; const minimumGuardCount = 2; @@ -63,8 +64,6 @@ const pathFailureThreshold = 3; // some naming issue here it seems) export let guardNodes: Array = []; -export const ed25519Str = (ed25519Key: string) => `(...${ed25519Key.substr(58)})`; - export async function buildNewOnionPathsOneAtATime() { // this function may be called concurrently make sure we only have one inflight return allowOnlyOneAtATime('buildNewOnionPaths', async () => { diff --git a/ts/session/sending/MessageSender.ts b/ts/session/sending/MessageSender.ts index e2dcdf4ab5..b7cbac6016 100644 --- a/ts/session/sending/MessageSender.ts +++ b/ts/session/sending/MessageSender.ts @@ -32,11 +32,10 @@ import { SharedConfigMessage } from '../messages/outgoing/controlMessage/SharedC import { UnsendMessage } from '../messages/outgoing/controlMessage/UnsendMessage'; import { ClosedGroupNewMessage } from '../messages/outgoing/controlMessage/group/ClosedGroupNewMessage'; import { OpenGroupVisibleMessage } from '../messages/outgoing/visibleMessage/OpenGroupVisibleMessage'; -import { ed25519Str } from '../onions/onionPath'; import { PubKey } from '../types'; import { RawMessage } from '../types/RawMessage'; import { UserUtils } from '../utils'; -import { fromUInt8ArrayToBase64 } from '../utils/String'; +import { ed25519Str, fromUInt8ArrayToBase64 } from '../utils/String'; import { EmptySwarmError } from '../utils/errors'; // ================ SNODE STORE ================ diff --git a/ts/session/utils/String.ts b/ts/session/utils/String.ts index b12f099715..68713e5e5d 100644 --- a/ts/session/utils/String.ts +++ b/ts/session/utils/String.ts @@ -71,3 +71,5 @@ export const sanitizeSessionUsername = (inputName: string) => { return validChars; }; + +export const ed25519Str = (ed25519Key: string) => `(...${ed25519Key.substr(58)})`; diff --git a/ts/session/utils/calling/CallManager.ts b/ts/session/utils/calling/CallManager.ts index cc411f0aa8..b584062976 100644 --- a/ts/session/utils/calling/CallManager.ts +++ b/ts/session/utils/calling/CallManager.ts @@ -18,7 +18,6 @@ import { import { openConversationWithMessages } from '../../../state/ducks/conversations'; import { getConversationController } from '../../conversations'; import { CallMessage } from '../../messages/outgoing/controlMessage/CallMessage'; -import { ed25519Str } from '../../onions/onionPath'; import { PubKey } from '../../types'; import { getMessageQueue } from '../..'; @@ -35,6 +34,7 @@ import { ReadyToDisappearMsgUpdate } from '../../disappearing_messages/types'; import { MessageSender } from '../../sending'; import { getIsRinging } from '../RingingManager'; import { getBlackSilenceMediaStream } from './Silence'; +import { ed25519Str } from '../String'; export type InputItem = { deviceId: string; label: string }; @@ -415,8 +415,10 @@ async function createOfferAndSendIt(recipient: string, msgIdentifier: string | n if (offer && offer.sdp) { const lines = offer.sdp.split(/\r?\n/); const lineWithFtmpIndex = lines.findIndex(f => f.startsWith('a=fmtp:111')); - const partBeforeComma = lines[lineWithFtmpIndex].split(';'); - lines[lineWithFtmpIndex] = `${partBeforeComma[0]};cbr=1`; + if (lineWithFtmpIndex > -1) { + const partBeforeComma = lines[lineWithFtmpIndex].split(';'); + lines[lineWithFtmpIndex] = `${partBeforeComma[0]};cbr=1`; + } let overridenSdps = lines.join('\n'); overridenSdps = overridenSdps.replace( // eslint-disable-next-line prefer-regex-literals From 49ab04d2fdaea921ff40fd775223440e17ba8a9f Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Wed, 24 Apr 2024 15:31:26 +1000 Subject: [PATCH 4/9] chore: move SessionSettingsCategory to .d.ts file and remove the enum (instead use string union) --- .../leftpane/LeftPaneSettingSection.tsx | 30 +++++++++--------- ts/components/settings/SessionSettings.tsx | 31 ++++++------------- .../settings/SessionSettingsHeader.tsx | 20 ++++++------ ts/session/utils/Toast.tsx | 3 +- ts/state/createStore.ts | 2 +- ts/state/ducks/section.tsx | 7 +++-- ts/state/reducer.ts | 21 +++++++------ ts/state/selectors/section.ts | 2 +- ts/types/SessionSettingCategory.d.ts | 10 ++++++ 9 files changed, 63 insertions(+), 63 deletions(-) create mode 100644 ts/types/SessionSettingCategory.d.ts diff --git a/ts/components/leftpane/LeftPaneSettingSection.tsx b/ts/components/leftpane/LeftPaneSettingSection.tsx index b2dd84315b..9efe37ba78 100644 --- a/ts/components/leftpane/LeftPaneSettingSection.tsx +++ b/ts/components/leftpane/LeftPaneSettingSection.tsx @@ -12,8 +12,8 @@ import { } from '../../state/ducks/section'; import { getFocusedSettingsSection } from '../../state/selectors/section'; import { SessionIcon } from '../icon'; -import { SessionSettingCategory } from '../settings/SessionSettings'; import { LeftPaneSectionHeader } from './LeftPaneSectionHeader'; +import type { SessionSettingCategory } from '../../types/SessionSettingCategory'; const StyledSettingsSectionTitle = styled.strong` font-family: var(--font-accent), var(--font-default); @@ -42,42 +42,42 @@ const StyledSettingsListItem = styled.div<{ active: boolean }>` } `; -const getCategories = () => { +const getCategories = (): Array<{ id: SessionSettingCategory; title: string }> => { return [ { - id: SessionSettingCategory.Privacy, + id: 'privacy' as const, title: window.i18n('privacySettingsTitle'), }, { - id: SessionSettingCategory.Notifications, + id: 'notifications' as const, title: window.i18n('notificationsSettingsTitle'), }, { - id: SessionSettingCategory.Conversations, + id: 'conversations' as const, title: window.i18n('conversationsSettingsTitle'), }, { - id: SessionSettingCategory.MessageRequests, + id: 'messageRequests' as const, title: window.i18n('openMessageRequestInbox'), }, { - id: SessionSettingCategory.Appearance, + id: 'appearance' as const, title: window.i18n('appearanceSettingsTitle'), }, { - id: SessionSettingCategory.Permissions, + id: 'permissions', title: window.i18n('permissionsSettingsTitle'), }, { - id: SessionSettingCategory.Help, + id: 'help' as const, title: window.i18n('helpSettingsTitle'), }, { - id: SessionSettingCategory.RecoveryPhrase, + id: 'recoveryPhrase' as const, title: window.i18n('recoveryPhrase'), }, { - id: SessionSettingCategory.ClearData, + id: 'ClearData' as const, title: window.i18n('clearDataSettingsTitle'), }, ]; @@ -93,7 +93,7 @@ const LeftPaneSettingsCategoryRow = (props: { const dataTestId = `${title.toLowerCase().replace(' ', '-')}-settings-menu-item`; - const isClearData = id === SessionSettingCategory.ClearData; + const isClearData = id === 'ClearData'; return ( { switch (id) { - case SessionSettingCategory.MessageRequests: + case 'messageRequests': dispatch(showLeftPaneSection(SectionType.Message)); dispatch(setLeftOverlayMode('message-requests')); dispatch(resetConversationExternal()); break; - case SessionSettingCategory.RecoveryPhrase: + case 'recoveryPhrase': dispatch(recoveryPhraseModal({})); break; - case SessionSettingCategory.ClearData: + case 'ClearData': dispatch(updateDeleteAccountModal({})); break; default: diff --git a/ts/components/settings/SessionSettings.tsx b/ts/components/settings/SessionSettings.tsx index 99e4867d75..5a2b919f23 100644 --- a/ts/components/settings/SessionSettings.tsx +++ b/ts/components/settings/SessionSettings.tsx @@ -19,6 +19,7 @@ import { CategoryConversations } from './section/CategoryConversations'; import { SettingsCategoryHelp } from './section/CategoryHelp'; import { SettingsCategoryPermissions } from './section/CategoryPermissions'; import { SettingsCategoryPrivacy } from './section/CategoryPrivacy'; +import type { SessionSettingCategory } from '../../types/SessionSettingCategory'; export function displayPasswordModal( passwordAction: PasswordAction, @@ -42,18 +43,6 @@ export function getCallMediaPermissionsSettings() { return window.getSettingValue('call-media-permissions'); } -export enum SessionSettingCategory { - Privacy = 'privacy', - Notifications = 'notifications', - Conversations = 'conversations', - MessageRequests = 'messageRequests', - Appearance = 'appearance', - Permissions = 'permissions', - Help = 'help', - RecoveryPhrase = 'recoveryPhrase', - ClearData = 'ClearData', -} - export interface SettingsViewProps { category: SessionSettingCategory; } @@ -113,25 +102,25 @@ const SettingInCategory = (props: { switch (category) { // special case for blocked user - case SessionSettingCategory.Conversations: + case 'conversations': return ; - case SessionSettingCategory.Appearance: + case 'appearance': return ; - case SessionSettingCategory.Notifications: + case 'notifications': return ; - case SessionSettingCategory.Privacy: + case 'privacy': return ( ); - case SessionSettingCategory.Help: + case 'help': return ; - case SessionSettingCategory.Permissions: + case 'permissions': return ; // these three down there have no options, they are just a button - case SessionSettingCategory.ClearData: - case SessionSettingCategory.MessageRequests: - case SessionSettingCategory.RecoveryPhrase: + case 'ClearData': + case 'messageRequests': + case 'recoveryPhrase': default: return null; } diff --git a/ts/components/settings/SessionSettingsHeader.tsx b/ts/components/settings/SessionSettingsHeader.tsx index 2f6f357726..91b0cefe5e 100644 --- a/ts/components/settings/SessionSettingsHeader.tsx +++ b/ts/components/settings/SessionSettingsHeader.tsx @@ -1,7 +1,7 @@ import React from 'react'; import styled from 'styled-components'; import { assertUnreachable } from '../../types/sqlSharedTypes'; -import { SessionSettingCategory, SettingsViewProps } from './SessionSettings'; +import { SettingsViewProps } from './SessionSettings'; type Props = Pick; @@ -26,27 +26,27 @@ export const SettingsHeader = (props: Props) => { let categoryTitle: string | null = null; switch (category) { - case SessionSettingCategory.Appearance: + case 'appearance': categoryTitle = window.i18n('appearanceSettingsTitle'); break; - case SessionSettingCategory.Conversations: + case 'conversations': categoryTitle = window.i18n('conversationsSettingsTitle'); break; - case SessionSettingCategory.Notifications: + case 'notifications': categoryTitle = window.i18n('notificationsSettingsTitle'); break; - case SessionSettingCategory.Help: + case 'help': categoryTitle = window.i18n('helpSettingsTitle'); break; - case SessionSettingCategory.Permissions: + case 'permissions': categoryTitle = window.i18n('permissionsSettingsTitle'); break; - case SessionSettingCategory.Privacy: + case 'privacy': categoryTitle = window.i18n('privacySettingsTitle'); break; - case SessionSettingCategory.ClearData: - case SessionSettingCategory.MessageRequests: - case SessionSettingCategory.RecoveryPhrase: + case 'ClearData': + case 'messageRequests': + case 'recoveryPhrase': throw new Error(`no header for should be tried to be rendered for "${category}"`); default: diff --git a/ts/session/utils/Toast.tsx b/ts/session/utils/Toast.tsx index 7e7be394bd..a6673aa594 100644 --- a/ts/session/utils/Toast.tsx +++ b/ts/session/utils/Toast.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { toast } from 'react-toastify'; import { SessionToast, SessionToastType } from '../../components/basic/SessionToast'; -import { SessionSettingCategory } from '../../components/settings/SessionSettings'; import { SectionType, showLeftPaneSection, showSettingsSection } from '../../state/ducks/section'; // if you push a toast manually with toast...() be sure to set the type attribute of the SessionToast component @@ -132,7 +131,7 @@ export function pushedMissedCall(conversationName: string) { const openPermissionsSettings = () => { window.inboxStore?.dispatch(showLeftPaneSection(SectionType.Settings)); - window.inboxStore?.dispatch(showSettingsSection(SessionSettingCategory.Permissions)); + window.inboxStore?.dispatch(showSettingsSection('permissions')); }; export function pushedMissedCallCauseOfPermission(conversationName: string) { diff --git a/ts/state/createStore.ts b/ts/state/createStore.ts index 097a7ccfd8..a9c72dace3 100644 --- a/ts/state/createStore.ts +++ b/ts/state/createStore.ts @@ -23,7 +23,7 @@ const logger = createLogger({ logger: directConsole, }); -export const persistConfig = { +const persistConfig = { key: 'root', storage, whitelist: ['userConfig'], diff --git a/ts/state/ducks/section.tsx b/ts/state/ducks/section.tsx index 5060af4cca..a07d4a6f60 100644 --- a/ts/state/ducks/section.tsx +++ b/ts/state/ducks/section.tsx @@ -1,5 +1,6 @@ -// TODOLATER move into redux slice -import { SessionSettingCategory } from '../../components/settings/SessionSettings'; +// TODO move into redux slice + +import type { SessionSettingCategory } from '../../types/SessionSettingCategory'; export const FOCUS_SECTION = 'FOCUS_SECTION'; export const FOCUS_SETTINGS_SECTION = 'FOCUS_SETTINGS_SECTION'; @@ -173,7 +174,7 @@ export const reducer = ( return { ...state, focusedSection: payload, - focusedSettingsSection: SessionSettingCategory.Privacy, + focusedSettingsSection: 'privacy', }; case FOCUS_SETTINGS_SECTION: return { diff --git a/ts/state/reducer.ts b/ts/state/reducer.ts index eb382c372b..0715c8a75c 100644 --- a/ts/state/reducer.ts +++ b/ts/state/reducer.ts @@ -1,24 +1,25 @@ import { combineReducers } from '@reduxjs/toolkit'; -import { callReducer as call, CallStateType } from './ducks/call'; -import { reducer as conversations, ConversationsStateType } from './ducks/conversations'; -import { defaultRoomReducer as defaultRooms, DefaultRoomsState } from './ducks/defaultRooms'; -import { reducer as primaryColor } from './ducks/primaryColor'; + +import { callReducer as call, CallStateType } from './ducks/call'; // ok: importing only RingingManager.ts which is not importing anything else +import { reducer as conversations, ConversationsStateType } from './ducks/conversations'; // todo +import { defaultRoomReducer as defaultRooms, DefaultRoomsState } from './ducks/defaultRooms'; // todo +import { reducer as primaryColor } from './ducks/primaryColor'; // ok: importing only Constants.tsx which is not importing anything else import { reducer as search, SearchStateType } from './ducks/search'; import { reducer as section, SectionStateType } from './ducks/section'; import { ReduxSogsRoomInfos, SogsRoomInfoState } from './ducks/sogsRoomInfo'; -import { reducer as theme } from './ducks/theme'; -import { reducer as user, UserStateType } from './ducks/user'; +import { reducer as theme } from './ducks/theme'; // ok: importing only Constants.tsx which is not importing anything else +import { reducer as user, UserStateType } from './ducks/user'; // ok: not importing anything else -import { PrimaryColorStateType, ThemeStateType } from '../themes/constants/colors'; +import { PrimaryColorStateType, ThemeStateType } from '../themes/constants/colors'; // ok: not importing anything else import { modalReducer as modals, ModalState } from './ducks/modalDialog'; -import { defaultOnionReducer as onionPaths, OnionState } from './ducks/onion'; +import { defaultOnionReducer as onionPaths, OnionState } from './ducks/onion'; // ok: not importing anything else import { settingsReducer, SettingsState } from './ducks/settings'; import { reducer as stagedAttachments, StagedAttachmentsStateType, } from './ducks/stagedAttachments'; -import { userConfigReducer as userConfig, UserConfigState } from './ducks/userConfig'; +import { userConfigReducer as userConfig, UserConfigState } from './ducks/userConfig'; // ok: not importing anything else export type StateType = { search: SearchStateType; @@ -37,7 +38,7 @@ export type StateType = { settings: SettingsState; }; -export const reducers = { +const reducers = { search, conversations, user, diff --git a/ts/state/selectors/section.ts b/ts/state/selectors/section.ts index 9599f35860..7640dde938 100644 --- a/ts/state/selectors/section.ts +++ b/ts/state/selectors/section.ts @@ -1,8 +1,8 @@ import { createSelector } from '@reduxjs/toolkit'; -import { SessionSettingCategory } from '../../components/settings/SessionSettings'; import { LeftOverlayMode, SectionStateType, SectionType } from '../ducks/section'; import { StateType } from '../reducer'; +import type { SessionSettingCategory } from '../../types/SessionSettingCategory'; export const getSection = (state: StateType): SectionStateType => state.section; diff --git a/ts/types/SessionSettingCategory.d.ts b/ts/types/SessionSettingCategory.d.ts new file mode 100644 index 0000000000..21ffad665c --- /dev/null +++ b/ts/types/SessionSettingCategory.d.ts @@ -0,0 +1,10 @@ +export type SessionSettingCategory = + | 'privacy' + | 'notifications' + | 'conversations' + | 'messageRequests' + | 'appearance' + | 'permissions' + | 'help' + | 'recoveryPhrase' + | 'ClearData'; From fb99c3491ceb1824f91a30db031bf9392fe2cd80 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Wed, 24 Apr 2024 15:55:32 +1000 Subject: [PATCH 5/9] chore: remove calls to Storage from settings.tsx --- ts/components/SessionInboxView.tsx | 24 ++++++++++++-- ts/state/ducks/settings.tsx | 53 ++++++++++++++---------------- ts/state/reducer.ts | 13 ++++---- 3 files changed, 53 insertions(+), 37 deletions(-) diff --git a/ts/components/SessionInboxView.tsx b/ts/components/SessionInboxView.tsx index f2d8c76165..798ac095f2 100644 --- a/ts/components/SessionInboxView.tsx +++ b/ts/components/SessionInboxView.tsx @@ -1,4 +1,4 @@ -import { fromPairs, map } from 'lodash'; +import { fromPairs, isBoolean, map } from 'lodash'; import moment from 'moment'; import React from 'react'; import { Provider } from 'react-redux'; @@ -87,11 +87,31 @@ function createSessionInboxStore() { return createStore(initialState); } +function getBoolFromStorageOrFalse(settingsKey: string): boolean { + const got = Storage.get(settingsKey, false); + if (isBoolean(got)) { + return got; + } + return false; +} function setupLeftPane(forceUpdateInboxComponent: () => void) { window.openConversationWithMessages = openConversationWithMessages; window.inboxStore = createSessionInboxStore(); - window.inboxStore.dispatch(updateAllOnStorageReady()); + + window.inboxStore.dispatch( + updateAllOnStorageReady({ + hasBlindedMsgRequestsEnabled: getBoolFromStorageOrFalse( + SettingsKey.hasBlindedMsgRequestsEnabled + ), + someDeviceOutdatedSyncing: getBoolFromStorageOrFalse(SettingsKey.someDeviceOutdatedSyncing), + settingsLinkPreview: getBoolFromStorageOrFalse(SettingsKey.settingsLinkPreview), + hasFollowSystemThemeEnabled: getBoolFromStorageOrFalse( + SettingsKey.hasFollowSystemThemeEnabled + ), + hasShiftSendEnabled: getBoolFromStorageOrFalse(SettingsKey.hasShiftSendEnabled), + }) + ); forceUpdateInboxComponent(); } diff --git a/ts/state/ducks/settings.tsx b/ts/state/ducks/settings.tsx index 318e7fc234..dd89ac1b20 100644 --- a/ts/state/ducks/settings.tsx +++ b/ts/state/ducks/settings.tsx @@ -1,8 +1,7 @@ import { isBoolean } from 'lodash'; import { PayloadAction, createSlice } from '@reduxjs/toolkit'; -import { SettingsKey } from '../../data/settings-key'; -import { Storage } from '../../util/storage'; +import { SettingsKey } from '../../data/settings-key'; // ok: does not import anything else const SettingsBoolsKeyTrackedInRedux = [ SettingsKey.someDeviceOutdatedSyncing, @@ -44,33 +43,31 @@ const settingsSlice = createSlice({ // Once the storage is ready, initialState: getSettingsInitialState(), reducers: { - updateAllOnStorageReady(state) { - const linkPreview = Storage.get(SettingsKey.settingsLinkPreview, false); - const outdatedSync = Storage.get(SettingsKey.someDeviceOutdatedSyncing, false); - const hasBlindedMsgRequestsEnabled = Storage.get( - SettingsKey.hasBlindedMsgRequestsEnabled, - false - ); - const hasFollowSystemThemeEnabled = Storage.get( - SettingsKey.hasFollowSystemThemeEnabled, - false - ); - const hasShiftSendEnabled = Storage.get(SettingsKey.hasShiftSendEnabled, false); - state.settingsBools.someDeviceOutdatedSyncing = isBoolean(outdatedSync) - ? outdatedSync - : false; - state.settingsBools['link-preview-setting'] = isBoolean(linkPreview) ? linkPreview : false; // this is the value of SettingsKey.settingsLinkPreview - state.settingsBools.hasBlindedMsgRequestsEnabled = isBoolean(hasBlindedMsgRequestsEnabled) - ? hasBlindedMsgRequestsEnabled - : false; + updateAllOnStorageReady( + state, + { + payload, + }: PayloadAction<{ + settingsLinkPreview: boolean; + someDeviceOutdatedSyncing: boolean; + hasBlindedMsgRequestsEnabled: boolean; + hasFollowSystemThemeEnabled: boolean; + hasShiftSendEnabled: boolean; + }> + ) { + const { + hasBlindedMsgRequestsEnabled, + hasFollowSystemThemeEnabled, + settingsLinkPreview, + someDeviceOutdatedSyncing, + hasShiftSendEnabled, + } = payload; - state.settingsBools.hasFollowSystemThemeEnabled = isBoolean(hasFollowSystemThemeEnabled) - ? hasFollowSystemThemeEnabled - : false; - - state.settingsBools.hasShiftSendEnabled = isBoolean(hasShiftSendEnabled) - ? hasShiftSendEnabled - : false; + state.settingsBools.someDeviceOutdatedSyncing = someDeviceOutdatedSyncing; + state.settingsBools['link-preview-setting'] = settingsLinkPreview; + state.settingsBools.hasBlindedMsgRequestsEnabled = hasBlindedMsgRequestsEnabled; + state.settingsBools.hasFollowSystemThemeEnabled = hasFollowSystemThemeEnabled; + state.settingsBools.hasShiftSendEnabled = hasShiftSendEnabled; return state; }, diff --git a/ts/state/reducer.ts b/ts/state/reducer.ts index 0715c8a75c..da8148b46b 100644 --- a/ts/state/reducer.ts +++ b/ts/state/reducer.ts @@ -1,17 +1,16 @@ import { combineReducers } from '@reduxjs/toolkit'; - import { callReducer as call, CallStateType } from './ducks/call'; // ok: importing only RingingManager.ts which is not importing anything else import { reducer as conversations, ConversationsStateType } from './ducks/conversations'; // todo import { defaultRoomReducer as defaultRooms, DefaultRoomsState } from './ducks/defaultRooms'; // todo import { reducer as primaryColor } from './ducks/primaryColor'; // ok: importing only Constants.tsx which is not importing anything else -import { reducer as search, SearchStateType } from './ducks/search'; -import { reducer as section, SectionStateType } from './ducks/section'; -import { ReduxSogsRoomInfos, SogsRoomInfoState } from './ducks/sogsRoomInfo'; -import { reducer as theme } from './ducks/theme'; // ok: importing only Constants.tsx which is not importing anything else -import { reducer as user, UserStateType } from './ducks/user'; // ok: not importing anything else +import { reducer as search, SearchStateType } from './ducks/search'; // todo +import { reducer as section, SectionStateType } from './ducks/section'; // ok: importing only SessionSettingsCategory which is not importing anything else +import { ReduxSogsRoomInfos, SogsRoomInfoState } from './ducks/sogsRoomInfo'; // todo +import { reducer as theme } from './ducks/theme'; // ok: importing only Constants.tsx which is not importing anything else +import { reducer as user, UserStateType } from './ducks/user'; // ok: not importing anything else -import { PrimaryColorStateType, ThemeStateType } from '../themes/constants/colors'; // ok: not importing anything else +import { PrimaryColorStateType, ThemeStateType } from '../themes/constants/colors'; // ok: not importing anything else import { modalReducer as modals, ModalState } from './ducks/modalDialog'; import { defaultOnionReducer as onionPaths, OnionState } from './ducks/onion'; // ok: not importing anything else import { settingsReducer, SettingsState } from './ducks/settings'; From 4589bde6720acff22a0e6bad31f8e7c26ee065b5 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Wed, 24 Apr 2024 17:12:34 +1000 Subject: [PATCH 6/9] chore: moved more types to ReduxTypes.d.ts --- ts/components/basic/SessionInput.tsx | 3 +-- .../composition/CompositionButtons.tsx | 9 ++++---- .../dialog/EditProfilePictureModal.tsx | 6 +---- .../dialog/SessionPasswordDialog.tsx | 3 +-- .../leftpane/LeftPaneSettingSection.tsx | 2 +- ts/components/registration/SignUpTab.tsx | 5 ++-- .../settings/SessionSettingListItem.tsx | 4 ++-- ts/components/settings/SessionSettings.tsx | 3 +-- ts/node/menu.ts | 3 +-- ts/state/ducks/modalDialog.tsx | 6 ++--- ts/state/ducks/section.tsx | 2 +- ts/state/ducks/sogsRoomInfo.tsx | 10 -------- ts/state/reducer.ts | 6 ++--- ts/state/selectors/section.ts | 2 +- ts/types/ReduxTypes.d.ts | 23 +++++++++++++++++++ ts/types/SessionSettingCategory.d.ts | 10 -------- ts/types/Util.ts | 2 -- 17 files changed, 44 insertions(+), 55 deletions(-) create mode 100644 ts/types/ReduxTypes.d.ts delete mode 100644 ts/types/SessionSettingCategory.d.ts diff --git a/ts/components/basic/SessionInput.tsx b/ts/components/basic/SessionInput.tsx index 0c33a22b0a..a34de03cc8 100644 --- a/ts/components/basic/SessionInput.tsx +++ b/ts/components/basic/SessionInput.tsx @@ -2,7 +2,6 @@ import React, { useState } from 'react'; import classNames from 'classnames'; import { SessionIconButton } from '../icon'; -import { Noop } from '../../types/Util'; import { useHTMLDirection } from '../../util/i18n'; type Props = { @@ -46,7 +45,7 @@ const ErrorItem = (props: { error: string | undefined }) => { ); }; -const ShowHideButton = (props: { toggleForceShow: Noop }) => { +const ShowHideButton = (props: { toggleForceShow: () => void }) => { const htmlDirection = useHTMLDirection(); const position = htmlDirection === 'ltr' ? { right: '0px' } : { left: '0px' }; diff --git a/ts/components/conversation/composition/CompositionButtons.tsx b/ts/components/conversation/composition/CompositionButtons.tsx index 8a2a4d6657..3b145e2975 100644 --- a/ts/components/conversation/composition/CompositionButtons.tsx +++ b/ts/components/conversation/composition/CompositionButtons.tsx @@ -1,6 +1,5 @@ import React from 'react'; import styled from 'styled-components'; -import { Noop } from '../../../types/Util'; import { SessionIconButton } from '../../icon'; const StyledChatButtonContainer = styled.div` @@ -15,7 +14,7 @@ const StyledChatButtonContainer = styled.div` } `; -export const AddStagedAttachmentButton = (props: { onClick: Noop }) => { +export const AddStagedAttachmentButton = (props: { onClick: () => void }) => { return ( { ); }; -export const StartRecordingButton = (props: { onClick: Noop }) => { +export const StartRecordingButton = (props: { onClick: () => void }) => { return ( { }; // eslint-disable-next-line react/display-name -export const ToggleEmojiButton = React.forwardRef( +export const ToggleEmojiButton = React.forwardRef void }>( (props, ref) => { return ( @@ -70,7 +69,7 @@ export const ToggleEmojiButton = React.forwardRef { +export const SendMessageButton = (props: { onClick: () => void }) => { return ( { } }; -export type EditProfilePictureModalProps = { - avatarPath: string | null; - profileName: string | undefined; - ourId: string; -}; export const EditProfilePictureModal = (props: EditProfilePictureModalProps) => { const dispatch = useDispatch(); diff --git a/ts/components/dialog/SessionPasswordDialog.tsx b/ts/components/dialog/SessionPasswordDialog.tsx index 98d7369021..7de8b4435a 100644 --- a/ts/components/dialog/SessionPasswordDialog.tsx +++ b/ts/components/dialog/SessionPasswordDialog.tsx @@ -11,8 +11,7 @@ import { SessionButton, SessionButtonColor, SessionButtonType } from '../basic/S import { SessionWrapperModal } from '../SessionWrapperModal'; import { matchesHash, validatePassword } from '../../util/passwordUtils'; import { assertUnreachable } from '../../types/sqlSharedTypes'; - -export type PasswordAction = 'set' | 'change' | 'remove' | 'enter'; +import type { PasswordAction } from '../../types/ReduxTypes'; interface Props { passwordAction: PasswordAction; diff --git a/ts/components/leftpane/LeftPaneSettingSection.tsx b/ts/components/leftpane/LeftPaneSettingSection.tsx index 9efe37ba78..a05912a789 100644 --- a/ts/components/leftpane/LeftPaneSettingSection.tsx +++ b/ts/components/leftpane/LeftPaneSettingSection.tsx @@ -13,7 +13,7 @@ import { import { getFocusedSettingsSection } from '../../state/selectors/section'; import { SessionIcon } from '../icon'; import { LeftPaneSectionHeader } from './LeftPaneSectionHeader'; -import type { SessionSettingCategory } from '../../types/SessionSettingCategory'; +import type { SessionSettingCategory } from '../../types/ReduxTypes'; const StyledSettingsSectionTitle = styled.strong` font-family: var(--font-accent), var(--font-default); diff --git a/ts/components/registration/SignUpTab.tsx b/ts/components/registration/SignUpTab.tsx index a54d0a01c3..9a5f27ab8f 100644 --- a/ts/components/registration/SignUpTab.tsx +++ b/ts/components/registration/SignUpTab.tsx @@ -7,7 +7,6 @@ import { RegistrationContext, RegistrationPhase, signUp } from './RegistrationSt import { RegistrationUserDetails } from './RegistrationUserDetails'; import { sanitizeDisplayNameOrToast, SignInMode } from './SignInTab'; import { TermsAndConditions } from './TermsAndConditions'; -import { Noop } from '../../types/Util'; export enum SignUpMode { Default, @@ -23,7 +22,7 @@ const ContinueSignUpButton = ({ continueSignUp }: { continueSignUp: any }) => { return ; }; -const SignUpDefault = (props: { createSessionID: Noop }) => { +const SignUpDefault = (props: { createSessionID: () => void }) => { return (
@@ -47,7 +46,7 @@ export const GoBackMainMenuButton = () => { ); }; -const SignUpSessionIDShown = (props: { continueSignUp: Noop }) => { +const SignUpSessionIDShown = (props: { continueSignUp: () => void }) => { return (
diff --git a/ts/components/settings/SessionSettingListItem.tsx b/ts/components/settings/SessionSettingListItem.tsx index 20311ff228..ca99d3a317 100644 --- a/ts/components/settings/SessionSettingListItem.tsx +++ b/ts/components/settings/SessionSettingListItem.tsx @@ -10,7 +10,7 @@ import { import { SessionToggle } from '../basic/SessionToggle'; import { SessionConfirmDialogProps } from '../dialog/SessionConfirm'; import { SessionIconButton } from '../icon'; -import { Noop } from '../../types/Util'; + type ButtonSettingsProps = { title?: string; @@ -113,7 +113,7 @@ export const SessionSettingsItemWrapper = (props: { ); }; -export const SessionSettingsTitleWithLink = (props: { title: string; onClick: Noop }) => { +export const SessionSettingsTitleWithLink = (props: { title: string; onClick: () => void }) => { const { onClick, title } = props; return ( diff --git a/ts/components/settings/SessionSettings.tsx b/ts/components/settings/SessionSettings.tsx index 5a2b919f23..27fa5dced0 100644 --- a/ts/components/settings/SessionSettings.tsx +++ b/ts/components/settings/SessionSettings.tsx @@ -13,13 +13,12 @@ import { SessionNotificationGroupSettings } from './SessionNotificationGroupSett import { Data } from '../../data/data'; import { sessionPassword } from '../../state/ducks/modalDialog'; import { SectionType, showLeftPaneSection } from '../../state/ducks/section'; -import { PasswordAction } from '../dialog/SessionPasswordDialog'; import { SettingsCategoryAppearance } from './section/CategoryAppearance'; import { CategoryConversations } from './section/CategoryConversations'; import { SettingsCategoryHelp } from './section/CategoryHelp'; import { SettingsCategoryPermissions } from './section/CategoryPermissions'; import { SettingsCategoryPrivacy } from './section/CategoryPrivacy'; -import type { SessionSettingCategory } from '../../types/SessionSettingCategory'; +import type { SessionSettingCategory, PasswordAction } from '../../types/ReduxTypes'; export function displayPasswordModal( passwordAction: PasswordAction, diff --git a/ts/node/menu.ts b/ts/node/menu.ts index fb45418505..f75b661ca3 100644 --- a/ts/node/menu.ts +++ b/ts/node/menu.ts @@ -1,6 +1,5 @@ import { isString } from 'lodash'; import { LocaleMessagesType } from './locale'; -import { Noop } from '../types/Util'; export const createTemplate = ( options: { @@ -157,7 +156,7 @@ export const createTemplate = ( function updateForMac( template: any, messages: LocaleMessagesType, - options: { showAbout: Noop; showWindow: Noop } + options: { showAbout: () => void; showWindow: () => void } ) { const { showAbout, showWindow } = options; diff --git a/ts/state/ducks/modalDialog.tsx b/ts/state/ducks/modalDialog.tsx index 187d5fe2b3..715c8c5c05 100644 --- a/ts/state/ducks/modalDialog.tsx +++ b/ts/state/ducks/modalDialog.tsx @@ -1,8 +1,6 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; -import { EditProfilePictureModalProps } from '../../components/dialog/EditProfilePictureModal'; import { SessionConfirmDialogProps } from '../../components/dialog/SessionConfirm'; -import { PasswordAction } from '../../components/dialog/SessionPasswordDialog'; -import { Noop } from '../../types/Util'; +import type { EditProfilePictureModalProps, PasswordAction } from '../../types/ReduxTypes'; export type BanType = 'ban' | 'unban'; @@ -23,7 +21,7 @@ export type OnionPathModalState = EditProfileModalState; export type RecoveryPhraseModalState = EditProfileModalState; export type DeleteAccountModalState = EditProfileModalState; -export type SessionPasswordModalState = { passwordAction: PasswordAction; onOk: Noop } | null; +export type SessionPasswordModalState = { passwordAction: PasswordAction; onOk: () => void } | null; export type UserDetailsModalState = { conversationId: string; diff --git a/ts/state/ducks/section.tsx b/ts/state/ducks/section.tsx index a07d4a6f60..27cbe1cb76 100644 --- a/ts/state/ducks/section.tsx +++ b/ts/state/ducks/section.tsx @@ -1,6 +1,6 @@ // TODO move into redux slice -import type { SessionSettingCategory } from '../../types/SessionSettingCategory'; +import type { SessionSettingCategory } from '../../types/ReduxTypes'; export const FOCUS_SECTION = 'FOCUS_SECTION'; export const FOCUS_SETTINGS_SECTION = 'FOCUS_SETTINGS_SECTION'; diff --git a/ts/state/ducks/sogsRoomInfo.tsx b/ts/state/ducks/sogsRoomInfo.tsx index 6ebe3c6ce4..df8821d38b 100644 --- a/ts/state/ducks/sogsRoomInfo.tsx +++ b/ts/state/ducks/sogsRoomInfo.tsx @@ -1,9 +1,5 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import { isFinite, sortBy, uniq, xor } from 'lodash'; -import { - getCanWriteOutsideRedux, - getCurrentSubscriberCountOutsideRedux, -} from '../selectors/sogsRoomInfo'; type RoomInfo = { canWrite: boolean; @@ -79,16 +75,10 @@ export const ReduxSogsRoomInfos = { }; function setSubscriberCountOutsideRedux(convoId: string, subscriberCount: number) { - if (subscriberCount === getCurrentSubscriberCountOutsideRedux(convoId)) { - return; - } window.inboxStore?.dispatch(setSubscriberCount({ convoId, subscriberCount })); } function setCanWriteOutsideRedux(convoId: string, canWrite: boolean) { - if (getCanWriteOutsideRedux(convoId) === canWrite) { - return; - } window.inboxStore?.dispatch(setCanWrite({ convoId, canWrite })); } diff --git a/ts/state/reducer.ts b/ts/state/reducer.ts index da8148b46b..7d77e6611a 100644 --- a/ts/state/reducer.ts +++ b/ts/state/reducer.ts @@ -6,14 +6,14 @@ import { defaultRoomReducer as defaultRooms, DefaultRoomsState } from './ducks/d import { reducer as primaryColor } from './ducks/primaryColor'; // ok: importing only Constants.tsx which is not importing anything else import { reducer as search, SearchStateType } from './ducks/search'; // todo import { reducer as section, SectionStateType } from './ducks/section'; // ok: importing only SessionSettingsCategory which is not importing anything else -import { ReduxSogsRoomInfos, SogsRoomInfoState } from './ducks/sogsRoomInfo'; // todo +import { ReduxSogsRoomInfos, SogsRoomInfoState } from './ducks/sogsRoomInfo'; // ok: importing nothing else import { reducer as theme } from './ducks/theme'; // ok: importing only Constants.tsx which is not importing anything else import { reducer as user, UserStateType } from './ducks/user'; // ok: not importing anything else import { PrimaryColorStateType, ThemeStateType } from '../themes/constants/colors'; // ok: not importing anything else -import { modalReducer as modals, ModalState } from './ducks/modalDialog'; +import { modalReducer as modals, ModalState } from './ducks/modalDialog'; // todo import { defaultOnionReducer as onionPaths, OnionState } from './ducks/onion'; // ok: not importing anything else -import { settingsReducer, SettingsState } from './ducks/settings'; +import { settingsReducer, SettingsState } from './ducks/settings'; // ok: just importing settings-key.tsx which is not importing anything else import { reducer as stagedAttachments, StagedAttachmentsStateType, diff --git a/ts/state/selectors/section.ts b/ts/state/selectors/section.ts index 7640dde938..39992174e2 100644 --- a/ts/state/selectors/section.ts +++ b/ts/state/selectors/section.ts @@ -2,7 +2,7 @@ import { createSelector } from '@reduxjs/toolkit'; import { LeftOverlayMode, SectionStateType, SectionType } from '../ducks/section'; import { StateType } from '../reducer'; -import type { SessionSettingCategory } from '../../types/SessionSettingCategory'; +import type { SessionSettingCategory } from '../../types/ReduxTypes'; export const getSection = (state: StateType): SectionStateType => state.section; diff --git a/ts/types/ReduxTypes.d.ts b/ts/types/ReduxTypes.d.ts new file mode 100644 index 0000000000..41195a6581 --- /dev/null +++ b/ts/types/ReduxTypes.d.ts @@ -0,0 +1,23 @@ +/** + * Note: The types defined in this file have to be self contained. + * We must not import anything in this file, especially not something relying on the window object (even indirectly, through an import chain). + */ + +export type SessionSettingCategory = + | 'privacy' + | 'notifications' + | 'conversations' + | 'messageRequests' + | 'appearance' + | 'permissions' + | 'help' + | 'recoveryPhrase' + | 'ClearData'; + +export type PasswordAction = 'set' | 'change' | 'remove' | 'enter'; + +export type EditProfilePictureModalProps = { + avatarPath: string | null; + profileName: string | undefined; + ourId: string; +}; diff --git a/ts/types/SessionSettingCategory.d.ts b/ts/types/SessionSettingCategory.d.ts deleted file mode 100644 index 21ffad665c..0000000000 --- a/ts/types/SessionSettingCategory.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -export type SessionSettingCategory = - | 'privacy' - | 'notifications' - | 'conversations' - | 'messageRequests' - | 'appearance' - | 'permissions' - | 'help' - | 'recoveryPhrase' - | 'ClearData'; diff --git a/ts/types/Util.ts b/ts/types/Util.ts index 76f1d68008..ccea6b792a 100644 --- a/ts/types/Util.ts +++ b/ts/types/Util.ts @@ -7,5 +7,3 @@ export type RenderTextCallbackType = (options: { }) => JSX.Element; export type LocalizerType = (key: LocalizerKeys, values?: Array) => string; - -export type Noop = () => void; From 42bea0264c8bb8ee1132812bf65991ec2d78670c Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Thu, 25 Apr 2024 16:28:41 +1000 Subject: [PATCH 7/9] fix: improve retrieve timeout to 10s also: - add comments about not adding the limit:256 on snode list fetch - fix an issue when no audio are found when starting a webrtc call --- ts/components/SessionInboxView.tsx | 21 +++++----------- .../dialog/EditProfilePictureModal.tsx | 1 - .../settings/SessionSettingListItem.tsx | 1 - ts/session/apis/seed_node_api/SeedNodeAPI.ts | 3 +++ .../apis/snode_api/SnodeRequestTypes.ts | 3 +++ .../apis/snode_api/getServiceNodesList.ts | 3 +++ ts/session/apis/snode_api/onions.ts | 2 +- ts/session/apis/snode_api/retrieveRequest.ts | 24 +++++++------------ ts/session/utils/calling/CallManager.ts | 1 + ts/util/storage.ts | 11 ++++++++- 10 files changed, 36 insertions(+), 34 deletions(-) diff --git a/ts/components/SessionInboxView.tsx b/ts/components/SessionInboxView.tsx index 798ac095f2..b4ff71b007 100644 --- a/ts/components/SessionInboxView.tsx +++ b/ts/components/SessionInboxView.tsx @@ -1,4 +1,4 @@ -import { fromPairs, isBoolean, map } from 'lodash'; +import { fromPairs, map } from 'lodash'; import moment from 'moment'; import React from 'react'; import { Provider } from 'react-redux'; @@ -87,13 +87,6 @@ function createSessionInboxStore() { return createStore(initialState); } -function getBoolFromStorageOrFalse(settingsKey: string): boolean { - const got = Storage.get(settingsKey, false); - if (isBoolean(got)) { - return got; - } - return false; -} function setupLeftPane(forceUpdateInboxComponent: () => void) { window.openConversationWithMessages = openConversationWithMessages; @@ -101,15 +94,13 @@ function setupLeftPane(forceUpdateInboxComponent: () => void) { window.inboxStore.dispatch( updateAllOnStorageReady({ - hasBlindedMsgRequestsEnabled: getBoolFromStorageOrFalse( + hasBlindedMsgRequestsEnabled: Storage.getBoolOrFalse( SettingsKey.hasBlindedMsgRequestsEnabled ), - someDeviceOutdatedSyncing: getBoolFromStorageOrFalse(SettingsKey.someDeviceOutdatedSyncing), - settingsLinkPreview: getBoolFromStorageOrFalse(SettingsKey.settingsLinkPreview), - hasFollowSystemThemeEnabled: getBoolFromStorageOrFalse( - SettingsKey.hasFollowSystemThemeEnabled - ), - hasShiftSendEnabled: getBoolFromStorageOrFalse(SettingsKey.hasShiftSendEnabled), + someDeviceOutdatedSyncing: Storage.getBoolOrFalse(SettingsKey.someDeviceOutdatedSyncing), + settingsLinkPreview: Storage.getBoolOrFalse(SettingsKey.settingsLinkPreview), + hasFollowSystemThemeEnabled: Storage.getBoolOrFalse(SettingsKey.hasFollowSystemThemeEnabled), + hasShiftSendEnabled: Storage.getBoolOrFalse(SettingsKey.hasShiftSendEnabled), }) ); forceUpdateInboxComponent(); diff --git a/ts/components/dialog/EditProfilePictureModal.tsx b/ts/components/dialog/EditProfilePictureModal.tsx index a92de7a60c..7828d951bf 100644 --- a/ts/components/dialog/EditProfilePictureModal.tsx +++ b/ts/components/dialog/EditProfilePictureModal.tsx @@ -60,7 +60,6 @@ const uploadProfileAvatar = async (scaledAvatarUrl: string | null) => { } }; - export const EditProfilePictureModal = (props: EditProfilePictureModalProps) => { const dispatch = useDispatch(); diff --git a/ts/components/settings/SessionSettingListItem.tsx b/ts/components/settings/SessionSettingListItem.tsx index ca99d3a317..c2b111b231 100644 --- a/ts/components/settings/SessionSettingListItem.tsx +++ b/ts/components/settings/SessionSettingListItem.tsx @@ -11,7 +11,6 @@ import { SessionToggle } from '../basic/SessionToggle'; import { SessionConfirmDialogProps } from '../dialog/SessionConfirm'; import { SessionIconButton } from '../icon'; - type ButtonSettingsProps = { title?: string; description?: string; diff --git a/ts/session/apis/seed_node_api/SeedNodeAPI.ts b/ts/session/apis/seed_node_api/SeedNodeAPI.ts index 3a31aedff2..a28f03eb23 100644 --- a/ts/session/apis/seed_node_api/SeedNodeAPI.ts +++ b/ts/session/apis/seed_node_api/SeedNodeAPI.ts @@ -230,6 +230,9 @@ async function getSnodesFromSeedUrl(urlObj: URL): Promise> { const params = { active_only: true, + // If you are thinking of adding the `limit` field here: don't. + // We fetch the full list because when we retrieve it we also remove from all the swarms we already know, any snode not part of that fetched list. + // If the limit was set, we would remove a lot of valid snodes from the swarms we've already fetched. fields: { public_ip: true, storage_port: true, diff --git a/ts/session/apis/snode_api/SnodeRequestTypes.ts b/ts/session/apis/snode_api/SnodeRequestTypes.ts index 1bed2be22b..8b54cfd7f4 100644 --- a/ts/session/apis/snode_api/SnodeRequestTypes.ts +++ b/ts/session/apis/snode_api/SnodeRequestTypes.ts @@ -73,6 +73,9 @@ export type GetServiceNodesSubRequest = { endpoint: 'get_service_nodes'; params: { active_only: true; + // If you are thinking of adding the `limit` field here: don't. + // We fetch the full list because when we retrieve it we also remove from all the swarms we already know, any snode not part of that fetched list. + // If the limit was set, we would remove a lot of valid snodes from the swarms we've already fetched. fields: { public_ip: true; storage_port: true; diff --git a/ts/session/apis/snode_api/getServiceNodesList.ts b/ts/session/apis/snode_api/getServiceNodesList.ts index 8f35d82888..6c85050a33 100644 --- a/ts/session/apis/snode_api/getServiceNodesList.ts +++ b/ts/session/apis/snode_api/getServiceNodesList.ts @@ -13,6 +13,9 @@ function buildSnodeListRequests(): Array { endpoint: 'get_service_nodes', params: { active_only: true, + // If you are thinking of adding the `limit` field here: don't. + // We fetch the full list because when we retrieve it we also remove from all the swarms we already know, any snode not part of that fetched list. + // If the limit was set, we would remove a lot of valid snodes from the swarms we've already fetched. fields: { public_ip: true, storage_port: true, diff --git a/ts/session/apis/snode_api/onions.ts b/ts/session/apis/snode_api/onions.ts index 4f2c1750a0..9aedbea6d6 100644 --- a/ts/session/apis/snode_api/onions.ts +++ b/ts/session/apis/snode_api/onions.ts @@ -11,7 +11,7 @@ import { AbortSignal as AbortSignalNode } from 'node-fetch/externals'; import { dropSnodeFromSnodePool, dropSnodeFromSwarmIfNeeded, updateSwarmFor } from './snodePool'; import { OnionPaths } from '../../onions'; -import { incrementBadPathCountOrDrop } from '../../onions/onionPath'; +import { incrementBadPathCountOrDrop } from '../../onions/onionPath'; import { ed25519Str, toHex } from '../../utils/String'; import { Snode } from '../../../data/data'; diff --git a/ts/session/apis/snode_api/retrieveRequest.ts b/ts/session/apis/snode_api/retrieveRequest.ts index 5dfcaf007c..53f078eb0d 100644 --- a/ts/session/apis/snode_api/retrieveRequest.ts +++ b/ts/session/apis/snode_api/retrieveRequest.ts @@ -1,4 +1,4 @@ -import { isArray, omit, sortBy } from 'lodash'; +import { isArray, omit } from 'lodash'; import { Snode } from '../../../data/data'; import { updateIsOnline } from '../../../state/ducks/onion'; import { doSnodeBatchRequest } from './batchRequest'; @@ -124,7 +124,7 @@ async function retrieveNextMessages( ); // let exceptions bubble up // no retry for this one as this a call we do every few seconds while polling for messages - const timeOutMs = 10 * 1000; // yes this is a long timeout for just messages, but 4s timeout way to often... + const timeOutMs = 10 * 1000; // yes this is a long timeout for just messages, but 4s timeouts way to often... const timeoutPromise = async () => sleepFor(timeOutMs); const fetchPromise = async () => doSnodeBatchRequest(retrieveRequestsParams, targetNode, timeOutMs, associatedWith); @@ -166,19 +166,13 @@ async function retrieveNextMessages( GetNetworkTime.handleTimestampOffsetFromNetwork('retrieve', bodyFirstResult.t); - // merge results with their corresponding namespaces - return results.map((result, index) => { - const messages = result.body as RetrieveMessagesResultsContent; - // Not sure if that makes sense, but we probably want those messages sorted. - const sortedMessages = sortBy(messages.messages, m => m.timestamp); - messages.messages = sortedMessages; - - return { - code: result.code, - messages, - namespace: namespaces[index], - }; - }); + // NOTE: We don't want to sort messages here because the ordering depends on the snode and when it received each messages. + // The last_hash for that snode has to be the last one we've received from that same snode, othwerwise we end up fetching the same messages over and over again. + return results.map((result, index) => ({ + code: result.code, + messages: result.body as RetrieveMessagesResultsContent, + namespace: namespaces[index], + })); } catch (e) { window?.log?.warn('exception while parsing json of nextMessage:', e); if (!window.inboxStore?.getState().onionPaths.isOnline) { diff --git a/ts/session/utils/calling/CallManager.ts b/ts/session/utils/calling/CallManager.ts index b584062976..0c04cdaf76 100644 --- a/ts/session/utils/calling/CallManager.ts +++ b/ts/session/utils/calling/CallManager.ts @@ -415,6 +415,7 @@ async function createOfferAndSendIt(recipient: string, msgIdentifier: string | n if (offer && offer.sdp) { const lines = offer.sdp.split(/\r?\n/); const lineWithFtmpIndex = lines.findIndex(f => f.startsWith('a=fmtp:111')); + // If webrtc does not find any audio input when initializing, the offer will not have a line with `a=fmtp:111` at all, `lineWithFtmpIndex` will be invalid. if (lineWithFtmpIndex > -1) { const partBeforeComma = lines[lineWithFtmpIndex].split(';'); lines[lineWithFtmpIndex] = `${partBeforeComma[0]};cbr=1`; diff --git a/ts/util/storage.ts b/ts/util/storage.ts index ba229323ce..e40dac4e9a 100644 --- a/ts/util/storage.ts +++ b/ts/util/storage.ts @@ -160,4 +160,13 @@ export async function saveRecentReations(reactions: Array) { return Storage.put('recent_reactions', reactions.join(' ')); } -export const Storage = { fetch, put, get, remove, onready, reset }; + +function getBoolOrFalse(settingsKey: string): boolean { + const got = Storage.get(settingsKey, false); + if (isBoolean(got)) { + return got; + } + return false; +} + +export const Storage = { fetch, put, get, getBoolOrFalse, remove, onready, reset }; From faa24ce9a646d6534f91ff82da82873fb4aea6df Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Mon, 29 Apr 2024 10:43:36 +1000 Subject: [PATCH 8/9] fix: lint --- ts/state/reducer.ts | 28 ++++++++++++++-------------- ts/util/storage.ts | 1 - 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/ts/state/reducer.ts b/ts/state/reducer.ts index 7d77e6611a..2e9b1a9d36 100644 --- a/ts/state/reducer.ts +++ b/ts/state/reducer.ts @@ -1,24 +1,24 @@ import { combineReducers } from '@reduxjs/toolkit'; -import { callReducer as call, CallStateType } from './ducks/call'; // ok: importing only RingingManager.ts which is not importing anything else -import { reducer as conversations, ConversationsStateType } from './ducks/conversations'; // todo -import { defaultRoomReducer as defaultRooms, DefaultRoomsState } from './ducks/defaultRooms'; // todo -import { reducer as primaryColor } from './ducks/primaryColor'; // ok: importing only Constants.tsx which is not importing anything else -import { reducer as search, SearchStateType } from './ducks/search'; // todo -import { reducer as section, SectionStateType } from './ducks/section'; // ok: importing only SessionSettingsCategory which is not importing anything else -import { ReduxSogsRoomInfos, SogsRoomInfoState } from './ducks/sogsRoomInfo'; // ok: importing nothing else -import { reducer as theme } from './ducks/theme'; // ok: importing only Constants.tsx which is not importing anything else -import { reducer as user, UserStateType } from './ducks/user'; // ok: not importing anything else +import { callReducer as call, CallStateType } from './ducks/call'; +import { reducer as conversations, ConversationsStateType } from './ducks/conversations'; +import { defaultRoomReducer as defaultRooms, DefaultRoomsState } from './ducks/defaultRooms'; +import { reducer as primaryColor } from './ducks/primaryColor'; +import { reducer as search, SearchStateType } from './ducks/search'; +import { reducer as section, SectionStateType } from './ducks/section'; +import { ReduxSogsRoomInfos, SogsRoomInfoState } from './ducks/sogsRoomInfo'; +import { reducer as theme } from './ducks/theme'; +import { reducer as user, UserStateType } from './ducks/user'; -import { PrimaryColorStateType, ThemeStateType } from '../themes/constants/colors'; // ok: not importing anything else -import { modalReducer as modals, ModalState } from './ducks/modalDialog'; // todo -import { defaultOnionReducer as onionPaths, OnionState } from './ducks/onion'; // ok: not importing anything else -import { settingsReducer, SettingsState } from './ducks/settings'; // ok: just importing settings-key.tsx which is not importing anything else +import { PrimaryColorStateType, ThemeStateType } from '../themes/constants/colors'; +import { modalReducer as modals, ModalState } from './ducks/modalDialog'; +import { defaultOnionReducer as onionPaths, OnionState } from './ducks/onion'; +import { settingsReducer, SettingsState } from './ducks/settings'; import { reducer as stagedAttachments, StagedAttachmentsStateType, } from './ducks/stagedAttachments'; -import { userConfigReducer as userConfig, UserConfigState } from './ducks/userConfig'; // ok: not importing anything else +import { userConfigReducer as userConfig, UserConfigState } from './ducks/userConfig'; export type StateType = { search: SearchStateType; diff --git a/ts/util/storage.ts b/ts/util/storage.ts index e40dac4e9a..5710d30bcd 100644 --- a/ts/util/storage.ts +++ b/ts/util/storage.ts @@ -160,7 +160,6 @@ export async function saveRecentReations(reactions: Array) { return Storage.put('recent_reactions', reactions.join(' ')); } - function getBoolOrFalse(settingsKey: string): boolean { const got = Storage.get(settingsKey, false); if (isBoolean(got)) { From 51c307af25996e3bded9d1b8d4d9b726ce845060 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Mon, 29 Apr 2024 14:38:49 +1000 Subject: [PATCH 9/9] chore: fix PR reviews --- .../leftpane/LeftPaneSettingSection.tsx | 6 +-- ts/components/settings/SessionSettings.tsx | 2 +- .../settings/SessionSettingsHeader.tsx | 2 +- ts/session/apis/seed_node_api/SeedNodeAPI.ts | 26 +++++------ .../apis/snode_api/SnodeRequestTypes.ts | 44 ++++++++++++++----- .../apis/snode_api/getServiceNodesList.ts | 3 -- ts/session/apis/snode_api/retrieveRequest.ts | 6 +-- ts/state/ducks/settings.tsx | 2 +- ts/types/ReduxTypes.d.ts | 2 +- 9 files changed, 53 insertions(+), 40 deletions(-) diff --git a/ts/components/leftpane/LeftPaneSettingSection.tsx b/ts/components/leftpane/LeftPaneSettingSection.tsx index a05912a789..8f154d157b 100644 --- a/ts/components/leftpane/LeftPaneSettingSection.tsx +++ b/ts/components/leftpane/LeftPaneSettingSection.tsx @@ -77,7 +77,7 @@ const getCategories = (): Array<{ id: SessionSettingCategory; title: string }> = title: window.i18n('recoveryPhrase'), }, { - id: 'ClearData' as const, + id: 'clearData' as const, title: window.i18n('clearDataSettingsTitle'), }, ]; @@ -93,7 +93,7 @@ const LeftPaneSettingsCategoryRow = (props: { const dataTestId = `${title.toLowerCase().replace(' ', '-')}-settings-menu-item`; - const isClearData = id === 'ClearData'; + const isClearData = id === 'clearData'; return ( ; // these three down there have no options, they are just a button - case 'ClearData': + case 'clearData': case 'messageRequests': case 'recoveryPhrase': default: diff --git a/ts/components/settings/SessionSettingsHeader.tsx b/ts/components/settings/SessionSettingsHeader.tsx index 91b0cefe5e..f2ed4646f9 100644 --- a/ts/components/settings/SessionSettingsHeader.tsx +++ b/ts/components/settings/SessionSettingsHeader.tsx @@ -44,7 +44,7 @@ export const SettingsHeader = (props: Props) => { case 'privacy': categoryTitle = window.i18n('privacySettingsTitle'); break; - case 'ClearData': + case 'clearData': case 'messageRequests': case 'recoveryPhrase': throw new Error(`no header for should be tried to be rendered for "${category}"`); diff --git a/ts/session/apis/seed_node_api/SeedNodeAPI.ts b/ts/session/apis/seed_node_api/SeedNodeAPI.ts index a28f03eb23..899b26f4d6 100644 --- a/ts/session/apis/seed_node_api/SeedNodeAPI.ts +++ b/ts/session/apis/seed_node_api/SeedNodeAPI.ts @@ -12,6 +12,7 @@ import { allowOnlyOneAtATime } from '../../utils/Promise'; import { APPLICATION_JSON } from '../../../types/MIME'; import { isLinux } from '../../../OS'; import { Snode } from '../../../data/data'; +import { GetServicesNodesFromSeedRequest } from '../snode_api/SnodeRequestTypes'; /** * Fetch all snodes from seed nodes. @@ -228,25 +229,20 @@ async function getSnodesFromSeedUrl(urlObj: URL): Promise> { // we get all active nodes window?.log?.info(`getSnodesFromSeedUrl starting with ${urlObj.href}`); - const params = { - active_only: true, - // If you are thinking of adding the `limit` field here: don't. - // We fetch the full list because when we retrieve it we also remove from all the swarms we already know, any snode not part of that fetched list. - // If the limit was set, we would remove a lot of valid snodes from the swarms we've already fetched. - fields: { - public_ip: true, - storage_port: true, - pubkey_x25519: true, - pubkey_ed25519: true, - }, - }; - const endpoint = 'json_rpc'; const url = `${urlObj.href}${endpoint}`; - const body = { + const body: GetServicesNodesFromSeedRequest = { jsonrpc: '2.0', method: 'get_n_service_nodes', - params, + params: { + active_only: true, + fields: { + public_ip: true, + storage_port: true, + pubkey_x25519: true, + pubkey_ed25519: true, + }, + }, }; const sslAgent = await getSslAgentForSeedNode( diff --git a/ts/session/apis/snode_api/SnodeRequestTypes.ts b/ts/session/apis/snode_api/SnodeRequestTypes.ts index 8b54cfd7f4..ee2a7dbb74 100644 --- a/ts/session/apis/snode_api/SnodeRequestTypes.ts +++ b/ts/session/apis/snode_api/SnodeRequestTypes.ts @@ -67,22 +67,42 @@ export type OnsResolveSubRequest = { }; }; +/** + * If you are thinking of adding the `limit` field here: don't. + * We fetch the full list because we will remove from every cached swarms the snodes not found in that fresh list. + * If a `limit` was set, we would remove a lot of valid snodes from those cached swarms. + */ +type FetchSnodeListParams = { + active_only: true; + fields: { + public_ip: true; + storage_port: true; + pubkey_x25519: true; + pubkey_ed25519: true; + }; +}; + +export type GetServicesNodesFromSeedRequest = { + method: 'get_n_service_nodes'; + jsonrpc: '2.0'; + /** + * If you are thinking of adding the `limit` field here: don't. + * We fetch the full list because we will remove from every cached swarms the snodes not found in that fresh list. + * If the limit was set, we would remove a lot of valid snodes from the swarms we've already fetched. + */ + params: FetchSnodeListParams; +}; + export type GetServiceNodesSubRequest = { method: 'oxend_request'; params: { endpoint: 'get_service_nodes'; - params: { - active_only: true; - // If you are thinking of adding the `limit` field here: don't. - // We fetch the full list because when we retrieve it we also remove from all the swarms we already know, any snode not part of that fetched list. - // If the limit was set, we would remove a lot of valid snodes from the swarms we've already fetched. - fields: { - public_ip: true; - storage_port: true; - pubkey_x25519: true; - pubkey_ed25519: true; - }; - }; + /** + * If you are thinking of adding the `limit` field here: don't. + * We fetch the full list because we will remove from every cached swarms the snodes not found in that fresh list. + * If the limit was set, we would remove a lot of valid snodes from the swarms we've already fetched. + */ + params: FetchSnodeListParams; }; }; diff --git a/ts/session/apis/snode_api/getServiceNodesList.ts b/ts/session/apis/snode_api/getServiceNodesList.ts index 6c85050a33..8f35d82888 100644 --- a/ts/session/apis/snode_api/getServiceNodesList.ts +++ b/ts/session/apis/snode_api/getServiceNodesList.ts @@ -13,9 +13,6 @@ function buildSnodeListRequests(): Array { endpoint: 'get_service_nodes', params: { active_only: true, - // If you are thinking of adding the `limit` field here: don't. - // We fetch the full list because when we retrieve it we also remove from all the swarms we already know, any snode not part of that fetched list. - // If the limit was set, we would remove a lot of valid snodes from the swarms we've already fetched. fields: { public_ip: true, storage_port: true, diff --git a/ts/session/apis/snode_api/retrieveRequest.ts b/ts/session/apis/snode_api/retrieveRequest.ts index 53f078eb0d..50d94eadac 100644 --- a/ts/session/apis/snode_api/retrieveRequest.ts +++ b/ts/session/apis/snode_api/retrieveRequest.ts @@ -5,7 +5,7 @@ import { doSnodeBatchRequest } from './batchRequest'; import { GetNetworkTime } from './getNetworkTime'; import { SnodeNamespace, SnodeNamespaces } from './namespaces'; -import { TTL_DEFAULT } from '../../constants'; +import { DURATION, TTL_DEFAULT } from '../../constants'; import { UserUtils } from '../../utils'; import { sleepFor } from '../../utils/Promise'; import { @@ -124,7 +124,7 @@ async function retrieveNextMessages( ); // let exceptions bubble up // no retry for this one as this a call we do every few seconds while polling for messages - const timeOutMs = 10 * 1000; // yes this is a long timeout for just messages, but 4s timeouts way to often... + const timeOutMs = 10 * DURATION.SECONDS; // yes this is a long timeout for just messages, but 4s timeouts way to often... const timeoutPromise = async () => sleepFor(timeOutMs); const fetchPromise = async () => doSnodeBatchRequest(retrieveRequestsParams, targetNode, timeOutMs, associatedWith); @@ -166,7 +166,7 @@ async function retrieveNextMessages( GetNetworkTime.handleTimestampOffsetFromNetwork('retrieve', bodyFirstResult.t); - // NOTE: We don't want to sort messages here because the ordering depends on the snode and when it received each messages. + // NOTE: We don't want to sort messages here because the ordering depends on the snode and when it received each message. // The last_hash for that snode has to be the last one we've received from that same snode, othwerwise we end up fetching the same messages over and over again. return results.map((result, index) => ({ code: result.code, diff --git a/ts/state/ducks/settings.tsx b/ts/state/ducks/settings.tsx index dd89ac1b20..7397b47c37 100644 --- a/ts/state/ducks/settings.tsx +++ b/ts/state/ducks/settings.tsx @@ -1,7 +1,7 @@ import { isBoolean } from 'lodash'; import { PayloadAction, createSlice } from '@reduxjs/toolkit'; -import { SettingsKey } from '../../data/settings-key'; // ok: does not import anything else +import { SettingsKey } from '../../data/settings-key'; const SettingsBoolsKeyTrackedInRedux = [ SettingsKey.someDeviceOutdatedSyncing, diff --git a/ts/types/ReduxTypes.d.ts b/ts/types/ReduxTypes.d.ts index 41195a6581..d744b0f140 100644 --- a/ts/types/ReduxTypes.d.ts +++ b/ts/types/ReduxTypes.d.ts @@ -12,7 +12,7 @@ export type SessionSettingCategory = | 'permissions' | 'help' | 'recoveryPhrase' - | 'ClearData'; + | 'clearData'; export type PasswordAction = 'set' | 'change' | 'remove' | 'enter';