Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Subscribe to Pusher only when it's initialized #53751

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions src/libs/Navigation/AppNavigator/AuthScreens.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,6 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie
const {toggleSearch} = useSearchRouterContext();

const modal = useRef<OnyxTypes.Modal>({});
const [didPusherInit, setDidPusherInit] = useState(false);
const {isOnboardingCompleted} = useOnboardingFlowRouter();
const [initialReportID] = useState(() => {
const currentURL = getCurrentUrl();
Expand Down Expand Up @@ -280,9 +279,7 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie
NetworkConnection.listenForReconnect();
NetworkConnection.onReconnect(handleNetworkReconnect);
PusherConnectionManager.init();
initializePusher().then(() => {
setDidPusherInit(true);
});
initializePusher();

// In Hybrid App we decide to call one of those method when booting ND and we don't want to duplicate calls
if (!NativeModules.HybridAppModule) {
Expand Down Expand Up @@ -591,7 +588,7 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie
<TestToolsModal />
<SearchRouterModal />
</View>
{didPusherInit && <ActiveGuidesEventListener />}
<ActiveGuidesEventListener />
</ComposeProviders>
);
}
Expand Down
89 changes: 48 additions & 41 deletions src/libs/Pusher/pusher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ let pusherSocketID = '';
const socketEventCallbacks: SocketEventCallback[] = [];
let customAuthorizer: ChannelAuthorizerGenerator;

let initPromise: Promise<void>;

const eventsBoundToChannels = new Map<Channel, Set<PusherEventName>>();

/**
Expand All @@ -88,7 +90,7 @@ function callSocketEventCallbacks(eventName: SocketEventName, data?: EventCallba
* @returns resolves when Pusher has connected
*/
function init(args: Args, params?: unknown): Promise<void> {
return new Promise((resolve) => {
initPromise = new Promise((resolve) => {
if (socket) {
resolve();
return;
Expand Down Expand Up @@ -140,6 +142,8 @@ function init(args: Args, params?: unknown): Promise<void> {
callSocketEventCallbacks('state_change', states);
});
});

return initPromise;
}

/**
Expand Down Expand Up @@ -236,52 +240,55 @@ function subscribe<EventName extends PusherEventName>(
eventCallback: (data: EventData<EventName>) => void = () => {},
onResubscribe = () => {},
): Promise<void> {
return new Promise((resolve, reject) => {
InteractionManager.runAfterInteractions(() => {
// We cannot call subscribe() before init(). Prevent any attempt to do this on dev.
if (!socket) {
throw new Error(`[Pusher] instance not found. Pusher.subscribe()
return initPromise.then(
() =>
new Promise((resolve, reject) => {
InteractionManager.runAfterInteractions(() => {
// We cannot call subscribe() before init(). Prevent any attempt to do this on dev.
if (!socket) {
throw new Error(`[Pusher] instance not found. Pusher.subscribe()
most likely has been called before Pusher.init()`);
}

Log.info('[Pusher] Attempting to subscribe to channel', false, {channelName, eventName});
let channel = getChannel(channelName);
}

if (!channel?.subscribed) {
channel = socket.subscribe(channelName);
let isBound = false;
channel.bind('pusher:subscription_succeeded', () => {
// Check so that we do not bind another event with each reconnect attempt
if (!isBound) {
Log.info('[Pusher] Attempting to subscribe to channel', false, {channelName, eventName});
let channel = getChannel(channelName);

if (!channel?.subscribed) {
channel = socket.subscribe(channelName);
let isBound = false;
channel.bind('pusher:subscription_succeeded', () => {
// Check so that we do not bind another event with each reconnect attempt
if (!isBound) {
bindEventToChannel(channel, eventName, eventCallback);
resolve();
isBound = true;
return;
}

// When subscribing for the first time we register a success callback that can be
// called multiple times when the subscription succeeds again in the future
// e.g. as a result of Pusher disconnecting and reconnecting. This callback does
// not fire on the first subscription_succeeded event.
onResubscribe();
});

channel.bind('pusher:subscription_error', (data: PusherSubscribtionErrorData = {}) => {
const {type, error, status} = data;
Log.hmmm('[Pusher] Issue authenticating with Pusher during subscribe attempt.', {
channelName,
status,
type,
error,
});
reject(error);
});
} else {
bindEventToChannel(channel, eventName, eventCallback);
resolve();
isBound = true;
return;
}

// When subscribing for the first time we register a success callback that can be
// called multiple times when the subscription succeeds again in the future
// e.g. as a result of Pusher disconnecting and reconnecting. This callback does
// not fire on the first subscription_succeeded event.
onResubscribe();
});

channel.bind('pusher:subscription_error', (data: PusherSubscribtionErrorData = {}) => {
const {type, error, status} = data;
Log.hmmm('[Pusher] Issue authenticating with Pusher during subscribe attempt.', {
channelName,
status,
type,
error,
});
reject(error);
});
} else {
bindEventToChannel(channel, eventName, eventCallback);
resolve();
}
});
});
}),
);
}

/**
Expand Down
6 changes: 5 additions & 1 deletion tests/ui/UnreadIndicatorsTest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,11 @@ function signInAndGetAppWithUnreadChat(): Promise<void> {
}

describe('Unread Indicators', () => {
afterEach(() => {
beforeAll(() => {
PusherHelper.setup();
});

beforeEach(() => {
jest.clearAllMocks();
Onyx.clear();

Expand Down
2 changes: 2 additions & 0 deletions tests/utils/PusherHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ function setup() {
cluster: CONFIG.PUSHER.CLUSTER,
authEndpoint: `${CONFIG.EXPENSIFY.DEFAULT_API_ROOT}api/AuthenticatePusher?`,
});

window.getPusherInstance()?.connection.emit('connected');
}

function emitOnyxUpdate(args: OnyxServerUpdate[]) {
Expand Down
Loading