Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Mobile-Expensify
4 changes: 2 additions & 2 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
versionCode 1009044405
versionName "9.4.44-5"
versionCode 1009044406
versionName "9.4.44-6"
// Supported language variants must be declared here to avoid from being removed during the compilation.
// This also helps us to not include unnecessary language variants in the APK.
resConfigs "en", "es"
Expand Down
2 changes: 1 addition & 1 deletion ios/NewExpensify/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>9.4.44.5</string>
<string>9.4.44.6</string>
<key>FullStory</key>
<dict>
<key>OrgId</key>
Expand Down
2 changes: 1 addition & 1 deletion ios/NotificationServiceExtension/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<key>CFBundleShortVersionString</key>
<string>9.4.44</string>
<key>CFBundleVersion</key>
<string>9.4.44.5</string>
<string>9.4.44.6</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
Expand Down
2 changes: 1 addition & 1 deletion ios/ShareViewController/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<key>CFBundleShortVersionString</key>
<string>9.4.44</string>
<key>CFBundleVersion</key>
<string>9.4.44.5</string>
<string>9.4.44.6</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
"version": "9.4.44-5",
"version": "9.4.44-6",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Cherry-pick the revert before bumping staging

At this bump to 9.4.44-6, the commit is supposed to cherry-pick #97003's deploy-blocker revert, but the diff against the parent only changes version metadata and the Mobile-Expensify gitlink. The App-side revert is still absent in this tree (MoneyRequestReportView still wraps expense reports with AgentZeroStatusProvider, AgentZeroStatusContext still enables hasServerDrivenStatus, and ReportActionsListTailIndicator.tsx still exists), so deploying this version to staging will not remove the Concierge thinking indicator that the CP is meant to fix.

Useful? React with 👍 / 👎.

"author": "Expensify, Inc.",
"homepage": "https://new.expensify.com",
"description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import FlatListWithScrollKey from '@components/FlatList/FlatListWithScrollKey';
import {AUTOSCROLL_TO_TOP_THRESHOLD} from '@components/FlatList/hooks/useFlatListScrollKey';
import ScrollView from '@components/ScrollView';

import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
Expand Down Expand Up @@ -55,7 +54,6 @@ import FloatingMessageCounter from '@pages/inbox/report/FloatingMessageCounter';
import getInitialNumToRender from '@pages/inbox/report/getInitialNumReportActionsToRender';
import ReportActionIndexContext from '@pages/inbox/report/ReportActionIndexContext';
import ReportActionsListItemRenderer from '@pages/inbox/report/ReportActionsListItemRenderer';
import ReportActionsListTailIndicator from '@pages/inbox/report/ReportActionsListTailIndicator';
import {getUnreadMarkerReportAction} from '@pages/inbox/report/shouldDisplayNewMarkerOnReportAction';
import useReportUnreadMessageScrollTracking from '@pages/inbox/report/useReportUnreadMessageScrollTracking';

Expand Down Expand Up @@ -128,7 +126,7 @@ function MoneyRequestReportActionsList({onLayout}: MoneyRequestReportListProps)

const {reportActions: unfilteredReportActions, hasNewerActions, hasOlderActions} = usePaginatedReportActions(reportID, route?.params?.reportActionID);
const reportActions = useMemo(() => getFilteredReportActionsForReportView(unfilteredReportActions), [unfilteredReportActions]);
const {draftReportAction, isDraftPendingCompletion} = useConciergeDraft();
const {draftReportAction} = useConciergeDraft();
const draftReportActionID = draftReportAction?.reportActionID;

const allReportTransactions = useReportTransactionsCollection(reportIDFromRoute);
Expand Down Expand Up @@ -214,7 +212,6 @@ function MoneyRequestReportActionsList({onLayout}: MoneyRequestReportListProps)

