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

[No QA] Fullstory small changes #55487

Merged
merged 19 commits into from
Jan 24, 2025
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
4 changes: 4 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1705,6 +1705,10 @@ const CONST = {
CONCIERGE: 'concierge',
OTHER: 'other',
WEB_PROP_ATTR: 'data-testid',
SHUTDOWN: 'shutdown',
RESTART: 'restart',
SET_IDENTITY: 'setIdentity',
OBSERVE: 'observe',
},

CONCIERGE_DISPLAY_NAME: 'Concierge',
Expand Down
4 changes: 2 additions & 2 deletions src/libs/Fullstory/index.native.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import FullStory, {FSPage} from '@fullstory/react-native';
import {Str} from 'expensify-common';
import type {OnyxEntry} from 'react-native-onyx';
import {isExpensifyTeam} from '@libs/PolicyUtils';
import {isConciergeChatReport, shouldUnmaskChat} from '@libs/ReportUtils';
import CONST from '@src/CONST';
import * as Environment from '@src/libs/Environment/Environment';
Expand Down Expand Up @@ -43,7 +43,7 @@ const FS = {
// UserMetadata onyx key.
Environment.getEnvironment().then((envName: string) => {
const isTestEmail = value.email !== undefined && value.email.startsWith('fullstory') && value.email.endsWith(CONST.EMAIL.QA_DOMAIN);
if ((CONST.ENVIRONMENT.PRODUCTION !== envName && !isTestEmail) || isExpensifyTeam(value?.email)) {
if ((CONST.ENVIRONMENT.PRODUCTION !== envName && !isTestEmail) || Str.extractEmailDomain(value.email ?? '') === CONST.EXPENSIFY_PARTNER_NAME) {
return;
}
FullStory.restart();
Expand Down
26 changes: 20 additions & 6 deletions src/libs/Fullstory/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import {FullStory, init, isInitialized} from '@fullstory/browser';
import {Str} from 'expensify-common';
import type {OnyxEntry} from 'react-native-onyx';
import {isExpensifyTeam} from '@libs/PolicyUtils';
import {isConciergeChatReport, shouldUnmaskChat} from '@libs/ReportUtils';
import * as Session from '@userActions/Session';
import CONST from '@src/CONST';
import * as Environment from '@src/libs/Environment/Environment';
import type {OnyxInputOrEntry, PersonalDetailsList, Report, UserMetadata} from '@src/types/onyx';
Expand Down Expand Up @@ -103,20 +104,26 @@ const FS = {
new Promise((resolve) => {
if (!isInitialized()) {
init({orgId: ''}, resolve);

// FS init function might have a race condition with the head snippet. If the head snipped is loaded first,
// then the init function will not call the resolve function, and we'll never identify the user logging in,
// and we need to call resolve manually. We're adding a 1s timeout to make sure the init function has enough
// time to call the resolve function in case it ran successfully.
setTimeout(resolve, 1000);
} else {
FullStory('observe', {type: 'start', callback: resolve});
FullStory(CONST.FULL_STORY.OBSERVE, {type: 'start', callback: resolve});
}
}),

/**
* Sets the identity as anonymous using the FullStory library.
*/
anonymize: () => FullStory('setIdentity', {anonymous: true}),
anonymize: () => FullStory(CONST.FULL_STORY.SET_IDENTITY, {anonymous: true}),

/**
* Sets the identity consent status using the FullStory library.
*/
consent: (c: boolean) => FullStory('setIdentity', {consent: c}),
consent: (c: boolean) => FullStory(CONST.FULL_STORY.SET_IDENTITY, {consent: c}),

/**
* Initializes the FullStory metadata with the provided metadata information.
Expand All @@ -130,9 +137,16 @@ const FS = {
try {
Environment.getEnvironment().then((envName: string) => {
const isTestEmail = value.email !== undefined && value.email.startsWith('fullstory') && value.email.endsWith(CONST.EMAIL.QA_DOMAIN);
if ((CONST.ENVIRONMENT.PRODUCTION !== envName && !isTestEmail) || isExpensifyTeam(value?.email)) {
if (
(CONST.ENVIRONMENT.PRODUCTION !== envName && !isTestEmail) ||
Str.extractEmailDomain(value.email ?? '') === CONST.EXPENSIFY_PARTNER_NAME ||
Session.isSupportAuthToken()
) {
// On web, if we started FS at some point in a browser, it will run forever. So let's shut it down if we don't want it to run.
FullStory(CONST.FULL_STORY.SHUTDOWN);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-FullStory(CONST.FULL_STORY.SHUTDOWN);
+ if (isInitialized()) {
+         FullStory(CONST.FULL_STORY.SHUTDOWN);
+  }

@danieldoglas

I got this error @rushatgabhane reported on latest main.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you need to update your .env file

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

USE_THIRD_PARTY_SCRIPTS=true

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm good suggestion. Let me put up a PR

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checking if it is initialized is a good way to prevent this from happening on dev.

return;
}
FullStory(CONST.FULL_STORY.RESTART);
FS.onReady().then(() => {
FS.consent(true);
const localMetadata = value;
Expand All @@ -151,7 +165,7 @@ const FS = {
* If the metadata contains an accountID, the user identity is defined with it.
*/
fsIdentify: (metadata: UserMetadata) => {
FullStory('setIdentity', {
FullStory(CONST.FULL_STORY.SET_IDENTITY, {
uid: String(metadata.accountID),
properties: metadata,
});
Expand Down
5 changes: 4 additions & 1 deletion src/pages/home/report/ReportActionsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import DateUtils from '@libs/DateUtils';
import {getChatFSAttributes} from '@libs/Fullstory';
import {getChatFSAttributes, parseFSAttributes} from '@libs/Fullstory';
import isReportScreenTopmostCentralPane from '@libs/Navigation/isReportScreenTopmostCentralPane';
import isSearchTopmostCentralPane from '@libs/Navigation/isSearchTopmostCentralPane';
import Navigation from '@libs/Navigation/Navigation';
Expand Down Expand Up @@ -782,6 +782,9 @@ function ReportActionsList({
loadOlderChats(false);
}, [loadOlderChats]);

// Parse Fullstory attributes on initial render
useLayoutEffect(parseFSAttributes, []);

const [reportActionsListTestID, reportActionsListFSClass] = getChatFSAttributes(participantsContext, 'ReportActionsList', report);

return (
Expand Down
1 change: 1 addition & 0 deletions tests/perf-test/ReportActionsList.perf-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ jest.mock('@libs/Fullstory', () => ({
},
getFSAttributes: jest.fn(),
getChatFSAttributes: jest.fn().mockReturnValue(['mockTestID', 'mockFSClass']),
parseFSAttributes: jest.fn(),
}));

jest.mock('@components/withCurrentUserPersonalDetails', () => {
Expand Down
Loading