diff --git a/src/hooks/useLatestWithSubscription.js b/src/hooks/useLatestWithSubscription.js
index 42c3fe0..c3c5b15 100644
--- a/src/hooks/useLatestWithSubscription.js
+++ b/src/hooks/useLatestWithSubscription.js
@@ -128,7 +128,7 @@ export default function useLatestWithSubscription(
// Some devices keep the WS transport "connected" after a lock/unlock, but the
// per-operation subscription stops delivering. Trigger a controlled resubscribe.
- const FOREGROUND_KICK_MIN_INACTIVE_MS = 3_000;
+ const FOREGROUND_KICK_MIN_INACTIVE_MS = 30_000;
const FOREGROUND_KICK_MIN_INTERVAL_MS = 15_000;
if (
@@ -239,6 +239,15 @@ export default function useLatestWithSubscription(
if (age < livenessStaleMs) return;
const now = Date.now();
+ const becameInactiveAt = lastBecameInactiveAtRef.current;
+ const inactiveWindowMs = becameInactiveAt ? now - becameInactiveAt : null;
+ if (
+ typeof inactiveWindowMs === "number" &&
+ inactiveWindowMs < livenessStaleMs + 15_000
+ ) {
+ return;
+ }
+
if (now - lastLivenessKickAtRef.current < livenessStaleMs) return;
lastLivenessKickAtRef.current = now;
@@ -276,7 +285,7 @@ export default function useLatestWithSubscription(
// Escalation policy for repeated consecutive stale kicks.
if (
- consecutiveStaleKicksRef.current >= STALE_KICKS_BEFORE_RELOAD &&
+ consecutiveStaleKicksRef.current >= STALE_KICKS_BEFORE_RELOAD + 2 &&
now - lastReloadAtRef.current >= MIN_ESCALATION_INTERVAL_MS
) {
const lastRecovery = wsLastRecoveryDateRef.current
@@ -310,7 +319,7 @@ export default function useLatestWithSubscription(
// ignore
}
- networkActions.triggerReload();
+ networkActions.triggerReload("transport");
} else if (
consecutiveStaleKicksRef.current >= STALE_KICKS_BEFORE_WS_RESTART &&
now - lastWsRestartAtRef.current >= MIN_ESCALATION_INTERVAL_MS
diff --git a/src/hooks/useStreamQueryWithSubscription.js b/src/hooks/useStreamQueryWithSubscription.js
index 48e10ba..4b123d4 100644
--- a/src/hooks/useStreamQueryWithSubscription.js
+++ b/src/hooks/useStreamQueryWithSubscription.js
@@ -131,7 +131,7 @@ export default function useStreamQueryWithSubscription(
// Some devices keep the WS transport "connected" after a lock/unlock, but the
// per-operation subscription stops delivering. Trigger a controlled resubscribe.
- const FOREGROUND_KICK_MIN_INACTIVE_MS = 3_000;
+ const FOREGROUND_KICK_MIN_INACTIVE_MS = 30_000;
const FOREGROUND_KICK_MIN_INTERVAL_MS = 15_000;
if (
@@ -281,6 +281,15 @@ export default function useStreamQueryWithSubscription(
});
}
// Avoid spamming resubscribe triggers.
+ const becameInactiveAt = lastBecameInactiveAtRef.current;
+ const inactiveWindowMs = becameInactiveAt ? now - becameInactiveAt : null;
+ if (
+ typeof inactiveWindowMs === "number" &&
+ inactiveWindowMs < livenessStaleMs + 15_000
+ ) {
+ return;
+ }
+
if (now - lastLivenessKickAtRef.current < livenessStaleMs) return;
lastLivenessKickAtRef.current = now;
@@ -318,7 +327,7 @@ export default function useStreamQueryWithSubscription(
// Escalation policy for repeated consecutive stale kicks.
if (
- consecutiveStaleKicksRef.current >= STALE_KICKS_BEFORE_RELOAD &&
+ consecutiveStaleKicksRef.current >= STALE_KICKS_BEFORE_RELOAD + 2 &&
now - lastReloadAtRef.current >= MIN_ESCALATION_INTERVAL_MS
) {
const lastRecovery = wsLastRecoveryDateRef.current
@@ -352,7 +361,7 @@ export default function useStreamQueryWithSubscription(
// ignore
}
- networkActions.triggerReload();
+ networkActions.triggerReload("transport");
} else if (
consecutiveStaleKicksRef.current >= STALE_KICKS_BEFORE_WS_RESTART &&
now - lastWsRestartAtRef.current >= MIN_ESCALATION_INTERVAL_MS
diff --git a/src/network/NetworkProviders.js b/src/network/NetworkProviders.js
index 9bd701e..77f8b27 100644
--- a/src/network/NetworkProviders.js
+++ b/src/network/NetworkProviders.js
@@ -20,6 +20,7 @@ import getRetryMaxAttempts from "./getRetryMaxAttemps";
import { createLogger } from "~/lib/logger";
import { NETWORK_SCOPES } from "~/lib/logger/scopes";
+import createCache from "./cache";
const { useNetworkState, networkActions } = store;
@@ -28,11 +29,15 @@ const networkProvidersLogger = createLogger({
feature: "NetworkProviders",
});
+const sharedApolloCache = createCache();
+
const initializeNewApolloClient = (reload) => {
if (reload) {
const { apolloClient } = network;
apolloClient.stop();
- apolloClient.clearStore();
+ if (apolloClient.cache !== sharedApolloCache) {
+ apolloClient.clearStore();
+ }
}
network.apolloClient = createApolloClient({
@@ -40,6 +45,7 @@ const initializeNewApolloClient = (reload) => {
GRAPHQL_URL: env.GRAPHQL_URL,
GRAPHQL_WS_URL: env.GRAPHQL_WS_URL,
getRetryMaxAttempts,
+ cache: sharedApolloCache,
});
};
initializeNewApolloClient();
@@ -51,34 +57,60 @@ network.oaFilesKy = oaFilesKy;
export default function NetworkProviders({ children }) {
const [key, setKey] = useState(0);
+ const [transportClient, setTransportClient] = useState(() => network.apolloClient);
- const networkState = useNetworkState(["initialized", "triggerReload"]);
+ const networkState = useNetworkState([
+ "initialized",
+ "triggerReload",
+ "reloadKind",
+ "transportGeneration",
+ ]);
useEffect(() => {
if (networkState.triggerReload) {
networkProvidersLogger.debug("Network triggerReload received", {
+ reloadKind: networkState.reloadKind,
reloadId: store.getAuthState()?.reloadId,
hasUserToken: !!store.getAuthState()?.userToken,
});
+
+ const isFullReload = networkState.reloadKind !== "transport";
initializeNewApolloClient(true);
- setKey((prevKey) => prevKey + 1);
+
+ if (isFullReload) {
+ setTransportClient(network.apolloClient);
+ setKey((prevKey) => prevKey + 1);
+ } else {
+ setTransportClient(network.apolloClient);
+ networkProvidersLogger.debug("Network transport recovered in place", {
+ reloadId: store.getAuthState()?.reloadId,
+ hasUserToken: !!store.getAuthState()?.userToken,
+ transportGeneration: networkState.transportGeneration,
+ });
+ networkActions.onReload();
+ }
}
- }, [networkState.triggerReload]);
+ }, [
+ networkState.triggerReload,
+ networkState.reloadKind,
+ networkState.transportGeneration,
+ ]);
useEffect(() => {
if (key > 0) {
networkProvidersLogger.debug("Network reloaded", {
+ reloadKind: networkState.reloadKind,
reloadId: store.getAuthState()?.reloadId,
hasUserToken: !!store.getAuthState()?.userToken,
});
networkActions.onReload();
}
- }, [key]);
+ }, [key, networkState.reloadKind]);
if (!networkState.initialized) {
return ;
}
- const providers = [[ApolloProvider, { client: network.apolloClient }]];
+ const providers = [[ApolloProvider, { client: transportClient }]];
return (
diff --git a/src/network/apollo.js b/src/network/apollo.js
index d9df11b..cf9ca45 100644
--- a/src/network/apollo.js
+++ b/src/network/apollo.js
@@ -19,6 +19,7 @@ if (__DEV__ || process.env.NODE_ENV !== "production") {
}
export default function createApolloClient(options) {
+ const cache = options.cache || createCache();
const errorLink = createErrorLink(options);
const authLink = createAuthLink(options);
const cancelLink = createCancelLink();
@@ -50,8 +51,6 @@ export default function createApolloClient(options) {
httpLink: httpChain,
});
- const cache = createCache();
-
const apolloClient = new ApolloClient({
cache,
// connectToDevTools: true, // Enable dev tools for better debugging
diff --git a/src/stores/network.js b/src/stores/network.js
index 2113f08..0835c2d 100644
--- a/src/stores/network.js
+++ b/src/stores/network.js
@@ -9,20 +9,28 @@ export default createAtom(({ merge, get }) => {
wsLastHeartbeatDate: null,
wsLastRecoveryDate: null,
triggerReload: false,
+ reloadKind: null,
initialized: true,
hasInternetConnection: true,
+ transportGeneration: 0,
},
actions: {
- triggerReload: () => {
+ triggerReload: (reloadKind = "full") => {
merge({
- initialized: false,
triggerReload: true,
+ reloadKind,
+ initialized: reloadKind === "transport" ? true : false,
+ transportGeneration:
+ reloadKind === "transport"
+ ? get("transportGeneration") + 1
+ : get("transportGeneration"),
});
},
onReload: () => {
merge({
initialized: true,
triggerReload: false,
+ reloadKind: null,
});
},
WSConnected: () => {