VideoGallery.tsx 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. import React, { useCallback, useMemo, useState } from 'react'
  2. import { css, SerializedStyles } from '@emotion/core'
  3. import styled from '@emotion/styled'
  4. import { navigate } from '@reach/router'
  5. import { Gallery, MAX_VIDEO_PREVIEW_WIDTH, VideoPreview, VideoPreviewBase } from '@/shared/components'
  6. import { VideoFields } from '@/api/queries/__generated__/VideoFields'
  7. import { CAROUSEL_CONTROL_SIZE } from '@/shared/components/Carousel'
  8. import routes from '@/config/routes'
  9. type VideoGalleryProps = {
  10. title: string
  11. action?: string
  12. videos?: VideoFields[]
  13. loading?: boolean
  14. }
  15. const PLACEHOLDERS_COUNT = 12
  16. const VideoGallery: React.FC<VideoGalleryProps> = ({ title, action, videos, loading }) => {
  17. const [posterSize, setPosterSize] = useState(0)
  18. const [galleryControlCss, setGalleryControlCss] = useState<SerializedStyles>(css``)
  19. useMemo(() => {
  20. if (!posterSize) {
  21. return
  22. }
  23. const topPx = posterSize / 2 - CAROUSEL_CONTROL_SIZE / 2
  24. setGalleryControlCss(css`
  25. top: ${topPx}px;
  26. `)
  27. }, [posterSize])
  28. const displayPlaceholders = loading || !videos
  29. const imgRef = useCallback((node: HTMLImageElement) => {
  30. if (node != null) {
  31. setPosterSize(node.clientHeight)
  32. }
  33. }, [])
  34. const handleVideoClick = (id: string) => {
  35. navigate(routes.video(id))
  36. }
  37. const handleChannelClick = (id: string) => {
  38. navigate(routes.channel(id))
  39. }
  40. return (
  41. <Gallery
  42. title={title}
  43. action={action}
  44. leftControlCss={galleryControlCss}
  45. rightControlCss={galleryControlCss}
  46. disableControls={displayPlaceholders}
  47. >
  48. {displayPlaceholders
  49. ? Array.from({ length: PLACEHOLDERS_COUNT }).map((_, idx) => (
  50. <StyledVideoPreviewBase key={`video-placeholder-${idx}`} />
  51. ))
  52. : videos!.map((video, idx) => (
  53. <StyledVideoPreview
  54. title={video.title}
  55. channelName={video.channel.handle}
  56. channelAvatarURL={video.channel.avatarPhotoURL}
  57. views={video.views}
  58. createdAt={video.publishedOnJoystreamAt}
  59. duration={video.duration}
  60. posterURL={video.thumbnailURL}
  61. onClick={() => handleVideoClick(video.id)}
  62. onChannelClick={() => handleChannelClick(video.channel.id)}
  63. imgRef={idx === 0 ? imgRef : null}
  64. key={video.id}
  65. />
  66. ))}
  67. </Gallery>
  68. )
  69. }
  70. const StyledVideoPreviewBase = styled(VideoPreviewBase)`
  71. & + & {
  72. margin-left: 1.25rem;
  73. }
  74. width: ${MAX_VIDEO_PREVIEW_WIDTH};
  75. `
  76. const StyledVideoPreview = styled(VideoPreview)`
  77. & + & {
  78. margin-left: 1.25rem;
  79. }
  80. width: ${MAX_VIDEO_PREVIEW_WIDTH};
  81. `
  82. export default VideoGallery