Skip to content

Commit 8973fd4

Browse files
authored
Fix bounce rate from redirect and callback flows (#3619)
Various flows, like the legacy domain redirect or the OpenID callback flows, are only shown temporarily without any user interaction, causing false bounce rates to be registered. To resolve this, we explicitly track the page views only on pages we know are actually user interactive: landing, authorization and dashboard. # Changes - Move `analytics.pageView()` call from root layout to specific pages known to be user interactive. - Remove `building` condition in `manuallyReroute` since it's called in a lifecycle method for a while now (used to be within the render itself).
1 parent 48ab4b1 commit 8973fd4

File tree

5 files changed

+42
-24
lines changed

5 files changed

+42
-24
lines changed

src/frontend/src/lib/utils/reroute.ts

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -31,23 +31,21 @@ export const reroute: Reroute = ({ url }) => {
3131

3232
// SSG routes don't reroute by default anymore, so this method is used
3333
// there to manually add the rerouting back after the page has loaded.
34-
export const manuallyReroute = async () => {
35-
if (!building) {
36-
const next = await reroute({
37-
url: new URL(window.location.href),
38-
fetch: window.fetch,
39-
});
40-
if (nonNullish(next)) {
41-
// Capture current URL
42-
const currentURL = new URL(window.location.href);
43-
// Cast to string since `nonNullish` doesn't exclude `void` type
44-
const nextURL = new URL(next as string, window.location.origin);
45-
// Copy over the current query params
46-
nextURL.search = currentURL.search;
47-
// Reroute to destination
48-
await goto(nextURL, { replaceState: true });
49-
// After rerouting, change the URL back to what the user expects to see
50-
replaceState(currentURL, {});
51-
}
34+
export const manuallyReroute = async (): Promise<boolean> => {
35+
const next = await reroute({
36+
url: new URL(window.location.href),
37+
fetch: window.fetch,
38+
});
39+
if (next !== undefined) {
40+
// Capture current URL
41+
const currentURL = new URL(window.location.href);
42+
const nextURL = new URL(next, window.location.origin);
43+
// Copy over the current query params
44+
nextURL.search = currentURL.search;
45+
// Reroute to destination
46+
await goto(nextURL, { replaceState: true });
47+
// After rerouting, change the URL back to what the user expects to see
48+
replaceState(currentURL, {});
5249
}
50+
return next !== undefined;
5351
};

src/frontend/src/routes/(new-styling)/(channel)/authorize/+layout.svelte

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
import { waitFor } from "$lib/utils/utils";
2323
import FeaturedIcon from "$lib/components/ui/FeaturedIcon.svelte";
2424
import { CircleAlertIcon, RotateCcwIcon } from "@lucide/svelte";
25+
import { onMount } from "svelte";
26+
import { analytics } from "$lib/utils/analytics/analytics";
2527
2628
const { children, data }: LayoutProps = $props();
2729
@@ -71,6 +73,11 @@
7173
lastUsedIdentities.map(({ identityNumber }) => identityNumber),
7274
),
7375
);
76+
77+
// Track page view for authorization flow
78+
onMount(() => {
79+
analytics.pageView();
80+
});
7481
</script>
7582

7683
<div class="flex min-h-[100dvh] flex-col" data-page="new-authorize-view">

src/frontend/src/routes/(new-styling)/+page.svelte

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
import Popover from "$lib/components/ui/Popover.svelte";
4545
import IdentitySwitcher from "$lib/components/ui/IdentitySwitcher.svelte";
4646
import { AuthLastUsedFlow } from "$lib/flows/authLastUsedFlow.svelte";
47+
import { onMount } from "svelte";
48+
import { analytics } from "$lib/utils/analytics/analytics";
4749
4850
const authLastUsedFlow = new AuthLastUsedFlow();
4951
@@ -91,11 +93,6 @@
9193
});
9294
};
9395
94-
// Add rerouting back on this SSG route
95-
$effect(() => {
96-
manuallyReroute();
97-
});
98-
9996
let triggerAnimation =
10097
$state<(opts: FlairAnimationOptions) => Promise<void>>();
10198
let clearAnimation = $state<() => void>();
@@ -141,6 +138,16 @@
141138
authenticationV2Funnel.trigger(AuthenticationV2Events.GoToDashboard);
142139
authenticationV2Funnel.close();
143140
});
141+
142+
onMount(async () => {
143+
// Add rerouting back on this SSG route
144+
const rerouted = await manuallyReroute();
145+
// Track page view for landing page unless we have rerouted,
146+
// in which case it will be tracked on the destination page.
147+
if (!rerouted) {
148+
analytics.pageView();
149+
}
150+
});
144151
</script>
145152

146153
<div class="flex min-h-[100dvh] flex-col">

src/frontend/src/routes/(new-styling)/manage/(authenticated)/+layout.svelte

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
import { Trans } from "$lib/components/locale";
4545
import { getMetadataString } from "$lib/utils/openID";
4646
import ProgressRing from "$lib/components/ui/ProgressRing.svelte";
47+
import { analytics } from "$lib/utils/analytics/analytics";
48+
import { onMount } from "svelte";
4749
4850
const { children, data }: LayoutProps = $props();
4951
@@ -121,6 +123,11 @@
121123
afterNavigate(() => {
122124
isMobileSidebarOpen = false;
123125
});
126+
127+
// Track page view for dashboard
128+
onMount(() => {
129+
analytics.pageView();
130+
});
124131
</script>
125132

126133
{#snippet recoveryPhraseSetUp()}

src/frontend/src/routes/+layout.svelte

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@
5858
5959
// Initialize analytics
6060
initAnalytics(canisterConfig.analytics_config[0]?.[0]);
61-
analytics.pageView();
6261
});
6362
</script>
6463

0 commit comments

Comments
 (0)