Skip to content

Commit

Permalink
feature: Update Image.getSize/getSizeWithHeaders methods to return…
Browse files Browse the repository at this point in the history
… a promise (#42895)

Summary:
`Image.getSize/getSizeWithHeaders` are still working in old fashioned "callback" way

```tsx
Image.getSize(uri, function success(width,height) {  }, function failure(){   } ); // undefined
Image.getSizeWithHeaders(uri, headers, function success(width,height) {  }, function failure(){   } ); // undefined
```

But in 2024 more developers prefer use async/await syntax for asynchronous operations

So, in this PR I added support for Promise API with **backward compatibility**, modern way:

```tsx
Image.getSize(uri).then(({width,height}) => {    }); // Promise
Image.getSizeWithHeaders(uri, headers).then(({width,height}) => {    }); // Promise
```
bypass-github-export-checks
## Changelog:

[GENERAL] [ADDED] - `Image.getSize/getSizeWithHeaders` method returns a promise if you don't pass a `success` callback

Pull Request resolved: #42895

Test Plan:
1. ts: New test cases added in typescript tests
2. runtime: you can create a new project and put code from this PR into the next files
  a. `node_modules/react-native/Libraries/Image/Image.android.js`
  b. `node_modules/react-native/Libraries/Image/Image.ios.js`

Reviewed By: javache

Differential Revision: D53919431

Pulled By: cipolleschi

fbshipit-source-id: 508b201e17e0ffda2e67aa5292bf9906b88d09c5
  • Loading branch information
retyui authored and facebook-github-bot committed Feb 27, 2024
1 parent 489df72 commit 2c1bcba
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 30 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ module.exports = {
files: ['**/*.d.ts'],
plugins: ['redundant-undefined'],
rules: {
'no-dupe-class-members': 'off',
'redundant-undefined/redundant-undefined': [
'error',
{followExactOptionalPropertyTypes: true},
Expand Down
32 changes: 19 additions & 13 deletions packages/react-native/Libraries/Image/Image.android.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ import {
import {getImageSourcesFromImageProps} from './ImageSourceUtils';
import {convertObjectFitToResizeMode} from './ImageUtils';
import ImageViewNativeComponent from './ImageViewNativeComponent';
import NativeImageLoaderAndroid from './NativeImageLoaderAndroid';
import NativeImageLoaderAndroid, {
type ImageSize,
} from './NativeImageLoaderAndroid';
import resolveAssetSource from './resolveAssetSource';
import TextInlineImageNativeComponent from './TextInlineImageNativeComponent';
import * as React from 'react';
Expand All @@ -40,13 +42,15 @@ function generateRequestId() {
*/
function getSize(
url: string,
success: (width: number, height: number) => void,
success?: (width: number, height: number) => void,
failure?: (error: mixed) => void,
): void {
NativeImageLoaderAndroid.getSize(url)
.then(function (sizes) {
success(sizes.width, sizes.height);
})
): void | Promise<ImageSize> {
const promise = NativeImageLoaderAndroid.getSize(url);
if (typeof success !== 'function') {
return promise;
}
promise
.then(sizes => success(sizes.width, sizes.height))
.catch(
failure ||
function () {
Expand All @@ -64,13 +68,15 @@ function getSize(
function getSizeWithHeaders(
url: string,
headers: {[string]: string, ...},
success: (width: number, height: number) => void,
success?: (width: number, height: number) => void,
failure?: (error: mixed) => void,
): void {
NativeImageLoaderAndroid.getSizeWithHeaders(url, headers)
.then(function (sizes) {
success(sizes.width, sizes.height);
})
): void | Promise<ImageSize> {
const promise = NativeImageLoaderAndroid.getSizeWithHeaders(url, headers);
if (typeof success !== 'function') {
return promise;
}
promise
.then(sizes => success(sizes.width, sizes.height))
.catch(
failure ||
function () {
Expand Down
15 changes: 13 additions & 2 deletions packages/react-native/Libraries/Image/Image.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -325,20 +325,31 @@ export interface ImageProps extends ImagePropsBase {
style?: StyleProp<ImageStyle> | undefined;
}

export interface ImageSize {
width: number;
height: number;
}

declare class ImageComponent extends React.Component<ImageProps> {}
declare const ImageBase: Constructor<NativeMethods> & typeof ImageComponent;
export class Image extends ImageBase {
static getSize(uri: string): Promise<ImageSize>;
static getSize(
uri: string,
success: (width: number, height: number) => void,
failure?: (error: any) => void,
): any;
): void;

static getSizeWithHeaders(
uri: string,
headers: {[index: string]: string},
): Promise<ImageSize>;
static getSizeWithHeaders(
uri: string,
headers: {[index: string]: string},
success: (width: number, height: number) => void,
failure?: (error: any) => void,
): any;
): void;
static prefetch(url: string): Promise<boolean>;
static prefetchWithMetadata(
url: string,
Expand Down
32 changes: 21 additions & 11 deletions packages/react-native/Libraries/Image/Image.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import type {ImageStyle, ImageStyleProp} from '../StyleSheet/StyleSheet';
import type {RootTag} from '../Types/RootTagTypes';
import type {AbstractImageIOS, ImageIOS} from './ImageTypes.flow';
import type {ImageSize} from './NativeImageLoaderAndroid';

import {createRootTag} from '../ReactNative/RootTag';
import flattenStyle from '../StyleSheet/flattenStyle';
Expand All @@ -29,29 +30,38 @@ import * as React from 'react';

function getSize(
uri: string,
success: (width: number, height: number) => void,
success?: (width: number, height: number) => void,
failure?: (error: mixed) => void,
): void {
NativeImageLoaderIOS.getSize(uri)
.then(([width, height]) => success(width, height))
): void | Promise<ImageSize> {
const promise = NativeImageLoaderIOS.getSize(uri).then(([width, height]) => ({
width,
height,
}));
if (typeof success !== 'function') {
return promise;
}
promise
.then(sizes => success(sizes.width, sizes.height))
.catch(
failure ||
function () {
console.warn('Failed to get size for image ' + uri);
console.warn('Failed to get size for image: ' + uri);
},
);
}

function getSizeWithHeaders(
uri: string,
headers: {[string]: string, ...},
success: (width: number, height: number) => void,
success?: (width: number, height: number) => void,
failure?: (error: mixed) => void,
): void {
NativeImageLoaderIOS.getSizeWithHeaders(uri, headers)
.then(function (sizes) {
success(sizes.width, sizes.height);
})
): void | Promise<ImageSize> {
const promise = NativeImageLoaderIOS.getSizeWithHeaders(uri, headers);
if (typeof success !== 'function') {
return promise;
}
promise
.then(sizes => success(sizes.width, sizes.height))
.catch(
failure ||
function () {
Expand Down
9 changes: 7 additions & 2 deletions packages/react-native/Libraries/Image/ImageTypes.flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,17 @@ import typeof TextInlineImageNativeComponent from './TextInlineImageNativeCompon
import * as React from 'react';

type ImageComponentStaticsIOS = $ReadOnly<{
getSize: (
getSize(uri: string): Promise<{width: number, height: number}>,
getSize(
uri: string,
success: (width: number, height: number) => void,
failure?: (error: mixed) => void,
) => void,
): void,

getSizeWithHeaders(
uri: string,
headers: {[string]: string, ...},
): Promise<{width: number, height: number}>,
getSizeWithHeaders(
uri: string,
headers: {[string]: string, ...},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4484,11 +4484,16 @@ exports[`public API should not change unintentionally Libraries/Image/ImageSourc

exports[`public API should not change unintentionally Libraries/Image/ImageTypes.flow.js 1`] = `
"type ImageComponentStaticsIOS = $ReadOnly<{
getSize: (
getSize(uri: string): Promise<{ width: number, height: number }>,
getSize(
uri: string,
success: (width: number, height: number) => void,
failure?: (error: mixed) => void
) => void,
): void,
getSizeWithHeaders(
uri: string,
headers: { [string]: string, ... }
): Promise<{ width: number, height: number }>,
getSizeWithHeaders(
uri: string,
headers: { [string]: string, ... },
Expand Down
6 changes: 6 additions & 0 deletions packages/react-native/types/__typetests__/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1267,12 +1267,18 @@ export class ImageTest extends React.Component {
}
});

const promise1: Promise<any> = Image.getSize(uri).then(({width, height}) =>
console.log(width, height),
);
Image.getSize(uri, (width, height) => console.log(width, height));
Image.getSize(
uri,
(width, height) => console.log(width, height),
error => console.error(error),
);
const promise2: Promise<any> = Image.getSizeWithHeaders(uri, headers).then(
({width, height}) => console.log(width, height),
);
Image.getSizeWithHeaders(uri, headers, (width, height) =>
console.log(width, height),
);
Expand Down

0 comments on commit 2c1bcba

Please sign in to comment.