From a1aa3458a0c6e4821496851262fe90d4d094b25b Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Mon, 25 Sep 2023 10:22:10 +0200 Subject: [PATCH 01/10] chore: move Pusher to TS --- .../Pusher/{EventType.js => EventType.ts} | 0 .../{index.native.js => index.native.ts} | 0 .../Pusher/library/{index.js => index.ts} | 0 src/libs/Pusher/{pusher.js => pusher.ts} | 143 +++++++----------- src/types/modules/pusher.d.ts | 8 + 5 files changed, 65 insertions(+), 86 deletions(-) rename src/libs/Pusher/{EventType.js => EventType.ts} (100%) rename src/libs/Pusher/library/{index.native.js => index.native.ts} (100%) rename src/libs/Pusher/library/{index.js => index.ts} (100%) rename src/libs/Pusher/{pusher.js => pusher.ts} (77%) create mode 100644 src/types/modules/pusher.d.ts diff --git a/src/libs/Pusher/EventType.js b/src/libs/Pusher/EventType.ts similarity index 100% rename from src/libs/Pusher/EventType.js rename to src/libs/Pusher/EventType.ts diff --git a/src/libs/Pusher/library/index.native.js b/src/libs/Pusher/library/index.native.ts similarity index 100% rename from src/libs/Pusher/library/index.native.js rename to src/libs/Pusher/library/index.native.ts diff --git a/src/libs/Pusher/library/index.js b/src/libs/Pusher/library/index.ts similarity index 100% rename from src/libs/Pusher/library/index.js rename to src/libs/Pusher/library/index.ts diff --git a/src/libs/Pusher/pusher.js b/src/libs/Pusher/pusher.ts similarity index 77% rename from src/libs/Pusher/pusher.js rename to src/libs/Pusher/pusher.ts index 43fde187d00b..09fd2d9b34bc 100644 --- a/src/libs/Pusher/pusher.js +++ b/src/libs/Pusher/pusher.ts @@ -1,10 +1,32 @@ import Onyx from 'react-native-onyx'; -import _ from 'underscore'; +import {Channel, ChannelAuthorizerGenerator, Options} from 'pusher-js/with-encryption'; +import isObject from 'lodash/isObject'; import ONYXKEYS from '../../ONYXKEYS'; import Pusher from './library'; import TYPE from './EventType'; import Log from '../Log'; +type States = { + previous: string; + current: string; +}; + +type Args = { + appKey: string; + cluster: string; + authEndpoint: string; +}; + +type SocketEventCallback = (eventName: string, data?: T) => void; + +type PusherWithAuthParams = Pusher & { + config: { + auth?: { + params?: unknown; + }; + }; +}; + let shouldForceOffline = false; Onyx.connect({ key: ONYXKEYS.NETWORK, @@ -16,33 +38,23 @@ Onyx.connect({ }, }); -let socket; +let socket: PusherWithAuthParams | null; let pusherSocketID = ''; -const socketEventCallbacks = []; -let customAuthorizer; +const socketEventCallbacks: SocketEventCallback[] = []; +let customAuthorizer: ChannelAuthorizerGenerator; /** * Trigger each of the socket event callbacks with the event information - * - * @param {String} eventName - * @param {*} data */ -function callSocketEventCallbacks(eventName, data) { - _.each(socketEventCallbacks, (cb) => cb(eventName, data)); +function callSocketEventCallbacks(eventName: string, data?: T) { + socketEventCallbacks.forEach((cb) => cb(eventName, data)); } /** * Initialize our pusher lib - * - * @param {Object} args - * @param {String} args.appKey - * @param {String} args.cluster - * @param {String} args.authEndpoint - * @param {Object} [params] - * @public - * @returns {Promise} resolves when Pusher has connected + * @returns resolves when Pusher has connected */ -function init(args, params) { +function init(args: Args, params?: unknown): Promise { return new Promise((resolve) => { if (socket) { return resolve(); @@ -55,7 +67,7 @@ function init(args, params) { // } // }; - const options = { + const options: Options = { cluster: args.cluster, authEndpoint: args.authEndpoint, }; @@ -77,12 +89,13 @@ function init(args, params) { } // Listen for connection errors and log them - socket.connection.bind('error', (error) => { + // TODO: check if true + socket.connection.bind('error', (error: string) => { callSocketEventCallbacks('error', error); }); socket.connection.bind('connected', () => { - pusherSocketID = socket.connection.socket_id; + pusherSocketID = socket?.connection.socket_id ?? ''; callSocketEventCallbacks('connected'); resolve(); }); @@ -91,7 +104,7 @@ function init(args, params) { callSocketEventCallbacks('disconnected'); }); - socket.connection.bind('state_change', (states) => { + socket.connection.bind('state_change', (states: States) => { callSocketEventCallbacks('state_change', states); }); }); @@ -99,12 +112,8 @@ function init(args, params) { /** * Returns a Pusher channel for a channel name - * - * @param {String} channelName - * - * @returns {Channel} */ -function getChannel(channelName) { +function getChannel(channelName: string): Channel | undefined { if (!socket) { return; } @@ -114,27 +123,22 @@ function getChannel(channelName) { /** * Binds an event callback to a channel + eventName - * @param {Pusher.Channel} channel - * @param {String} eventName - * @param {Function} [eventCallback] - * - * @private */ -function bindEventToChannel(channel, eventName, eventCallback = () => {}) { +function bindEventToChannel(channel: Channel | undefined, eventName: string, eventCallback: (data: T) => void = () => {}) { if (!eventName) { return; } - - const chunkedDataEvents = {}; - const callback = (eventData) => { + // TODO: Create a seperate type + const chunkedDataEvents: Record = {}; + const callback = (eventData: string | Record) => { if (shouldForceOffline) { Log.info('[Pusher] Ignoring a Push event because shouldForceOffline = true'); return; } - - let data; + // TODO: create a seperate type + let data: {id?: string; chunk?: unknown; final?: boolean; index: number}; try { - data = _.isObject(eventData) ? eventData : JSON.parse(eventData); + data = isObject(eventData) ? eventData : JSON.parse(eventData); } catch (err) { Log.alert('[Pusher] Unable to parse single JSON event data from Pusher', {error: err, eventData}); return; @@ -164,7 +168,7 @@ function bindEventToChannel(channel, eventName, eventCallback = () => {}) { // Only call the event callback if we've received the last packet and we don't have any holes in the complete // packet. - if (chunkedEvent.receivedFinal && chunkedEvent.chunks.length === _.keys(chunkedEvent.chunks).length) { + if (chunkedEvent.receivedFinal && chunkedEvent.chunks.length === Object.keys(chunkedEvent.chunks).length) { try { eventCallback(JSON.parse(chunkedEvent.chunks.join(''))); } catch (err) { @@ -181,22 +185,14 @@ function bindEventToChannel(channel, eventName, eventCallback = () => {}) { } }; - channel.bind(eventName, callback); + channel?.bind(eventName, callback); } /** * Subscribe to a channel and an event - * - * @param {String} channelName - * @param {String} eventName - * @param {Function} [eventCallback] - * @param {Function} [onResubscribe] Callback to be called when reconnection happen - * - * @return {Promise} - * - * @public + * @param [onResubscribe] Callback to be called when reconnection happen */ -function subscribe(channelName, eventName, eventCallback = () => {}, onResubscribe = () => {}) { +function subscribe(channelName: string, eventName: string, eventCallback = () => {}, onResubscribe = () => {}): Promise { return new Promise((resolve, reject) => { // We cannot call subscribe() before init(). Prevent any attempt to do this on dev. if (!socket) { @@ -226,7 +222,7 @@ function subscribe(channelName, eventName, eventCallback = () => {}, onResubscri onResubscribe(); }); - channel.bind('pusher:subscription_error', (data = {}) => { + channel.bind('pusher:subscription_error', (data: {type?: string; error?: string; status?: string} = {}) => { const {type, error, status} = data; Log.hmmm('[Pusher] Issue authenticating with Pusher during subscribe attempt.', { channelName, @@ -245,12 +241,8 @@ function subscribe(channelName, eventName, eventCallback = () => {}, onResubscri /** * Unsubscribe from a channel and optionally a specific event - * - * @param {String} channelName - * @param {String} [eventName] - * @public */ -function unsubscribe(channelName, eventName = '') { +function unsubscribe(channelName: string, eventName = '') { const channel = getChannel(channelName); if (!channel) { @@ -269,18 +261,14 @@ function unsubscribe(channelName, eventName = '') { Log.info('[Pusher] Unsubscribing from channel', false, {channelName}); channel.unbind(); - socket.unsubscribe(channelName); + socket?.unsubscribe(channelName); } } /** * Are we already in the process of subscribing to this channel? - * - * @param {String} channelName - * - * @returns {Boolean} */ -function isAlreadySubscribing(channelName) { +function isAlreadySubscribing(channelName: string): boolean { if (!socket) { return false; } @@ -291,12 +279,8 @@ function isAlreadySubscribing(channelName) { /** * Are we already subscribed to this channel? - * - * @param {String} channelName - * - * @returns {Boolean} */ -function isSubscribed(channelName) { +function isSubscribed(channelName: string): boolean { if (!socket) { return false; } @@ -307,12 +291,8 @@ function isSubscribed(channelName) { /** * Sends an event over a specific event/channel in pusher. - * - * @param {String} channelName - * @param {String} eventName - * @param {Object} payload */ -function sendEvent(channelName, eventName, payload) { +function sendEvent(channelName: string, eventName: string, payload: T) { // Check to see if we are subscribed to this channel before sending the event. Sending client events over channels // we are not subscribed too will throw errors and cause reconnection attempts. Subscriptions are not instant and // can happen later than we expect. @@ -320,15 +300,13 @@ function sendEvent(channelName, eventName, payload) { return; } - socket.send_event(eventName, payload, channelName); + socket?.send_event(eventName, payload, channelName); } /** * Register a method that will be triggered when a socket event happens (like disconnecting) - * - * @param {Function} cb */ -function registerSocketEventCallback(cb) { +function registerSocketEventCallback(cb: SocketEventCallback) { socketEventCallbacks.push(cb); } @@ -336,10 +314,8 @@ function registerSocketEventCallback(cb) { * A custom authorizer allows us to take a more fine-grained approach to * authenticating Pusher. e.g. we can handle failed attempts to authorize * with an expired authToken and retry the attempt. - * - * @param {Function} authorizer */ -function registerCustomAuthorizer(authorizer) { +function registerCustomAuthorizer(authorizer: ChannelAuthorizerGenerator) { customAuthorizer = authorizer; } @@ -371,18 +347,13 @@ function reconnect() { socket.connect(); } -/** - * @returns {String} - */ -function getPusherSocketID() { +function getPusherSocketID(): string { return pusherSocketID; } if (window) { /** * Pusher socket for debugging purposes - * - * @returns {Function} */ window.getPusherInstance = () => socket; } diff --git a/src/types/modules/pusher.d.ts b/src/types/modules/pusher.d.ts new file mode 100644 index 000000000000..b54a0508c309 --- /dev/null +++ b/src/types/modules/pusher.d.ts @@ -0,0 +1,8 @@ +import Pusher from 'pusher-js/types/src/core/pusher'; + +declare global { + // eslint-disable-next-line @typescript-eslint/consistent-type-definitions + interface Window { + getPusherInstance: () => Pusher | null; + } +} From 862a98a0f0043b620e570093b69982f020fc0117 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Mon, 25 Sep 2023 11:54:46 +0200 Subject: [PATCH 02/10] fix: created separate types --- src/libs/Pusher/pusher.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/libs/Pusher/pusher.ts b/src/libs/Pusher/pusher.ts index 09fd2d9b34bc..b795f3abcccd 100644 --- a/src/libs/Pusher/pusher.ts +++ b/src/libs/Pusher/pusher.ts @@ -17,6 +17,10 @@ type Args = { authEndpoint: string; }; +type ChunkedDataEvents = {chunks: unknown[]; receivedFinal: boolean}; + +type EventData = {id?: string; chunk?: unknown; final?: boolean; index: number}; + type SocketEventCallback = (eventName: string, data?: T) => void; type PusherWithAuthParams = Pusher & { @@ -128,15 +132,15 @@ function bindEventToChannel(channel: Channel | undefined, eventName: string, eve if (!eventName) { return; } - // TODO: Create a seperate type - const chunkedDataEvents: Record = {}; + + const chunkedDataEvents: Record = {}; const callback = (eventData: string | Record) => { if (shouldForceOffline) { Log.info('[Pusher] Ignoring a Push event because shouldForceOffline = true'); return; } - // TODO: create a seperate type - let data: {id?: string; chunk?: unknown; final?: boolean; index: number}; + + let data: EventData; try { data = isObject(eventData) ? eventData : JSON.parse(eventData); } catch (err) { From ea1b3e0afa662e862dd63cb95f42a0536c7b73a2 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Mon, 25 Sep 2023 13:41:28 +0200 Subject: [PATCH 03/10] fix: create types file --- src/libs/Pusher/library/index.native.ts | 5 +++-- src/libs/Pusher/library/index.ts | 5 +++-- src/libs/Pusher/library/types.ts | 3 +++ src/libs/Pusher/pusher.ts | 10 +++++----- 4 files changed, 14 insertions(+), 9 deletions(-) create mode 100644 src/libs/Pusher/library/types.ts diff --git a/src/libs/Pusher/library/index.native.ts b/src/libs/Pusher/library/index.native.ts index 7b87d0c8bdfb..20f99ba0b0a5 100644 --- a/src/libs/Pusher/library/index.native.ts +++ b/src/libs/Pusher/library/index.native.ts @@ -2,6 +2,7 @@ * We use the pusher-js/react-native module to support pusher on native environments. * @see: https://github.com/pusher/pusher-js */ -import Pusher from 'pusher-js/react-native'; +import PusherNative from 'pusher-js/react-native'; +import Pusher from './types'; -export default Pusher; +export default PusherNative as unknown as Pusher; diff --git a/src/libs/Pusher/library/index.ts b/src/libs/Pusher/library/index.ts index 12cfae7df02f..f770acdec568 100644 --- a/src/libs/Pusher/library/index.ts +++ b/src/libs/Pusher/library/index.ts @@ -2,6 +2,7 @@ * We use the standard pusher-js module to support pusher on web environments. * @see: https://github.com/pusher/pusher-js */ -import Pusher from 'pusher-js/with-encryption'; +import PusherWeb from 'pusher-js/with-encryption'; +import Pusher from './types'; -export default Pusher; +export default PusherWeb as unknown as Pusher; diff --git a/src/libs/Pusher/library/types.ts b/src/libs/Pusher/library/types.ts new file mode 100644 index 000000000000..ae3ed28a9701 --- /dev/null +++ b/src/libs/Pusher/library/types.ts @@ -0,0 +1,3 @@ +import Pusher from 'pusher-js/with-encryption'; + +export default Pusher; diff --git a/src/libs/Pusher/pusher.ts b/src/libs/Pusher/pusher.ts index b795f3abcccd..7b4448aa14cc 100644 --- a/src/libs/Pusher/pusher.ts +++ b/src/libs/Pusher/pusher.ts @@ -2,7 +2,7 @@ import Onyx from 'react-native-onyx'; import {Channel, ChannelAuthorizerGenerator, Options} from 'pusher-js/with-encryption'; import isObject from 'lodash/isObject'; import ONYXKEYS from '../../ONYXKEYS'; -import Pusher from './library'; +import Pusher from './library/types'; import TYPE from './EventType'; import Log from '../Log'; @@ -94,21 +94,21 @@ function init(args: Args, params?: unknown): Promise { // Listen for connection errors and log them // TODO: check if true - socket.connection.bind('error', (error: string) => { + socket?.connection.bind('error', (error: string) => { callSocketEventCallbacks('error', error); }); - socket.connection.bind('connected', () => { + socket?.connection.bind('connected', () => { pusherSocketID = socket?.connection.socket_id ?? ''; callSocketEventCallbacks('connected'); resolve(); }); - socket.connection.bind('disconnected', () => { + socket?.connection.bind('disconnected', () => { callSocketEventCallbacks('disconnected'); }); - socket.connection.bind('state_change', (states: States) => { + socket?.connection.bind('state_change', (states: States) => { callSocketEventCallbacks('state_change', states); }); }); From 36df7b5d9272084d3b8039247448b4b099a9a384 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Tue, 26 Sep 2023 10:40:13 +0200 Subject: [PATCH 04/10] fix: resolve comments --- src/libs/Pusher/EventType.ts | 2 +- src/libs/Pusher/library/index.native.ts | 2 +- src/libs/Pusher/library/index.ts | 2 +- src/libs/Pusher/library/types.ts | 4 +++- src/libs/Pusher/pusher.ts | 30 +++++++++++++++---------- 5 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/libs/Pusher/EventType.ts b/src/libs/Pusher/EventType.ts index 85ccc5e17242..89e8a0ca0260 100644 --- a/src/libs/Pusher/EventType.ts +++ b/src/libs/Pusher/EventType.ts @@ -11,4 +11,4 @@ export default { MULTIPLE_EVENT_TYPE: { ONYX_API_UPDATE: 'onyxApiUpdate', }, -}; +} as const; diff --git a/src/libs/Pusher/library/index.native.ts b/src/libs/Pusher/library/index.native.ts index 20f99ba0b0a5..2f82f7980a7f 100644 --- a/src/libs/Pusher/library/index.native.ts +++ b/src/libs/Pusher/library/index.native.ts @@ -5,4 +5,4 @@ import PusherNative from 'pusher-js/react-native'; import Pusher from './types'; -export default PusherNative as unknown as Pusher; +export default PusherNative satisfies Pusher; diff --git a/src/libs/Pusher/library/index.ts b/src/libs/Pusher/library/index.ts index f770acdec568..7e0ff9e36b6d 100644 --- a/src/libs/Pusher/library/index.ts +++ b/src/libs/Pusher/library/index.ts @@ -5,4 +5,4 @@ import PusherWeb from 'pusher-js/with-encryption'; import Pusher from './types'; -export default PusherWeb as unknown as Pusher; +export default PusherWeb satisfies Pusher; diff --git a/src/libs/Pusher/library/types.ts b/src/libs/Pusher/library/types.ts index ae3ed28a9701..bb79339a4aae 100644 --- a/src/libs/Pusher/library/types.ts +++ b/src/libs/Pusher/library/types.ts @@ -1,3 +1,5 @@ -import Pusher from 'pusher-js/with-encryption'; +import PusherClass from 'pusher-js/with-encryption'; + +type Pusher = typeof PusherClass; export default Pusher; diff --git a/src/libs/Pusher/pusher.ts b/src/libs/Pusher/pusher.ts index 7b4448aa14cc..37ac046cdd7f 100644 --- a/src/libs/Pusher/pusher.ts +++ b/src/libs/Pusher/pusher.ts @@ -1,10 +1,12 @@ import Onyx from 'react-native-onyx'; import {Channel, ChannelAuthorizerGenerator, Options} from 'pusher-js/with-encryption'; import isObject from 'lodash/isObject'; +import {LiteralUnion} from 'type-fest'; import ONYXKEYS from '../../ONYXKEYS'; -import Pusher from './library/types'; +import Pusher from './library'; import TYPE from './EventType'; import Log from '../Log'; +import DeepValueOf from '../../types/utils/DeepValueOf'; type States = { previous: string; @@ -21,9 +23,11 @@ type ChunkedDataEvents = {chunks: unknown[]; receivedFinal: boolean}; type EventData = {id?: string; chunk?: unknown; final?: boolean; index: number}; -type SocketEventCallback = (eventName: string, data?: T) => void; +type SocketEventName = LiteralUnion<'error' | 'connected' | 'disconnected' | 'state_change', string>; -type PusherWithAuthParams = Pusher & { +type SocketEventCallback = (eventName: SocketEventName, data?: unknown) => void; + +type PusherWithAuthParams = InstanceType & { config: { auth?: { params?: unknown; @@ -31,6 +35,10 @@ type PusherWithAuthParams = Pusher & { }; }; +type PusherEventName = LiteralUnion, string>; + +type PusherSubscribtionErrorData = {type?: string; error?: string; status?: string}; + let shouldForceOffline = false; Onyx.connect({ key: ONYXKEYS.NETWORK, @@ -50,7 +58,7 @@ let customAuthorizer: ChannelAuthorizerGenerator; /** * Trigger each of the socket event callbacks with the event information */ -function callSocketEventCallbacks(eventName: string, data?: T) { +function callSocketEventCallbacks(eventName: SocketEventName, data?: unknown) { socketEventCallbacks.forEach((cb) => cb(eventName, data)); } @@ -81,7 +89,6 @@ function init(args: Args, params?: unknown): Promise { } socket = new Pusher(args.appKey, options); - // If we want to pass params in our requests to api.php we'll need to add it to socket.config.auth.params // as per the documentation // (https://pusher.com/docs/channels/using_channels/connection#channels-options-parameter). @@ -93,8 +100,7 @@ function init(args: Args, params?: unknown): Promise { } // Listen for connection errors and log them - // TODO: check if true - socket?.connection.bind('error', (error: string) => { + socket?.connection.bind('error', (error: unknown) => { callSocketEventCallbacks('error', error); }); @@ -128,7 +134,7 @@ function getChannel(channelName: string): Channel | undefined { /** * Binds an event callback to a channel + eventName */ -function bindEventToChannel(channel: Channel | undefined, eventName: string, eventCallback: (data: T) => void = () => {}) { +function bindEventToChannel(channel: Channel | undefined, eventName: PusherEventName, eventCallback: (data: unknown) => void = () => {}) { if (!eventName) { return; } @@ -196,7 +202,7 @@ function bindEventToChannel(channel: Channel | undefined, eventName: string, eve * Subscribe to a channel and an event * @param [onResubscribe] Callback to be called when reconnection happen */ -function subscribe(channelName: string, eventName: string, eventCallback = () => {}, onResubscribe = () => {}): Promise { +function subscribe(channelName: string, eventName: PusherEventName, eventCallback = () => {}, onResubscribe = () => {}): Promise { return new Promise((resolve, reject) => { // We cannot call subscribe() before init(). Prevent any attempt to do this on dev. if (!socket) { @@ -226,7 +232,7 @@ function subscribe(channelName: string, eventName: string, eventCallback = () => onResubscribe(); }); - channel.bind('pusher:subscription_error', (data: {type?: string; error?: string; status?: string} = {}) => { + channel.bind('pusher:subscription_error', (data: PusherSubscribtionErrorData = {}) => { const {type, error, status} = data; Log.hmmm('[Pusher] Issue authenticating with Pusher during subscribe attempt.', { channelName, @@ -246,7 +252,7 @@ function subscribe(channelName: string, eventName: string, eventCallback = () => /** * Unsubscribe from a channel and optionally a specific event */ -function unsubscribe(channelName: string, eventName = '') { +function unsubscribe(channelName: string, eventName: PusherEventName = '') { const channel = getChannel(channelName); if (!channel) { @@ -296,7 +302,7 @@ function isSubscribed(channelName: string): boolean { /** * Sends an event over a specific event/channel in pusher. */ -function sendEvent(channelName: string, eventName: string, payload: T) { +function sendEvent(channelName: string, eventName: PusherEventName, payload: T) { // Check to see if we are subscribed to this channel before sending the event. Sending client events over channels // we are not subscribed too will throw errors and cause reconnection attempts. Subscriptions are not instant and // can happen later than we expect. From c8989de00f59bea6e3e541ecdf35ccf69d1c3a83 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Tue, 26 Sep 2023 13:07:13 +0200 Subject: [PATCH 05/10] fix: fixed types issues --- src/libs/Pusher/library/types.ts | 5 +++++ src/libs/Pusher/pusher.ts | 11 +++++------ src/libs/PusherConnectionManager.ts | 11 ++++++----- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/libs/Pusher/library/types.ts b/src/libs/Pusher/library/types.ts index bb79339a4aae..cc8c70fccdbb 100644 --- a/src/libs/Pusher/library/types.ts +++ b/src/libs/Pusher/library/types.ts @@ -1,5 +1,10 @@ import PusherClass from 'pusher-js/with-encryption'; +import {LiteralUnion} from 'type-fest'; type Pusher = typeof PusherClass; +type SocketEventName = LiteralUnion<'error' | 'connected' | 'disconnected' | 'state_change', string>; + export default Pusher; + +export type {SocketEventName}; diff --git a/src/libs/Pusher/pusher.ts b/src/libs/Pusher/pusher.ts index 37ac046cdd7f..87a53014ecc0 100644 --- a/src/libs/Pusher/pusher.ts +++ b/src/libs/Pusher/pusher.ts @@ -7,6 +7,7 @@ import Pusher from './library'; import TYPE from './EventType'; import Log from '../Log'; import DeepValueOf from '../../types/utils/DeepValueOf'; +import {SocketEventName} from './library/types'; type States = { previous: string; @@ -23,9 +24,7 @@ type ChunkedDataEvents = {chunks: unknown[]; receivedFinal: boolean}; type EventData = {id?: string; chunk?: unknown; final?: boolean; index: number}; -type SocketEventName = LiteralUnion<'error' | 'connected' | 'disconnected' | 'state_change', string>; - -type SocketEventCallback = (eventName: SocketEventName, data?: unknown) => void; +type SocketEventCallback = (eventName: SocketEventName, data?: T) => void; type PusherWithAuthParams = InstanceType & { config: { @@ -52,13 +51,13 @@ Onyx.connect({ let socket: PusherWithAuthParams | null; let pusherSocketID = ''; -const socketEventCallbacks: SocketEventCallback[] = []; +const socketEventCallbacks: Array> = []; let customAuthorizer: ChannelAuthorizerGenerator; /** * Trigger each of the socket event callbacks with the event information */ -function callSocketEventCallbacks(eventName: SocketEventName, data?: unknown) { +function callSocketEventCallbacks(eventName: SocketEventName, data?: T) { socketEventCallbacks.forEach((cb) => cb(eventName, data)); } @@ -316,7 +315,7 @@ function sendEvent(channelName: string, eventName: PusherEventName, payload: /** * Register a method that will be triggered when a socket event happens (like disconnecting) */ -function registerSocketEventCallback(cb: SocketEventCallback) { +function registerSocketEventCallback(cb: SocketEventCallback) { socketEventCallbacks.push(cb); } diff --git a/src/libs/PusherConnectionManager.ts b/src/libs/PusherConnectionManager.ts index 4ab08d6dc760..18684157c4b3 100644 --- a/src/libs/PusherConnectionManager.ts +++ b/src/libs/PusherConnectionManager.ts @@ -1,11 +1,12 @@ import {ValueOf} from 'type-fest'; +import {ChannelAuthorizationCallback} from 'pusher-js/with-encryption'; import * as Pusher from './Pusher/pusher'; import * as Session from './actions/Session'; import Log from './Log'; import CONST from '../CONST'; +import {SocketEventName} from './Pusher/library/types'; type EventCallbackError = {type: ValueOf; data: {code: number}}; -type CustomAuthorizerChannel = {name: string}; function init() { /** @@ -14,13 +15,13 @@ function init() { * current valid token to generate the signed auth response * needed to subscribe to Pusher channels. */ - Pusher.registerCustomAuthorizer((channel: CustomAuthorizerChannel) => ({ - authorize: (socketID: string, callback: () => void) => { - Session.authenticatePusher(socketID, channel.name, callback); + Pusher.registerCustomAuthorizer((channel) => ({ + authorize: (socketId: string, callback: ChannelAuthorizationCallback) => { + Session.authenticatePusher(socketId, channel.name, callback); }, })); - Pusher.registerSocketEventCallback((eventName: string, error: EventCallbackError) => { + Pusher.registerSocketEventCallback((eventName: SocketEventName, error?: EventCallbackError) => { switch (eventName) { case 'error': { const errorType = error?.type; From 111859f784793d6429fc867598aac5b031bb1e1a Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Tue, 26 Sep 2023 13:50:20 +0200 Subject: [PATCH 06/10] fix: problem with socketEventCallbacks --- src/libs/Pusher/pusher.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Pusher/pusher.ts b/src/libs/Pusher/pusher.ts index 87a53014ecc0..d0894b2faba7 100644 --- a/src/libs/Pusher/pusher.ts +++ b/src/libs/Pusher/pusher.ts @@ -316,7 +316,7 @@ function sendEvent(channelName: string, eventName: PusherEventName, payload: * Register a method that will be triggered when a socket event happens (like disconnecting) */ function registerSocketEventCallback(cb: SocketEventCallback) { - socketEventCallbacks.push(cb); + socketEventCallbacks.push(cb as SocketEventCallback); } /** From 505e7f23b98da97c6a9295181a898009ca9d4524 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Mon, 9 Oct 2023 11:02:09 +0200 Subject: [PATCH 07/10] fix: resolve comments --- src/libs/Pusher/pusher.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libs/Pusher/pusher.ts b/src/libs/Pusher/pusher.ts index d0894b2faba7..6b5d5d2ffe9e 100644 --- a/src/libs/Pusher/pusher.ts +++ b/src/libs/Pusher/pusher.ts @@ -24,7 +24,7 @@ type ChunkedDataEvents = {chunks: unknown[]; receivedFinal: boolean}; type EventData = {id?: string; chunk?: unknown; final?: boolean; index: number}; -type SocketEventCallback = (eventName: SocketEventName, data?: T) => void; +type SocketEventCallback = (eventName: SocketEventName, data?: unknown) => void; type PusherWithAuthParams = InstanceType & { config: { @@ -51,13 +51,13 @@ Onyx.connect({ let socket: PusherWithAuthParams | null; let pusherSocketID = ''; -const socketEventCallbacks: Array> = []; +const socketEventCallbacks: SocketEventCallback[] = []; let customAuthorizer: ChannelAuthorizerGenerator; /** * Trigger each of the socket event callbacks with the event information */ -function callSocketEventCallbacks(eventName: SocketEventName, data?: T) { +function callSocketEventCallbacks(eventName: SocketEventName, data?: unknown) { socketEventCallbacks.forEach((cb) => cb(eventName, data)); } @@ -301,7 +301,7 @@ function isSubscribed(channelName: string): boolean { /** * Sends an event over a specific event/channel in pusher. */ -function sendEvent(channelName: string, eventName: PusherEventName, payload: T) { +function sendEvent(channelName: string, eventName: PusherEventName, payload: Record) { // Check to see if we are subscribed to this channel before sending the event. Sending client events over channels // we are not subscribed too will throw errors and cause reconnection attempts. Subscriptions are not instant and // can happen later than we expect. @@ -315,8 +315,8 @@ function sendEvent(channelName: string, eventName: PusherEventName, payload: /** * Register a method that will be triggered when a socket event happens (like disconnecting) */ -function registerSocketEventCallback(cb: SocketEventCallback) { - socketEventCallbacks.push(cb as SocketEventCallback); +function registerSocketEventCallback(cb: SocketEventCallback) { + socketEventCallbacks.push(cb); } /** From db0d3e4caed66e2f2ee1f191eaa51503f97a6ecd Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Tue, 10 Oct 2023 14:31:26 +0200 Subject: [PATCH 08/10] fix: resolve comment --- src/libs/Pusher/library/index.native.ts | 6 ++++-- src/libs/Pusher/library/index.ts | 8 +++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/libs/Pusher/library/index.native.ts b/src/libs/Pusher/library/index.native.ts index 2f82f7980a7f..f50834366515 100644 --- a/src/libs/Pusher/library/index.native.ts +++ b/src/libs/Pusher/library/index.native.ts @@ -2,7 +2,9 @@ * We use the pusher-js/react-native module to support pusher on native environments. * @see: https://github.com/pusher/pusher-js */ -import PusherNative from 'pusher-js/react-native'; +import PusherImplementation from 'pusher-js/react-native'; import Pusher from './types'; -export default PusherNative satisfies Pusher; +const PusherNative: Pusher = PusherImplementation; + +export default PusherNative; diff --git a/src/libs/Pusher/library/index.ts b/src/libs/Pusher/library/index.ts index 7e0ff9e36b6d..6a7104a1d2a5 100644 --- a/src/libs/Pusher/library/index.ts +++ b/src/libs/Pusher/library/index.ts @@ -2,7 +2,9 @@ * We use the standard pusher-js module to support pusher on web environments. * @see: https://github.com/pusher/pusher-js */ -import PusherWeb from 'pusher-js/with-encryption'; -import Pusher from './types'; +import PusherImplementation from 'pusher-js/with-encryption'; +import type Pusher from './types'; -export default PusherWeb satisfies Pusher; +const PusherWeb: Pusher = PusherImplementation; + +export default PusherWeb; From b610ca81fcbc281e94b0fd21cf96845615f6f087 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Thu, 12 Oct 2023 13:30:11 +0200 Subject: [PATCH 09/10] fix: fix type errors --- src/libs/Pusher/pusher.ts | 24 ++++++++++++------- src/libs/PusherConnectionManager.ts | 37 +++++++++++++++-------------- src/libs/PusherUtils.ts | 4 +--- 3 files changed, 36 insertions(+), 29 deletions(-) diff --git a/src/libs/Pusher/pusher.ts b/src/libs/Pusher/pusher.ts index 01a02263ab7a..dad963e933fe 100644 --- a/src/libs/Pusher/pusher.ts +++ b/src/libs/Pusher/pusher.ts @@ -1,13 +1,15 @@ import Onyx from 'react-native-onyx'; import {Channel, ChannelAuthorizerGenerator, Options} from 'pusher-js/with-encryption'; import isObject from 'lodash/isObject'; -import {LiteralUnion} from 'type-fest'; +import {LiteralUnion, ValueOf} from 'type-fest'; import ONYXKEYS from '../../ONYXKEYS'; import Pusher from './library'; import TYPE from './EventType'; import Log from '../Log'; import DeepValueOf from '../../types/utils/DeepValueOf'; import {SocketEventName} from './library/types'; +import CONST from '../../CONST'; +import {OnyxUpdateEvent, OnyxUpdatesFromServer} from '../../types/onyx'; type States = { previous: string; @@ -20,11 +22,15 @@ type Args = { authEndpoint: string; }; +type PushJSON = OnyxUpdateEvent[] | OnyxUpdatesFromServer; + +type EventCallbackError = {type: ValueOf; data: {code: number}}; + type ChunkedDataEvents = {chunks: unknown[]; receivedFinal: boolean}; type EventData = {id?: string; chunk?: unknown; final?: boolean; index: number}; -type SocketEventCallback = (eventName: SocketEventName, data?: unknown) => void; +type SocketEventCallback = (eventName: SocketEventName, data?: States | EventCallbackError) => void; type PusherWithAuthParams = InstanceType & { config: { @@ -57,7 +63,7 @@ let customAuthorizer: ChannelAuthorizerGenerator; /** * Trigger each of the socket event callbacks with the event information */ -function callSocketEventCallbacks(eventName: SocketEventName, data?: unknown) { +function callSocketEventCallbacks(eventName: SocketEventName, data?: EventCallbackError | States) { socketEventCallbacks.forEach((cb) => cb(eventName, data)); } @@ -99,7 +105,7 @@ function init(args: Args, params?: unknown): Promise { } // Listen for connection errors and log them - socket?.connection.bind('error', (error: unknown) => { + socket?.connection.bind('error', (error: EventCallbackError) => { callSocketEventCallbacks('error', error); }); @@ -133,19 +139,19 @@ function getChannel(channelName: string): Channel | undefined { /** * Binds an event callback to a channel + eventName */ -function bindEventToChannel(channel: Channel | undefined, eventName: PusherEventName, eventCallback: (data: unknown) => void = () => {}) { +function bindEventToChannel(channel: Channel | undefined, eventName: PusherEventName, eventCallback: (data: PushJSON) => void = () => {}) { if (!eventName) { return; } const chunkedDataEvents: Record = {}; - const callback = (eventData: string | Record) => { + const callback = (eventData: string | Record | EventData) => { if (shouldForceOffline) { Log.info('[Pusher] Ignoring a Push event because shouldForceOffline = true'); return; } - let data: EventData; + let data; try { data = isObject(eventData) ? eventData : JSON.parse(eventData); } catch (err) { @@ -201,7 +207,7 @@ function bindEventToChannel(channel: Channel | undefined, eventName: PusherEvent * Subscribe to a channel and an event * @param [onResubscribe] Callback to be called when reconnection happen */ -function subscribe(channelName: string, eventName: PusherEventName, eventCallback = () => {}, onResubscribe = () => {}): Promise { +function subscribe(channelName: string, eventName: PusherEventName, eventCallback: (data: PushJSON) => void = () => {}, onResubscribe = () => {}): Promise { return new Promise((resolve, reject) => { // We cannot call subscribe() before init(). Prevent any attempt to do this on dev. if (!socket) { @@ -387,3 +393,5 @@ export { TYPE, getPusherSocketID, }; + +export type {EventCallbackError, States, PushJSON}; diff --git a/src/libs/PusherConnectionManager.ts b/src/libs/PusherConnectionManager.ts index 18684157c4b3..df8bc58f6a38 100644 --- a/src/libs/PusherConnectionManager.ts +++ b/src/libs/PusherConnectionManager.ts @@ -5,8 +5,7 @@ import * as Session from './actions/Session'; import Log from './Log'; import CONST from '../CONST'; import {SocketEventName} from './Pusher/library/types'; - -type EventCallbackError = {type: ValueOf; data: {code: number}}; +import {EventCallbackError, States} from './Pusher/pusher'; function init() { /** @@ -21,24 +20,26 @@ function init() { }, })); - Pusher.registerSocketEventCallback((eventName: SocketEventName, error?: EventCallbackError) => { + Pusher.registerSocketEventCallback((eventName: SocketEventName, error?: EventCallbackError | States) => { switch (eventName) { case 'error': { - const errorType = error?.type; - const code = error?.data?.code; - if (errorType === CONST.ERROR.PUSHER_ERROR && code === 1006) { - // 1006 code happens when a websocket connection is closed. There may or may not be a reason attached indicating why the connection was closed. - // https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.5 - Log.hmmm('[PusherConnectionManager] Channels Error 1006', {error}); - } else if (errorType === CONST.ERROR.PUSHER_ERROR && code === 4201) { - // This means the connection was closed because Pusher did not receive a reply from the client when it pinged them for a response - // https://pusher.com/docs/channels/library_auth_reference/pusher-websockets-protocol/#4200-4299 - Log.hmmm('[PusherConnectionManager] Pong reply not received', {error}); - } else if (errorType === CONST.ERROR.WEB_SOCKET_ERROR) { - // It's not clear why some errors are wrapped in a WebSocketError type - this error could mean different things depending on the contents. - Log.hmmm('[PusherConnectionManager] WebSocketError', {error}); - } else { - Log.alert(`${CONST.ERROR.ENSURE_BUGBOT} [PusherConnectionManager] Unknown error event`, {error}); + if (error && 'type' in error) { + const errorType = error?.type; + const code = error?.data?.code; + if (errorType === CONST.ERROR.PUSHER_ERROR && code === 1006) { + // 1006 code happens when a websocket connection is closed. There may or may not be a reason attached indicating why the connection was closed. + // https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.5 + Log.hmmm('[PusherConnectionManager] Channels Error 1006', {error}); + } else if (errorType === CONST.ERROR.PUSHER_ERROR && code === 4201) { + // This means the connection was closed because Pusher did not receive a reply from the client when it pinged them for a response + // https://pusher.com/docs/channels/library_auth_reference/pusher-websockets-protocol/#4200-4299 + Log.hmmm('[PusherConnectionManager] Pong reply not received', {error}); + } else if (errorType === CONST.ERROR.WEB_SOCKET_ERROR) { + // It's not clear why some errors are wrapped in a WebSocketError type - this error could mean different things depending on the contents. + Log.hmmm('[PusherConnectionManager] WebSocketError', {error}); + } else { + Log.alert(`${CONST.ERROR.ENSURE_BUGBOT} [PusherConnectionManager] Unknown error event`, {error}); + } } break; } diff --git a/src/libs/PusherUtils.ts b/src/libs/PusherUtils.ts index 5baa4b68d5f8..d47283f21bbf 100644 --- a/src/libs/PusherUtils.ts +++ b/src/libs/PusherUtils.ts @@ -4,9 +4,7 @@ import Log from './Log'; import NetworkConnection from './NetworkConnection'; import * as Pusher from './Pusher/pusher'; import CONST from '../CONST'; -import {OnyxUpdateEvent, OnyxUpdatesFromServer} from '../types/onyx'; - -type PushJSON = OnyxUpdateEvent[] | OnyxUpdatesFromServer; +import {PushJSON} from './Pusher/pusher'; type Callback = (data: OnyxUpdate[]) => Promise; From 9a003d9eab707492977daccea5c3b7c95ad6820a Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Thu, 12 Oct 2023 13:32:29 +0200 Subject: [PATCH 10/10] fix: lint error --- src/libs/PusherConnectionManager.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/PusherConnectionManager.ts b/src/libs/PusherConnectionManager.ts index df8bc58f6a38..9b1f6ebe1b2a 100644 --- a/src/libs/PusherConnectionManager.ts +++ b/src/libs/PusherConnectionManager.ts @@ -1,4 +1,3 @@ -import {ValueOf} from 'type-fest'; import {ChannelAuthorizationCallback} from 'pusher-js/with-encryption'; import * as Pusher from './Pusher/pusher'; import * as Session from './actions/Session';