-
Notifications
You must be signed in to change notification settings - Fork 3.7k
Allow creating time expenses from global menu #79780
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
Changes from 11 commits
8ab23f4
34a3e84
f6d3ab9
49e4c39
5eb6205
52d9c44
704325d
eb00d06
f94caa3
97a75f2
f35e93e
a2579fa
3f9b62d
74a112f
c8ffb45
9638050
b18ae23
c4d5329
9a8f960
27b3ef2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| import React from 'react'; | ||
| import {View} from 'react-native'; | ||
| import type {OnyxCollection} from 'react-native-onyx'; | ||
| import SearchBar from '@components/SearchBar'; | ||
| import SelectionList from '@components/SelectionList'; | ||
| import type {ListItem} from '@components/SelectionList/ListItem/types'; | ||
| import UserListItem from '@components/SelectionList/ListItem/UserListItem'; | ||
| import Text from '@components/Text'; | ||
| import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; | ||
| import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; | ||
| import useLocalize from '@hooks/useLocalize'; | ||
| import useOnyx from '@hooks/useOnyx'; | ||
| import useSearchResults from '@hooks/useSearchResults'; | ||
| import useThemeStyles from '@hooks/useThemeStyles'; | ||
| import {sortWorkspacesBySelected} from '@libs/PolicyUtils'; | ||
| import {getDefaultWorkspaceAvatar} from '@libs/ReportUtils'; | ||
| import tokenizedSearch from '@libs/tokenizedSearch'; | ||
| import CONST from '@src/CONST'; | ||
| import ONYXKEYS from '@src/ONYXKEYS'; | ||
| import type SCREENS from '@src/SCREENS'; | ||
| import type {Policy} from '@src/types/onyx'; | ||
| import type {WithFullTransactionOrNotFoundProps} from './withFullTransactionOrNotFound'; | ||
| import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; | ||
|
|
||
| type WorkspaceListItem = ListItem & { | ||
| value: string; | ||
| }; | ||
|
|
||
| type BaseRequestStepWorkspaceProps = WithFullTransactionOrNotFoundProps<typeof SCREENS.MONEY_REQUEST.CREATE> & { | ||
| /** Function returning available workspaces for the list. */ | ||
| getPolicies: (allPolicies: OnyxCollection<Policy>, currentUserLogin: string | undefined) => Policy[]; | ||
|
|
||
| /** Function to run after selecting a workspace. */ | ||
| onSelectWorkspace: (item: WorkspaceListItem, allPolicies: OnyxCollection<Policy>) => void; | ||
| }; | ||
|
|
||
| function BaseRequestStepWorkspace({transaction, getPolicies, onSelectWorkspace}: BaseRequestStepWorkspaceProps) { | ||
| const icons = useMemoizedLazyExpensifyIcons(['FallbackWorkspaceAvatar']); | ||
| const styles = useThemeStyles(); | ||
| const {translate, localeCompare} = useLocalize(); | ||
|
|
||
| const {login: currentUserLogin} = useCurrentUserPersonalDetails(); | ||
| const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true}); | ||
| const selectedWorkspace = transaction?.participants?.[0]; | ||
| const availableWorkspaces = getPolicies(allPolicies, currentUserLogin); | ||
| const workspaceOptions: WorkspaceListItem[] = availableWorkspaces | ||
| .sort((policy1, policy2) => | ||
| sortWorkspacesBySelected( | ||
| {policyID: policy1.id, name: policy1.name}, | ||
| {policyID: policy2.id, name: policy2.name}, | ||
| selectedWorkspace?.policyID ? [selectedWorkspace?.policyID] : [], | ||
| localeCompare, | ||
| ), | ||
| ) | ||
| .map((policy) => ({ | ||
| text: policy.name, | ||
| value: policy.id, | ||
| keyForList: policy.id, | ||
| icons: [ | ||
| { | ||
| id: policy.id, | ||
| source: policy?.avatarURL ? policy.avatarURL : getDefaultWorkspaceAvatar(policy.name), | ||
| fallbackIcon: icons.FallbackWorkspaceAvatar, | ||
| name: policy.name, | ||
| type: CONST.ICON_TYPE_WORKSPACE, | ||
| }, | ||
| ], | ||
| isSelected: selectedWorkspace?.policyID === policy.id, | ||
| })); | ||
|
|
||
| const filterWorkspace = (workspaceOption: WorkspaceListItem, searchInput: string) => { | ||
|
grgia marked this conversation as resolved.
|
||
| const results = tokenizedSearch([workspaceOption], searchInput, (option) => [option.text ?? '']); | ||
| return results.length > 0; | ||
| }; | ||
| const sortWorkspaces = (data: WorkspaceListItem[]) => data.sort((a, b) => localeCompare(a.text ?? '', b?.text ?? '')); | ||
| const [inputValue, setInputValue, filteredWorkspaceOptions] = useSearchResults(workspaceOptions, filterWorkspace, sortWorkspaces); | ||
|
|
||
| const selectWorkspace = (item: WorkspaceListItem) => { | ||
| onSelectWorkspace(item, allPolicies); | ||
| }; | ||
|
|
||
| return ( | ||
| <> | ||
| {workspaceOptions.length > CONST.SEARCH_ITEM_LIMIT ? ( | ||
| <SearchBar | ||
| label={translate('workspace.common.findWorkspace')} | ||
| inputValue={inputValue} | ||
| onChangeText={setInputValue} | ||
| shouldShowEmptyState={workspaceOptions.length > 0 && filteredWorkspaceOptions.length === 0} | ||
| /> | ||
| ) : ( | ||
| <View style={[styles.optionsListSectionHeader]}> | ||
| <Text style={[styles.ph5, styles.textLabelSupporting]}>{translate('iou.chooseWorkspace')}</Text> | ||
| </View> | ||
| )} | ||
| <SelectionList | ||
| key={selectedWorkspace?.policyID} | ||
| data={filteredWorkspaceOptions} | ||
| onSelectRow={selectWorkspace} | ||
| shouldSingleExecuteRowSelect | ||
| ListItem={UserListItem} | ||
| initiallyFocusedItemKey={selectedWorkspace?.policyID} | ||
| /> | ||
| </> | ||
| ); | ||
| } | ||
|
|
||
| export default withFullTransactionOrNotFound(BaseRequestStepWorkspace); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -436,9 +436,6 @@ function IOURequestStepConfirmation({ | |
| Navigation.goBack(ROUTES.MONEY_REQUEST_STEP_SUBRATE.getRoute(action, iouType, initialTransactionID, reportID)); | ||
| return; | ||
| } | ||
| if (isTimeRequest) { | ||
| return Navigation.goBack(ROUTES.MONEY_REQUEST_CREATE_TAB_TIME.getRoute(action, iouType, initialTransactionID, reportID)); | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why remove this block?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this block was unnecessary, the function should correctly handle navigation without it. I added it because I noticed the navigation wasn't correct and I was inspired by perDiem, but it turned out this block introduced some bugs and the thing that was missing was adding support for "time" type in this change was already merged here: https://github.com/Expensify/App/pull/79673/files |
||
|
|
||
| if (transaction?.isFromGlobalCreate && !transaction.receipt?.isTestReceipt) { | ||
| // If the participants weren't automatically added to the transaction, then we should go back to the IOURequestStepParticipants. | ||
|
|
@@ -480,7 +477,6 @@ function IOURequestStepConfirmation({ | |
| isMovingTransactionFromTrackExpense, | ||
| participantsAutoAssignedFromRoute, | ||
| backTo, | ||
| isTimeRequest, | ||
| ]); | ||
|
|
||
| // When the component mounts, if there is a receipt, see if the image can be read from the disk. If not, redirect the user to the starting step of the flow. | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we use MONEY_REQUEST_STEP_HOURS for the edit flow, like the others? The action parameter already distinguishes between edit and create flows
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need a new route
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in both cases the action parameter is "create", so the urls would be the same. I would need to add another parameter to distinguish between the two. should I?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh I understand your problem.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mhawryluk We also get this problem on other expense create flow. And we resolved it by checking the route name. Could you check this conversation and see If we can apply them here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the step after selecting a workspace would also be IOURequestStepHours and action "create". it's this one:
Nagranie.z.ekranu.2026-01-21.o.13.37.10.mov
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mhawryluk Ahh got it. Could we use transaction.amount or transaction.units.count to distinguish them? Or if impossible, I suggest creating a new parameter for the current route instead of creating a new route
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think basing it on transaction properties will not work, because we can go back from the confirmation page and then the transaction is going to be filled out. I will check this, and if that's the case then I will add a new parameter
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you, @mhawryluk. Please ping me when your update is ready.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@DylanDylann the update is ready