const scrollingVerticalBottomOffset = useRef(0);
const scrollingVerticalTopOffset = useRef(0);
const tailIndicatorHeightRef = useRef(0);
const wrapperViewRef = useRef<View>(null);
const readActionSkipped = useRef(false);
const lastVisibleActionCreated = getReportLastVisibleActionCreated(report, transactionThreadReport);
Expand Down Expand Up @@ -485,10 +482,6 @@ function MoneyRequestReportActionsList({onLayout}: MoneyRequestReportListProps)
resetKey: report?.reportID ?? reportIDFromRoute ?? '',
});

useEffect(() => {
tailIndicatorHeightRef.current = 0;
}, [report?.reportID]);

/**
* Subscribe to read/unread events and update our unreadMarkerTime
*/
Expand Down Expand Up @@ -652,24 +645,6 @@ function MoneyRequestReportActionsList({onLayout}: MoneyRequestReportListProps)
[reportScrollManager],
);

const handleTailIndicatorLayout = useCallback(
(event: LayoutChangeEvent) => {
const previousHeight = tailIndicatorHeightRef.current;
const nextHeight = event.nativeEvent.layout.height;
tailIndicatorHeightRef.current = nextHeight;

if (previousHeight > 0 || nextHeight <= 0 || linkedReportActionID || !hasNewestReportAction || scrollOffsetRef.current >= AUTOSCROLL_TO_TOP_THRESHOLD) {
return;
}

setIsFloatingMessageCounterVisible(false);
requestAnimationFrame(() => {
reportScrollManager.scrollToEnd();
});
},
[hasNewestReportAction, linkedReportActionID, reportScrollManager, scrollOffsetRef, setIsFloatingMessageCounterVisible],
);

/**
* Runs when the FlatList finishes laying out
*/
Expand Down Expand Up @@ -712,18 +687,6 @@ function MoneyRequestReportActionsList({onLayout}: MoneyRequestReportListProps)
return null;
}

const listFooterComponent = (
<View
testID="money-request-report-tail-indicator"
onLayout={handleTailIndicatorLayout}
>
<ReportActionsListTailIndicator
reportID={report.reportID}
isDraftPendingCompletion={isDraftPendingCompletion}
/>
</View>
);

const shouldUseMarkAsDoneCopy = shouldShowMarkAsDone({
policy,
report,
Expand Down Expand Up @@ -771,7 +734,7 @@ function MoneyRequestReportActionsList({onLayout}: MoneyRequestReportListProps)
style={styles.overscrollBehaviorContain}
data={visibleReportActions}
renderItem={renderItem}
extraData={[draftReportActionID, isDraftPendingCompletion]}
extraData={draftReportActionID}
onViewableItemsChanged={onViewableItemsChanged}
keyExtractor={keyExtractor}
onLayout={recordTimeToMeasureItemLayout}
Expand Down Expand Up @@ -802,10 +765,9 @@ function MoneyRequestReportActionsList({onLayout}: MoneyRequestReportListProps)
)}
</>
}
ListFooterComponent={listFooterComponent}
keyboardShouldPersistTaps="handled"
onScroll={trackVerticalScrolling}
contentContainerStyle={[shouldUseNarrowLayout ? styles.pt4 : styles.pt3, styles.pb4]}
contentContainerStyle={[shouldUseNarrowLayout ? styles.pt4 : styles.pt3]}
ref={listRef}
ListEmptyComponent={!isOffline && showReportActionsLoadingState ? <ReportActionsListLoadingSkeleton reasonAttributes={skeletonReasonAttributes} /> : undefined} // This skeleton component is only used for loading state, the empty state is handled by SearchMoneyRequestReportEmptyState
removeClippedSubviews={false}
Expand Down
44 changes: 19 additions & 25 deletions src/components/MoneyRequestReportView/MoneyRequestReportView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan

import Navigation from '@navigation/Navigation';

