import { Fragment } from 'react' import ReactSwipeableViews from 'react-swipeable-views' import ImmutablePropTypes from 'react-immutable-proptypes' import ImmutablePureComponent from 'react-immutable-pure-component' import { CX, BREAKPOINT_EXTRA_SMALL, GAB_COM_INTRODUCE_YOURSELF_GROUP_ID, } from '../constants' import { me } from '../initial_state' import { saveShownOnboarding } from '../actions/onboarding' import { fetchGroups } from '../actions/groups' import { saveUserProfileInformation } from '../actions/user' import { makeGetAccount } from '../selectors' import Button from '../components/button' import Divider from '../components/divider' import FileInput from '../components/file_input' import GroupListItem from '../components/group_list_item' import Heading from '../components/heading' import Icon from '../components/icon' import Image from '../components/image' import Input from '../components/input' import Text from '../components/text' import ComposeFormContainer from './compose/containers/compose_form_container' import Responsive from './ui/util/responsive_component' class SlideWelcome extends PureComponent { render() { return ( <div className={[_s.default, _s.width100PC, _s.height100PC].join(' ')}> <Image src='/headers/onboarding.png' alt='Welcome to Gab' /> <div className={[_s.default, _s.px15, _s.py15].join(' ')}> <Text size='large'>Gab is the home of free speech online and a place where users shape their own experience. </Text> <br /> <Text size='large'>You will discover many different ideas, people, and topics on Gab.</Text> <br /> <Text size='large'>Follow the people you find interesting and block or mute people you don't want to associate with.</Text> <br /> <Text size='large'>Speak freely, associate freely!</Text> <br /> <Text size='large'>Let's get started!</Text> </div> </div> ) } } class SlidePhotos extends ImmutablePureComponent { static propTypes = { account: ImmutablePropTypes.map.isRequired, } state = { displayNameValue: this.props.account.get('display_name'), } handleCoverPhotoChange = (e) => { try { this.props.onSave({ header: e.target.files[0] }) } catch (error) { // } } handleProfilePhotoChange = (e) => { try { this.props.onSave({ avatar: e.target.files[0] }) } catch (error) { // } } handleDisplayNameChange = (value) => { this.setState({ displayNameValue: value }) } handleDisplayNameBlur = () => { this.props.onSave({ displayName: this.state.displayNameValue, }) } render() { const { displayNameValue } = this.state return ( <div className={[_s.default, _s.width100PC].join(' ')}> <div className={[_s.default, _s.px15, _s.py15, _s.alignItemsCenter].join(' ')}> <div className={[_s.default, _s.py10, _s.maxWidth640PX].join(' ')}> <Text size='large' align='center'>Set your cover photo, profile photo and enter your display name so people can find you.</Text> </div> <div className={[_s.default, _s.mt15, _s.width100PC, _s.alignItemsCenter].join(' ')}> <div className={[_s.default, _s.border1PX, _s.borderColorSecondary, _s.overflowHidden, _s.radiusSmall, _s.bgPrimary].join(' ')}> <FileInput width='300px' height='140px' id='cover-photo' onChange={this.handleCoverPhotoChange} /> <div className={[_s.default, _s.mtNeg32PX, _s.alignItemsCenter, _s.justifyContentCenter].join(' ')}> <FileInput width='124px' height='124px' id='profile-photo' className={[_s.circle, _s.border6PX, _s.borderColorWhite, _s.bgPrimary].join(' ')} onChange={this.handleProfilePhotoChange} /> </div> <div className={[_s.default, _s.py5, _s.px15, _s.mt5, _s.mb15].join(' ')}> <Input id='display-name' title='Display name' placeholder='Add your name...' value={displayNameValue} onChange={this.handleDisplayNameChange} onBlur={this.handleDisplayNameBlur} /> </div> </div> </div> </div> </div> ) } } class SlideGroups extends ImmutablePureComponent { static propTypes = { groupIds: ImmutablePropTypes.list, } render() { const { groupIds } = this.props return ( <div className={[_s.default, _s.width100PC].join(' ')}> <div className={[_s.default, _s.py15, _s.alignItemsCenter].join(' ')}> <div className={[_s.default, _s.px15, _s.mb15].join(' ')}> <Text size='large'>Gab Groups are a great way to connect with people who share your interests. Please select a few groups to get started.</Text> </div> <div className={[_s.default, _s.width100PC].join(' ')}> { groupIds.map((groupId, i) => ( <GroupListItem isAddable isStatic key={`group-collection-item-${i}`} id={groupId} isLast={groupIds.count() - 1 === i} /> )) } </div> </div> </div> ) } } class SlideFirstPost extends PureComponent { static propTypes = { submitted: PropTypes.bool.isRequired, onNext: PropTypes.func.isRequired, } render() { const { submitted } = this.props return ( <div className={[_s.default, _s.width100PC].join(' ')}> <div className={[_s.default, _s.py15, _s.px15].join(' ')}> { !submitted && <Fragment> <Text size='large' className={_s.pb10}>Now let's make your very first Gab post! Please introduce yourself to the Gab community. How did you hear about Gab? What are you interested in?</Text> <br /> <Divider /> <div className={[_s.default, _s.mt15, _s.boxShadowBlock, _s.radiusSmall].join(' ')}> <ComposeFormContainer groupId={GAB_COM_INTRODUCE_YOURSELF_GROUP_ID} hidePro autoFocus autoJoinGroup /> </div> </Fragment> } { submitted && <Fragment> <Text size='large' align='center'>Your gab was posted!</Text> <br /> <Text size='large' align='center'>Welcome to our community, remember to speak freely.</Text> <br /> <Button href='/home' onClick={this.props.onNext} > Finish </Button> </Fragment> } </div> </div> ) } } const mapStateToProps = (state) => ({ account: makeGetAccount()(state, me), groupIds: state.getIn(['group_lists', 'featured', 'items']), shownOnboarding: state.getIn(['settings', 'shownOnboarding'], false), isSubmitting: state.getIn(['compose', 'is_submitting']), }) const mapDispatchToProps = (dispatch) => ({ onSaveShownOnboarding: () => dispatch(saveShownOnboarding()), onFetchFeaturedGroups: () => dispatch(fetchGroups('featured')), onSaveUserProfileInformation(data) { dispatch(saveUserProfileInformation(data)) }, }) export default @connect(mapStateToProps, mapDispatchToProps) class Introduction extends ImmutablePureComponent { static propTypes = { account: ImmutablePropTypes.map.isRequired, groupIds: ImmutablePropTypes.list, isSubmitting: PropTypes.bool.isRequired, shownOnboarding: PropTypes.bool.isRequired, onSaveShownOnboarding: PropTypes.func.isRequired, onFetchFeaturedGroups: PropTypes.func.isRequired, onSaveUserProfileInformation: PropTypes.func.isRequired, } state = { currentIndex: 0, submittedFirstPost: false, } componentDidMount() { window.addEventListener('keyup', this.handleKeyUp) this.props.onFetchFeaturedGroups() this.props.onSaveShownOnboarding() } componentDidUpdate(prevProps) { if (!this.state.submittedFirstPost && !prevProps.isSubmitting && this.props.isSubmitting) { this.setState({ submittedFirstPost: true }) } } componentWillUnmount() { window.addEventListener('keyup', this.handleKeyUp) } handleDot = (e) => { const i = Number(e.currentTarget.getAttribute('data-index')) e.preventDefault() this.setState({ currentIndex: i }) } handlePrev = () => { this.setState(({ currentIndex }) => ({ currentIndex: Math.max(0, currentIndex - 1), })) } handleNext = () => { const newIndex = Math.min(this.state.currentIndex + 1, 3) this.setState({ currentIndex: newIndex, }) } handleSwipe = (index) => { this.setState({ currentIndex: index }) } handleKeyUp = ({ key }) => { switch (key) { case 'ArrowLeft': this.handlePrev() break case 'ArrowRight': this.handleNext() break } } handleOnSaveUserProfileInformation = (data) => { this.props.onSaveUserProfileInformation(data) } render() { const { account, groupIds } = this.props const { currentIndex, submittedFirstPost } = this.state const pages = [ <SlideWelcome />, <SlidePhotos account={account} onSave={this.handleOnSaveUserProfileInformation} />, <SlideGroups groupIds={groupIds} />, <SlideFirstPost submitted={submittedFirstPost} onNext={this.handleNext} />, ] const titles = [ `Welcome to Gab!`, 'Complete your profile', 'Find your people', 'Start a conversation', ] const title = titles[currentIndex] const pagination = pages.map((page, i) => { const btnClasses = CX({ default: 1, width10PX: 1, height10PX: 1, outlineNone: 1, circle: 1, cursorPointer: 1, bgBrandLightOpaque: i !== currentIndex, bgBrand: i === currentIndex, }) return ( <li className={[_s.default, _s.px5].join(' ')} key={`intro-slide-${i}`}> <button tabIndex='0' className={btnClasses} onClick={this.handleDot} data-index={i} /> </li> ) }) const nextTitle = currentIndex === 3 ? 'Finish' : 'Next' return ( <div className={[_s.default, _s.width100PC, _s.heightMax80VH].join(' ')}> <div className={[_s.default, _s.flexRow, _s.alignItemsCenter, _s.justifyContentCenter, _s.borderBottom1PX, _s.borderColorSecondary, _s.height53PX, _s.px15].join(' ')}> <Responsive min={BREAKPOINT_EXTRA_SMALL}> <Heading> {title} </Heading> </Responsive> <Responsive max={BREAKPOINT_EXTRA_SMALL}> <Heading size='h2'> {title} </Heading> </Responsive> <div className={[_s.mlAuto].join(' ')}> <Button href={currentIndex === 3 ? '/home' : undefined} onClick={this.handleNext} className={_s.px10} icon={currentIndex !== 3 ? 'arrow-right' : undefined} iconSize={currentIndex !== 3 ? '18px' : undefined} > { currentIndex === 3 && <Fragment> <Responsive min={BREAKPOINT_EXTRA_SMALL}> <Text color='white' className={_s.px5}>{nextTitle}</Text> </Responsive> <Responsive max={BREAKPOINT_EXTRA_SMALL}> <Icon id='check' size='14px' className={_s.fillWhite} /> </Responsive> </Fragment> } </Button> </div> </div> <ReactSwipeableViews index={currentIndex} onChangeIndex={this.handleSwipe} className={[_s.default, _s.flexNormal, _s.heightCalc80VH106PX].join(' ')} containerStyle={{ width: '100%', }} slideStyle={{ // height: '100%', }} > { pages.map((page, i) => ( <div key={i} className={[_s.default, _s.heightCalc80VH106PX].join(' ')}> {page} </div> )) } </ReactSwipeableViews> <div className={[_s.default, _s.px15, _s.height53PX, _s.alignItemsCenter, _s.justifyContentCenter, _s.borderTop1PX, _s.borderColorSecondary, _s.width100PC, _s.flexRow].join(' ')}> <div className={[_s.default, _s.width50PX, _s.mrAuto].join(' ')}> { currentIndex !== 0 && <Button className={_s.opacity05} onClick={this.handlePrev} icon='arrow-left' backgroundColor='none' color='secondary' iconSize='20px' /> } </div> <ul className={[_s.default, _s.height100PC, _s.flexGrow1, _s.alignItemsCenter, _s.justifyContentCenter, _s.flexRow, _s.listStyleNone].join(' ')}> {pagination} </ul> <Button isText href={currentIndex === 3 ? '/home' : undefined} className={[_s.default, _s.width50PX, _s.height100PC, _s.justifyContentCenter, _s.pr0, _s.pl0, _s.mlAuto, _s.opacity05].join(' ')} onClick={this.handleNext} backgroundColor='none' color='secondary' > <Text color='inherit' align='right'>{nextTitle}</Text> </Button> </div> </div> ) } }