Image.android.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. /**
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. *
  7. * @flow
  8. * @format
  9. */
  10. 'use strict';
  11. const DeprecatedImageStylePropTypes = require('../DeprecatedPropTypes/DeprecatedImageStylePropTypes');
  12. const DeprecatedStyleSheetPropType = require('../DeprecatedPropTypes/DeprecatedStyleSheetPropType');
  13. const DeprecatedViewPropTypes = require('../DeprecatedPropTypes/DeprecatedViewPropTypes');
  14. import ImageViewNativeComponent from './ImageViewNativeComponent';
  15. const PropTypes = require('prop-types');
  16. const React = require('react');
  17. const ReactNative = require('../Renderer/shims/ReactNative'); // eslint-disable-line no-unused-vars
  18. const StyleSheet = require('../StyleSheet/StyleSheet');
  19. const TextAncestor = require('../Text/TextAncestor');
  20. const ImageAnalyticsTagContext = require('./ImageAnalyticsTagContext').default;
  21. const flattenStyle = require('../StyleSheet/flattenStyle');
  22. const resolveAssetSource = require('./resolveAssetSource');
  23. import NativeImageLoaderAndroid from './NativeImageLoaderAndroid';
  24. const TextInlineImageNativeComponent = require('./TextInlineImageNativeComponent');
  25. import type {ImageProps as ImagePropsType} from './ImageProps';
  26. let _requestId = 1;
  27. function generateRequestId() {
  28. return _requestId++;
  29. }
  30. const ImageProps = {
  31. ...DeprecatedViewPropTypes,
  32. style: (DeprecatedStyleSheetPropType(
  33. DeprecatedImageStylePropTypes,
  34. ): ReactPropsCheckType),
  35. /**
  36. * See https://reactnative.dev/docs/image.html#source
  37. */
  38. source: (PropTypes.oneOfType([
  39. PropTypes.shape({
  40. uri: PropTypes.string,
  41. headers: PropTypes.objectOf(PropTypes.string),
  42. }),
  43. // Opaque type returned by require('./image.jpg')
  44. PropTypes.number,
  45. // Multiple sources
  46. PropTypes.arrayOf(
  47. PropTypes.shape({
  48. uri: PropTypes.string,
  49. width: PropTypes.number,
  50. height: PropTypes.number,
  51. headers: PropTypes.objectOf(PropTypes.string),
  52. }),
  53. ),
  54. ]): React$PropType$Primitive<
  55. | {
  56. headers?: {[string]: string, ...},
  57. uri?: string,
  58. ...
  59. }
  60. | number
  61. | Array<{
  62. headers?: {[string]: string, ...},
  63. height?: number,
  64. uri?: string,
  65. width?: number,
  66. ...
  67. }>,
  68. >),
  69. /**
  70. * blurRadius: the blur radius of the blur filter added to the image
  71. *
  72. * See https://reactnative.dev/docs/image.html#blurradius
  73. */
  74. blurRadius: PropTypes.number,
  75. /**
  76. * See https://reactnative.dev/docs/image.html#defaultsource
  77. */
  78. defaultSource: PropTypes.number,
  79. /**
  80. * See https://reactnative.dev/docs/image.html#loadingindicatorsource
  81. */
  82. loadingIndicatorSource: (PropTypes.oneOfType([
  83. PropTypes.shape({
  84. uri: PropTypes.string,
  85. }),
  86. // Opaque type returned by require('./image.jpg')
  87. PropTypes.number,
  88. ]): React$PropType$Primitive<{uri?: string, ...} | number>),
  89. progressiveRenderingEnabled: PropTypes.bool,
  90. fadeDuration: PropTypes.number,
  91. /**
  92. * Analytics Tag used by this Image
  93. */
  94. internal_analyticTag: PropTypes.string,
  95. /**
  96. * Invoked on load start
  97. */
  98. onLoadStart: PropTypes.func,
  99. /**
  100. * Invoked on load error
  101. */
  102. onError: PropTypes.func,
  103. /**
  104. * Invoked when load completes successfully
  105. */
  106. onLoad: PropTypes.func,
  107. /**
  108. * Invoked when load either succeeds or fails
  109. */
  110. onLoadEnd: PropTypes.func,
  111. /**
  112. * Used to locate this view in end-to-end tests.
  113. */
  114. testID: PropTypes.string,
  115. /**
  116. * The mechanism that should be used to resize the image when the image's dimensions
  117. * differ from the image view's dimensions. Defaults to `auto`.
  118. *
  119. * See https://reactnative.dev/docs/image.html#resizemethod
  120. */
  121. resizeMethod: (PropTypes.oneOf([
  122. 'auto',
  123. 'resize',
  124. 'scale',
  125. ]): React$PropType$Primitive<'auto' | 'resize' | 'scale'>),
  126. /**
  127. * Determines how to resize the image when the frame doesn't match the raw
  128. * image dimensions.
  129. *
  130. * See https://reactnative.dev/docs/image.html#resizemode
  131. */
  132. resizeMode: (PropTypes.oneOf([
  133. 'cover',
  134. 'contain',
  135. 'stretch',
  136. 'repeat',
  137. 'center',
  138. ]): React$PropType$Primitive<
  139. 'cover' | 'contain' | 'stretch' | 'repeat' | 'center',
  140. >),
  141. };
  142. /**
  143. * Retrieve the width and height (in pixels) of an image prior to displaying it
  144. *
  145. * See https://reactnative.dev/docs/image.html#getsize
  146. */
  147. function getSize(
  148. url: string,
  149. success: (width: number, height: number) => void,
  150. failure?: (error: any) => void,
  151. ): any {
  152. return NativeImageLoaderAndroid.getSize(url)
  153. .then(function(sizes) {
  154. success(sizes.width, sizes.height);
  155. })
  156. .catch(
  157. failure ||
  158. function() {
  159. console.warn('Failed to get size for image: ' + url);
  160. },
  161. );
  162. }
  163. /**
  164. * Retrieve the width and height (in pixels) of an image prior to displaying it
  165. * with the ability to provide the headers for the request
  166. *
  167. * See https://reactnative.dev/docs/image.html#getsizewithheaders
  168. */
  169. function getSizeWithHeaders(
  170. url: string,
  171. headers: {[string]: string, ...},
  172. success: (width: number, height: number) => void,
  173. failure?: (error: any) => void,
  174. ): any {
  175. return NativeImageLoaderAndroid.getSizeWithHeaders(url, headers)
  176. .then(function(sizes) {
  177. success(sizes.width, sizes.height);
  178. })
  179. .catch(
  180. failure ||
  181. function() {
  182. console.warn('Failed to get size for image: ' + url);
  183. },
  184. );
  185. }
  186. function prefetch(url: string, callback: ?Function): any {
  187. const requestId = generateRequestId();
  188. callback && callback(requestId);
  189. return NativeImageLoaderAndroid.prefetchImage(url, requestId);
  190. }
  191. function abortPrefetch(requestId: number) {
  192. NativeImageLoaderAndroid.abortRequest(requestId);
  193. }
  194. /**
  195. * Perform cache interrogation.
  196. *
  197. * See https://reactnative.dev/docs/image.html#querycache
  198. */
  199. async function queryCache(
  200. urls: Array<string>,
  201. ): Promise<{[string]: 'memory' | 'disk' | 'disk/memory', ...}> {
  202. return await NativeImageLoaderAndroid.queryCache(urls);
  203. }
  204. type ImageComponentStatics = $ReadOnly<{|
  205. getSize: typeof getSize,
  206. getSizeWithHeaders: typeof getSizeWithHeaders,
  207. prefetch: typeof prefetch,
  208. abortPrefetch: typeof abortPrefetch,
  209. queryCache: typeof queryCache,
  210. resolveAssetSource: typeof resolveAssetSource,
  211. propTypes: typeof ImageProps,
  212. |}>;
  213. /**
  214. * A React component for displaying different types of images,
  215. * including network images, static resources, temporary local images, and
  216. * images from local disk, such as the camera roll.
  217. *
  218. * See https://reactnative.dev/docs/image.html
  219. */
  220. let Image = (props: ImagePropsType, forwardedRef) => {
  221. let source = resolveAssetSource(props.source);
  222. const defaultSource = resolveAssetSource(props.defaultSource);
  223. const loadingIndicatorSource = resolveAssetSource(
  224. props.loadingIndicatorSource,
  225. );
  226. if (source && source.uri === '') {
  227. console.warn('source.uri should not be an empty string');
  228. }
  229. if (props.src) {
  230. console.warn(
  231. 'The <Image> component requires a `source` property rather than `src`.',
  232. );
  233. }
  234. if (props.children) {
  235. throw new Error(
  236. 'The <Image> component cannot contain children. If you want to render content on top of the image, consider using the <ImageBackground> component or absolute positioning.',
  237. );
  238. }
  239. if (props.defaultSource && props.loadingIndicatorSource) {
  240. throw new Error(
  241. 'The <Image> component cannot have defaultSource and loadingIndicatorSource at the same time. Please use either defaultSource or loadingIndicatorSource.',
  242. );
  243. }
  244. if (source && !source.uri && !Array.isArray(source)) {
  245. source = null;
  246. }
  247. let style;
  248. let sources;
  249. if (source?.uri != null) {
  250. const {width, height} = source;
  251. style = flattenStyle([{width, height}, styles.base, props.style]);
  252. sources = [{uri: source.uri}];
  253. } else {
  254. style = flattenStyle([styles.base, props.style]);
  255. sources = source;
  256. }
  257. const {onLoadStart, onLoad, onLoadEnd, onError} = props;
  258. const nativeProps = {
  259. ...props,
  260. style,
  261. shouldNotifyLoadEvents: !!(onLoadStart || onLoad || onLoadEnd || onError),
  262. src: sources,
  263. /* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue was found
  264. * when making Flow check .android.js files. */
  265. headers: source?.headers,
  266. defaultSrc: defaultSource ? defaultSource.uri : null,
  267. loadingIndicatorSrc: loadingIndicatorSource
  268. ? loadingIndicatorSource.uri
  269. : null,
  270. ref: forwardedRef,
  271. };
  272. return (
  273. <ImageAnalyticsTagContext.Consumer>
  274. {analyticTag => {
  275. const nativePropsWithAnalytics =
  276. analyticTag !== null
  277. ? {
  278. ...nativeProps,
  279. internal_analyticTag: analyticTag,
  280. }
  281. : nativeProps;
  282. return (
  283. <TextAncestor.Consumer>
  284. {hasTextAncestor =>
  285. hasTextAncestor ? (
  286. <TextInlineImageNativeComponent {...nativePropsWithAnalytics} />
  287. ) : (
  288. <ImageViewNativeComponent {...nativePropsWithAnalytics} />
  289. )
  290. }
  291. </TextAncestor.Consumer>
  292. );
  293. }}
  294. </ImageAnalyticsTagContext.Consumer>
  295. );
  296. };
  297. Image = React.forwardRef<
  298. ImagePropsType,
  299. | React.ElementRef<typeof TextInlineImageNativeComponent>
  300. | React.ElementRef<typeof ImageViewNativeComponent>,
  301. >(Image);
  302. Image.displayName = 'Image';
  303. /**
  304. * Retrieve the width and height (in pixels) of an image prior to displaying it
  305. *
  306. * See https://reactnative.dev/docs/image.html#getsize
  307. */
  308. /* $FlowFixMe(>=0.89.0 site=react_native_android_fb) This comment suppresses an
  309. * error found when Flow v0.89 was deployed. To see the error, delete this
  310. * comment and run Flow. */
  311. Image.getSize = getSize;
  312. /**
  313. * Retrieve the width and height (in pixels) of an image prior to displaying it
  314. * with the ability to provide the headers for the request
  315. *
  316. * See https://reactnative.dev/docs/image.html#getsizewithheaders
  317. */
  318. /* $FlowFixMe(>=0.89.0 site=react_native_android_fb) This comment suppresses an
  319. * error found when Flow v0.89 was deployed. To see the error, delete this
  320. * comment and run Flow. */
  321. Image.getSizeWithHeaders = getSizeWithHeaders;
  322. /**
  323. * Prefetches a remote image for later use by downloading it to the disk
  324. * cache
  325. *
  326. * See https://reactnative.dev/docs/image.html#prefetch
  327. */
  328. /* $FlowFixMe(>=0.89.0 site=react_native_android_fb) This comment suppresses an
  329. * error found when Flow v0.89 was deployed. To see the error, delete this
  330. * comment and run Flow. */
  331. Image.prefetch = prefetch;
  332. /**
  333. * Abort prefetch request.
  334. *
  335. * See https://reactnative.dev/docs/image.html#abortprefetch
  336. */
  337. /* $FlowFixMe(>=0.89.0 site=react_native_android_fb) This comment suppresses an
  338. * error found when Flow v0.89 was deployed. To see the error, delete this
  339. * comment and run Flow. */
  340. Image.abortPrefetch = abortPrefetch;
  341. /**
  342. * Perform cache interrogation.
  343. *
  344. * See https://reactnative.dev/docs/image.html#querycache
  345. */
  346. /* $FlowFixMe(>=0.89.0 site=react_native_android_fb) This comment suppresses an
  347. * error found when Flow v0.89 was deployed. To see the error, delete this
  348. * comment and run Flow. */
  349. Image.queryCache = queryCache;
  350. /**
  351. * Resolves an asset reference into an object.
  352. *
  353. * See https://reactnative.dev/docs/image.html#resolveassetsource
  354. */
  355. /* $FlowFixMe(>=0.89.0 site=react_native_android_fb) This comment suppresses an
  356. * error found when Flow v0.89 was deployed. To see the error, delete this
  357. * comment and run Flow. */
  358. Image.resolveAssetSource = resolveAssetSource;
  359. /* $FlowFixMe(>=0.89.0 site=react_native_android_fb) This comment suppresses an
  360. * error found when Flow v0.89 was deployed. To see the error, delete this
  361. * comment and run Flow. */
  362. Image.propTypes = ImageProps;
  363. const styles = StyleSheet.create({
  364. base: {
  365. overflow: 'hidden',
  366. },
  367. });
  368. module.exports = ((Image: any): React.AbstractComponent<
  369. ImagePropsType,
  370. | React.ElementRef<typeof TextInlineImageNativeComponent>
  371. | React.ElementRef<typeof ImageViewNativeComponent>,
  372. > &
  373. ImageComponentStatics);