-
Notifications
You must be signed in to change notification settings - Fork 2.9k
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
Ex4 chat masking #51790
base: main
Are you sure you want to change the base?
Ex4 chat masking #51790
Changes from all commits
34fc112
7d20ce8
cd31436
74f0354
41f0c6a
d8282a6
08b15e5
0894527
f6e387f
46d994d
85e7858
9b2a46f
74abee5
01cf070
ad347d2
d8fc78f
f905148
2cfb212
58ee1b9
c955f0d
ffe1c8a
b8618bc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1682,6 +1682,7 @@ const CONST = { | |
STUDENT_AMBASSADOR: '[email protected]', | ||
SVFG: '[email protected]', | ||
EXPENSIFY_EMAIL_DOMAIN: '@expensify.com', | ||
EXPENSIFY_TEAM_EMAIL_DOMAIN: '@team.expensify.com', | ||
}, | ||
|
||
CONCIERGE_DISPLAY_NAME: 'Concierge', | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,18 @@ | ||
import {FullStory, init, isInitialized} from '@fullstory/browser'; | ||
import type {OnyxEntry} from 'react-native-onyx'; | ||
import {isConciergeChatReport, shouldUnmaskChat} from '@libs/ReportUtils'; | ||
import CONST from '@src/CONST'; | ||
import * as Environment from '@src/libs/Environment/Environment'; | ||
import type {UserMetadata} from '@src/types/onyx'; | ||
import type {OnyxInputOrEntry, PersonalDetailsList, Report, UserMetadata} from '@src/types/onyx'; | ||
import type NavigationProperties from './types'; | ||
|
||
const WEB_PROP_ATTR = 'data-testid'; | ||
const MASK = 'fs-mask'; | ||
const UNMASK = 'fs-unmask'; | ||
const CUSTOMER = 'customer'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see these values repeated again from native and web files, this breaks the checklist checkbox which wants our code to be DRY, can you define these values in CONST.ts and use here and in native file ? |
||
const CONCIERGE = 'concierge'; | ||
const OTHER = 'other'; | ||
|
||
// Placeholder Browser API does not support Manual Page definition | ||
class FSPage { | ||
private pageName; | ||
|
@@ -16,7 +24,9 @@ class FSPage { | |
this.properties = properties; | ||
} | ||
|
||
start() {} | ||
start() { | ||
parseFSAttributes(); | ||
} | ||
} | ||
|
||
/** | ||
|
@@ -92,5 +102,73 @@ const FS = { | |
init: (_value: OnyxEntry<UserMetadata>) => {}, | ||
}; | ||
|
||
/** | ||
* Extract values from non-scraped at build time attribute WEB_PROP_ATTR, | ||
* reevaluate "fs-class". | ||
*/ | ||
function parseFSAttributes(): void { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I also suggest to add test on this function, which seems complex and any future update should be cover by tests |
||
window?.document?.querySelectorAll(`[${WEB_PROP_ATTR}]`).forEach((o) => { | ||
const attr = o.getAttribute(WEB_PROP_ATTR) ?? ''; | ||
if (!/fs-/gim.test(attr)) { | ||
return; | ||
} | ||
|
||
const fsAttrs = attr.match(/fs-[a-zA-Z0-9_-]+/g) ?? []; | ||
o.setAttribute('fs-class', fsAttrs.join(',')); | ||
|
||
let cleanedAttrs = attr; | ||
fsAttrs.forEach((fsAttr) => { | ||
cleanedAttrs = cleanedAttrs.replace(fsAttr, ''); | ||
}); | ||
|
||
cleanedAttrs = cleanedAttrs | ||
.replace(/,+/g, ',') | ||
.replace(/\s*,\s*/g, ',') | ||
.replace(/^,+|,+$/g, '') | ||
.replace(/\s+/g, ' ') | ||
.trim(); | ||
|
||
if (cleanedAttrs) { | ||
o.setAttribute(WEB_PROP_ATTR, cleanedAttrs); | ||
} else { | ||
o.removeAttribute(WEB_PROP_ATTR); | ||
} | ||
}); | ||
} | ||
|
||
/* | ||
prefix? if component name should be used as a prefix, | ||
in case data-test-id attribute usage, | ||
clean component name should be preserved in data-test-id. | ||
*/ | ||
function getFSAttributes(name: string, mask: boolean, prefix: boolean): string { | ||
if (!name) { | ||
return `${mask ? MASK : UNMASK}`; | ||
} | ||
|
||
if (prefix) { | ||
return `${name},${mask ? MASK : UNMASK}`; | ||
} | ||
|
||
return `${name}`; | ||
} | ||
|
||
function getChatFSAttributes(context: OnyxEntry<PersonalDetailsList>, name: string, report: OnyxInputOrEntry<Report>): string[] { | ||
if (!name) { | ||
return ['', '']; | ||
} | ||
if (isConciergeChatReport(report)) { | ||
const formattedName = `${CONCIERGE}-${name}`; | ||
return [`${formattedName},${UNMASK}`, `${formattedName}`]; | ||
} | ||
if (shouldUnmaskChat(context, report)) { | ||
const formattedName = `${CUSTOMER}-${name}`; | ||
return [`${formattedName},${UNMASK}`, `${formattedName}`]; | ||
} | ||
|
||
const formattedName = `${OTHER}-${name}`; | ||
return [`${formattedName},${MASK}`, `${formattedName}`]; | ||
Comment on lines
+162
to
+170
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. there is no need to interpolate There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please address this comment too |
||
} | ||
|
||
export default FS; | ||
export {FSPage}; | ||
export {FSPage, parseFSAttributes, getFSAttributes, getChatFSAttributes}; |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -8478,6 +8478,51 @@ function hasInvoiceReports() { | |||||
return allReports.some((report) => isInvoiceReport(report)); | ||||||
} | ||||||
|
||||||
function shouldUnmaskChat(participantsContext: OnyxEntry<PersonalDetailsList>, report: OnyxInputOrEntry<Report>): boolean { | ||||||
if (!report?.participants) { | ||||||
return true; | ||||||
} | ||||||
|
||||||
if (isThread(report) && report.chatType && report.chatType === CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
I worry that the app will crash if we don't have any of those, won't it ? do you think that this is unnecessary ? |
||||||
return true; | ||||||
} | ||||||
|
||||||
if (isThread(report) && report.type === CONST.REPORT.TYPE.EXPENSE) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same as ^ |
||||||
return true; | ||||||
} | ||||||
|
||||||
const participantAccountIDs = new Set(Object.keys(report.participants)); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same as ^ |
||||||
if (participantAccountIDs.size > 2) { | ||||||
return false; | ||||||
} | ||||||
Comment on lines
+8494
to
+8497
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we don't need to create a Set
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please address this comment too |
||||||
|
||||||
if (participantsContext) { | ||||||
// by email participants | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not sure if i understand this comment, can you be more specific here ? |
||||||
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) { | ||||||
return allReportMetadata?.[`${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`]; | ||||||
} | ||||||
|
@@ -8675,6 +8720,7 @@ export { | |||||
isClosedExpenseReportWithNoExpenses, | ||||||
isCompletedTaskReport, | ||||||
isConciergeChatReport, | ||||||
shouldUnmaskChat, | ||||||
isControlPolicyExpenseChat, | ||||||
isControlPolicyExpenseReport, | ||||||
isCurrentUserSubmitter, | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,7 @@ import type {ListRenderItemInfo} from '@react-native/virtualized-lists/Lists/Vir | |
import {useIsFocused, useRoute} from '@react-navigation/native'; | ||
// eslint-disable-next-line lodash/import-scope | ||
import type {DebouncedFunc} from 'lodash'; | ||
import React, {memo, useCallback, useEffect, useMemo, useRef, useState} from 'react'; | ||
import React, {memo, useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react'; | ||
import {DeviceEventEmitter, InteractionManager, View} from 'react-native'; | ||
import type {LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent, StyleProp, ViewStyle} from 'react-native'; | ||
import {useOnyx} from 'react-native-onyx'; | ||
|
@@ -19,6 +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 isSearchTopmostCentralPane from '@libs/Navigation/isSearchTopmostCentralPane'; | ||
import Navigation from '@libs/Navigation/Navigation'; | ||
import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; | ||
|
@@ -29,6 +30,7 @@ import Visibility from '@libs/Visibility'; | |
import type {AuthScreensParamList} from '@navigation/types'; | ||
import variables from '@styles/variables'; | ||
import * as Report from '@userActions/Report'; | ||
import {PersonalDetailsContext} from '@src/components/OnyxProvider'; | ||
import CONST from '@src/CONST'; | ||
import ONYXKEYS from '@src/ONYXKEYS'; | ||
import ROUTES from '@src/ROUTES'; | ||
|
@@ -171,6 +173,7 @@ function ReportActionsList({ | |
|
||
const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID ?? -1}`); | ||
const [accountID] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.accountID}); | ||
const participantsContext = useContext(PersonalDetailsContext); | ||
|
||
useEffect(() => { | ||
const unsubscriber = Visibility.onVisibilityChange(() => { | ||
|
@@ -719,13 +722,19 @@ function ReportActionsList({ | |
// When performing comment linking, initially 25 items are added to the list. Subsequent fetches add 15 items from the cache or 50 items from the server. | ||
// This is to ensure that the user is able to see the 'scroll to newer comments' button when they do comment linking and have not reached the end of the list yet. | ||
const canScrollToNewerComments = !isLoadingInitialReportActions && !hasNewestReportAction && sortedReportActions.length > 25 && !isLastPendingActionIsDelete; | ||
const [reportActionsListTestID, reportActionsListFSClass] = getChatFSAttributes(participantsContext, 'ReportActionsList', report); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. was comment from @gedu addressed here ? they mentioned that |
||
|
||
Comment on lines
+725
to
+726
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please address this comment too |
||
return ( | ||
<> | ||
<FloatingMessageCounter | ||
isActive={(isFloatingMessageCounterVisible && !!unreadMarkerReportActionID) || canScrollToNewerComments} | ||
onClick={scrollToBottomAndMarkReportAsRead} | ||
/> | ||
<View style={[styles.flex1, !shouldShowReportRecipientLocalTime && !hideComposer ? styles.pb4 : {}]}> | ||
<View | ||
style={[styles.flex1, !shouldShowReportRecipientLocalTime && !hideComposer ? styles.pb4 : {}]} | ||
testID={reportActionsListTestID} | ||
fsClass={reportActionsListFSClass} | ||
> | ||
<InvertedFlatList | ||
accessibilityLabel={translate('sidebarScreen.listOfChatMessages')} | ||
ref={reportScrollManager.ref} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay considering this placeholder, I will overlook it, but what is the use of this ?