react-native-fast-image 10.3.0 → 10.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -4
- package/android/src/main/java/com/fastimage/FastImageViewConverter.java +1 -1
- package/android/src/main/java/com/fastimage/FastImageViewManager.java +14 -1
- package/android/src/main/java/com/fastimage/FastImageViewWithUrl.java +145 -5
- package/dist/index.cjs.js +10 -4
- package/dist/index.cjs.js.flow +9 -1
- package/dist/index.cjs.web.js +29 -7
- package/dist/index.cjs.web.js.flow +9 -1
- package/dist/index.d.ts +30 -3
- package/dist/index.js +10 -4
- package/dist/index.js.flow +9 -1
- package/dist/index.web.js +29 -7
- package/dist/index.web.js.flow +9 -1
- package/dist/placeholders.d.ts +2 -1
- package/dist/specs/FastImageViewNativeComponent.d.ts +1 -0
- package/ios/FastImage/FFFDownsampledImage.h +10 -7
- package/ios/FastImage/FFFDownsampledImage.m +25 -3
- package/ios/FastImage/FFFastImageModule.mm +1 -1
- package/ios/FastImage/FFFastImageView.h +8 -0
- package/ios/FastImage/FFFastImageView.m +239 -16
- package/ios/FastImage/FFFastImageViewComponentView.mm +22 -1
- package/package.json +1 -1
- package/src/index.js.flow +9 -1
- package/src/index.tsx +60 -13
- package/src/index.web.tsx +48 -4
- package/src/placeholders.ts +26 -9
- package/src/specs/FastImageViewNativeComponent.ts +7 -2
package/src/index.web.tsx
CHANGED
|
@@ -32,7 +32,11 @@ import { getAssetByID } from 'react-native-web/dist/modules/AssetRegistry'
|
|
|
32
32
|
import { cacheControl, priority, resizeMode } from './constants'
|
|
33
33
|
import { resolveObjectFit } from './objectFit'
|
|
34
34
|
import { cssPosition, objectPositionOrCentered } from './objectPosition'
|
|
35
|
-
import {
|
|
35
|
+
import {
|
|
36
|
+
dataUri,
|
|
37
|
+
hasSourcePlaceholder,
|
|
38
|
+
placeholderSource,
|
|
39
|
+
} from './placeholders'
|
|
36
40
|
import { warnIfChildren, warnOnce } from './warnings'
|
|
37
41
|
import type {
|
|
38
42
|
CachePathResult,
|
|
@@ -82,6 +86,7 @@ function FastImageBase({
|
|
|
82
86
|
objectPosition,
|
|
83
87
|
loading,
|
|
84
88
|
sizes,
|
|
89
|
+
dynamicRangeLimit,
|
|
85
90
|
forwardedRef,
|
|
86
91
|
// On the wrapper, the view the layout and touches are for here (on
|
|
87
92
|
// native, FastImage's only view).
|
|
@@ -131,11 +136,20 @@ function FastImageBase({
|
|
|
131
136
|
const blurUri =
|
|
132
137
|
resolveUri(dataUri(shared(source, 'blurDataUri', preview))) ??
|
|
133
138
|
thumbhashUri(shared(source, 'thumbhash', preview))
|
|
139
|
+
// `placeholder` as a url (or a source with one) shows over the other
|
|
140
|
+
// placeholders as it loads, as on iOS and Android. The browser loads it
|
|
141
|
+
// like any image: it can't look in its cache only (cache: 'cacheOnly').
|
|
142
|
+
const urlPlaceholder = placeholderSource(placeholder)?.uri
|
|
143
|
+
const [failedPlaceholder, setFailedPlaceholder] = useState<string>()
|
|
134
144
|
// A thumbhash or blurDataUri in `placeholder` isn't an image to show as it
|
|
135
|
-
// is: defaultSource still is.
|
|
145
|
+
// is, nor is a url (above): defaultSource still is.
|
|
136
146
|
const defaultUri =
|
|
137
147
|
blurUri ??
|
|
138
|
-
resolveUri(
|
|
148
|
+
resolveUri(
|
|
149
|
+
preview || urlPlaceholder
|
|
150
|
+
? defaultSource
|
|
151
|
+
: (placeholder ?? defaultSource),
|
|
152
|
+
)
|
|
139
153
|
// Whether the image for `key` loaded, or failed.
|
|
140
154
|
const [result, setResult] = useState<{ key: string; ok: boolean }>()
|
|
141
155
|
const ok = result && result.key === key ? result.ok : undefined
|
|
@@ -155,7 +169,7 @@ function FastImageBase({
|
|
|
155
169
|
.filter(Boolean)
|
|
156
170
|
.join(' ') || undefined
|
|
157
171
|
// The placeholder shows until the image has loaded, and if it fails.
|
|
158
|
-
const showDefault =
|
|
172
|
+
const showDefault = ok !== true
|
|
159
173
|
|
|
160
174
|
useClientLayoutEffect(() => {
|
|
161
175
|
if (key) onLoadStart?.()
|
|
@@ -179,6 +193,7 @@ function FastImageBase({
|
|
|
179
193
|
// Loaded when the image is (a data uri needs no request).
|
|
180
194
|
loading={loading}
|
|
181
195
|
priority={priority}
|
|
196
|
+
dynamicRangeLimit={dynamicRangeLimit}
|
|
182
197
|
// A source's placeholder stands for an image whose size
|
|
183
198
|
// isn't known yet: as contain for scale-down and none,
|
|
184
199
|
// which show an image at its own size.
|
|
@@ -205,6 +220,28 @@ function FastImageBase({
|
|
|
205
220
|
alt={key ? '' : (label ?? '')}
|
|
206
221
|
/>
|
|
207
222
|
) : null}
|
|
223
|
+
{urlPlaceholder && showDefault ? (
|
|
224
|
+
<Picture
|
|
225
|
+
key={`placeholder ${urlPlaceholder}`}
|
|
226
|
+
src={urlPlaceholder}
|
|
227
|
+
loading={loading}
|
|
228
|
+
// It only helps if it comes before the image, as on iOS
|
|
229
|
+
// and Android.
|
|
230
|
+
priority="high"
|
|
231
|
+
dynamicRangeLimit={dynamicRangeLimit}
|
|
232
|
+
// It stands for the image, as a source's preview does.
|
|
233
|
+
fit={
|
|
234
|
+
fit === 'scale-down' || fit === 'none' ? 'contain' : fit
|
|
235
|
+
}
|
|
236
|
+
position={position}
|
|
237
|
+
// Tinted, not blurred, as on iOS and Android.
|
|
238
|
+
filter={tintColor ? `url(#${tintId})` : undefined}
|
|
239
|
+
// Nothing (no broken image icon) if it fails.
|
|
240
|
+
hidden={failedPlaceholder === urlPlaceholder}
|
|
241
|
+
onError={() => setFailedPlaceholder(urlPlaceholder)}
|
|
242
|
+
alt=""
|
|
243
|
+
/>
|
|
244
|
+
) : null}
|
|
208
245
|
{key && src ? (
|
|
209
246
|
<Picture
|
|
210
247
|
key={several ? 'sizes' : key}
|
|
@@ -213,6 +250,7 @@ function FastImageBase({
|
|
|
213
250
|
sizes={sizes}
|
|
214
251
|
loading={loading}
|
|
215
252
|
priority={priority}
|
|
253
|
+
dynamicRangeLimit={dynamicRangeLimit}
|
|
216
254
|
fit={fit}
|
|
217
255
|
position={position}
|
|
218
256
|
filter={filter}
|
|
@@ -414,6 +452,7 @@ function Picture({
|
|
|
414
452
|
sizes,
|
|
415
453
|
loading,
|
|
416
454
|
priority,
|
|
455
|
+
dynamicRangeLimit,
|
|
417
456
|
fit,
|
|
418
457
|
position,
|
|
419
458
|
filter,
|
|
@@ -429,6 +468,9 @@ function Picture({
|
|
|
429
468
|
loading?: 'lazy' | 'eager'
|
|
430
469
|
// source.priority, as the <img>'s fetchpriority.
|
|
431
470
|
priority?: Priority
|
|
471
|
+
// The prop, as CSS's dynamic-range-limit (the browser's default when
|
|
472
|
+
// not set).
|
|
473
|
+
dynamicRangeLimit?: 'standard' | 'constrained' | 'no-limit'
|
|
432
474
|
fit: ObjectFit
|
|
433
475
|
// objectPosition, as CSS.
|
|
434
476
|
position: string
|
|
@@ -513,6 +555,8 @@ function Picture({
|
|
|
513
555
|
objectPosition: position,
|
|
514
556
|
filter,
|
|
515
557
|
opacity: hidden ? 0 : undefined,
|
|
558
|
+
// Not in React's CSS types yet.
|
|
559
|
+
...({ dynamicRangeLimit } as object),
|
|
516
560
|
}}
|
|
517
561
|
onLoad={(event) =>
|
|
518
562
|
loaded(event.currentTarget as unknown as LoadedImage)
|
package/src/placeholders.ts
CHANGED
|
@@ -1,20 +1,37 @@
|
|
|
1
|
-
// Placeholders given as strings
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
|
|
7
|
-
import type { SourcePlaceholder } from './index'
|
|
1
|
+
// Placeholders given as strings, shared by the native (index.tsx) and web
|
|
2
|
+
// (index.web.tsx) versions: a source's `blurDataUri` is a data uri, decoded at
|
|
3
|
+
// once, so it shows in the view's first frame (a url there is ignored, with a
|
|
4
|
+
// warning in development). `placeholder` can also be the name of an image
|
|
5
|
+
// bundled with the app, or a url, loaded as a source (placeholderSource).
|
|
6
|
+
import type { PlaceholderSource, SourcePlaceholder } from './index'
|
|
8
7
|
import { warnOnce } from './warnings'
|
|
9
8
|
|
|
10
9
|
// Metro's (and React Native's Jest preset's) development flag; other web
|
|
11
10
|
// bundlers may not define it.
|
|
12
11
|
declare const __DEV__: boolean | undefined
|
|
13
12
|
|
|
13
|
+
const SCHEME = /^[a-z][a-z\d+.-]*:/i
|
|
14
|
+
|
|
14
15
|
// Whether `placeholder` is the name of an image bundled with the app (an iOS
|
|
15
16
|
// asset catalog image, an Android drawable): no scheme, not a path.
|
|
16
17
|
export function isImageName(value: string): boolean {
|
|
17
|
-
return
|
|
18
|
+
return !SCHEME.test(value) && !value.startsWith('/')
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// `placeholder` as a source to load: an http or https url, as a string or a
|
|
22
|
+
// source object's uri. Undefined for anything else (a local file or a data uri
|
|
23
|
+
// is decoded at once, as the native placeholder prop).
|
|
24
|
+
export function placeholderSource(
|
|
25
|
+
value: unknown,
|
|
26
|
+
): PlaceholderSource | undefined {
|
|
27
|
+
const uri =
|
|
28
|
+
typeof value === 'string'
|
|
29
|
+
? value
|
|
30
|
+
: value && typeof value === 'object'
|
|
31
|
+
? (value as PlaceholderSource).uri
|
|
32
|
+
: undefined
|
|
33
|
+
if (typeof uri !== 'string' || !/^https?:/i.test(uri)) return undefined
|
|
34
|
+
return typeof value === 'string' ? { uri } : (value as PlaceholderSource)
|
|
18
35
|
}
|
|
19
36
|
|
|
20
37
|
// Whether a source has a thumbhash or blurDataUri, or `placeholder` is one
|
|
@@ -37,7 +54,7 @@ export function dataUri(value: unknown): string | undefined {
|
|
|
37
54
|
if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
|
38
55
|
warnOnce(
|
|
39
56
|
'placeholder-url',
|
|
40
|
-
`react-native-fast-image: \`
|
|
57
|
+
`react-native-fast-image: \`blurDataUri\` takes a data uri (data:image/…): "${value.slice(0, 60)}" is ignored. For a placeholder loaded from a url, use \`placeholder\`.`,
|
|
41
58
|
'warn',
|
|
42
59
|
)
|
|
43
60
|
}
|
|
@@ -45,8 +45,10 @@ export interface NativeProps extends ViewProps {
|
|
|
45
45
|
source?: CodegenTypes.UnsafeMixed
|
|
46
46
|
// Several sources (sizes of one image), picked from for the view's size.
|
|
47
47
|
sources?: CodegenTypes.UnsafeMixed
|
|
48
|
-
// The `placeholder` prop: a
|
|
49
|
-
//
|
|
48
|
+
// The `placeholder` prop: a url to load apart from the image, as a source
|
|
49
|
+
// ({ uri, headers, cacheKey, cache }), or an image shown at once: a
|
|
50
|
+
// resolved asset source ({ uri, width, height, scale }) or a data uri
|
|
51
|
+
// ({ uri } on iOS; the uri on Android, as a string).
|
|
50
52
|
placeholder?: CodegenTypes.UnsafeMixed
|
|
51
53
|
// What shows in place of placeholder while a source loads: its ThumbHash,
|
|
52
54
|
// or its blurDataUri (blurred natively), by the source's index ('' for
|
|
@@ -70,6 +72,9 @@ export interface NativeProps extends ViewProps {
|
|
|
70
72
|
transitionBetweenImages?: boolean
|
|
71
73
|
transitionSkipOnCacheHit?: string
|
|
72
74
|
downsample?: CodegenTypes.WithDefault<boolean, true>
|
|
75
|
+
// standard, constrained or no-limit ('' when not set). iOS and tvOS 17+
|
|
76
|
+
// (Android ignores it).
|
|
77
|
+
dynamicRangeLimit?: string
|
|
73
78
|
blurRadius?: CodegenTypes.Float
|
|
74
79
|
// The events JS has a handler for, as bits: 1 onFastImageLoadStart,
|
|
75
80
|
// 2 onFastImageProgress, 4 onFastImageLoad, 8 onFastImageError, 16
|