import {AgentZeroStatusProvider} from '@pages/inbox/AgentZeroStatusContext';
import {ConciergeDraftProvider} from '@pages/inbox/ConciergeDraftContext';
import ReportActionsList from '@pages/inbox/report/ReportActionsList';
import ReportFooter from '@pages/inbox/report/ReportFooter';
import UserTypingEventListener from '@pages/inbox/report/UserTypingEventListener';
Expand Down Expand Up @@ -278,29 +276,25 @@ function MoneyRequestReportView({report, reportLoadingState, shouldDisplayReport
</ScrollView>
</Animated.View>
)}
<AgentZeroStatusProvider reportID={report.reportID}>
<ConciergeDraftProvider reportID={report.reportID}>
<View style={[styles.overflowHidden, styles.justifyContentEnd, styles.flex1]}>
{shouldDisplayMoneyRequestActionsList ? (
<MoneyRequestReportActionsList onLayout={onLayout} />
) : (
<>
<ReportActionsList
reportID={report.reportID}
onLayout={onLayout}
/>
<UserTypingEventListener report={report} />
</>
)}
{shouldDisplayReportFooter ? (
<>
<ReportFooter />
<PortalHost name="suggestions" />
</>
) : null}
</View>
</ConciergeDraftProvider>
</AgentZeroStatusProvider>
<View style={[styles.overflowHidden, styles.justifyContentEnd, styles.flex1]}>
{shouldDisplayMoneyRequestActionsList ? (
<MoneyRequestReportActionsList onLayout={onLayout} />
) : (
<>
<ReportActionsList
reportID={report.reportID}
onLayout={onLayout}
/>
<UserTypingEventListener report={report} />
</>
)}
{shouldDisplayReportFooter ? (
<>
<ReportFooter />
<PortalHost name="suggestions" />
</>
) : null}
</View>
</View>
</OfflineWithFeedback>
</View>
Expand Down
9 changes: 3 additions & 6 deletions src/pages/inbox/AgentZeroStatusContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,8 @@ const AgentZeroStatusStateContext = createContext<AgentZeroStatusState>(defaultS
const AgentZeroStatusActionsContext = createContext<AgentZeroStatusActions>(defaultActions);

/**
* Cheap outer guard — subscribes to scalar report metadata plus this report's processing-indicator
* NVP. For non-AgentZero reports with no server-driven indicator (the common case), returns
* children directly.
* Cheap outer guard — only subscribes to the scalar CONCIERGE_REPORT_ID and the report's chat
* metadata. For non-AgentZero reports (the common case), returns children directly.
*
* AgentZero chats include Concierge DMs, policy #admins rooms, and custom-agent chats (any
* report with a participant whose personalDetails carries `isCustomAgent: true`, stamped
Expand All @@ -73,15 +72,13 @@ function AgentZeroStatusProvider({reportID, children}: React.PropsWithChildren<{
const [agentParticipantAccountID] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {selector: getCustomAgentParticipantAccountID(participantAccountIDs)});
const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID);
const [currentUserAccountID] = useOnyx(ONYXKEYS.SESSION, {selector: accountIDSelector});
const [serverAgentIDs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`, {selector: agentZeroProcessingAgentIDsSelector});

const isConciergeChat = reportID === conciergeReportID;
const isAdmin = chatType === CONST.REPORT.CHAT_TYPE.POLICY_ADMINS;
const isCustomAgentChat = agentParticipantAccountID !== undefined;
const hasServerDrivenStatus = (serverAgentIDs?.length ?? 0) > 0;
const otherParticipantCount = currentUserAccountID === undefined ? 0 : (participantAccountIDs ?? []).filter((accountID) => accountID !== currentUserAccountID).length;
const customAgentDMAccountID = isCustomAgentChat && isDMReport && otherParticipantCount === 1 ? agentParticipantAccountID : undefined;
const isAgentZeroChat = isConciergeChat || isAdmin || isCustomAgentChat || hasServerDrivenStatus;
const isAgentZeroChat = isConciergeChat || isAdmin || isCustomAgentChat;

if (!reportID || !isAgentZeroChat) {
return children;
Expand Down
4 changes: 2 additions & 2 deletions src/pages/inbox/report/ReportActionsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@ import React, {useEffect, useRef, useState} from 'react';
import FloatingMessageCounter from './FloatingMessageCounter';
import ReportActionIndexContext from './ReportActionIndexContext';
import {useReportActionsListActions, useReportActionsListState} from './ReportActionsListContext';
import ReportActionsListHeader from './ReportActionsListHeader';
import ReportActionsListItemRenderer from './ReportActionsListItemRenderer';
import ReportActionsListPaddingView from './ReportActionsListPaddingView';
import ReportActionsListTailIndicator from './ReportActionsListTailIndicator';
import ReportActionsSkeletonGuard from './ReportActionsSkeletonGuard';
import ShowPreviousMessagesButton from './ShowPreviousMessagesButton';
import useFollowActionBadgeTarget from './useFollowActionBadgeTarget';
Expand Down Expand Up @@ -396,7 +396,7 @@ function ReportActionsListContent({reportID, onLayout}: ReportActionsListProps)
const extraData = [shouldUseNarrowLayout ? unreadMarkerReportActionID : undefined, isArchivedNonExpenseReport(report, isReportArchived), draftReportActionID, draftMessageHTML];

const listHeaderComponent = (
<ReportActionsListTailIndicator
<ReportActionsListHeader
reportID={reportID}
isDraftPendingCompletion={isDraftPendingCompletion}
/>
Expand Down
21 changes: 21 additions & 0 deletions src/pages/inbox/report/ReportActionsListHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import ConciergeThinkingMessage from '@pages/home/report/ConciergeThinkingMessage';

import React from 'react';

type ReportActionsListHeaderProps = {
/** The ID of the report being displayed */
reportID: string;

/** Whether a Concierge draft is still streaming in — hides the thinking indicator only while the reply is actively revealing, not after it completes */
isDraftPendingCompletion?: boolean;
};

