Skip to content

Commit

Permalink
Merge pull request Expensify#15672 from abdulrahuman5196/inviteMessag…
Browse files Browse the repository at this point in the history
…ePage
  • Loading branch information
thienlnam authored Apr 28, 2023
2 parents 73c05c0 + 781e6e3 commit 4492f90
Show file tree
Hide file tree
Showing 17 changed files with 448 additions and 131 deletions.
2 changes: 2 additions & 0 deletions src/ONYXKEYS.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ export default {
DOWNLOAD: 'download_',
POLICY: 'policy_',
POLICY_MEMBER_LIST: 'policyMemberList_',
WORKSPACE_INVITE_MEMBERS_DRAFT: 'workspaceInviteMembersDraft_',
REPORT: 'report_',
REPORT_ACTIONS: 'reportActions_',
REPORT_ACTIONS_DRAFTS: 'reportActionsDrafts_',
Expand Down Expand Up @@ -187,6 +188,7 @@ export default {
PROFILE_SETTINGS_FORM: 'profileSettingsForm',
DISPLAY_NAME_FORM: 'displayNameForm',
LEGAL_NAME_FORM: 'legalNameForm',
WORKSPACE_INVITE_MESSAGE_FORM: 'workspaceInviteMessageForm',
DATE_OF_BIRTH_FORM: 'dateOfBirthForm',
HOME_ADDRESS_FORM: 'homeAddressForm',
NEW_ROOM_FORM: 'newRoomForm',
Expand Down
2 changes: 2 additions & 0 deletions src/ROUTES.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ export default {
WORKSPACE_NEW: 'workspace/new',
WORKSPACE_INITIAL: 'workspace/:policyID',
WORKSPACE_INVITE: 'workspace/:policyID/invite',
WORKSPACE_INVITE_MESSAGE: 'workspace/:policyID/invite-message',
WORKSPACE_SETTINGS: 'workspace/:policyID/settings',
WORKSPACE_CARD: 'workspace/:policyID/card',
WORKSPACE_REIMBURSE: 'workspace/:policyID/reimburse',
Expand All @@ -132,6 +133,7 @@ export default {
WORKSPACE_NEW_ROOM: 'workspace/new-room',
getWorkspaceInitialRoute: policyID => `workspace/${policyID}`,
getWorkspaceInviteRoute: policyID => `workspace/${policyID}/invite`,
getWorkspaceInviteMessageRoute: policyID => `workspace/${policyID}/invite-message`,
getWorkspaceSettingsRoute: policyID => `workspace/${policyID}/settings`,
getWorkspaceCardRoute: policyID => `workspace/${policyID}/card`,
getWorkspaceReimburseRoute: policyID => `workspace/${policyID}/reimburse`,
Expand Down
5 changes: 5 additions & 0 deletions src/components/Form.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ const propTypes = {
/** Container styles */
style: stylePropTypes,

/** Custom content to display in the footer after submit button */
footerContent: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),

...withLocalizePropTypes,
};

Expand All @@ -84,6 +87,7 @@ const defaultProps = {
enabledWhenOffline: false,
isSubmitActionDangerous: false,
scrollContextEnabled: false,
footerContent: null,
style: [],
};

Expand Down Expand Up @@ -332,6 +336,7 @@ class Form extends React.Component {
isLoading={this.props.formState.isLoading}
message={_.isEmpty(this.props.formState.errorFields) ? this.getErrorMessage() : null}
onSubmit={this.submit}
footerContent={this.props.footerContent}
onFixTheErrorsLinkPressed={() => {
const errors = !_.isEmpty(this.state.errors) ? this.state.errors : this.props.formState.errorFields;
const focusKey = _.find(_.keys(this.inputRefs), key => _.keys(errors).includes(key));
Expand Down
48 changes: 29 additions & 19 deletions src/components/FormAlertWithSubmitButton.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import {View} from 'react-native';
import styles from '../styles/styles';
import Button from './Button';
import FormAlertWrapper from './FormAlertWrapper';
Expand Down Expand Up @@ -41,6 +42,9 @@ const propTypes = {

/** Whether the form submit action is dangerous */
isSubmitActionDangerous: PropTypes.bool,

/** Custom content to display in the footer after submit button */
footerContent: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
};

const defaultProps = {
Expand All @@ -53,6 +57,7 @@ const defaultProps = {
enabledWhenOffline: false,
disablePressOnEnter: false,
isSubmitActionDangerous: false,
footerContent: null,
};

const FormAlertWithSubmitButton = props => (
Expand All @@ -63,25 +68,30 @@ const FormAlertWithSubmitButton = props => (
message={props.message}
onFixTheErrorsLinkPressed={props.onFixTheErrorsLinkPressed}
>
{isOffline => ((isOffline && !props.enabledWhenOffline) ? (
<Button
success
isDisabled
text={props.buttonText}
style={[styles.mb3]}
danger={props.isSubmitActionDangerous}
/>
) : (
<Button
success
pressOnEnter={!props.disablePressOnEnter}
text={props.buttonText}
onPress={props.onSubmit}
isDisabled={props.isDisabled}
isLoading={props.isLoading}
danger={props.isSubmitActionDangerous}
/>
))}
{isOffline => (
<View>
{(isOffline && !props.enabledWhenOffline) ? (
<Button
success
isDisabled
text={props.buttonText}
style={[styles.mb3]}
danger={props.isSubmitActionDangerous}
/>
) : (
<Button
success
pressOnEnter={!props.disablePressOnEnter}
text={props.buttonText}
onPress={props.onSubmit}
isDisabled={props.isDisabled}
isLoading={props.isLoading}
danger={props.isSubmitActionDangerous}
/>
)}
{props.footerContent}
</View>
)}
</FormAlertWrapper>
);

Expand Down
7 changes: 5 additions & 2 deletions src/components/MenuItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ const defaultProps = {
brickRoadIndicator: '',
floatRightAvatars: [],
shouldStackHorizontally: false,
avatarSize: undefined,
shouldBlockSelection: false,
};

Expand All @@ -77,6 +78,8 @@ const MenuItem = (props) => {
props.title ? descriptionVerticalMargin : undefined,
]);

const fallbackAvatarSize = props.viewMode === CONST.OPTION_MODE.COMPACT ? CONST.AVATAR_SIZE.SMALL : CONST.AVATAR_SIZE.DEFAULT;

return (
<PressableWithSecondaryInteraction
onPress={(e) => {
Expand Down Expand Up @@ -189,12 +192,12 @@ const MenuItem = (props) => {
</View>
)}
{!_.isEmpty(props.floatRightAvatars) && (
<View style={[styles.justifyContentCenter, (props.brickRoadIndicator ? styles.mr4 : styles.mr3)]}>
<View style={[styles.justifyContentCenter, (props.brickRoadIndicator ? styles.mr2 : undefined)]}>
<MultipleAvatars
isHovered={hovered}
isPressed={pressed}
icons={props.floatRightAvatars}
size={props.viewMode === CONST.OPTION_MODE.COMPACT ? CONST.AVATAR_SIZE.SMALL : CONST.AVATAR_SIZE.DEFAULT}
size={props.avatarSize || fallbackAvatarSize}
fallbackIcon={defaultWorkspaceAvatars.WorkspaceBuilding}
shouldStackHorizontally={props.shouldStackHorizontally}
/>
Expand Down
62 changes: 51 additions & 11 deletions src/components/MultipleAvatars.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,12 @@ const defaultProps = {
};

const MultipleAvatars = (props) => {
const avatarContainerStyles = props.size === CONST.AVATAR_SIZE.SMALL ? styles.emptyAvatarSmall : styles.emptyAvatar;
let avatarContainerStyles = props.size === CONST.AVATAR_SIZE.SMALL ? styles.emptyAvatarSmall : styles.emptyAvatar;
const singleAvatarStyles = props.size === CONST.AVATAR_SIZE.SMALL ? styles.singleAvatarSmall : styles.singleAvatar;
const secondAvatarStyles = [
props.size === CONST.AVATAR_SIZE.SMALL ? styles.secondAvatarSmall : styles.secondAvatar,
...props.secondAvatarStyle,
];
const horizontalStyles = [styles.horizontalStackedAvatar4, styles.horizontalStackedAvatar3, styles.horizontalStackedAvatar2, styles.horizontalStackedAvatar1];

if (!props.icons.length) {
return null;
Expand All @@ -83,21 +82,49 @@ const MultipleAvatars = (props) => {
);
}

const oneAvatarSize = StyleUtils.getAvatarStyle(props.size);
const oneAvatarBorderWidth = StyleUtils.getAvatarBorderWidth(props.size);
const overlapSize = oneAvatarSize.width / 3;

if (props.shouldStackHorizontally) {
let width;

// Height of one avatar + border space
const height = oneAvatarSize.height + (2 * oneAvatarBorderWidth);
if (props.icons.length > 4) {
// Width of overlapping avatars + border space
width = (oneAvatarSize.width * 3) + (oneAvatarBorderWidth * 8);
} else {
// one avatar width + overlaping avatar sizes + border space
width = oneAvatarSize.width + (overlapSize * 2 * (props.icons.length - 1)) + (oneAvatarBorderWidth * (props.icons.length * 2));
}
avatarContainerStyles = StyleUtils.combineStyles([
styles.alignItemsCenter,
styles.flexRow,
StyleUtils.getHeight(height),
StyleUtils.getWidthStyle(width),
]);
}

return (
<View style={avatarContainerStyles}>
{props.shouldStackHorizontally ? (
<>
{
_.map([...props.icons].splice(0, 4).reverse(), (icon, index) => (
_.map([...props.icons].splice(0, 4), (icon, index) => (
<View
key={`stackedAvatars-${index}`}
style={[styles.horizontalStackedAvatar, StyleUtils.getHorizontalStackedAvatarBorderStyle(props.isHovered, props.isPressed), horizontalStyles[index],
StyleUtils.getAvatarBorderRadius(props.size, icon.type)]}
style={[styles.justifyContentCenter,
styles.alignItemsCenter,
StyleUtils.getHorizontalStackedAvatarBorderStyle(props.isHovered, props.isPressed),
StyleUtils.getHorizontalStackedAvatarStyle(index, overlapSize, oneAvatarBorderWidth, oneAvatarSize.width),
(icon.type === CONST.ICON_TYPE_WORKSPACE ? StyleUtils.getAvatarBorderRadius(props.size, icon.type) : {}),
]}
>
<Avatar
source={icon.source || props.fallbackIcon}
fill={themeColors.iconSuccessFill}
size={CONST.AVATAR_SIZE.SMALLER}
size={props.size}
name={icon.name}
type={icon.type}
/>
Expand All @@ -113,13 +140,26 @@ const MultipleAvatars = (props) => {

// Set overlay background color with RGBA value so that the text will not inherit opacity
StyleUtils.getBackgroundColorWithOpacityStyle(themeColors.overlay, variables.overlayOpacity),
styles.horizontalStackedAvatar4Overlay,
StyleUtils.getAvatarBorderRadius(props.size, props.icons[3].type),
StyleUtils.getHorizontalStackedOverlayAvatarStyle(oneAvatarSize, oneAvatarBorderWidth),
(props.icons[3].type === CONST.ICON_TYPE_WORKSPACE ? StyleUtils.getAvatarBorderRadius(props.size, props.icons[3].type) : {}),
]}
>
<Text style={styles.avatarInnerTextSmall}>
{`+${props.icons.length - 4}`}
</Text>
<View
style={[styles.justifyContentCenter,
styles.alignItemsCenter,
StyleUtils.getHeight(oneAvatarSize.height),
StyleUtils.getWidthStyle(oneAvatarSize.width),
]}
>
<Text
style={[
styles.avatarInnerTextSmall,
StyleUtils.getAvatarExtraFontSizeStyle(props.size),
]}
>
{`+${props.icons.length - 4}`}
</Text>
</View>
</View>
)}
</>
Expand Down
4 changes: 4 additions & 0 deletions src/components/menuItemPropTypes.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import PropTypes from 'prop-types';
import _ from 'underscore';
import CONST from '../CONST';
import stylePropTypes from '../styles/stylePropTypes';
import avatarPropTypes from './avatarPropTypes';
Expand Down Expand Up @@ -89,6 +90,9 @@ const propTypes = {
/** Prop to identify if we should load avatars vertically instead of diagonally */
shouldStackHorizontally: PropTypes.bool,

/** Prop to represent the size of the avatar images to be shown */
avatarSize: PropTypes.oneOf(_.values(CONST.AVATAR_SIZE)),

/** The function that should be called when this component is LongPressed or right-clicked. */
onSecondaryInteraction: PropTypes.func,

Expand Down
10 changes: 8 additions & 2 deletions src/languages/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -1055,11 +1055,17 @@ export default {
},
invite: {
invitePeople: 'Invite new members',
personalMessagePrompt: 'Add a personal message (optional)',
genericFailureMessage: 'An error occurred inviting the user to the workspace, please try again.',
welcomeNote: ({workspaceName}) => `You have been invited to ${workspaceName || 'a workspace'}! Download the Expensify mobile app at use.expensify.com/download to start tracking your expenses.`,
pleaseEnterValidLogin: `Please ensure the email or phone number is valid (e.g. ${CONST.EXAMPLE_PHONE_NUMBER}).`,
},
inviteMessage: {
inviteMessageTitle: 'Add message',
inviteMessagePrompt: 'Make your invitation extra special by adding a message below',
personalMessagePrompt: 'Message',
genericFailureMessage: 'An error occurred inviting the user to the workspace, please try again.',
inviteNoMembersError: 'Please select at least one member to invite',
welcomeNote: ({workspaceName}) => `You have been invited to ${workspaceName || 'a workspace'}! Download the Expensify mobile app at use.expensify.com/download to start tracking your expenses.`,
},
editor: {
nameInputLabel: 'Name',
nameInputHelpText: 'This is the name you will see on your workspace.',
Expand Down
10 changes: 8 additions & 2 deletions src/languages/es.js
Original file line number Diff line number Diff line change
Expand Up @@ -1056,11 +1056,17 @@ export default {
},
invite: {
invitePeople: 'Invitar nuevos miembros',
personalMessagePrompt: 'Agregar un mensaje personal (Opcional)',
genericFailureMessage: 'Se produjo un error al invitar al usuario al espacio de trabajo. Vuelva a intentarlo..',
welcomeNote: ({workspaceName}) => `¡Has sido invitado a ${workspaceName}! Descargue la aplicación móvil Expensify en use.expensify.com/download para comenzar a rastrear sus gastos.`,
pleaseEnterValidLogin: `Asegúrese de que el correo electrónico o el número de teléfono sean válidos (p. ej. ${CONST.EXAMPLE_PHONE_NUMBER}).`,
},
inviteMessage: {
inviteMessageTitle: 'Añadir un mensaje',
inviteMessagePrompt: 'Añadir un mensaje para hacer tu invitación destacar',
personalMessagePrompt: 'Mensaje',
inviteNoMembersError: 'Por favor, selecciona al menos un miembro a invitar',
genericFailureMessage: 'Se produjo un error al invitar al usuario al espacio de trabajo. Vuelva a intentarlo..',
welcomeNote: ({workspaceName}) => `¡Has sido invitado a ${workspaceName}! Descargue la aplicación móvil Expensify en use.expensify.com/download para comenzar a rastrear sus gastos.`,
},
editor: {
nameInputLabel: 'Nombre',
nameInputHelpText: 'Este es el nombre que verás en tu espacio de trabajo.',
Expand Down
7 changes: 7 additions & 0 deletions src/libs/Navigation/AppNavigator/ModalStackNavigators.js
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,13 @@ const SettingsModalStackNavigator = createModalStackNavigator([
},
name: 'Workspace_Invite',
},
{
getComponent: () => {
const WorkspaceInviteMessagePage = require('../../../pages/workspace/WorkspaceInviteMessagePage').default;
return WorkspaceInviteMessagePage;
},
name: 'Workspace_Invite_Message',
},
{
getComponent: () => {
const WorkspaceNewRoomPage = require('../../../pages/workspace/WorkspaceNewRoomPage').default;
Expand Down
3 changes: 3 additions & 0 deletions src/libs/Navigation/linkingConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,9 @@ export default {
Workspace_Invite: {
path: ROUTES.WORKSPACE_INVITE,
},
Workspace_Invite_Message: {
path: ROUTES.WORKSPACE_INVITE_MESSAGE,
},
Workspace_NewRoom: {
path: ROUTES.WORKSPACE_NEW_ROOM,
},
Expand Down
5 changes: 5 additions & 0 deletions src/libs/actions/Policy.js
Original file line number Diff line number Diff line change
Expand Up @@ -1124,6 +1124,10 @@ function openWorkspaceInvitePage(policyID, clientMemberEmails) {
});
}

function setWorkspaceInviteMembersDraft(policyID, memberEmails) {
Onyx.set(`${ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MEMBERS_DRAFT}${policyID}`, memberEmails);
}

/**
*
* @param {String} reportID
Expand Down Expand Up @@ -1183,6 +1187,7 @@ export {
openWorkspaceMembersPage,
openWorkspaceInvitePage,
removeWorkspace,
setWorkspaceInviteMembersDraft,
isPolicyOwner,
leaveRoom,
};
2 changes: 2 additions & 0 deletions src/pages/settings/InitialSettingsPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ class InitialSettingsPage extends React.Component {
action: () => { Navigation.navigate(ROUTES.SETTINGS_WORKSPACES); },
floatRightAvatars: policiesAvatars,
shouldStackHorizontally: true,
avatarSize: CONST.AVATAR_SIZE.SMALLER,
brickRoadIndicator: policyBrickRoadIndicator,
},
{
Expand Down Expand Up @@ -246,6 +247,7 @@ class InitialSettingsPage extends React.Component {
brickRoadIndicator={item.brickRoadIndicator}
floatRightAvatars={item.floatRightAvatars}
shouldStackHorizontally={item.shouldStackHorizontally}
avatarSize={item.avatarSize}
ref={this.popoverAnchor}
shouldBlockSelection={Boolean(item.link)}
onSecondaryInteraction={!_.isEmpty(item.link) ? e => ReportActionContextMenu.showContextMenu(CONTEXT_MENU_TYPES.LINK, e, item.link, this.popoverAnchor.current) : undefined}
Expand Down
Loading

0 comments on commit 4492f90

Please sign in to comment.