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

fix: analytics at boot time #3759

Merged
merged 21 commits into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
9c03a97
refactor: BootProvider and log events
ilasw Oct 28, 2024
d91853f
Merge branch 'main' into fix-analytics-boot
ilasw Oct 29, 2024
1c3f281
refactor: revert getCachedBootOrNull
ilasw Oct 29, 2024
3feb747
Merge remote-tracking branch 'origin/fix-analytics-boot' into fix-ana…
ilasw Oct 29, 2024
e88029a
Merge branch 'main' of https://github.com/dailydotdev/apps into fix-a…
ilasw Nov 4, 2024
c52a354
Merge branch 'main' into fix-analytics-boot
ilasw Nov 4, 2024
5b6dc71
Merge branch 'main' of https://github.com/dailydotdev/apps into fix-a…
ilasw Nov 4, 2024
540d67b
fix: ssr shift due to firstLoad
ilasw Nov 4, 2024
10c6323
Merge branch 'main' of https://github.com/dailydotdev/apps into fix-a…
ilasw Nov 5, 2024
d5a54d5
fix: test with wrong context set
ilasw Nov 5, 2024
f8d709d
Merge branch 'main' into fix-analytics-boot
ilasw Nov 5, 2024
681f647
Merge branch 'main' into fix-analytics-boot
ilasw Nov 11, 2024
b775f9d
Merge branch 'main' into fix-analytics-boot
ilasw Nov 13, 2024
bda2bfe
Merge branch 'main' into fix-analytics-boot
ilasw Nov 14, 2024
e71bb25
Merge branch 'main' of https://github.com/dailydotdev/apps into fix-a…
ilasw Nov 26, 2024
15a1b78
Merge branch 'main' into fix-analytics-boot
ilasw Nov 26, 2024
c4547dd
Merge branch 'main' of https://github.com/dailydotdev/apps into fix-a…
ilasw Dec 13, 2024
91f4875
Merge branch 'main' of https://github.com/dailydotdev/apps into fix-a…
ilasw Dec 19, 2024
75ca26b
Merge branch 'main' of https://github.com/dailydotdev/apps into fix-a…
ilasw Dec 20, 2024
712a9f0
Merge branch 'main' of https://github.com/dailydotdev/apps into fix-a…
ilasw Dec 23, 2024
efcccf3
Merge branch 'main' into fix-analytics-boot
ilasw Dec 23, 2024
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
10 changes: 4 additions & 6 deletions packages/extension/src/companion/Companion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,11 @@ export default function Companion({
}),
});
const [assetsLoadedDebounce] = useDebounceFn(() => setAssetsLoaded(true), 10);
const routeChangedCallbackRef = useLogPageView();
const routeChangedCallback = useLogPageView();

useEffect(() => {
if (routeChangedCallbackRef.current) {
routeChangedCallbackRef.current();
}
}, [routeChangedCallbackRef]);
routeChangedCallback?.();
}, [routeChangedCallback]);

