EditChannel.tsx 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. import React from 'react';
  2. import { Button } from 'semantic-ui-react';
  3. import { Form, withFormik } from 'formik';
  4. import { History } from 'history';
  5. import { Text, Option } from '@polkadot/types';
  6. import TxButton from '@polkadot/joy-utils/TxButton';
  7. import { onImageError } from '@polkadot/joy-utils/images';
  8. import { withMediaForm, MediaFormProps } from '../common/MediaForms';
  9. import { ChannelType, ChannelClass as Fields, buildChannelValidationSchema, ChannelFormValues, ChannelToFormValues, ChannelGenericProp } from '../schemas/channel/Channel';
  10. import { MediaDropdownOptions } from '../common/MediaDropdownOptions';
  11. import { ChannelId, ChannelContentType, ChannelPublicationStatus, OptionalText } from '@joystream/types/content-working-group';
  12. import { newOptionalText, findFirstParamOfSubstrateEvent } from '@polkadot/joy-utils/index';
  13. import { useMyMembership } from '@polkadot/joy-utils/MyMembershipContext';
  14. import { ChannelPublicationStatusDropdownOptions, isAccountAChannelOwner } from './ChannelHelpers';
  15. import { TxCallback } from '@polkadot/react-components/Status/types';
  16. import { SubmittableResult } from '@polkadot/api';
  17. import { ChannelValidationConstraints } from '../transport';
  18. import { JoyError } from '@polkadot/joy-utils/JoyStatus';
  19. import Section from '@polkadot/joy-utils/Section';
  20. export type OuterProps = {
  21. history?: History;
  22. id?: ChannelId;
  23. entity?: ChannelType;
  24. constraints?: ChannelValidationConstraints;
  25. opts?: MediaDropdownOptions;
  26. };
  27. type FormValues = ChannelFormValues;
  28. const InnerForm = (props: MediaFormProps<OuterProps, FormValues>) => {
  29. const {
  30. // React components for form fields:
  31. MediaText,
  32. MediaDropdown,
  33. LabelledField,
  34. // Callbacks:
  35. onSubmit,
  36. // onTxSuccess,
  37. onTxFailed,
  38. history,
  39. id: existingId,
  40. entity,
  41. isFieldChanged,
  42. // Formik stuff:
  43. values,
  44. dirty,
  45. isValid,
  46. isSubmitting,
  47. setSubmitting,
  48. resetForm
  49. } = props;
  50. const { myAccountId, myMemberId } = useMyMembership();
  51. if (entity && !isAccountAChannelOwner(entity, myAccountId)) {
  52. return <JoyError title={'Only owner can edit channel'} />;
  53. }
  54. const { avatar } = values;
  55. const isNew = !entity;
  56. // if user is not the channel owner don't render the edit form
  57. // return null
  58. const onTxSuccess: TxCallback = (txResult: SubmittableResult) => {
  59. setSubmitting(false);
  60. if (!history) return;
  61. const id = existingId || findFirstParamOfSubstrateEvent<ChannelId>(txResult, 'ChannelCreated');
  62. console.log('Channel id:', id?.toString());
  63. if (id) {
  64. history.push('/media/channels/' + id.toString());
  65. }
  66. };
  67. const buildTxParams = () => {
  68. if (!isValid) return [];
  69. // TODO get value from the form:
  70. const publicationStatus = new ChannelPublicationStatus('Public');
  71. if (!entity) {
  72. // Create a new channel
  73. const channelOwner = myMemberId;
  74. const roleAccount = myAccountId;
  75. const contentType = new ChannelContentType(values.content);
  76. return [
  77. channelOwner,
  78. roleAccount,
  79. contentType,
  80. new Text(values.handle),
  81. newOptionalText(values.title),
  82. newOptionalText(values.description),
  83. newOptionalText(values.avatar),
  84. newOptionalText(values.banner),
  85. publicationStatus
  86. ];
  87. } else {
  88. // Update an existing channel
  89. const updOptText = (field: ChannelGenericProp): Option<OptionalText> => {
  90. return new Option(OptionalText,
  91. isFieldChanged(field)
  92. ? newOptionalText(values[field.id])
  93. : null
  94. );
  95. };
  96. const updHandle = new Option(Text,
  97. isFieldChanged(Fields.handle)
  98. ? values[Fields.handle.id]
  99. : null
  100. );
  101. const updPublicationStatus = new Option(ChannelPublicationStatus,
  102. isFieldChanged(Fields.publicationStatus)
  103. ? new ChannelPublicationStatus(values[Fields.publicationStatus.id] as any)
  104. : null
  105. );
  106. return [
  107. new ChannelId(entity.id),
  108. updHandle,
  109. updOptText(Fields.title),
  110. updOptText(Fields.description),
  111. updOptText(Fields.avatar),
  112. updOptText(Fields.banner),
  113. updPublicationStatus
  114. ];
  115. }
  116. };
  117. const formFields = () => <>
  118. <MediaText field={Fields.handle} {...props} />
  119. <MediaText field={Fields.title} {...props} />
  120. <MediaText field={Fields.avatar} {...props} />
  121. <MediaText field={Fields.banner} {...props} />
  122. <MediaText field={Fields.description} textarea {...props} />
  123. <MediaDropdown
  124. {...props}
  125. field={Fields.publicationStatus}
  126. options={ChannelPublicationStatusDropdownOptions}
  127. />
  128. </>;
  129. const renderMainButton = () =>
  130. <TxButton
  131. type='submit'
  132. size='large'
  133. isDisabled={!dirty || isSubmitting}
  134. label={isNew
  135. ? 'Create channel'
  136. : 'Update channel'
  137. }
  138. params={buildTxParams()}
  139. tx={isNew
  140. ? 'contentWorkingGroup.createChannel'
  141. : 'contentWorkingGroup.updateChannelAsOwner'
  142. }
  143. onClick={onSubmit}
  144. txFailedCb={onTxFailed}
  145. txSuccessCb={onTxSuccess}
  146. />;
  147. return <div className='EditMetaBox'>
  148. <div className='EditMetaThumb'>
  149. {avatar && <img src={avatar} onError={onImageError} />}
  150. </div>
  151. <Section title={isNew ? 'Create a channel' : 'Edit a channel'}>
  152. <Form className='ui form JoyForm EditMetaForm'>
  153. {formFields()}
  154. <LabelledField style={{ marginTop: '1rem' }} {...props}>
  155. {renderMainButton()}
  156. <Button
  157. type='button'
  158. size='large'
  159. disabled={!dirty || isSubmitting}
  160. onClick={() => resetForm()}
  161. content='Reset form'
  162. />
  163. </LabelledField>
  164. </Form>
  165. </Section>
  166. </div>;
  167. };
  168. export const EditForm = withFormik<OuterProps, FormValues>({
  169. // Transform outer props into form values
  170. mapPropsToValues: (props): FormValues => {
  171. const { entity } = props;
  172. return ChannelToFormValues(entity);
  173. },
  174. validationSchema: (props: OuterProps): any => {
  175. const { constraints } = props;
  176. if (!constraints) return null;
  177. return buildChannelValidationSchema(constraints);
  178. },
  179. handleSubmit: () => {
  180. // do submitting things
  181. }
  182. })(withMediaForm(InnerForm) as any);
  183. export default EditForm;