Commit 447640de authored by Sam Gluck's avatar Sam Gluck
Browse files

- Split out user profile component into own file;

- Add logic in for displaying the selected interests as tags in user
profile;
parent 7bd5d7a1
import * as React from 'react'; import * as React from 'react';
import { Tag as ZenTag, Close } from '@zendeskgarden/react-tags'; import { Tag as ZenTag, Close } from '@zendeskgarden/react-tags';
import styled from '../../../themes/styled';
export type TagProps = { export type TagProps = {
onClick?: Function; onClick?: Function;
closeable?: boolean; closeable?: boolean;
...@@ -26,4 +28,10 @@ const Tag: React.SFC<TagProps> = ({ ...@@ -26,4 +28,10 @@ const Tag: React.SFC<TagProps> = ({
); );
}; };
export const TagContainer = styled.div`
${ZenTag} {
margin: 0 10px 10px 0;
}
`;
export default Tag; export default Tag;
import * as React from 'react'; import * as React from 'react';
import { Redirect, RouteComponentProps } from 'react-router'; import { Redirect, RouteComponentProps } from 'react-router';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faImage, faUser } from '@fortawesome/free-solid-svg-icons';
import { Grid, Row, Col } from '@zendeskgarden/react-grid'; import { Grid, Row, Col } from '@zendeskgarden/react-grid';
import styled, { StyledThemeInterface } from '../../themes/styled'; import styled, { StyledThemeInterface } from '../../themes/styled';
import Logo from '../../components/brand/Logo/Logo'; import Logo from '../../components/brand/Logo/Logo';
import Body from '../../components/chrome/Body/Body'; import Body from '../../components/chrome/Body/Body';
import H6 from '../../components/typography/H6/H6';
import PreviousStep from './PreviousStep'; import PreviousStep from './PreviousStep';
import Tag, { TagContainer } from '../../components/elements/Tag/Tag';
import Step1 from './Step1'; import Step1 from './Step1';
import Step2 from './Step2'; import Step2 from './Step2';
import P from '../../components/typography/P/P'; import UserProfile from '../user/UserProfile';
import H4 from '../../components/typography/H4/H4';
import User from '../../types/User'; import User from '../../types/User';
import H4 from '../../components/typography/H4/H4';
import Button from '../../components/elements/Button/Button';
const SignUpBody = styled(Body)` const SignUpBody = styled(Body)`
display: flex; display: flex;
...@@ -30,120 +29,6 @@ const Sidebar = styled.div` ...@@ -30,120 +29,6 @@ const Sidebar = styled.div`
props.theme.styles.colour.base5}; props.theme.styles.colour.base5};
`; `;
//TODO media queries/responsivity
const UserProfile = styled.div`
overflow: auto;
width: 75%;
height: 100%;
margin-left: 1%;
background-color: ${(props: StyledThemeInterface) =>
props.theme.styles.colour.base5};
`;
//TODO don't use any props type
const UserProfileImage = styled.div<any>`
position: relative;
width: 100%;
height: 400px;
background-color: ${props => props.theme.styles.colour.base4};
background-image: ${props =>
props.backgroundImage
? `linear-gradient(transparent, rgba(0, 0, 0, .75)), url(${
props.backgroundImage
})`
: ''};
background-position: center;
background-size: 100% auto;
color: ${props => props.theme.styles.colour.base3};
display: flex;
align-items: center;
justify-content: center;
font-size: 6rem;
`;
const liftUserAvatar = -75;
const AvatarPlaceholder = styled.div`
font-size: 12px;
position: absolute;
top: 150px;
text-align: center;
text-shadow: 1px 1px 0 #00000069;
transition: all 0.2s linear;
`;
//TODO don't use any props type
const UserAvatar = styled.div<any>`
overflow: hidden;
height: 150px;
width: 150px;
border-radius: 75px;
background-color: ${props => props.theme.styles.colour.base3};
background-image: ${props => `url(${props.backgroundImage})`};
background-position: center;
background-size: cover;
background-repeat: no-repeat;
color: ${props => props.theme.styles.colour.base5};
position: relative;
top: ${liftUserAvatar}px;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: center;
font-size: 4rem;
// avatar placeholder
> div {
font-size: 6px;
pointer-events: none;
}
&:hover > div {
top: 50%;
font-size: 12px;
}
// end avatar placeholder
// avatar font awesome icon
svg {
top: 0;
position: relative;
transition: all 0.2s linear;
pointer-events: none;
}
&:hover svg {
font-size: 30px;
top: -25px;
}
// end avatar font awesome icon
`;
const UserDetails = styled.div`
position: relative;
top: ${liftUserAvatar}px;
text-align: center;
`;
const SignUpProfileSection = styled.div`
position: relative;
top: ${liftUserAvatar}px;
padding: 50px;
`;
const FileInput = styled.input`
cursor: pointer;
height: calc(100% + 100px);
width: 100%;
position: absolute;
top: -100px;
left: 0;
&:focus {
outline: 0;
}
`;
const stepScrollTo = { const stepScrollTo = {
1: 0, 1: 0,
2: 650 2: 650
...@@ -195,12 +80,9 @@ interface SignUpState { ...@@ -195,12 +80,9 @@ interface SignUpState {
interface SignUpProps extends RouteComponentProps<SignUpMatchParams> {} interface SignUpProps extends RouteComponentProps<SignUpMatchParams> {}
const FadedFallbackText = ({ value, fallback }) => const SignUpProfileSection = styled.div<any>`
value ? ( padding: 50px;
<span>{value}</span> `;
) : (
<span style={{ color: 'lightgrey' }}>{fallback}</span>
);
const Step2Section = styled.div<any>` const Step2Section = styled.div<any>`
opacity: ${props => (props.active ? 1 : 0.1)}; opacity: ${props => (props.active ? 1 : 0.1)};
...@@ -208,9 +90,21 @@ const Step2Section = styled.div<any>` ...@@ -208,9 +90,21 @@ const Step2Section = styled.div<any>`
transition: opacity 1.75s linear; transition: opacity 1.75s linear;
`; `;
const Interests = ({ active, interests }) => ( const Interests = ({ active, interests, onTagClick }) => (
<Step2Section active={active}> <Step2Section active={active}>
<H4>Interests</H4> <H4>Interests</H4>
<TagContainer>
{interests.map(interest => (
<Tag
focused
closeable
key={interest}
onClick={() => onTagClick(interest)}
>
{interest}
</Tag>
))}
</TagContainer>
</Step2Section> </Step2Section>
); );
...@@ -265,20 +159,23 @@ export default class SignUp extends React.Component<SignUpProps, SignUpState> { ...@@ -265,20 +159,23 @@ export default class SignUp extends React.Component<SignUpProps, SignUpState> {
} }
getStepComponent() { getStepComponent() {
const stepProps = { const stepIdx = this.state.currentStep - 1;
let Step = SignUp.stepComponents[stepIdx];
if (!Step) {
return null;
}
return (
<Step
{...{
user: this.state.user, user: this.state.user,
goToNextStep: this.goToNextStep, goToNextStep: this.goToNextStep,
goToPreviousStep: this.goToPreviousStep, goToPreviousStep: this.goToPreviousStep,
randomizeEmojiId: this.randomizeEmojiId, randomizeEmojiId: this.randomizeEmojiId,
linkUserState: this.linkUserState, linkUserState: this.linkUserState,
toggleInterest: this.toggleUserInterest toggleInterest: this.toggleUserInterest
}; }}
const stepIdx = this.state.currentStep - 1; />
let Step = SignUp.stepComponents[stepIdx]; );
if (!Step) {
return null;
}
return <Step {...stepProps} />;
} }
randomizeEmojiId() { randomizeEmojiId() {
...@@ -369,7 +266,14 @@ export default class SignUp extends React.Component<SignUpProps, SignUpState> { ...@@ -369,7 +266,14 @@ export default class SignUp extends React.Component<SignUpProps, SignUpState> {
}); });
} }
setUserImage(name: string): (evt: React.SyntheticEvent) => void { /**
* Load an image from the user file system and set as `imageTypeName` on the
* state so it can be displayed without uploading.
* @param imageTypeName the type of image being set
*/
setUserImage(
imageTypeName: 'profileImage' | 'avatarImage'
): (evt: React.SyntheticEvent) => void {
return (evt: any) => { return (evt: any) => {
const reader = new FileReader(); const reader = new FileReader();
...@@ -379,7 +283,7 @@ export default class SignUp extends React.Component<SignUpProps, SignUpState> { ...@@ -379,7 +283,7 @@ export default class SignUp extends React.Component<SignUpProps, SignUpState> {
this.setState({ this.setState({
user: { user: {
...this.state.user, ...this.state.user,
[name]: reader.result as string [imageTypeName]: reader.result as string
} }
}); });
}, },
...@@ -392,6 +296,10 @@ export default class SignUp extends React.Component<SignUpProps, SignUpState> { ...@@ -392,6 +296,10 @@ export default class SignUp extends React.Component<SignUpProps, SignUpState> {
}; };
} }
private getNextStepName() {
return ['your interests', 'discover'][this.state.currentStep - 1];
}
render() { render() {
if (!('step' in this.props.match.params)) { if (!('step' in this.props.match.params)) {
return <Redirect to="/sign-up/1" />; return <Redirect to="/sign-up/1" />;
...@@ -416,81 +324,42 @@ export default class SignUp extends React.Component<SignUpProps, SignUpState> { ...@@ -416,81 +324,42 @@ export default class SignUp extends React.Component<SignUpProps, SignUpState> {
</Col> </Col>
</Row> </Row>
{this.getStepComponent()} {this.getStepComponent()}
</Grid> <Row style={{ flexGrow: 1 }} />
</Sidebar> <Row>
<UserProfile innerRef={e => (this._profileElem = e)}> {this.getNextStepName() ? (
<UserProfileImage <Col
title="Click to select a profile background"
backgroundImage={this.state.user.profileImage}
>
<FileInput
onChange={this.setUserImage('profileImage')}
type="file"
/>
{!this.state.user.profileImage ? (
<div
style={{
pointerEvents: 'none',
position: 'relative',
top: '-20px',
textAlign: 'center'
}}
>
<FontAwesomeIcon icon={faImage} />
<P
style={{ style={{
pointerEvents: 'none', display: 'flex',
marginTop: '-12px', alignItems: 'center',
fontSize: '.8rem', color: 'grey'
textShadow: '1px 1px 0 lightgrey'
}} }}
> >
Click to select Next: {this.getNextStepName()}
<br />a profile background </Col>
</P>
</div>
) : null} ) : null}
</UserProfileImage> <Col style={{ display: 'flex', justifyContent: 'flex-end' }}>
<UserAvatar {this.state.currentStep > 1 ? (
title="Click to select your profile image"
backgroundImage={this.state.user.avatarImage}
>
<FileInput
onChange={this.setUserImage('avatarImage')}
type="file"
/>
{!this.state.user.avatarImage ? (
<> <>
<FontAwesomeIcon icon={faUser} /> <Button secondary onClick={this.goToNextStep}>
<AvatarPlaceholder> Skip
Click to select </Button>
<br />a profile picture <div style={{ height: '10px', width: '10px' }} />
</AvatarPlaceholder>
</> </>
) : null} ) : null}
</UserAvatar> <Button onClick={this.goToNextStep}>Continue</Button>
<UserDetails> </Col>
<H6>{this.state.user.emojiId}</H6> </Row>
<H6> </Grid>
<FadedFallbackText </Sidebar>
value={this.state.user.username} <UserProfile
fallback="joebloggs84" innerRef={e => (this._profileElem = e)}
/> user={this.state.user}
</H6> setUserImage={this.setUserImage}
<H6> body={({ containerProps }) => {
<FadedFallbackText return (
value={this.state.user.role} <SignUpProfileSection {...containerProps}>
fallback="Head Teacher"
/>
<span style={{ color: 'lightgrey' }}>&nbsp;&nbsp;</span>
<FadedFallbackText
value={this.state.user.location}
fallback="London, UK"
/>
</H6>
</UserDetails>
<SignUpProfileSection>
<Interests <Interests
onTagClick={this.toggleUserInterest}
active={this.state.currentStep > 1} active={this.state.currentStep > 1}
interests={this.state.user.interests} interests={this.state.user.interests}
/> />
...@@ -499,9 +368,9 @@ export default class SignUp extends React.Component<SignUpProps, SignUpState> { ...@@ -499,9 +368,9 @@ export default class SignUp extends React.Component<SignUpProps, SignUpState> {
languages={this.state.user.languages} languages={this.state.user.languages}
/> />
</SignUpProfileSection> </SignUpProfileSection>
{/*padding forces profile to be scrollable*/} );
<div style={{ height: '75%', width: '100%' }} /> }}
</UserProfile> />
</SignUpBody> </SignUpBody>
); );
} }
......
...@@ -18,7 +18,7 @@ const OverflowCol = styled(Col)` ...@@ -18,7 +18,7 @@ const OverflowCol = styled(Col)`
overflow: auto; overflow: auto;
`; `;
export default ({ user, goToNextStep, randomizeEmojiId, linkUserState }) => { export default ({ user, randomizeEmojiId, linkUserState }) => {
return ( return (
<> <>
<Row> <Row>
...@@ -97,15 +97,6 @@ export default ({ user, goToNextStep, randomizeEmojiId, linkUserState }) => { ...@@ -97,15 +97,6 @@ export default ({ user, goToNextStep, randomizeEmojiId, linkUserState }) => {
</TextField> </TextField>
</OverflowCol> </OverflowCol>
</Row> </Row>
<Row style={{ flexGrow: 1 }} />
<Row>
<Col style={{ display: 'flex', alignItems: 'center', color: 'grey' }}>
Next: your interests
</Col>
<Col style={{ display: 'flex', justifyContent: 'flex-end' }}>
<Button onClick={goToNextStep}>Continue</Button>
</Col>
</Row>
</> </>
); );
}; };
import * as React from 'react'; import * as React from 'react';
import { Col, Row } from '@zendeskgarden/react-grid'; import { Col, Row } from '@zendeskgarden/react-grid';
import { Tag as ZenTag } from '@zendeskgarden/react-tags';
import H6 from '../../components/typography/H6/H6'; import H6 from '../../components/typography/H6/H6';
import P from '../../components/typography/P/P'; import P from '../../components/typography/P/P';
import Button from '../../components/elements/Button/Button'; import Tag, { TagContainer } from '../../components/elements/Tag/Tag';
import styled from '../../themes/styled';
import Tag from '../../components/elements/Tag/Tag';
const Spacer = styled.div`
width: 10px;
height: 10px;
`;
//TODO get tags from the API //TODO get tags from the API
const words = `offer const words = `offer
...@@ -32,12 +24,6 @@ support ...@@ -32,12 +24,6 @@ support
shell shell
neck`; neck`;
const TagContainer = styled.div`
${ZenTag} {
margin: 0 5px 5px 0;
}
`;
// https://stackoverflow.com/a/6274381/2039244 // https://stackoverflow.com/a/6274381/2039244
function shuffle(a) { function shuffle(a) {
for (let i = a.length - 1; i > 0; i--) { for (let i = a.length - 1; i > 0; i--) {
...@@ -65,7 +51,14 @@ export default ({ user, goToNextStep, toggleInterest }) => { ...@@ -65,7 +51,14 @@ export default ({ user, goToNextStep, toggleInterest }) => {
</P> </P>
<TagContainer> <TagContainer>
{words2.map(word => ( {words2.map(word => (
<Tag key={word}>{word}</Tag> <Tag
closeable={user.interests.includes(word)}
focused={user.interests.includes(word)}
key={word}
onClick={() => toggleInterest(word)}
>
{word}
</Tag>
))} ))}
</TagContainer> </TagContainer>
<P style={{ fontWeight: 'bold' }}>Popular on MoodleNet</P> <P style={{ fontWeight: 'bold' }}>Popular on MoodleNet</P>
...@@ -73,8 +66,8 @@ export default ({ user, goToNextStep, toggleInterest }) => { ...@@ -73,8 +66,8 @@ export default ({ user, goToNextStep, toggleInterest }) => {
<TagContainer> <TagContainer>
{words1.map(word => ( {words1.map(word => (
<Tag <Tag
focused={user.interests.includes(word)}
key={word} key={word}
type={user.interests.includes(word) ? 'green' : undefined}
onClick={() => toggleInterest(word)} onClick={() => toggleInterest(word)}
> >
{word} {word}
...@@ -83,19 +76,6 @@ export default ({ user, goToNextStep, toggleInterest }) => { ...@@ -83,19 +76,6 @@ export default ({ user, goToNextStep, toggleInterest }) => {
</TagContainer> </TagContainer>
</Col> </Col>
</Row> </Row>
<Row style={{ flexGrow: 1 }} />
<Row>
<Col style={{ display: 'flex', alignItems: 'center', color: 'grey' }}>
Next: discover
</Col>
<Col style={{ display: 'flex', justifyContent: 'flex-end' }}>
<Button secondary onClick={goToNextStep}>
Skip
</Button>
<Spacer />
<Button onClick={goToNextStep}>Continue</Button>
</Col>
</Row>
</> </>
); );
}; };
import * as React from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faImage, faUser } from '@fortawesome/free-solid-svg-icons';
import P from '../../components/typography/P/P';
import H6 from '../../components/typography/H6/H6';
import styled, { StyledThemeInterface } from '../../themes/styled';
import User from '../../types/User';
const FileInput = styled.input`
cursor: pointer;
height: calc(100% + 100px);
width: 100%;
position: absolute;
top: -100px;
left: 0;
&:focus {
outline: 0;
}
`;
//TODO media queries/responsivity
export const UserProfile = styled.div`
overflow: auto;