import React from 'react'; import { MessageDescriptor, IntlShape, injectIntl } from 'react-intl'; import { UserContextValue, UserContext } from '@tutorbook/account'; import { signup } from '@tutorbook/account/signup'; import { TextField } from '@rmwc/textfield'; import { ListDivider } from '@rmwc/list'; import { Card } from '@rmwc/card'; import { Availability, UserInterface, SocialTypeAlias, User, Aspect, SocialInterface, Option, } from '@tutorbook/model'; import Title from '@tutorbook/title'; import PhotoInput from '@tutorbook/photo-input'; import ScheduleInput from '@tutorbook/schedule-input'; import SubjectSelect from '@tutorbook/subject-select'; import LangSelect from '@tutorbook/lang-select'; import Loader from '@tutorbook/loader'; import Button from '@tutorbook/button'; import firebase from '@tutorbook/firebase'; import msgs from './msgs'; import styles from './volunteer-form.module.scss'; interface VolunteerFormProps { intl: IntlShape; aspect: Aspect; } type VolunteerFormState = { headerHeight: number; descHeight: number; submittingMentor: boolean; submittingTutor: boolean; submittedMentor: boolean; submittedTutor: boolean; tutoringSubjects: Option[]; mentoringSubjects: Option[]; langs: Option[]; }; /** * Wrapper for the two distinct volunteer sign-up forms: * 0. The mentor sign-up form where experts (e.g. grad students, professionals) * tell us what they're working on so we can match them up with students who are * interested in working on the same thing. * 1. The volunteer tutor sign-up form where altruistic individuals can sign-up * to help tutor somebody affected by COVID-19. */ class VolunteerForm extends React.Component< VolunteerFormProps, VolunteerFormState > { public static readonly contextType: React.Context< UserContextValue > = UserContext; public readonly context: UserContextValue; private readonly headerRef: React.RefObject; private readonly descRef: React.RefObject; public constructor(props: VolunteerFormProps, context: UserContextValue) { super(props); this.context = context; this.state = { headerHeight: 0, descHeight: 0, submittingMentor: false, submittingTutor: false, submittedMentor: false, submittedTutor: false, tutoringSubjects: [], mentoringSubjects: [], langs: [], }; this.headerRef = React.createRef(); this.descRef = React.createRef(); this.handleSubmit = this.handleSubmit.bind(this); } public componentDidMount(): void { const { headerHeight, descHeight } = this.state; if (this.headerRef.current) { const newHeight: number = this.headerRef.current.clientHeight; if (newHeight !== headerHeight) this.setState({ headerHeight: newHeight }); } if (this.descRef.current) { const newHeight: number = this.descRef.current.clientHeight; if (newHeight !== descHeight) this.setState({ descHeight: newHeight }); } } private get submitting(): boolean { const { aspect } = this.props; const { submittingMentor, submittingTutor } = this.state; return aspect === 'mentoring' ? submittingMentor : submittingTutor; } private get submitted(): boolean { const { aspect } = this.props; const { submittedMentor, submittedTutor } = this.state; return aspect === 'mentoring' ? submittedMentor : submittedTutor; } private getHeaderStyle(a: Aspect): Record { const { aspect } = this.props; const { headerHeight } = this.state; if (aspect === a) return {}; const height: string = headerHeight ? `${headerHeight}px` : '125px'; const transform: string = aspect === 'mentoring' ? `translateY(-${height})` : `translateY(${height})`; return { transform }; } private getDescStyle(a: Aspect): Record { const { aspect } = this.props; const { descHeight } = this.state; if (aspect === a) return {}; const height: string = descHeight ? `${descHeight}px` : '84px'; const transform: string = aspect === 'mentoring' ? `translateY(-${height})` : `translateY(${height})`; return { transform }; } private async handleSubmit(event: React.FormEvent): Promise { event.preventDefault(); const { user } = this.context; const { aspect } = this.props; firebase.analytics().logEvent('sign_up', { method: aspect === 'mentoring' ? 'mentor_form' : 'tutor_form', }); this.setState(({ submittingMentor, submittingTutor }) => ({ submittingMentor: aspect === 'mentoring' || submittingMentor, submittingTutor: aspect === 'tutoring' || submittingTutor, })); await signup(user); this.setState( ({ submittedMentor, submittedTutor, submittingMentor, submittingTutor, }) => ({ submittedMentor: aspect === 'mentoring' || submittedMentor, submittedTutor: aspect === 'tutoring' || submittedTutor, submittingMentor: aspect === 'mentoring' && !submittingMentor, submittingTutor: aspect === 'tutoring' && !submittingTutor, }) ); setTimeout( () => this.setState(({ submittedMentor, submittedTutor }) => ({ submittedMentor: aspect === 'mentoring' && !submittedMentor, submittedTutor: aspect === 'tutoring' && !submittedTutor, })), 2000 ); } private renderInputs(): JSX.Element { const { intl, aspect } = this.props; const { updateUser, user } = this.context; const { langs, mentoringSubjects, tutoringSubjects } = this.state; const msg = (message: MessageDescriptor) => intl.formatMessage(message); const sharedProps = { className: styles.formField, outlined: true, }; const shared = (key: Extract) => ({ ...sharedProps, label: msg(msgs[key]), onChange: (event: React.FormEvent) => updateUser( new User({ ...user, [key]: event.currentTarget.value, }) ), }); const getSocialIndex = (type: string) => { return user.socials.findIndex((s: SocialInterface) => s.type === type); }; const getSocial = (type: SocialTypeAlias) => { const index: number = getSocialIndex(type); return index >= 0 ? user.socials[index].url : ''; }; const hasSocial = (type: SocialTypeAlias) => getSocialIndex(type) >= 0; const updateSocial = (type: SocialTypeAlias, url: string) => { const index: number = getSocialIndex(type); const socials: SocialInterface[] = Array.from(user.socials); if (index >= 0) { socials[index] = { type, url }; } else { socials.push({ type, url }); } return updateUser(new User({ ...user, socials })); }; const s = (type: SocialTypeAlias, placeholder: (v: string) => string) => ({ ...sharedProps, value: getSocial(type), label: msg(msgs[type]), onFocus: () => { const name: string = user.name ? user.name.replace(' ', '').toLowerCase() : 'yourname'; if (!hasSocial(type)) { void updateSocial(type, placeholder(name)); } }, onChange: (event: React.FormEvent) => { return updateSocial(type, event.currentTarget.value); }, }); return ( <> updateUser(new User({ ...user, photo }))} /> []) => { this.setState({ langs: newLangs }); return updateUser( new User({ ...user, langs: newLangs.map((lang: Option) => lang.value), }) ); }} required /> {aspect === 'mentoring' && ( <> []) => { this.setState({ mentoringSubjects: subjects }); return updateUser( new User({ ...user, [aspect]: { subjects: subjects.map((subject) => subject.value), searches: user[aspect].searches, }, }) ); }} aspect={aspect} required /> updateUser( new User({ ...user, bio: event.currentTarget.value, }) ) } value={user.bio} label={msg(msgs.project)} placeholder={msg(msgs.projectPlaceholder)} required rows={4} textarea /> )} {aspect === 'tutoring' && ( <> []) => { this.setState({ tutoringSubjects: subjects }); return updateUser( new User({ ...user, [aspect]: { subjects: subjects.map((subject) => subject.value), searches: user[aspect].searches, }, }) ); }} aspect={aspect} required /> updateUser( new User({ ...user, availability, }) ) } required /> updateUser( new User({ ...user, bio: event.currentTarget.value, }) ) } value={user.bio} label={msg(msgs.experience)} placeholder={msg(msgs.experiencePlaceholder)} required rows={4} textarea /> )} `https://${v}.com`)} /> `https://linkedin.com/in/${v}`)} /> `https://twitter.com/${v}`)} /> `https://facebook.com/${v}`)} /> `https://instagram.com/${v}`)} /> `https://github.com/${v}`)} /> `https://indiehackers.com/${v}`)} /> ); } public render(): JSX.Element { const { user } = this.context; const { intl, aspect } = this.props; const label = aspect === 'mentoring' ? msgs.mentorSubmit : msgs.tutorSubmit; const msg = (message: MessageDescriptor) => intl.formatMessage(message); return (
{msg(msgs.mentorHeader)} {msg(msgs.tutorHeader)}
{msg(msgs.mentorDesc)} {msg(msgs.tutorDesc)}
{this.renderInputs()} {!user.id && (
); } } export default injectIntl(VolunteerForm);