From 2dd1ec3b70230e6d1ca62969d431e97f96159c7c Mon Sep 17 00:00:00 2001 From: Vit Horacek Date: Sat, 30 May 2026 09:48:20 +0200 Subject: [PATCH 1/3] Revert "Merge pull request #91750 from Expensify/cmartins-fixCrashOnMain" This reverts commit b6572e3d74189ef75ec5759257ef8afd9c812fd2, reversing changes made to 9573a6cb7dbb0475ddd2e077baf264131f6c9ce1. --- .../inbox/report/ReportActionCompose/ComposerActionButton.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerActionButton.tsx b/src/pages/inbox/report/ReportActionCompose/ComposerActionButton.tsx index e9147bd9afd7..c0f694363919 100644 --- a/src/pages/inbox/report/ReportActionCompose/ComposerActionButton.tsx +++ b/src/pages/inbox/report/ReportActionCompose/ComposerActionButton.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import ComposerActionMenu from './ComposerActionMenu'; import {useComposerEditState} from './ComposerContext'; import ComposerEditingButtons from './ComposerEditingButtons'; From 5b499cf983721d05c980a22fee7f84356ed050aa Mon Sep 17 00:00:00 2001 From: Vit Horacek Date: Sat, 30 May 2026 10:13:50 +0200 Subject: [PATCH 2/3] Revert "Merge pull request #90915 from margelo/@chrispader/refactor/compositional-edit-only-report-action-compose" This reverts commit 6a3c80832c9a30777415bbd8ceb919bfde18fa12, reversing changes made to e09715e09f95bd1dffd02251feb92e2ab680be08. --- src/pages/inbox/ReportScreen.tsx | 4 +- ....tsx => AgentZeroAwareTypingIndicator.tsx} | 6 +- .../ComposerActionButton.tsx | 14 --- .../ComposerActionMenu.tsx | 7 +- .../ReportActionCompose/ComposerBox.tsx | 9 +- .../ReportActionCompose/ComposerContainer.tsx | 29 ----- .../ReportActionCompose/ComposerContext.ts | 2 - .../ComposerDefaultFooter.tsx | 22 ---- .../ReportActionCompose/ComposerDropZone.tsx | 10 +- .../ComposerEditingButtons.tsx | 10 +- .../ComposerEmojiPicker.tsx | 9 +- .../ReportActionCompose/ComposerFooter.tsx | 7 +- .../ReportActionCompose/ComposerInput.tsx | 7 +- .../ReportActionCompose/ComposerInputArea.tsx | 46 -------- .../ReportActionCompose/ComposerLocalTime.tsx | 8 +- .../ReportActionCompose/ComposerProvider.tsx | 1 - .../ComposerSendButton.tsx | 5 +- .../ReportActionCompose.tsx | 102 ++++++++++++------ ...tsx => ReportActionComposePlaceholder.tsx} | 4 +- src/pages/inbox/report/ReportFooter.tsx | 22 ++-- tests/unit/ComposerLocalTimeTest.tsx | 15 +-- 21 files changed, 145 insertions(+), 194 deletions(-) rename src/pages/inbox/report/ReportActionCompose/{ComposerTypingIndicator.tsx => AgentZeroAwareTypingIndicator.tsx} (69%) delete mode 100644 src/pages/inbox/report/ReportActionCompose/ComposerActionButton.tsx delete mode 100644 src/pages/inbox/report/ReportActionCompose/ComposerContainer.tsx delete mode 100644 src/pages/inbox/report/ReportActionCompose/ComposerDefaultFooter.tsx delete mode 100644 src/pages/inbox/report/ReportActionCompose/ComposerInputArea.tsx rename src/pages/inbox/report/ReportActionCompose/{ComposerPlaceholder.tsx => ReportActionComposePlaceholder.tsx} (97%) diff --git a/src/pages/inbox/ReportScreen.tsx b/src/pages/inbox/ReportScreen.tsx index 0fe5cab9d7fb..684f69b66284 100644 --- a/src/pages/inbox/ReportScreen.tsx +++ b/src/pages/inbox/ReportScreen.tsx @@ -28,7 +28,7 @@ import useDeferNonEssentials from './hooks/useDeferNonEssentials'; import useFlushDeferredWriteOnFocus from './hooks/useFlushDeferredWriteOnFocus'; import LinkedActionNotFoundGuard from './LinkedActionNotFoundGuard'; import ReactionListWrapper from './ReactionListWrapper'; -import ReportActionCompose from './report/ReportActionCompose/ReportActionCompose'; +import ReportActionComposePlaceholder from './report/ReportActionCompose/ReportActionComposePlaceholder'; import {ReportActionEditMessageContextProvider, ReportScreenEditMessageProviderWithTransactionThread} from './report/ReportActionEditMessageContext'; import ReportFooter from './report/ReportFooter'; import useClearReportActionDraftsOnReportChange from './report/useClearReportActionDraftsOnReportChange'; @@ -148,7 +148,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) { testID="report-actions-view-wrapper" > - {shouldDeferNonEssentials ? : } + {shouldDeferNonEssentials ? : } diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerTypingIndicator.tsx b/src/pages/inbox/report/ReportActionCompose/AgentZeroAwareTypingIndicator.tsx similarity index 69% rename from src/pages/inbox/report/ReportActionCompose/ComposerTypingIndicator.tsx rename to src/pages/inbox/report/ReportActionCompose/AgentZeroAwareTypingIndicator.tsx index 7ced0f7cc9c7..835cc1323b4f 100644 --- a/src/pages/inbox/report/ReportActionCompose/ComposerTypingIndicator.tsx +++ b/src/pages/inbox/report/ReportActionCompose/AgentZeroAwareTypingIndicator.tsx @@ -1,10 +1,8 @@ import React from 'react'; import useShouldSuppressConciergeIndicators from '@hooks/useShouldSuppressConciergeIndicators'; import ReportTypingIndicator from '@pages/inbox/report/ReportTypingIndicator'; -import {useComposerState} from './ComposerContext'; -function ComposerTypingIndicator() { - const {reportID} = useComposerState(); +function AgentZeroAwareTypingIndicator({reportID}: {reportID: string}) { const shouldSuppress = useShouldSuppressConciergeIndicators(reportID); if (shouldSuppress) { return null; @@ -12,4 +10,4 @@ function ComposerTypingIndicator() { return ; } -export default ComposerTypingIndicator; +export default AgentZeroAwareTypingIndicator; diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerActionButton.tsx b/src/pages/inbox/report/ReportActionCompose/ComposerActionButton.tsx deleted file mode 100644 index c0f694363919..000000000000 --- a/src/pages/inbox/report/ReportActionCompose/ComposerActionButton.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import ComposerActionMenu from './ComposerActionMenu'; -import {useComposerEditState} from './ComposerContext'; -import ComposerEditingButtons from './ComposerEditingButtons'; - -function ComposerActionButton() { - const {isEditingInComposer} = useComposerEditState(); - - if (isEditingInComposer) { - return ; - } - return ; -} - -export default ComposerActionButton; diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerActionMenu.tsx b/src/pages/inbox/report/ReportActionCompose/ComposerActionMenu.tsx index b7e8f4e14569..c84eda56f95f 100644 --- a/src/pages/inbox/report/ReportActionCompose/ComposerActionMenu.tsx +++ b/src/pages/inbox/report/ReportActionCompose/ComposerActionMenu.tsx @@ -11,8 +11,11 @@ import AttachmentPickerWithMenuItems from './AttachmentPickerWithMenuItems'; import {useComposerActions, useComposerEditState, useComposerMeta, useComposerSendState, useComposerState} from './ComposerContext'; import useAttachmentPicker from './useAttachmentPicker'; -function ComposerActionMenu() { - const {reportID} = useComposerState(); +type ComposerActionMenuProps = { + reportID: string; +}; + +function ComposerActionMenu({reportID}: ComposerActionMenuProps) { const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const {isMenuVisible, isFullComposerAvailable} = useComposerState(); const {draftComment} = useComposerEditState(); diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerBox.tsx b/src/pages/inbox/report/ReportActionCompose/ComposerBox.tsx index 0c1414788420..568c90b2a619 100644 --- a/src/pages/inbox/report/ReportActionCompose/ComposerBox.tsx +++ b/src/pages/inbox/report/ReportActionCompose/ComposerBox.tsx @@ -1,13 +1,16 @@ import React from 'react'; -import type {PropsWithChildren} from 'react'; import {View} from 'react-native'; import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; import ONYXKEYS from '@src/ONYXKEYS'; import {useComposerMeta, useComposerSendState, useComposerState} from './ComposerContext'; -function ComposerBox({children}: PropsWithChildren) { - const {reportID} = useComposerState(); +type ComposerBoxProps = { + reportID: string; + children: React.ReactNode; +}; + +function ComposerBox({reportID, children}: ComposerBoxProps) { const styles = useThemeStyles(); const {isFocused} = useComposerState(); const {isExceedingMaxLength, isBlockedFromConcierge} = useComposerSendState(); diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerContainer.tsx b/src/pages/inbox/report/ReportActionCompose/ComposerContainer.tsx deleted file mode 100644 index 7a11d44e5f90..000000000000 --- a/src/pages/inbox/report/ReportActionCompose/ComposerContainer.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; -import type {PropsWithChildren} from 'react'; -import OfflineWithFeedback from '@components/OfflineWithFeedback'; -import useOnyx from '@hooks/useOnyx'; -import useThemeStyles from '@hooks/useThemeStyles'; -import {getReportOfflinePendingActionAndErrors} from '@libs/ReportUtils'; -import ONYXKEYS from '@src/ONYXKEYS'; -import {useComposerState} from './ComposerContext'; - -function ComposerContainer({children}: PropsWithChildren) { - const {reportID} = useComposerState(); - const styles = useThemeStyles(); - const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`); - const [isComposerFullSize = false] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_IS_COMPOSER_FULL_SIZE}${reportID}`); - const {reportPendingAction: pendingAction} = getReportOfflinePendingActionAndErrors(report); - - return ( - - {children} - - ); -} - -export default ComposerContainer; diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerContext.ts b/src/pages/inbox/report/ReportActionCompose/ComposerContext.ts index 64c32912a0a3..ec9b33de1433 100644 --- a/src/pages/inbox/report/ReportActionCompose/ComposerContext.ts +++ b/src/pages/inbox/report/ReportActionCompose/ComposerContext.ts @@ -26,7 +26,6 @@ type ComposerText = string; // Warm — changes on interaction type ComposerState = { - reportID: string; isFocused: boolean; isMenuVisible: boolean; isFullComposerAvailable: boolean; @@ -91,7 +90,6 @@ const noop = () => {}; const ComposerTextContext = createContext(''); const defaultState: ComposerState = { - reportID: '', isFocused: false, isMenuVisible: false, isFullComposerAvailable: false, diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerDefaultFooter.tsx b/src/pages/inbox/report/ReportActionCompose/ComposerDefaultFooter.tsx deleted file mode 100644 index 0c83e2b890ef..000000000000 --- a/src/pages/inbox/report/ReportActionCompose/ComposerDefaultFooter.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react'; -import OfflineIndicator from '@components/OfflineIndicator'; -import useResponsiveLayout from '@hooks/useResponsiveLayout'; -import useThemeStyles from '@hooks/useThemeStyles'; -import ComposerExceededLength from './ComposerExceededLength'; -import ComposerFooter from './ComposerFooter'; -import ComposerTypingIndicator from './ComposerTypingIndicator'; - -function ComposerDefaultFooter() { - const styles = useThemeStyles(); - const {shouldUseNarrowLayout} = useResponsiveLayout(); - - return ( - - {!shouldUseNarrowLayout && } - - - - ); -} - -export default ComposerDefaultFooter; diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerDropZone.tsx b/src/pages/inbox/report/ReportActionCompose/ComposerDropZone.tsx index 0db4107e2e22..344a2316925c 100644 --- a/src/pages/inbox/report/ReportActionCompose/ComposerDropZone.tsx +++ b/src/pages/inbox/report/ReportActionCompose/ComposerDropZone.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import type {PropsWithChildren} from 'react'; import DragAndDropConsumer from '@components/DragAndDrop/Consumer'; import DropZoneUI from '@components/DropZone/DropZoneUI'; import DualDropZone from '@components/DropZone/DualDropZone'; @@ -15,11 +14,15 @@ import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import {getParentReport, isChatRoom, isGroupChat, isInvoiceReport, isReportApproved, isSettled, temporary_getMoneyRequestOptions} from '@libs/ReportUtils'; import {hasReceipt as hasReceiptTransactionUtils} from '@libs/TransactionUtils'; import ONYXKEYS from '@src/ONYXKEYS'; -import {useComposerState} from './ComposerContext'; import useAttachmentPicker from './useAttachmentPicker'; import useReceiptDrop from './useReceiptDrop'; import useShouldAddOrReplaceReceipt from './useShouldAddOrReplaceReceipt'; +type ComposerDropZoneProps = { + reportID: string; + children: React.ReactNode; +}; + type RichDropZoneProps = { reportID: string; shouldAddOrReplaceReceipt: boolean; @@ -107,8 +110,7 @@ function RichDropZone({reportID, shouldAddOrReplaceReceipt, transactionID, onAtt ); } -function ComposerDropZone({children}: PropsWithChildren) { - const {reportID} = useComposerState(); +function ComposerDropZone({reportID, children}: ComposerDropZoneProps) { const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`); const {shouldAddOrReplaceReceipt, transactionID} = useShouldAddOrReplaceReceipt(reportID); const {pickAttachments, PDFValidationComponent: AttachmentPDFValidation, ErrorModal: AttachmentErrorModal} = useAttachmentPicker(reportID); diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerEditingButtons.tsx b/src/pages/inbox/report/ReportActionCompose/ComposerEditingButtons.tsx index 4bf7e056ad13..77f8f58dfae0 100644 --- a/src/pages/inbox/report/ReportActionCompose/ComposerEditingButtons.tsx +++ b/src/pages/inbox/report/ReportActionCompose/ComposerEditingButtons.tsx @@ -2,12 +2,16 @@ import React from 'react'; import {View} from 'react-native'; import useThemeStyles from '@hooks/useThemeStyles'; import CONST from '@src/CONST'; -import {useComposerEditActions, useComposerState} from './ComposerContext'; +import {useComposerEditActions} from './ComposerContext'; import ComposerExpandCollapseButton from './ComposerExpandCollapseButton'; import MessageEditCancelButton from './MessageEditCancelButton'; -function ComposerEditingButtons() { - const {reportID} = useComposerState(); +type ComposerEditingButtonsProps = { + /** The report ID */ + reportID: string; +}; + +function ComposerEditingButtons({reportID}: ComposerEditingButtonsProps) { const styles = useThemeStyles(); const {deleteDraft} = useComposerEditActions(); diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerEmojiPicker.tsx b/src/pages/inbox/report/ReportActionCompose/ComposerEmojiPicker.tsx index 43f4ff1c8c47..99c26549cd59 100644 --- a/src/pages/inbox/report/ReportActionCompose/ComposerEmojiPicker.tsx +++ b/src/pages/inbox/report/ReportActionCompose/ComposerEmojiPicker.tsx @@ -6,10 +6,13 @@ import {canUseTouchScreen} from '@libs/DeviceCapabilities'; import DomUtils from '@libs/DomUtils'; import {hideEmojiPicker, isActive as isActiveEmojiPickerAction} from '@userActions/EmojiPickerAction'; import CONST from '@src/CONST'; -import {useComposerMeta, useComposerSendState, useComposerState} from './ComposerContext'; +import {useComposerMeta, useComposerSendState} from './ComposerContext'; -function ComposerEmojiPicker() { - const {reportID} = useComposerState(); +type ComposerEmojiPickerProps = { + reportID: string; +}; + +function ComposerEmojiPicker({reportID}: ComposerEmojiPickerProps) { const styles = useThemeStyles(); const {isMediumScreenWidth} = useResponsiveLayout(); diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerFooter.tsx b/src/pages/inbox/report/ReportActionCompose/ComposerFooter.tsx index 4bcfe5289c4c..c34ca8e8e9de 100644 --- a/src/pages/inbox/report/ReportActionCompose/ComposerFooter.tsx +++ b/src/pages/inbox/report/ReportActionCompose/ComposerFooter.tsx @@ -1,11 +1,14 @@ import React from 'react'; -import type {PropsWithChildren} from 'react'; import {View} from 'react-native'; import useNetwork from '@hooks/useNetwork'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; -function ComposerFooter({children}: PropsWithChildren) { +type ComposerFooterProps = { + children: React.ReactNode; +}; + +function ComposerFooter({children}: ComposerFooterProps) { const styles = useThemeStyles(); // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth const {isSmallScreenWidth} = useResponsiveLayout(); diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerInput.tsx b/src/pages/inbox/report/ReportActionCompose/ComposerInput.tsx index c8425c634336..1b01a369687f 100644 --- a/src/pages/inbox/report/ReportActionCompose/ComposerInput.tsx +++ b/src/pages/inbox/report/ReportActionCompose/ComposerInput.tsx @@ -23,6 +23,10 @@ import ComposerWithSuggestions from './ComposerWithSuggestions'; import useAttachmentPicker from './useAttachmentPicker'; import useComposerSubmit from './useComposerSubmit'; +type ComposerInputProps = { + reportID: string; +}; + const AI_PLACEHOLDER_KEYS = ['reportActionCompose.askConciergeToUpdate', 'reportActionCompose.askConciergeToCorrect', 'reportActionCompose.askConciergeForHelp'] as const; function getRandomPlaceholder(translate: LocalizedTranslate): string { @@ -30,8 +34,7 @@ function getRandomPlaceholder(translate: LocalizedTranslate): string { return translate(AI_PLACEHOLDER_KEYS[randomIndex]); } -function ComposerInput() { - const {reportID} = useComposerState(); +function ComposerInput({reportID}: ComposerInputProps) { const {translate, preferredLocale} = useLocalize(); const {isMenuVisible} = useComposerState(); const {isBlockedFromConcierge, debouncedCommentMaxLengthValidation} = useComposerSendState(); diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerInputArea.tsx b/src/pages/inbox/report/ReportActionCompose/ComposerInputArea.tsx deleted file mode 100644 index 2b59b2f8db2a..000000000000 --- a/src/pages/inbox/report/ReportActionCompose/ComposerInputArea.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import React from 'react'; -import {View} from 'react-native'; -import useOnyx from '@hooks/useOnyx'; -import useThemeStyles from '@hooks/useThemeStyles'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import ComposerActionButton from './ComposerActionButton'; -import ComposerBox from './ComposerBox'; -import ComposerContainer from './ComposerContainer'; -import {useComposerState} from './ComposerContext'; -import ComposerDropZone from './ComposerDropZone'; -import ComposerEmojiPicker from './ComposerEmojiPicker'; -import ComposerImportedState from './ComposerImportedState'; -import ComposerInput from './ComposerInput'; -import ComposerLocalTime from './ComposerLocalTime'; -import ComposerSendButton from './ComposerSendButton'; - -function ComposerInputArea() { - const {reportID} = useComposerState(); - const styles = useThemeStyles(); - const [isComposerFullSize = false] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_IS_COMPOSER_FULL_SIZE}${reportID}`); - - return ( - - - - - - - - - - - - - - - - - ); -} - -export default ComposerInputArea; diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerLocalTime.tsx b/src/pages/inbox/report/ReportActionCompose/ComposerLocalTime.tsx index 2eca6ac0d47c..98d14016f4ce 100644 --- a/src/pages/inbox/report/ReportActionCompose/ComposerLocalTime.tsx +++ b/src/pages/inbox/report/ReportActionCompose/ComposerLocalTime.tsx @@ -9,10 +9,12 @@ import {isAgentEmail} from '@libs/SessionUtils'; import ParticipantLocalTime from '@pages/inbox/report/ParticipantLocalTime'; import ONYXKEYS from '@src/ONYXKEYS'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -import {useComposerState} from './ComposerContext'; -function ComposerLocalTime() { - const {reportID} = useComposerState(); +type ComposerLocalTimeProps = { + reportID: string; +}; + +function ComposerLocalTime({reportID}: ComposerLocalTimeProps) { const [isComposerFullSize = false] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_IS_COMPOSER_FULL_SIZE}${reportID}`); const {accountID: currentUserAccountID} = useCurrentUserPersonalDetails(); const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`); diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerProvider.tsx b/src/pages/inbox/report/ReportActionCompose/ComposerProvider.tsx index 3435222bd241..7c3b41392182 100644 --- a/src/pages/inbox/report/ReportActionCompose/ComposerProvider.tsx +++ b/src/pages/inbox/report/ReportActionCompose/ComposerProvider.tsx @@ -124,7 +124,6 @@ function ComposerProvider({children, reportID}: ComposerProviderProps) { }; const composerState = { - reportID, isFocused, isMenuVisible, isFullComposerAvailable, diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerSendButton.tsx b/src/pages/inbox/report/ReportActionCompose/ComposerSendButton.tsx index 361c4828cccb..79aab7007d88 100644 --- a/src/pages/inbox/report/ReportActionCompose/ComposerSendButton.tsx +++ b/src/pages/inbox/report/ReportActionCompose/ComposerSendButton.tsx @@ -6,12 +6,11 @@ import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; import CONST from '@src/CONST'; -import {useComposerEditState, useComposerSendState, useComposerState} from './ComposerContext'; +import {useComposerEditState, useComposerSendState} from './ComposerContext'; import SubmitDraftButton from './SubmitDraftButton'; import useComposerSubmit from './useComposerSubmit'; -function ComposerSendButton() { - const {reportID} = useComposerState(); +function ComposerSendButton({reportID}: {reportID: string}) { const styles = useThemeStyles(); const {translate} = useLocalize(); const icons = useMemoizedLazyExpensifyIcons(['Send', 'Checkmark']); diff --git a/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx index 984b242f8c9d..b81935a33ada 100644 --- a/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx @@ -1,9 +1,18 @@ import React from 'react'; +import {View} from 'react-native'; +import OfflineIndicator from '@components/OfflineIndicator'; +import OfflineWithFeedback from '@components/OfflineWithFeedback'; +import useOnyx from '@hooks/useOnyx'; +import useResponsiveLayout from '@hooks/useResponsiveLayout'; +import useThemeStyles from '@hooks/useThemeStyles'; +import {getReportOfflinePendingActionAndErrors} from '@libs/ReportUtils'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import AgentZeroAwareTypingIndicator from './AgentZeroAwareTypingIndicator'; import ComposerActionMenu from './ComposerActionMenu'; import ComposerBox from './ComposerBox'; -import ComposerContainer from './ComposerContainer'; +import {useComposerEditState} from './ComposerContext'; import type {SuggestionsRef} from './ComposerContext'; -import ComposerDefaultFooter from './ComposerDefaultFooter'; import ComposerDropZone from './ComposerDropZone'; import ComposerEditingButtons from './ComposerEditingButtons'; import ComposerEmojiPicker from './ComposerEmojiPicker'; @@ -11,53 +20,84 @@ import ComposerExceededLength from './ComposerExceededLength'; import ComposerFooter from './ComposerFooter'; import ComposerImportedState from './ComposerImportedState'; import ComposerInput from './ComposerInput'; -import ComposerInputArea from './ComposerInputArea'; import ComposerLocalTime from './ComposerLocalTime'; -import ComposerPlaceholder from './ComposerPlaceholder'; import ComposerProvider from './ComposerProvider'; import ComposerSendButton from './ComposerSendButton'; -import ComposerTypingIndicator from './ComposerTypingIndicator'; type ReportActionComposeProps = { /** Report ID */ reportID: string; + + /** Whether the composer is edit only */ + isEditOnly?: boolean; }; -function ReportActionCompose({reportID}: ReportActionComposeProps) { +function ComposerInner({reportID, isEditOnly = false}: ReportActionComposeProps) { + const styles = useThemeStyles(); + const {shouldUseNarrowLayout} = useResponsiveLayout(); + const {isEditingInComposer} = useComposerEditState(); + const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`); + const [isComposerFullSize = false] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_IS_COMPOSER_FULL_SIZE}${reportID}`); + const {reportPendingAction: pendingAction} = getReportOfflinePendingActionAndErrors(report); + return ( - - - - + + + + + + + {isEditingInComposer ? : } + + + + + + {!isEditOnly && ( + + {!shouldUseNarrowLayout && } + + + + )} + + + + ); } -function EditOnlyReportActionCompose({reportID}: ReportActionComposeProps) { +function Composer({reportID, ...restProps}: ReportActionComposeProps) { return ( - + ); } -ReportActionCompose.LocalTime = ComposerLocalTime; -ReportActionCompose.Container = ComposerContainer; -ReportActionCompose.ImportedState = ComposerImportedState; -ReportActionCompose.DropZone = ComposerDropZone; -ReportActionCompose.Box = ComposerBox; -ReportActionCompose.ActionMenu = ComposerActionMenu; -ReportActionCompose.Input = ComposerInput; -ReportActionCompose.EmojiPicker = ComposerEmojiPicker; -ReportActionCompose.SendButton = ComposerSendButton; -ReportActionCompose.EditingButtons = ComposerEditingButtons; -ReportActionCompose.Footer = ComposerFooter; -ReportActionCompose.TypingIndicator = ComposerTypingIndicator; -ReportActionCompose.ExceededLength = ComposerExceededLength; -ReportActionCompose.Layout = ComposerInputArea; -ReportActionCompose.Placeholder = ComposerPlaceholder; -ReportActionCompose.InputArea = ComposerInputArea; -ReportActionCompose.DefaultFooter = ComposerDefaultFooter; -ReportActionCompose.EditOnly = EditOnlyReportActionCompose; +Composer.LocalTime = ComposerLocalTime; +Composer.DropZone = ComposerDropZone; +Composer.Box = ComposerBox; +Composer.ActionMenu = ComposerActionMenu; +Composer.Input = ComposerInput; +Composer.EmojiPicker = ComposerEmojiPicker; +Composer.SendButton = ComposerSendButton; +Composer.EditingButtons = ComposerEditingButtons; +Composer.Footer = ComposerFooter; +Composer.TypingIndicator = AgentZeroAwareTypingIndicator; +Composer.ExceededLength = ComposerExceededLength; +Composer.ImportedState = ComposerImportedState; -export default ReportActionCompose; +export default Composer; export type {SuggestionsRef, ReportActionComposeProps}; diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerPlaceholder.tsx b/src/pages/inbox/report/ReportActionCompose/ReportActionComposePlaceholder.tsx similarity index 97% rename from src/pages/inbox/report/ReportActionCompose/ComposerPlaceholder.tsx rename to src/pages/inbox/report/ReportActionCompose/ReportActionComposePlaceholder.tsx index f3a997f5357a..7a90fad4a08c 100644 --- a/src/pages/inbox/report/ReportActionCompose/ComposerPlaceholder.tsx +++ b/src/pages/inbox/report/ReportActionCompose/ReportActionComposePlaceholder.tsx @@ -22,7 +22,7 @@ import CONST from '@src/CONST'; * chatItemComposeSecondaryRow, and icons [Plus, Emoji, Send]. * If the real composer changes its layout or icon set, update this placeholder to match. */ -function ComposerPlaceholder() { +function ReportActionComposePlaceholder() { const theme = useTheme(); const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -86,4 +86,4 @@ function ComposerPlaceholder() { ); } -export default ComposerPlaceholder; +export default ReportActionComposePlaceholder; diff --git a/src/pages/inbox/report/ReportFooter.tsx b/src/pages/inbox/report/ReportFooter.tsx index ab26c755e5f2..510a60ec8221 100644 --- a/src/pages/inbox/report/ReportFooter.tsx +++ b/src/pages/inbox/report/ReportFooter.tsx @@ -97,9 +97,7 @@ function ReportFooter() { {shouldShowEnableNotificationsBanner && } - - - + ); @@ -169,9 +167,10 @@ function ReportFooter() { {isEditingWithComposer && ( - - - + )} + + + ); +} + export default ReportFooter; diff --git a/tests/unit/ComposerLocalTimeTest.tsx b/tests/unit/ComposerLocalTimeTest.tsx index 7bf6f13c107e..0fdb4ca2e4f8 100644 --- a/tests/unit/ComposerLocalTimeTest.tsx +++ b/tests/unit/ComposerLocalTimeTest.tsx @@ -5,7 +5,6 @@ import ComposeProviders from '@components/ComposeProviders'; import {LocaleContextProvider} from '@components/LocaleContextProvider'; import OnyxListItemProvider from '@components/OnyxListItemProvider'; import ComposerLocalTime from '@pages/inbox/report/ReportActionCompose/ComposerLocalTime'; -import ComposerProvider from '@pages/inbox/report/ReportActionCompose/ComposerProvider'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetailsList, Report} from '@src/types/onyx'; @@ -38,12 +37,8 @@ function buildReport(participantAccountIDs: number[]): Report { } as Report; } -function renderWithProviders(component: React.ReactElement, reportID: string) { - return render( - - {component} - , - ); +function renderWithProviders(component: React.ReactElement) { + return render({component}); } describe('ComposerLocalTime', () => { @@ -72,7 +67,7 @@ describe('ComposerLocalTime', () => { await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, personalDetails); await waitForBatchedUpdates(); - renderWithProviders(, REPORT_ID); + renderWithProviders(); await waitForBatchedUpdates(); @@ -98,7 +93,7 @@ describe('ComposerLocalTime', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_IS_COMPOSER_FULL_SIZE}${REPORT_ID}`, true); await waitForBatchedUpdates(); - const {toJSON} = renderWithProviders(, REPORT_ID); + const {toJSON} = renderWithProviders(); await waitForBatchedUpdates(); @@ -122,7 +117,7 @@ describe('ComposerLocalTime', () => { await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, personalDetails); await waitForBatchedUpdates(); - const {toJSON} = renderWithProviders(, REPORT_ID); + const {toJSON} = renderWithProviders(); await waitForBatchedUpdates(); From d32f2d6c478448358fc13a22bbf4c8ea17b116a7 Mon Sep 17 00:00:00 2001 From: Vit Horacek Date: Sat, 30 May 2026 10:41:39 +0200 Subject: [PATCH 3/3] Migrate third-party GHA actions off deprecated node20/node16 runtimes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Node 20 hits EOL April 30 2026 and GitHub removes it as the default Actions runtime on June 16 2026. Bump every pinned third-party action to a node24-compatible release and replace two archived actions that will never ship a node24 build: - actions/download-artifact → v8.0.1 (node24) - 1password/load-secrets-action → v4.0.0 (node24) - aws-actions/configure-aws-credentials → v6.1.3 (node24) - actions/setup-java → v5.2.0 (node24) - dorny/paths-filter → v4.0.1 (node24) - mxschmitt/action-tmate → v3.24 (node24) - ruby/setup-ruby → v1.310.0 (node24) - cloudflare/pages-action (node16, archived) → cloudflare/wrangler-action v4.0.0 - 8398a7/action-slack (node20, archived) → slackapi/slack-github-action v3.0.3 The slack migration converts all JS-template-literal custom payloads to proper JSON and moves the webhook URL from an env var to the `webhook:` input. process.env.AS_REPO references are replaced with ${{ github.repository }}. The Cloudflare migration switches from the deprecated pages-action to wrangler-action's `pages deploy` command and updates the output reference from `.alias` to `.deployment-url`. Co-authored-by: Cursor --- .../announceFailedWorkflowInSlack/action.yml | 21 ++- .github/workflows/androidBump.yml | 22 ++- .github/workflows/buildAndroid.yml | 4 +- .github/workflows/buildIOS.yml | 2 +- .github/workflows/cherryPick.yml | 94 +++++------ .github/workflows/claude-review.yml | 2 +- .github/workflows/deploy.yml | 152 ++++++++---------- .github/workflows/deployExpensifyHelp.yml | 30 ++-- .github/workflows/generateTranslations.yml | 2 +- .../publishReactNativeAndroidArtifacts.yml | 2 +- .github/workflows/remote-build-android.yml | 2 +- .github/workflows/remote-build-ios.yml | 4 +- .github/workflows/reviewerChecklist.yml | 2 +- .github/workflows/syncVersions.yml | 18 +-- .github/workflows/test.yml | 2 +- 15 files changed, 159 insertions(+), 200 deletions(-) diff --git a/.github/actions/composite/announceFailedWorkflowInSlack/action.yml b/.github/actions/composite/announceFailedWorkflowInSlack/action.yml index 356f122e09b1..981caa3b5622 100644 --- a/.github/actions/composite/announceFailedWorkflowInSlack/action.yml +++ b/.github/actions/composite/announceFailedWorkflowInSlack/action.yml @@ -13,20 +13,17 @@ inputs: runs: using: composite steps: - - uses: 8398a7/action-slack@1750b5085f3ec60384090fb7c52965ef822e869e + - uses: slackapi/slack-github-action@45a88b9581bfab2566dc881e2cd66d334e621e2c # v3.0.3 name: Job failed Slack notification with: - status: custom - fields: workflow, repo - custom_payload: | + webhook: ${{ inputs.SLACK_WEBHOOK }} + webhook-type: incoming-webhook + payload: | { - channel: '${{ inputs.CHANNEL }}', - attachments: [{ - color: "#DB4545", - pretext: ``, - text: `💥 ${process.env.AS_REPO} failed on workflow 💥`, + "channel": "${{ inputs.CHANNEL }}", + "attachments": [{ + "color": "#DB4545", + "pretext": "", + "text": "💥 ${{ github.repository }} failed on workflow 💥" }] } - env: - GITHUB_TOKEN: ${{ github.token }} - SLACK_WEBHOOK_URL: ${{ inputs.SLACK_WEBHOOK }} diff --git a/.github/workflows/androidBump.yml b/.github/workflows/androidBump.yml index 3f50feed24a6..fd5ae8418cf8 100644 --- a/.github/workflows/androidBump.yml +++ b/.github/workflows/androidBump.yml @@ -17,7 +17,7 @@ jobs: - name: Setup Ruby # v1.229.0 - uses: ruby/setup-ruby@354a1ad156761f5ee2b7b13fa8e09943a5e8d252 + uses: ruby/setup-ruby@afeafc3d1ab54a631816aba4c914a0081c12ff2f with: bundler-cache: true @@ -53,18 +53,16 @@ jobs: - name: Warn deployers if Android rollout percentage could not be updated if: ${{ failure() }} # v3 - uses: 8398a7/action-slack@1750b5085f3ec60384090fb7c52965ef822e869e + uses: slackapi/slack-github-action@45a88b9581bfab2566dc881e2cd66d334e621e2c # v3.0.3 with: - status: custom - custom_payload: | + webhook: ${{ secrets.SLACK_WEBHOOK }} + webhook-type: incoming-webhook + payload: | { - channel: '#deployer', - attachments: [{ - color: "#DB4545", - pretext: ``, - text: `💥 Android rollout percentage could not be updated. 💥`, + "channel": "#deployer", + "attachments": [{ + "color": "#DB4545", + "pretext": "", + "text": "💥 Android rollout percentage could not be updated. 💥" }] } - env: - GITHUB_TOKEN: ${{ github.token }} - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} diff --git a/.github/workflows/buildAndroid.yml b/.github/workflows/buildAndroid.yml index b91d9f9032fe..b02b75c70662 100644 --- a/.github/workflows/buildAndroid.yml +++ b/.github/workflows/buildAndroid.yml @@ -117,7 +117,7 @@ jobs: - name: Setup Java # v4 - uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 with: distribution: oracle java-version: ${{ steps.get-java-version.outputs.version }} @@ -143,7 +143,7 @@ jobs: - name: Load Android upload keystore credentials from 1Password id: load-credentials # v3 - uses: 1password/load-secrets-action@8d0d610af187e78a2772c2d18d627f4c52d3fbfb + uses: 1password/load-secrets-action@92467eb28f72e8255933372f1e0707c567ce2259 with: export-env: false env: diff --git a/.github/workflows/buildIOS.yml b/.github/workflows/buildIOS.yml index 79bea43b2c24..99353a0795d6 100644 --- a/.github/workflows/buildIOS.yml +++ b/.github/workflows/buildIOS.yml @@ -105,7 +105,7 @@ jobs: - name: Setup Ruby # v1.229.0 - uses: ruby/setup-ruby@354a1ad156761f5ee2b7b13fa8e09943a5e8d252 + uses: ruby/setup-ruby@afeafc3d1ab54a631816aba4c914a0081c12ff2f with: bundler-cache: true diff --git a/.github/workflows/cherryPick.yml b/.github/workflows/cherryPick.yml index 924885090e32..c4412c481e1d 100644 --- a/.github/workflows/cherryPick.yml +++ b/.github/workflows/cherryPick.yml @@ -240,19 +240,18 @@ jobs: - name: Warn that previous deploy was superseded if: ${{ steps.cherryPick.outputs.HAS_CONFLICTS != 'true' && steps.checkInProgressDeploy.outputs.DEPLOY_RUN_URL != '' }} - uses: 8398a7/action-slack@1750b5085f3ec60384090fb7c52965ef822e869e + uses: slackapi/slack-github-action@45a88b9581bfab2566dc881e2cd66d334e621e2c # v3.0.3 with: - status: custom - custom_payload: | + webhook: ${{ secrets.SLACK_WEBHOOK }} + webhook-type: incoming-webhook + payload: | { - channel: '#deployer', - attachments: [{ - color: 'warning', - text: `⚠️ ${{ inputs.TARGET == 'production' && 'Production' || 'Staging' }} <${{ steps.checkInProgressDeploy.outputs.DEPLOY_RUN_URL }}|deploy> cancelled by new cherry-pick` + "channel": "#deployer", + "attachments": [{ + "color": "warning", + "text": "⚠️ ${{ inputs.TARGET == 'production' && 'Production' || 'Staging' }} <${{ steps.checkInProgressDeploy.outputs.DEPLOY_RUN_URL }}|deploy> cancelled by new cherry-pick" }] } - env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} - name: Find deploy workflow run # Also runs for version-bump-only CPs where HAS_CONFLICTS is unset @@ -296,20 +295,18 @@ jobs: - name: Announce successful CP in #deployer # Also runs for version-bump-only CPs where HAS_CONFLICTS is unset if: ${{ steps.cherryPick.outputs.HAS_CONFLICTS != 'true' }} - uses: 8398a7/action-slack@1750b5085f3ec60384090fb7c52965ef822e869e + uses: slackapi/slack-github-action@45a88b9581bfab2566dc881e2cd66d334e621e2c # v3.0.3 with: - status: custom - custom_payload: | + webhook: ${{ secrets.SLACK_WEBHOOK }} + webhook-type: incoming-webhook + payload: | { - channel: '#deployer', - attachments: [{ - color: 'good', - text: `🍒 Cherry-pick to *${{ inputs.TARGET }}* successfully started\nPR: ${{ inputs.PULL_REQUEST_URL || '(version bump only)' }}\nDeploy workflow: ${{ steps.findDeployRun.outputs.DEPLOY_RUN_MESSAGE }}` + "channel": "#deployer", + "attachments": [{ + "color": "good", + "text": "🍒 Cherry-pick to *${{ inputs.TARGET }}* successfully started\nPR: ${{ inputs.PULL_REQUEST_URL || '(version bump only)' }}\nDeploy workflow: ${{ steps.findDeployRun.outputs.DEPLOY_RUN_MESSAGE }}" }] } - env: - GITHUB_TOKEN: ${{ github.token }} - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} - name: Write workflow summary # Also runs for version-bump-only CPs where HAS_CONFLICTS is unset @@ -423,21 +420,19 @@ jobs: - name: Announce CP conflict in #deployer if: steps.cherryPick.outputs.HAS_CONFLICTS == 'true' - uses: 8398a7/action-slack@1750b5085f3ec60384090fb7c52965ef822e869e + uses: slackapi/slack-github-action@45a88b9581bfab2566dc881e2cd66d334e621e2c # v3.0.3 with: - status: custom - custom_payload: | + webhook: ${{ secrets.SLACK_WEBHOOK }} + webhook-type: incoming-webhook + payload: | { - channel: '#deployer', - attachments: [{ - color: "#DB4545", - pretext: `${{ steps.resolveSlack.outputs.SLACK_MENTION }}`, - text: "🚨 Cherry-pick to ${{ inputs.TARGET }} has conflicts and requires manual resolution.\nOriginal PR: ${{ inputs.PULL_REQUEST_URL }}\nConflict PR: ${{ steps.createPullRequest.outputs.PR_URL }}" + "channel": "#deployer", + "attachments": [{ + "color": "#DB4545", + "pretext": "${{ steps.resolveSlack.outputs.SLACK_MENTION }}", + "text": "🚨 Cherry-pick to ${{ inputs.TARGET }} has conflicts and requires manual resolution.\nOriginal PR: ${{ inputs.PULL_REQUEST_URL }}\nConflict PR: ${{ steps.createPullRequest.outputs.PR_URL }}" }] } - env: - GITHUB_TOKEN: ${{ github.token }} - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} - name: Add assignees to conflict PRs if: steps.cherryPick.outputs.HAS_CONFLICTS == 'true' @@ -455,37 +450,32 @@ jobs: GITHUB_TOKEN: ${{ secrets.OS_BOTIFY_TOKEN }} - name: "Announces a CP failure in the #announce Slack room" - # v3 - uses: 8398a7/action-slack@1750b5085f3ec60384090fb7c52965ef822e869e + uses: slackapi/slack-github-action@45a88b9581bfab2566dc881e2cd66d334e621e2c # v3.0.3 if: ${{ failure() }} with: - status: custom - custom_payload: | + webhook: ${{ secrets.SLACK_WEBHOOK }} + webhook-type: incoming-webhook + payload: | { - channel: '#announce', - attachments: [{ - color: "#DB4545", - pretext: ``, - text: `💥 Failed to CP ${{ inputs.PULL_REQUEST_URL }} to ${{ inputs.TARGET }} 💥`, + "channel": "#announce", + "attachments": [{ + "color": "#DB4545", + "pretext": "", + "text": "💥 Failed to CP ${{ inputs.PULL_REQUEST_URL }} to ${{ inputs.TARGET }} 💥" }] } - env: - GITHUB_TOKEN: ${{ github.token }} - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} - name: "Announce CP cancellation in #deployer" - uses: 8398a7/action-slack@1750b5085f3ec60384090fb7c52965ef822e869e + uses: slackapi/slack-github-action@45a88b9581bfab2566dc881e2cd66d334e621e2c # v3.0.3 if: ${{ cancelled() }} with: - status: custom - custom_payload: | + webhook: ${{ secrets.SLACK_WEBHOOK }} + webhook-type: incoming-webhook + payload: | { - channel: '#deployer', - attachments: [{ - color: 'warning', - text: `🍒 Cherry-pick to *${{ inputs.TARGET }}* was cancelled\nPR: ${{ inputs.PULL_REQUEST_URL || '(version bump only)' }}\nWorkflow run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}` + "channel": "#deployer", + "attachments": [{ + "color": "warning", + "text": "🍒 Cherry-pick to *${{ inputs.TARGET }}* was cancelled\nPR: ${{ inputs.PULL_REQUEST_URL || '(version bump only)' }}\nWorkflow run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" }] } - env: - GITHUB_TOKEN: ${{ github.token }} - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} diff --git a/.github/workflows/claude-review.yml b/.github/workflows/claude-review.yml index 3600cbe99d0f..80ca931f4b8e 100644 --- a/.github/workflows/claude-review.yml +++ b/.github/workflows/claude-review.yml @@ -81,7 +81,7 @@ jobs: - name: Filter paths if: steps.set-authorized.outputs.IS_AUTHORIZED == 'true' - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1 id: filter with: filters: | diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 721f30b6b7c5..b8f8888ea12d 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -237,7 +237,7 @@ jobs: - name: Setup Ruby # v1.229.0 - uses: ruby/setup-ruby@354a1ad156761f5ee2b7b13fa8e09943a5e8d252 + uses: ruby/setup-ruby@afeafc3d1ab54a631816aba4c914a0081c12ff2f with: bundler-cache: true @@ -275,7 +275,7 @@ jobs: - name: Setup Ruby # v1.229.0 - uses: ruby/setup-ruby@354a1ad156761f5ee2b7b13fa8e09943a5e8d252 + uses: ruby/setup-ruby@afeafc3d1ab54a631816aba4c914a0081c12ff2f with: bundler-cache: true @@ -314,22 +314,19 @@ jobs: - name: Warn deployers if Android production deploy failed if: ${{ failure() }} - # v3 - uses: 8398a7/action-slack@1750b5085f3ec60384090fb7c52965ef822e869e + uses: slackapi/slack-github-action@45a88b9581bfab2566dc881e2cd66d334e621e2c # v3.0.3 with: - status: custom - custom_payload: | + webhook: ${{ secrets.SLACK_WEBHOOK }} + webhook-type: incoming-webhook + payload: | { - channel: '#deployer', - attachments: [{ - color: "#DB4545", - pretext: ``, - text: `💥 Android HybridApp production failed. Please ${{ needs.prep.outputs.APP_VERSION }} in the 💥`, + "channel": "#deployer", + "attachments": [{ + "color": "#DB4545", + "pretext": "", + "text": "💥 Android HybridApp production failed. Please ${{ needs.prep.outputs.APP_VERSION }} in the 💥" }] } - env: - GITHUB_TOKEN: ${{ github.token }} - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} androidUploadBrowserStack: name: Upload Android to BrowserStack @@ -384,7 +381,7 @@ jobs: - name: Load Applause API key from 1Password id: load-credentials # v2 - uses: 1password/load-secrets-action@581a835fb51b8e7ec56b71cf2ffddd7e68bb25e0 + uses: 1password/load-secrets-action@92467eb28f72e8255933372f1e0707c567ce2259 with: export-env: false env: @@ -471,7 +468,7 @@ jobs: - name: Setup Ruby # v1.229.0 - uses: ruby/setup-ruby@354a1ad156761f5ee2b7b13fa8e09943a5e8d252 + uses: ruby/setup-ruby@afeafc3d1ab54a631816aba4c914a0081c12ff2f with: bundler-cache: true @@ -514,7 +511,7 @@ jobs: - name: Setup Ruby # v1.229.0 - uses: ruby/setup-ruby@354a1ad156761f5ee2b7b13fa8e09943a5e8d252 + uses: ruby/setup-ruby@afeafc3d1ab54a631816aba4c914a0081c12ff2f with: bundler-cache: true @@ -545,22 +542,19 @@ jobs: - name: Warn deployers if iOS production deploy failed if: ${{ failure() }} - # v3 - uses: 8398a7/action-slack@1750b5085f3ec60384090fb7c52965ef822e869e + uses: slackapi/slack-github-action@45a88b9581bfab2566dc881e2cd66d334e621e2c # v3.0.3 with: - status: custom - custom_payload: | + webhook: ${{ secrets.SLACK_WEBHOOK }} + webhook-type: incoming-webhook + payload: | { - channel: '#deployer', - attachments: [{ - color: "#DB4545", - pretext: ``, - text: `💥 iOS HybridApp production deploy failed. Please ${{ needs.prep.outputs.IOS_VERSION }} in the . 💥`, + "channel": "#deployer", + "attachments": [{ + "color": "#DB4545", + "pretext": "", + "text": "💥 iOS HybridApp production deploy failed. Please ${{ needs.prep.outputs.IOS_VERSION }} in the . 💥" }] } - env: - GITHUB_TOKEN: ${{ github.token }} - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} iosUploadBrowserStack: name: Upload iOS to BrowserStack @@ -615,7 +609,7 @@ jobs: - name: Load Applause API key from 1Password id: load-credentials # v2 - uses: 1password/load-secrets-action@581a835fb51b8e7ec56b71cf2ffddd7e68bb25e0 + uses: 1password/load-secrets-action@92467eb28f72e8255933372f1e0707c567ce2259 with: export-env: false env: @@ -775,20 +769,18 @@ jobs: - name: Post Slack message on manual cancellation if: ${{ steps.check.outputs.SKIP != 'true' }} - # v3 - uses: 8398a7/action-slack@1750b5085f3ec60384090fb7c52965ef822e869e + uses: slackapi/slack-github-action@45a88b9581bfab2566dc881e2cd66d334e621e2c # v3.0.3 with: - status: custom - custom_payload: | + webhook: ${{ secrets.SLACK_WEBHOOK }} + webhook-type: incoming-webhook + payload: | { - channel: '#deployer', - attachments: [{ - color: 'warning', - text: `⚠️ ${{ env.DEPLOY_ENV == 'production' && 'Production' || 'Staging' }} <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|deploy> cancelled manually` + "channel": "#deployer", + "attachments": [{ + "color": "warning", + "text": "⚠️ ${{ env.DEPLOY_ENV == 'production' && 'Production' || 'Staging' }} <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|deploy> cancelled manually" }] } - env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} checkDeploymentSuccess: runs-on: blacksmith-2vcpu-ubuntu-2404 @@ -874,7 +866,7 @@ jobs: steps: # v4.2.1 - name: Download all workflow run artifacts - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c - name: Get last production release id: get_last_prod_version @@ -960,22 +952,19 @@ jobs: - name: Warn deployers if deploy failed if: ${{ failure() }} - # v3 - uses: 8398a7/action-slack@1750b5085f3ec60384090fb7c52965ef822e869e + uses: slackapi/slack-github-action@45a88b9581bfab2566dc881e2cd66d334e621e2c # v3.0.3 with: - status: custom - custom_payload: | + webhook: ${{ secrets.SLACK_WEBHOOK }} + webhook-type: incoming-webhook + payload: | { - channel: '#deployer', - attachments: [{ - color: "#DB4545", - pretext: ``, - text: `💥 NewDot ${{ env.DEPLOY_ENV }} deploy failed. 💥`, + "channel": "#deployer", + "attachments": [{ + "color": "#DB4545", + "pretext": "", + "text": "💥 NewDot ${{ env.DEPLOY_ENV }} deploy failed. 💥" }] } - env: - GITHUB_TOKEN: ${{ github.token }} - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} # Why is this necessary for CP-to-prod? Consider this scenario: # 1. You close a checklist and we create a new staging version `9.0.34-0` and a new checklist @@ -1001,56 +990,47 @@ jobs: needs: [prep, androidUploadGooglePlay, androidSubmit, iosUploadTestflight, iosSubmit, webDeploy, checkDeploymentSuccess, createRelease] steps: - name: 'Announces the deploy in the #announce Slack room' - # v3 - uses: 8398a7/action-slack@1750b5085f3ec60384090fb7c52965ef822e869e + uses: slackapi/slack-github-action@45a88b9581bfab2566dc881e2cd66d334e621e2c # v3.0.3 with: - status: custom - custom_payload: | + webhook: ${{ secrets.SLACK_WEBHOOK }} + webhook-type: incoming-webhook + payload: | { - channel: '#announce', - attachments: [{ - color: 'good', - text: `🎉️ Successfully deployed ${process.env.AS_REPO} to ${{ env.DEPLOY_ENV }} 🎉️`, + "channel": "#announce", + "attachments": [{ + "color": "good", + "text": "🎉️ Successfully deployed ${{ github.repository }} to ${{ env.DEPLOY_ENV }} 🎉️" }] } - env: - GITHUB_TOKEN: ${{ github.token }} - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} - name: 'Announces the deploy in the #deployer Slack room' - # v3 - uses: 8398a7/action-slack@1750b5085f3ec60384090fb7c52965ef822e869e + uses: slackapi/slack-github-action@45a88b9581bfab2566dc881e2cd66d334e621e2c # v3.0.3 with: - status: custom - custom_payload: | + webhook: ${{ secrets.SLACK_WEBHOOK }} + webhook-type: incoming-webhook + payload: | { - channel: '#deployer', - attachments: [{ - color: 'good', - text: `🎉️ Successfully deployed ${process.env.AS_REPO} to ${{ env.DEPLOY_ENV }} 🎉️`, + "channel": "#deployer", + "attachments": [{ + "color": "good", + "text": "🎉️ Successfully deployed ${{ github.repository }} to ${{ env.DEPLOY_ENV }} 🎉️" }] } - env: - GITHUB_TOKEN: ${{ github.token }} - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} - name: 'Announces a production deploy in the #expensify-open-source Slack room' - # v3 - uses: 8398a7/action-slack@1750b5085f3ec60384090fb7c52965ef822e869e + uses: slackapi/slack-github-action@45a88b9581bfab2566dc881e2cd66d334e621e2c # v3.0.3 if: ${{ env.DEPLOY_ENV == 'production' }} with: - status: custom - custom_payload: | + webhook: ${{ secrets.SLACK_WEBHOOK }} + webhook-type: incoming-webhook + payload: | { - channel: '#expensify-open-source', - attachments: [{ - color: 'good', - text: `🎉️ Successfully deployed ${process.env.AS_REPO} to production 🎉️`, + "channel": "#expensify-open-source", + "attachments": [{ + "color": "good", + "text": "🎉️ Successfully deployed ${{ github.repository }} to production 🎉️" }] } - env: - GITHUB_TOKEN: ${{ github.token }} - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} postGithubComments: uses: ./.github/workflows/postDeployComments.yml diff --git a/.github/workflows/deployExpensifyHelp.yml b/.github/workflows/deployExpensifyHelp.yml index 1cb0a21d9a1a..773aa76b8c72 100644 --- a/.github/workflows/deployExpensifyHelp.yml +++ b/.github/workflows/deployExpensifyHelp.yml @@ -70,14 +70,13 @@ jobs: working-directory: ./docs - name: Deploy to Cloudflare Pages - uses: cloudflare/pages-action@f0a1cd58cd66095dee69bfa18fa5efd1dde93bca + uses: cloudflare/wrangler-action@ebbaa1584979971c8614a24965b4405ff95890e0 # v4.0.0 id: deploy if: env.IS_PR_FROM_FORK != 'true' with: apiToken: ${{ secrets.CLOUDFLARE_PAGES_TOKEN }} accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - projectName: helpdot - directory: ./docs/_site + command: pages deploy ./docs/_site --project-name=helpdot - name: Setup Cloudflare CLI if: env.IS_PR_FROM_FORK != 'true' @@ -92,7 +91,7 @@ jobs: - name: Check if only articles are updated if: ${{ github.event_name == 'pull_request' }} id: verify_articles_only_updated - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1 with: filters: | articles: @@ -119,7 +118,7 @@ jobs: uses: ./.github/actions/javascript/generateHelpPreviewComment with: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - ROOT_URL: ${{ steps.deploy.outputs.alias }} + ROOT_URL: ${{ steps.deploy.outputs.deployment-url }} PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} - name: Leave a comment on the PR @@ -139,19 +138,16 @@ jobs: if: ${{ failure() && github.ref == 'refs/heads/main' && github.event_name != 'pull_request' }} steps: - name: Warn deployers if Expensify Help deploy failed - # v3 - uses: 8398a7/action-slack@1750b5085f3ec60384090fb7c52965ef822e869e + uses: slackapi/slack-github-action@45a88b9581bfab2566dc881e2cd66d334e621e2c # v3.0.3 with: - status: custom - custom_payload: | + webhook: ${{ secrets.SLACK_WEBHOOK }} + webhook-type: incoming-webhook + payload: | { - channel: '#deployer', - attachments: [{ - color: "#DB4545", - pretext: ``, - text: `Expensify Help failed. Please fix it!`, + "channel": "#deployer", + "attachments": [{ + "color": "#DB4545", + "pretext": "", + "text": "Expensify Help failed. Please fix it!" }] } - env: - GITHUB_TOKEN: ${{ github.token }} - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} diff --git a/.github/workflows/generateTranslations.yml b/.github/workflows/generateTranslations.yml index 071c1279a0d3..fe3a8913a5d6 100644 --- a/.github/workflows/generateTranslations.yml +++ b/.github/workflows/generateTranslations.yml @@ -51,7 +51,7 @@ jobs: - name: Setup tmate if: runner.debug == '1' - uses: mxschmitt/action-tmate@e5c7151931ca95bad1c6f4190c730ecf8c7dde48 + uses: mxschmitt/action-tmate@35b54afac29c97fb54faba5b513f8fbd1882f113 timeout-minutes: 60 with: limit-access-to-actor: true diff --git a/.github/workflows/publishReactNativeAndroidArtifacts.yml b/.github/workflows/publishReactNativeAndroidArtifacts.yml index c8eafa4bd5e9..2d7a23ac808f 100644 --- a/.github/workflows/publishReactNativeAndroidArtifacts.yml +++ b/.github/workflows/publishReactNativeAndroidArtifacts.yml @@ -153,7 +153,7 @@ jobs: uses: ./.github/actions/composite/getJavaVersion - name: Setup Java - uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 with: distribution: oracle java-version: ${{ steps.get-java-version.outputs.version }} diff --git a/.github/workflows/remote-build-android.yml b/.github/workflows/remote-build-android.yml index 94e553078c0b..3f2b84666b12 100644 --- a/.github/workflows/remote-build-android.yml +++ b/.github/workflows/remote-build-android.yml @@ -50,7 +50,7 @@ jobs: - name: Configure AWS Credentials # v4 - uses: aws-actions/configure-aws-credentials@ececac1a45f3b08a01d2dd070d28d111c5fe6722 + uses: aws-actions/configure-aws-credentials@99214aa6889fcddfa57764031d71add364327e59 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} diff --git a/.github/workflows/remote-build-ios.yml b/.github/workflows/remote-build-ios.yml index b7bf1f0dbbbb..1fc075da0310 100644 --- a/.github/workflows/remote-build-ios.yml +++ b/.github/workflows/remote-build-ios.yml @@ -46,7 +46,7 @@ jobs: - name: Setup Ruby # v1.229.0 - uses: ruby/setup-ruby@354a1ad156761f5ee2b7b13fa8e09943a5e8d252 + uses: ruby/setup-ruby@afeafc3d1ab54a631816aba4c914a0081c12ff2f with: bundler-cache: true @@ -60,7 +60,7 @@ jobs: - name: Configure AWS Credentials # v4 - uses: aws-actions/configure-aws-credentials@ececac1a45f3b08a01d2dd070d28d111c5fe6722 + uses: aws-actions/configure-aws-credentials@99214aa6889fcddfa57764031d71add364327e59 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} diff --git a/.github/workflows/reviewerChecklist.yml b/.github/workflows/reviewerChecklist.yml index 065a8fd1aec4..646b2c0de94c 100644 --- a/.github/workflows/reviewerChecklist.yml +++ b/.github/workflows/reviewerChecklist.yml @@ -12,7 +12,7 @@ jobs: - uses: useblacksmith/checkout@c9796daa2a4bdebdab5bd16be2c09a70cd4e1121 # v1 - name: Filter paths - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1 id: filter with: predicate-quantifier: 'every' diff --git a/.github/workflows/syncVersions.yml b/.github/workflows/syncVersions.yml index 5930c2906af8..00b15b7da63c 100644 --- a/.github/workflows/syncVersions.yml +++ b/.github/workflows/syncVersions.yml @@ -201,20 +201,18 @@ jobs: - name: Announce sync in Slack if: steps.checkVersions.outputs.IN_SYNC != 'true' # v3 - uses: 8398a7/action-slack@1750b5085f3ec60384090fb7c52965ef822e869e + uses: slackapi/slack-github-action@45a88b9581bfab2566dc881e2cd66d334e621e2c # v3.0.3 with: - status: custom - custom_payload: | + webhook: ${{ secrets.SLACK_WEBHOOK }} + webhook-type: incoming-webhook + payload: | { - channel: '#deployer', - attachments: [{ - color: "good", - text: `✅ Version sync completed. E/App and Mobile-Expensify are at ${{ steps.verifySync.outputs.POST_SYNC_APP_VERSION }}; submodule matches Mobile-Expensify main.` + "channel": "#deployer", + "attachments": [{ + "color": "good", + "text": "✅ Version sync completed. E/App and Mobile-Expensify are at ${{ steps.verifySync.outputs.POST_SYNC_APP_VERSION }}; submodule matches Mobile-Expensify main." }] } - env: - GITHUB_TOKEN: ${{ github.token }} - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} - name: Announce failed workflow in Slack if: ${{ failure() }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ad9d0ff8ec8f..3ed6bdfbf04c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -58,7 +58,7 @@ jobs: # Codecov needs a token to be run on the default branch (which happens in preDeploy.yml after a PR is merged to main) if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} # v2 - uses: 1password/load-secrets-action@581a835fb51b8e7ec56b71cf2ffddd7e68bb25e0 + uses: 1password/load-secrets-action@92467eb28f72e8255933372f1e0707c567ce2259 with: export-env: false env: