Skip to content

Commit 6a3c808

Browse files
authored
Merge pull request #90915 from margelo/@chrispader/refactor/compositional-edit-only-report-action-compose
refactor: Split `ReportActionCompose` into more compositional components to allow for separate `EditOnlyReportActionCompose`
2 parents e09715e + b4bdb34 commit 6a3c808

21 files changed

Lines changed: 193 additions & 144 deletions

src/pages/inbox/ReportScreen.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import useDeferNonEssentials from './hooks/useDeferNonEssentials';
2828
import useFlushDeferredWriteOnFocus from './hooks/useFlushDeferredWriteOnFocus';
2929
import LinkedActionNotFoundGuard from './LinkedActionNotFoundGuard';
3030
import ReactionListWrapper from './ReactionListWrapper';
31-
import ReportActionComposePlaceholder from './report/ReportActionCompose/ReportActionComposePlaceholder';
31+
import ReportActionCompose from './report/ReportActionCompose/ReportActionCompose';
3232
import {ReportActionEditMessageContextProvider, ReportScreenEditMessageProviderWithTransactionThread} from './report/ReportActionEditMessageContext';
3333
import ReportFooter from './report/ReportFooter';
3434
import useClearReportActionDraftsOnReportChange from './report/useClearReportActionDraftsOnReportChange';
@@ -148,7 +148,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) {
148148
testID="report-actions-view-wrapper"
149149
>
150150
<ReportActionsList />
151-
{shouldDeferNonEssentials ? <ReportActionComposePlaceholder /> : <ReportFooter />}
151+
{shouldDeferNonEssentials ? <ReportActionCompose.Placeholder /> : <ReportFooter />}
152152
</View>
153153
</ConciergeDraftProvider>
154154
</AgentZeroStatusProvider>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import ComposerActionMenu from './ComposerActionMenu';
2+
import {useComposerEditState} from './ComposerContext';
3+
import ComposerEditingButtons from './ComposerEditingButtons';
4+
5+
function ComposerActionButton() {
6+
const {isEditingInComposer} = useComposerEditState();
7+
8+
if (isEditingInComposer) {
9+
return <ComposerEditingButtons />;
10+
}
11+
return <ComposerActionMenu />;
12+
}
13+
14+
export default ComposerActionButton;

src/pages/inbox/report/ReportActionCompose/ComposerActionMenu.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,8 @@ import AttachmentPickerWithMenuItems from './AttachmentPickerWithMenuItems';
1111
import {useComposerActions, useComposerEditState, useComposerMeta, useComposerSendState, useComposerState} from './ComposerContext';
1212
import useAttachmentPicker from './useAttachmentPicker';
1313

14-
type ComposerActionMenuProps = {
15-
reportID: string;
16-
};
17-
18-
function ComposerActionMenu({reportID}: ComposerActionMenuProps) {
14+
function ComposerActionMenu() {
15+
const {reportID} = useComposerState();
1916
const currentUserPersonalDetails = useCurrentUserPersonalDetails();
2017
const {isMenuVisible, isFullComposerAvailable} = useComposerState();
2118
const {draftComment} = useComposerEditState();

src/pages/inbox/report/ReportActionCompose/ComposerBox.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
import React from 'react';
2+
import type {PropsWithChildren} from 'react';
23
import {View} from 'react-native';
34
import useOnyx from '@hooks/useOnyx';
45
import useThemeStyles from '@hooks/useThemeStyles';
56
import ONYXKEYS from '@src/ONYXKEYS';
67
import {useComposerMeta, useComposerSendState, useComposerState} from './ComposerContext';
78

8-
type ComposerBoxProps = {
9-
reportID: string;
10-
children: React.ReactNode;
11-
};
12-
13-
function ComposerBox({reportID, children}: ComposerBoxProps) {
9+
function ComposerBox({children}: PropsWithChildren) {
10+
const {reportID} = useComposerState();
1411
const styles = useThemeStyles();
1512
const {isFocused} = useComposerState();
1613
const {isExceedingMaxLength, isBlockedFromConcierge} = useComposerSendState();
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import React from 'react';
2+
import type {PropsWithChildren} from 'react';
3+
import OfflineWithFeedback from '@components/OfflineWithFeedback';
4+
import useOnyx from '@hooks/useOnyx';
5+
import useThemeStyles from '@hooks/useThemeStyles';
6+
import {getReportOfflinePendingActionAndErrors} from '@libs/ReportUtils';
7+
import ONYXKEYS from '@src/ONYXKEYS';
8+
import {useComposerState} from './ComposerContext';
9+
10+
function ComposerContainer({children}: PropsWithChildren) {
11+
const {reportID} = useComposerState();
12+
const styles = useThemeStyles();
13+
const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`);
14+
const [isComposerFullSize = false] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_IS_COMPOSER_FULL_SIZE}${reportID}`);
15+
const {reportPendingAction: pendingAction} = getReportOfflinePendingActionAndErrors(report);
16+
17+
return (
18+
<OfflineWithFeedback
19+
shouldDisableOpacity
20+
pendingAction={pendingAction}
21+
style={isComposerFullSize ? styles.chatItemFullComposeRow : {}}
22+
contentContainerStyle={isComposerFullSize ? styles.flex1 : {}}
23+
>
24+
{children}
25+
</OfflineWithFeedback>
26+
);
27+
}
28+
29+
export default ComposerContainer;

src/pages/inbox/report/ReportActionCompose/ComposerContext.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ type ComposerText = string;
2626

2727
// Warm — changes on interaction
2828
type ComposerState = {
29+
reportID: string;
2930
isFocused: boolean;
3031
isMenuVisible: boolean;
3132
isFullComposerAvailable: boolean;
@@ -90,6 +91,7 @@ const noop = () => {};
9091
const ComposerTextContext = createContext<ComposerText>('');
9192

9293
const defaultState: ComposerState = {
94+
reportID: '',
9395
isFocused: false,
9496
isMenuVisible: false,
9597
isFullComposerAvailable: false,
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import React from 'react';
2+
import OfflineIndicator from '@components/OfflineIndicator';
3+
import useResponsiveLayout from '@hooks/useResponsiveLayout';
4+
import useThemeStyles from '@hooks/useThemeStyles';
5+
import ComposerExceededLength from './ComposerExceededLength';
6+
import ComposerFooter from './ComposerFooter';
7+
import ComposerTypingIndicator from './ComposerTypingIndicator';
8+
9+
function ComposerDefaultFooter() {
10+
const styles = useThemeStyles();
11+
const {shouldUseNarrowLayout} = useResponsiveLayout();
12+
13+
return (
14+
<ComposerFooter>
15+
{!shouldUseNarrowLayout && <OfflineIndicator containerStyles={[styles.chatItemComposeSecondaryRow]} />}
16+
<ComposerTypingIndicator />
17+
<ComposerExceededLength />
18+
</ComposerFooter>
19+
);
20+
}
21+
22+
export default ComposerDefaultFooter;

src/pages/inbox/report/ReportActionCompose/ComposerDropZone.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React from 'react';
2+
import type {PropsWithChildren} from 'react';
23
import DragAndDropConsumer from '@components/DragAndDrop/Consumer';
34
import DropZoneUI from '@components/DropZone/DropZoneUI';
45
import DualDropZone from '@components/DropZone/DualDropZone';
@@ -14,15 +15,11 @@ import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID';
1415
import {getParentReport, isChatRoom, isGroupChat, isInvoiceReport, isReportApproved, isSettled, temporary_getMoneyRequestOptions} from '@libs/ReportUtils';
1516
import {hasReceipt as hasReceiptTransactionUtils} from '@libs/TransactionUtils';
1617
import ONYXKEYS from '@src/ONYXKEYS';
18+
import {useComposerState} from './ComposerContext';
1719
import useAttachmentPicker from './useAttachmentPicker';
1820
import useReceiptDrop from './useReceiptDrop';
1921
import useShouldAddOrReplaceReceipt from './useShouldAddOrReplaceReceipt';
2022

21-
type ComposerDropZoneProps = {
22-
reportID: string;
23-
children: React.ReactNode;
24-
};
25-
2623
type RichDropZoneProps = {
2724
reportID: string;
2825
shouldAddOrReplaceReceipt: boolean;
@@ -110,7 +107,8 @@ function RichDropZone({reportID, shouldAddOrReplaceReceipt, transactionID, onAtt
110107
);
111108
}
112109

113-
function ComposerDropZone({reportID, children}: ComposerDropZoneProps) {
110+
function ComposerDropZone({children}: PropsWithChildren) {
111+
const {reportID} = useComposerState();
114112
const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`);
115113
const {shouldAddOrReplaceReceipt, transactionID} = useShouldAddOrReplaceReceipt(reportID);
116114
const {pickAttachments, PDFValidationComponent: AttachmentPDFValidation, ErrorModal: AttachmentErrorModal} = useAttachmentPicker(reportID);

src/pages/inbox/report/ReportActionCompose/ComposerEditingButtons.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,12 @@ import React from 'react';
22
import {View} from 'react-native';
33
import useThemeStyles from '@hooks/useThemeStyles';
44
import CONST from '@src/CONST';
5-
import {useComposerEditActions} from './ComposerContext';
5+
import {useComposerEditActions, useComposerState} from './ComposerContext';
66
import ComposerExpandCollapseButton from './ComposerExpandCollapseButton';
77
import MessageEditCancelButton from './MessageEditCancelButton';
88

9-
type ComposerEditingButtonsProps = {
10-
/** The report ID */
11-
reportID: string;
12-
};
13-
14-
function ComposerEditingButtons({reportID}: ComposerEditingButtonsProps) {
9+
function ComposerEditingButtons() {
10+
const {reportID} = useComposerState();
1511
const styles = useThemeStyles();
1612

1713
const {deleteDraft} = useComposerEditActions();

src/pages/inbox/report/ReportActionCompose/ComposerEmojiPicker.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,10 @@ import {canUseTouchScreen} from '@libs/DeviceCapabilities';
66
import DomUtils from '@libs/DomUtils';
77
import {hideEmojiPicker, isActive as isActiveEmojiPickerAction} from '@userActions/EmojiPickerAction';
88
import CONST from '@src/CONST';
9-
import {useComposerMeta, useComposerSendState} from './ComposerContext';
9+
import {useComposerMeta, useComposerSendState, useComposerState} from './ComposerContext';
1010

11-
type ComposerEmojiPickerProps = {
12-
reportID: string;
13-
};
14-
15-
function ComposerEmojiPicker({reportID}: ComposerEmojiPickerProps) {
11+
function ComposerEmojiPicker() {
12+
const {reportID} = useComposerState();
1613
const styles = useThemeStyles();
1714

1815
const {isMediumScreenWidth} = useResponsiveLayout();

0 commit comments

Comments
 (0)