Skip to content

Commit

Permalink
Merge pull request Expensify#34897 from ruben-rebelo/ts-migration/Wor…
Browse files Browse the repository at this point in the history
…kspaceSettings

[TS Migration] Migrate WorkspaceProfilePage and WorkspaceProfileCurrencyPage
  • Loading branch information
arosiclair authored Feb 22, 2024
2 parents 94dc249 + 5d06edb commit 6754f15
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 102 deletions.
1 change: 1 addition & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1555,6 +1555,7 @@ const CONST = {
WORKSPACE_TRAVEL: 'WorkspaceBookTravel',
WORKSPACE_MEMBERS: 'WorkspaceManageMembers',
WORKSPACE_BANK_ACCOUNT: 'WorkspaceBankAccount',
WORKSPACE_SETTINGS: 'WorkspaceSettings',
},
get EXPENSIFY_EMAILS() {
return [
Expand Down
2 changes: 1 addition & 1 deletion src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,7 @@ type OnyxValuesMapping = {
[ONYXKEYS.PRIVATE_PERSONAL_DETAILS]: OnyxTypes.PrivatePersonalDetails;
[ONYXKEYS.TASK]: OnyxTypes.Task;
[ONYXKEYS.WORKSPACE_RATE_AND_UNIT]: OnyxTypes.WorkspaceRateAndUnit;
[ONYXKEYS.CURRENCY_LIST]: Record<string, OnyxTypes.Currency>;
[ONYXKEYS.CURRENCY_LIST]: OnyxTypes.CurrencyList;
[ONYXKEYS.UPDATE_AVAILABLE]: boolean;
[ONYXKEYS.SCREEN_SHARE_REQUEST]: OnyxTypes.ScreenShareRequest;
[ONYXKEYS.COUNTRY_CODE]: number;
Expand Down
2 changes: 1 addition & 1 deletion src/libs/UserUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ function getAvatarUrl(avatarSource: AvatarSource | undefined, accountID: number)
* Avatars uploaded by users will have a _128 appended so that the asset server returns a small version.
* This removes that part of the URL so the full version of the image can load.
*/
function getFullSizeAvatar(avatarSource: AvatarSource | undefined, accountID: number): AvatarSource {
function getFullSizeAvatar(avatarSource: AvatarSource | undefined, accountID?: number): AvatarSource {
const source = getAvatar(avatarSource, accountID);
if (typeof source !== 'string') {
return source;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,65 +1,62 @@
import PropTypes from 'prop-types';
import React, {useState} from 'react';
import type {OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
import SelectionList from '@components/SelectionList';
import RadioListItem from '@components/SelectionList/RadioListItem';
import useLocalize from '@hooks/useLocalize';
import compose from '@libs/compose';
import Navigation from '@libs/Navigation/Navigation';
import * as PolicyUtils from '@libs/PolicyUtils';
import * as Policy from '@userActions/Policy';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import {policyDefaultProps, policyPropTypes} from './withPolicy';
import type {CurrencyList} from '@src/types/onyx';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import type {WithPolicyAndFullscreenLoadingProps} from './withPolicyAndFullscreenLoading';
import withPolicyAndFullscreenLoading from './withPolicyAndFullscreenLoading';

const propTypes = {
type WorkspaceProfileCurrentPageOnyxProps = {
/** Constant, list of available currencies */
currencyList: PropTypes.objectOf(
PropTypes.shape({
/** Symbol of the currency */
symbol: PropTypes.string.isRequired,
}),
),
isLoadingReportData: PropTypes.bool,
...policyPropTypes,
currencyList: OnyxEntry<CurrencyList>;
};

const defaultProps = {
currencyList: {},
isLoadingReportData: true,
...policyDefaultProps,
type WorkspaceProfileCurrentPageProps = WithPolicyAndFullscreenLoadingProps & WorkspaceProfileCurrentPageOnyxProps;

type WorkspaceProfileCurrencyPageSectionItem = {
text: string;
keyForList: string;
isSelected: boolean;
};

const getDisplayText = (currencyCode, currencySymbol) => `${currencyCode} - ${currencySymbol}`;
const getDisplayText = (currencyCode: string, currencySymbol: string) => `${currencyCode} - ${currencySymbol}`;

function WorkspaceSettingsCurrencyPage({currencyList, policy, isLoadingReportData}) {
function WorkspaceProfileCurrencyPage({currencyList = {}, policy, isLoadingReportData = true}: WorkspaceProfileCurrentPageProps) {
const {translate} = useLocalize();
const [searchText, setSearchText] = useState('');
const trimmedText = searchText.trim().toLowerCase();
const currencyListKeys = _.keys(currencyList);
const currencyListKeys = Object.keys(currencyList ?? {});

const filteredItems = _.filter(currencyListKeys, (currencyCode) => {
const currency = currencyList[currencyCode];
return getDisplayText(currencyCode, currency.symbol).toLowerCase().includes(trimmedText);
const filteredItems = currencyListKeys.filter((currencyCode: string) => {
const currency = currencyList?.[currencyCode];
return getDisplayText(currencyCode, currency?.symbol ?? '')
.toLowerCase()
.includes(trimmedText);
});

let initiallyFocusedOptionKey;

const currencyItems = _.map(filteredItems, (currencyCode) => {
const currency = currencyList[currencyCode];
const isSelected = policy.outputCurrency === currencyCode;
const currencyItems: WorkspaceProfileCurrencyPageSectionItem[] = filteredItems.map((currencyCode: string) => {
const currency = currencyList?.[currencyCode];
const isSelected = policy?.outputCurrency === currencyCode;

if (isSelected) {
initiallyFocusedOptionKey = currencyCode;
}

return {
text: getDisplayText(currencyCode, currency.symbol),
text: getDisplayText(currencyCode, currency?.symbol ?? ''),
keyForList: currencyCode,
isSelected,
};
Expand All @@ -69,20 +66,20 @@ function WorkspaceSettingsCurrencyPage({currencyList, policy, isLoadingReportDat

const headerMessage = searchText.trim() && !currencyItems.length ? translate('common.noResultsFound') : '';

const onSelectCurrency = (item) => {
Policy.updateGeneralSettings(policy.id, policy.name, item.keyForList);
const onSelectCurrency = (item: WorkspaceProfileCurrencyPageSectionItem) => {
Policy.updateGeneralSettings(policy?.id ?? '', policy?.name ?? '', item.keyForList);
Navigation.goBack();
};

return (
<ScreenWrapper
includeSafeAreaPaddingBottom={false}
testID={WorkspaceSettingsCurrencyPage.displayName}
testID={WorkspaceProfileCurrencyPage.displayName}
>
<FullPageNotFoundView
onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS_WORKSPACES)}
shouldShow={(_.isEmpty(policy) && !isLoadingReportData) || !PolicyUtils.isPolicyAdmin(policy) || PolicyUtils.isPendingDeletePolicy(policy)}
subtitleKey={_.isEmpty(policy) ? undefined : 'workspace.common.notAuthorized'}
shouldShow={(isEmptyObject(policy) && !isLoadingReportData) || !PolicyUtils.isPolicyAdmin(policy) || PolicyUtils.isPendingDeletePolicy(policy)}
subtitleKey={isEmptyObject(policy) ? undefined : 'workspace.common.notAuthorized'}
>
<HeaderWithBackButton
title={translate('workspace.editor.currencyInputLabel')}
Expand All @@ -105,13 +102,10 @@ function WorkspaceSettingsCurrencyPage({currencyList, policy, isLoadingReportDat
);
}

WorkspaceSettingsCurrencyPage.displayName = 'WorkspaceSettingsCurrencyPage';
WorkspaceSettingsCurrencyPage.propTypes = propTypes;
WorkspaceSettingsCurrencyPage.defaultProps = defaultProps;
WorkspaceProfileCurrencyPage.displayName = 'WorkspaceProfileCurrencyPage';

export default compose(
withPolicyAndFullscreenLoading,
withOnyx({
export default withPolicyAndFullscreenLoading(
withOnyx<WorkspaceProfileCurrentPageProps, WorkspaceProfileCurrentPageOnyxProps>({
currencyList: {key: ONYXKEYS.CURRENCY_LIST},
}),
)(WorkspaceSettingsCurrencyPage);
})(WorkspaceProfileCurrencyPage),
);
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import lodashGet from 'lodash/get';
import PropTypes from 'prop-types';
import React, {useCallback} from 'react';
import type {ImageStyle, StyleProp} from 'react-native';
import {Image, ScrollView, StyleSheet, View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
import WorkspaceProfile from '@assets/images/workspace-profile.png';
import Avatar from '@components/Avatar';
import AvatarWithImagePicker from '@components/AvatarWithImagePicker';
Expand All @@ -16,59 +15,46 @@ import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import compose from '@libs/compose';
import Navigation from '@libs/Navigation/Navigation';
import * as PolicyUtils from '@libs/PolicyUtils';
import * as ReportUtils from '@libs/ReportUtils';
import StringUtils from '@libs/StringUtils';
import * as UserUtils from '@libs/UserUtils';
import * as Policy from '@userActions/Policy';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import withPolicy, {policyDefaultProps, policyPropTypes} from './withPolicy';
import type {CurrencyList} from '@src/types/onyx';
import type * as OnyxCommon from '@src/types/onyx/OnyxCommon';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import withPolicy from './withPolicy';
import type {WithPolicyProps} from './withPolicy';
import WorkspacePageWithSections from './WorkspacePageWithSections';

const propTypes = {
type WorkSpaceProfilePageOnyxProps = {
/** Constant, list of available currencies */
currencyList: PropTypes.objectOf(
PropTypes.shape({
/** Symbol of the currency */
symbol: PropTypes.string.isRequired,
}),
),

/** The route object passed to this page from the navigator */
route: PropTypes.shape({
/** Each parameter passed via the URL */
params: PropTypes.shape({
/** The policyID that is being configured */
policyID: PropTypes.string.isRequired,
}).isRequired,
}).isRequired,

...policyPropTypes,
currencyList: OnyxEntry<CurrencyList>;
};

const defaultProps = {
currencyList: {},
...policyDefaultProps,
};
type WorkSpaceProfilePageProps = WithPolicyProps & WorkSpaceProfilePageOnyxProps;

function WorkspaceProfilePage({policy, currencyList, route}) {
function WorkspaceProfilePage({policy, currencyList = {}, route}: WorkSpaceProfilePageProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const {isSmallScreenWidth} = useWindowDimensions();

const formattedCurrency = !_.isEmpty(policy) && !_.isEmpty(currencyList) && !!policy.outputCurrency ? `${policy.outputCurrency} - ${currencyList[policy.outputCurrency].symbol}` : '';
const outputCurrency = policy?.outputCurrency ?? '';
const currencySymbol = currencyList?.[outputCurrency]?.symbol ?? '';
const formattedCurrency = !isEmptyObject(policy) && !isEmptyObject(currencyList) ? `${outputCurrency} - ${currencySymbol}` : '';

const onPressCurrency = useCallback(() => Navigation.navigate(ROUTES.WORKSPACE_PROFILE_CURRENCY.getRoute(policy.id)), [policy.id]);
const onPressName = useCallback(() => Navigation.navigate(ROUTES.WORKSPACE_PROFILE_NAME.getRoute(policy.id)), [policy.id]);
const onPressDescription = useCallback(() => Navigation.navigate(ROUTES.WORKSPACE_PROFILE_DESCRIPTION.getRoute(policy.id)), [policy.id]);
const onPressCurrency = useCallback(() => Navigation.navigate(ROUTES.WORKSPACE_PROFILE_CURRENCY.getRoute(policy?.id ?? '')), [policy?.id]);
const onPressName = useCallback(() => Navigation.navigate(ROUTES.WORKSPACE_PROFILE_NAME.getRoute(policy?.id ?? '')), [policy?.id]);
const onPressDescription = useCallback(() => Navigation.navigate(ROUTES.WORKSPACE_PROFILE_DESCRIPTION.getRoute(policy?.id ?? '')), [policy?.id]);

const policyName = lodashGet(policy, 'name', '');
const policyDescription = lodashGet(policy, 'description', '');
const policyName = policy?.name ?? '';
const policyDescription = policy?.description ?? '';
const readOnly = !PolicyUtils.isPolicyAdmin(policy);
const imageStyle = isSmallScreenWidth ? [styles.mhv12, styles.mhn5] : [styles.mhv8, styles.mhn8];
const imageStyle: StyleProp<ImageStyle> = isSmallScreenWidth ? [styles.mhv12, styles.mhn5] : [styles.mhv8, styles.mhn8];

return (
<WorkspacePageWithSections
Expand All @@ -81,26 +67,29 @@ function WorkspaceProfilePage({policy, currencyList, route}) {
shouldShowNonAdmin
icon={Illustrations.House}
>
{(hasVBA) => (
{(hasVBA?: boolean) => (
<ScrollView>
<View style={[styles.flex1, isSmallScreenWidth ? styles.workspaceSectionMobile : styles.workspaceSection]}>
<Section isCentralPane>
<Section
isCentralPane
title=""
>
<Image
style={StyleSheet.flatten([styles.br4, styles.wAuto, styles.h68, imageStyle])}
source={WorkspaceProfile}
resizeMode="cover"
/>
<AvatarWithImagePicker
onViewPhotoPress={() => Navigation.navigate(ROUTES.WORKSPACE_AVATAR.getRoute(policy.id))}
source={lodashGet(policy, 'avatar')}
onViewPhotoPress={() => Navigation.navigate(ROUTES.WORKSPACE_AVATAR.getRoute(policy?.id ?? ''))}
source={policy?.avatar ?? ''}
size={CONST.AVATAR_SIZE.XLARGE}
avatarStyle={styles.avatarXLarge}
enablePreview
DefaultAvatar={() => (
<Avatar
containerStyles={styles.avatarXLarge}
imageStyles={[styles.avatarXLarge, styles.alignSelfCenter]}
source={policy.avatar ? policy.avatar : ReportUtils.getDefaultWorkspaceAvatar(policyName)}
source={policy?.avatar ? policy?.avatar : ReportUtils.getDefaultWorkspaceAvatar(policyName)}
fallbackIcon={Expensicons.FallbackWorkspaceAvatar}
size={CONST.AVATAR_SIZE.XLARGE}
name={policyName}
Expand All @@ -110,20 +99,21 @@ function WorkspaceProfilePage({policy, currencyList, route}) {
type={CONST.ICON_TYPE_WORKSPACE}
fallbackIcon={Expensicons.FallbackWorkspaceAvatar}
style={[styles.mb3, isSmallScreenWidth ? styles.mtn17 : styles.mtn20, styles.alignItemsStart, styles.sectionMenuItemTopDescription]}
isUsingDefaultAvatar={!lodashGet(policy, 'avatar', null)}
onImageSelected={(file) => Policy.updateWorkspaceAvatar(lodashGet(policy, 'id', ''), file)}
onImageRemoved={() => Policy.deleteWorkspaceAvatar(lodashGet(policy, 'id', ''))}
isUsingDefaultAvatar={!policy?.avatar ?? null}
onImageSelected={(file: File) => Policy.updateWorkspaceAvatar(policy?.id ?? '', file)}
onImageRemoved={() => Policy.deleteWorkspaceAvatar(policy?.id ?? '')}
editorMaskImage={Expensicons.ImageCropSquareMask}
pendingAction={lodashGet(policy, 'pendingFields.avatar', null)}
errors={lodashGet(policy, 'errorFields.avatar', null)}
onErrorClose={() => Policy.clearAvatarErrors(policy.id)}
previewSource={UserUtils.getFullSizeAvatar(policy.avatar, '')}
pendingAction={policy?.pendingFields?.avatar ?? null}
errors={policy?.errorFields?.avatar ?? null}
onErrorClose={() => Policy.clearAvatarErrors(policy?.id ?? '')}
previewSource={UserUtils.getFullSizeAvatar(policy?.avatar ?? '')}
headerTitle={translate('workspace.common.workspaceAvatar')}
originalFileName={policy.originalFileName}
originalFileName={policy?.originalFileName}
disabled={readOnly}
disabledStyle={styles.cursorDefault}
errorRowStyles={undefined}
/>
<OfflineWithFeedback pendingAction={lodashGet(policy, 'pendingFields.generalSettings')}>
<OfflineWithFeedback pendingAction={policy?.pendingFields?.generalSettings as OnyxCommon.PendingAction}>
<MenuItemWithTopDescription
title={policyName}
titleStyle={styles.workspaceTitleStyle}
Expand All @@ -136,8 +126,8 @@ function WorkspaceProfilePage({policy, currencyList, route}) {
shouldUseDefaultCursorWhenDisabled
/>
</OfflineWithFeedback>
{(!_.isEmpty(policy.description) || !readOnly) && (
<OfflineWithFeedback pendingAction={lodashGet(policy, 'pendingFields.description')}>
{(!StringUtils.isEmptyString(policy?.description ?? '') || !readOnly) && (
<OfflineWithFeedback pendingAction={policy?.pendingFields?.description as OnyxCommon.PendingAction}>
<MenuItemWithTopDescription
title={policyDescription}
description={translate('workspace.editor.descriptionInputLabel')}
Expand All @@ -151,13 +141,13 @@ function WorkspaceProfilePage({policy, currencyList, route}) {
/>
</OfflineWithFeedback>
)}
<OfflineWithFeedback pendingAction={lodashGet(policy, 'pendingFields.generalSettings')}>
<OfflineWithFeedback pendingAction={policy?.pendingFields?.generalSettings as OnyxCommon.PendingAction}>
<View>
<MenuItemWithTopDescription
title={formattedCurrency}
description={translate('workspace.editor.currencyInputLabel')}
shouldShowRightIcon={!readOnly}
disabled={hasVBA || readOnly}
disabled={hasVBA ?? readOnly}
wrapperStyle={styles.sectionMenuItemTopDescription}
onPress={onPressCurrency}
shouldGreyOutWhenDisabled={false}
Expand All @@ -176,13 +166,10 @@ function WorkspaceProfilePage({policy, currencyList, route}) {
);
}

WorkspaceProfilePage.propTypes = propTypes;
WorkspaceProfilePage.defaultProps = defaultProps;
WorkspaceProfilePage.displayName = 'WorkspaceProfilePage';

export default compose(
withPolicy,
withOnyx({
export default withPolicy(
withOnyx<WorkSpaceProfilePageProps, WorkSpaceProfilePageOnyxProps>({
currencyList: {key: ONYXKEYS.CURRENCY_LIST},
}),
)(WorkspaceProfilePage);
})(WorkspaceProfilePage),
);
3 changes: 3 additions & 0 deletions src/types/onyx/Currency.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,7 @@ type Currency = {
cacheBurst?: number;
};

type CurrencyList = Record<string, Currency | null>;

export default Currency;
export type {CurrencyList};
Loading

0 comments on commit 6754f15

Please sign in to comment.