const [checkAssets, clearCheckAssets] = useDebounceFn(() => {
if (containerRef?.current?.offsetLeft === 0) {
Expand All @@ -133,7 +131,7 @@ export default function Companion({
}

checkAssets();
routeChangedCallbackRef.current();
routeChangedCallback?.();
// @NOTE see https://dailydotdev.atlassian.net/l/cp/dK9h1zoM
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [containerRef]);
Expand Down
8 changes: 4 additions & 4 deletions packages/extension/src/newtab/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ function InternalApp(): ReactElement {
const { contentScriptGranted } = useContentScriptStatus();
const { hostGranted, isFetching: isCheckingHostPermissions } =
useHostStatus();
const routeChangedCallbackRef = useLogPageView();
const routeChangedCallback = useLogPageView();
useConsoleLogo();
const { user, isAuthReady } = useAuthContext();
const { growthbook } = useGrowthBookContext();
Expand All @@ -92,10 +92,10 @@ function InternalApp(): ReactElement {
const isFirefoxExtension = process.env.TARGET_BROWSER === 'firefox';

useEffect(() => {
if (routeChangedCallbackRef.current && isPageReady) {
routeChangedCallbackRef.current();
if (isPageReady && currentPage) {
routeChangedCallback?.();
}
}, [isPageReady, routeChangedCallbackRef, currentPage]);
}, [isPageReady, routeChangedCallback, currentPage]);

const { dismissToast } = useToastNotification();

Expand Down
11 changes: 6 additions & 5 deletions packages/shared/src/contexts/AuthContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { AccessToken, Boot, Visit } from '../lib/boot';
import { isCompanionActivated } from '../lib/element';
import { AuthTriggers, AuthTriggersType } from '../lib/auth';
import { Squad } from '../graphql/sources';
import { checkIsExtension, isNullOrUndefined } from '../lib/func';
import { checkIsExtension } from '../lib/func';

export interface LoginState {
trigger: AuthTriggersType;
Expand Down Expand Up @@ -65,6 +65,7 @@ export interface AuthContextData {
isAuthReady?: boolean;
geo?: Boot['geo'];
}

const isExtension = checkIsExtension();
const AuthContext = React.createContext<AuthContextData>(null);
export const useAuthContext = (): AuthContextData => useContext(AuthContext);
Expand Down Expand Up @@ -104,7 +105,7 @@ export type AuthContextProviderProps = {
isFetched?: boolean;
isLegacyLogout?: boolean;
children?: ReactNode;
firstLoad?: boolean;
isAuthReady?: boolean;
} & Pick<
AuthContextData,
| 'getRedirectUri'
Expand Down Expand Up @@ -133,15 +134,15 @@ export const AuthContextProvider = ({
isLegacyLogout,
accessToken,
squads,
firstLoad,
isAuthReady,
geo,
}: AuthContextProviderProps): ReactElement => {
const [loginState, setLoginState] = useState<LoginState | null>(null);
const endUser = user && 'providers' in user ? user : null;
const referral = user?.referralId || user?.referrer;
const referralOrigin = user?.referralOrigin;

if (firstLoad === true && endUser && !endUser?.infoConfirmed) {
if (!!isAuthReady && endUser && !endUser?.infoConfirmed) {
logout(LogoutReason.IncomleteOnboarding);
}

Expand All @@ -152,7 +153,7 @@ export const AuthContextProvider = ({
return (
<AuthContext.Provider
value={{
isAuthReady: !isNullOrUndefined(firstLoad),
isAuthReady,
user: endUser,
isLoggedIn: !!endUser?.id,
referral: loginState?.referral ?? referral,
Expand Down
90 changes: 42 additions & 48 deletions packages/shared/src/contexts/BootProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ import React, {
ReactElement,
ReactNode,
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import dynamic from 'next/dynamic';
Expand Down Expand Up @@ -87,7 +86,7 @@ const updateLocalBootData = (
return result;
};

const getCachedOrNull = () => {
const getCachedBootOrNull = () => {
try {
return JSON.parse(storage.getItem(BOOT_LOCAL_KEY));
} catch (err) {
Expand All @@ -112,6 +111,8 @@ export const BootDataProvider = ({
getRedirectUri,
getPage,
}: BootDataProviderProps): ReactElement => {
const { hostGranted } = useHostStatus();
const isExtension = checkIsExtension();
const queryClient = useQueryClient();
const preloadFeedsRef = useRef<PreloadFeeds>();
preloadFeedsRef.current = ({ feeds, user }) => {
Expand All @@ -130,22 +131,15 @@ export const BootDataProvider = ({
);
};

const [initialLoad, setInitialLoad] = useState<boolean>(null);
const [cachedBootData, setCachedBootData] = useState<Partial<Boot>>();

useEffect(() => {
const initialData = useMemo(() => {
if (localBootData) {
setCachedBootData(localBootData);

return;
return localBootData;
}

const boot = getLocalBootData();

if (!boot) {
setCachedBootData(null);

return;
return null;
}

if (boot?.settings?.theme) {
Expand All @@ -154,17 +148,15 @@ export const BootDataProvider = ({

preloadFeedsRef.current({ feeds: boot.feeds, user: boot.user });

setCachedBootData(boot);
return boot;
}, [localBootData]);

const { hostGranted } = useHostStatus();
const isExtension = checkIsExtension();
const logged = cachedBootData?.user as LoggedUser;
const logged = initialData?.user as LoggedUser;
const shouldRefetch = !!logged?.providers && !!logged?.id;
const lastAppliedChangeRef = useRef<Partial<BootCacheData>>();

const {
data: remoteData,
data: bootData,
error,
refetch,
isFetched,
Expand All @@ -175,24 +167,25 @@ export const BootDataProvider = ({
queryFn: async () => {
const result = await getBootData(app);
preloadFeedsRef.current({ feeds: result.feeds, user: result.user });
updateLocalBootData(bootData || {}, result);

return result;
},
refetchOnWindowFocus: shouldRefetch,
staleTime: STALE_TIME,
enabled: !isExtension || !!hostGranted,
placeholderData: initialData,
});

const isBootReady = isFetched && !isError;
const loadedFromCache = !!cachedBootData;
const { user, settings, alerts, notifications, squads, geo } =
cachedBootData || {};
const isBootReady = isFetched && !isError && !!bootData;
const loadedFromCache = !!bootData;
const { user, settings, alerts, notifications, squads, geo } = bootData || {};

useRefreshToken(remoteData?.accessToken, refetch);
useRefreshToken(bootData?.accessToken, refetch);
const updatedAtActive = user ? dataUpdatedAt : null;
const updateBootData = useCallback(
const updateQueryCache = useCallback(
(updatedBootData: Partial<BootCacheData>, update = true) => {
const cachedData = getCachedOrNull() || {};
const cachedData = getCachedBootOrNull() ?? {};
const lastAppliedChange = lastAppliedChangeRef.current;
let updatedData = { ...updatedBootData };
if (update) {
Expand All @@ -208,51 +201,52 @@ export const BootDataProvider = ({
}

const updated = updateLocalBootData(cachedData, updatedData);
setCachedBootData(updated);

queryClient.setQueryData<Partial<Boot>>(BOOT_QUERY_KEY, (previous) => {
if (!previous) {
return updated;
}

return { ...previous, ...updated };
});
},
[],
[queryClient],
);

const updateUser = useCallback(
async (newUser: LoggedUser | AnonymousUser) => {
updateBootData({ user: newUser });
updateQueryCache({ user: newUser });
await queryClient.invalidateQueries({
queryKey: generateQueryKey(RequestKey.Profile, newUser),
});
},
[updateBootData, queryClient],
[updateQueryCache, queryClient],
);

const updateSettings = useCallback(
(updatedSettings) => updateBootData({ settings: updatedSettings }),
[updateBootData],
(updatedSettings: Boot['settings']) =>
updateQueryCache({ settings: updatedSettings }),
[updateQueryCache],
);

const updateAlerts = useCallback(
(updatedAlerts) => updateBootData({ alerts: updatedAlerts }),
[updateBootData],
(updatedAlerts: Boot['alerts']) =>
updateQueryCache({ alerts: updatedAlerts }),
[updateQueryCache],
);

const updateExperimentation = useCallback(
(exp: BootCacheData['exp']) => {
updateLocalBootData(cachedBootData, { exp });
updateLocalBootData(bootData, { exp });
},
[cachedBootData],
[bootData],
);

gqlClient.setHeader(
'content-language',
(user as Partial<LoggedUser>)?.language || ContentLanguage.English,
);

useEffect(() => {
if (remoteData) {
setInitialLoad(initialLoad === null);
updateBootData(remoteData);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [remoteData]);

if (error) {
return (
<div className="mx-2 flex h-screen items-center justify-center">
Expand All @@ -266,7 +260,7 @@ export const BootDataProvider = ({
app={app}
user={user}
deviceId={deviceId}
experimentation={cachedBootData?.exp}
experimentation={bootData?.exp}
updateExperimentation={updateExperimentation}
>
<AuthContextProvider
Expand All @@ -276,13 +270,13 @@ export const BootDataProvider = ({
getRedirectUri={getRedirectUri}
loadingUser={!dataUpdatedAt || !user}
loadedUserFromCache={loadedFromCache}
visit={remoteData?.visit}
visit={bootData?.visit}
refetchBoot={refetch}
isFetched={isBootReady}
isLegacyLogout={remoteData?.isLegacyLogout}
accessToken={remoteData?.accessToken}
isLegacyLogout={bootData?.isLegacyLogout}
accessToken={bootData?.accessToken}
squads={squads}
firstLoad={initialLoad}
isAuthReady={isBootReady}
geo={geo}
>
<SettingsContextProvider
Expand Down
65 changes: 37 additions & 28 deletions packages/shared/src/hooks/log/useLogContextData.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MutableRefObject, useMemo } from 'react';
import { MutableRefObject, useCallback } from 'react';
import { LogEvent, PushToQueueFunc } from './useLogQueue';
import { getCurrentLifecycleState } from '../../lib/lifecycle';
import { Origin } from '../../lib/log';
Expand Down Expand Up @@ -51,33 +51,42 @@ export default function useLogContextData(
durationEventsQueue: MutableRefObject<Map<string, LogEvent>>,
sendBeacon: () => void,
): LogContextData {
return useMemo<LogContextData>(
() => ({
logEvent(event: LogEvent) {
pushToQueue([generateEvent(event, sharedPropsRef, getPage())]);
},
logEventStart(id, event) {
if (!durationEventsQueue.current.has(id)) {
durationEventsQueue.current.set(
id,
generateEvent(event, sharedPropsRef, getPage()),
);
}
},
logEventEnd(id, now = new Date()) {
const event = durationEventsQueue.current.get(id);
if (event) {
durationEventsQueue.current.delete(id);
event.event_duration =
now.getTime() - event.event_timestamp.getTime();
if (window.scrollY > 0 && event.event_name !== 'page inactive') {
event.page_state = 'active';
}
pushToQueue([event]);
const logEvent = useCallback(
(event: LogEvent) => {
pushToQueue([generateEvent(event, sharedPropsRef, getPage())]);
},
[getPage, pushToQueue, sharedPropsRef],
);
const logEventStart = useCallback(
(id, event) => {
if (!durationEventsQueue.current.has(id)) {
durationEventsQueue.current.set(
id,
generateEvent(event, sharedPropsRef, getPage()),
);
}
},
[durationEventsQueue, getPage, sharedPropsRef],
);
const logEventEnd = useCallback(
(id, now = new Date()) => {
const event = durationEventsQueue.current.get(id);
if (event) {
durationEventsQueue.current.delete(id);
event.event_duration = now.getTime() - event.event_timestamp.getTime();
if (window.scrollY > 0 && event.event_name !== 'page inactive') {
event.page_state = 'active';
}
},
sendBeacon,
}),
[sharedPropsRef, getPage, pushToQueue, durationEventsQueue, sendBeacon],
pushToQueue([event]);
}
},
[durationEventsQueue, pushToQueue],
);

return {
logEvent,
logEventStart,
logEventEnd,
sendBeacon,
};
}
Loading
Loading