MediaView.tsx 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. import React, { useState, useEffect } from 'react';
  2. import { MediaTransport } from './transport';
  3. import { MemberId } from '@joystream/types/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'].includes(typeof val)) {
  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 || <div className='ui active centered inline loader' />;
  65. };
  66. return propsResolved
  67. ? <Component {...initialProps} {...resolvedProps} />
  68. : renderResolving();
  69. }
  70. const { membersOnly = false } = baseProps;
  71. return membersOnly
  72. ? withMembershipRequired(InnerView)
  73. : InnerView;
  74. }