123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- import React from 'react';
- import { Button, Message } from 'semantic-ui-react';
- import styled from 'styled-components';
- import { Form, Field, withFormik, FormikProps } from 'formik';
- import * as Yup from 'yup';
- import { TxButton, Section } from '@polkadot/joy-utils/react/components';
- import { SubmittableResult } from '@polkadot/api';
- import { withMulti } from '@polkadot/react-api/hoc';
- import * as JoyForms from '@polkadot/joy-utils/react/components/forms';
- import { PostId, ThreadId } from '@joystream/types/common';
- import { Post } from '@joystream/types/forum';
- import { withOnlyMembers } from '@polkadot/joy-utils/react/hocs/guards';
- import { useMyAccount } from '@polkadot/joy-utils/react/hooks';
- import { withForumCalls } from './calls';
- import { ValidationProps, withReplyValidation } from './validation';
- import { TxFailedCallback, TxCallback } from '@polkadot/react-components/Status/types';
- const buildSchema = (props: ValidationProps) => {
- const {
- postTextConstraint
- } = props;
- if (!postTextConstraint) {
- throw new Error('Missing some validation constraints');
- }
- const minText = postTextConstraint.min.toNumber();
- const maxText = postTextConstraint.max.toNumber();
- return Yup.object().shape({
- text: Yup.string()
- .min(minText, `Reply text is too short. Minimum length is ${minText} chars.`)
- .max(maxText, `Reply text is too long. Maximum length is ${maxText} chars.`)
- .required('Text is required')
- });
- };
- const FormActionsContainer = styled.div`
- display: flex;
- justify-content: space-between;
- `;
- type OuterProps = ValidationProps & {
- id?: PostId;
- struct?: Post;
- threadId: ThreadId;
- quotedPost?: Post | null;
- onEditSuccess?: () => void;
- onEditCancel?: () => void;
- };
- type FormValues = {
- text: string;
- };
- type FormProps = OuterProps & FormikProps<FormValues>;
- const LabelledField = JoyForms.LabelledField<FormValues>();
- const InnerForm = (props: FormProps) => {
- const {
- id,
- threadId,
- struct,
- values,
- dirty,
- isValid,
- isSubmitting,
- setSubmitting,
- resetForm,
- onEditSuccess,
- onEditCancel
- } = props;
- const {
- text
- } = values;
- const isNew = struct === undefined;
- const onSubmit = (sendTx: () => void) => {
- if (isValid) sendTx();
- };
- const onTxFailed: TxFailedCallback = (txResult: SubmittableResult | Error | null) => {
- setSubmitting(false);
- if (txResult == null) {
- // Tx cancelled.
- }
- };
- const onTxSuccess: TxCallback = (_txResult: SubmittableResult) => {
- setSubmitting(false);
- resetForm();
- if (!isNew && onEditSuccess) {
- onEditSuccess();
- }
- };
- const buildTxParams = () => {
- if (!isValid) return [];
- if (!id) {
- return [threadId, text];
- } else {
- return [id, text];
- }
- };
- const form =
- <Form className='ui form JoyForm EditEntityForm'>
- <LabelledField name='text' {...props}>
- <Field component='textarea' id='text' name='text' disabled={isSubmitting} rows={5} placeholder='Type here. You can use Markdown.' />
- </LabelledField>
- <LabelledField {...props}>
- <FormActionsContainer>
- <div style={{ display: 'flex' }}>
- <TxButton
- type='submit'
- label={isNew
- ? 'Post a reply'
- : 'Update a reply'
- }
- isDisabled={!dirty || isSubmitting}
- params={buildTxParams()}
- tx={isNew
- ? 'forum.addPost'
- : 'forum.editPostText'
- }
- onClick={onSubmit}
- txFailedCb={onTxFailed}
- txSuccessCb={onTxSuccess}
- />
- <Button
- type='button'
- size='large'
- disabled={!dirty || isSubmitting}
- onClick={() => resetForm()}
- content='Reset form'
- />
- </div>
- {
- !isNew && (
- <Button
- type='button'
- size='large'
- disabled={isSubmitting}
- content='Cancel edit'
- onClick={() => onEditCancel && onEditCancel()}
- />
- )
- }
- </FormActionsContainer>
- </LabelledField>
- </Form>;
- const sectionTitle = isNew
- ? 'New reply'
- : `Edit my reply #${struct?.nr_in_thread.toString() || ''}`;
- return (
- <Section className='EditEntityBox' title={sectionTitle}>
- {form}
- </Section>
- );
- };
- const getQuotedPostString = (post: Post) => {
- const lines = post.current_text.split('\n');
- return lines.reduce((acc, line) => {
- return `${acc}> ${line}\n`;
- }, '');
- };
- const EditForm = withFormik<OuterProps, FormValues>({
- // Transform outer props into form values
- mapPropsToValues: (props) => {
- const { struct, quotedPost } = props;
- return {
- text: struct
- ? struct.current_text
- : quotedPost
- ? getQuotedPostString(quotedPost)
- : ''
- };
- },
- validationSchema: buildSchema,
- handleSubmit: (values) => {
- // do submitting things
- }
- })(InnerForm);
- function FormOrLoading (props: OuterProps) {
- const { state: { address } } = useMyAccount();
- const { struct } = props;
- if (!address || !struct) {
- return <em>Loading reply...</em>;
- }
- if (struct.isEmpty) {
- return <em>Reply not found</em>;
- }
- const isMyStruct = address === struct.author_id.toString();
- if (isMyStruct) {
- return <EditForm {...props} threadId={struct.thread_id} />;
- }
- return <Message error className='JoyMainStatus' header='You are not allowed edit this reply.' />;
- }
- export const NewReply = withMulti(
- EditForm,
- withOnlyMembers,
- withReplyValidation
- );
- export const EditReply = withMulti(
- FormOrLoading,
- withOnlyMembers,
- withReplyValidation,
- withForumCalls<OuterProps>(
- ['postById', { paramName: 'id', propName: 'struct' }]
- )
- );
|