MediaView.tsx 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. import React, { useState, useEffect } from 'react';
  2. import { MediaTransport } from './transport';
  3. import { MemberId } from '@joystream/types/lib/members';
  4. import { useMyMembership } from '@polkadot/joy-utils/MyMembershipContext';
  5. import { useTransportContext } from './TransportContext';
  6. import { withMembershipRequired } from '@polkadot/joy-utils/MyAccount';
  7. type InitialPropsWithMembership<A> = A & {
  8. myAddress?: string
  9. myMemberId?: MemberId
  10. }
  11. type ResolverProps<A> = InitialPropsWithMembership<A> & {
  12. transport: MediaTransport
  13. }
  14. type BaseProps<A, B> = {
  15. component: React.ComponentType<A & B>
  16. unresolvedView?: React.ReactElement
  17. resolveProps?: (props: ResolverProps<A>) => Promise<B>
  18. /**
  19. * Array of property names that can trigger re-render of the view,
  20. * if values of such properties changed.
  21. */
  22. triggers?: (keyof A)[]
  23. /** Set `true` if only members should have access to this component. `false` by default. */
  24. membersOnly?: boolean
  25. }
  26. function serializeTrigger(val: any): any {
  27. if (['number', 'boolean', 'string'].indexOf(typeof val) >= 0) {
  28. return val
  29. } else if (typeof val === 'object' && typeof val.toString === 'function') {
  30. return val.toString()
  31. } else {
  32. return undefined
  33. }
  34. }
  35. export function MediaView<A = {}, B = {}> (baseProps: BaseProps<A, B>) {
  36. function InnerView (initialProps: A & B) {
  37. const { component: Component, resolveProps, triggers = [], unresolvedView = null } = baseProps;
  38. const transport = useTransportContext();
  39. const { myAddress, myMemberId } = useMyMembership();
  40. const resolverProps = {...initialProps, transport, myAddress, myMemberId }
  41. const [ resolvedProps, setResolvedProps ] = useState({} as B);
  42. const [ propsResolved, setPropsResolved ] = useState(false);
  43. const initialDeps = triggers.map(propName => serializeTrigger(initialProps[propName]))
  44. const rerenderDeps = [ ...initialDeps, myAddress ]
  45. useEffect(() => {
  46. async function doResolveProps () {
  47. if (typeof resolveProps !== 'function') return;
  48. console.log('Resolving props of media view');
  49. // Transport session allows us to cache loaded channels, entites and classes
  50. // during the render of this view:
  51. transport.openSession()
  52. setResolvedProps(await resolveProps(resolverProps));
  53. transport.closeSession()
  54. setPropsResolved(true);
  55. }
  56. if (!transport) {
  57. console.error('Transport is not defined');
  58. } else {
  59. doResolveProps();
  60. }
  61. }, rerenderDeps);
  62. console.log('Rerender deps of Media View:', rerenderDeps);
  63. const renderResolving = () => {
  64. return unresolvedView
  65. ? unresolvedView
  66. : <div className='ui active centered inline loader' />
  67. }
  68. return propsResolved
  69. ? <Component {...initialProps} {...resolvedProps} />
  70. : renderResolving()
  71. }
  72. const { membersOnly = false } = baseProps
  73. return membersOnly
  74. ? withMembershipRequired(InnerView)
  75. : InnerView
  76. }