Skip to content

Commit

Permalink
(Feature) #1431 user should have nicer profile screen (#1497)
Browse files Browse the repository at this point in the history
* fix: change profile screen layout/styles

* fix: update snapshots

* fix: fix color for disabled input text

* fix: add user name below avatar

* refactor: ProfileDataTable file

* refactor: InputRounded file

* fix: Update snapshots

* refactor: More refactor for Avatar components

* fix: fix tests for TabsView component, update snapshots
  • Loading branch information
yaroslav-fedyshyn-nordwhale committed Mar 24, 2020
1 parent 8aac719 commit eb2915f
Show file tree
Hide file tree
Showing 9 changed files with 481 additions and 319 deletions.
13 changes: 11 additions & 2 deletions src/components/appNavigation/__tests__/TabsView.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import renderer from 'react-test-renderer'
import TabsView from '../TabsView'
import Dashboard from '../../dashboard/Dashboard'
import Profile from '../../profile/Profile'
import SimpleStore from '../../../lib/undux/SimpleStore'

// Note: test renderer must be required after react-native.

Expand All @@ -17,12 +18,20 @@ describe('TabsView', () => {
}

it('renders without errors', () => {
const tree = renderer.create(<TabsView routes={routes} />)
const tree = renderer.create(
<SimpleStore.Container>
<TabsView routes={routes} />
</SimpleStore.Container>
)
expect(tree.toJSON()).toBeTruthy()
})

it('matches snapshot', () => {
const component = renderer.create(<TabsView routes={routes} />)
const component = renderer.create(
<SimpleStore.Container>
<TabsView routes={routes} />
</SimpleStore.Container>
)
expect(component.toJSON()).toMatchSnapshot()
})
})
13 changes: 9 additions & 4 deletions src/components/common/form/InputRounded.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react'
import React, { useCallback } from 'react'
import { TextInput, View } from 'react-native'
import normalize from '../../../lib/utils/normalizeText'
import { withStyles } from '../../../lib/styles'
Expand All @@ -14,9 +14,12 @@ import ErrorText from './ErrorText'
* @returns {React.Node}
*/
const InputRounded = ({ styles, theme, icon, iconSize, iconColor, error, onChange, ...inputProps }) => {
const handleChange = event => {
onChange(event.target.value)
}
const handleChange = useCallback(
event => {
onChange(event.target.value)
},
[onChange]
)

return (
<View style={styles.inputContainer}>
Expand All @@ -28,6 +31,8 @@ const InputRounded = ({ styles, theme, icon, iconSize, iconColor, error, onChang
placeholderTextColor={theme.colors.gray50Percent}
style={error ? styles.inputError : styles.input}
{...inputProps}
disabled={false}
editable={!inputProps.disabled}
/>
<View style={styles.suffixIcon}>
<Icon
Expand Down
46 changes: 17 additions & 29 deletions src/components/common/view/Avatar.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,6 @@ import { Avatar } from 'react-native-paper'
import unknownProfile from '../../../assets/unknownProfile.svg'
import { withStyles } from '../../../lib/styles'

export type AvatarProps = {
onPress?: () => {},
size?: number,
source?: string,
style?: {},
styles?: any,
}

/**
* Touchable Avatar
* @param {Props} props
Expand All @@ -22,27 +14,23 @@ export type AvatarProps = {
* @param {Number} [props.size=34]
* @returns {React.Node}
*/
const CustomAvatar = (props: AvatarProps) => {
const { styles, style, source, onPress, size, ...restProps } = props

return (
<TouchableOpacity
activeOpacity={0.5}
disabled={!onPress}
onPress={onPress}
style={[styles.avatarContainer, { width: size, height: size, borderRadius: size / 2 }, style]}
underlayColor="#fff"
>
<Avatar.Image
size={size - 2}
source={{ uri: source || unknownProfile }}
style={{ backgroundColor: 'rgba(0, 0, 0, 0)' }}
{...restProps}
/>
{props.children}
</TouchableOpacity>
)
}
const CustomAvatar = ({ styles, style, source, onPress, size, children, ...avatarProps }) => (
<TouchableOpacity
activeOpacity={0.5}
disabled={!onPress}
onPress={onPress}
style={[styles.avatarContainer, { width: size, height: size, borderRadius: size / 2 }, style]}
underlayColor="#fff"
>
<Avatar.Image
size={size - 2}
source={{ uri: source || unknownProfile }}
style={{ backgroundColor: 'rgba(0, 0, 0, 0)' }}
{...avatarProps}
/>
{children}
</TouchableOpacity>
)

CustomAvatar.defaultProps = {
size: 42,
Expand Down
24 changes: 11 additions & 13 deletions src/components/common/view/UserAvatar.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,18 @@ export type AvatarProps = {
* @param {React.Node} props.children
* @returns {React.Node}
*/
const UserAvatar = (props: AvatarProps) => {
const { profile, children, styles, containerStyle } = props

const avatarSize = getDesignRelativeWidth(AVATAR_DESIGN_WIDTH)

return (
<View style={styles.avatar}>
<View style={[styles.innerAvatar, containerStyle]}>
<Avatar size={avatarSize} {...props} source={profile.avatar}>
{children}
</Avatar>
</View>
const UserAvatar = ({ profile, children, styles, containerStyle, size, ...rest }: AvatarProps) => (
<View style={styles.avatar}>
<View style={[styles.innerAvatar, containerStyle]}>
<Avatar size={size} {...rest} source={profile.avatar}>
{children}
</Avatar>
</View>
)
</View>
)

UserAvatar.defaultProps = {
size: getDesignRelativeWidth(AVATAR_DESIGN_WIDTH),
}

const getStylesFromProps = ({ theme }) => ({
Expand Down
16 changes: 11 additions & 5 deletions src/components/profile/CircleButtonWrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,32 @@ const CircleButtonWrapper = ({
disabled,
styles,
style,
containerStyle,
iconName,
iconColor = '#fff',
iconSize = 20,
iconColor,
iconSize,
}) => (
<View>
<View style={containerStyle}>
<TouchableOpacity
cursor={disabled ? 'inherit' : 'pointer'}
onPress={disabled ? undefined : onPress}
style={[styles.button, style]}
>
<Icon color={iconColor} size={iconSize} name={iconName} />
</TouchableOpacity>
{label && (
<Text fontSize={10} fontWeight="500" lineHeight={11} style={[styles.label, labelStyles]}>
{!!label && (
<Text fontSize={10} fontWeight="500" lineHeight={11} color="white" style={[styles.label, labelStyles]}>
{label}
</Text>
)}
</View>
)

CircleButtonWrapper.defaultProps = {
iconColor: '#fff',
iconSize: 20,
}

const getStylesFromProps = ({ theme }) => ({
wrapper: {
display: 'flex',
Expand Down
83 changes: 70 additions & 13 deletions src/components/profile/Profile.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// @flow
import React, { useCallback } from 'react'
import { View } from 'react-native'
import GDStore from '../../lib/undux/GDStore'
import { createStackNavigator } from '../appNavigation/stackNavigation'
import { Section, UserAvatar, Wrapper } from '../common'
import { Section, Text, UserAvatar, Wrapper } from '../common'
import { withStyles } from '../../lib/styles'
import { getDesignRelativeWidth } from '../../lib/utils/sizes'
import EditAvatar from './EditAvatar'
import EditProfile from './EditProfile'
import ProfileDataTable from './ProfileDataTable'
Expand All @@ -15,6 +17,8 @@ import VerifyEditCode from './VerifyEditCode'

const TITLE = 'Profile'

const avatarSize = getDesignRelativeWidth(136)

const ProfileWrapper = props => {
const store = GDStore.useStore()
const profile = store.get('profile')
Expand All @@ -34,18 +38,37 @@ const ProfileWrapper = props => {

return (
<Wrapper>
<Section style={styles.section}>
<Section.Row justifyContent="space-between" alignItems="flex-start">
<CircleButtonWrapper label={'Privacy'} iconName={'privacy'} iconSize={23} onPress={handlePrivacyPress} />
<UserAvatar profile={profile} onPress={handleAvatarPress} />
<CircleButtonWrapper
label={'Edit'}
iconName={'edit'}
iconSize={25}
onPress={handleEditProfilePress}
style={[styles.iconRight]}
<Section.Row justifyContent="space-between" alignItems="flex-start" style={styles.userDataAndButtonsRow}>
<CircleButtonWrapper
label={'Privacy'}
iconName={'privacy'}
iconSize={23}
onPress={handlePrivacyPress}
containerStyle={styles.iconLeft}
/>
<View style={styles.userDataWrapper}>
<UserAvatar
containerStyle={styles.userAvatarWrapper}
style={styles.userAvatar}
profile={profile}
onPress={handleAvatarPress}
size={avatarSize}
/>
</Section.Row>
<Text fontSize={22} fontFamily="Roboto Slab" lineHeight={29} style={styles.userName}>
{!!profile && profile.fullName}
</Text>
</View>
<CircleButtonWrapper
label={'Edit'}
iconName={'edit'}
iconSize={25}
onPress={handleEditProfilePress}
style={styles.iconRightContainer}
containerStyle={styles.iconRight}
/>
</Section.Row>
<Section style={styles.section}>
<View style={styles.emptySpace} />
<ProfileDataTable profile={profile} />
</Section>
</Wrapper>
Expand All @@ -57,13 +80,47 @@ ProfileWrapper.navigationOptions = {
}

const getStylesFromProps = ({ theme }) => ({
emptySpace: {
height: 75,
width: '100%',
},
section: {
flexGrow: 1,
padding: theme.sizes.defaultDouble,
},
iconRight: {
iconRightContainer: {
transform: [{ rotateY: '180deg' }],
},
iconLeft: {
position: 'absolute',
left: getDesignRelativeWidth(20),
},
iconRight: {
position: 'absolute',
right: getDesignRelativeWidth(20),
},
userDataWrapper: {
position: 'absolute',
},
userAvatarWrapper: {
borderColor: theme.colors.white,
borderWidth: 3,
borderStyle: 'solid',
borderRadius: '50%',
},
userAvatar: {
borderWidth: 0,
},
userDataAndButtonsRow: {
display: 'flex',
justifyContent: 'center',
position: 'relative',
zIndex: 1,
height: avatarSize / 2,
},
userName: {
marginTop: theme.sizes.default,
},
})

const Profile = withStyles(getStylesFromProps)(ProfileWrapper)
Expand Down
Loading

0 comments on commit eb2915f

Please sign in to comment.