1import React, { useEffect, Ref, useMemo } from 'react';
2
3import ColorTintFilter, { getTintColorStyle } from './ColorTintFilter';
4import { ImageWrapperProps } from './ImageWrapper.types';
5import { getImageWrapperEventHandler } from './getImageWrapperEventHandler';
6import { absoluteFilledPosition, getObjectPositionFromContentPositionObject } from './positioning';
7import { SrcSetSource } from './useSourceSelection';
8import { ImageNativeProps, ImageSource } from '../Image.types';
9import { useBlurhash } from '../utils/blurhash/useBlurhash';
10import { isBlurhashString, isThumbhashString } from '../utils/resolveSources';
11import { thumbHashStringToDataURL } from '../utils/thumbhash/thumbhash';
12
13function getFetchPriorityFromImagePriority(priority: ImageNativeProps['priority'] = 'normal') {
14  return priority && ['low', 'high'].includes(priority) ? priority : 'auto';
15}
16
17function getImgPropsFromSource(source: ImageSource | SrcSetSource | null | undefined) {
18  if (source && 'srcset' in source) {
19    return {
20      srcSet: source.srcset,
21      sizes: source.sizes,
22    };
23  }
24  return {};
25}
26
27const ImageWrapper = React.forwardRef(
28  (
29    {
30      source,
31      events,
32      contentPosition,
33      hashPlaceholderContentPosition,
34      priority,
35      style,
36      hashPlaceholderStyle,
37      tintColor,
38      className,
39      accessibilityLabel,
40      ...props
41    }: ImageWrapperProps,
42    ref: Ref<HTMLImageElement>
43  ) => {
44    useEffect(() => {
45      events?.onMount?.forEach((e) => e?.());
46    }, []);
47
48    const isBlurhash = isBlurhashString(source?.uri || '');
49    const isThumbhash = isThumbhashString(source?.uri || '');
50    const isHash = isBlurhash || isThumbhash;
51
52    // Thumbhash uri always has to start with 'thumbhash:/'
53    const thumbhash = source?.uri?.replace(/thumbhash:\//, '');
54    const thumbhashUri = useMemo(
55      () => (isThumbhash ? thumbHashStringToDataURL(thumbhash ?? '') : null),
56      [thumbhash]
57    );
58
59    const blurhashUri = useBlurhash(isBlurhash ? source?.uri : null, source?.width, source?.height);
60    if (!source) {
61      return null;
62    }
63
64    const objectPosition = getObjectPositionFromContentPositionObject(
65      isHash ? hashPlaceholderContentPosition : contentPosition
66    );
67
68    const uri = isHash ? blurhashUri ?? thumbhashUri : source?.uri;
69
70    return (
71      <>
72        <ColorTintFilter tintColor={tintColor} />
73        <img
74          ref={ref}
75          alt={accessibilityLabel}
76          className={className}
77          src={uri || undefined}
78          key={source?.uri}
79          style={{
80            objectPosition,
81            ...absoluteFilledPosition,
82            ...getTintColorStyle(tintColor),
83            ...(isHash ? hashPlaceholderStyle : {}),
84            ...style,
85          }}
86          // @ts-ignore
87          // eslint-disable-next-line react/no-unknown-property
88          fetchpriority={getFetchPriorityFromImagePriority(priority || 'normal')}
89          {...getImageWrapperEventHandler(events, source)}
90          {...getImgPropsFromSource(source)}
91          {...props}
92        />
93      </>
94    );
95  }
96);
97
98export default ImageWrapper;
99