Skip to content

Commit

Permalink
Merge pull request #25 from software-mansion-labs/wave8/workspace-swi…
Browse files Browse the repository at this point in the history
…tcher-url

Wave8/workspace switcher url
  • Loading branch information
adamgrzybowski authored Jan 18, 2024
2 parents 073a96b + 3d29b64 commit 0149e26
Show file tree
Hide file tree
Showing 46 changed files with 824 additions and 79 deletions.
2 changes: 2 additions & 0 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Onyx from 'react-native-onyx';
import {PickerStateProvider} from 'react-native-picker-select';
import {SafeAreaProvider} from 'react-native-safe-area-context';
import '../wdyr';
import ActiveWorkspaceContextProvider from './components/ActiveWorkspace/ActiveWorkspaceProvider';
import ColorSchemeWrapper from './components/ColorSchemeWrapper';
import ComposeProviders from './components/ComposeProviders';
import CustomStatusBarAndBackground from './components/CustomStatusBarAndBackground';
Expand Down Expand Up @@ -69,6 +70,7 @@ function App() {
PickerStateProvider,
EnvironmentProvider,
CustomStatusBarAndBackgroundContextProvider,
ActiveWorkspaceContextProvider,
]}
>
<CustomStatusBarAndBackground />
Expand Down
6 changes: 2 additions & 4 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3074,12 +3074,10 @@ const CONST = {
DEFAULT: 5,
CAROUSEL: 3,
},

BRICK_ROAD: {
GBR: 'GBR',
RBR: 'RBR',
GBR: 'info',
RBR: 'error',
},

VIOLATIONS: {
ALL_TAG_LEVELS_REQUIRED: 'allTagLevelsRequired',
AUTO_REPORTED_REJECTED_EXPENSE: 'autoReportedRejectedExpense',
Expand Down
4 changes: 3 additions & 1 deletion src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ function getUrlWithBackToParam<TUrl extends string>(url: TUrl, backTo?: string):
}

