Skip to content
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

Add Share page for Workspace Profile #36907

Merged
4 changes: 4 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,10 @@ const ROUTES = {
route: 'workspace/:policyID/profile/description',
getRoute: (policyID: string) => `workspace/${policyID}/profile/description` as const,
},
WORKSPACE_PROFILE_SHARE: {
route: 'workspace/:policyID/profile/share',
getRoute: (policyID: string) => `workspace/${policyID}/profile/share` as const,
},
WORKSPACE_AVATAR: {
route: 'workspace/:policyID/avatar',
getRoute: (policyID: string) => `workspace/${policyID}/avatar` as const,
Expand Down
1 change: 1 addition & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ const SCREENS = {
INVITE_MESSAGE: 'Workspace_Invite_Message',
CURRENCY: 'Workspace_Profile_Currency',
DESCRIPTION: 'Workspace_Profile_Description',
SHARE: 'Workspace_Profile_Share',
NAME: 'Workspace_Profile_Name',
},

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ const SettingsModalStackNavigator = createModalStackNavigator<SettingsNavigatorP
[SCREENS.WORKSPACE.INVITE_MESSAGE]: () => require('../../../pages/workspace/WorkspaceInviteMessagePage').default as React.ComponentType,
[SCREENS.WORKSPACE.NAME]: () => require('../../../pages/workspace/WorkspaceNamePage').default as React.ComponentType,
[SCREENS.WORKSPACE.DESCRIPTION]: () => require('../../../pages/workspace/WorkspaceProfileDescriptionPage').default as React.ComponentType,
[SCREENS.WORKSPACE.SHARE]: () => require('../../../pages/workspace/WorkspaceProfileSharePage').default as React.ComponentType,
[SCREENS.WORKSPACE.CURRENCY]: () => require('../../../pages/workspace/WorkspaceProfileCurrencyPage').default as React.ComponentType,
[SCREENS.REIMBURSEMENT_ACCOUNT]: () => require('../../../pages/ReimbursementAccount/ReimbursementAccountPage').default as React.ComponentType,
[SCREENS.GET_ASSISTANCE]: () => require('../../../pages/GetAssistancePage').default as React.ComponentType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type {CentralPaneName} from '@libs/Navigation/types';
import SCREENS from '@src/SCREENS';

const CENTRAL_PANE_TO_RHP_MAPPING: Partial<Record<CentralPaneName, string[]>> = {
[SCREENS.WORKSPACE.PROFILE]: [SCREENS.WORKSPACE.NAME, SCREENS.WORKSPACE.CURRENCY, SCREENS.WORKSPACE.DESCRIPTION],
[SCREENS.WORKSPACE.PROFILE]: [SCREENS.WORKSPACE.NAME, SCREENS.WORKSPACE.CURRENCY, SCREENS.WORKSPACE.DESCRIPTION, SCREENS.WORKSPACE.SHARE],
[SCREENS.WORKSPACE.REIMBURSE]: [SCREENS.WORKSPACE.RATE_AND_UNIT, SCREENS.WORKSPACE.RATE_AND_UNIT_RATE, SCREENS.WORKSPACE.RATE_AND_UNIT_UNIT],
[SCREENS.WORKSPACE.MEMBERS]: [SCREENS.WORKSPACE.INVITE, SCREENS.WORKSPACE.INVITE_MESSAGE],
};
Expand Down
3 changes: 3 additions & 0 deletions src/libs/Navigation/linkingConfig/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,9 @@ const config: LinkingOptions<RootStackParamList>['config'] = {
[SCREENS.WORKSPACE.DESCRIPTION]: {
path: ROUTES.WORKSPACE_PROFILE_DESCRIPTION.route,
},
[SCREENS.WORKSPACE.SHARE]: {
path: ROUTES.WORKSPACE_PROFILE_SHARE.route,
},
[SCREENS.WORKSPACE.RATE_AND_UNIT]: {
path: ROUTES.WORKSPACE_RATE_AND_UNIT.route,
},
Expand Down
1 change: 1 addition & 0 deletions src/libs/Navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ type SettingsNavigatorParamList = {
[SCREENS.WORKSPACE.CURRENCY]: undefined;
[SCREENS.WORKSPACE.NAME]: undefined;
[SCREENS.WORKSPACE.DESCRIPTION]: undefined;
[SCREENS.WORKSPACE.SHARE]: undefined;
[SCREENS.WORKSPACE.RATE_AND_UNIT]: {
policyID: string;
};
Expand Down
14 changes: 14 additions & 0 deletions src/pages/workspace/WorkspaceProfilePage.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,23 @@ import _ from 'underscore';
import WorkspaceProfile from '@assets/images/workspace-profile.png';
import Avatar from '@components/Avatar';
import AvatarWithImagePicker from '@components/AvatarWithImagePicker';
import Button from '@components/Button';
import * as Expensicons from '@components/Icon/Expensicons';
import * as Illustrations from '@components/Icon/Illustrations';
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
import Section from '@components/Section';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import useStyleUtils from '@hooks/useStyleUtils';
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 * as UserUtils from '@libs/UserUtils';
import variables from '@styles/variables';
import * as Policy from '@userActions/Policy';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
Expand Down Expand Up @@ -57,13 +60,15 @@ const defaultProps = {
function WorkspaceProfilePage({policy, currencyList, route}) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const StyleUtils = useStyleUtils();
const {isSmallScreenWidth} = useWindowDimensions();

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

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 onPressShare = useCallback(() => Navigation.navigate(ROUTES.WORKSPACE_PROFILE_SHARE.getRoute(policy.id)), [policy.id]);

const policyName = lodashGet(policy, 'name', '');
const policyDescription = lodashGet(policy, 'description', '');
Expand Down Expand Up @@ -168,6 +173,15 @@ function WorkspaceProfilePage({policy, currencyList, route}) {
</Text>
</View>
</OfflineWithFeedback>
{!readOnly && (
<Button
style={[StyleUtils.getWidthStyle(variables.avatarSizeLarge), styles.mt6, styles.p0]}
text={translate('common.share')}
onPress={onPressShare}
small
narefyev91 marked this conversation as resolved.
Show resolved Hide resolved
shouldUseDefaultHover={false}
narefyev91 marked this conversation as resolved.
Show resolved Hide resolved
/>
)}
</Section>
</View>
</ScrollView>
Expand Down
93 changes: 93 additions & 0 deletions src/pages/workspace/WorkspaceProfileSharePage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import React, {useRef} from 'react';
import {ScrollView, View} from 'react-native';
import type {ImageSourcePropType} from 'react-native';
import expensifyLogo from '@assets/images/expensify-logo-round-transparent.png';
import ContextMenuItem from '@components/ContextMenuItem';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import * as Expensicons from '@components/Icon/Expensicons';
import * as Illustrations from '@components/Icon/Illustrations';
import QRShareWithDownload from '@components/QRShare/QRShareWithDownload';
import type QRShareWithDownloadHandle from '@components/QRShare/QRShareWithDownload/types';
import ScreenWrapper from '@components/ScreenWrapper';
import Section from '@components/Section';
import useEnvironment from '@hooks/useEnvironment';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import Clipboard from '@libs/Clipboard';
import Navigation from '@libs/Navigation/Navigation';
import * as Url from '@libs/Url';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
import DownloadMenuItem from './download';
import withPolicy from './withPolicy';
import type {WithPolicyProps} from './withPolicy';

type Props = WithPolicyProps;

function WorkspaceProfileSharePage({policy}: Props) {
narefyev91 marked this conversation as resolved.
Show resolved Hide resolved
const themeStyles = useThemeStyles();
const {translate} = useLocalize();
const {environmentURL} = useEnvironment();
const qrCodeRef = useRef<QRShareWithDownloadHandle>(null);
const {isSmallScreenWidth} = useWindowDimensions();

const policyName = policy?.name ?? '';
const id = policy?.id ?? '';
const urlWithTrailingSlash = Url.addTrailingForwardSlash(environmentURL);

const url = `${urlWithTrailingSlash}${ROUTES.WORKSPACE_PROFILE.getRoute(id)}`;
return (
<ScreenWrapper
testID={WorkspaceProfileSharePage.displayName}
shouldShowOfflineIndicatorInWideScreen
>
<HeaderWithBackButton
title={translate('common.shareCode')}
narefyev91 marked this conversation as resolved.
Show resolved Hide resolved
onBackButtonPress={() => Navigation.goBack()}
narefyev91 marked this conversation as resolved.
Show resolved Hide resolved
icon={Illustrations.QRCode}
narefyev91 marked this conversation as resolved.
Show resolved Hide resolved
/>
<ScrollView style={[themeStyles.flex1, themeStyles.pt3]}>
<View style={[themeStyles.flex1, isSmallScreenWidth ? themeStyles.workspaceSectionMobile : themeStyles.workspaceSection]}>
<Section
title={translate('shareCodePage.title')}
isCentralPane
childrenStyles={themeStyles.pt5}
titleStyles={themeStyles.accountSettingsSectionTitle}
>
narefyev91 marked this conversation as resolved.
Show resolved Hide resolved
<View style={[isSmallScreenWidth ? themeStyles.workspaceSectionMobile : themeStyles.qrShareSection]}>
<QRShareWithDownload
ref={qrCodeRef}
url={url}
title={policyName}
logo={(policy?.avatar ? policy.avatar : expensifyLogo) as ImageSourcePropType}
logoRatio={CONST.QR.DEFAULT_LOGO_SIZE_RATIO}
logoMarginRatio={CONST.QR.DEFAULT_LOGO_MARGIN_RATIO}
/>
</View>

<View style={themeStyles.mt1}>
<ContextMenuItem
isAnonymousAction
text={translate('qrCodes.copy')}
icon={Expensicons.Copy}
successIcon={Expensicons.Checkmark}
successText={translate('qrCodes.copied')}
onPress={() => Clipboard.setString(url)}
shouldLimitWidth={false}
wrapperStyle={themeStyles.sectionMenuItemTopDescription}
/>

{/* eslint-disable-next-line @typescript-eslint/no-misused-promises */}
<DownloadMenuItem download={() => qrCodeRef.current?.download?.()} />
narefyev91 marked this conversation as resolved.
Show resolved Hide resolved
</View>
</Section>
</View>
</ScrollView>
</ScreenWrapper>
);
}

WorkspaceProfileSharePage.displayName = 'WorkspaceProfileSharePage';

export default withPolicy(WorkspaceProfileSharePage);
24 changes: 24 additions & 0 deletions src/pages/workspace/download/index.native.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from 'react';
import * as Expensicons from '@components/Icon/Expensicons';
import MenuItem from '@components/MenuItem';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import type DownloadQRCodeProps from './types';

function DownloadMenuItem({download}: DownloadQRCodeProps) {
const themeStyles = useThemeStyles();
const {translate} = useLocalize();
return (
<MenuItem
isAnonymousAction
title={translate('common.download')}
icon={Expensicons.Download}
onPress={download}
wrapperStyle={themeStyles.sectionMenuItemTopDescription}
/>
narefyev91 marked this conversation as resolved.
Show resolved Hide resolved
);
}

DownloadMenuItem.displayName = 'DownloadMenuItem';

export default DownloadMenuItem;
10 changes: 10 additions & 0 deletions src/pages/workspace/download/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type DownloadQRCodeProps from './types';

// eslint-disable-next-line @typescript-eslint/no-unused-vars
function DownloadMenuItem({download}: DownloadQRCodeProps) {
return null;
narefyev91 marked this conversation as resolved.
Show resolved Hide resolved
}

DownloadMenuItem.displayName = 'DownloadMenuItem';

export default DownloadMenuItem;
5 changes: 5 additions & 0 deletions src/pages/workspace/download/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
type DownloadQRCodeProps = {
download?: () => void;
};

export default DownloadQRCodeProps;
Loading