Skip to content

Commit

Permalink
Merge pull request #12 from software-mansion-labs/ideal-nav/all-settings
Browse files Browse the repository at this point in the history
Redesign settings Sidebar
  • Loading branch information
adamgrzybowski committed Dec 20, 2023
2 parents 06eedf9 + 0cb565c commit 96a2875
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 81 deletions.
5 changes: 5 additions & 0 deletions assets/images/cards-and-domains.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions assets/images/home.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,8 @@ const CONST = {
// Use Environment.getEnvironmentURL to get the complete URL with port number
DEV_NEW_EXPENSIFY_URL: 'https://dev.new.expensify.com:',
EXPENSIFY_INBOX_URL: 'https://www.expensify.com/inbox',
ADMIN_POLICIES_URL: 'admin_policies',
ADMIN_DOMAINS_URL: 'admin_domains',

SIGN_IN_FORM_WIDTH: 300,

Expand Down
4 changes: 4 additions & 0 deletions src/components/Icon/Expensicons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import Building from '@assets/images/building.svg';
import Calendar from '@assets/images/calendar.svg';
import Camera from '@assets/images/camera.svg';
import Car from '@assets/images/car.svg';
import CardsAndDomains from '@assets/images/cards-and-domains.svg';
import Cash from '@assets/images/cash.svg';
import Chair from '@assets/images/chair.svg';
import ChatBubble from '@assets/images/chatbubble.svg';
Expand Down Expand Up @@ -67,6 +68,7 @@ import Globe from '@assets/images/globe.svg';
import Hashtag from '@assets/images/hashtag.svg';
import Heart from '@assets/images/heart.svg';
import History from '@assets/images/history.svg';
import Home from '@assets/images/home.svg';
import Hourglass from '@assets/images/hourglass.svg';
import ImageCropCircleMask from '@assets/images/image-crop-circle-mask.svg';
import ImageCropSquareMask from '@assets/images/image-crop-square-mask.svg';
Expand Down Expand Up @@ -154,6 +156,7 @@ export {
Calendar,
Camera,
Car,
CardsAndDomains,
Cash,
ChatBubble,
ChatBubbles,
Expand Down Expand Up @@ -202,6 +205,7 @@ export {
Hashtag,
Heart,
History,
Home,
Hourglass,
ImageCropCircleMask,
ImageCropSquareMask,
Expand Down
6 changes: 5 additions & 1 deletion src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,10 @@ export default {
listOfChats: 'List of chats',
saveTheWorld: 'Save the world',
},
allSettingsScreen: {
subscriptions: 'Subscriptions',
cardsAndDomains: 'Cards & Domains',
},
tabSelector: {
chat: 'Chat',
room: 'Room',
Expand Down Expand Up @@ -1993,7 +1997,7 @@ export default {
},
breadcrumbs: {
// TODO-IDEAL: Verify translations in Spanish
chats: 'Chats'
chats: 'Chats',
},
referralProgram: {
[CONST.REFERRAL_PROGRAM.CONTENT_TYPES.START_CHAT]: {
Expand Down
8 changes: 8 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,10 @@ export default {
listOfChats: 'lista de chats',
saveTheWorld: 'Salvar el mundo',
},
allSettingsScreen: {
subscriptions: 'Suscripciones',
cardsAndDomains: 'Tarjetas y Dominios',
},
tabSelector: {
chat: 'Chat',
room: 'Sala',
Expand Down Expand Up @@ -2478,6 +2482,10 @@ export default {
guaranteed: 'eRecibo garantizado',
transactionDate: 'Fecha de transacción',
},
breadcrumbs: {
// TODO-IDEAL: Verify translations in Spanish
chats: 'Chats',
},
referralProgram: {
[CONST.REFERRAL_PROGRAM.CONTENT_TYPES.START_CHAT]: {
buttonText1: 'Inicia un chat y ',
Expand Down
27 changes: 2 additions & 25 deletions src/libs/Navigation/AppNavigator/Navigators/BottomTabNavigator.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,15 @@
import {StackNavigationOptions} from '@react-navigation/stack';
import React from 'react';
import {Text, View} from 'react-native';
import {PressableWithFeedback} from '@components/Pressable';
import createCustomBottomTabNavigator from '@libs/Navigation/AppNavigator/createCustomBottomTabNavigator';
import Navigation from '@libs/Navigation/Navigation';
import {BottomTabNavigatorParamList} from '@libs/Navigation/types';
import AllSettingsScreen from '@pages/home/sidebar/AllSettingsScreen';
import SidebarScreen from '@pages/home/sidebar/SidebarScreen';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
import SCREENS from '@src/SCREENS';

const loadWorkspaceInitialPage = () => require('../../../../pages/workspace/WorkspaceInitialPage').default as React.ComponentType;

const Tab = createCustomBottomTabNavigator<BottomTabNavigatorParamList>();

// TODO-IDEAL replace with the actuall screen.
function SecondTab() {
return (
<View>
<Text style={{color: 'white', fontSize: 30}}>Expensify settings</Text>

<PressableWithFeedback
role={CONST.ACCESSIBILITY_ROLE.BUTTON}
accessibilityLabel="Workspaces"
onPress={() => {
Navigation.navigate(ROUTES.SETTINGS_WORKSPACES);
}}
>
<Text style={{color: 'white', fontSize: 30}}>Workspaces</Text>
</PressableWithFeedback>
</View>
);
}

const screenOptions: StackNavigationOptions = {
headerShown: false,
};
Expand All @@ -46,7 +23,7 @@ function BottomTabNavigator() {
/>
<Tab.Screen
name={SCREENS.ALL_SETTINGS}
component={SecondTab}
component={AllSettingsScreen}
/>
<Tab.Screen
name={SCREENS.WORKSPACE.INITIAL}
Expand Down
126 changes: 126 additions & 0 deletions src/pages/home/sidebar/AllSettingsScreen.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import {useNavigationState} from '@react-navigation/native';
import React, {useCallback, useMemo} from 'react';
import {View} from 'react-native';
import _ from 'underscore';
import Breadcrumbs from '@components/Breadcrumbs';
import * as Expensicons from '@components/Icon/Expensicons';
import MenuItem from '@components/MenuItem';
import useLocalize from '@hooks/useLocalize';
import useSingleExecution from '@hooks/useSingleExecution';
import useThemeStyles from '@hooks/useThemeStyles';
import useWaitForNavigation from '@hooks/useWaitForNavigation';
import getTopmostCentralPaneName from '@libs/Navigation/getTopmostCentralPanePath';
import Navigation from '@libs/Navigation/Navigation';
import * as Link from '@userActions/Link';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';

function AllSettingsScreen() {
const styles = useThemeStyles();
const {isExecuting, singleExecution} = useSingleExecution();
const waitForNavigate = useWaitForNavigation();
const {translate} = useLocalize();
const activeRoute = useNavigationState(getTopmostCentralPaneName);

/**
* Retuns a list of menu items data for "everything" settings
* @returns {Object} object with translationKey, style and items
*/
const menuItemsData = useMemo(
() => ({
sectionStyle: styles.accountSettingsSectionContainer,
sectionTranslationKey: 'initialSettingsPage.account',
items: [
{
translationKey: 'common.workspaces',
icon: Expensicons.Building,
routeName: ROUTES.SETTINGS_WORKSPACES,
},
{
translationKey: 'allSettingsScreen.subscriptions',
icon: Expensicons.MoneyBag,
action: () => {
Link.openOldDotLink(CONST.ADMIN_POLICIES_URL);
},
shouldShowRightIcon: true,
iconRight: Expensicons.NewWindow,
link: CONST.ADMIN_POLICIES_URL,
},
{
translationKey: 'allSettingsScreen.cardsAndDomains',
icon: Expensicons.CardsAndDomains,
action: () => {
Link.openOldDotLink(CONST.ADMIN_DOMAINS_URL);
},
shouldShowRightIcon: true,
iconRight: Expensicons.NewWindow,
link: CONST.ADMIN_DOMAINS_URL,
},
],
}),
[styles.accountSettingsSectionContainer],
);

/**
* Retuns JSX.Element with menu items
* @param {Object} data list with menu items data
* @returns {JSX.Element} the menu items for passed data
*/
const getMenuItemsSection = useCallback(
(data) => (
<View style={[styles.pb4, styles.mh3]}>
{_.map(data.items, (item, index) => {
const keyTitle = item.translationKey ? translate(item.translationKey) : item.title;

return (
<MenuItem
key={`${keyTitle}_${index}`}
wrapperStyle={styles.sectionMenuItem}
title={keyTitle}
icon={item.icon}
shouldShowRightIcon={item.shouldShowRightIcon}
iconRight={item.iconRight}
disabled={isExecuting}
onPress={singleExecution(() => {
if (item.action) {
item.action();
} else {
waitForNavigate(() => {
Navigation.navigate(item.routeName);
})();
}
})}
shouldBlockSelection={Boolean(item.link)}
focused={activeRoute && activeRoute.startsWith(item.routeName, 1)}
/>
);
})}
</View>
),
[activeRoute, isExecuting, singleExecution, styles.mh3, styles.pb4, styles.sectionMenuItem, translate, waitForNavigate],
);

const accountMenuItems = useMemo(() => getMenuItemsSection(menuItemsData), [menuItemsData, getMenuItemsSection]);

return (
<View style={styles.w100}>
<View style={[styles.ph5, styles.pv5, styles.justifyContentBetween]}>
<Breadcrumbs
breadcrumbs={[
{
type: CONST.BREADCRUMB_TYPE.ROOT,
},
{
text: translate('common.settings'),
},
]}
/>
</View>
{accountMenuItems}
</View>
);
}

AllSettingsScreen.displayName = 'AllSettingsScreen';

export default AllSettingsScreen;
71 changes: 16 additions & 55 deletions src/pages/workspace/WorkspaceInitialPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,14 @@ import React, {useCallback, useEffect, useMemo, useState} from 'react';
import {ScrollView, View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
import Avatar from '@components/Avatar';
import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView';
import Breadcrumbs from '@components/Breadcrumbs';
import ConfirmModal from '@components/ConfirmModal';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import * as Expensicons from '@components/Icon/Expensicons';
import MenuItem from '@components/MenuItem';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback';
import ScreenWrapper from '@components/ScreenWrapper';
import Text from '@components/Text';
import Tooltip from '@components/Tooltip';
import useLocalize from '@hooks/useLocalize';
import usePrevious from '@hooks/usePrevious';
import useSingleExecution from '@hooks/useSingleExecution';
Expand Down Expand Up @@ -52,13 +49,6 @@ const defaultProps = {
reimbursementAccount: {},
};

/**
* @param {string} policyID
*/
function openEditor(policyID) {
Navigation.navigate(ROUTES.WORKSPACE_OVERVIEW.getRoute(policyID));
}

/**
* @param {string} policyID
*/
Expand Down Expand Up @@ -151,7 +141,7 @@ function WorkspaceInitialPage(props) {
const menuItems = [
{
translationKey: 'workspace.common.overview',
icon: Expensicons.Gear,
icon: Expensicons.Home,
action: singleExecution(waitForNavigate(() => Navigation.navigate(ROUTES.WORKSPACE_OVERVIEW.getRoute(policy.id)))),
brickRoadIndicator: hasGeneralSettingsError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : '',
},
Expand Down Expand Up @@ -262,47 +252,19 @@ function WorkspaceInitialPage(props) {
errors={policy.errors}
errorRowStyles={[styles.ph5, styles.pv2]}
>
<View style={[styles.flex1]}>
<View style={styles.avatarSectionWrapper}>
<View style={[styles.settingsPageBody, styles.alignItemsCenter]}>
<Tooltip text={translate('workspace.common.settings')}>
<PressableWithoutFeedback
disabled={hasPolicyCreationError || isExecuting}
style={[styles.pRelative, styles.avatarLarge]}
onPress={singleExecution(waitForNavigate(() => openEditor(policy.id)))}
accessibilityLabel={translate('workspace.common.settings')}
role={CONST.ROLE.BUTTON}
>
<Avatar
containerStyles={styles.avatarLarge}
imageStyles={[styles.avatarLarge, styles.alignSelfCenter]}
source={policy.avatar ? policy.avatar : ReportUtils.getDefaultWorkspaceAvatar(policyName)}
fallbackIcon={Expensicons.FallbackWorkspaceAvatar}
size={CONST.AVATAR_SIZE.LARGE}
name={policyName}
type={CONST.ICON_TYPE_WORKSPACE}
/>
</PressableWithoutFeedback>
</Tooltip>
{!_.isEmpty(policy.name) && (
<Tooltip text={translate('workspace.common.settings')}>
<PressableWithoutFeedback
disabled={hasPolicyCreationError || isExecuting}
style={[styles.alignSelfCenter, styles.mt4, styles.w100]}
onPress={singleExecution(waitForNavigate(() => openEditor(policy.id)))}
accessibilityLabel={translate('workspace.common.settings')}
role={CONST.ROLE.BUTTON}
>
<Text
numberOfLines={1}
style={[styles.textHeadline, styles.alignSelfCenter, styles.pre]}
>
{policy.name}
</Text>
</PressableWithoutFeedback>
</Tooltip>
)}
</View>
<View style={[styles.mh3, styles.flex1]}>
<View style={[styles.mh5, styles.pv5, styles.justifyContentBetween]}>
<Breadcrumbs
breadcrumbs={[
{
type: CONST.BREADCRUMB_TYPE.STRONG,
text: policyName,
},
{
text: translate('common.settings'),
},
]}
/>
</View>
{/*
Ideally we should use MenuList component for MenuItems with singleExecution/Navigation actions.
Expand All @@ -315,10 +277,9 @@ function WorkspaceInitialPage(props) {
interactive={!hasPolicyCreationError}
title={translate(item.translationKey)}
icon={item.icon}
iconRight={item.iconRight}
onPress={item.action}
shouldShowRightIcon
brickRoadIndicator={item.brickRoadIndicator}
wrapperStyle={styles.sectionMenuItem}
/>
))}
</View>
Expand Down

0 comments on commit 96a2875

Please sign in to comment.