const ROUTES = {
ROOT: '',

HOME: 'home',

ALL_SETTINGS: 'all-settings',
Expand Down Expand Up @@ -57,7 +59,7 @@ const ROUTES = {
route: 'bank-account/:stepToOpen?',
getRoute: (stepToOpen = '', policyID = '', backTo?: string) => getUrlWithBackToParam(`bank-account/${stepToOpen}?policyID=${policyID}`, backTo),
},

WORKSPACE_SWITCHER: 'workspaceSwitcher',
SETTINGS: 'settings',
SETTINGS_PROFILE: 'settings/profile',
SETTINGS_SHARE_CODE: 'settings/shareCode',
Expand Down
4 changes: 4 additions & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ const SCREENS = {
},
LEFT_MODAL: {
SEARCH: 'Search',
WORKSPACE_SWITCHER: 'WorkspaceSwitcher',
},
WORKSPACE_SWITCHER: {
ROOT: 'WorkspaceSwitcher_Root',
},
RIGHT_MODAL: {
SETTINGS: 'Settings',
Expand Down
11 changes: 11 additions & 0 deletions src/components/ActiveWorkspace/ActiveWorkspaceContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {createContext} from 'react';

type ActiveWorkspaceContextType = {
activeWorkspaceID?: string;
setActiveWorkspaceID: (activeWorkspaceID?: string) => void;
};

const ActiveWorkspaceContext = createContext<ActiveWorkspaceContextType>({activeWorkspaceID: undefined, setActiveWorkspaceID: () => undefined});

export default ActiveWorkspaceContext;
export {type ActiveWorkspaceContextType};
18 changes: 18 additions & 0 deletions src/components/ActiveWorkspace/ActiveWorkspaceProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React, {useMemo, useState} from 'react';
import ActiveWorkspaceContext from './ActiveWorkspaceContext';

function ActiveWorkspaceContextProvider({children}: React.PropsWithChildren) {
const [activeWorkspaceID, setActiveWorkspaceID] = useState<string | undefined>(undefined);

const value = useMemo(
() => ({
activeWorkspaceID,
setActiveWorkspaceID,
}),
[activeWorkspaceID],
);

return <ActiveWorkspaceContext.Provider value={value}>{children}</ActiveWorkspaceContext.Provider>;
}

export default ActiveWorkspaceContextProvider;
30 changes: 20 additions & 10 deletions src/components/AvatarWithImagePicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ const propTypes = {

/** Style applied to the avatar */
avatarStyle: stylePropTypes.isRequired,

/** Indicates if picker feature should be disabled */
disabled: PropTypes.bool,
};

const defaultProps = {
Expand All @@ -105,6 +108,7 @@ const defaultProps = {
headerTitle: '',
previewSource: '',
originalFileName: '',
disabled: false,
};

function AvatarWithImagePicker({
Expand All @@ -127,6 +131,7 @@ function AvatarWithImagePicker({
onImageSelected,
editorMaskImage,
avatarStyle,
disabled,
}) {
const theme = useTheme();
const styles = useThemeStyles();
Expand Down Expand Up @@ -300,12 +305,15 @@ function AvatarWithImagePicker({
errorRowStyles={errorRowStyles}
onClose={onErrorClose}
>
<Tooltip text={translate('avatarWithImagePicker.editImage')}>
<Tooltip
shouldRender={!disabled}
text={translate('avatarWithImagePicker.editImage')}
>
<PressableWithoutFeedback
onPress={() => setIsMenuVisible((prev) => !prev)}
accessibilityRole={CONST.ACCESSIBILITY_ROLE.IMAGEBUTTON}
accessibilityLabel={translate('avatarWithImagePicker.editImage')}
disabled={isAvatarCropModalOpen}
disabled={isAvatarCropModalOpen || disabled}
ref={anchorRef}
>
<View>
Expand All @@ -322,14 +330,16 @@ function AvatarWithImagePicker({
<DefaultAvatar />
)}
</View>
<View style={[styles.smallEditIcon, styles.smallAvatarEditIcon]}>
<Icon
src={Expensicons.Pencil}
width={variables.iconSizeSmall}
height={variables.iconSizeSmall}
fill={theme.icon}
/>
</View>
{!disabled && (
<View style={[styles.smallEditIcon, styles.smallAvatarEditIcon]}>
<Icon
src={Expensicons.Pencil}
width={variables.iconSizeSmall}
height={variables.iconSizeSmall}
fill={theme.icon}
/>
</View>
)}
</PressableWithoutFeedback>
</Tooltip>
</OfflineWithFeedback>
Expand Down
1 change: 1 addition & 0 deletions src/components/EnvironmentBadge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ function EnvironmentBadge() {
badgeStyles={[styles.alignSelfStart, styles.headerEnvBadge]}
textStyles={[styles.headerEnvBadgeText, {fontWeight: '700'}]}
environment={environment}
pressable
/>
);
}
Expand Down
1 change: 1 addition & 0 deletions src/components/MultipleAvatars.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ function MultipleAvatars({
<Avatar
source={icons[0].source}
size={size}
fill={icons[0].fill ?? theme.iconSuccessFill}
name={icons[0].name}
type={icons[0].type}
fallbackIcon={icons[0].fallbackIcon}
Expand Down
8 changes: 8 additions & 0 deletions src/components/OptionRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,14 @@ function OptionRow({
/>
</View>
)}
{option.brickRoadIndicator === CONST.BRICK_ROAD_INDICATOR_STATUS.INFO && (
<View style={[styles.alignItemsCenter, styles.justifyContentCenter]}>
<Icon
src={Expensicons.DotIndicator}
fill={theme.iconSuccessFill}
/>
</View>
)}
{showSelectedState && (
<>
{shouldShowSelectedStateAsButton && !isSelected ? (
Expand Down
8 changes: 6 additions & 2 deletions src/components/OptionsList/BaseOptionsList.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,10 @@ function BaseOptionsList({
return true;
}

if (option.policyID && option.policyID === item.policyID) {
return true;
}

if (_.isEmpty(option.name)) {
return false;
}
Expand All @@ -201,7 +205,7 @@ function BaseOptionsList({
return (
<OptionRow
keyForList={item.keyForList}
option={item}
option={{...item, brickRoadIndicator: isSelected ? undefined : item.brickRoadIndicator}}
showTitleTooltip={showTitleTooltip}
hoverStyle={optionHoveredStyle}
optionIsFocused={!disableFocusOptions && !isItemDisabled && focusedIndex === index + section.indexOffset}
Expand All @@ -212,7 +216,7 @@ function BaseOptionsList({
selectedStateButtonText={multipleOptionSelectorButtonText}
onSelectedStatePressed={onAddToSelection}
highlightSelected={highlightSelectedOptions}
boldStyle={boldStyle}
boldStyle={_.isUndefined(item.boldStyle) ? boldStyle : item.boldStyle}
isDisabled={isItemDisabled}
shouldHaveOptionSeparator={index > 0 && shouldHaveOptionSeparator}
shouldDisableRowInnerPadding={shouldDisableRowInnerPadding}
Expand Down
2 changes: 2 additions & 0 deletions src/components/OptionsSelector/BaseOptionsSelector.js
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,7 @@ class BaseOptionsSelector extends Component {
spellCheck={false}
shouldInterceptSwipe={this.props.shouldTextInputInterceptSwipe}
isLoading={this.props.isLoadingNewOptions}
iconLeft={this.props.textIconLeft}
testID="options-selector-input"
/>
);
Expand All @@ -544,6 +545,7 @@ class BaseOptionsSelector extends Component {
onSelectRow={this.props.onSelectRow ? this.selectRow : undefined}
sections={this.state.sections}
focusedIndex={this.state.focusedIndex}
disableFocusOptions={this.props.disableFocusOptions}
selectedOptions={this.props.selectedOptions}
canSelectMultipleOptions={this.props.canSelectMultipleOptions}
shouldShowMultipleOptionSelectorAsButton={this.props.shouldShowMultipleOptionSelectorAsButton}
Expand Down
8 changes: 8 additions & 0 deletions src/components/OptionsSelector/optionsSelectorPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ const propTypes = {
/** Whether to disable interactivity of option rows */
isDisabled: PropTypes.bool,

/** Whether to disable focus options of rows */
disableFocusOptions: PropTypes.bool,

/** Display the text of the option in bold font style */
boldStyle: PropTypes.bool,

Expand Down Expand Up @@ -134,6 +137,9 @@ const propTypes = {

/** Whether nested scroll of options is enabled, true by default */
nestedScrollEnabled: PropTypes.bool,

/** Left icon to display in TextInput */
textIconLeft: PropTypes.elementType,
};

const defaultProps = {
Expand Down Expand Up @@ -163,6 +169,7 @@ const defaultProps = {
shouldShowOptions: true,
disableArrowKeysActions: false,
isDisabled: false,
disableFocusOptions: false,
shouldHaveOptionSeparator: false,
initiallyFocusedOptionKey: undefined,
maxLength: CONST.SEARCH_MAX_LENGTH,
Expand All @@ -174,6 +181,7 @@ const defaultProps = {
shouldTextInputInterceptSwipe: false,
shouldAllowScrollingChildren: false,
nestedScrollEnabled: true,
textIconLeft: undefined,
};

export {propTypes, defaultProps};
11 changes: 11 additions & 0 deletions src/components/TextInput/BaseTextInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ function BaseTextInput(
placeholder = '',
errorText = '',
icon = null,
iconLeft = null,
textInputContainerStyles,
touchableInputWrapperStyle,
containerStyles,
Expand Down Expand Up @@ -317,6 +318,16 @@ function BaseTextInput(
</>
) : null}
<View style={[styles.textInputAndIconContainer, isMultiline && hasLabel && styles.textInputMultilineContainer, styles.pointerEventsBoxNone]}>
{!inputProps.secureTextEntry && iconLeft && (
<View style={[styles.textInputLeftIconContainer, !isReadOnly ? styles.cursorPointer : styles.pointerEventsNone]}>
<Icon
src={iconLeft}
fill={theme.icon}
height={20}
width={20}
/>
</View>
)}
{Boolean(prefixCharacter) && (
<View style={styles.textInputPrefixWrapper}>
<Text
Expand Down
3 changes: 3 additions & 0 deletions src/components/TextInput/BaseTextInput/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ type CustomBaseTextInputProps = {
/** Icon to display in right side of text input */
icon?: IconAsset | null;

/** Icon to display in left side of text input */
iconLeft?: IconAsset | null;

/** Customize the TextInput container */
textInputContainerStyles?: StyleProp<ViewStyle>;

Expand Down
27 changes: 24 additions & 3 deletions src/components/WorkspaceSwitcherButton.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,43 @@
import React from 'react';
import React, {useMemo} from 'react';
import useActiveWorkspace from '@hooks/useActiveWorkspace';
import useLocalize from '@hooks/useLocalize';
import Navigation from '@libs/Navigation/Navigation';
import {getDefaultWorkspaceAvatar, getPolicy} from '@libs/ReportUtils';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
import * as Expensicons from './Icon/Expensicons';
import {PressableWithFeedback} from './Pressable';
import SubscriptAvatar from './SubscriptAvatar';

function WorkspaceSwitcherButton() {
const {translate} = useLocalize();
const {activeWorkspaceID} = useActiveWorkspace();

const {source, name, type} = useMemo(() => {
if (!activeWorkspaceID) {
return {source: Expensicons.ExpensifyAppIcon, name: CONST.WORKSPACE_SWITCHER.NAME, type: CONST.ICON_TYPE_AVATAR};
}

const policy = getPolicy(activeWorkspaceID);
const avatar = policy?.avatar && policy?.avatar?.length > 0 ? policy.avatar : getDefaultWorkspaceAvatar(policy?.name);
return {
source: avatar,
name: policy?.name,
type: CONST.ICON_TYPE_WORKSPACE,
};
}, [activeWorkspaceID]);

return (
<PressableWithFeedback
accessibilityRole={CONST.ROLE.BUTTON}
accessibilityLabel={translate('common.workspaces')}
accessible
onPress={() => {}}
onPress={() => {
Navigation.navigate(ROUTES.WORKSPACE_SWITCHER);
}}
>
<SubscriptAvatar
mainAvatar={{source: Expensicons.ExpensifyAppIcon, name: CONST.WORKSPACE_SWITCHER.NAME, type: CONST.ICON_TYPE_AVATAR}}
mainAvatar={{source, name, type}}
subscriptIcon={{source: Expensicons.DownArrow, width: CONST.WORKSPACE_SWITCHER.SUBSCRIPT_ICON_SIZE, height: CONST.WORKSPACE_SWITCHER.SUBSCRIPT_ICON_SIZE}}
showTooltip={false}
noMargin
Expand Down
9 changes: 9 additions & 0 deletions src/hooks/useActiveWorkspace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import {useContext} from 'react';
import ActiveWorkspaceContext from '@components/ActiveWorkspace/ActiveWorkspaceContext';
import type {ActiveWorkspaceContextType} from '@components/ActiveWorkspace/ActiveWorkspaceContext';

function useActiveWorkspace(): ActiveWorkspaceContextType {
return useContext(ActiveWorkspaceContext);
}

export default useActiveWorkspace;
5 changes: 5 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1556,6 +1556,11 @@ export default {
notFound: 'No workspace found',
description: 'Rooms are a great place to discuss and work with multiple people. To begin collaborating, create or join a workspace',
},
switcher: {
headerTitle: 'Choose a workspace',
everythingSection: 'Everything',
placeholder: 'Find a workspace',
},
new: {
newWorkspace: 'New workspace',
getTheExpensifyCardAndMore: 'Get the Expensify Card and more',
Expand Down
5 changes: 5 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1579,6 +1579,11 @@ export default {
notFound: 'No se encontró ningún espacio de trabajo',
description: 'Las salas son un gran lugar para discutir y trabajar con varias personas. Para comenzar a colaborar, cree o únase a un espacio de trabajo',
},
switcher: {
headerTitle: 'Elige un espacio de trabajo',
everythingSection: 'Todo',
placeholder: 'Encuentra un espacio de trabajo',
},
new: {
newWorkspace: 'Nuevo espacio de trabajo',
getTheExpensifyCardAndMore: 'Consigue la Tarjeta Expensify y más',
Expand Down
Loading

0 comments on commit 0149e26

Please sign in to comment.