From 9c3a6fe7d657ee3b66dc45bd23390c74d6b3fea8 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 15 Jan 2025 14:21:18 -0700 Subject: [PATCH 01/22] Expose a method for getting the user channel name --- src/libs/PusherUtils.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libs/PusherUtils.ts b/src/libs/PusherUtils.ts index 8a83bb012b3a..ef8d57e8bec4 100644 --- a/src/libs/PusherUtils.ts +++ b/src/libs/PusherUtils.ts @@ -11,6 +11,10 @@ type Callback = (data: OnyxUpdate[]) => Promise; // Keeps track of all the callbacks that need triggered for each event type const multiEventCallbackMapping: Record = {}; +function getUserChannelName(accountID: string) { + return `${CONST.PUSHER.PRIVATE_USER_CHANNEL_PREFIX}${accountID}${CONFIG.PUSHER.SUFFIX}` as const; +} + function subscribeToMultiEvent(eventType: string, callback: Callback) { multiEventCallbackMapping[eventType] = callback; } @@ -27,7 +31,7 @@ function triggerMultiEventHandler(eventType: string, data: OnyxUpdate[]): Promis * Abstraction around subscribing to private user channel events. Handles all logs and errors automatically. */ function subscribeToPrivateUserChannelEvent(eventName: string, accountID: string, onEvent: (pushJSON: OnyxUpdatesFromServer) => void) { - const pusherChannelName = `${CONST.PUSHER.PRIVATE_USER_CHANNEL_PREFIX}${accountID}${CONFIG.PUSHER.SUFFIX}` as const; + const pusherChannelName = getUserChannelName(accountID); function logPusherEvent(pushJSON: OnyxUpdatesFromServer) { Log.info(`[Report] Handled ${eventName} event sent by Pusher`, false, pushJSON); @@ -49,6 +53,7 @@ function subscribeToPrivateUserChannelEvent(eventName: string, accountID: string } export default { + getUserChannelName, subscribeToPrivateUserChannelEvent, subscribeToMultiEvent, triggerMultiEventHandler, From b9b946822e5964eb586aa2aa2a58c0c4988c572a Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 15 Jan 2025 14:22:07 -0700 Subject: [PATCH 02/22] Add the ping pong events --- src/libs/Pusher/EventType.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libs/Pusher/EventType.ts b/src/libs/Pusher/EventType.ts index f1f0592aeac6..4b6938d7e9bc 100644 --- a/src/libs/Pusher/EventType.ts +++ b/src/libs/Pusher/EventType.ts @@ -8,6 +8,12 @@ export default { USER_IS_LEAVING_ROOM: 'client-userIsLeavingRoom', USER_IS_TYPING: 'client-userIsTyping', MULTIPLE_EVENTS: 'multipleEvents', + + // An event that the client sends to the server and then listens for a returned "pong" event + PING: 'ping', + + // An event that the server sends back to the client in response to a "ping" event + PONG: 'pong', MULTIPLE_EVENT_TYPE: { ONYX_API_UPDATE: 'onyxApiUpdate', RECONNECT_APP: 'reconnectApp', From f5f21fa12f3ebcce3b3e143526d4d407d732efa9 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 15 Jan 2025 14:22:19 -0700 Subject: [PATCH 03/22] Add a basic structure for the pingpong --- src/libs/actions/User.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index 80d04a4617bd..14a276b47e51 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -888,6 +888,29 @@ function playSoundForMessageType(pushJSON: OnyxServerUpdate[]) { }); } +function subscribeToPusherPong() { + // If there is no user accountID yet (because the app isn't fully setup yet), the channel can't be subscribed to so return early + if (currentUserAccountID === -1) { + return; + } + + PusherUtils.subscribeToPrivateUserChannelEvent(Pusher.TYPE.PONG, currentUserAccountID.toString(), (pushJSON) => {}); +} + +let pingPongStarted = false; +function initializePusherPingPong() { + // Ignore any additional calls to initialize the ping pong if it's already been started + if (pingPongStarted) { + return; + } + + pingPongStarted = true; +} + +function pingPusher() { + Pusher.sendEvent(privateReportChannelName, Pusher.TYPE.USER_IS_TYPING, typingStatus); +} + /** * Handles the newest events from Pusher where a single mega multipleEvents contains * an array of singular events all in one event From 15b6ede755fba0c308a18f8da6869244258bc787 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 15 Jan 2025 15:40:31 -0700 Subject: [PATCH 04/22] Record event data in an event map --- src/libs/Pusher/pusher.ts | 9 +++++++- src/libs/PusherUtils.ts | 6 +++--- src/libs/actions/User.ts | 43 ++++++++++++++++++++++++++++++++++----- 3 files changed, 49 insertions(+), 9 deletions(-) diff --git a/src/libs/Pusher/pusher.ts b/src/libs/Pusher/pusher.ts index 1dd1628e53d2..71f30e046ead 100644 --- a/src/libs/Pusher/pusher.ts +++ b/src/libs/Pusher/pusher.ts @@ -31,9 +31,16 @@ type UserIsLeavingRoomEvent = Record & { userLogin?: string; }; +type PingPongEvent = Record & { + id: string; + timestamp: number; +}; + type PusherEventMap = { [TYPE.USER_IS_TYPING]: UserIsTypingEvent; [TYPE.USER_IS_LEAVING_ROOM]: UserIsLeavingRoomEvent; + [TYPE.PING]: PingPongEvent; + [TYPE.PONG]: PingPongEvent; }; type EventData = {chunk?: string; id?: string; index?: number; final?: boolean} & (EventName extends keyof PusherEventMap @@ -441,4 +448,4 @@ export { getPusherSocketID, }; -export type {EventCallbackError, States, UserIsTypingEvent, UserIsLeavingRoomEvent}; +export type {EventCallbackError, States, UserIsTypingEvent, UserIsLeavingRoomEvent, PingPongEvent}; diff --git a/src/libs/PusherUtils.ts b/src/libs/PusherUtils.ts index ef8d57e8bec4..5243090063e5 100644 --- a/src/libs/PusherUtils.ts +++ b/src/libs/PusherUtils.ts @@ -30,10 +30,10 @@ function triggerMultiEventHandler(eventType: string, data: OnyxUpdate[]): Promis /** * Abstraction around subscribing to private user channel events. Handles all logs and errors automatically. */ -function subscribeToPrivateUserChannelEvent(eventName: string, accountID: string, onEvent: (pushJSON: OnyxUpdatesFromServer) => void) { +function subscribeToPrivateUserChannelEvent(eventName: string, accountID: string, onEvent: (pushJSON: OnyxUpdatesFromServer | Pusher.PingPongEvent) => void) { const pusherChannelName = getUserChannelName(accountID); - function logPusherEvent(pushJSON: OnyxUpdatesFromServer) { + function logPusherEvent(pushJSON: OnyxUpdatesFromServer | Pusher.PingPongEvent) { Log.info(`[Report] Handled ${eventName} event sent by Pusher`, false, pushJSON); } @@ -41,7 +41,7 @@ function subscribeToPrivateUserChannelEvent(eventName: string, accountID: string NetworkConnection.triggerReconnectionCallbacks('Pusher re-subscribed to private user channel'); } - function onEventPush(pushJSON: OnyxUpdatesFromServer) { + function onEventPush(pushJSON: OnyxUpdatesFromServer | Pusher.PingPongEvent) { logPusherEvent(pushJSON); onEvent(pushJSON); } diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index 14a276b47e51..28293d404f77 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -28,6 +28,7 @@ import type Platform from '@libs/getPlatform/types'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; import * as SequentialQueue from '@libs/Network/SequentialQueue'; +import * as NumberUtils from '@libs/NumberUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as Pusher from '@libs/Pusher/pusher'; import PusherUtils from '@libs/PusherUtils'; @@ -888,15 +889,48 @@ function playSoundForMessageType(pushJSON: OnyxServerUpdate[]) { }); } +// Holds a map of all the PINGs that have been sent to the server and when they were sent +// Once a PONG is received, the event data will be removed from this map. +type PingPongTimestampMap = Record; +const pingIDsAndTimestamps: PingPongTimestampMap = {}; + function subscribeToPusherPong() { // If there is no user accountID yet (because the app isn't fully setup yet), the channel can't be subscribed to so return early if (currentUserAccountID === -1) { return; } - PusherUtils.subscribeToPrivateUserChannelEvent(Pusher.TYPE.PONG, currentUserAccountID.toString(), (pushJSON) => {}); + PusherUtils.subscribeToPrivateUserChannelEvent(Pusher.TYPE.PONG, currentUserAccountID.toString(), (pushJSON) => { + const pongEvent = pushJSON as Pusher.PingPongEvent; + // First, check to see if the PONG event is in the pingIDsAndTimestamps map + const pingEvent = pingIDsAndTimestamps[pongEvent.id]; + if (!pingEvent) { + Log.info(`[Pusher PINGPONG] Received a PONG event from the server with an ID that wasn't sent by the client: ${pongEvent.id}`); + return; + } + + // Calculate the latency between the client and the server + const latency = Date.now() - Number(pingEvent.timestamp); + }); } +function pingPusher() { + // Send a PING event to the server with a specific ID and timestamp + // The server will response with a PONG event with the same ID and timestamp + // Then we can calculate the latency between the client and the server (or if the server never replies) + const pingID = NumberUtils.rand64(); + const timestamp = Date.now(); + const eventPayload: Pusher.PingPongEvent = { + id: pingID, + timestamp, + }; + Pusher.sendEvent(PusherUtils.getUserChannelName(currentUserAccountID.toString()), Pusher.TYPE.PING, eventPayload); + pingIDsAndTimestamps[pingID] = timestamp; + Log.info(`[Pusher PINGPONG] Sending a PING to the server: ${pingID} timestamp: ${timestamp}`); +} + +// Specify how long between each PING event to the server +const PING_PONG_INTERVAL_LENGTH_IN_SECONDS = 60000; let pingPongStarted = false; function initializePusherPingPong() { // Ignore any additional calls to initialize the ping pong if it's already been started @@ -904,11 +938,10 @@ function initializePusherPingPong() { return; } - pingPongStarted = true; -} + subscribeToPusherPong(); + setInterval(pingPusher, PING_PONG_INTERVAL_LENGTH_IN_SECONDS); -function pingPusher() { - Pusher.sendEvent(privateReportChannelName, Pusher.TYPE.USER_IS_TYPING, typingStatus); + pingPongStarted = true; } /** From 0bf599366cad39b830b0b2c0f638ba24dab2c1b8 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Tue, 21 Jan 2025 11:00:18 -0700 Subject: [PATCH 05/22] Switch to sending the ping as an API request --- src/libs/API/parameters/PusherPingParams.ts | 6 ++++++ src/libs/API/parameters/index.ts | 1 + src/libs/API/types.ts | 2 ++ src/libs/Pusher/EventType.ts | 5 +---- src/libs/Pusher/pusher.ts | 1 - src/libs/actions/User.ts | 21 ++++++++++++--------- 6 files changed, 22 insertions(+), 14 deletions(-) create mode 100644 src/libs/API/parameters/PusherPingParams.ts diff --git a/src/libs/API/parameters/PusherPingParams.ts b/src/libs/API/parameters/PusherPingParams.ts new file mode 100644 index 000000000000..c0bd5df3b12d --- /dev/null +++ b/src/libs/API/parameters/PusherPingParams.ts @@ -0,0 +1,6 @@ +type PusherPingParams = { + id: string; + timestamp: number; +}; + +export default PusherPingParams; diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index 4d02027b720a..5494aa50a201 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -47,6 +47,7 @@ export type {default as OpenReimbursementAccountPageParams} from './OpenReimburs export type {default as OpenReportParams} from './OpenReportParams'; export type {default as OpenRoomMembersPageParams} from './OpenRoomMembersPageParams'; export type {default as PaymentCardParams} from './PaymentCardParams'; +export type {default as PusherPingParams} from './PusherPingParams'; export type {default as ReconnectAppParams} from './ReconnectAppParams'; export type {default as ReferTeachersUniteVolunteerParams} from './ReferTeachersUniteVolunteerParams'; export type {default as ReportVirtualExpensifyCardFraudParams} from './ReportVirtualExpensifyCardFraudParams'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index 72251fd817dc..e57b65322b87 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -123,6 +123,7 @@ const WRITE_COMMANDS = { ADD_EMOJI_REACTION: 'AddEmojiReaction', REMOVE_EMOJI_REACTION: 'RemoveEmojiReaction', LEAVE_ROOM: 'LeaveRoom', + PUSHER_PING: 'PusherPing', LEAVE_GROUP_CHAT: 'LeaveGroupChat', INVITE_TO_ROOM: 'InviteToRoom', INVITE_TO_GROUP_CHAT: 'InviteToGroupChat', @@ -558,6 +559,7 @@ type WriteCommandParameters = { [WRITE_COMMANDS.INVITE_TO_ROOM]: Parameters.InviteToRoomParams; [WRITE_COMMANDS.INVITE_TO_GROUP_CHAT]: Parameters.InviteToGroupChatParams; [WRITE_COMMANDS.UPDATE_GROUP_CHAT_AVATAR]: Parameters.UpdateGroupChatAvatarParams; + [WRITE_COMMANDS.PUSHER_PING]: Parameters.PusherPingParams; [WRITE_COMMANDS.LEAVE_GROUP_CHAT]: Parameters.LeaveGroupChatParams; [WRITE_COMMANDS.REMOVE_FROM_GROUP_CHAT]: Parameters.RemoveFromGroupChatParams; [WRITE_COMMANDS.UPDATE_GROUP_CHAT_MEMBER_ROLES]: Parameters.UpdateGroupChatMemberRolesParams; diff --git a/src/libs/Pusher/EventType.ts b/src/libs/Pusher/EventType.ts index 4b6938d7e9bc..b29aecac5f5b 100644 --- a/src/libs/Pusher/EventType.ts +++ b/src/libs/Pusher/EventType.ts @@ -9,10 +9,7 @@ export default { USER_IS_TYPING: 'client-userIsTyping', MULTIPLE_EVENTS: 'multipleEvents', - // An event that the client sends to the server and then listens for a returned "pong" event - PING: 'ping', - - // An event that the server sends back to the client in response to a "ping" event + // An event that the server sends back to the client in response to a "ping" API command PONG: 'pong', MULTIPLE_EVENT_TYPE: { ONYX_API_UPDATE: 'onyxApiUpdate', diff --git a/src/libs/Pusher/pusher.ts b/src/libs/Pusher/pusher.ts index b97e2b23a420..c05bb4f1b1b4 100644 --- a/src/libs/Pusher/pusher.ts +++ b/src/libs/Pusher/pusher.ts @@ -39,7 +39,6 @@ type PingPongEvent = Record & { type PusherEventMap = { [TYPE.USER_IS_TYPING]: UserIsTypingEvent; [TYPE.USER_IS_LEAVING_ROOM]: UserIsLeavingRoomEvent; - [TYPE.PING]: PingPongEvent; [TYPE.PONG]: PingPongEvent; }; diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index 28293d404f77..32330d30916a 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -9,6 +9,7 @@ import type { CloseAccountParams, DeleteContactMethodParams, GetStatementPDFParams, + PusherPingParams, RequestContactMethodValidateCodeParams, SetContactMethodAsDefaultParams, SetNameValuePairParams, @@ -891,7 +892,7 @@ function playSoundForMessageType(pushJSON: OnyxServerUpdate[]) { // Holds a map of all the PINGs that have been sent to the server and when they were sent // Once a PONG is received, the event data will be removed from this map. -type PingPongTimestampMap = Record; +type PingPongTimestampMap = Record; const pingIDsAndTimestamps: PingPongTimestampMap = {}; function subscribeToPusherPong() { @@ -903,14 +904,16 @@ function subscribeToPusherPong() { PusherUtils.subscribeToPrivateUserChannelEvent(Pusher.TYPE.PONG, currentUserAccountID.toString(), (pushJSON) => { const pongEvent = pushJSON as Pusher.PingPongEvent; // First, check to see if the PONG event is in the pingIDsAndTimestamps map - const pingEvent = pingIDsAndTimestamps[pongEvent.id]; - if (!pingEvent) { + const pingEventTimestamp = pingIDsAndTimestamps[pongEvent.id]; + if (!pingEventTimestamp) { Log.info(`[Pusher PINGPONG] Received a PONG event from the server with an ID that wasn't sent by the client: ${pongEvent.id}`); return; } // Calculate the latency between the client and the server - const latency = Date.now() - Number(pingEvent.timestamp); + const latency = Date.now() - Number(pingEventTimestamp); + + // TODO implement a way to tell the client it's offline }); } @@ -920,12 +923,9 @@ function pingPusher() { // Then we can calculate the latency between the client and the server (or if the server never replies) const pingID = NumberUtils.rand64(); const timestamp = Date.now(); - const eventPayload: Pusher.PingPongEvent = { - id: pingID, - timestamp, - }; - Pusher.sendEvent(PusherUtils.getUserChannelName(currentUserAccountID.toString()), Pusher.TYPE.PING, eventPayload); pingIDsAndTimestamps[pingID] = timestamp; + const parameters: PusherPingParams = {id: pingID, timestamp}; + API.write(WRITE_COMMANDS.PUSHER_PING, parameters); Log.info(`[Pusher PINGPONG] Sending a PING to the server: ${pingID} timestamp: ${timestamp}`); } @@ -938,6 +938,7 @@ function initializePusherPingPong() { return; } + Log.info(`[Pusher PINGPONG] Starting Pusher Ping Pong and pinging every ${PING_PONG_INTERVAL_LENGTH_IN_SECONDS} seconds`); subscribeToPusherPong(); setInterval(pingPusher, PING_PONG_INTERVAL_LENGTH_IN_SECONDS); @@ -1004,6 +1005,8 @@ function subscribeToUserEvents() { App.reconnectApp(); return Promise.resolve(); }); + + initializePusherPingPong(); } /** From d3ceb2e8e832cf0163386e5ae515227a83e6de91 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Tue, 21 Jan 2025 11:45:13 -0700 Subject: [PATCH 06/22] Change parameter names --- src/libs/API/parameters/PusherPingParams.ts | 4 ++-- src/libs/Pusher/pusher.ts | 2 +- src/libs/actions/User.ts | 18 ++++++++++-------- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/libs/API/parameters/PusherPingParams.ts b/src/libs/API/parameters/PusherPingParams.ts index c0bd5df3b12d..b156d6fa6039 100644 --- a/src/libs/API/parameters/PusherPingParams.ts +++ b/src/libs/API/parameters/PusherPingParams.ts @@ -1,6 +1,6 @@ type PusherPingParams = { - id: string; - timestamp: number; + pingID: string; + pingTimestamp: number; }; export default PusherPingParams; diff --git a/src/libs/Pusher/pusher.ts b/src/libs/Pusher/pusher.ts index c05bb4f1b1b4..e4b0cee4622b 100644 --- a/src/libs/Pusher/pusher.ts +++ b/src/libs/Pusher/pusher.ts @@ -32,7 +32,7 @@ type UserIsLeavingRoomEvent = Record & { }; type PingPongEvent = Record & { - id: string; + pingID: string; timestamp: number; }; diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index 32330d30916a..d99d5b2899fc 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -902,16 +902,18 @@ function subscribeToPusherPong() { } PusherUtils.subscribeToPrivateUserChannelEvent(Pusher.TYPE.PONG, currentUserAccountID.toString(), (pushJSON) => { + Log.info(`[Pusher PINGPONG] Received a PONG event from the server`, false, pushJSON); const pongEvent = pushJSON as Pusher.PingPongEvent; // First, check to see if the PONG event is in the pingIDsAndTimestamps map - const pingEventTimestamp = pingIDsAndTimestamps[pongEvent.id]; + const pingEventTimestamp = pingIDsAndTimestamps[pongEvent.pingID]; if (!pingEventTimestamp) { - Log.info(`[Pusher PINGPONG] Received a PONG event from the server with an ID that wasn't sent by the client: ${pongEvent.id}`); + Log.info(`[Pusher PINGPONG] Received a PONG event from the server with an ID that wasn't sent by the client: ${pongEvent.pingID}`); return; } // Calculate the latency between the client and the server const latency = Date.now() - Number(pingEventTimestamp); + Log.info(`[Pusher PINGPONG] The event took ${latency} ms`); // TODO implement a way to tell the client it's offline }); @@ -922,15 +924,15 @@ function pingPusher() { // The server will response with a PONG event with the same ID and timestamp // Then we can calculate the latency between the client and the server (or if the server never replies) const pingID = NumberUtils.rand64(); - const timestamp = Date.now(); - pingIDsAndTimestamps[pingID] = timestamp; - const parameters: PusherPingParams = {id: pingID, timestamp}; + const pingTimestamp = Date.now(); + pingIDsAndTimestamps[pingID] = pingTimestamp; + const parameters: PusherPingParams = {pingID, pingTimestamp}; API.write(WRITE_COMMANDS.PUSHER_PING, parameters); - Log.info(`[Pusher PINGPONG] Sending a PING to the server: ${pingID} timestamp: ${timestamp}`); + Log.info(`[Pusher PINGPONG] Sending a PING to the server: ${pingID} timestamp: ${pingTimestamp}`); } // Specify how long between each PING event to the server -const PING_PONG_INTERVAL_LENGTH_IN_SECONDS = 60000; +const PING_PONG_INTERVAL_LENGTH_IN_SECONDS = 5; let pingPongStarted = false; function initializePusherPingPong() { // Ignore any additional calls to initialize the ping pong if it's already been started @@ -940,7 +942,7 @@ function initializePusherPingPong() { Log.info(`[Pusher PINGPONG] Starting Pusher Ping Pong and pinging every ${PING_PONG_INTERVAL_LENGTH_IN_SECONDS} seconds`); subscribeToPusherPong(); - setInterval(pingPusher, PING_PONG_INTERVAL_LENGTH_IN_SECONDS); + setInterval(pingPusher, PING_PONG_INTERVAL_LENGTH_IN_SECONDS * 1000); pingPongStarted = true; } From a37ea8006d9fd71e3f2f053c5d75268833563110 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Tue, 21 Jan 2025 11:59:51 -0700 Subject: [PATCH 07/22] Protect against multiple loops --- src/libs/actions/User.ts | 40 +++++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index d99d5b2899fc..23057df719e8 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -915,36 +915,62 @@ function subscribeToPusherPong() { const latency = Date.now() - Number(pingEventTimestamp); Log.info(`[Pusher PINGPONG] The event took ${latency} ms`); - // TODO implement a way to tell the client it's offline + // Remove the event from the map + delete pingIDsAndTimestamps[pongEvent.pingID]; }); } +// Specify how long between each PING event to the server +const PING_INTERVAL_LENGTH_IN_SECONDS = 5; + +// Specify how long between each check for missing PONG events +const CHECK_MISSING_PONG_INTERVAL_LENGTH_IN_SECONDS = 5; + +let lastTimestamp = Date.now(); function pingPusher() { // Send a PING event to the server with a specific ID and timestamp - // The server will response with a PONG event with the same ID and timestamp + // The server will respond with a PONG event with the same ID and timestamp // Then we can calculate the latency between the client and the server (or if the server never replies) const pingID = NumberUtils.rand64(); const pingTimestamp = Date.now(); + + if (pingTimestamp - lastTimestamp < PING_INTERVAL_LENGTH_IN_SECONDS * 1000) { + Log.info(`[Pusher PINGPONG] Skipping PING event because the last event was sent too recently ${pingTimestamp - lastTimestamp} ${PING_INTERVAL_LENGTH_IN_SECONDS * 1000}`); + return; + } + pingIDsAndTimestamps[pingID] = pingTimestamp; const parameters: PusherPingParams = {pingID, pingTimestamp}; API.write(WRITE_COMMANDS.PUSHER_PING, parameters); Log.info(`[Pusher PINGPONG] Sending a PING to the server: ${pingID} timestamp: ${pingTimestamp}`); + lastTimestamp = pingTimestamp; } -// Specify how long between each PING event to the server -const PING_PONG_INTERVAL_LENGTH_IN_SECONDS = 5; +function checkforMissingPongEvents() {} + let pingPongStarted = false; function initializePusherPingPong() { // Ignore any additional calls to initialize the ping pong if it's already been started if (pingPongStarted) { return; } + pingPongStarted = true; - Log.info(`[Pusher PINGPONG] Starting Pusher Ping Pong and pinging every ${PING_PONG_INTERVAL_LENGTH_IN_SECONDS} seconds`); + Log.info(`[Pusher PINGPONG] Starting Pusher Ping Pong and pinging every ${PING_INTERVAL_LENGTH_IN_SECONDS} seconds`); + + // Subscribe to the pong event from Pusher. Unfortunately, there is no way of knowing when the client is actually subscribed + // so there could be a little delay before the client is actually listening to this event. subscribeToPusherPong(); - setInterval(pingPusher, PING_PONG_INTERVAL_LENGTH_IN_SECONDS * 1000); - pingPongStarted = true; + // Send a ping to pusher on a regular interval + setInterval(pingPusher, PING_INTERVAL_LENGTH_IN_SECONDS * 1000); + + // Delay the start of this by double the length of PING_INTERVAL_LENGTH_IN_SECONDS to give a chance for the first + // events to be sent and received + setTimeout(() => { + // Check for any missing pong events on a regular interval + setInterval(checkforMissingPongEvents, CHECK_MISSING_PONG_INTERVAL_LENGTH_IN_SECONDS * 1000); + }, PING_INTERVAL_LENGTH_IN_SECONDS * 2); } /** From af2476428fa7c05037bba75c6ff139012df2dc06 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Tue, 21 Jan 2025 12:52:39 -0700 Subject: [PATCH 08/22] Implement offline switch --- src/libs/actions/User.ts | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index 23057df719e8..2b291f01d07f 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -29,6 +29,7 @@ import type Platform from '@libs/getPlatform/types'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; import * as SequentialQueue from '@libs/Network/SequentialQueue'; +import {setOfflineStatus} from '@libs/NetworkConnection'; import * as NumberUtils from '@libs/NumberUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as Pusher from '@libs/Pusher/pusher'; @@ -905,9 +906,10 @@ function subscribeToPusherPong() { Log.info(`[Pusher PINGPONG] Received a PONG event from the server`, false, pushJSON); const pongEvent = pushJSON as Pusher.PingPongEvent; // First, check to see if the PONG event is in the pingIDsAndTimestamps map + // It's OK if it doesn't exist. The client was maybe refreshed while still waiting for a PONG event, in which case it might + // receive the PONG event but has already lost it's memory of the PING. const pingEventTimestamp = pingIDsAndTimestamps[pongEvent.pingID]; if (!pingEventTimestamp) { - Log.info(`[Pusher PINGPONG] Received a PONG event from the server with an ID that wasn't sent by the client: ${pongEvent.pingID}`); return; } @@ -926,6 +928,9 @@ const PING_INTERVAL_LENGTH_IN_SECONDS = 5; // Specify how long between each check for missing PONG events const CHECK_MISSING_PONG_INTERVAL_LENGTH_IN_SECONDS = 5; +// Specifiy how long before a PING event is considered to be missing a PONG event in order to put the application in offline mode +const NO_EVENT_RECEIVED_TO_BE_OFFLINE_THRESHOLD_IN_SECONDS = 2 * PING_INTERVAL_LENGTH_IN_SECONDS; + let lastTimestamp = Date.now(); function pingPusher() { // Send a PING event to the server with a specific ID and timestamp @@ -934,22 +939,44 @@ function pingPusher() { const pingID = NumberUtils.rand64(); const pingTimestamp = Date.now(); + // In local development, there can end up being multiple intervals running because when JS code is replaced with hot module replacement, the old interval is not cleared + // and keeps running. This little bit of logic will attempt to keep multiple pings from happening. if (pingTimestamp - lastTimestamp < PING_INTERVAL_LENGTH_IN_SECONDS * 1000) { - Log.info(`[Pusher PINGPONG] Skipping PING event because the last event was sent too recently ${pingTimestamp - lastTimestamp} ${PING_INTERVAL_LENGTH_IN_SECONDS * 1000}`); return; } + lastTimestamp = pingTimestamp; pingIDsAndTimestamps[pingID] = pingTimestamp; const parameters: PusherPingParams = {pingID, pingTimestamp}; API.write(WRITE_COMMANDS.PUSHER_PING, parameters); Log.info(`[Pusher PINGPONG] Sending a PING to the server: ${pingID} timestamp: ${pingTimestamp}`); - lastTimestamp = pingTimestamp; } -function checkforMissingPongEvents() {} +function checkforMissingPongEvents() { + Log.info(`[Pusher PINGPONG] Checking for missing PONG events`); + // Get the oldest PING timestamp that is left in the event map + const oldestPingTimestamp = Math.min(...Object.values(pingIDsAndTimestamps)); + const ageOfEventInMS = Date.now() - oldestPingTimestamp; + + // Get the eventID of that timestamp + const eventID = Object.keys(pingIDsAndTimestamps).find((key) => pingIDsAndTimestamps[key] === oldestPingTimestamp); + + // If the oldest timestamp is older than 2 * PING_INTERVAL_LENGTH_IN_SECONDS, then log a message to the console. + // This means that the server never replied to the PING event. + if (ageOfEventInMS > NO_EVENT_RECEIVED_TO_BE_OFFLINE_THRESHOLD_IN_SECONDS * 1000) { + Log.info(`[Pusher PINGPONG] The server has not replied to the PING event ${eventID} in ${ageOfEventInMS} ms so going offline`); + setOfflineStatus(true, 'The client never got a Pusher PONG event after sending a Pusher PING event'); + } +} let pingPongStarted = false; function initializePusherPingPong() { + // Only run the ping pong from the leader client + if (!ActiveClientManager.isClientTheLeader()) { + Log.info("[Pusher PINGPONG] Not starting PING PONG because this instance isn't the leader client"); + return; + } + // Ignore any additional calls to initialize the ping pong if it's already been started if (pingPongStarted) { return; From d2659fe723213619865b233201308a7a14c2f3fd Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Tue, 21 Jan 2025 13:04:48 -0700 Subject: [PATCH 09/22] Reset when going offline --- src/libs/actions/User.ts | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index 2b291f01d07f..fb79faf2d58d 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -1,4 +1,5 @@ import {isBefore} from 'date-fns'; +import {Network} from 'expensify-common'; import type {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; @@ -28,8 +29,9 @@ import * as ErrorUtils from '@libs/ErrorUtils'; import type Platform from '@libs/getPlatform/types'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; +import {isOffline} from '@libs/Network/NetworkStore'; import * as SequentialQueue from '@libs/Network/SequentialQueue'; -import {setOfflineStatus} from '@libs/NetworkConnection'; +import NetworkConnection from '@libs/NetworkConnection'; import * as NumberUtils from '@libs/NumberUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as Pusher from '@libs/Pusher/pusher'; @@ -894,7 +896,7 @@ function playSoundForMessageType(pushJSON: OnyxServerUpdate[]) { // Holds a map of all the PINGs that have been sent to the server and when they were sent // Once a PONG is received, the event data will be removed from this map. type PingPongTimestampMap = Record; -const pingIDsAndTimestamps: PingPongTimestampMap = {}; +let pingIDsAndTimestamps: PingPongTimestampMap = {}; function subscribeToPusherPong() { // If there is no user accountID yet (because the app isn't fully setup yet), the channel can't be subscribed to so return early @@ -923,16 +925,20 @@ function subscribeToPusherPong() { } // Specify how long between each PING event to the server -const PING_INTERVAL_LENGTH_IN_SECONDS = 5; +const PING_INTERVAL_LENGTH_IN_SECONDS = 30; // Specify how long between each check for missing PONG events -const CHECK_MISSING_PONG_INTERVAL_LENGTH_IN_SECONDS = 5; +const CHECK_MISSING_PONG_INTERVAL_LENGTH_IN_SECONDS = 60; // Specifiy how long before a PING event is considered to be missing a PONG event in order to put the application in offline mode const NO_EVENT_RECEIVED_TO_BE_OFFLINE_THRESHOLD_IN_SECONDS = 2 * PING_INTERVAL_LENGTH_IN_SECONDS; let lastTimestamp = Date.now(); function pingPusher() { + if (isOffline()) { + Log.info('[Pusher PINGPONG] Skipping ping because the client is offline'); + return; + } // Send a PING event to the server with a specific ID and timestamp // The server will respond with a PONG event with the same ID and timestamp // Then we can calculate the latency between the client and the server (or if the server never replies) @@ -965,7 +971,11 @@ function checkforMissingPongEvents() { // This means that the server never replied to the PING event. if (ageOfEventInMS > NO_EVENT_RECEIVED_TO_BE_OFFLINE_THRESHOLD_IN_SECONDS * 1000) { Log.info(`[Pusher PINGPONG] The server has not replied to the PING event ${eventID} in ${ageOfEventInMS} ms so going offline`); - setOfflineStatus(true, 'The client never got a Pusher PONG event after sending a Pusher PING event'); + NetworkConnection.setOfflineStatus(true, 'The client never got a Pusher PONG event after sending a Pusher PING event'); + + // When going offline, reset the pingpong state so that when the network reconnects, the client will start fresh + lastTimestamp = Date.now(); + pingIDsAndTimestamps = {}; } } From 6039614e40efeac035987dd38a2956fc15d2ec9e Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Tue, 21 Jan 2025 13:05:20 -0700 Subject: [PATCH 10/22] Correct the comment --- src/libs/actions/User.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index fb79faf2d58d..c03b7ac69b37 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -967,8 +967,7 @@ function checkforMissingPongEvents() { // Get the eventID of that timestamp const eventID = Object.keys(pingIDsAndTimestamps).find((key) => pingIDsAndTimestamps[key] === oldestPingTimestamp); - // If the oldest timestamp is older than 2 * PING_INTERVAL_LENGTH_IN_SECONDS, then log a message to the console. - // This means that the server never replied to the PING event. + // If the oldest timestamp is older than 2 * PING_INTERVAL_LENGTH_IN_SECONDS, then set the network status to offline if (ageOfEventInMS > NO_EVENT_RECEIVED_TO_BE_OFFLINE_THRESHOLD_IN_SECONDS * 1000) { Log.info(`[Pusher PINGPONG] The server has not replied to the PING event ${eventID} in ${ageOfEventInMS} ms so going offline`); NetworkConnection.setOfflineStatus(true, 'The client never got a Pusher PONG event after sending a Pusher PING event'); From e01d6e85ee6b1c509e2fa011d33b450f75986377 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Tue, 21 Jan 2025 13:34:22 -0700 Subject: [PATCH 11/22] Lint and TS fixes --- src/CONST.ts | 1 + src/libs/PusherUtils.ts | 11 ++++++----- src/libs/actions/User.ts | 24 ++++++++++++------------ 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 8b2c173c7ca8..371ee082209d 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1326,6 +1326,7 @@ const CONST = { SEARCH_FILTER_OPTIONS: 'search_filter_options', USE_DEBOUNCED_STATE_DELAY: 300, LIST_SCROLLING_DEBOUNCE_TIME: 200, + PUSHER_PING_PONG: 'pusher_ping_pong', }, PRIORITY_MODE: { GSD: 'gsd', diff --git a/src/libs/PusherUtils.ts b/src/libs/PusherUtils.ts index 5243090063e5..2c5f37a10ca2 100644 --- a/src/libs/PusherUtils.ts +++ b/src/libs/PusherUtils.ts @@ -4,7 +4,8 @@ import CONST from '@src/CONST'; import type {OnyxUpdatesFromServer} from '@src/types/onyx'; import Log from './Log'; import NetworkConnection from './NetworkConnection'; -import * as Pusher from './Pusher/pusher'; +import {subscribe} from './Pusher/pusher'; +import type {PingPongEvent} from './Pusher/pusher'; type Callback = (data: OnyxUpdate[]) => Promise; @@ -30,10 +31,10 @@ function triggerMultiEventHandler(eventType: string, data: OnyxUpdate[]): Promis /** * Abstraction around subscribing to private user channel events. Handles all logs and errors automatically. */ -function subscribeToPrivateUserChannelEvent(eventName: string, accountID: string, onEvent: (pushJSON: OnyxUpdatesFromServer | Pusher.PingPongEvent) => void) { +function subscribeToPrivateUserChannelEvent(eventName: string, accountID: string, onEvent: (pushJSON: OnyxUpdatesFromServer | PingPongEvent) => void) { const pusherChannelName = getUserChannelName(accountID); - function logPusherEvent(pushJSON: OnyxUpdatesFromServer | Pusher.PingPongEvent) { + function logPusherEvent(pushJSON: OnyxUpdatesFromServer | PingPongEvent) { Log.info(`[Report] Handled ${eventName} event sent by Pusher`, false, pushJSON); } @@ -41,7 +42,7 @@ function subscribeToPrivateUserChannelEvent(eventName: string, accountID: string NetworkConnection.triggerReconnectionCallbacks('Pusher re-subscribed to private user channel'); } - function onEventPush(pushJSON: OnyxUpdatesFromServer | Pusher.PingPongEvent) { + function onEventPush(pushJSON: OnyxUpdatesFromServer | PingPongEvent) { logPusherEvent(pushJSON); onEvent(pushJSON); } @@ -49,7 +50,7 @@ function subscribeToPrivateUserChannelEvent(eventName: string, accountID: string function onSubscriptionFailed(error: Error) { Log.hmmm('Failed to subscribe to Pusher channel', {error, pusherChannelName, eventName}); } - Pusher.subscribe(pusherChannelName, eventName, onEventPush, onPusherResubscribeToPrivateUserChannel).catch(onSubscriptionFailed); + subscribe(pusherChannelName, eventName, onEventPush, onPusherResubscribeToPrivateUserChannel).catch(onSubscriptionFailed); } export default { diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index c03b7ac69b37..9c0397214b04 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -1,5 +1,4 @@ import {isBefore} from 'date-fns'; -import {Network} from 'expensify-common'; import type {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; @@ -47,16 +46,16 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {BlockedFromConcierge, CustomStatusDraft, LoginList, Policy} from '@src/types/onyx'; import type Login from '@src/types/onyx/Login'; -import type {OnyxServerUpdate} from '@src/types/onyx/OnyxUpdatesFromServer'; +import type {OnyxServerUpdate, OnyxUpdatesFromServer} from '@src/types/onyx/OnyxUpdatesFromServer'; import type OnyxPersonalDetails from '@src/types/onyx/PersonalDetails'; import type {Status} from '@src/types/onyx/PersonalDetails'; import type ReportAction from '@src/types/onyx/ReportAction'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -import * as App from './App'; +import {reconnectApp} from './App'; import applyOnyxUpdatesReliably from './applyOnyxUpdatesReliably'; -import * as Link from './Link'; -import * as Report from './Report'; -import * as Session from './Session'; +import {openOldDotLink} from './Link'; +import {showReportActionNotification} from './Report'; +import {resendValidateCode as sessionResendValidateCode} from './Session'; let currentUserAccountID = -1; let currentEmail = ''; @@ -121,7 +120,7 @@ function closeAccount(reason: string) { * Resends a validation link to a given login */ function resendValidateCode(login: string) { - Session.resendValidateCode(login); + sessionResendValidateCode(login); } /** @@ -789,7 +788,7 @@ function triggerNotifications(onyxUpdates: OnyxServerUpdate[]) { const reportID = update.key.replace(ONYXKEYS.COLLECTION.REPORT_ACTIONS, ''); const reportActions = Object.values((update.value as OnyxCollection) ?? {}); - reportActions.forEach((action) => action && ReportActionsUtils.isNotifiableReportAction(action) && Report.showReportActionNotification(reportID, action)); + reportActions.forEach((action) => action && ReportActionsUtils.isNotifiableReportAction(action) && showReportActionNotification(reportID, action)); }); } @@ -1022,6 +1021,7 @@ function subscribeToUserEvents() { // Handles the mega multipleEvents from Pusher which contains an array of single events. // Each single event is passed to PusherUtils in order to trigger the callbacks for that event PusherUtils.subscribeToPrivateUserChannelEvent(Pusher.TYPE.MULTIPLE_EVENTS, currentUserAccountID.toString(), (pushJSON) => { + const pushEventData = pushJSON as OnyxUpdatesFromServer; // If this is not the main client, we shouldn't process any data received from pusher. if (!ActiveClientManager.isClientTheLeader()) { Log.info('[Pusher] Received updates, but ignoring it since this is not the active client'); @@ -1031,8 +1031,8 @@ function subscribeToUserEvents() { // Example: {lastUpdateID: 1, previousUpdateID: 0, updates: [{onyxMethod: 'whatever', key: 'foo', value: 'bar'}]} const updates = { type: CONST.ONYX_UPDATE_TYPES.PUSHER, - lastUpdateID: Number(pushJSON.lastUpdateID ?? CONST.DEFAULT_NUMBER_ID), - updates: pushJSON.updates ?? [], + lastUpdateID: Number(pushEventData.lastUpdateID ?? CONST.DEFAULT_NUMBER_ID), + updates: pushEventData.updates ?? [], previousUpdateID: Number(pushJSON.previousUpdateID ?? CONST.DEFAULT_NUMBER_ID), }; applyOnyxUpdatesReliably(updates); @@ -1066,7 +1066,7 @@ function subscribeToUserEvents() { // We have an event to reconnect the App. It is triggered when we detect that the user passed updateID // is not in the DB PusherUtils.subscribeToMultiEvent(Pusher.TYPE.MULTIPLE_EVENT_TYPE.RECONNECT_APP, () => { - App.reconnectApp(); + reconnectApp(); return Promise.resolve(); }); @@ -1176,7 +1176,7 @@ function clearScreenShareRequest() { * @param roomName Name of the screen share room to join */ function joinScreenShare(accessToken: string, roomName: string) { - Link.openOldDotLink(`inbox?action=screenShare&accessToken=${accessToken}&name=${roomName}`); + openOldDotLink(`inbox?action=screenShare&accessToken=${accessToken}&name=${roomName}`); clearScreenShareRequest(); } From 9a3724a0302dff1a675bf8251f2dafb77f4646ce Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Tue, 21 Jan 2025 13:42:11 -0700 Subject: [PATCH 12/22] Add performance monitoring --- contributingGuides/PERFORMANCE_METRICS.md | 1 + src/libs/actions/User.ts | 3 +++ 2 files changed, 4 insertions(+) diff --git a/contributingGuides/PERFORMANCE_METRICS.md b/contributingGuides/PERFORMANCE_METRICS.md index 9e942f21d918..d8bf79970006 100644 --- a/contributingGuides/PERFORMANCE_METRICS.md +++ b/contributingGuides/PERFORMANCE_METRICS.md @@ -24,6 +24,7 @@ Project is using Firebase for tracking these metrics. However, not all of them a | `open_report_from_preview` | ✅ | Time taken to open a report from preview.

