EditChannel.tsx 6.0 KB

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