Repository navigation
[CFI] Detail view dropdown #106208
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
Open
DylanDylann
wants to merge
19
commits into
Expensify:main
Choose a base branch
from
DylanDylann:dylann/105904-saved-expense-text-inputs
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+3,339
−1,291
Open
[CFI] Detail view dropdown #106208
Changes from 1 commit
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
df224eb
saved-expense-text-inputs
DylanDylann d276574
saved-expense-text-inputs
DylanDylann 73af02d
Merge branch 'main' into dylann/105904-saved-expense-text-inputs
DylanDylann a438559
enhance merchant input handling by stripping invisible characters and…
DylanDylann 0733cb7
update attendee handling in MoneyRequest components to use new hooks …
DylanDylann f960425
Merge branch 'main' into dylann/105904-saved-expense-text-inputs
DylanDylann ea6b8b0
Refactor Money Request components to utilize new `useMoneyRequestUpda…
DylanDylann f19eb0a
address comments
DylanDylann 54466ae
address comments
DylanDylann 47a9c2f
Merge branch 'main' into dylann/105904-saved-expense-text-inputs
DylanDylann 6d5c7f6
Refactor TaxAmountInputField to use AmountFormField component instead…
DylanDylann 0379e58
Enhance AmountActionButtons and AmountInputField components by adding…
DylanDylann 491612a
Merge branch 'main' into dylann/105904-saved-expense-text-inputs
DylanDylann c059c72
update padding
DylanDylann d405b51
Fix selected tax rate calculation in MoneyRequestView component
DylanDylann c8d7453
Enhance MenuItemContent to accept custom styles and update ExpenseFie…
DylanDylann fe8de32
Merge branch 'main' into dylann/105904-saved-expense-text-inputs
DylanDylann ec532f0
merge main
DylanDylann c417d65
address comments
DylanDylann File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
88 changes: 88 additions & 0 deletions
88
src/components/ReportActionItem/MoneyRequestViewFields/DescriptionInputField.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,88 @@ | ||
| import TextInput from '@components/TextInput'; | ||
|
|
||
| import useLocalize from '@hooks/useLocalize'; | ||
| import useMirroredInputValue from '@hooks/useMirroredInputValue'; | ||
| import useThemeStyles from '@hooks/useThemeStyles'; | ||
| import useUpdateTransactionDescription from '@hooks/useUpdateTransactionDescription'; | ||
|
|
||
| import Parser from '@libs/Parser'; | ||
| import StringUtils from '@libs/StringUtils'; | ||
| import {getDescription} from '@libs/TransactionUtils'; | ||
| import {getInlineDescriptionErrorMessage} from '@libs/ValidationUtils'; | ||
|
|
||
| import variables from '@styles/variables'; | ||
|
|
||
| import CONST from '@src/CONST'; | ||
| import type * as OnyxTypes from '@src/types/onyx'; | ||
|
|
||
| import type {OnyxEntry} from 'react-native-onyx'; | ||
|
|
||
| import React, {useState} from 'react'; | ||
| import {View} from 'react-native'; | ||
|
|
||
| type DescriptionInputFieldProps = { | ||
| /** Expense whose description is edited */ | ||
| transaction: OnyxEntry<OnyxTypes.Transaction>; | ||
|
|
||
| /** Transaction thread report of the expense */ | ||
| transactionThreadReport: OnyxEntry<OnyxTypes.Report>; | ||
|
|
||
| /** Whether the expense is on a group workspace, where a description can mention a room */ | ||
| isReportInGroupPolicy: boolean; | ||
|
|
||
| /** Violation message for the description, shown while the typed value has no error of its own */ | ||
| errorText?: string; | ||
| }; | ||
|
|
||
| /** | ||
| * The description of a saved expense, edited in place as markdown. The value is saved when the input loses focus, | ||
| * through the same write the description page uses. | ||
| */ | ||
| function DescriptionInputField({transaction, transactionThreadReport, isReportInGroupPolicy, errorText = ''}: DescriptionInputFieldProps) { | ||
| const styles = useThemeStyles(); | ||
| const {translate} = useLocalize(); | ||
| const {updateDescription} = useUpdateTransactionDescription({transaction, transactionThreadReport}); | ||
|
|
||
| // The saved description is HTML, so it is compared and edited as markdown. | ||
| const savedDescription = Parser.htmlToMarkdown(getDescription(transaction)); | ||
| const {inputValue, setInputValue, markFocused, markBlurred} = useMirroredInputValue(savedDescription); | ||
| const [inputError, setInputError] = useState(''); | ||
|
|
||
| const saveDescription = () => { | ||
| markBlurred(); | ||
| // The description page saves the value after its form strips invisible characters and trims it. | ||
| const newDescription = StringUtils.removeInvisibleCharacters(inputValue).trim(); | ||
| const descriptionError = getInlineDescriptionErrorMessage(translate, newDescription); | ||
| if (descriptionError) { | ||
| setInputError(descriptionError); | ||
| return; | ||
| } | ||
| setInputError(''); | ||
| setInputValue(newDescription); | ||
| if (newDescription === savedDescription) { | ||
| return; | ||
| } | ||
| updateDescription(newDescription); | ||
| }; | ||
|
|
||
| return ( | ||
| <View style={[styles.mh5, styles.mv2]}> | ||
| <TextInput | ||
| value={inputValue} | ||
| onChangeText={setInputValue} | ||
| onFocus={markFocused} | ||
| onBlur={saveDescription} | ||
| label={translate('common.description')} | ||
| accessibilityLabel={translate('common.description')} | ||
| autoGrowHeight | ||
| maxAutoGrowHeight={variables.textInputAutoGrowMaxHeight} | ||
| type="markdown" | ||
| excludedMarkdownStyles={!isReportInGroupPolicy ? ['mentionReport'] : []} | ||
| errorText={inputError || errorText} | ||
| sentryLabel={CONST.SENTRY_LABEL.MONEY_REQUEST.DESCRIPTION_INPUT} | ||
| /> | ||
| </View> | ||
| ); | ||
| } | ||
|
|
||
| export default DescriptionInputField; | ||
82 changes: 82 additions & 0 deletions
82
src/components/ReportActionItem/MoneyRequestViewFields/MerchantInputField.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| import TextInput from '@components/TextInput'; | ||
|
|
||
| import useLocalize from '@hooks/useLocalize'; | ||
| import useMirroredInputValue from '@hooks/useMirroredInputValue'; | ||
| import useThemeStyles from '@hooks/useThemeStyles'; | ||
| import useUpdateTransactionMerchant from '@hooks/useUpdateTransactionMerchant'; | ||
|
|
||
| import {isMerchantRequired} from '@libs/MoneyRequestUtils'; | ||
| import {getTransactionDetails} from '@libs/ReportUtils'; | ||
| import {getInlineMerchantErrorMessage, isInvalidMerchantValue} from '@libs/ValidationUtils'; | ||
|
|
||
| import CONST from '@src/CONST'; | ||
| import type * as OnyxTypes from '@src/types/onyx'; | ||
|
|
||
| import type {ReactNode} from 'react'; | ||
| import type {OnyxEntry} from 'react-native-onyx'; | ||
|
|
||
| import React, {useState} from 'react'; | ||
| import {View} from 'react-native'; | ||
|
|
||
| type MerchantInputFieldProps = { | ||
|
DylanDylann marked this conversation as resolved.
Resolved
|
||
| /** Expense whose merchant is edited */ | ||
| transaction: OnyxEntry<OnyxTypes.Transaction>; | ||
|
|
||
| /** Transaction thread report of the expense */ | ||
| transactionThreadReport: OnyxEntry<OnyxTypes.Report>; | ||
|
|
||
| /** Violation message for the merchant, shown while the typed value has no error of its own */ | ||
| errorText?: string; | ||
|
|
||
| /** Rendered below the input, e.g. the Google search link for a card merchant */ | ||
| footer?: ReactNode; | ||
|
DylanDylann marked this conversation as resolved.
Resolved
|
||
| }; | ||
|
|
||
| /** | ||
| * The merchant of a saved expense, edited in place. The value is saved when the input loses focus, through the same | ||
| * write the merchant page uses. | ||
| */ | ||
| function MerchantInputField({transaction, transactionThreadReport, errorText = '', footer}: MerchantInputFieldProps) { | ||
| const styles = useThemeStyles(); | ||
| const {translate} = useLocalize(); | ||
| const {updateMerchant} = useUpdateTransactionMerchant({transactionID: transaction?.transactionID, transactionThreadReport}); | ||
|
|
||
| const merchant = getTransactionDetails(transaction)?.merchant; | ||
| const savedMerchant = isInvalidMerchantValue(merchant) ? '' : (merchant ?? ''); | ||
| const {inputValue, setInputValue, markFocused, markBlurred} = useMirroredInputValue(savedMerchant); | ||
| const [inputError, setInputError] = useState(''); | ||
|
|
||
| const saveMerchant = () => { | ||
| markBlurred(); | ||
| const newMerchant = inputValue.trim(); | ||
|
DylanDylann marked this conversation as resolved.
Resolved
|
||
| const merchantError = getInlineMerchantErrorMessage(translate, newMerchant, isMerchantRequired(transactionThreadReport, transaction)); | ||
| if (merchantError) { | ||
| setInputError(merchantError); | ||
| return; | ||
| } | ||
| setInputError(''); | ||
| setInputValue(newMerchant); | ||
| if (newMerchant === savedMerchant) { | ||
| return; | ||
| } | ||
| updateMerchant(newMerchant); | ||
| }; | ||
|
|
||
| return ( | ||
| <View style={[styles.mh5, styles.mv2]}> | ||
| <TextInput | ||
| value={inputValue} | ||
| onChangeText={setInputValue} | ||
| onFocus={markFocused} | ||
| onBlur={saveMerchant} | ||
| label={translate('common.merchant')} | ||
| accessibilityLabel={translate('common.merchant')} | ||
| errorText={inputError || errorText} | ||
| sentryLabel={CONST.SENTRY_LABEL.MONEY_REQUEST.MERCHANT_INPUT} | ||
| /> | ||
| {!!footer && <View style={styles.mt2}>{footer}</View>} | ||
| </View> | ||
| ); | ||
| } | ||
|
|
||
| export default MerchantInputField; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| import {useState} from 'react'; | ||
|
|
||
| type UseMirroredInputValueResult = { | ||
|
DylanDylann marked this conversation as resolved.
Resolved
|
||
| /** What the input shows */ | ||
| inputValue: string; | ||
|
|
||
| /** Sets what the input shows, as the user types */ | ||
| setInputValue: (value: string) => void; | ||
|
|
||
| /** Whether the input is being edited */ | ||
| isFocused: boolean; | ||
|
|
||
| /** Marks the input as being edited, which holds off outside changes */ | ||
| markFocused: () => void; | ||
|
|
||
| /** Marks the input as no longer being edited */ | ||
| markBlurred: () => void; | ||
| }; | ||
|
|
||
| /** | ||
| * Mirrors a value stored in Onyx in local state for a controlled text input. Feeding the stored value straight to the | ||
| * input lets a write that lands mid-typing overwrite what the user typed and move the caret, so the input shows the | ||
| * mirror instead, which picks up outside changes to the stored value only while the input is not being edited. | ||
| */ | ||
| function useMirroredInputValue(storedValue: string): UseMirroredInputValueResult { | ||
| const [isFocused, setIsFocused] = useState(false); | ||
| const [inputValue, setInputValue] = useState(storedValue); | ||
| const [prevStoredValue, setPrevStoredValue] = useState(storedValue); | ||
|
|
||
| // Synced during render rather than in an effect, to avoid a render pass that shows the stale value. | ||
| if (storedValue !== prevStoredValue) { | ||
| setPrevStoredValue(storedValue); | ||
| if (!isFocused) { | ||
| setInputValue(storedValue); | ||
| } | ||
|
DylanDylann marked this conversation as resolved.
Resolved
|
||
| } | ||
|
|
||
| return {inputValue, setInputValue, isFocused, markFocused: () => setIsFocused(true), markBlurred: () => setIsFocused(false)}; | ||
| } | ||
|
|
||
| export default useMirroredInputValue; | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.