(previously `switch_report_from_preview`)

**Platforms:** All | Starts when the user presses the Report Preview. | Stops when the `ReportActionsList` finishes laying out. | | `open_report_thread` | ✅ | Time taken to open a thread in a report.

**Platforms:** All | Starts when user presses Report Action Item. | Stops when the `ReportActionsList` finishes laying out. | | `send_message` | ✅ | Time taken to send a message.

**Platforms:** All | Starts when the new message is sent. | Stops when the message is being rendered in the chat. | +| `pusher_ping_pong` | ✅ | The time it takes to receive a PONG event through Pusher.

**Platforms:** All | Starts every minute and repeats on the minute. | Stops when the event is received from the server. | ## Documentation Maintenance diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index 9c0397214b04..b772d229ca56 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -51,6 +51,7 @@ import type OnyxPersonalDetails from '@src/types/onyx/PersonalDetails'; import type {Status} from '@src/types/onyx/PersonalDetails'; import type ReportAction from '@src/types/onyx/ReportAction'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import Performance from '../Performance'; import {reconnectApp} from './App'; import applyOnyxUpdatesReliably from './applyOnyxUpdatesReliably'; import {openOldDotLink} from './Link'; @@ -920,6 +921,7 @@ function subscribeToPusherPong() { // Remove the event from the map delete pingIDsAndTimestamps[pongEvent.pingID]; + Performance.markEnd(CONST.TIMING.PUSHER_PING_PONG); }); } @@ -955,6 +957,7 @@ function pingPusher() { const parameters: PusherPingParams = {pingID, pingTimestamp}; API.write(WRITE_COMMANDS.PUSHER_PING, parameters); Log.info(`[Pusher PINGPONG] Sending a PING to the server: ${pingID} timestamp: ${pingTimestamp}`); + Performance.markStart(CONST.TIMING.PUSHER_PING_PONG); } function checkforMissingPongEvents() { From 4081444f42ac27ef109370c7d0a4083d650cf78d Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Tue, 21 Jan 2025 13:55:16 -0700 Subject: [PATCH 13/22] Fix import --- src/libs/actions/User.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index b772d229ca56..ad7d974404e0 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -32,6 +32,7 @@ import {isOffline} from '@libs/Network/NetworkStore'; import * as SequentialQueue from '@libs/Network/SequentialQueue'; import NetworkConnection from '@libs/NetworkConnection'; import * as NumberUtils from '@libs/NumberUtils'; +import Performance from '@libs/Performance'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as Pusher from '@libs/Pusher/pusher'; import PusherUtils from '@libs/PusherUtils'; @@ -51,7 +52,6 @@ import type OnyxPersonalDetails from '@src/types/onyx/PersonalDetails'; import type {Status} from '@src/types/onyx/PersonalDetails'; import type ReportAction from '@src/types/onyx/ReportAction'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -import Performance from '../Performance'; import {reconnectApp} from './App'; import applyOnyxUpdatesReliably from './applyOnyxUpdatesReliably'; import {openOldDotLink} from './Link'; From 6857762e047dbf267233fc5c700781e4a7f5b105 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Fri, 24 Jan 2025 09:16:22 -0700 Subject: [PATCH 14/22] Use timing library --- src/libs/actions/User.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index ad7d974404e0..4f334681002c 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -32,7 +32,7 @@ import {isOffline} from '@libs/Network/NetworkStore'; import * as SequentialQueue from '@libs/Network/SequentialQueue'; import NetworkConnection from '@libs/NetworkConnection'; import * as NumberUtils from '@libs/NumberUtils'; -import Performance from '@libs/Performance'; +import Timing from '@userActions/Timing'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as Pusher from '@libs/Pusher/pusher'; import PusherUtils from '@libs/PusherUtils'; @@ -921,7 +921,7 @@ function subscribeToPusherPong() { // Remove the event from the map delete pingIDsAndTimestamps[pongEvent.pingID]; - Performance.markEnd(CONST.TIMING.PUSHER_PING_PONG); + Timing.end(CONST.TIMING.OPEN_REPORT); }); } @@ -957,7 +957,7 @@ function pingPusher() { const parameters: PusherPingParams = {pingID, pingTimestamp}; API.write(WRITE_COMMANDS.PUSHER_PING, parameters); Log.info(`[Pusher PINGPONG] Sending a PING to the server: ${pingID} timestamp: ${pingTimestamp}`); - Performance.markStart(CONST.TIMING.PUSHER_PING_PONG); + Timing.start(CONST.TIMING.OPEN_REPORT); } function checkforMissingPongEvents() { From a1782f4f52c0c683a39ee394a0396e36d93a6dd0 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Fri, 24 Jan 2025 09:31:29 -0700 Subject: [PATCH 15/22] Fix the constant --- Mobile-Expensify | 2 +- src/libs/actions/User.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index e8427456032e..20be9a151000 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit e8427456032e803863da64610f17a2ea948b9b75 +Subproject commit 20be9a151000b756fcbf7c028bd59405695e41d6 diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index 4f334681002c..fd2f1386a4db 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -32,7 +32,6 @@ import {isOffline} from '@libs/Network/NetworkStore'; import * as SequentialQueue from '@libs/Network/SequentialQueue'; import NetworkConnection from '@libs/NetworkConnection'; import * as NumberUtils from '@libs/NumberUtils'; -import Timing from '@userActions/Timing'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as Pusher from '@libs/Pusher/pusher'; import PusherUtils from '@libs/PusherUtils'; @@ -41,6 +40,7 @@ import * as ReportUtils from '@libs/ReportUtils'; import playSound, {SOUNDS} from '@libs/Sound'; import playSoundExcludingMobile from '@libs/Sound/playSoundExcludingMobile'; import Visibility from '@libs/Visibility'; +import Timing from '@userActions/Timing'; import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -921,7 +921,7 @@ function subscribeToPusherPong() { // Remove the event from the map delete pingIDsAndTimestamps[pongEvent.pingID]; - Timing.end(CONST.TIMING.OPEN_REPORT); + Timing.end(CONST.TIMING.PUSHER_PING_PONG); }); } @@ -957,7 +957,7 @@ function pingPusher() { const parameters: PusherPingParams = {pingID, pingTimestamp}; API.write(WRITE_COMMANDS.PUSHER_PING, parameters); Log.info(`[Pusher PINGPONG] Sending a PING to the server: ${pingID} timestamp: ${pingTimestamp}`); - Timing.start(CONST.TIMING.OPEN_REPORT); + Timing.start(CONST.TIMING.PUSHER_PING_PONG); } function checkforMissingPongEvents() { From 1f2fa1f2d8ba0971aec79387a283ca15eadb92d2 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Mon, 27 Jan 2025 10:09:15 -0700 Subject: [PATCH 16/22] Remove export --- src/libs/PusherUtils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/PusherUtils.ts b/src/libs/PusherUtils.ts index 2c5f37a10ca2..547aa06e770e 100644 --- a/src/libs/PusherUtils.ts +++ b/src/libs/PusherUtils.ts @@ -54,7 +54,6 @@ function subscribeToPrivateUserChannelEvent(eventName: string, accountID: string } export default { - getUserChannelName, subscribeToPrivateUserChannelEvent, subscribeToMultiEvent, triggerMultiEventHandler, From c8cd3f682cce06e3e7854b7b0e98634d32a12f1b Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Mon, 27 Jan 2025 10:20:35 -0700 Subject: [PATCH 17/22] Update Mobile-Expensify --- Mobile-Expensify | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index 20be9a151000..10beb38304d3 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit 20be9a151000b756fcbf7c028bd59405695e41d6 +Subproject commit 10beb38304d35ee86b948f64afcdf49d51ab3d4a From f92d8cc7149b73eea127f32d1c54b9c81df90ea0 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Mon, 27 Jan 2025 10:31:07 -0700 Subject: [PATCH 18/22] Reset submodule --- Mobile-Expensify | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index 10beb38304d3..e8427456032e 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit 10beb38304d35ee86b948f64afcdf49d51ab3d4a +Subproject commit e8427456032e803863da64610f17a2ea948b9b75 From f222260d4314081921ea8430e0295ace1a86165b Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Mon, 27 Jan 2025 14:09:27 -0700 Subject: [PATCH 19/22] Fix lint --- src/libs/actions/User.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index fd2f1386a4db..dcc4dc319d83 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -40,7 +40,7 @@ import * as ReportUtils from '@libs/ReportUtils'; import playSound, {SOUNDS} from '@libs/Sound'; import playSoundExcludingMobile from '@libs/Sound/playSoundExcludingMobile'; import Visibility from '@libs/Visibility'; -import Timing from '@userActions/Timing'; +import Timing from './Timing'; import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; From d745da0496505695d207aa8b9ce127171b01b4af Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Mon, 27 Jan 2025 14:26:05 -0700 Subject: [PATCH 20/22] Move import --- src/libs/actions/User.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index dcc4dc319d83..10111c9b55b0 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -40,7 +40,6 @@ import * as ReportUtils from '@libs/ReportUtils'; import playSound, {SOUNDS} from '@libs/Sound'; import playSoundExcludingMobile from '@libs/Sound/playSoundExcludingMobile'; import Visibility from '@libs/Visibility'; -import Timing from './Timing'; import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -52,6 +51,7 @@ import type OnyxPersonalDetails from '@src/types/onyx/PersonalDetails'; import type {Status} from '@src/types/onyx/PersonalDetails'; import type ReportAction from '@src/types/onyx/ReportAction'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import Timing from './Timing'; import {reconnectApp} from './App'; import applyOnyxUpdatesReliably from './applyOnyxUpdatesReliably'; import {openOldDotLink} from './Link'; From 46449aa86a892a22cf9b786708ddd496b12c1a05 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Mon, 27 Jan 2025 14:29:34 -0700 Subject: [PATCH 21/22] Srsly --- src/libs/actions/User.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index 10111c9b55b0..c779c7ed6774 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -51,12 +51,12 @@ import type OnyxPersonalDetails from '@src/types/onyx/PersonalDetails'; import type {Status} from '@src/types/onyx/PersonalDetails'; import type ReportAction from '@src/types/onyx/ReportAction'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -import Timing from './Timing'; import {reconnectApp} from './App'; import applyOnyxUpdatesReliably from './applyOnyxUpdatesReliably'; import {openOldDotLink} from './Link'; import {showReportActionNotification} from './Report'; import {resendValidateCode as sessionResendValidateCode} from './Session'; +import Timing from './Timing'; let currentUserAccountID = -1; let currentEmail = ''; From 600c9f02abc70fbb6e7db5b194488177bbe74a5f Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 29 Jan 2025 10:43:13 -0700 Subject: [PATCH 22/22] Update src/libs/actions/User.ts Co-authored-by: Vit Horacek <36083550+mountiny@users.noreply.github.com> --- src/libs/actions/User.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index c779c7ed6774..9764296c3a9a 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -931,7 +931,7 @@ const PING_INTERVAL_LENGTH_IN_SECONDS = 30; // Specify how long between each check for missing PONG events const CHECK_MISSING_PONG_INTERVAL_LENGTH_IN_SECONDS = 60; -// Specifiy how long before a PING event is considered to be missing a PONG event in order to put the application in offline mode +// Specify how long before a PING event is considered to be missing a PONG event in order to put the application in offline mode const NO_EVENT_RECEIVED_TO_BE_OFFLINE_THRESHOLD_IN_SECONDS = 2 * PING_INTERVAL_LENGTH_IN_SECONDS; let lastTimestamp = Date.now();