Browse Source

Merge pull request #1482 from Gamaranto/make-elements-clickable

Make All Elements Clickable
Bedeho Mender 4 năm trước cách đây
mục cha
commit
b93feaf7f9

+ 3 - 4
src/components/ChannelGallery.tsx

@@ -1,10 +1,9 @@
 import React from 'react'
 import styled from '@emotion/styled'
-import { navigate } from '@reach/router'
 
-import { ChannelPreview, ChannelPreviewBase, Gallery } from '@/shared/components'
+import { ChannelPreviewBase, Gallery } from '@/shared/components'
+import ChannelPreview from './ChannelPreviewWithNavigation'
 import { ChannelFields } from '@/api/queries/__generated__/ChannelFields'
-import routes from '@/config/routes'
 
 type ChannelGalleryProps = {
   title: string
@@ -26,11 +25,11 @@ const ChannelGallery: React.FC<ChannelGalleryProps> = ({ title, action, channels
           ))
         : channels!.map((channel) => (
             <StyledChannelPreview
+              id={channel.id}
               name={channel.handle}
               avatarURL={channel.avatarPhotoURL}
               views={channel.totalViews}
               key={channel.id}
-              onClick={() => navigate(routes.channel(channel.id))}
               animated
             />
           ))}

+ 24 - 0
src/components/ChannelPreviewWithNavigation.tsx

@@ -0,0 +1,24 @@
+import React from 'react'
+import { navigate } from '@reach/router'
+
+import { ChannelPreview } from '@/shared/components'
+import routes from '@/config/routes'
+
+type ChannelPreviewWithNavigationProps = {
+  id?: string
+} & React.ComponentProps<typeof ChannelPreview>
+
+const ChannelPreviewWithNavigation: React.FC<ChannelPreviewWithNavigationProps> = ({
+  id,
+  onClick,
+  ...channelPreviewProps
+}) => {
+  const handleClick = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
+    if (onClick) {
+      onClick(e)
+    }
+    navigate(routes.channel(id))
+  }
+  return <ChannelPreview {...channelPreviewProps} onClick={handleClick} />
+}
+export default ChannelPreviewWithNavigation

+ 4 - 12
src/components/VideoGallery.tsx

@@ -1,12 +1,11 @@
 import React, { useCallback, useMemo, useState } from 'react'
 import { css, SerializedStyles } from '@emotion/core'
 import styled from '@emotion/styled'
-import { navigate } from '@reach/router'
 
-import { Gallery, MAX_VIDEO_PREVIEW_WIDTH, VideoPreview, VideoPreviewBase } from '@/shared/components'
+import { Gallery, MAX_VIDEO_PREVIEW_WIDTH, VideoPreviewBase } from '@/shared/components'
+import VideoPreview from './VideoPreviewWithNavigation'
 import { VideoFields } from '@/api/queries/__generated__/VideoFields'
 import { CAROUSEL_CONTROL_SIZE } from '@/shared/components/Carousel'
-import routes from '@/config/routes'
 
 type VideoGalleryProps = {
   title: string
@@ -40,13 +39,6 @@ const VideoGallery: React.FC<VideoGalleryProps> = ({ title, action, videos, load
     }
   }, [])
 
