diff --git a/assets/images/simple-illustrations/simple-illustration__hotdogstand.svg b/assets/images/simple-illustrations/simple-illustration__hotdogstand.svg new file mode 100644 index 000000000000..732c16a75a2b --- /dev/null +++ b/assets/images/simple-illustrations/simple-illustration__hotdogstand.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/DatePicker/CalendarPicker/index.js b/src/components/DatePicker/CalendarPicker/index.js index a404c4746397..bbdeda6ef84f 100644 --- a/src/components/DatePicker/CalendarPicker/index.js +++ b/src/components/DatePicker/CalendarPicker/index.js @@ -236,7 +236,7 @@ class CalendarPicker extends React.PureComponent { diff --git a/src/components/Icon/Illustrations.ts b/src/components/Icon/Illustrations.ts index 440350895826..c8a14c7aba03 100644 --- a/src/components/Icon/Illustrations.ts +++ b/src/components/Icon/Illustrations.ts @@ -35,6 +35,7 @@ import ConciergeNew from '@assets/images/simple-illustrations/simple-illustratio import CreditCardsNew from '@assets/images/simple-illustrations/simple-illustration__credit-cards.svg'; import EmailAddress from '@assets/images/simple-illustrations/simple-illustration__email-address.svg'; import HandEarth from '@assets/images/simple-illustrations/simple-illustration__handearth.svg'; +import HotDogStand from '@assets/images/simple-illustrations/simple-illustration__hotdogstand.svg'; import InvoiceBlue from '@assets/images/simple-illustrations/simple-illustration__invoice.svg'; import LockOpen from '@assets/images/simple-illustrations/simple-illustration__lockopen.svg'; import Luggage from '@assets/images/simple-illustrations/simple-illustration__luggage.svg'; @@ -60,6 +61,7 @@ export { ConciergeExclamation, CreditCardsBlue, EmailAddress, + HotDogStand, InvoiceOrange, JewelBoxBlue, JewelBoxGreen, diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx index a5e02dccddd3..db150d55f0d2 100644 --- a/src/components/MenuItem.tsx +++ b/src/components/MenuItem.tsx @@ -547,7 +547,7 @@ function MenuItem( {badgeText && ( )} {/* Since subtitle can be of type number, we should allow 0 to be shown */} diff --git a/src/components/Section/IconSection.js b/src/components/Section/IconSection.js new file mode 100644 index 000000000000..307331aa36d6 --- /dev/null +++ b/src/components/Section/IconSection.js @@ -0,0 +1,41 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import {View} from 'react-native'; +import Icon from '@components/Icon'; +import sourcePropTypes from '@components/Image/sourcePropTypes'; +import useThemeStyles from '@hooks/useThemeStyles'; + +const iconSectionPropTypes = { + icon: sourcePropTypes, + IconComponent: PropTypes.IconComponent, + iconContainerStyles: PropTypes.iconContainerStyles, +}; + +const defaultIconSectionPropTypes = { + icon: null, + IconComponent: null, + iconContainerStyles: [], +}; + +function IconSection({icon, IconComponent, iconContainerStyles}) { + const styles = useThemeStyles(); + + return ( + + {Boolean(icon) && ( + + )} + {Boolean(IconComponent) && } + + ); +} + +IconSection.displayName = 'IconSection'; +IconSection.propTypes = iconSectionPropTypes; +IconSection.defaultProps = defaultIconSectionPropTypes; + +export default IconSection; diff --git a/src/components/Section.js b/src/components/Section/index.js similarity index 60% rename from src/components/Section.js rename to src/components/Section/index.js index c204632e2a25..50576abef025 100644 --- a/src/components/Section.js +++ b/src/components/Section/index.js @@ -1,12 +1,17 @@ import PropTypes from 'prop-types'; import React from 'react'; import {View} from 'react-native'; +import sourcePropTypes from '@components/Image/sourcePropTypes'; +import MenuItemList from '@components/MenuItemList'; +import menuItemPropTypes from '@components/menuItemPropTypes'; +import Text from '@components/Text'; import useThemeStyles from '@hooks/useThemeStyles'; -import Icon from './Icon'; -import sourcePropTypes from './Image/sourcePropTypes'; -import MenuItemList from './MenuItemList'; -import menuItemPropTypes from './menuItemPropTypes'; -import Text from './Text'; +import IconSection from './IconSection'; + +const CARD_LAYOUT = { + ICON_ON_TOP: 'iconOnTop', + ICON_ON_RIGHT: 'iconOnRight', +}; const propTypes = { /** An array of props that are pass to individual MenuItem components */ @@ -24,6 +29,10 @@ const propTypes = { /** Icon component */ IconComponent: PropTypes.func, + /** Card layout that affects icon positioning, margins, sizes. */ + // eslint-disable-next-line rulesdir/prefer-underscore-method + cardLayout: PropTypes.oneOf(Object.values(CARD_LAYOUT)), + /** Contents to display inside the section */ children: PropTypes.node, @@ -39,6 +48,9 @@ const propTypes = { // eslint-disable-next-line react/forbid-prop-types subtitleStyles: PropTypes.arrayOf(PropTypes.object), + /** Whether the subtitle should have a muted style */ + subtitleMuted: PropTypes.bool, + /** Customize the Section container */ // eslint-disable-next-line react/forbid-prop-types childrenStyles: PropTypes.arrayOf(PropTypes.object), @@ -53,38 +65,45 @@ const defaultProps = { children: null, icon: null, IconComponent: null, + cardLayout: CARD_LAYOUT.ICON_ON_RIGHT, containerStyles: [], iconContainerStyles: [], titleStyles: [], subtitleStyles: [], + subtitleMuted: false, childrenStyles: [], subtitle: null, }; -function Section({children, childrenStyles, containerStyles, icon, IconComponent, iconContainerStyles, menuItems, subtitle, subtitleStyles, title, titleStyles}) { +function Section({children, childrenStyles, containerStyles, icon, IconComponent, cardLayout, iconContainerStyles, menuItems, subtitle, subtitleStyles, subtitleMuted, title, titleStyles}) { const styles = useThemeStyles(); + return ( <> - + {cardLayout === CARD_LAYOUT.ICON_ON_TOP && ( + + )} + {title} - - {Boolean(icon) && ( - - )} - {Boolean(IconComponent) && } - + {cardLayout === CARD_LAYOUT.ICON_ON_RIGHT && ( + + )} {Boolean(subtitle) && ( - - {subtitle} + + {subtitle} )} @@ -95,9 +114,9 @@ function Section({children, childrenStyles, containerStyles, icon, IconComponent ); } - Section.displayName = 'Section'; Section.propTypes = propTypes; Section.defaultProps = defaultProps; +export {CARD_LAYOUT}; export default Section; diff --git a/src/languages/en.ts b/src/languages/en.ts index fec747ae253b..71d27e341cac 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1489,8 +1489,9 @@ export default { mustBeOnlineToViewMembers: 'You must be online in order to view members of this workspace.', }, emptyWorkspace: { - title: 'Create a new workspace', - subtitle: "Workspaces are where you'll chat with your team, reimburse expenses, issue cards, send invoices, pay bills, and more — all in one place.", + title: 'Create a workspace', + subtitle: 'Manage business expenses, issue cards, send invoices, and more.', + createAWorkspaceCTA: 'Get Started', features: { trackAndCollect: 'Track and collect receipts', companyCards: 'Company credit cards', diff --git a/src/languages/es.ts b/src/languages/es.ts index 73ee616d57bb..85223c559f81 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1512,8 +1512,9 @@ export default { mustBeOnlineToViewMembers: 'Debes estar en línea para poder ver los miembros de este espacio de trabajo.', }, emptyWorkspace: { - title: 'Crear un nuevo espacio de trabajo', - subtitle: 'En los espacios de trabajo es donde puedes chatear con tu equipo, reembolsar gastos, emitir tarjetas, enviar y pagar facturas y mas — todo en un mismo lugar', + title: 'Crea un espacio de trabajo', + subtitle: 'Administra gastos de empresa, emite tarjetas, envía facturas y mucho más.', + createAWorkspaceCTA: 'Comenzar', features: { trackAndCollect: 'Organiza recibos', companyCards: 'Tarjetas de crédito corporativas', diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 9a3099ba6c02..4847eee2c8c6 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -735,7 +735,7 @@ function getMemberChangeMessageFragment(reportAction: OnyxEntry): .map((messageElement) => { switch (messageElement.kind) { case 'userMention': - return ``; + return `${messageElement.content}`; case 'roomReference': return `${messageElement.roomName}`; default: diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 135e616f7691..0ea325cacf00 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -24,6 +24,7 @@ import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as Pusher from '@libs/Pusher/pusher'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; +import shouldSkipDeepLinkNavigation from '@libs/shouldSkipDeepLinkNavigation'; import * as UserUtils from '@libs/UserUtils'; import Visibility from '@libs/Visibility'; import CONFIG from '@src/CONFIG'; @@ -2032,6 +2033,10 @@ function openReportFromDeepLink(url: string, isAuthenticated: boolean) { return; } + if (shouldSkipDeepLinkNavigation(route)) { + return; + } + Navigation.navigate(route as Route, CONST.NAVIGATION.ACTION_TYPE.PUSH); }); }); diff --git a/src/libs/shouldSkipDeepLinkNavigation/index.desktop.ts b/src/libs/shouldSkipDeepLinkNavigation/index.desktop.ts new file mode 100644 index 000000000000..0a2d7f533e74 --- /dev/null +++ b/src/libs/shouldSkipDeepLinkNavigation/index.desktop.ts @@ -0,0 +1,12 @@ +import ROUTES from '@src/ROUTES'; + +export default function shouldSkipDeepLinkNavigation(route: string) { + // When deep-linking to desktop app with `transition` route we don't want to call navigate + // on the route because it will display an infinite loading indicator. + // See issue: https://github.com/Expensify/App/issues/33149 + if (route.includes(ROUTES.TRANSITION_BETWEEN_APPS)) { + return true; + } + + return false; +} diff --git a/src/libs/shouldSkipDeepLinkNavigation/index.ts b/src/libs/shouldSkipDeepLinkNavigation/index.ts new file mode 100644 index 000000000000..8a2d8035507f --- /dev/null +++ b/src/libs/shouldSkipDeepLinkNavigation/index.ts @@ -0,0 +1,5 @@ +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export default function shouldSkipDeepLinkNavigation(route: string) { + // no-op for all other platforms + return false; +} diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.js b/src/pages/home/report/ContextMenu/ContextMenuActions.js index a5e946e6a78a..3682ef602a2b 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.js +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.js @@ -35,6 +35,20 @@ function getActionText(reportAction) { return lodashGet(message, 'html', ''); } +/** + * Sets the HTML string to Clipboard. + * @param {String} content + */ +function setClipboardMessage(content) { + const parser = new ExpensiMark(); + if (!Clipboard.canSetHtml()) { + Clipboard.setString(parser.htmlToMarkdown(content)); + } else { + const plainText = parser.htmlToText(content); + Clipboard.setHtml(content, plainText); + } +} + const CONTEXT_MENU_TYPES = { LINK: 'LINK', REPORT_ACTION: 'REPORT_ACTION', @@ -291,19 +305,13 @@ export default [ const taskPreviewMessage = TaskUtils.getTaskCreatedMessage(reportAction); Clipboard.setString(taskPreviewMessage); } else if (ReportActionsUtils.isMemberChangeAction(reportAction)) { - const logMessage = ReportActionsUtils.getMemberChangeMessagePlainText(reportAction); - Clipboard.setString(logMessage); + const logMessage = ReportActionsUtils.getMemberChangeMessageFragment(reportAction).html; + setClipboardMessage(logMessage); } else if (ReportActionsUtils.isSubmittedExpenseAction(reportAction)) { const submittedMessage = _.reduce(reportAction.message, (acc, curr) => `${acc}${curr.text}`, ''); Clipboard.setString(submittedMessage); } else if (content) { - const parser = new ExpensiMark(); - if (!Clipboard.canSetHtml()) { - Clipboard.setString(parser.htmlToMarkdown(content)); - } else { - const plainText = parser.htmlToText(content); - Clipboard.setHtml(content, plainText); - } + setClipboardMessage(content); } } diff --git a/src/pages/settings/Profile/CustomStatus/StatusClearAfterPage.js b/src/pages/settings/Profile/CustomStatus/StatusClearAfterPage.js index 8dda0ea0025d..84ca74c2842f 100644 --- a/src/pages/settings/Profile/CustomStatus/StatusClearAfterPage.js +++ b/src/pages/settings/Profile/CustomStatus/StatusClearAfterPage.js @@ -166,6 +166,7 @@ function StatusClearAfterPage({currentUserPersonalDetails, customStatus}) { key={`${index}+${item.value}`} onSelectRow={() => updateMode(item)} showTooltip={false} + isFocused={item.isSelected} /> )), [statusType, updateMode], diff --git a/src/pages/workspace/card/WorkspaceCardCreateAWorkspace.tsx b/src/pages/workspace/card/WorkspaceCardCreateAWorkspace.tsx new file mode 100644 index 000000000000..9e45bd143e7e --- /dev/null +++ b/src/pages/workspace/card/WorkspaceCardCreateAWorkspace.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import Button from '@components/Button'; +import * as Illustrations from '@components/Icon/Illustrations'; +import Section, {CARD_LAYOUT} from '@components/Section'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; + +function WorkspaceCardCreateAWorkspace() { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + + return ( +
+
+ ); +} + +WorkspaceCardCreateAWorkspace.displayName = 'WorkspaceCardNoVBAView'; + +export default WorkspaceCardCreateAWorkspace; diff --git a/src/styles/index.ts b/src/styles/index.ts index aececf93beb9..074e70622dac 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -938,10 +938,14 @@ const styles = (theme: ThemeColors) => overflow: 'hidden', }, - calendarDayContainerSelected: { + buttonDefaultBG: { backgroundColor: theme.buttonDefaultBG, }, + buttonHoveredBG: { + backgroundColor: theme.buttonHoveredBG, + }, + autoGrowHeightInputContainer: (textInputHeight: number, minHeight: number, maxHeight: number) => ({ height: lodashClamp(textInputHeight, minHeight, maxHeight), @@ -1952,10 +1956,6 @@ const styles = (theme: ThemeColors) => alignSelf: 'flex-end', }, - hoveredButton: { - backgroundColor: theme.buttonHoveredBG, - }, - composerSizeButton: { alignSelf: 'center', height: 32,