Skip to content
This repository has been archived by the owner on Aug 16, 2021. It is now read-only.

Commit

Permalink
Split Settings screen into separate components (#37)
Browse files Browse the repository at this point in the history
* move Separator, SettingsRow and RowText to separate components

* move touch id functionality to separate TouchId component

* move Button and ProfilePic components to separate files

* move Profile to separate component

* fix linter error

* move team to Profile component

* fix linter error

* fix flow errors

* move usage limits to separate UsageLimits component

* remove TODO comment
  • Loading branch information
cherniavskii authored and rdev committed Feb 10, 2019
1 parent c3e6ce2 commit 2946ce1
Show file tree
Hide file tree
Showing 10 changed files with 561 additions and 450 deletions.
9 changes: 9 additions & 0 deletions src/components/elements/settings/Button.js
@@ -0,0 +1,9 @@
import styled from 'styled-components';

const Button = styled.Text`
font-size: 18px;
font-weight: 300;
color: ${props => props.theme.settingsButton};
`;

export default Button;
278 changes: 278 additions & 0 deletions src/components/elements/settings/Profile.js
@@ -0,0 +1,278 @@
// @flow
import React from 'react';
import { TouchableOpacity, Alert, Image, ActionSheetIOS } from 'react-native';
import styled from 'styled-components';
import { connect } from '../../../Provider';
import { isAndroid } from '../../../lib/utils';
import api from '../../../lib/api';
import gradient from '../../../../assets/gradient.jpg';
import Input from './Input';
import Button from './Button';
import ProfilePic from './ProfilePic';

type Props = {
context: Context,
};

type State = {
editing: boolean,
inputValue: string,
};


const ProfileInfo = styled.View`
flex-direction: column;
align-items: center;
height: 56px;
width: 100%;
margin-bottom: 30px;
`;

const ProfileMeta = styled.View`
flex-direction: row;
height: 28px;
align-items: center;
`;

const ButtonGroup = styled.View`
flex-direction: row;
justify-content: space-between;
width: 40%;
margin-top: 15px;
`;

const ProfileName = styled.Text`
font-size: 18px;
font-weight: 700;
letter-spacing: 0.2px;
color: ${props => props.theme.text};
margin-right: 5px;
`;

const Text = styled.Text`
font-size: 18px;
font-weight: 300;
letter-spacing: 0.2px;
color: ${props => props.theme.text};
`;

const Email = styled.Text`
font-size: 16px;
font-weight: 300;
color: ${props => props.theme.dimmedText};
margin-top: 15px;
`;

const DeleteText = styled.Text`
color: ${props => props.theme.deploymentErrorText};
`;

@connect
class Profile extends React.Component<Props, State> {
state = {
editing: false,
inputValue: this.props.context.user.username,
};

static getDerivedStateFromProps = (nextProps: Props, prevState: State) => {
const { user, team } = nextProps.context;
const { inputValue } = prevState;

if (!team && inputValue !== user.username) {
// If user
return {
inputValue: user.username,
};
} else if (team && inputValue !== team.name) {
// If team
return {
inputValue: team.name,
};
}

return null;
};

toggleEditing = () => {
this.setState({ editing: !this.state.editing });
};


handleInput = (inputValue: string) => {
this.setState({ inputValue });
};

handleNameChange = (message: string) => {
const { refreshUserInfo, refreshTeamInfo, team } = this.props.context;

if (message) {
// This one doesn't have an "error" field
Alert.alert('Error', message, [{ text: 'Dismiss' }]);
} else if (team) {
refreshTeamInfo(team.id);
} else {
refreshUserInfo();
}

this.toggleEditing();
};

changeUsername = async () => {
const result = await api.user.changeUsername(this.state.inputValue);

this.handleNameChange(result.message);
};

changeTeamName = async () => {
const { team } = this.props.context;
if (!team) return;

const result = await api.teams.changeTeamName(team.id, this.state.inputValue);

this.handleNameChange(result.message);
};

deleteTeam = async () => {
const message = 'Are you sure you want delete this team?';
const { deleteTeam, team } = this.props.context;

if (!team) return;

if (isAndroid) {
Alert.alert(
message,
null,
[
{ text: 'Cancel', onPress: () => {} },
{
text: 'Delete',
onPress: async () => {
await deleteTeam(team.id);
},
},
],
{ cancelable: false },
);
} else {
ActionSheetIOS.showActionSheetWithOptions(
{
title: message,
options: ['Cancel', 'Delete'],
destructiveButtonIndex: 1,
cancelButtonIndex: 0,
},
async (buttonIndex): any => {
if (buttonIndex === 1) {
await deleteTeam(team.id);
}
},
);
}
};

render() {
const {
user,
team,
} = this.props.context;
const changeName = team ? this.changeTeamName : this.changeUsername;

const current = team
? {
avatar: team.avatar || null,
name: team.name,
}
: {
avatar: user.avatar || user.uid,
name: user.username,
};

return (
// $FlowFixMe
<React.Fragment>
<ProfilePic>
<Image
source={
current.avatar
? {
uri: api.user.avatarPath(current.avatar),
cache: 'force-cache',
}
: gradient
}
style={{ width: '100%', height: '100%' }}
/>
</ProfilePic>
<ProfileInfo>
{(() => {
if (this.state.editing) {
return (
// $FlowFixMe
<React.Fragment>
<Input
onChangeText={this.handleInput}
value={this.state.inputValue}
/>
<ButtonGroup>
<TouchableOpacity
activeOpacity={0.65}
onPress={changeName}
>
<Button>save</Button>
</TouchableOpacity>
<TouchableOpacity
activeOpacity={0.65}
onPress={this.toggleEditing}
>
<Button>cancel</Button>
</TouchableOpacity>
</ButtonGroup>
</React.Fragment>
);
}
// @TODO Team editing
return (
// $FlowFixMe
<React.Fragment>
<ProfileMeta>
<ProfileName>{`${current.name}`}</ProfileName>
{/* We can't have anything except text inside <Text> on Android, sooo */}
<Text>(</Text>
<TouchableOpacity
activeOpacity={0.65}
style={{ height: 20 }}
onPress={this.toggleEditing}
>
<Button
style={isAndroid ? { marginTop: -3 } : {}}
>
change
</Button>
</TouchableOpacity>
<Text>)</Text>
</ProfileMeta>
{team ? null : <Email>{user.email}</Email>}
</React.Fragment>
);
})()}
</ProfileInfo>
{(() => {
if (team) {
return (
<TouchableOpacity
activeOpacity={0.65}
onPress={this.deleteTeam}
>
<DeleteText>DELETE TEAM</DeleteText>
</TouchableOpacity>
);
}

return null;
})()}
</React.Fragment>
);
}
}

export default Profile;
14 changes: 14 additions & 0 deletions src/components/elements/settings/ProfilePic.js
@@ -0,0 +1,14 @@
import styled from 'styled-components';
import { isIphoneSE } from '../../../lib/utils';

const ProfilePic = styled.View`
height: 128px;
width: 128px;
border-radius: 100px;
background: #e0e0e0;
overflow: hidden;
margin-bottom: 30px;
margin-top: ${isIphoneSE() ? '60px' : '120px'};
`;

export default ProfilePic;
9 changes: 9 additions & 0 deletions src/components/elements/settings/RowText.js
@@ -0,0 +1,9 @@
import styled from 'styled-components';

const RowText = styled.Text`
font-size: 18px
font-weight: 400;
color: ${props => props.theme.text};
`;

export default RowText;
11 changes: 11 additions & 0 deletions src/components/elements/settings/Separator.js
@@ -0,0 +1,11 @@
import styled from 'styled-components';

const Separator = styled.View`
height: 1px;
border-bottom-color: ${props => props.theme.border};
border-bottom-width: 1px;
margin-vertical: 12px;
width: 80%;
`;

export default Separator;
11 changes: 11 additions & 0 deletions src/components/elements/settings/SettingsRow.js
@@ -0,0 +1,11 @@
import styled from 'styled-components';

const SettingsRow = styled.View`
width: 80%;
flex-direction: row;
justify-content: space-between;
align-items: center;
margin-vertical: 5px;
`;

export default SettingsRow;

0 comments on commit 2946ce1

Please sign in to comment.