-  const handleVideoClick = (id: string) => {
-    navigate(routes.video(id))
-  }
-  const handleChannelClick = (id: string) => {
-    navigate(routes.channel(id))
-  }
-
   return (
     <Gallery
       title={title}
@@ -61,6 +53,8 @@ const VideoGallery: React.FC<VideoGalleryProps> = ({ title, action, videos, load
           ))
         : videos!.map((video, idx) => (
             <StyledVideoPreview
+              id={video.id}
+              channelId={video.channel.id}
               title={video.title}
               channelName={video.channel.handle}
               channelAvatarURL={video.channel.avatarPhotoURL}
@@ -68,8 +62,6 @@ const VideoGallery: React.FC<VideoGalleryProps> = ({ title, action, videos, load
               createdAt={video.publishedOnJoystreamAt}
               duration={video.duration}
               posterURL={video.thumbnailURL}
-              onClick={() => handleVideoClick(video.id)}
-              onChannelClick={() => handleChannelClick(video.channel.id)}
               imgRef={idx === 0 ? imgRef : null}
               key={video.id}
             />

+ 4 - 4
src/components/VideoGrid.tsx

@@ -1,10 +1,9 @@
 import React from 'react'
 import styled from '@emotion/styled'
-import { navigate } from '@reach/router'
 
-import routes from '@/config/routes'
 import { VideoFields } from '@/api/queries/__generated__/VideoFields'
-import { VideoPreview, Grid } from '@/shared/components'
+import { Grid } from '@/shared/components'
+import VideoPreview from './VideoPreviewWithNavigation'
 
 const StyledVideoPreview = styled(VideoPreview)`
   margin: 0 auto;
@@ -18,6 +17,8 @@ const VideoGrid: React.FC<VideoGridProps> = ({ videos }) => {
     <Grid>
       {videos.map((v, idx) => (
         <StyledVideoPreview
+          id={v.id}
+          channelId={v.channel.id}
           key={idx}
           title={v.title}
           channelName={v.channel.handle}
@@ -26,7 +27,6 @@ const VideoGrid: React.FC<VideoGridProps> = ({ videos }) => {
           duration={v.duration}
           views={v.views}
           posterURL={v.thumbnailURL}
-          onClick={() => navigate(routes.video(v.id))}
         />
       ))}
     </Grid>

+ 35 - 0
src/components/VideoPreviewWithNavigation.tsx

@@ -0,0 +1,35 @@
+import React from 'react'
+
+import { navigate } from '@reach/router'
+import { VideoPreview } from '@/shared/components'
+
+import routes from '@/config/routes'
+
+type VideoPreviewWithNavigationProps = {
+  id: string
+  channelId: string
+} & React.ComponentProps<typeof VideoPreview>
+
+const VideoPreviewWithNavigation: React.FC<VideoPreviewWithNavigationProps> = ({
+  id,
+  channelId,
+  onClick,
+  onChannelClick,
+  ...videoPreviewProps
+}) => {
+  const handleClick = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
+    if (onClick) {
+      onClick(e)
+    }
+    navigate(routes.video(id))
+  }
+  const handleChannelClick = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
+    if (onChannelClick) {
+      onChannelClick(e)
+    }
+    navigate(routes.channel(channelId))
+  }
+  return <VideoPreview {...videoPreviewProps} onClick={handleClick} onChannelClick={handleChannelClick} />
+}
+
+export default VideoPreviewWithNavigation

+ 2 - 0
src/components/index.ts

@@ -6,3 +6,5 @@ export { default as ChannelGallery } from './ChannelGallery'
 export { default as Navbar } from './Navbar'
 export { default as VideoBestMatch } from './VideoBestMatch'
 export { default as VideoGrid } from './VideoGrid'
+export { default as VideoPreview } from './VideoPreviewWithNavigation'
+export { default as ChannelPreview } from './ChannelPreviewWithNavigation'

+ 3 - 2
src/shared/components/ChannelAvatar/ChannelAvatar.tsx

@@ -5,11 +5,12 @@ type ChannelAvatarProps = {
   name: string
   avatarUrl?: string | null
   className?: string
+  onClick?: (e: React.MouseEvent<HTMLDivElement>) => void
 }
 
-const ChannelAvatar: React.FC<ChannelAvatarProps> = ({ name, avatarUrl, className }) => {
+const ChannelAvatar: React.FC<ChannelAvatarProps> = ({ name, avatarUrl, className, onClick }) => {
   return (
-    <Container className={className}>
+    <Container className={className} onClick={onClick}>
       <StyledAvatar name={name} img={avatarUrl} />
       <Name>{name}</Name>
     </Container>

+ 7 - 3
src/shared/components/InfiniteVideoGrid/InfiniteVideoGrid.tsx

@@ -1,10 +1,12 @@
 import React, { useEffect, useState } from 'react'
 import styled from '@emotion/styled'
-import { spacing, typography } from '../../theme'
-import { VideoPreview, VideoPreviewBase, Grid } from '..'
-import sizes from '@/shared/theme/sizes'
 import { debounce } from 'lodash'
 import { useLazyQuery } from '@apollo/client'
+
+import { typography, sizes } from '../../theme'
+import { VideoPreviewBase } from '../VideoPreview'
+import Grid from '../Grid'
+import VideoPreview from '@/components/VideoPreviewWithNavigation'
 import { GET_NEWEST_VIDEOS } from '@/api/queries'
 import { GetNewestVideos, GetNewestVideosVariables } from '@/api/queries/__generated__/GetNewestVideos'
 
@@ -119,6 +121,8 @@ const InfiniteVideoGrid: React.FC<InfiniteVideoGridProps> = ({
     <>
       {displayedVideos.map((v) => (
         <StyledVideoPreview
+          id={v.id}
+          channelId={v.channel.id}
           title={v.title}
           channelName={v.channel.handle}
           channelAvatarURL={v.channel.avatarPhotoURL}

+ 6 - 4
src/views/BrowseView.tsx

@@ -28,7 +28,7 @@ const BrowseView: React.FC<RouteComponentProps> = () => {
         selectedCategoryId={selectedCategoryId}
         onChange={handleCategoryChange}
       />
-      <InfiniteVideoGrid categoryId={selectedCategoryId || undefined} ready={!!selectedCategoryId} />
+      <StyledInfiniteVideoGrid categoryId={selectedCategoryId || undefined} ready={!!selectedCategoryId} />
     </div>
   )
 }
@@ -41,9 +41,11 @@ const StyledCategoryPicker = styled(CategoryPicker)`
   z-index: 10;
   position: sticky;
   top: 0;
-  padding-top: ${sizes.b5}px;
-  padding-bottom: ${sizes.b2}px;
+  padding: ${sizes.b5}px ${sizes.b8}px ${sizes.b2}px;
+  margin: 0 -${sizes.b8}px;
   background-color: ${colors.black};
 `
-
+const StyledInfiniteVideoGrid = styled(InfiniteVideoGrid)`
+  padding-top: ${sizes.b2}px;
+`
 export default BrowseView

+ 3 - 0
src/views/VideoView/VideoView.style.tsx

@@ -34,6 +34,9 @@ export const Meta = styled.span`
 
 export const StyledChannelAvatar = styled(ChannelAvatar)`
   margin-top: ${theme.spacing.m};
+  :hover {
+    cursor: pointer;
+  }
 `
 
 export const DescriptionContainer = styled.div`

+ 7 - 2
src/views/VideoView/VideoView.tsx

@@ -1,5 +1,5 @@
 import React from 'react'
-import { RouteComponentProps, useParams } from '@reach/router'
+import { RouteComponentProps, useParams, navigate } from '@reach/router'
 import {
   Container,
   DescriptionContainer,
@@ -19,6 +19,7 @@ import { formatNumber } from '@/utils/number'
 import { useQuery } from '@apollo/client'
 import { GET_VIDEO } from '@/api/queries'
 import { GetVideo, GetVideoVariables } from '@/api/queries/__generated__/GetVideo'
+import routes from '@/config/routes'
 
 const VideoView: React.FC<RouteComponentProps> = () => {
   const { id } = useParams()
@@ -50,7 +51,11 @@ const VideoView: React.FC<RouteComponentProps> = () => {
         <Meta>
           {formatNumber(views)} views • {formatDateAgo(publishedOnJoystreamAt)}
         </Meta>
-        <StyledChannelAvatar name={channel.handle} avatarUrl={channel.avatarPhotoURL} />
+        <StyledChannelAvatar
+          name={channel.handle}
+          avatarUrl={channel.avatarPhotoURL}
+          onClick={() => navigate(routes.channel(channel.id))}
+        />
         <DescriptionContainer>
           {descriptionLines.map((line, idx) => (
             <p key={idx}>{line}</p>