Prechádzať zdrojové kódy

hide unknown interrupted videos and followed channels (#690)

* hide unknown interrupted videos and followed channels

* remove default cache policy

* Apply suggestions from code review

Co-authored-by: Bartosz Dryl <drylbartosz@gmail.com>

Co-authored-by: Bartosz Dryl <drylbartosz@gmail.com>
Klaudiusz Dembler 3 rokov pred
rodič
commit
136d85fdd8

+ 7 - 1
src/components/ChannelLink/ChannelLink.tsx

@@ -15,6 +15,7 @@ type ChannelLinkProps = {
   overrideChannel?: BasicChannelFieldsFragment
   avatarSize?: AvatarSize
   className?: string
+  onNotFound?: () => void
 }
 
 const ChannelLink: React.FC<ChannelLinkProps> = ({
@@ -24,9 +25,14 @@ const ChannelLink: React.FC<ChannelLinkProps> = ({
   noLink,
   overrideChannel,
   avatarSize = 'default',
+  onNotFound,
   className,
 }) => {
-  const { channel } = useBasicChannel(id || '', { fetchPolicy: 'cache-first', skip: !id })
+  const { channel } = useBasicChannel(id || '', {
+    skip: !id,
+    onCompleted: (data) => !data && onNotFound?.(),
+    onError: (error) => console.error('Failed to fetch channel', error),
+  })
   const { getAssetUrl } = useAsset()
 
   const displayedChannel = overrideChannel || channel

+ 6 - 0
src/components/InterruptedVideosGallery.tsx

@@ -24,6 +24,11 @@ const InterruptedVideosGallery: React.FC<RouteComponentProps> = () => {
     updateWatchedVideos('REMOVED', id)
   }
 
+  const onVideoNotFound = (id: string) => {
+    console.warn(`Interrupted video not found, removing id: ${id}`)
+    updateWatchedVideos('REMOVED', id)
+  }
+
   return (
     <VideoGallery
       title="Continue watching"
@@ -31,6 +36,7 @@ const InterruptedVideosGallery: React.FC<RouteComponentProps> = () => {
       loading={false}
       removeButton
       onRemoveButtonClick={onRemoveButtonClick}
+      onVideoNotFound={onVideoNotFound}
     />
   )
 }

+ 8 - 2
src/components/Sidenav/ViewerSidenav/FollowedChannels.tsx

@@ -20,9 +20,15 @@ type FollowedChannelsProps = {
   followedChannels: FollowedChannel[]
   expanded: boolean
   onClick: () => void
+  onChannelNotFound?: (id: string) => void
 }
 
-const FollowedChannels: React.FC<FollowedChannelsProps> = ({ followedChannels, expanded, onClick }) => {
+const FollowedChannels: React.FC<FollowedChannelsProps> = ({
+  followedChannels,
+  expanded,
+  onClick,
+  onChannelNotFound,
+}) => {
   const [isShowingMore, setIsShowingMore] = useState(false)
 
   const numberOfChannels = followedChannels.length
@@ -41,7 +47,7 @@ const FollowedChannels: React.FC<FollowedChannelsProps> = ({ followedChannels, e
           <ChannelsList>
             {channels.map(({ id }) => (
               <ChannelsItem key={id} onClick={onClick}>
-                <StyledChannelLink id={id} />
+                <StyledChannelLink id={id} onNotFound={() => onChannelNotFound?.(id)} />
               </ChannelsItem>
             ))}
           </ChannelsList>

+ 12 - 1
src/components/Sidenav/ViewerSidenav/ViewerSidenav.tsx

@@ -28,15 +28,26 @@ export const ViewerSidenav: React.FC = () => {
   const [expanded, setExpanded] = useState(false)
   const {
     state: { followedChannels },
+    updateChannelFollowing,
   } = usePersonalData()
 
+  const handleChannelNotFound = (id: string) => {
+    console.warn(`Followed channel not found, removing id: ${id}`)
+    updateChannelFollowing(id, false)
+  }
+
   return (
     <SidenavBase
       expanded={expanded}
       toggleSideNav={setExpanded}
       items={viewerSidenavItems}
       additionalContent={
-        <FollowedChannels onClick={() => setExpanded(false)} followedChannels={followedChannels} expanded={expanded} />
+        <FollowedChannels
+          onClick={() => setExpanded(false)}
+          onChannelNotFound={handleChannelNotFound}
+          followedChannels={followedChannels}
+          expanded={expanded}
+        />
       }
       buttonsContent={
         <>

+ 4 - 0
src/components/VideoGallery.tsx

@@ -25,6 +25,7 @@ type VideoGalleryProps = {
   loading?: boolean
   removeButton?: boolean
   onRemoveButtonClick?: (id: string) => void
+  onVideoNotFound?: (id: string) => void
   onVideoClick?: (id: string) => void
 }
 
@@ -51,6 +52,7 @@ const VideoGallery: React.FC<VideoGalleryProps> = ({
   onVideoClick,
   removeButton,
   onRemoveButtonClick,
+  onVideoNotFound,
 }) => {
   const [coverHeight, setCoverHeight] = useState<number>()
   const onCoverResize = useCallback((_, imgHeight) => {
@@ -75,6 +77,7 @@ const VideoGallery: React.FC<VideoGalleryProps> = ({
   }))
   const createClickHandler = (id?: string) => () => id && onVideoClick && onVideoClick(id)
   const createRemoveButtonClickHandler = (id?: string) => () => id && onRemoveButtonClick && onRemoveButtonClick(id)
+  const createNotFoundHandler = (id?: string) => () => id && onVideoNotFound && onVideoNotFound(id)
   return (
     <Gallery
       title={title}
@@ -92,6 +95,7 @@ const VideoGallery: React.FC<VideoGalleryProps> = ({
           removeButton={video ? removeButton : false}
           onCoverResize={onCoverResize}
           onClick={createClickHandler(video.id)}
+          onNotFound={createNotFoundHandler(video.id)}
           onRemoveButtonClick={createRemoveButtonClickHandler(video.id)}
         />
       ))}

+ 21 - 6
src/components/VideoPreview.tsx

@@ -12,11 +12,12 @@ import { AssetAvailability } from '@/api/queries'
 
 export type VideoPreviewProps = {
   id?: string
+  onNotFound?: () => void
 } & VideoPreviewBaseMetaProps &
   Pick<VideoPreviewBaseProps, 'progress' | 'className'>
 
-const VideoPreview: React.FC<VideoPreviewProps> = ({ id, ...metaProps }) => {
-  const { video, loading, videoHref } = useVideoSharedLogic(id, false)
+const VideoPreview: React.FC<VideoPreviewProps> = ({ id, onNotFound, ...metaProps }) => {
+  const { video, loading, videoHref } = useVideoSharedLogic({ id, isDraft: false, onNotFound })
   const { getAssetUrl } = useAsset()
 
   const thumbnailPhotoUrl = getAssetUrl(
@@ -52,8 +53,13 @@ export default VideoPreview
 
 export type VideoPreviewWPublisherProps = VideoPreviewProps &
   Omit<VideoPreviewPublisherProps, 'publisherMode' | 'videoPublishState'>
-export const VideoPreviewPublisher: React.FC<VideoPreviewWPublisherProps> = ({ id, isDraft, ...metaProps }) => {
-  const { video, loading, videoHref } = useVideoSharedLogic(id, isDraft)
+export const VideoPreviewPublisher: React.FC<VideoPreviewWPublisherProps> = ({
+  id,
+  isDraft,
+  onNotFound,
+  ...metaProps
+}) => {
+  const { video, loading, videoHref } = useVideoSharedLogic({ id, isDraft, onNotFound })
   const { activeChannelId } = useAuthorizedUser()
   const { drafts } = useDrafts('video', activeChannelId)
   const draft = id ? drafts.find((draft) => draft.id === id) : undefined
@@ -94,8 +100,17 @@ export const VideoPreviewPublisher: React.FC<VideoPreviewWPublisherProps> = ({ i
   )
 }
 
-const useVideoSharedLogic = (id?: string, isDraft?: boolean) => {
-  const { video, loading } = useVideo(id ?? '', { skip: !id || isDraft })
+type UseVideoSharedLogicOpts = {
+  id?: string
+  isDraft?: boolean
+  onNotFound?: () => void
+}
+const useVideoSharedLogic = ({ id, isDraft, onNotFound }: UseVideoSharedLogicOpts) => {
+  const { video, loading } = useVideo(id ?? '', {
+    skip: !id || isDraft,
+    onCompleted: (data) => !data && onNotFound?.(),
+    onError: (error) => console.error('Failed to fetch video', error),
+  })
   const internalIsLoadingState = loading || !id
   const videoHref = id ? absoluteRoutes.viewer.video(id) : undefined
   return { video, loading: internalIsLoadingState, videoHref }