zcash-grant-system/frontend/client/components/Profile/ProfileEdit.tsx

252 lines
6.9 KiB
TypeScript
Raw Normal View History

import React from 'react';
import lodash from 'lodash';
2018-11-16 19:33:25 -08:00
import axios from 'api/axios';
import { Input, Form, Col, Row, Button, Alert } from 'antd';
import { SOCIAL_INFO, socialMediaToUrl } from 'utils/social';
import { SOCIAL_SERVICE, User } from 'types';
import { UserState } from 'modules/users/reducers';
import { getCreateTeamMemberError } from 'modules/create/utils';
2018-11-16 19:33:25 -08:00
import AvatarEdit from './AvatarEdit';
import './ProfileEdit.less';
interface Props {
user: UserState;
onDone(): void;
onEdit(user: User): void;
}
interface State {
fields: User;
isChanged: boolean;
showError: boolean;
}
export default class ProfileEdit extends React.PureComponent<Props, State> {
state: State = {
fields: { ...this.props.user } as User,
isChanged: false,
showError: false,
};
componentDidUpdate(prevProps: Props, _: State) {
if (
prevProps.user.isUpdating &&
!this.props.user.isUpdating &&
!this.state.showError
) {
this.setState({ showError: true });
}
if (
prevProps.user.isUpdating &&
!this.props.user.isUpdating &&
!this.props.user.updateError
) {
this.props.onDone();
}
}
render() {
const { fields } = this.state;
const error = getCreateTeamMemberError(fields);
2018-12-14 11:36:22 -08:00
const isMissingField = !fields.displayName || !fields.title || !fields.emailAddress;
const isDisabled = !!error || isMissingField || !this.state.isChanged;
return (
<>
<div className="ProfileEdit">
2018-11-16 19:33:25 -08:00
<AvatarEdit
user={fields}
onDone={this.handleChangePhoto}
onDelete={this.handleDeletePhoto}
/>
<div className="ProfileEdit-info">
<Form
className="ProfileEdit-info-form"
layout="vertical"
onSubmit={this.handleSave}
>
<Form.Item>
<Input
2018-12-14 11:36:22 -08:00
name="displayName"
autoComplete="off"
placeholder="Display name (Required)"
value={fields.displayName}
onChange={this.handleChangeField}
/>
</Form.Item>
<Form.Item>
<Input
name="title"
autoComplete="off"
placeholder="Title (Required)"
value={fields.title}
onChange={this.handleChangeField}
/>
</Form.Item>
<Form.Item>
<Input
name="emailAddress"
disabled={true}
placeholder="Email address (Required)"
type="email"
autoComplete="email"
value={fields.emailAddress}
onChange={this.handleChangeField}
/>
</Form.Item>
<Row gutter={12}>
{Object.values(SOCIAL_INFO).map(s => {
const field = fields.socialMedias.find(sm => sm.service === s.service);
return (
<Col xs={24} sm={12} key={s.service}>
<Form.Item>
<Input
placeholder={`${s.name} account`}
autoComplete="off"
value={field ? field.username : ''}
onChange={ev => this.handleSocialChange(ev, s.service)}
addonBefore={s.icon}
/>
</Form.Item>
</Col>
);
})}
</Row>
{!isMissingField &&
error && (
<Alert
type="error"
message={error}
showIcon
style={{ marginBottom: '0.75rem' }}
/>
)}
<Row>
<Button
type="primary"
htmlType="submit"
disabled={isDisabled}
loading={this.props.user.isUpdating}
>
Save changes
</Button>
<Button type="ghost" htmlType="button" onClick={this.handleCancel}>
Cancel
</Button>
</Row>
</Form>
{this.state.showError &&
this.props.user.updateError && (
<Alert
className="ProfileEdit-alert"
message={`There was an error attempting to update your profile. (code ${
this.props.user.updateError
})`}
type="error"
/>
)}
</div>
</div>
<div className="ProfileEditShade" />
</>
);
}
private handleSave = (evt: React.SyntheticEvent<any>) => {
evt.preventDefault();
this.props.onEdit(this.state.fields);
};
private handleCancel = () => {
2018-11-26 17:14:00 -08:00
const propsAvatar = this.props.user.avatar;
const stateAvatar = this.state.fields.avatar;
2018-11-16 19:33:25 -08:00
// cleanup uploaded file if we cancel
2018-12-14 11:36:22 -08:00
if (
stateAvatar &&
stateAvatar.imageUrl &&
(!propsAvatar || propsAvatar.imageUrl !== stateAvatar.imageUrl)
) {
2018-11-16 19:33:25 -08:00
axios.delete('/api/v1/users/avatar', {
2018-11-26 17:14:00 -08:00
params: { url: stateAvatar.imageUrl },
2018-11-16 19:33:25 -08:00
});
}
this.props.onDone();
};
private handleChangeField = (ev: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = ev.currentTarget;
const fields = {
...this.state.fields,
[name as any]: value,
};
const isChanged = this.isChangedCheck(fields);
this.setState({
isChanged,
fields,
});
};
private handleSocialChange = (
ev: React.ChangeEvent<HTMLInputElement>,
service: SOCIAL_SERVICE,
) => {
const { value } = ev.currentTarget;
// First remove...
const socialMedias = this.state.fields.socialMedias.filter(
sm => sm.service !== service,
);
if (value) {
// Then re-add if there as a value
socialMedias.push({
service,
username: value,
url: socialMediaToUrl(service, value),
});
}
const fields = {
...this.state.fields,
socialMedias,
};
const isChanged = this.isChangedCheck(fields);
this.setState({
isChanged,
fields,
});
};
2018-11-16 19:33:25 -08:00
private handleChangePhoto = (url: string) => {
const fields = {
...this.state.fields,
avatar: {
2018-11-26 17:14:00 -08:00
imageUrl: url,
},
};
const isChanged = this.isChangedCheck(fields);
this.setState({
isChanged,
fields,
});
};
private handleDeletePhoto = () => {
const fields = {
...this.state.fields,
avatar: null,
};
const isChanged = this.isChangedCheck(fields);
this.setState({ isChanged, fields });
};
private isChangedCheck = (a: User) => {
return !lodash.isEqual(a, this.props.user);
};
}