Browse Source

Improve assetimage loading (#4377)

Bartosz Dryl 1 year ago
parent
commit
fd49d85576

+ 27 - 8
packages/atlas/src/components/AssetImage/AssetImage.tsx

@@ -1,17 +1,36 @@
-import { ImgHTMLAttributes } from 'react'
+import { FC, ImgHTMLAttributes, ReactNode } from 'react'
+import { CSSTransition, SwitchTransition } from 'react-transition-group'
 
 import { SkeletonLoader } from '@/components/_loaders/SkeletonLoader'
 import { useGetAssetUrl } from '@/hooks/useGetAssetUrl'
+import { cVar, transitions } from '@/styles'
 
-export type AssetImage = {
+export type AssetImageProps = {
+  isLoading?: boolean
   resolvedUrls: string[] | undefined | null
+  imagePlaceholder?: ReactNode
 } & Omit<ImgHTMLAttributes<HTMLImageElement>, 'src'>
 
-export const AssetImage = ({ resolvedUrls, ...imgProps }: AssetImage) => {
-  const { url, isLoading } = useGetAssetUrl(resolvedUrls, 'image')
-  if (isLoading) {
-    return <SkeletonLoader className={imgProps.className} />
-  }
+export const AssetImage: FC<AssetImageProps> = ({ resolvedUrls, isLoading, imagePlaceholder, ...imgProps }) => {
+  const { url, isLoading: isResolving } = useGetAssetUrl(resolvedUrls, 'image')
 
-  return <img {...imgProps} src={url} />
+  const loading = isLoading || isResolving
+
+  return (
+    <SwitchTransition>
+      <CSSTransition
+        key={String(loading)}
+        timeout={parseInt(cVar('animationTimingFast', true))}
+        classNames={transitions.names.fade}
+      >
+        {loading ? (
+          <SkeletonLoader className={imgProps.className} />
+        ) : imagePlaceholder && !url ? (
+          <>{imagePlaceholder}</>
+        ) : (
+          <img {...imgProps} src={url} />
+        )}
+      </CSSTransition>
+    </SwitchTransition>
+  )
 }

+ 0 - 21
packages/atlas/src/components/Avatar/Avatar.styles.ts

@@ -59,27 +59,6 @@ export const Overlay = styled.div<{ isEdit?: boolean }>`
     `};
 `
 
-// const coverAvatarCss = css`
-//   ${square('64px')};
-
-//   ${media.md} {
-//     ${square('88px')};
-//   }
-// `
-
-// const channelAvatarCss = css`
-//   ${square('88px')};
-//   ${media.md} {
-//     ${square('136px')};
-//   }
-// `
-// const channelCardAvatarCss = css`
-//   ${square('88px')};
-//   ${media.md} {
-//     ${square('104px')};
-//   }
-// `
-
 export const sharedAvatarHoverStyles = (props: { disableHoverDimm?: boolean }) => css`
   ::after {
     border: 1px solid ${cVar('colorBackgroundAlpha')};

+ 6 - 17
packages/atlas/src/components/Avatar/Avatar.tsx

@@ -1,8 +1,6 @@
 import { FC, MouseEvent, PropsWithChildren, useCallback } from 'react'
-import { CSSTransition, SwitchTransition } from 'react-transition-group'
 
 import { SvgActionNewChannel } from '@/assets/icons'
-import { cVar, transitions } from '@/styles'
 
 import {
   AvatarSize,
@@ -97,21 +95,12 @@ export const Avatar: FC<AvatarProps> = ({
             )}
           </NewChannelAvatar>
         ) : (
-          <SwitchTransition>
-            <CSSTransition
-              key={loading ? 'placeholder' : 'content'}
-              timeout={parseInt(cVar('animationTimingFast', true))}
-              classNames={transitions.names.fade}
-            >
-              {loading ? (
-                <StyledSkeletonLoader rounded />
-              ) : assetUrls?.length ? (
-                <StyledImage resolvedUrls={assetUrls} onError={onError} />
-              ) : (
-                <SilhouetteAvatar />
-              )}
-            </CSSTransition>
-          </SwitchTransition>
+          <StyledImage
+            resolvedUrls={assetUrls}
+            onError={onError}
+            isLoading={loading}
+            imagePlaceholder={<SilhouetteAvatar />}
+          />
         ))}
       {children && (loading ? <StyledSkeletonLoader rounded /> : <ChildrenWrapper>{children}</ChildrenWrapper>)}
     </Container>

+ 7 - 14
packages/atlas/src/components/_channel/ChannelCover/ChannelCover.tsx

@@ -1,9 +1,7 @@
 import { FC, MouseEvent } from 'react'
-import { CSSTransition, TransitionGroup } from 'react-transition-group'
 
 import { SvgActionImage, SvgActionImageFile } from '@/assets/icons'
 import { Text } from '@/components/Text'
-import { transitions } from '@/styles'
 
 import {
   CoverImage,
@@ -48,15 +46,10 @@ export const ChannelCover: FC<ChannelCoverProps> = ({
           </EditableControls>
         )}
         <Media>
-          <TransitionGroup>
-            <CSSTransition
-              key={assetUrls ? 'cover' : 'pattern'}
-              timeout={parseInt(transitions.timings.loading)}
-              classNames={transitions.names.fade}
-            >
-              {assetUrls ? (
-                <CoverImage resolvedUrls={assetUrls} />
-              ) : hasCoverUploadFailed ? (
+          <CoverImage
+            resolvedUrls={assetUrls}
+            imagePlaceholder={
+              hasCoverUploadFailed ? (
                 <FailedUploadContainer>
                   <StyledSvgIllustrativeFileFailed />
                   <Text as="span" variant="t100" color="colorText">
@@ -65,9 +58,9 @@ export const ChannelCover: FC<ChannelCoverProps> = ({
                 </FailedUploadContainer>
               ) : (
                 <StyledBackgroundPattern />
-              )}
-            </CSSTransition>
-          </TransitionGroup>
+              )
+            }
+          />
         </Media>
       </MediaWrapper>
     </CoverWrapper>

+ 0 - 9
packages/atlas/src/components/_video/VideoThumbnail/VideoThumbnail.styles.ts

@@ -3,7 +3,6 @@ import styled from '@emotion/styled'
 import { Link } from 'react-router-dom'
 
 import { AssetImage } from '@/components/AssetImage'
-import { SkeletonLoader } from '@/components/_loaders/SkeletonLoader'
 import { cVar, square } from '@/styles'
 
 const sharedOverlayStyles = css`
@@ -248,11 +247,3 @@ export const VideoThumbnailContainer = styled(Link, { shouldForwardProp: isPropV
   }
   /* stylelint-enable no-duplicate-selectors */
 `
-
-export const ThumbnailSkeletonLoader = styled(SkeletonLoader)`
-  ${square('100%')}
-
-  position: absolute;
-  top: 0;
-  left: 0;
-`

+ 7 - 17
packages/atlas/src/components/_video/VideoThumbnail/VideoThumbnail.tsx

@@ -1,7 +1,7 @@
 import { To } from 'history'
 import { MouseEvent, ReactNode, forwardRef, useState } from 'react'
 import { LinkProps } from 'react-router-dom'
-import { CSSTransition, SwitchTransition } from 'react-transition-group'
+import { CSSTransition } from 'react-transition-group'
 
 import { SvgControlsPlaylist } from '@/assets/icons'
 import { Text } from '@/components/Text'
@@ -17,7 +17,6 @@ import {
   SlotsOverlay,
   ThumbnailBackground,
   ThumbnailImage,
-  ThumbnailSkeletonLoader,
   VideoThumbnailContainer,
 } from './VideoThumbnail.styles'
 
@@ -95,21 +94,12 @@ export const VideoThumbnail = forwardRef<HTMLAnchorElement, VideoThumbnailProps>
         isPlaylist={type === 'playlist'}
       >
         <ContentOverlay>
-          <SwitchTransition>
-            <CSSTransition
-              key={String(loading)}
-              timeout={parseInt(cVar('animationTimingFast', true))}
-              classNames={transitions.names.fade}
-            >
-              {loading ? (
-                <ThumbnailSkeletonLoader />
-              ) : (
-                <ThumbnailBackground>
-                  {thumbnailUrls && <ThumbnailImage resolvedUrls={thumbnailUrls || ''} alt={thumbnailAlt || ''} />}
-                </ThumbnailBackground>
-              )}
-            </CSSTransition>
-          </SwitchTransition>
+          <ThumbnailImage
+            isLoading={loading}
+            resolvedUrls={thumbnailUrls}
+            alt={thumbnailAlt || ''}
+            imagePlaceholder={<ThumbnailBackground />}
+          />
           {contentSlot && (
             <CSSTransition
               in={!!contentSlot}

+ 0 - 11
packages/atlas/src/views/viewer/MemberView/ActivityItem.styles.ts

@@ -64,17 +64,6 @@ export const Title = styled(Text)`
   text-overflow: ellipsis;
 `
 export const Thumbnail = styled(AssetImage)`
-  height: 40px;
-
-  ${media.sm} {
-    height: 64px;
-  }
-
-  ${media.lg} {
-    height: 80px;
-  }
-`
-export const ThumbnailSkeletonLoader = styled(SkeletonLoader)`
   width: 71px;
   height: 40px;
 

+ 1 - 2
packages/atlas/src/views/viewer/MemberView/ActivityItem.tsx

@@ -12,7 +12,6 @@ import {
   PillAndDateContainer,
   PillSkeletonLoader,
   Thumbnail,
-  ThumbnailSkeletonLoader,
   Title,
   TitleAndDescriptionContainer,
   TitleSkeletonLoader,
@@ -55,7 +54,7 @@ export const ActivityItem: FC<ActivityItemProps> = ({
   const isImageLoading = loading || thumbnailLoading
   return (
     <ActivityItemContainer loading={loading} onClick={onItemClick}>
-      {isImageLoading ? <ThumbnailSkeletonLoader /> : <Thumbnail resolvedUrls={thumbnailUris} />}
+      <Thumbnail resolvedUrls={thumbnailUris} isLoading={isImageLoading} />
       <TitleAndDescriptionContainer>
         {loading ? (
           <TitleSkeletonLoader />