-
Notifications
You must be signed in to change notification settings - Fork 14
chore(PLA-2224): re-fetch guides when the existing socket is disconnected and reconnected #869
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
base: main
Are you sure you want to change the base?
Changes from all commits
0b667b1
327226c
a2000c7
ad56c8c
13e8c3b
58d3def
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@knocklabs/client": patch | ||
| --- | ||
|
|
||
| Proactively re-fetches guides when the socket is reconnected |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| import { GenericData } from "@knocklabs/types"; | ||
| import { Store } from "@tanstack/store"; | ||
| import { Channel, Socket } from "phoenix"; | ||
| import { Channel, type MessageRef, Socket } from "phoenix"; | ||
| import { URLPattern } from "urlpattern-polyfill"; | ||
|
|
||
| import Knock from "../../knock"; | ||
|
|
@@ -257,6 +257,7 @@ export class KnockGuideClient { | |
| "guide.live_preview_updated", | ||
| ]; | ||
| private subscribeRetryCount = 0; | ||
| private socketOpenListenerId: MessageRef | undefined; | ||
|
|
||
| // Original history methods to monkey patch, or restore in cleanups. | ||
| private pushStateFn: History["pushState"] | undefined; | ||
|
|
@@ -345,16 +346,16 @@ export class KnockGuideClient { | |
| this.clearCounterInterval(); | ||
| } | ||
|
|
||
| async fetch(opts?: { filters?: QueryFilterParams }) { | ||
| async fetch(opts?: { filters?: QueryFilterParams; force?: boolean }) { | ||
| this.knock.log("[Guide] .fetch"); | ||
| this.knock.failIfNotAuthenticated(); | ||
|
|
||
| const queryParams = this.buildQueryParams(opts?.filters); | ||
| const queryKey = this.formatQueryKey(queryParams); | ||
|
|
||
| // If already fetched before, then noop. | ||
| // If already fetched before, then noop (unless force refetch). | ||
| const maybeQueryStatus = this.store.state.queries[queryKey]; | ||
| if (maybeQueryStatus) { | ||
| if (maybeQueryStatus && !opts?.force) { | ||
| return maybeQueryStatus; | ||
| } | ||
|
|
||
|
|
@@ -416,6 +417,12 @@ export class KnockGuideClient { | |
| this.unsubscribe(); | ||
| } | ||
|
|
||
| // If there's an existing open listener, then remove it. | ||
| if (this.socketOpenListenerId) { | ||
| this.socket.off([this.socketOpenListenerId]); | ||
| this.socketOpenListenerId = undefined; | ||
| } | ||
|
|
||
| // Join the channel topic and subscribe to supported events. | ||
| const debugState = this.store.state.debug; | ||
| const params = { | ||
|
|
@@ -455,6 +462,21 @@ export class KnockGuideClient { | |
|
|
||
| // Track the joined channel. | ||
| this.socketChannel = newChannel; | ||
|
|
||
| // Refetch guides on socket reconnect to pick up any changes that | ||
| // occurred while disconnected (e.g. tab was hidden for a long time). | ||
| // If the socket is already connected, there's no initial open event coming, | ||
| // so we don't need to skip anything. If it's not yet connected, skip the | ||
| // first onOpen since that's the initial connection, not a reconnect. | ||
| let isFirstOpen = !this.socket.isConnected(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given we call this.socket.connect() if this.socket.isConnected() is not true above, I would think this is always true by the time we get here, but maybe this is just for out of caution? |
||
| this.socketOpenListenerId = this.socket.onOpen(() => { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it fair to assume this will fire when we deploy switchboard? I guess switchboard-sockets specifically? If so, I kinda want to avoid triggering ALL connected clients to re-fetch at the same time from a deploy. Curious if you have any thoughts on targeting re-connect events from the tab visibility state change more granularly? Those will happen more organically and spread out across connected clients, which I think is would be the right thing to do. But i'm a bit hesitant to just blanket re-fetch based on every socket onOpen events. |
||
| if (isFirstOpen) { | ||
| isFirstOpen = false; | ||
| return; | ||
| } | ||
| this.knock.log("[Guide] Socket reconnected, refetching guides"); | ||
| this.fetch({ force: true }); | ||
| }); | ||
cursor[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| private handleChannelJoinError() { | ||
|
|
@@ -481,6 +503,11 @@ export class KnockGuideClient { | |
| } | ||
| this.socketChannel.leave(); | ||
|
|
||
| if (this.socketOpenListenerId && this.socket) { | ||
| this.socket.off([this.socketOpenListenerId]); | ||
| this.socketOpenListenerId = undefined; | ||
| } | ||
|
|
||
| // Unset the channel. | ||
| this.socketChannel = undefined; | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we move this to a private method and re-use here and in unsubscribe?