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 square mask for workspace image upload #15975

Merged
merged 10 commits into from
Mar 20, 2023
5 changes: 5 additions & 0 deletions src/CONST.js
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,11 @@ const CONST = {
ICON_TYPE_AVATAR: 'avatar',
ICON_TYPE_WORKSPACE: 'workspace',

IMAGE_CROP_MASK_TYPE: {
CIRCLE: 'circle',
SQUARE: 'square',
},

AVATAR_SIZE: {
LARGE: 'large',
MEDIUM: 'medium',
Expand Down
5 changes: 5 additions & 0 deletions src/components/AvatarCropModal/AvatarCropModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ const propTypes = {
/** Modal visibility */
isVisible: PropTypes.bool.isRequired,

/** Type of image crop mask */
imageCropMaskType: PropTypes.oneOf([CONST.IMAGE_CROP_MASK_TYPE.CIRCLE, CONST.IMAGE_CROP_MASK_TYPE.SQUARE]),

...withLocalizePropTypes,
...windowDimensionsPropTypes,
};
Expand All @@ -60,6 +63,7 @@ const defaultProps = {
imageType: '',
onClose: () => {},
onSave: () => {},
imageCropMaskType: CONST.IMAGE_CROP_MASK_TYPE.CIRCLE,
};

// This component can't be written using class since reanimated API uses hooks.
Expand Down Expand Up @@ -339,6 +343,7 @@ const AvatarCropModal = (props) => {
translateY={translateY}
translateX={translateX}
rotation={rotation}
imageCropMaskType={props.imageCropMaskType}
/>
<View style={[styles.mt5, styles.justifyContentBetween, styles.alignItemsCenter, styles.flexRow, StyleUtils.getWidthStyle(imageContainerSize)]}>
<Icon src={Expensicons.Zoom} fill={themeColors.icons} />
Expand Down
13 changes: 11 additions & 2 deletions src/components/AvatarCropModal/ImageCropView.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import * as Expensicons from '../Icon/Expensicons';
import * as StyleUtils from '../../styles/StyleUtils';
import gestureHandlerPropTypes from './gestureHandlerPropTypes';
import ControlSelection from '../../libs/ControlSelection';
import CONST from '../../CONST';

const propTypes = {
/** Link to image for cropping */
Expand Down Expand Up @@ -37,12 +38,16 @@ const propTypes = {

/** React-native-reanimated lib handler which executes when the user is panning image */
panGestureEventHandler: gestureHandlerPropTypes,

/** Type of image crop mask */
imageCropMaskType: PropTypes.oneOf([CONST.IMAGE_CROP_MASK_TYPE.CIRCLE, CONST.IMAGE_CROP_MASK_TYPE.SQUARE]),
};

const defaultProps = {
imageUri: '',
containerSize: 0,
panGestureEventHandler: () => {},
imageCropMaskType: CONST.IMAGE_CROP_MASK_TYPE.CIRCLE,
};

const ImageCropView = (props) => {
Expand All @@ -64,14 +69,18 @@ const ImageCropView = (props) => {
};
}, [props.originalImageHeight, props.originalImageWidth]);

// If image crop mask type is square then apply border around View
const maskStyles = props.imageCropMaskType === CONST.IMAGE_CROP_MASK_TYPE.SQUARE ? styles.imageCropViewBorder : {};

// We're preventing text selection with ControlSelection.blockElement to prevent safari
// default behaviour of cursor - I-beam cursor on drag. See https://github.com/Expensify/App/issues/13688
return (
<PanGestureHandler onGestureEvent={props.panGestureEventHandler}>
<Animated.View ref={ControlSelection.blockElement} style={[containerStyle, styles.imageCropContainer]}>
<Animated.Image style={[imageStyle, styles.h100, styles.w100]} source={{uri: props.imageUri}} resizeMode="contain" />
<View style={[containerStyle, styles.l0, styles.b0, styles.pAbsolute]}>
<Icon src={Expensicons.ImageCropMask} width={props.containerSize} height={props.containerSize} />
<View style={[containerStyle, styles.l0, styles.b0, styles.pAbsolute, maskStyles]}>
{props.imageCropMaskType === CONST.IMAGE_CROP_MASK_TYPE.CIRCLE
&& <Icon src={Expensicons.ImageCropMask} width={props.containerSize} height={props.containerSize} />}
</View>
</Animated.View>
</PanGestureHandler>
Expand Down
5 changes: 5 additions & 0 deletions src/components/AvatarWithImagePicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ const propTypes = {
/** Denotes whether it is an avatar or a workspace avatar */
type: PropTypes.oneOf([CONST.ICON_TYPE_AVATAR, CONST.ICON_TYPE_WORKSPACE]),

/** Type of image crop mask */
imageCropMaskType: PropTypes.oneOf([CONST.IMAGE_CROP_MASK_TYPE.CIRCLE, CONST.IMAGE_CROP_MASK_TYPE.SQUARE]),

...withLocalizePropTypes,
};

Expand All @@ -74,6 +77,7 @@ const defaultProps = {
size: CONST.AVATAR_SIZE.DEFAULT,
fallbackIcon: Expensicons.FallbackAvatar,
type: CONST.ICON_TYPE_AVATAR,
imageCropMaskType: CONST.IMAGE_CROP_MASK_TYPE.CIRCLE,
};

class AvatarWithImagePicker extends React.Component {
Expand Down Expand Up @@ -309,6 +313,7 @@ class AvatarWithImagePicker extends React.Component {
imageUri={this.state.imageUri}
imageName={this.state.imageName}
imageType={this.state.imageType}
imageCropMaskType={this.props.imageCropMaskType}
/>
</View>
);
Expand Down
1 change: 1 addition & 0 deletions src/pages/workspace/WorkspaceSettingsPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ class WorkspaceSettingsPage extends React.Component {
isUsingDefaultAvatar={!lodashGet(this.props.policy, 'avatar', null)}
onImageSelected={file => Policy.updateWorkspaceAvatar(lodashGet(this.props.policy, 'id', ''), file)}
onImageRemoved={() => Policy.deleteWorkspaceAvatar(lodashGet(this.props.policy, 'id', ''))}
imageCropMaskType={CONST.IMAGE_CROP_MASK_TYPE.SQUARE}
/>
</OfflineWithFeedback>
<OfflineWithFeedback
Expand Down
6 changes: 6 additions & 0 deletions src/styles/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -2821,6 +2821,12 @@ const styles = {
cursor: 'move',
},

imageCropViewBorder: {
borderRadius: variables.cropViewBorderRadius,
borderWidth: 2,
borderColor: themeColors.text,
PrashantMangukiya marked this conversation as resolved.
Show resolved Hide resolved
PrashantMangukiya marked this conversation as resolved.
Show resolved Hide resolved
},

sliderKnob: {
backgroundColor: themeColors.success,
position: 'absolute',
Expand Down
1 change: 1 addition & 0 deletions src/styles/variables.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export default {
componentBorderRadiusCard: 12,
componentBorderRadiusRounded: 20,
buttonBorderRadius: 100,
cropViewBorderRadius: 12,
avatarSizeLarge: 80,
avatarSizeHeader: 40,
avatarSizeLargeBordered: 88,
Expand Down