diff --git a/jest/setupMockFullstoryLib.ts b/jest/setupMockFullstoryLib.ts
index 343703ee2f08..51a5f85b3af5 100644
--- a/jest/setupMockFullstoryLib.ts
+++ b/jest/setupMockFullstoryLib.ts
@@ -18,6 +18,7 @@ export default function mockFSLibrary() {
getChatFSClass: jest.fn(),
init: jest.fn(),
onReady: jest.fn(),
+ shouldInitialize: jest.fn().mockReturnValue(false),
consent: jest.fn(),
identify: jest.fn(),
consentAndIdentify: jest.fn(),
diff --git a/package.json b/package.json
index 138618415374..753d773a1b61 100644
--- a/package.json
+++ b/package.json
@@ -47,7 +47,7 @@
"test:debug": "TZ=utc NODE_OPTIONS='--inspect-brk --experimental-vm-modules' jest --runInBand",
"perf-test": "NODE_OPTIONS=--experimental-vm-modules npx reassure",
"typecheck": "NODE_OPTIONS=--max_old_space_size=8192 tsc",
- "lint": "NODE_OPTIONS=--max_old_space_size=8192 eslint . --max-warnings=111 --cache --cache-location=node_modules/.cache/eslint --concurrency=auto",
+ "lint": "NODE_OPTIONS=--max_old_space_size=8192 eslint . --max-warnings=112 --cache --cache-location=node_modules/.cache/eslint --concurrency=auto",
"lint-changed": "NODE_OPTIONS=--max_old_space_size=8192 ./scripts/lintChanged.sh",
"check-lazy-loading": "ts-node scripts/checkLazyLoading.ts",
"lint-watch": "npx eslint-watch --watch --changed",
diff --git a/src/components/AddPaymentCard/PaymentCardForm.tsx b/src/components/AddPaymentCard/PaymentCardForm.tsx
index 8bc5c02d3df9..f22cac53cec0 100644
--- a/src/components/AddPaymentCard/PaymentCardForm.tsx
+++ b/src/components/AddPaymentCard/PaymentCardForm.tsx
@@ -262,7 +262,6 @@ function PaymentCardForm({
submitButtonText={submitButtonText}
scrollContextEnabled
style={[styles.mh5, styles.flexGrow1]}
- forwardedFSClass={CONST.FULLSTORY.CLASS.MASK}
>
@@ -301,6 +302,7 @@ function PaymentCardForm({
placeholder={translate(label.defaults.expirationDate)}
inputMode={CONST.INPUT_MODE.NUMERIC}
maxLength={5}
+ forwardedFSClass={CONST.FULLSTORY.CLASS.MASK}
/>
@@ -313,6 +315,7 @@ function PaymentCardForm({
role={CONST.ROLE.PRESENTATION}
maxLength={4}
inputMode={CONST.INPUT_MODE.NUMERIC}
+ forwardedFSClass={CONST.FULLSTORY.CLASS.MASK}
/>
@@ -327,6 +330,7 @@ function PaymentCardForm({
maxInputLength={CONST.FORM_CHARACTER_LIMIT}
// Limit the address search only to the USA until we fully can support international debit cards
limitSearchesToCountry={CONST.COUNTRY.US}
+ forwardedFSClass={CONST.FULLSTORY.CLASS.MASK}
/>
)}
diff --git a/src/components/AddressSearch/index.tsx b/src/components/AddressSearch/index.tsx
index 259bdc5afd20..252571a81f39 100644
--- a/src/components/AddressSearch/index.tsx
+++ b/src/components/AddressSearch/index.tsx
@@ -78,6 +78,7 @@ function AddressSearch({
value,
locationBias,
caretHidden,
+ forwardedFSClass,
ref,
}: AddressSearchProps) {
const theme = useTheme();
@@ -363,6 +364,7 @@ function AddressSearch({
& {
- /** Indicate whether input is multiline */
- multiline?: boolean;
+type ComposerProps = Omit &
+ ForwardedFSClassProps & {
+ /** Indicate whether input is multiline */
+ multiline?: boolean;
- /** Maximum number of lines in the text input */
- maxLines?: number;
+ /** Maximum number of lines in the text input */
+ maxLines?: number;
- /** The default value of the comment box */
- defaultValue?: string;
+ /** The default value of the comment box */
+ defaultValue?: string;
- /** The value of the comment box */
- value?: string;
+ /** The value of the comment box */
+ value?: string;
- /**
- * Callback when the input was cleared using the .clear ref method.
- * The text parameter will be the value of the text that was cleared.
- */
- onClear?: (text: string) => void;
+ /**
+ * Callback when the input was cleared using the .clear ref method.
+ * The text parameter will be the value of the text that was cleared.
+ */
+ onClear?: (text: string) => void;
- /** Callback method handle when the input is changed */
- onChangeText?: (numberOfLines: string) => void;
+ /** Callback method handle when the input is changed */
+ onChangeText?: (numberOfLines: string) => void;
- /** Callback method to handle pasting a file */
- onPasteFile?: (files: FileObject | FileObject[]) => void;
+ /** Callback method to handle pasting a file */
+ onPasteFile?: (files: FileObject | FileObject[]) => void;
- /** General styles to apply to the text input */
- // eslint-disable-next-line react/forbid-prop-types
- style?: StyleProp;
+ /** General styles to apply to the text input */
+ // eslint-disable-next-line react/forbid-prop-types
+ style?: StyleProp;
- /** Whether or not this TextInput is disabled. */
- isDisabled?: boolean;
+ /** Whether or not this TextInput is disabled. */
+ isDisabled?: boolean;
- /** Set focus to this component the first time it renders.
+ /** Set focus to this component the first time it renders.
Override this in case you need to set focus on one field out of many, or when you want to disable autoFocus */
- autoFocus?: boolean;
+ autoFocus?: boolean;
- /** Update selection position on change */
- onSelectionChange?: (event: CustomSelectionChangeEvent) => void;
+ /** Update selection position on change */
+ onSelectionChange?: (event: CustomSelectionChangeEvent) => void;
- /** Selection Object */
- selection?: TextSelection;
+ /** Selection Object */
+ selection?: TextSelection;
- /** Should we calculate the caret position */
- shouldCalculateCaretPosition?: boolean;
+ /** Should we calculate the caret position */
+ shouldCalculateCaretPosition?: boolean;
- /** Function to check whether composer is covered up or not */
- checkComposerVisibility?: () => boolean;
+ /** Function to check whether composer is covered up or not */
+ checkComposerVisibility?: () => boolean;
- /** Whether the full composer is open */
- isComposerFullSize?: boolean;
+ /** Whether the full composer is open */
+ isComposerFullSize?: boolean;
- /** Should make the input only scroll inside the element avoid scroll out to parent */
- shouldContainScroll?: boolean;
+ /** Should make the input only scroll inside the element avoid scroll out to parent */
+ shouldContainScroll?: boolean;
- /** Indicates whether the composer is in a group policy report. Used for disabling report mentioning style in markdown input */
- isGroupPolicyReport?: boolean;
+ /** Indicates whether the composer is in a group policy report. Used for disabling report mentioning style in markdown input */
+ isGroupPolicyReport?: boolean;
- /** Ref exposing imperative methods on the underlying text input */
- ref?: Ref;
-};
+ /** Ref exposing imperative methods on the underlying text input */
+ ref?: Ref;
+ };
export type {TextSelection, ComposerProps, CustomSelectionChangeEvent};
diff --git a/src/components/DatePicker/DatePickerModal.tsx b/src/components/DatePicker/DatePickerModal.tsx
index ddadb8d46808..ce38a776dd29 100644
--- a/src/components/DatePicker/DatePickerModal.tsx
+++ b/src/components/DatePicker/DatePickerModal.tsx
@@ -34,6 +34,7 @@ function DatePickerModal({
onSelected,
shouldCloseWhenBrowserNavigationChanged = false,
shouldPositionFromTop = false,
+ forwardedFSClass,
}: DatePickerProps) {
const [selectedDate, setSelectedDate] = useState(value ?? defaultValue ?? undefined);
const anchorRef = useRef(null);
@@ -74,6 +75,7 @@ function DatePickerModal({
shouldEnableNewFocusManagement
shouldMeasureAnchorPositionFromTop={shouldPositionFromTop}
shouldSkipRemeasurement
+ forwardedFSClass={forwardedFSClass}
>
@@ -156,6 +158,7 @@ function DatePicker({
onClose={closeDatePicker}
anchorPosition={popoverPosition}
shouldPositionFromTop={!isInverted}
+ forwardedFSClass={forwardedFSClass}
/>
>
);
diff --git a/src/components/DatePicker/types.ts b/src/components/DatePicker/types.ts
index 24ef3e61f706..8f097d608c29 100644
--- a/src/components/DatePicker/types.ts
+++ b/src/components/DatePicker/types.ts
@@ -1,9 +1,10 @@
import type {ForwardedRef} from 'react';
import type PopoverWithMeasuredContentProps from '@components/PopoverWithMeasuredContent/types';
import type {BaseTextInputProps, BaseTextInputRef} from '@components/TextInput/BaseTextInput/types';
+import type {ForwardedFSClassProps} from '@libs/Fullstory/types';
import type {OnyxFormValuesMapping} from '@src/ONYXKEYS';
-type DatePickerBaseProps = {
+type DatePickerBaseProps = ForwardedFSClassProps & {
/**
* The datepicker supports any value that `new Date()` can parse.
* `onInputChange` would always be called with a Date (or null)
diff --git a/src/components/LHNOptionsList/OptionRowLHN.tsx b/src/components/LHNOptionsList/OptionRowLHN.tsx
index 416f819d3d6a..d8e5936f0cda 100644
--- a/src/components/LHNOptionsList/OptionRowLHN.tsx
+++ b/src/components/LHNOptionsList/OptionRowLHN.tsx
@@ -22,6 +22,7 @@ import useThemeStyles from '@hooks/useThemeStyles';
import DateUtils from '@libs/DateUtils';
import DomUtils from '@libs/DomUtils';
import {containsCustomEmoji as containsCustomEmojiUtils, containsOnlyCustomEmoji} from '@libs/EmojiUtils';
+import FS from '@libs/Fullstory';
import {shouldOptionShowTooltip, shouldUseBoldText} from '@libs/OptionsListUtils';
import Performance from '@libs/Performance';
import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager';
@@ -167,6 +168,8 @@ function OptionRowLHN({
const subscriptAvatarBorderColor = isOptionFocused ? focusedBackgroundColor : theme.sidebar;
const firstIcon = optionItem.icons?.at(0);
+ const alternateTextFSClass = FS.getChatFSClass(report);
+
const onOptionPress = (event: GestureResponderEvent | KeyboardEvent | undefined) => {
Performance.markStart(CONST.TIMING.OPEN_REPORT);
Timing.start(CONST.TIMING.OPEN_REPORT);
@@ -299,7 +302,7 @@ function OptionRowLHN({
style={alternateTextStyle}
numberOfLines={1}
accessibilityLabel={translate('accessibilityHints.lastChatMessagePreview')}
- fsClass={CONST.FULLSTORY.CLASS.MASK}
+ fsClass={alternateTextFSClass}
>
{alternateTextContainsCustomEmojiWithText ? (
{optionItem.descriptiveText}
diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx
index f814aed00a91..e68a53726be7 100644
--- a/src/components/MenuItem.tsx
+++ b/src/components/MenuItem.tsx
@@ -12,6 +12,7 @@ import ControlSelection from '@libs/ControlSelection';
import convertToLTR from '@libs/convertToLTR';
import {canUseTouchScreen, hasHoverSupport} from '@libs/DeviceCapabilities';
import {containsCustomEmoji, containsOnlyCustomEmoji} from '@libs/EmojiUtils';
+import type {ForwardedFSClassProps} from '@libs/Fullstory/types';
import getButtonState from '@libs/getButtonState';
import mergeRefs from '@libs/mergeRefs';
import Parser from '@libs/Parser';
@@ -64,7 +65,7 @@ type NoIcon = {
icon?: undefined;
};
-type MenuItemBaseProps = {
+type MenuItemBaseProps = ForwardedFSClassProps & {
/** Reference to the outer element */
ref?: PressableRef | Ref;
@@ -518,6 +519,7 @@ function MenuItem({
copyValue = title,
copyable = false,
hasSubMenuItems = false,
+ forwardedFSClass,
ref,
isFocused,
}: MenuItemProps) {
@@ -848,7 +850,10 @@ function MenuItem({
)}
{(!!title || !!shouldShowTitleIcon) && (
-
+
{!!title && (shouldRenderAsHTML || (shouldParseTitle && !!html.length)) && (
diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionItem.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionItem.tsx
index f4a3a38847a9..c7e35a8892cd 100644
--- a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionItem.tsx
+++ b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionItem.tsx
@@ -13,14 +13,13 @@ import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import ControlSelection from '@libs/ControlSelection';
import canUseTouchScreen from '@libs/DeviceCapabilities/canUseTouchScreen';
-import type {ForwardedFSClassProps} from '@libs/Fullstory/types';
import {getTransactionPendingAction, isTransactionPendingDelete} from '@libs/TransactionUtils';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import type {Report, TransactionViolation} from '@src/types/onyx';
import type {TransactionWithOptionalHighlight} from './MoneyRequestReportTransactionList';
-type MoneyRequestReportTransactionItemProps = ForwardedFSClassProps & {
+type MoneyRequestReportTransactionItemProps = {
/** The transaction that is being displayed */
transaction: TransactionWithOptionalHighlight;
@@ -80,7 +79,6 @@ function MoneyRequestReportTransactionItem({
amountColumnSize,
taxAmountColumnSize,
scrollToNewTransaction,
- forwardedFSClass,
onArrowRightPress,
}: MoneyRequestReportTransactionItemProps) {
const {translate} = useLocalize();
@@ -137,7 +135,6 @@ function MoneyRequestReportTransactionItem({
disabled={isTransactionPendingDelete(transaction)}
ref={viewRef}
wrapperStyle={[animatedHighlightStyle, styles.userSelectNone]}
- forwardedFSClass={forwardedFSClass}
>
{
@@ -428,8 +425,6 @@ function MoneyRequestReportTransactionList({
const listHorizontalPadding = styles.ph5;
- const transactionItemFSClass = FS.getChatFSClass(personalDetailsList, report);
-
if (isEmptyTransactions) {
return (
<>
@@ -528,7 +523,6 @@ function MoneyRequestReportTransactionList({
amountColumnSize={amountColumnSize}
taxAmountColumnSize={taxAmountColumnSize}
scrollToNewTransaction={transaction.transactionID === newTransactions?.at(0)?.transactionID ? scrollToNewTransaction : undefined}
- forwardedFSClass={transactionItemFSClass}
onArrowRightPress={handleArrowRightPress}
/>
);
@@ -552,7 +546,6 @@ function MoneyRequestReportTransactionList({
amountColumnSize={amountColumnSize}
taxAmountColumnSize={taxAmountColumnSize}
scrollToNewTransaction={transaction.transactionID === newTransactions?.at(0)?.transactionID ? scrollToNewTransaction : undefined}
- forwardedFSClass={transactionItemFSClass}
onArrowRightPress={handleArrowRightPress}
/>
))}
diff --git a/src/components/RNMarkdownTextInput.tsx b/src/components/RNMarkdownTextInput.tsx
index d05c4d978771..cc99e32d9c80 100644
--- a/src/components/RNMarkdownTextInput.tsx
+++ b/src/components/RNMarkdownTextInput.tsx
@@ -2,9 +2,11 @@ import type {MarkdownTextInputProps} from '@expensify/react-native-live-markdown
import {MarkdownTextInput} from '@expensify/react-native-live-markdown';
import type {ForwardedRef} from 'react';
import React, {useCallback, useEffect, useRef} from 'react';
+import {View} from 'react-native';
import Animated, {useSharedValue} from 'react-native-reanimated';
import useShortMentionsList from '@hooks/useShortMentionsList';
import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import toggleSelectionFormat from '@libs/FormatSelectionUtils';
import type {ForwardedFSClassProps} from '@libs/Fullstory/types';
import {parseExpensiMarkWithShortMentions} from '@libs/ParsingUtils';
@@ -23,8 +25,9 @@ type RNMarkdownTextInputWithRefProps = Omit &
ref?: ForwardedRef;
};
-function RNMarkdownTextInputWithRef({maxLength, parser, ref, forwardedFSClass = CONST.FULLSTORY.CLASS.MASK, ...props}: RNMarkdownTextInputWithRefProps) {
+function RNMarkdownTextInputWithRef({maxLength, parser, ref, forwardedFSClass = CONST.FULLSTORY.CLASS.UNMASK, ...props}: RNMarkdownTextInputWithRefProps) {
const theme = useTheme();
+ const styles = useThemeStyles();
const {availableLoginsList, currentUserMentions} = useShortMentionsList();
const mentionsSharedVal = useSharedValue(availableLoginsList);
@@ -71,22 +74,30 @@ function RNMarkdownTextInputWithRef({maxLength, parser, ref, forwardedFSClass =
}, [availableLoginsList, mentionsSharedVal]);
return (
-
+ >
+
+
);
}
diff --git a/src/components/RNMaskedTextInput.tsx b/src/components/RNMaskedTextInput.tsx
index 9891b3fa4830..0d8136e604a5 100644
--- a/src/components/RNMaskedTextInput.tsx
+++ b/src/components/RNMaskedTextInput.tsx
@@ -18,7 +18,7 @@ type RNMaskedTextInputWithRefProps = MaskedTextInputProps &
ref?: ForwardedRef;
};
-function RNMaskedTextInputWithRef({ref, forwardedFSClass = CONST.FULLSTORY.CLASS.MASK, ...props}: RNMaskedTextInputWithRefProps) {
+function RNMaskedTextInputWithRef({ref, forwardedFSClass = CONST.FULLSTORY.CLASS.UNMASK, ...props}: RNMaskedTextInputWithRefProps) {
const theme = useTheme();
return (
diff --git a/src/components/RNTextInput.tsx b/src/components/RNTextInput.tsx
index bffdce8aa466..0ca4c1a3f509 100644
--- a/src/components/RNTextInput.tsx
+++ b/src/components/RNTextInput.tsx
@@ -17,7 +17,7 @@ type RNTextInputWithRefProps = TextInputProps &
ref?: ForwardedRef;
};
-function RNTextInputWithRef({ref, forwardedFSClass = CONST.FULLSTORY.CLASS.MASK, ...props}: RNTextInputWithRefProps) {
+function RNTextInputWithRef({ref, forwardedFSClass = CONST.FULLSTORY.CLASS.UNMASK, ...props}: RNTextInputWithRefProps) {
const theme = useTheme();
return (
diff --git a/src/components/RadioButtonWithLabel.tsx b/src/components/RadioButtonWithLabel.tsx
index 824660d631af..a077782041d8 100644
--- a/src/components/RadioButtonWithLabel.tsx
+++ b/src/components/RadioButtonWithLabel.tsx
@@ -3,12 +3,13 @@ import React from 'react';
import type {StyleProp, ViewStyle} from 'react-native';
import {View} from 'react-native';
import useThemeStyles from '@hooks/useThemeStyles';
+import type {ForwardedFSClassProps} from '@libs/Fullstory/types';
import FormHelpMessage from './FormHelpMessage';
import * as Pressables from './Pressable';
import RadioButton from './RadioButton';
import Text from './Text';
-type RadioButtonWithLabelProps = {
+type RadioButtonWithLabelProps = ForwardedFSClassProps & {
/** Whether the radioButton is checked */
isChecked: boolean;
@@ -42,7 +43,18 @@ type RadioButtonWithLabelProps = {
const PressableWithFeedback = Pressables.PressableWithFeedback;
-function RadioButtonWithLabel({labelElement, style, label = '', hasError = false, errorText = '', isChecked, onPress, wrapperStyle, shouldBlendOpacity}: RadioButtonWithLabelProps) {
+function RadioButtonWithLabel({
+ labelElement,
+ style,
+ label = '',
+ hasError = false,
+ errorText = '',
+ isChecked,
+ onPress,
+ wrapperStyle,
+ shouldBlendOpacity,
+ forwardedFSClass,
+}: RadioButtonWithLabelProps) {
const styles = useThemeStyles();
const defaultStyles = [styles.flexRow, styles.alignItemsCenter];
@@ -69,7 +81,14 @@ function RadioButtonWithLabel({labelElement, style, label = '', hasError = false
pressDimmingValue={0.5}
shouldBlendOpacity={shouldBlendOpacity}
>
- {!!label && {label}}
+ {!!label && (
+
+ {label}
+
+ )}
{!!labelElement && labelElement}
diff --git a/src/components/RadioButtons.tsx b/src/components/RadioButtons.tsx
index afbc8879c68c..cf087a4233d5 100644
--- a/src/components/RadioButtons.tsx
+++ b/src/components/RadioButtons.tsx
@@ -3,6 +3,7 @@ import type {ForwardedRef} from 'react';
import {View} from 'react-native';
import type {StyleProp, ViewStyle} from 'react-native';
import useThemeStyles from '@hooks/useThemeStyles';
+import type {ForwardedFSClassProps} from '@libs/Fullstory/types';
import FormHelpMessage from './FormHelpMessage';
import RadioButtonWithLabel from './RadioButtonWithLabel';
@@ -12,7 +13,7 @@ type Choice = {
style?: StyleProp;
};
-type RadioButtonsProps = {
+type RadioButtonsProps = ForwardedFSClassProps & {
/** List of choices to display via radio buttons */
items: Choice[];
@@ -38,7 +39,7 @@ type RadioButtonsProps = {
ref?: ForwardedRef;
};
-function RadioButtons({items, onPress, defaultCheckedValue = '', radioButtonStyle, errorText, onInputChange = () => {}, value, ref}: RadioButtonsProps) {
+function RadioButtons({items, onPress, defaultCheckedValue = '', radioButtonStyle, errorText, onInputChange = () => {}, value, forwardedFSClass, ref}: RadioButtonsProps) {
const styles = useThemeStyles();
const [checkedValue, setCheckedValue] = useState(defaultCheckedValue);
@@ -66,6 +67,7 @@ function RadioButtons({items, onPress, defaultCheckedValue = '', radioButtonStyl
return onPress(item.value);
}}
label={item.label}
+ forwardedFSClass={forwardedFSClass}
/>
))}
diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx
index c28357497c5b..e0cbaf0c33e3 100644
--- a/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx
+++ b/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx
@@ -8,7 +8,6 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useStyleUtils from '@hooks/useStyleUtils';
import useThemeStyles from '@hooks/useThemeStyles';
import useTransactionViolations from '@hooks/useTransactionViolations';
-import FS from '@libs/Fullstory';
import Performance from '@libs/Performance';
import {getIOUActionForReportID, isSplitBillAction as isSplitBillActionReportActionsUtils, isTrackExpenseAction as isTrackExpenseActionReportActionsUtils} from '@libs/ReportActionsUtils';
import {isIOUReport} from '@libs/ReportUtils';
@@ -137,8 +136,6 @@ function MoneyRequestReportPreview({
/>
);
- const fsClass = FS.getChatFSClass(personalDetailsList, iouReport);
-
return (
);
}
diff --git a/src/components/Search/SearchAutocompleteInput.tsx b/src/components/Search/SearchAutocompleteInput.tsx
index e251c1c32706..02cb765e2119 100644
--- a/src/components/Search/SearchAutocompleteInput.tsx
+++ b/src/components/Search/SearchAutocompleteInput.tsx
@@ -261,7 +261,6 @@ function SearchAutocompleteInput({
shouldShowClearButton={!!value && !isSearchingForReports}
shouldHideClearButton={false}
onClearInput={clearFilters}
- forwardedFSClass={CONST.FULLSTORY.CLASS.UNMASK}
/>
diff --git a/src/components/Search/SearchAutocompleteList.tsx b/src/components/Search/SearchAutocompleteList.tsx
index 04c7fec445e3..e02cab09a4df 100644
--- a/src/components/Search/SearchAutocompleteList.tsx
+++ b/src/components/Search/SearchAutocompleteList.tsx
@@ -18,8 +18,9 @@ import useThemeStyles from '@hooks/useThemeStyles';
import {getCardFeedsForDisplay} from '@libs/CardFeedUtils';
import {getCardDescription, isCard, isCardHiddenFromSearch} from '@libs/CardUtils';
import {getDecodedCategoryName} from '@libs/CategoryUtils';
+import FS from '@libs/Fullstory';
import Log from '@libs/Log';
-import type {Options} from '@libs/OptionsListUtils';
+import type {Options, SearchOption} from '@libs/OptionsListUtils';
import {combineOrderingOfReportsAndPersonalDetails, getSearchOptions} from '@libs/OptionsListUtils';
import Performance from '@libs/Performance';
import {getAllTaxRates, getCleanedTagName, shouldShowPolicy} from '@libs/PolicyUtils';
@@ -150,10 +151,13 @@ function SearchRouterItem(props: UserListItemProps | SearchQueryList
/>
);
}
+
+ const fsClass = FS.getChatFSClass((props.item as SearchOption | undefined)?.item);
+
return (
diff --git a/src/components/SelectionListWithSections/ChatListItem.tsx b/src/components/SelectionListWithSections/ChatListItem.tsx
index a337558d57af..233a7c44cf0c 100644
--- a/src/components/SelectionListWithSections/ChatListItem.tsx
+++ b/src/components/SelectionListWithSections/ChatListItem.tsx
@@ -49,7 +49,7 @@ function ChatListItem({
item.cursorStyle,
];
- const fsClass = FS.getChatFSClass(personalDetails, report);
+ const fsClass = FS.getChatFSClass(report);
return (
({
onLongPressRow,
shouldSyncFocus,
allReports,
- personalDetails,
}: TaskListItemProps) {
const taskItem = item as unknown as TaskListItemType;
const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${taskItem?.parentReportID}`];
@@ -53,7 +52,7 @@ function TaskListItem({
backgroundColor: theme.highlightBG,
});
- const fsClass = FS.getChatFSClass(personalDetails, parentReport);
+ const fsClass = FS.getChatFSClass(parentReport);
return (
;
};
-function SingleChoiceQuestion({prompt, errorText, possibleAnswers, currentQuestionIndex, onInputChange, ref}: SingleChoiceQuestionProps) {
+function SingleChoiceQuestion({prompt, errorText, possibleAnswers, currentQuestionIndex, onInputChange, forwardedFSClass, ref}: SingleChoiceQuestionProps) {
const styles = useThemeStyles();
return (
@@ -32,6 +33,7 @@ function SingleChoiceQuestion({prompt, errorText, possibleAnswers, currentQuesti
key={currentQuestionIndex}
onPress={onInputChange}
errorText={errorText}
+ forwardedFSClass={forwardedFSClass}
/>
>
);
diff --git a/src/components/SubStepForms/AddressStep.tsx b/src/components/SubStepForms/AddressStep.tsx
index b0a1087e8c75..a841b2cf5312 100644
--- a/src/components/SubStepForms/AddressStep.tsx
+++ b/src/components/SubStepForms/AddressStep.tsx
@@ -6,6 +6,7 @@ import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import type {SubStepProps} from '@hooks/useSubStep/types';
import useThemeStyles from '@hooks/useThemeStyles';
+import type {ForwardedFSClassProps} from '@libs/Fullstory/types';
import {getFieldRequiredErrors, isValidAddress, isValidZipCode, isValidZipCodeInternational} from '@libs/ValidationUtils';
import PatriotActLink from '@pages/EnablePayments/PatriotActLink';
import AddressFormFields from '@pages/ReimbursementAccount/AddressFormFields';
@@ -31,64 +32,65 @@ type AddressInputIDs = {
country?: string;
};
-type AddressStepProps = SubStepProps & {
- /** The ID of the form */
- formID: TFormID;
+type AddressStepProps = SubStepProps &
+ ForwardedFSClassProps & {
+ /** The ID of the form */
+ formID: TFormID;
- /** The title of the form */
- formTitle: string;
+ /** The title of the form */
+ formTitle: string;
- /** The disclaimer informing that PO box is not allowed */
- formPOBoxDisclaimer?: string;
+ /** The disclaimer informing that PO box is not allowed */
+ formPOBoxDisclaimer?: string;
- /** The validation function to call when the form is submitted */
- customValidate?: (values: FormOnyxValues) => FormInputErrors;
+ /** The validation function to call when the form is submitted */
+ customValidate?: (values: FormOnyxValues) => FormInputErrors;
- /** A function to call when the form is submitted */
- onSubmit: (values: FormOnyxValues) => void;
+ /** A function to call when the form is submitted */
+ onSubmit: (values: FormOnyxValues) => void;
- /** Fields list of the form */
- stepFields: Array>;
+ /** Fields list of the form */
+ stepFields: Array>;
- /** The IDs of the input fields */
- inputFieldsIDs: AddressInputIDs;
+ /** The IDs of the input fields */
+ inputFieldsIDs: AddressInputIDs;
- /** The default values for the form */
- defaultValues: AddressValues;
+ /** The default values for the form */
+ defaultValues: AddressValues;
- /** Should show help links */
- shouldShowHelpLinks?: boolean;
+ /** Should show help links */
+ shouldShowHelpLinks?: boolean;
- /** Indicates if country selector should be displayed */
- shouldDisplayCountrySelector?: boolean;
+ /** Indicates if country selector should be displayed */
+ shouldDisplayCountrySelector?: boolean;
- /** Indicates if state selector should be displayed */
- shouldDisplayStateSelector?: boolean;
+ /** Indicates if state selector should be displayed */
+ shouldDisplayStateSelector?: boolean;
- /** Label for the state selector */
- stateSelectorLabel?: string;
+ /** Label for the state selector */
+ stateSelectorLabel?: string;
- /** The title of the state selector modal */
- stateSelectorModalHeaderTitle?: string;
+ /** The title of the state selector modal */
+ stateSelectorModalHeaderTitle?: string;
- /** The title of the state selector search input */
- stateSelectorSearchInputTitle?: string;
+ /** The title of the state selector search input */
+ stateSelectorSearchInputTitle?: string;
- /** Callback to be called when the country is changed */
- onCountryChange?: (country: unknown) => void;
+ /** Callback to be called when the country is changed */
+ onCountryChange?: (country: unknown) => void;
- /** Translation key of street field */
- streetTranslationKey?: TranslationPaths;
+ /** Translation key of street field */
+ streetTranslationKey?: TranslationPaths;
- /** Indicates if country can be changed by user */
- shouldAllowCountryChange?: boolean;
+ /** Indicates if country can be changed by user */
+ shouldAllowCountryChange?: boolean;
- /** Indicates if zip code format should be validated */
- shouldValidateZipCodeFormat?: boolean;
+ /** Indicates if zip code format should be validated */
+ shouldValidateZipCodeFormat?: boolean;
- /** Whether to show the Patriot Act help link (EnablePayments-only) */
- shouldShowPatriotActLink?: boolean;
-};
+ /** Whether to show the Patriot Act help link (EnablePayments-only) */
+ shouldShowPatriotActLink?: boolean;
+ };
function AddressStep({
formID,
@@ -111,6 +113,7 @@ function AddressStep({
shouldAllowCountryChange = true,
shouldValidateZipCodeFormat = true,
shouldShowPatriotActLink = false,
+ forwardedFSClass,
}: AddressStepProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();
@@ -178,6 +181,7 @@ function AddressStep({
onCountryChange={onCountryChange}
shouldAllowCountryChange={shouldAllowCountryChange}
shouldValidateZipCodeFormat={shouldValidateZipCodeFormat}
+ forwardedFSClass={forwardedFSClass}
/>
{!!shouldShowHelpLinks && (
<>
diff --git a/src/components/SubStepForms/ConfirmationStep.tsx b/src/components/SubStepForms/ConfirmationStep.tsx
index c7e963dc13db..641433b78242 100644
--- a/src/components/SubStepForms/ConfirmationStep.tsx
+++ b/src/components/SubStepForms/ConfirmationStep.tsx
@@ -11,6 +11,7 @@ import useNetwork from '@hooks/useNetwork';
import useSafeAreaPaddings from '@hooks/useSafeAreaPaddings';
import type {SubStepProps} from '@hooks/useSubStep/types';
import useThemeStyles from '@hooks/useThemeStyles';
+import type {ForwardedFSClassProps} from '@libs/Fullstory/types';
import CONST from '@src/CONST';
type SummaryItem = {
@@ -20,30 +21,41 @@ type SummaryItem = {
onPress: () => void;
};
-type ConfirmationStepProps = SubStepProps & {
- /** The title of the step */
- pageTitle: string;
+type ConfirmationStepProps = SubStepProps &
+ ForwardedFSClassProps & {
+ /** The title of the step */
+ pageTitle: string;
- /** The summary items to display */
- summaryItems: SummaryItem[];
+ /** The summary items to display */
+ summaryItems: SummaryItem[];
- /** Whether show additional section with Onfido terms etc. */
- showOnfidoLinks: boolean;
+ /** Whether show additional section with Onfido terms etc. */
+ showOnfidoLinks: boolean;
- /** The title of the Onfido section */
- onfidoLinksTitle?: string;
+ /** The title of the Onfido section */
+ onfidoLinksTitle?: string;
- /** Whether the data is loading */
- isLoading?: boolean;
+ /** Whether the data is loading */
+ isLoading?: boolean;
- /** The error message to display */
- error?: string;
+ /** The error message to display */
+ error?: string;
- /** Whether to apply safe area padding bottom */
- shouldApplySafeAreaPaddingBottom?: boolean;
-};
+ /** Whether to apply safe area padding bottom */
+ shouldApplySafeAreaPaddingBottom?: boolean;
+ };
-function ConfirmationStep({pageTitle, summaryItems, showOnfidoLinks, onfidoLinksTitle, isLoading, error, onNext, shouldApplySafeAreaPaddingBottom = true}: ConfirmationStepProps) {
+function ConfirmationStep({
+ pageTitle,
+ summaryItems,
+ showOnfidoLinks,
+ onfidoLinksTitle,
+ isLoading,
+ error,
+ onNext,
+ shouldApplySafeAreaPaddingBottom = true,
+ forwardedFSClass,
+}: ConfirmationStepProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();
const {isOffline} = useNetwork();
@@ -63,6 +75,7 @@ function ConfirmationStep({pageTitle, summaryItems, showOnfidoLinks, onfidoLinks
title={title}
shouldShowRightIcon={shouldShowRightIcon}
onPress={onPress}
+ forwardedFSClass={forwardedFSClass}
/>
))}
diff --git a/src/components/SubStepForms/DateOfBirthStep.tsx b/src/components/SubStepForms/DateOfBirthStep.tsx
index e05f53d535d6..4b035b0f6fd1 100644
--- a/src/components/SubStepForms/DateOfBirthStep.tsx
+++ b/src/components/SubStepForms/DateOfBirthStep.tsx
@@ -8,39 +8,41 @@ import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import type {SubStepProps} from '@hooks/useSubStep/types';
import useThemeStyles from '@hooks/useThemeStyles';
+import type {ForwardedFSClassProps} from '@libs/Fullstory/types';
import {getFieldRequiredErrors, isValidPastDate, meetsMaximumAgeRequirement, meetsMinimumAgeRequirement} from '@libs/ValidationUtils';
import PatriotActLink from '@pages/EnablePayments/PatriotActLink';
import CONST from '@src/CONST';
import type {OnyxFormValuesMapping} from '@src/ONYXKEYS';
-type DateOfBirthStepProps = SubStepProps & {
- /** The ID of the form */
- formID: TFormID;
+type DateOfBirthStepProps = SubStepProps &
+ ForwardedFSClassProps & {
+ /** The ID of the form */
+ formID: TFormID;
- /** The title of the form */
- formTitle: string;
+ /** The title of the form */
+ formTitle: string;
- /** The validation function to call when the form is submitted */
- customValidate?: (values: FormOnyxValues) => FormInputErrors;
+ /** The validation function to call when the form is submitted */
+ customValidate?: (values: FormOnyxValues) => FormInputErrors;
- /** A function to call when the form is submitted */
- onSubmit: (values: FormOnyxValues) => void;
+ /** A function to call when the form is submitted */
+ onSubmit: (values: FormOnyxValues) => void;
- /** Fields list of the form */
- stepFields: Array>;
+ /** Fields list of the form */
+ stepFields: Array>;
- /** The ID of the date of birth input */
- dobInputID: string;
+ /** The ID of the date of birth input */
+ dobInputID: string;
- /** The default value for the date of birth input */
- dobDefaultValue: string;
+ /** The default value for the date of birth input */
+ dobDefaultValue: string;
- /** Optional footer component */
- footerComponent?: React.ReactNode;
+ /** Optional footer component */
+ footerComponent?: React.ReactNode;
- /** Whether to show the Patriot Act help link (EnablePayments-only) */
- shouldShowPatriotActLink?: boolean;
-};
+ /** Whether to show the Patriot Act help link (EnablePayments-only) */
+ shouldShowPatriotActLink?: boolean;
+ };
function DateOfBirthStep({
formID,
@@ -53,6 +55,7 @@ function DateOfBirthStep({
isEditing,
footerComponent,
shouldShowPatriotActLink = false,
+ forwardedFSClass,
}: DateOfBirthStepProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();
@@ -102,6 +105,7 @@ function DateOfBirthStep({
maxDate={maxDate}
shouldSaveDraft={!isEditing}
autoFocus
+ forwardedFSClass={forwardedFSClass}
/>
{footerComponent}
{shouldShowPatriotActLink && }
diff --git a/src/components/SubStepForms/FullNameStep.tsx b/src/components/SubStepForms/FullNameStep.tsx
index cb9a5f3d6e28..b697c87ff8a6 100644
--- a/src/components/SubStepForms/FullNameStep.tsx
+++ b/src/components/SubStepForms/FullNameStep.tsx
@@ -8,52 +8,54 @@ import TextInput from '@components/TextInput';
import useLocalize from '@hooks/useLocalize';
import type {SubStepProps} from '@hooks/useSubStep/types';
import useThemeStyles from '@hooks/useThemeStyles';
+import type {ForwardedFSClassProps} from '@libs/Fullstory/types';
import {doesContainReservedWord, getFieldRequiredErrors, isRequiredFulfilled, isValidLegalName} from '@libs/ValidationUtils';
import PatriotActLink from '@pages/EnablePayments/PatriotActLink';
import HelpLinks from '@pages/ReimbursementAccount/USD/Requestor/PersonalInfo/HelpLinks';
import CONST from '@src/CONST';
import type {OnyxFormValuesMapping} from '@src/ONYXKEYS';
-type FullNameStepProps = SubStepProps & {
- /** The ID of the form */
- formID: TFormID;
+type FullNameStepProps = SubStepProps &
+ ForwardedFSClassProps & {
+ /** The ID of the form */
+ formID: TFormID;
- /** The title of the form */
- formTitle: string;
+ /** The title of the form */
+ formTitle: string;
- /** The validation function to call when the form is submitted */
- customValidate?: (values: FormOnyxValues) => FormInputErrors;
+ /** The validation function to call when the form is submitted */
+ customValidate?: (values: FormOnyxValues) => FormInputErrors;
- /** A function to call when the form is submitted */
- onSubmit: (values: FormOnyxValues) => void;
+ /** A function to call when the form is submitted */
+ onSubmit: (values: FormOnyxValues) => void;
- /** Fields list of the form */
- stepFields: Array>;
+ /** Fields list of the form */
+ stepFields: Array>;
- /** The ID of the first name input */
- firstNameInputID: string;
+ /** The ID of the first name input */
+ firstNameInputID: string;
- /** The ID of the last name input */
- lastNameInputID: string;
+ /** The ID of the last name input */
+ lastNameInputID: string;
- /** The default values for the form */
- defaultValues: {
- firstName: string;
- lastName: string;
- };
+ /** The default values for the form */
+ defaultValues: {
+ firstName: string;
+ lastName: string;
+ };
- /** Should show the help link or not */
- shouldShowHelpLinks?: boolean;
+ /** Should show the help link or not */
+ shouldShowHelpLinks?: boolean;
- /** Custom label of the first name input */
- customFirstNameLabel?: string;
+ /** Custom label of the first name input */
+ customFirstNameLabel?: string;
- /** Custom label of the last name input */
- customLastNameLabel?: string;
+ /** Custom label of the last name input */
+ customLastNameLabel?: string;
- /** Whether to show the Patriot Act help link (EnablePayments-only) */
- shouldShowPatriotActLink?: boolean;
-};
+ /** Whether to show the Patriot Act help link (EnablePayments-only) */
+ shouldShowPatriotActLink?: boolean;
+ };
function FullNameStep({
formID,
@@ -69,6 +71,7 @@ function FullNameStep({
customFirstNameLabel,
customLastNameLabel,
shouldShowPatriotActLink = false,
+ forwardedFSClass,
}: FullNameStepProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();
@@ -141,6 +144,7 @@ function FullNameStep({
defaultValue={defaultValues.firstName}
shouldSaveDraft={!isEditing}
containerStyles={[styles.mb6]}
+ forwardedFSClass={forwardedFSClass}
/>
({
defaultValue={defaultValues.lastName}
shouldSaveDraft={!isEditing}
containerStyles={[styles.mb6]}
+ forwardedFSClass={forwardedFSClass}
/>
{shouldShowHelpLinks && (
<>
diff --git a/src/components/SubStepForms/RegistrationNumberStep.tsx b/src/components/SubStepForms/RegistrationNumberStep.tsx
index 97c339a82ceb..c5955b69682f 100644
--- a/src/components/SubStepForms/RegistrationNumberStep.tsx
+++ b/src/components/SubStepForms/RegistrationNumberStep.tsx
@@ -92,6 +92,7 @@ function RegistrationNumberStep({
shouldSaveDraft={!isEditing}
autoFocus={!shouldDelayAutoFocus}
ref={internalInputRef}
+ forwardedFSClass={CONST.FULLSTORY.CLASS.MASK}
/>
= SubStepProps & {
- /** The ID of the form */
- formID: TFormID;
+type SingleFieldStepProps = SubStepProps &
+ ForwardedFSClassProps & {
+ /** The ID of the form */
+ formID: TFormID;
- /** The title of the form */
- formTitle: string;
+ /** The title of the form */
+ formTitle: string;
- /** The disclaimer to show below the form title */
- formDisclaimer?: string;
+ /** The disclaimer to show below the form title */
+ formDisclaimer?: string;
- /** The validation function to call when the form is submitted */
- validate: (values: FormOnyxValues) => FormInputErrors;
+ /** The validation function to call when the form is submitted */
+ validate: (values: FormOnyxValues) => FormInputErrors;
- /** A function to call when the form is submitted */
- onSubmit: (values: FormOnyxValues) => void;
+ /** A function to call when the form is submitted */
+ onSubmit: (values: FormOnyxValues) => void;
- /** The ID of the form input */
- inputId: string;
+ /** The ID of the form input */
+ inputId: string;
- /** The label of the input */
- inputLabel: string;
+ /** The label of the input */
+ inputLabel: string;
- /** The mode of the input */
- inputMode?: InputModeOptions;
+ /** The mode of the input */
+ inputMode?: InputModeOptions;
- /** The default values for the form */
- defaultValue: string;
+ /** The default values for the form */
+ defaultValue: string;
- /** Whether to show help links */
- shouldShowHelpLinks?: boolean;
+ /** Whether to show help links */
+ shouldShowHelpLinks?: boolean;
- /** Max length of the field */
- maxLength?: number;
+ /** Max length of the field */
+ maxLength?: number;
- /** Should the submit button be enabled when offline */
- enabledWhenOffline?: boolean;
+ /** Should the submit button be enabled when offline */
+ enabledWhenOffline?: boolean;
- /** Set the default value to the input if there is a valid saved value */
- shouldUseDefaultValue?: boolean;
+ /** Set the default value to the input if there is a valid saved value */
+ shouldUseDefaultValue?: boolean;
- /** Should the input be disabled */
- disabled?: boolean;
+ /** Should the input be disabled */
+ disabled?: boolean;
- /** Placeholder displayed inside input */
- placeholder?: string;
+ /** Placeholder displayed inside input */
+ placeholder?: string;
- /** Whether to delay autoFocus to avoid conflicts with navigation animations */
- shouldDelayAutoFocus?: boolean;
+ /** Whether to delay autoFocus to avoid conflicts with navigation animations */
+ shouldDelayAutoFocus?: boolean;
- /** Whether to show the Patriot Act help link (EnablePayments-only) */
- shouldShowPatriotActLink?: boolean;
-};
+ /** Whether to show the Patriot Act help link (EnablePayments-only) */
+ shouldShowPatriotActLink?: boolean;
+ };
function SingleFieldStep({
formID,
@@ -88,6 +90,7 @@ function SingleFieldStep({
placeholder,
shouldDelayAutoFocus = false,
shouldShowPatriotActLink = false,
+ forwardedFSClass,
}: SingleFieldStepProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();
@@ -125,6 +128,7 @@ function SingleFieldStep({
placeholder={placeholder}
autoFocus={!shouldDelayAutoFocus}
ref={internalInputRef}
+ forwardedFSClass={forwardedFSClass}
/>
{shouldShowHelpLinks && (
diff --git a/src/components/ValuePicker/index.tsx b/src/components/ValuePicker/index.tsx
index 11423916f720..d298f4ca8a0e 100644
--- a/src/components/ValuePicker/index.tsx
+++ b/src/components/ValuePicker/index.tsx
@@ -7,7 +7,19 @@ import type {ValuePickerItem, ValuePickerProps} from './types';
import ValueSelectionList from './ValueSelectionList';
import ValueSelectorModal from './ValueSelectorModal';
-function ValuePicker({value, label, items, placeholder = '', errorText = '', onInputChange, furtherDetails, shouldShowTooltips = true, shouldShowModal = true, ref}: ValuePickerProps) {
+function ValuePicker({
+ value,
+ label,
+ items,
+ placeholder = '',
+ errorText = '',
+ onInputChange,
+ furtherDetails,
+ shouldShowTooltips = true,
+ shouldShowModal = true,
+ ref,
+ forwardedFSClass,
+}: ValuePickerProps) {
const [isPickerVisible, setIsPickerVisible] = useState(false);
const showPickerModal = () => {
@@ -41,6 +53,7 @@ function ValuePicker({value, label, items, placeholder = '', errorText = '', onI
furtherDetails={furtherDetails}
brickRoadIndicator={errorText ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined}
errorText={errorText}
+ forwardedFSClass={forwardedFSClass}
/>
;
-type ValuePickerProps = {
+type ValuePickerProps = ForwardedFSClassProps & {
/** Item to display */
value?: string;
diff --git a/src/libs/Fullstory/common.ts b/src/libs/Fullstory/common.ts
index ad3b7ad86fdd..461d49897ab2 100644
--- a/src/libs/Fullstory/common.ts
+++ b/src/libs/Fullstory/common.ts
@@ -1,17 +1,76 @@
-import {isConciergeChatReport, shouldUnmaskChat} from '@libs/ReportUtils';
+import {Str} from 'expensify-common';
+import type {OnyxCollection} from 'react-native-onyx';
+import Onyx from 'react-native-onyx';
+import type {ValueOf} from 'type-fest';
+import {chatIncludesConcierge} from '@libs/ReportUtils';
import CONST from '@src/CONST';
-import type {GetChatFSClass} from './types';
+import ONYXKEYS from '@src/ONYXKEYS';
+import type {Report} from '@src/types/onyx';
+import type {GetChatFSClass, ShouldInitialize} from './types';
-const getChatFSClass: GetChatFSClass = (context, report) => {
- if (isConciergeChatReport(report)) {
+// This data is only used for Fullstory to determine if a chat-related element should be
+// masked or not, so it's acceptable to use `Onyx.connectWithoutView` and avoid many UI elements
+// having to subscribe to this whole collection.
+let allReports: OnyxCollection;
+Onyx.connectWithoutView({
+ key: ONYXKEYS.COLLECTION.REPORT,
+ waitForCollectionCallback: true,
+ callback: (value) => {
+ allReports = value;
+ },
+});
+
+const allowedReportChatTypes = new Set>([
+ CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT,
+ CONST.REPORT.CHAT_TYPE.POLICY_ADMINS,
+ CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE,
+ CONST.REPORT.CHAT_TYPE.INVOICE,
+]);
+
+const allowedReportTypes = new Set>([CONST.REPORT.TYPE.IOU, CONST.REPORT.TYPE.EXPENSE, CONST.REPORT.TYPE.INVOICE]);
+
+const getChatFSClass: GetChatFSClass = (report) => {
+ if (!report) {
return CONST.FULLSTORY.CLASS.UNMASK;
}
- if (shouldUnmaskChat(context, report)) {
+ // Self DMs should be masked.
+ if (report.chatType === CONST.REPORT.CHAT_TYPE.SELF_DM) {
+ return CONST.FULLSTORY.CLASS.MASK;
+ }
+
+ // Workspace expense chat, #admins, #announce rooms and invoices should be unmasked.
+ if (report.chatType && allowedReportChatTypes.has(report.chatType)) {
return CONST.FULLSTORY.CLASS.UNMASK;
}
- return CONST.FULLSTORY.CLASS.MASK;
+ // IOUs, expenses and invoices should be unmasked.
+ if (report.type && allowedReportTypes.has(report.type as ValueOf)) {
+ return CONST.FULLSTORY.CLASS.UNMASK;
+ }
+
+ // If the report doesn't meet the condition above we check if the parent report is an IOU, expense or invoice.
+ const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`];
+ if (parentReport?.type && allowedReportTypes.has(parentReport.type as ValueOf)) {
+ return CONST.FULLSTORY.CLASS.UNMASK;
+ }
+
+ // DMs / Groups / Rooms should be unmasked only if Concierge is in the chat.
+ const participantAccountIDs = Object.keys(report.participants ?? {});
+ if (participantAccountIDs.length > 0) {
+ return chatIncludesConcierge(report) ? CONST.FULLSTORY.CLASS.UNMASK : CONST.FULLSTORY.CLASS.MASK;
+ }
+
+ return CONST.FULLSTORY.CLASS.UNMASK;
+};
+
+const shouldInitializeFullstory: ShouldInitialize = (userMetadata, envName) => {
+ const isTestEmail = userMetadata.email !== undefined && userMetadata.email.startsWith('fullstory') && userMetadata.email.endsWith(CONST.EMAIL.QA_DOMAIN);
+ if ((CONST.ENVIRONMENT.PRODUCTION !== envName && !isTestEmail) || Str.extractEmailDomain(userMetadata.email ?? '') === CONST.EXPENSIFY_PARTNER_NAME) {
+ return false;
+ }
+
+ return true;
};
-export default getChatFSClass;
+export {getChatFSClass, shouldInitializeFullstory};
diff --git a/src/libs/Fullstory/index.native.ts b/src/libs/Fullstory/index.native.ts
index a88d82056f32..04f99dedf1e3 100644
--- a/src/libs/Fullstory/index.native.ts
+++ b/src/libs/Fullstory/index.native.ts
@@ -1,8 +1,6 @@
import FullStory, {FSPage} from '@fullstory/react-native';
-import {Str} from 'expensify-common';
-import CONST from '@src/CONST';
import getEnvironment from '@src/libs/Environment/getEnvironment';
-import getChatFSClass from './common';
+import {getChatFSClass, shouldInitializeFullstory} from './common';
import type {Fullstory} from './types';
const FS: Fullstory = {
@@ -14,6 +12,8 @@ const FS: Fullstory = {
onReady: () => Promise.resolve(),
+ shouldInitialize: shouldInitializeFullstory,
+
consent: (shouldConsent) => FullStory.consent(shouldConsent),
identify: (userMetadata, envName) => {
@@ -34,10 +34,10 @@ const FS: Fullstory = {
// after the init function since this function is also called on updates for
// UserMetadata onyx key.
getEnvironment().then((envName: string) => {
- const isTestEmail = userMetadata.email !== undefined && userMetadata.email.startsWith('fullstory') && userMetadata.email.endsWith(CONST.EMAIL.QA_DOMAIN);
- if ((CONST.ENVIRONMENT.PRODUCTION !== envName && !isTestEmail) || Str.extractEmailDomain(userMetadata.email ?? '') === CONST.EXPENSIFY_PARTNER_NAME) {
+ if (!FS.shouldInitialize(userMetadata, envName)) {
return;
}
+
FullStory.restart();
FullStory.consent(true);
FS.identify(userMetadata, envName);
diff --git a/src/libs/Fullstory/index.ts b/src/libs/Fullstory/index.ts
index 188e31b7de9e..43273a942dbc 100644
--- a/src/libs/Fullstory/index.ts
+++ b/src/libs/Fullstory/index.ts
@@ -1,9 +1,8 @@
import {FullStory, init, isInitialized} from '@fullstory/browser';
-import {Str} from 'expensify-common';
import * as Session from '@userActions/Session';
import CONST from '@src/CONST';
import getEnvironment from '@src/libs/Environment/getEnvironment';
-import getChatFSClass from './common';
+import {getChatFSClass, shouldInitializeFullstory} from './common';
import type {FSPageLike, Fullstory} from './types';
// Placeholder Browser API does not support Manual Page definition
@@ -33,6 +32,8 @@ const FS: Fullstory = {
}
}),
+ shouldInitialize: (userMetadata, envName) => shouldInitializeFullstory(userMetadata, envName) && !Session.isSupportAuthToken(),
+
consent: (shouldConsent) => FullStory(CONST.FULLSTORY.OPERATION.SET_IDENTITY, {consent: shouldConsent}),
identify: (userMetadata) => {
@@ -53,14 +54,13 @@ const FS: Fullstory = {
if (!userMetadata?.accountID) {
return;
}
+
try {
+ // We only use FullStory in production environment. We need to check this here
+ // after the init function since this function is also called on updates for
+ // UserMetadata onyx key.
getEnvironment().then((envName: string) => {
- const isTestEmail = userMetadata.email !== undefined && userMetadata.email.startsWith('fullstory') && userMetadata.email.endsWith(CONST.EMAIL.QA_DOMAIN);
- if (
- (CONST.ENVIRONMENT.PRODUCTION !== envName && !isTestEmail) ||
- Str.extractEmailDomain(userMetadata.email ?? '') === CONST.EXPENSIFY_PARTNER_NAME ||
- Session.isSupportAuthToken()
- ) {
+ if (!FS.shouldInitialize(userMetadata, envName)) {
// 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.
if (isInitialized()) {
FullStory(CONST.FULLSTORY.OPERATION.SHUTDOWN);
diff --git a/src/libs/Fullstory/types.ts b/src/libs/Fullstory/types.ts
index e78490d48dce..536d92588d00 100644
--- a/src/libs/Fullstory/types.ts
+++ b/src/libs/Fullstory/types.ts
@@ -1,7 +1,7 @@
import type {OnyxEntry} from 'react-native-onyx';
import type {ValueOf} from 'type-fest';
import type CONST from '@src/CONST';
-import type {OnyxInputOrEntry, PersonalDetailsList, Report, UserMetadata} from '@src/types/onyx';
+import type {OnyxInputOrEntry, Report, UserMetadata} from '@src/types/onyx';
type FSClass = ValueOf;
@@ -24,7 +24,9 @@ interface FSPageLikeConstructor {
new (name: string, properties: PropertiesWithoutPageName): FSPageLike;
}
-type GetChatFSClass = (context: OnyxEntry, report: OnyxInputOrEntry) => FSClass;
+type GetChatFSClass = (report: OnyxInputOrEntry) => FSClass;
+
+type ShouldInitialize = (userMetadata: UserMetadata, envName: string) => boolean;
type Fullstory = {
/**
@@ -47,6 +49,11 @@ type Fullstory = {
*/
onReady: () => Promise;
+ /**
+ * Whether Fullstory should be initialized.
+ */
+ shouldInitialize: ShouldInitialize;
+
/**
* Sets the identity consent status using the Fullstory library.
*/
@@ -164,4 +171,4 @@ type MultipleFSClassProps =
*/
Partial>;
-export type {FSPageLike, FSPageLikeConstructor, Fullstory, GetChatFSClass, PropertiesWithoutPageName, ForwardedFSClassProps, MultipleFSClassProps};
+export type {FSPageLike, FSPageLikeConstructor, Fullstory, GetChatFSClass, PropertiesWithoutPageName, ForwardedFSClassProps, MultipleFSClassProps, ShouldInitialize};
diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts
index a45164caaf15..b7955e54ba15 100644
--- a/src/libs/ReportUtils.ts
+++ b/src/libs/ReportUtils.ts
@@ -12426,57 +12426,6 @@ function hasInvoiceReports() {
return reports.some((report) => isInvoiceReport(report));
}
-function shouldUnmaskChat(participantsContext: OnyxEntry, report: OnyxInputOrEntry): boolean {
- if (!report?.participants) {
- return true;
- }
-
- if (isThread(report) && report?.chatType && report?.chatType === CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT) {
- return true;
- }
-
- if (isThread(report) && report?.type === CONST.REPORT.TYPE.EXPENSE) {
- return true;
- }
-
- if (isAdminRoom(report)) {
- return true;
- }
-
- const participantAccountIDs = Object.keys(report.participants);
-
- if (participantAccountIDs.length > 2) {
- return false;
- }
-
- if (participantsContext) {
- let teamInChat = false;
- let userInChat = false;
-
- for (const participantAccountID of participantAccountIDs) {
- const id = Number(participantAccountID);
- const contextAccountData = participantsContext[id];
-
- if (contextAccountData) {
- const login = contextAccountData.login ?? '';
-
- if (login.endsWith(CONST.EMAIL.EXPENSIFY_EMAIL_DOMAIN) || login.endsWith(CONST.EMAIL.EXPENSIFY_TEAM_EMAIL_DOMAIN)) {
- teamInChat = true;
- } else {
- userInChat = true;
- }
- }
- }
-
- // exclude teamOnly chat
- if (teamInChat && userInChat) {
- return true;
- }
- }
-
- return false;
-}
-
function getReportMetadata(reportID: string | undefined) {
return reportID ? allReportMetadataKeyValue[reportID] : undefined;
}
@@ -13323,7 +13272,6 @@ export {
getAllReportErrors,
getAllReportActionsErrorsAndReportActionThatRequiresAttention,
hasInvoiceReports,
- shouldUnmaskChat,
shouldExcludeAncestorReportAction,
getReportMetadata,
buildOptimisticSelfDMReport,
diff --git a/src/pages/EnablePayments/AdditionalDetailsStep.tsx b/src/pages/EnablePayments/AdditionalDetailsStep.tsx
index 3792e570711e..af9e70111c68 100644
--- a/src/pages/EnablePayments/AdditionalDetailsStep.tsx
+++ b/src/pages/EnablePayments/AdditionalDetailsStep.tsx
@@ -177,6 +177,7 @@ function AdditionalDetailsStep({currentUserPersonalDetails}: AdditionalDetailsSt
role={CONST.ROLE.PRESENTATION}
defaultValue={extractFirstAndLastNameFromAvailableDetails(currentUserPersonalDetails).firstName}
shouldSaveDraft
+ forwardedFSClass={CONST.FULLSTORY.CLASS.MASK}
/>
diff --git a/src/pages/EnablePayments/EnablePayments.tsx b/src/pages/EnablePayments/EnablePayments.tsx
index 98b33d46765f..c530e46511ba 100644
--- a/src/pages/EnablePayments/EnablePayments.tsx
+++ b/src/pages/EnablePayments/EnablePayments.tsx
@@ -84,14 +84,7 @@ function EnablePaymentsPage() {
}
if (CurrentStep) {
- return (
-
- {CurrentStep}
-
- );
+ return {CurrentStep};
}
return null;
diff --git a/src/pages/EnablePayments/EnablePaymentsPage.tsx b/src/pages/EnablePayments/EnablePaymentsPage.tsx
index 249524e58e4e..adc7fdcdab84 100644
--- a/src/pages/EnablePayments/EnablePaymentsPage.tsx
+++ b/src/pages/EnablePayments/EnablePaymentsPage.tsx
@@ -53,7 +53,6 @@ function EnablePaymentsPage() {
shouldShowOfflineIndicator={userWallet?.currentStep !== CONST.WALLET.STEP.ONFIDO}
includeSafeAreaPaddingBottom
testID={EnablePaymentsPage.displayName}
- forwardedFSClass={CONST.FULLSTORY.CLASS.MASK}
>
{() => {
if (userWallet?.errorCode === CONST.WALLET.ERROR.KYC) {
diff --git a/src/pages/EnablePayments/IdologyQuestions.tsx b/src/pages/EnablePayments/IdologyQuestions.tsx
index 330fa3ec6aab..95e6d74010fd 100644
--- a/src/pages/EnablePayments/IdologyQuestions.tsx
+++ b/src/pages/EnablePayments/IdologyQuestions.tsx
@@ -131,6 +131,7 @@ function IdologyQuestions({questions, idNumber}: IdologyQuestionsProps) {
chooseAnswer(SafeString(value));
}}
onInputChange={() => {}}
+ forwardedFSClass={CONST.FULLSTORY.CLASS.MASK}
/>
);
}
diff --git a/src/pages/EnablePayments/PersonalInfo/substeps/ConfirmationStep.tsx b/src/pages/EnablePayments/PersonalInfo/substeps/ConfirmationStep.tsx
index 64a7b4ff4a3d..952ac0b0798d 100644
--- a/src/pages/EnablePayments/PersonalInfo/substeps/ConfirmationStep.tsx
+++ b/src/pages/EnablePayments/PersonalInfo/substeps/ConfirmationStep.tsx
@@ -77,6 +77,7 @@ function ConfirmationStep({onNext, onMove, isEditing}: SubStepProps) {
onfidoLinksTitle={`${translate('personalInfoStep.byAddingThisBankAccount')} `}
isLoading={isLoading}
error={error}
+ forwardedFSClass={CONST.FULLSTORY.CLASS.MASK}
/>
);
}
diff --git a/src/pages/EnablePayments/PersonalInfo/substeps/DateOfBirthStep.tsx b/src/pages/EnablePayments/PersonalInfo/substeps/DateOfBirthStep.tsx
index 4400a96e86fa..d26a9f02ef2f 100644
--- a/src/pages/EnablePayments/PersonalInfo/substeps/DateOfBirthStep.tsx
+++ b/src/pages/EnablePayments/PersonalInfo/substeps/DateOfBirthStep.tsx
@@ -5,6 +5,7 @@ import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
import type {SubStepProps} from '@hooks/useSubStep/types';
import useWalletAdditionalDetailsStepFormSubmit from '@hooks/useWalletAdditionalDetailsStepFormSubmit';
+import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {OnyxFormValuesMapping} from '@src/ONYXKEYS';
import INPUT_IDS from '@src/types/form/WalletAdditionalDetailsForm';
@@ -36,6 +37,7 @@ function DateOfBirthStep({onNext, onMove, isEditing}: SubStepProps) {
dobInputID={PERSONAL_INFO_DOB_KEY as keyof FormOnyxValues}
dobDefaultValue={dobDefaultValue}
shouldShowPatriotActLink
+ forwardedFSClass={CONST.FULLSTORY.CLASS.MASK}
/>
);
}
diff --git a/src/pages/EnablePayments/PersonalInfo/substeps/LegalNameStep.tsx b/src/pages/EnablePayments/PersonalInfo/substeps/LegalNameStep.tsx
index 72ed9a2111fe..691b7bb9efd7 100644
--- a/src/pages/EnablePayments/PersonalInfo/substeps/LegalNameStep.tsx
+++ b/src/pages/EnablePayments/PersonalInfo/substeps/LegalNameStep.tsx
@@ -4,6 +4,7 @@ import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
import type {SubStepProps} from '@hooks/useSubStep/types';
import useWalletAdditionalDetailsStepFormSubmit from '@hooks/useWalletAdditionalDetailsStepFormSubmit';
+import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import INPUT_IDS from '@src/types/form/WalletAdditionalDetailsForm';
@@ -41,6 +42,7 @@ function LegalNameStep({onNext, onMove, isEditing}: SubStepProps) {
lastNameInputID={PERSONAL_INFO_STEP_KEY.LAST_NAME}
defaultValues={defaultValues}
shouldShowPatriotActLink
+ forwardedFSClass={CONST.FULLSTORY.CLASS.MASK}
/>
);
}
diff --git a/src/pages/EnablePayments/PersonalInfo/substeps/PhoneNumberStep.tsx b/src/pages/EnablePayments/PersonalInfo/substeps/PhoneNumberStep.tsx
index 0ae116d14384..2d88ec98821e 100644
--- a/src/pages/EnablePayments/PersonalInfo/substeps/PhoneNumberStep.tsx
+++ b/src/pages/EnablePayments/PersonalInfo/substeps/PhoneNumberStep.tsx
@@ -63,6 +63,7 @@ function PhoneNumberStep({onNext, onMove, isEditing}: SubStepProps) {
defaultValue={defaultPhoneNumber}
enabledWhenOffline
shouldShowPatriotActLink
+ forwardedFSClass={CONST.FULLSTORY.CLASS.MASK}
/>
);
}
diff --git a/src/pages/EnablePayments/PersonalInfo/substeps/SocialSecurityNumberStep.tsx b/src/pages/EnablePayments/PersonalInfo/substeps/SocialSecurityNumberStep.tsx
index 3392fc000a98..ee0720db1d1a 100644
--- a/src/pages/EnablePayments/PersonalInfo/substeps/SocialSecurityNumberStep.tsx
+++ b/src/pages/EnablePayments/PersonalInfo/substeps/SocialSecurityNumberStep.tsx
@@ -59,6 +59,7 @@ function SocialSecurityNumberStep({onNext, onMove, isEditing}: SubStepProps) {
defaultValue={defaultSsnLast4}
maxLength={shouldAskForFullSSN ? CONST.BANK_ACCOUNT.MAX_LENGTH.FULL_SSN : CONST.BANK_ACCOUNT.MAX_LENGTH.SSN}
shouldShowPatriotActLink
+ forwardedFSClass={CONST.FULLSTORY.CLASS.MASK}
/>
);
}
diff --git a/src/pages/ReimbursementAccount/AddressFormFields.tsx b/src/pages/ReimbursementAccount/AddressFormFields.tsx
index 254ecc1cda7f..1d608d8ad70e 100644
--- a/src/pages/ReimbursementAccount/AddressFormFields.tsx
+++ b/src/pages/ReimbursementAccount/AddressFormFields.tsx
@@ -8,6 +8,7 @@ import PushRowWithModal from '@components/PushRowWithModal';
import TextInput from '@components/TextInput';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
+import type {ForwardedFSClassProps} from '@libs/Fullstory/types';
import CONST from '@src/CONST';
import type {TranslationPaths} from '@src/languages/types';
import type {Address} from '@src/types/onyx/PrivatePersonalDetails';
@@ -31,7 +32,7 @@ type AddressInputKeys = {
type AddressErrors = Record;
-type AddressFormProps = {
+type AddressFormProps = ForwardedFSClassProps & {
/** Translate key for Street name */
streetTranslationKey: TranslationPaths;
@@ -110,6 +111,7 @@ function AddressFormFields({
onCountryChange,
shouldAllowCountryChange = true,
shouldValidateZipCodeFormat = true,
+ forwardedFSClass,
}: AddressFormProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();
@@ -141,6 +143,7 @@ function AddressFormFields({
maxInputLength={CONST.FORM_CHARACTER_LIMIT}
limitSearchesToCountry={shouldAllowCountryChange ? undefined : defaultValues?.country}
onCountryChange={handleCountryChange}
+ forwardedFSClass={forwardedFSClass}
/>
{shouldDisplayStateSelector && (
@@ -169,6 +173,7 @@ function AddressFormFields({
defaultValue={defaultValues?.state}
inputID={inputKeys.state ?? 'stateInput'}
errorText={errors?.state ? translate('bankAccount.error.addressState') : ''}
+ forwardedFSClass={forwardedFSClass}
/>
)}
@@ -185,6 +190,7 @@ function AddressFormFields({
errorText={errors?.zipCode ? translate('bankAccount.error.zipCode') : ''}
hint={translate('common.zipCodeExampleFormat', {zipSampleFormat: CONST.COUNTRY_ZIP_REGEX_DATA.US.samples})}
containerStyles={styles.mt3}
+ forwardedFSClass={forwardedFSClass}
/>
{shouldDisplayCountrySelector && (
@@ -201,6 +207,7 @@ function AddressFormFields({
onValueChange={handleCountryChange}
stateInputIDToReset={inputKeys.state ?? 'stateInput'}
shouldAllowChange={shouldAllowCountryChange}
+ forwardedFSClass={forwardedFSClass}
/>
)}
diff --git a/src/pages/ReimbursementAccount/ConnectedVerifiedBankAccount.tsx b/src/pages/ReimbursementAccount/ConnectedVerifiedBankAccount.tsx
index 0a1233d53bbf..1bc38ca3e2cf 100644
--- a/src/pages/ReimbursementAccount/ConnectedVerifiedBankAccount.tsx
+++ b/src/pages/ReimbursementAccount/ConnectedVerifiedBankAccount.tsx
@@ -17,7 +17,6 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useThemeStyles from '@hooks/useThemeStyles';
import WorkspaceResetBankAccountModal from '@pages/workspace/WorkspaceResetBankAccountModal';
import {requestResetBankAccount, resetReimbursementAccount} from '@userActions/ReimbursementAccount';
-import CONST from '@src/CONST';
import type {ReimbursementAccount} from '@src/types/onyx';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
@@ -71,7 +70,6 @@ function ConnectedVerifiedBankAccount({
shouldEnablePickerAvoiding={false}
shouldEnableMaxHeight
style={[styles.flex1, styles.justifyContentBetween, styles.mh2]}
- forwardedFSClass={CONST.FULLSTORY.CLASS.MASK}
>
);
diff --git a/src/pages/ReimbursementAccount/NonUSD/BankInfo/subSteps/BankAccountDetails.tsx b/src/pages/ReimbursementAccount/NonUSD/BankInfo/subSteps/BankAccountDetails.tsx
index 23eae2db2686..cb81b1ab9dd3 100644
--- a/src/pages/ReimbursementAccount/NonUSD/BankInfo/subSteps/BankAccountDetails.tsx
+++ b/src/pages/ReimbursementAccount/NonUSD/BankInfo/subSteps/BankAccountDetails.tsx
@@ -114,6 +114,7 @@ function BankAccountDetails({onNext, isEditing, corpayFields}: BankInfoSubStepPr
city: 'bankCity',
country: '',
}}
+ forwardedFSClass={CONST.FULLSTORY.CLASS.MASK}
/>
);
diff --git a/src/pages/ReimbursementAccount/NonUSD/BankInfo/subSteps/Confirmation.tsx b/src/pages/ReimbursementAccount/NonUSD/BankInfo/subSteps/Confirmation.tsx
index 9eba1ffc0ad9..c9369c902a82 100644
--- a/src/pages/ReimbursementAccount/NonUSD/BankInfo/subSteps/Confirmation.tsx
+++ b/src/pages/ReimbursementAccount/NonUSD/BankInfo/subSteps/Confirmation.tsx
@@ -47,6 +47,7 @@ function Confirmation({onNext, onMove, corpayFields}: BankInfoSubStepProps) {
}
}}
key={field.id}
+ forwardedFSClass={CONST.FULLSTORY.CLASS.MASK}
/>
);
}),
diff --git a/src/pages/ReimbursementAccount/NonUSD/NonUSDVerifiedBankAccountFlow.tsx b/src/pages/ReimbursementAccount/NonUSD/NonUSDVerifiedBankAccountFlow.tsx
index 3782514a1b07..a6cc82695cb6 100644
--- a/src/pages/ReimbursementAccount/NonUSD/NonUSDVerifiedBankAccountFlow.tsx
+++ b/src/pages/ReimbursementAccount/NonUSD/NonUSDVerifiedBankAccountFlow.tsx
@@ -178,14 +178,7 @@ function NonUSDVerifiedBankAccountFlow({
}
if (CurrentStep) {
- return (
-
- {CurrentStep}
-
- );
+ return {CurrentStep};
}
return null;
diff --git a/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/Address.tsx b/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/Address.tsx
index 3c96c7244545..1ed63f46e2ab 100644
--- a/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/Address.tsx
+++ b/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/Address.tsx
@@ -5,6 +5,7 @@ import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
import useReimbursementAccountStepFormSubmit from '@hooks/useReimbursementAccountStepFormSubmit';
import type {SubStepProps} from '@hooks/useSubStep/types';
+import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import INPUT_IDS from '@src/types/form/ReimbursementAccountForm';
import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';
@@ -57,6 +58,7 @@ function Address({onNext, onMove, isEditing}: SubStepProps) {
inputFieldsIDs={INPUT_KEYS}
defaultValues={defaultValues}
shouldAllowCountryChange={false}
+ forwardedFSClass={CONST.FULLSTORY.CLASS.MASK}
/>
);
}
diff --git a/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/Confirmation.tsx b/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/Confirmation.tsx
index 16ed4384ad3c..676649481200 100644
--- a/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/Confirmation.tsx
+++ b/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/Confirmation.tsx
@@ -68,6 +68,7 @@ function Confirmation({onNext, onMove, isEditing}: SubStepProps) {
onfidoLinksTitle={`${translate('personalInfoStep.byAddingThisBankAccount')} `}
isLoading={isLoading}
error={error}
+ forwardedFSClass={CONST.FULLSTORY.CLASS.MASK}
/>
);
}
diff --git a/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/DateOfBirth.tsx b/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/DateOfBirth.tsx
index a43ee49d960c..e4318d51885b 100644
--- a/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/DateOfBirth.tsx
+++ b/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/DateOfBirth.tsx
@@ -7,6 +7,7 @@ import useReimbursementAccountStepFormSubmit from '@hooks/useReimbursementAccoun
import type {SubStepProps} from '@hooks/useSubStep/types';
import useThemeStyles from '@hooks/useThemeStyles';
import HelpLinks from '@pages/ReimbursementAccount/USD/Requestor/PersonalInfo/HelpLinks';
+import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import INPUT_IDS from '@src/types/form/ReimbursementAccountForm';
import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';
@@ -46,6 +47,7 @@ function DateOfBirth({onNext, onMove, isEditing}: SubStepProps) {
dobInputID={PERSONAL_INFO_DOB_KEY}
dobDefaultValue={dobDefaultValue}
footerComponent={}
+ forwardedFSClass={CONST.FULLSTORY.CLASS.MASK}
/>
);
}
diff --git a/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/FullName.tsx b/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/FullName.tsx
index 4d34f7b393a3..2d11f94f7a60 100644
--- a/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/FullName.tsx
+++ b/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/FullName.tsx
@@ -4,6 +4,7 @@ import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
import useReimbursementAccountStepFormSubmit from '@hooks/useReimbursementAccountStepFormSubmit';
import type {SubStepProps} from '@hooks/useSubStep/types';
+import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import INPUT_IDS from '@src/types/form/ReimbursementAccountForm';
@@ -38,6 +39,7 @@ function FullName({onNext, onMove, isEditing}: SubStepProps) {
firstNameInputID={PERSONAL_INFO_STEP_KEY.FIRST_NAME}
lastNameInputID={PERSONAL_INFO_STEP_KEY.LAST_NAME}
defaultValues={defaultValues}
+ forwardedFSClass={CONST.FULLSTORY.CLASS.MASK}
/>
);
}
diff --git a/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/SocialSecurityNumber.tsx b/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/SocialSecurityNumber.tsx
index 17933aed18b9..6c49a1892d8f 100644
--- a/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/SocialSecurityNumber.tsx
+++ b/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/SocialSecurityNumber.tsx
@@ -62,6 +62,7 @@ function SocialSecurityNumber({onNext, onMove, isEditing}: SubStepProps) {
defaultValue={defaultSsnLast4}
maxLength={CONST.BANK_ACCOUNT.MAX_LENGTH.SSN}
enabledWhenOffline
+ forwardedFSClass={CONST.FULLSTORY.CLASS.MASK}
/>
);
}
diff --git a/src/pages/ReimbursementAccount/USD/USDVerifiedBankAccountFlow.tsx b/src/pages/ReimbursementAccount/USD/USDVerifiedBankAccountFlow.tsx
index f3ca3b9c65d9..b364bfa4a092 100644
--- a/src/pages/ReimbursementAccount/USD/USDVerifiedBankAccountFlow.tsx
+++ b/src/pages/ReimbursementAccount/USD/USDVerifiedBankAccountFlow.tsx
@@ -88,14 +88,7 @@ function USDVerifiedBankAccountFlow({
}
if (CurrentStep) {
- return (
-
- {CurrentStep}
-
- );
+ return {CurrentStep};
}
return null;
diff --git a/src/pages/home/HeaderView.tsx b/src/pages/home/HeaderView.tsx
index 663a86be8748..d75f4c6c06e8 100644
--- a/src/pages/home/HeaderView.tsx
+++ b/src/pages/home/HeaderView.tsx
@@ -35,7 +35,6 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useSubscriptionPlan from '@hooks/useSubscriptionPlan';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
-import FS from '@libs/Fullstory';
import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID';
import Navigation from '@libs/Navigation/Navigation';
import {getPersonalDetailsForAccountIDs} from '@libs/OptionsListUtils';
@@ -157,7 +156,6 @@ function HeaderView({report, parentReportAction, onNavigationMenuButtonClicked,
const hasTeam2025Pricing = useHasTeam2025Pricing();
const subscriptionPlan = useSubscriptionPlan();
const ancestors = useAncestors(report);
- const displayNamesFSClass = FS.getChatFSClass(personalDetails, report);
const shouldShowSubtitle = () => {
if (!subtitle) {
@@ -308,7 +306,6 @@ function HeaderView({report, parentReportAction, onNavigationMenuButtonClicked,
shouldUseFullTitle={isChatRoom || isPolicyExpenseChat || isChatThread || isTaskReport || shouldUseGroupTitle || isReportArchived}
renderAdditionalText={renderAdditionalText}
shouldAddEllipsis={shouldAddEllipsis}
- forwardedFSClass={displayNamesFSClass}
/>
{!isEmptyObject(parentNavigationSubtitleData) && (
diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx
index 86f27500cc9f..32071b07df60 100644
--- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx
+++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx
@@ -27,6 +27,7 @@ import {canSkipTriggerHotkeys, findCommonSuffixLength, insertText, insertWhiteSp
import convertToLTRForComposer from '@libs/convertToLTRForComposer';
import {containsOnlyEmojis, extractEmojis, getAddedEmojis, getPreferredSkinToneIndex, replaceAndExtractEmojis} from '@libs/EmojiUtils';
import focusComposerWithDelay from '@libs/focusComposerWithDelay';
+import type {ForwardedFSClassProps} from '@libs/Fullstory/types';
import getPlatform from '@libs/getPlatform';
import {addKeyDownPressListener, removeKeyDownPressListener} from '@libs/KeyboardShortcut/KeyDownPressListener';
import {detectAndRewritePaste} from '@libs/MarkdownLinkHelpers';
@@ -60,85 +61,86 @@ type SyncSelection = {
type NewlyAddedChars = {startIndex: number; endIndex: number; diff: string};
-type ComposerWithSuggestionsProps = Partial & {
- /** Report ID */
- reportID: string;
+type ComposerWithSuggestionsProps = Partial &
+ ForwardedFSClassProps & {
+ /** Report ID */
+ reportID: string;
- /** Callback to focus composer */
- onFocus: () => void;
+ /** Callback to focus composer */
+ onFocus: () => void;
- /** Callback to blur composer */
- onBlur: (event: BlurEvent) => void;
+ /** Callback to blur composer */
+ onBlur: (event: BlurEvent) => void;
- /** Callback when layout of composer changes */
- onLayout?: (event: LayoutChangeEvent) => void;
+ /** Callback when layout of composer changes */
+ onLayout?: (event: LayoutChangeEvent) => void;
- /** Callback to update the value of the composer */
- onValueChange: (value: string) => void;
+ /** Callback to update the value of the composer */
+ onValueChange: (value: string) => void;
- /** Callback when the composer got cleared on the UI thread */
- onCleared?: (text: string) => void;
+ /** Callback when the composer got cleared on the UI thread */
+ onCleared?: (text: string) => void;
- /** Whether the composer is full size */
- isComposerFullSize: boolean;
+ /** Whether the composer is full size */
+ isComposerFullSize: boolean;
- /** Function to set whether the full composer is available */
- setIsFullComposerAvailable: (isFullComposerAvailable: boolean) => void;
+ /** Function to set whether the full composer is available */
+ setIsFullComposerAvailable: (isFullComposerAvailable: boolean) => void;
- /** Whether the menu is visible */
- isMenuVisible: boolean;
+ /** Whether the menu is visible */
+ isMenuVisible: boolean;
- /** The placeholder for the input */
- inputPlaceholder: string;
+ /** The placeholder for the input */
+ inputPlaceholder: string;
- /** Callback when a file is pasted */
- onPasteFile: (file: FileObject | FileObject[]) => void;
+ /** Callback when a file is pasted */
+ onPasteFile: (file: FileObject | FileObject[]) => void;
- /** Whether the input is disabled, defaults to false */
- disabled?: boolean;
+ /** Whether the input is disabled, defaults to false */
+ disabled?: boolean;
- /** Function to set whether the comment is empty */
- setIsCommentEmpty: (isCommentEmpty: boolean) => void;
+ /** Function to set whether the comment is empty */
+ setIsCommentEmpty: (isCommentEmpty: boolean) => void;
- /** Function to handle sending a message */
- handleSendMessage: () => void;
+ /** Function to handle sending a message */
+ handleSendMessage: () => void;
- /** Whether the compose input should show */
- shouldShowComposeInput: OnyxEntry;
+ /** Whether the compose input should show */
+ shouldShowComposeInput: OnyxEntry;
- /** Function to measure the parent container */
- measureParentContainer: (callback: MeasureInWindowOnSuccessCallback) => void;
+ /** Function to measure the parent container */
+ measureParentContainer: (callback: MeasureInWindowOnSuccessCallback) => void;
- /** Whether the scroll is likely to trigger a layout */
- isScrollLikelyLayoutTriggered: RefObject;
+ /** Whether the scroll is likely to trigger a layout */
+ isScrollLikelyLayoutTriggered: RefObject;
- /** Function to raise the scroll is likely layout triggered */
- raiseIsScrollLikelyLayoutTriggered: () => void;
+ /** Function to raise the scroll is likely layout triggered */
+ raiseIsScrollLikelyLayoutTriggered: () => void;
- /** The ref to the suggestions */
- suggestionsRef: React.RefObject;
+ /** The ref to the suggestions */
+ suggestionsRef: React.RefObject;
- /** The ref to the next modal will open */
- isNextModalWillOpenRef: RefObject;
+ /** The ref to the next modal will open */
+ isNextModalWillOpenRef: RefObject;
- /** The last report action */
- lastReportAction?: OnyxEntry;
+ /** The last report action */
+ lastReportAction?: OnyxEntry;
- /** Whether to include chronos */
- includeChronos?: boolean;
+ /** Whether to include chronos */
+ includeChronos?: boolean;
- /** Whether report is from group policy */
- isGroupPolicyReport: boolean;
+ /** Whether report is from group policy */
+ isGroupPolicyReport: boolean;
- /** policy ID of the report */
- policyID?: string;
+ /** policy ID of the report */
+ policyID?: string;
- /** Whether the main composer was hidden */
- didHideComposerInput?: boolean;
+ /** Whether the main composer was hidden */
+ didHideComposerInput?: boolean;
- /** Reference to the outer element */
- ref?: ForwardedRef;
-};
+ /** Reference to the outer element */
+ ref?: ForwardedRef;
+ };
type SwitchToCurrentReportProps = {
preexistingReportID: string;
@@ -225,6 +227,9 @@ function ComposerWithSuggestions({
// For testing
children,
didHideComposerInput,
+
+ // Fullstory
+ forwardedFSClass,
}: ComposerWithSuggestionsProps) {
const {isKeyboardShown} = useKeyboardState();
const theme = useTheme();
@@ -866,6 +871,7 @@ function ComposerWithSuggestions({
onScroll={hideSuggestionMenu}
shouldContainScroll={isMobileSafari()}
isGroupPolicyReport={isGroupPolicyReport}
+ forwardedFSClass={forwardedFSClass}
/>
diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx
index 1ba94b09ebf2..ddef4f57581d 100644
--- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx
+++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx
@@ -35,6 +35,7 @@ import canFocusInputOnScreenFocus from '@libs/canFocusInputOnScreenFocus';
import ComposerFocusManager from '@libs/ComposerFocusManager';
import {canUseTouchScreen} from '@libs/DeviceCapabilities';
import DomUtils from '@libs/DomUtils';
+import FS from '@libs/Fullstory';
import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID';
import Performance from '@libs/Performance';
import {getLinkedTransactionID, getReportAction, isMoneyRequestAction} from '@libs/ReportActionsUtils';
@@ -498,6 +499,8 @@ function ReportActionCompose({
setIsAttachmentPreviewActive,
});
+ const fsClass = FS.getChatFSClass(report);
+
return (
@@ -576,6 +579,7 @@ function ReportActionCompose({
measureParentContainer={measureContainer}
onValueChange={onValueChange}
didHideComposerInput={didHideComposerInput}
+ forwardedFSClass={fsClass}
/>
{shouldDisplayDualDropZone && (
action.errors);
const prevLastIOUActionWithError = usePrevious(lastIOUActionWithError);
diff --git a/src/pages/settings/Security/TwoFactorAuth/CopyCodesPage.tsx b/src/pages/settings/Security/TwoFactorAuth/CopyCodesPage.tsx
index 2bd8818f3842..e482674f5827 100644
--- a/src/pages/settings/Security/TwoFactorAuth/CopyCodesPage.tsx
+++ b/src/pages/settings/Security/TwoFactorAuth/CopyCodesPage.tsx
@@ -89,7 +89,10 @@ function CopyCodesPage({route}: TwoFactorAuthPageProps) {
) : (
<>
-
+
{!!account?.recoveryCodes &&
account?.recoveryCodes?.split(', ').map((code) => (
{translate('twoFactorAuth.authenticatorApp')}.
-
+
{translate('twoFactorAuth.addKey')}
- {!!account?.twoFactorAuthSecretKey && {splitSecretInChunks(account?.twoFactorAuthSecretKey ?? '')}}
+ {!!account?.twoFactorAuthSecretKey && {splitSecretInChunks(account?.twoFactorAuthSecretKey ?? '')}}
))}
diff --git a/src/pages/settings/Wallet/InternationalDepositAccount/substeps/BankAccountDetails.tsx b/src/pages/settings/Wallet/InternationalDepositAccount/substeps/BankAccountDetails.tsx
index 5a2aa4a9230b..5e4e5ef4dc1d 100644
--- a/src/pages/settings/Wallet/InternationalDepositAccount/substeps/BankAccountDetails.tsx
+++ b/src/pages/settings/Wallet/InternationalDepositAccount/substeps/BankAccountDetails.tsx
@@ -92,6 +92,7 @@ function BankAccountDetails({isEditing, onNext, resetScreenIndex, formValues, fi
label={field.label + (field.isRequired ? '' : ` (${translate('common.optional')})`)}
items={(field.valueSet ?? []).map(({id, text}) => ({value: id, label: text}))}
shouldSaveDraft={!isEditing}
+ forwardedFSClass={CONST.FULLSTORY.CLASS.MASK}
/>
))}
diff --git a/src/pages/settings/Wallet/InternationalDepositAccount/substeps/BankInformation.tsx b/src/pages/settings/Wallet/InternationalDepositAccount/substeps/BankInformation.tsx
index 097d9cb199ca..097cf95a2269 100644
--- a/src/pages/settings/Wallet/InternationalDepositAccount/substeps/BankInformation.tsx
+++ b/src/pages/settings/Wallet/InternationalDepositAccount/substeps/BankInformation.tsx
@@ -106,6 +106,7 @@ function BankInformation({isEditing, onNext, formValues, fieldsMap}: CustomSubSt
lat: '',
lng: '',
}}
+ forwardedFSClass={CONST.FULLSTORY.CLASS.MASK}
/>
))}
diff --git a/src/pages/settings/Wallet/WalletPage/CardDetails.tsx b/src/pages/settings/Wallet/WalletPage/CardDetails.tsx
index 2c882ed2d19f..b4fb68179b53 100644
--- a/src/pages/settings/Wallet/WalletPage/CardDetails.tsx
+++ b/src/pages/settings/Wallet/WalletPage/CardDetails.tsx
@@ -42,7 +42,7 @@ function CardDetails({pan = '', expiration = '', cvv = '', onUpdateAddressPress}
const [privatePersonalDetails] = useOnyx(ONYXKEYS.PRIVATE_PERSONAL_DETAILS, {canBeMissing: true});
return (
-
+
{pan?.length > 0 && (
)}
{expiration?.length > 0 && (
@@ -58,6 +59,7 @@ function CardDetails({pan = '', expiration = '', cvv = '', onUpdateAddressPress}
title={expiration}
interactive={false}
copyable
+ forwardedFSClass={CONST.FULLSTORY.CLASS.MASK}
/>
)}
{cvv?.length > 0 && (
@@ -66,6 +68,7 @@ function CardDetails({pan = '', expiration = '', cvv = '', onUpdateAddressPress}
title={cvv}
interactive={false}
copyable
+ forwardedFSClass={CONST.FULLSTORY.CLASS.MASK}
/>
)}
{pan?.length > 0 && (
@@ -76,6 +79,7 @@ function CardDetails({pan = '', expiration = '', cvv = '', onUpdateAddressPress}
title={getFormattedAddress(privatePersonalDetails || defaultPrivatePersonalDetails)}
interactive={false}
copyable
+ forwardedFSClass={CONST.FULLSTORY.CLASS.MASK}
/>
-
- {CurrentStep}
-
+ {CurrentStep}
{!!feedProvider && !isStripeFeedProvider ? translate(`workspace.companyCards.addNewCard.feedDetails.${feedProvider}.title`) : ''}
- {renderInputs()}
+ {renderInputs()}
{!!feedProvider && !isStripeFeedProvider && (