function ReportActionsListHeader({reportID, isDraftPendingCompletion}: ReportActionsListHeaderProps) {
if (isDraftPendingCompletion) {
return null;
}

return <ConciergeThinkingMessage reportID={reportID} />;
}

export default ReportActionsListHeader;
21 changes: 0 additions & 21 deletions src/pages/inbox/report/ReportActionsListTailIndicator.tsx

This file was deleted.

27 changes: 0 additions & 27 deletions tests/ui/MoneyRequestReportActionsListRejectModalTest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,6 @@ jest.mock('@components/MoneyRequestReportView/MoneyRequestReportTransactionList'
return () => <View testID="MockMoneyRequestReportTransactionList" />;
});

jest.mock('@pages/home/report/ConciergeThinkingMessage', () => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const {View} = require('react-native');
return {
__esModule: true,
default: () => <View testID="ConciergeThinkingMessage" />,
};
});

jest.mock('@components/HoldOrRejectEducationalModal', () => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const {View} = require('react-native');
Expand Down Expand Up @@ -240,24 +231,6 @@ describe('MoneyRequestReportActionsList - Reject Educational Modal', () => {
});
});

it('renders Concierge thinking indicator in the money request report table path', async () => {
await act(async () => {
await Onyx.multiSet({
[`${ONYXKEYS.COLLECTION.REPORT}${FAKE_REPORT_ID}` as const]: mockReport,
[`${ONYXKEYS.COLLECTION.POLICY}${FAKE_POLICY_ID}` as const]: mockPolicy,
[`${ONYXKEYS.COLLECTION.TRANSACTION}${FAKE_TRANSACTION_ID}` as const]: mockTransaction,
[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${FAKE_REPORT_ID}` as const]: {[mockReportAction.reportActionID]: mockReportAction},
[`${ONYXKEYS.COLLECTION.RAM_ONLY_REPORT_LOADING_STATE}${FAKE_REPORT_ID}` as const]: {isLoadingInitialReportActions: false, hasOnceLoadedReportActions: true},
[ONYXKEYS.SESSION]: {accountID: FAKE_ACCOUNT_ID, email: FAKE_EMAIL} as Session,
});
});

renderComponent();
await waitForBatchedUpdatesWithAct();

expect(screen.getByTestId('ConciergeThinkingMessage')).toBeTruthy();
});

it('should show reject educational modal when reject option is selected and explanation has NOT been dismissed', async () => {
await act(async () => {
await Onyx.multiSet({
Expand Down
Loading
Loading