import BN from 'bn.js'; import React from 'react'; import { Link } from 'react-router-dom'; import { Form, Field, withFormik, FormikProps } from 'formik'; import * as Yup from 'yup'; import { Option, Vec } from '@polkadot/types'; import Section from '@polkadot/joy-utils/Section'; import TxButton from '@polkadot/joy-utils/TxButton'; import * as JoyForms from '@polkadot/joy-utils/forms'; import { SubmittableResult } from '@polkadot/api'; import { MemberId, UserInfo, Profile, PaidTermId, PaidMembershipTerms } from '@joystream/types/members'; import { OptionText } from '@joystream/types/common'; import { MyAccountProps, withMyAccount } from '@polkadot/joy-utils/MyAccount'; import { queryMembershipToProp } from './utils'; import { withCalls } from '@polkadot/react-api/index'; import { Button, Message } from 'semantic-ui-react'; import { formatBalance } from '@polkadot/util'; import { TxFailedCallback, TxCallback } from '@polkadot/react-components/Status/types'; import isEqual from 'lodash/isEqual'; // TODO get next settings from Substrate: const HANDLE_REGEX = /^[a-z0-9_]+$/; const buildSchema = (p: ValidationProps) => Yup.object().shape({ handle: Yup.string() .matches(HANDLE_REGEX, 'Handle can have only lowercase letters (a-z), numbers (0-9) and underscores (_).') .min(p.minHandleLength, `Handle is too short. Minimum length is ${p.minHandleLength} chars.`) .max(p.maxHandleLength, `Handle is too long. Maximum length is ${p.maxHandleLength} chars.`) .required('Handle is required'), avatar: Yup.string() .url('Avatar must be a valid URL of an image.') .max(p.maxAvatarUriLength, `Avatar URL is too long. Maximum length is ${p.maxAvatarUriLength} chars.`), about: Yup.string().max(p.maxAboutTextLength, `Text is too long. Maximum length is ${p.maxAboutTextLength} chars.`) }); type ValidationProps = { minHandleLength: number; maxHandleLength: number; maxAvatarUriLength: number; maxAboutTextLength: number; }; type OuterProps = ValidationProps & { profile?: Profile; paidTerms: PaidMembershipTerms; paidTermId: PaidTermId; memberId?: MemberId; }; type FormValues = { handle: string; avatar: string; about: string; }; type FieldName = keyof FormValues; type FormProps = OuterProps & FormikProps; const LabelledField = JoyForms.LabelledField(); const LabelledText = JoyForms.LabelledText(); const InnerForm = (props: FormProps) => { const { profile, paidTerms, paidTermId, initialValues, values, touched, dirty, isValid, isSubmitting, setSubmitting, resetForm, memberId } = props; const onSubmit = (sendTx: () => void) => { if (isValid) sendTx(); }; const onTxFailed: TxFailedCallback = (txResult: SubmittableResult | null) => { setSubmitting(false); if (txResult == null) { // Tx cancelled. } }; const onTxSuccess: TxCallback = (_txResult: SubmittableResult) => { setSubmitting(false); }; // TODO extract to forms.tsx const isFieldChanged = (field: FieldName): boolean => { return dirty && touched[field] === true && !isEqual(values[field], initialValues[field]); }; // TODO extract to forms.tsx const fieldToTextOption = (field: FieldName): OptionText => { return isFieldChanged(field) ? OptionText.some(values[field]) : OptionText.none(); }; const buildTxParams = () => { if (!isValid) return []; const userInfo = new UserInfo({ handle: fieldToTextOption('handle'), avatar_uri: fieldToTextOption('avatar'), about: fieldToTextOption('about') }); if (profile) { // update profile return [memberId, userInfo]; } else { // register as new member return [paidTermId, userInfo]; } }; // TODO show warning that you don't have enough balance to buy a membership return (
{!profile && paidTerms && (

Membership costs {formatBalance(paidTerms.fee)} tokens.

{'By clicking the "Register" button you agree to our '} Terms of Service and Privacy Policy.

)}
); }; const EditForm = withFormik({ // Transform outer props into form values mapPropsToValues: props => { const { profile: p } = props; return { handle: p ? p.handle.toString() : '', avatar: p ? p.avatar_uri.toString() : '', about: p ? p.about.toString() : '' }; }, validationSchema: buildSchema, handleSubmit: values => { // do submitting things } })(InnerForm); type WithMyProfileProps = { memberId?: MemberId; memberProfile?: Option; // TODO refactor to Option paidTermsId: PaidTermId; paidTerms?: Option; minHandleLength?: BN; maxHandleLength?: BN; maxAvatarUriLength?: BN; maxAboutTextLength?: BN; }; function WithMyProfileInner (p: WithMyProfileProps) { const triedToFindProfile = !p.memberId || p.memberProfile; if ( triedToFindProfile && p.paidTerms && p.minHandleLength && p.maxHandleLength && p.maxAvatarUriLength && p.maxAboutTextLength ) { const profile = p.memberProfile ? p.memberProfile.unwrapOr(undefined) : undefined; if (!profile && p.paidTerms.isNone) { console.error('Could not find active paid membership terms'); } return ( ); } else return Loading...; } const WithMyProfile = withCalls( queryMembershipToProp('minHandleLength'), queryMembershipToProp('maxHandleLength'), queryMembershipToProp('maxAvatarUriLength'), queryMembershipToProp('maxAboutTextLength'), queryMembershipToProp('memberProfile', 'memberId'), queryMembershipToProp('paidMembershipTermsById', { paramName: 'paidTermsId', propName: 'paidTerms' }) )(WithMyProfileInner); type WithMyMemberIdProps = MyAccountProps & { memberIdsByRootAccountId?: Vec; memberIdsByControllerAccountId?: Vec; paidTermsIds?: Vec; }; function WithMyMemberIdInner (p: WithMyMemberIdProps) { if (p.allAccounts && !Object.keys(p.allAccounts).length) { return ( Please create a key to get started.
Create key
); } if (p.memberIdsByRootAccountId && p.memberIdsByControllerAccountId && p.paidTermsIds) { if (p.paidTermsIds.length) { // let member_ids = p.memberIdsByRootAccountId.slice(); // u8a.subarray is not a function!! p.memberIdsByRootAccountId.concat(p.memberIdsByControllerAccountId); const memberId = p.memberIdsByRootAccountId.length ? p.memberIdsByRootAccountId[0] : undefined; return ; } else { console.error('Active paid membership terms is empty'); } } return Loading...; } const WithMyMemberId = withMyAccount( withCalls( queryMembershipToProp('memberIdsByRootAccountId', 'myAddress'), queryMembershipToProp('memberIdsByControllerAccountId', 'myAddress'), queryMembershipToProp('activePaidMembershipTerms', { propName: 'paidTermsIds' }) )(WithMyMemberIdInner) ); export default WithMyMemberId;