expo-image 1.2.0 → 1.2.2
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/CHANGELOG.md +17 -0
- package/README.md +11 -11
- package/android/build.gradle +1 -1
- package/android/src/main/java/expo/modules/image/ExpoImageModule.kt +4 -1
- package/android/src/main/java/expo/modules/image/ExpoImageViewWrapper.kt +9 -9
- package/android/src/main/java/expo/modules/image/okhttp/ExpoImageOkHttpClientGlideModule.kt +4 -0
- package/android/src/main/java/expo/modules/image/thumbhash/ThumbhashModelLoader.kt +5 -2
- package/build/utils/resolveHashString.d.ts.map +1 -1
- package/build/utils/resolveHashString.js +3 -1
- package/build/utils/resolveHashString.js.map +1 -1
- package/build/web/AnimationManager.d.ts.map +1 -1
- package/build/web/AnimationManager.js +8 -3
- package/build/web/AnimationManager.js.map +1 -1
- package/build/web/ImageWrapper.d.ts.map +1 -1
- package/build/web/ImageWrapper.js +9 -1
- package/build/web/ImageWrapper.js.map +1 -1
- package/ios/Loaders/PhotoLibraryAssetLoader.swift +2 -1
- package/ios/ThumbhashLoader.swift +5 -4
- package/package.json +2 -2
- package/src/utils/resolveHashString.tsx +3 -1
- package/src/web/AnimationManager.tsx +9 -3
- package/src/web/ImageWrapper.tsx +8 -1
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,21 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 1.2.2 — 2023-04-27
|
|
14
|
+
|
|
15
|
+
### 🐛 Bug fixes
|
|
16
|
+
|
|
17
|
+
- Fix for the "limited" media library permission. ([#22261](https://github.com/expo/expo/pull/22261) by [@tsapeta](https://github.com/tsapeta))
|
|
18
|
+
|
|
19
|
+
## 1.2.1 — 2023-04-17
|
|
20
|
+
|
|
21
|
+
### 🐛 Bug fixes
|
|
22
|
+
|
|
23
|
+
- [Android] Fix `url` property returned by the `onLoad` event. ([#22161](https://github.com/expo/expo/pull/22161) by [@lukmccall](https://github.com/lukmccall))
|
|
24
|
+
- [Android] Fix images not loading after the app was foregrounded. ([#22159](https://github.com/expo/expo/pull/22159) by [@lukmccall](https://github.com/lukmccall))
|
|
25
|
+
- [Android] Fixed image was loaded event if the view dimensions were 0. ([#22157](https://github.com/expo/expo/pull/22157) by [@lukmccall](https://github.com/lukmccall))
|
|
26
|
+
- Fix generating the image from ThumbHash that starts with a slash character. ([#22160](https://github.com/expo/expo/pull/22160) by [@tsapeta](https://github.com/tsapeta))
|
|
27
|
+
|
|
13
28
|
## 1.2.0 — 2023-04-14
|
|
14
29
|
|
|
15
30
|
### 🎉 New features
|
|
@@ -21,7 +36,9 @@
|
|
|
21
36
|
|
|
22
37
|
### 🐛 Bug fixes
|
|
23
38
|
|
|
39
|
+
- [Web] Improve transition behavior when switching back and forth two sources. ([#22099](https://github.com/expo/expo/pull/22099) by [@aleqsio](https://github.com/aleqsio))
|
|
24
40
|
- [Web] Prevent breaking in static rendering environments. ([#21883](https://github.com/expo/expo/pull/21883) by [@EvanBacon](https://github.com/EvanBacon))
|
|
41
|
+
- [Android] Fixed image disappearing before navigation animation is complete. ([#22066](https://github.com/expo/expo/pull/22066) by [@sallen450](https://github.com/sallen450))
|
|
25
42
|
- [Web] Fixed monorepo asset resolution in production for Metro web. ([#22094](https://github.com/expo/expo/pull/22094) by [@EvanBacon](https://github.com/EvanBacon))
|
|
26
43
|
|
|
27
44
|
## 1.1.0 — 2023-03-25
|
package/README.md
CHANGED
|
@@ -21,17 +21,17 @@ A cross-platform, performant image component for React Native and Expo.
|
|
|
21
21
|
|
|
22
22
|
## Supported image formats
|
|
23
23
|
|
|
24
|
-
|
|
|
25
|
-
|
|
26
|
-
|
|
|
27
|
-
| PNG / APNG |
|
|
28
|
-
|
|
|
29
|
-
|
|
|
30
|
-
|
|
|
31
|
-
|
|
|
32
|
-
|
|
|
33
|
-
|
|
|
34
|
-
|
|
|
24
|
+
| Format | Android | iOS | Web |
|
|
25
|
+
| :--------: | :-----: | :-: | :-----------------------------------------------: |
|
|
26
|
+
| WebP | ✅ | ✅ | ✅ [~96% adoption](https://caniuse.com/webp) |
|
|
27
|
+
| PNG / APNG | ✅ | ✅ | ✅ / ✅ [~96% adoption](https://caniuse.com/apng) |
|
|
28
|
+
| AVIF | ✅ | ✅ | ⏳ [~79% adoption](https://caniuse.com/avif) |
|
|
29
|
+
| HEIC | ✅ | ✅ | ❌ [not adopted yet](https://caniuse.com/heif) |
|
|
30
|
+
| JPEG | ✅ | ✅ | ✅ |
|
|
31
|
+
| GIF | ✅ | ✅ | ✅ |
|
|
32
|
+
| SVG | ✅ | ✅ | ✅ |
|
|
33
|
+
| ICO | ✅ | ✅ | ✅ |
|
|
34
|
+
| ICNS | ❌ | ✅ | ❌ |
|
|
35
35
|
|
|
36
36
|
# API documentation
|
|
37
37
|
|
package/android/build.gradle
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
package expo.modules.image
|
|
2
2
|
|
|
3
3
|
import android.view.View
|
|
4
|
+
import androidx.core.view.doOnDetach
|
|
4
5
|
import com.bumptech.glide.Glide
|
|
5
6
|
import com.bumptech.glide.load.model.GlideUrl
|
|
6
7
|
import com.facebook.react.uimanager.PixelUtil
|
|
@@ -171,7 +172,9 @@ class ExpoImageModule : Module() {
|
|
|
171
172
|
}
|
|
172
173
|
|
|
173
174
|
OnViewDestroys { view: ExpoImageViewWrapper ->
|
|
174
|
-
view.
|
|
175
|
+
view.doOnDetach {
|
|
176
|
+
view.onViewDestroys()
|
|
177
|
+
}
|
|
175
178
|
}
|
|
176
179
|
}
|
|
177
180
|
}
|
|
@@ -3,7 +3,6 @@ package expo.modules.image
|
|
|
3
3
|
import android.annotation.SuppressLint
|
|
4
4
|
import android.app.Activity
|
|
5
5
|
import android.content.Context
|
|
6
|
-
import android.graphics.Rect
|
|
7
6
|
import android.graphics.drawable.Animatable
|
|
8
7
|
import android.graphics.drawable.Drawable
|
|
9
8
|
import android.os.Build
|
|
@@ -371,13 +370,11 @@ class ExpoImageViewWrapper(context: Context, appContext: AppContext) : ExpoView(
|
|
|
371
370
|
return sources.first()
|
|
372
371
|
}
|
|
373
372
|
|
|
374
|
-
val
|
|
375
|
-
if (
|
|
373
|
+
val targetPixelCount = width * height
|
|
374
|
+
if (targetPixelCount == 0) {
|
|
376
375
|
return null
|
|
377
376
|
}
|
|
378
377
|
|
|
379
|
-
val targetPixelCount = selfRect.width() * selfRect.height()
|
|
380
|
-
|
|
381
378
|
var bestSource: SourceMap? = null
|
|
382
379
|
var bestFit = Double.MAX_VALUE
|
|
383
380
|
|
|
@@ -433,8 +430,8 @@ class ExpoImageViewWrapper(context: Context, appContext: AppContext) : ExpoView(
|
|
|
433
430
|
|
|
434
431
|
val sourceToLoad = bestSource?.createGlideModel(context)
|
|
435
432
|
val placeholder = bestPlaceholder?.createGlideModel(context)
|
|
436
|
-
// We only clean the image when the source is set to null and we don't have a placeholder.
|
|
437
|
-
if ((bestSource == null || sourceToLoad == null) && placeholder == null) {
|
|
433
|
+
// We only clean the image when the source is set to null and we don't have a placeholder or the view is empty.
|
|
434
|
+
if (width == 0 || height == 0 || (bestSource == null || sourceToLoad == null) && placeholder == null) {
|
|
438
435
|
firstView.recycleView()
|
|
439
436
|
secondView.recycleView()
|
|
440
437
|
|
|
@@ -621,6 +618,7 @@ class ExpoImageViewWrapper(context: Context, appContext: AppContext) : ExpoView(
|
|
|
621
618
|
companion object {
|
|
622
619
|
private var requestManager: RequestManager? = null
|
|
623
620
|
private var appContextRef: WeakReference<AppContext?> = WeakReference(null)
|
|
621
|
+
private var activityRef: WeakReference<Activity?> = WeakReference(null)
|
|
624
622
|
|
|
625
623
|
fun getOrCreateRequestManager(
|
|
626
624
|
appContext: AppContext,
|
|
@@ -630,13 +628,15 @@ class ExpoImageViewWrapper(context: Context, appContext: AppContext) : ExpoView(
|
|
|
630
628
|
?: return createNewRequestManager(activity).also {
|
|
631
629
|
requestManager = it
|
|
632
630
|
appContextRef = WeakReference(appContext)
|
|
631
|
+
activityRef = WeakReference(activity)
|
|
633
632
|
}
|
|
634
633
|
|
|
635
|
-
// Request manager was created using different activity
|
|
636
|
-
if (appContextRef.get() != appContext) {
|
|
634
|
+
// Request manager was created using different activity or app context
|
|
635
|
+
if (appContextRef.get() != appContext || activityRef.get() != activity) {
|
|
637
636
|
return createNewRequestManager(activity).also {
|
|
638
637
|
requestManager = it
|
|
639
638
|
appContextRef = WeakReference(appContext)
|
|
639
|
+
activityRef = WeakReference(activity)
|
|
640
640
|
}
|
|
641
641
|
}
|
|
642
642
|
|
|
@@ -13,8 +13,11 @@ class ThumbhashModelLoader : ModelLoader<GlideThumbhashModel, Bitmap> {
|
|
|
13
13
|
override fun handles(model: GlideThumbhashModel): Boolean = true
|
|
14
14
|
|
|
15
15
|
override fun buildLoadData(model: GlideThumbhashModel, width: Int, height: Int, options: Options): ModelLoader.LoadData<Bitmap> {
|
|
16
|
-
//
|
|
17
|
-
|
|
16
|
+
// The URI looks like this: thumbhash:/3OcRJYB4d3h\iIeHeEh3eIhw+j2w
|
|
17
|
+
// ThumbHash may include slashes which could break the structure of the URL, so we replace them
|
|
18
|
+
// with backslashes on the JS side and revert them back to slashes here, before generating the image.
|
|
19
|
+
val thumbhash = model.uri.pathSegments.joinToString(separator = "/").replace("\\", "/")
|
|
20
|
+
|
|
18
21
|
return ModelLoader.LoadData(
|
|
19
22
|
ObjectKey(model),
|
|
20
23
|
ThumbhashFetcher(thumbhash)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolveHashString.d.ts","sourceRoot":"","sources":["../../src/utils/resolveHashString.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAS7C;;;;KAIK;AACL,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,CAO9D;AAED;;;;KAIK;AACL,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,
|
|
1
|
+
{"version":3,"file":"resolveHashString.d.ts","sourceRoot":"","sources":["../../src/utils/resolveHashString.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAS7C;;;;KAIK;AACL,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,CAO9D;AAED;;;;KAIK;AACL,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,CAO/D"}
|
|
@@ -21,7 +21,9 @@ export function resolveBlurhashString(str) {
|
|
|
21
21
|
* @return An ImageSource representing the provided thumbhash.
|
|
22
22
|
* */
|
|
23
23
|
export function resolveThumbhashString(str) {
|
|
24
|
-
|
|
24
|
+
// ThumbHash may contain slashes that could break the url when the slash is at the beginning.
|
|
25
|
+
// We replace slashes with backslashes to make sure we don't break the url's path.
|
|
26
|
+
const thumbhash = str.replace(/^thumbhash:\//, '').replace(/\//g, '\\');
|
|
25
27
|
return {
|
|
26
28
|
uri: hashToUri('thumbhash', thumbhash),
|
|
27
29
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolveHashString.js","sourceRoot":"","sources":["../../src/utils/resolveHashString.tsx"],"names":[],"mappings":"AAIA,SAAS,SAAS,CAAC,IAAmB,EAAE,IAAY;IAClD,MAAM,eAAe,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACnF,OAAO,GAAG,IAAI,KAAK,eAAe,EAAE,CAAC;AACvC,CAAC;AAED;;;;KAIK;AACL,MAAM,UAAU,qBAAqB,CAAC,GAAW;IAC/C,MAAM,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7E,OAAO;QACL,GAAG,EAAE,SAAS,CAAC,UAAU,EAAE,QAAQ,CAAC;QACpC,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,EAAE;QAChC,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,EAAE;KACnC,CAAC;AACJ,CAAC;AAED;;;;KAIK;AACL,MAAM,UAAU,sBAAsB,CAAC,GAAW;IAChD,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"resolveHashString.js","sourceRoot":"","sources":["../../src/utils/resolveHashString.tsx"],"names":[],"mappings":"AAIA,SAAS,SAAS,CAAC,IAAmB,EAAE,IAAY;IAClD,MAAM,eAAe,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACnF,OAAO,GAAG,IAAI,KAAK,eAAe,EAAE,CAAC;AACvC,CAAC;AAED;;;;KAIK;AACL,MAAM,UAAU,qBAAqB,CAAC,GAAW;IAC/C,MAAM,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7E,OAAO;QACL,GAAG,EAAE,SAAS,CAAC,UAAU,EAAE,QAAQ,CAAC;QACpC,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,EAAE;QAChC,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,EAAE;KACnC,CAAC;AACJ,CAAC;AAED;;;;KAIK;AACL,MAAM,UAAU,sBAAsB,CAAC,GAAW;IAChD,6FAA6F;IAC7F,kFAAkF;IAClF,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACxE,OAAO;QACL,GAAG,EAAE,SAAS,CAAC,WAAW,EAAE,SAAS,CAAC;KACvC,CAAC;AACJ,CAAC","sourcesContent":["import { ImageSource } from '../Image.types';\n\ntype ImageHashType = 'blurhash' | 'thumbhash';\n\nfunction hashToUri(type: ImageHashType, hash: string): string {\n const encodedBlurhash = encodeURI(hash).replace(/#/g, '%23').replace(/\\?/g, '%3F');\n return `${type}:/${encodedBlurhash}`;\n}\n\n/**\n * Converts a blurhash string (`blurhash:/<hash>/<width>/<height>` or <hash>/<width>/<height>) into an `ImageSource`.\n *\n * @return An ImageSource representing the provided blurhash.\n * */\nexport function resolveBlurhashString(str: string): ImageSource {\n const [blurhash, width, height] = str.replace(/^blurhash:\\//, '').split('/');\n return {\n uri: hashToUri('blurhash', blurhash),\n width: parseInt(width, 10) || 16,\n height: parseInt(height, 10) || 16,\n };\n}\n\n/**\n * Converts a thumbhash string (`thumbhash:/<hash>` or `<hash>`) into an `ImageSource`.\n *\n * @return An ImageSource representing the provided thumbhash.\n * */\nexport function resolveThumbhashString(str: string): ImageSource {\n // ThumbHash may contain slashes that could break the url when the slash is at the beginning.\n // We replace slashes with backslashes to make sure we don't break the url's path.\n const thumbhash = str.replace(/^thumbhash:\\//, '').replace(/\\//g, '\\\\');\n return {\n uri: hashToUri('thumbhash', thumbhash),\n };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnimationManager.d.ts","sourceRoot":"","sources":["../../src/web/AnimationManager.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD,KAAK,SAAS,GAAG;IACf,OAAO,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAC;IAC9B,mBAAmB,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAC;IAC1C,OAAO,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAC;IAC9B,OAAO,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,GAAG,EAAE,MAAM;IACX,cAAc,EAAE,CACd,WAAW,EAAE,WAAW,CAAC,SAAS,CAAC,KAChC,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,aAAa,KAAK,KAAK,CAAC,YAAY;CAC3E,CAAC;AAgDF,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,eAAe,GAAG,IAAI,GAAG,SAAS;;;;;;;;;;;;;;;;SA+BvF;AAUD,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,EACvC,QAAQ,EAAE,cAAc,EACxB,OAAO,EACP,UAAU,EACV,YAAY,GACb,EAAE;IACD,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,OAAO,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACrC,UAAU,EAAE,eAAe,GAAG,IAAI,GAAG,SAAS,CAAC;IAC/C,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;CAC1C,
|
|
1
|
+
{"version":3,"file":"AnimationManager.d.ts","sourceRoot":"","sources":["../../src/web/AnimationManager.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD,KAAK,SAAS,GAAG;IACf,OAAO,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAC;IAC9B,mBAAmB,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAC;IAC1C,OAAO,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAC;IAC9B,OAAO,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,GAAG,EAAE,MAAM;IACX,cAAc,EAAE,CACd,WAAW,EAAE,WAAW,CAAC,SAAS,CAAC,KAChC,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,aAAa,KAAK,KAAK,CAAC,YAAY;CAC3E,CAAC;AAgDF,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,eAAe,GAAG,IAAI,GAAG,SAAS;;;;;;;;;;;;;;;;SA+BvF;AAUD,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,EACvC,QAAQ,EAAE,cAAc,EACxB,OAAO,EACP,UAAU,EACV,YAAY,GACb,EAAE;IACD,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,OAAO,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACrC,UAAU,EAAE,eAAe,GAAG,IAAI,GAAG,SAAS,CAAC;IAC/C,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;CAC1C,eA4GA"}
|
|
@@ -86,9 +86,14 @@ export default function AnimationManager({ children: renderFunction, initial, tr
|
|
|
86
86
|
}
|
|
87
87
|
const existingNodeIndex = n.findIndex((node) => node.animationKey === newNode.animationKey);
|
|
88
88
|
if (existingNodeIndex >= 0) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
if (animation) {
|
|
90
|
+
return n.map((n2) => n2.animationKey === newNode.animationKey
|
|
91
|
+
? { ...newNode, status: 'in' }
|
|
92
|
+
: { ...n2, status: 'out' });
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
return [{ ...newNode, status: 'in' }];
|
|
96
|
+
}
|
|
92
97
|
}
|
|
93
98
|
return [...n, newNode];
|
|
94
99
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnimationManager.js","sourceRoot":"","sources":["../../src/web/AnimationManager.tsx"],"names":[],"mappings":"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","sourcesContent":["import React from 'react';\n\nimport { ImageTransition } from '../Image.types';\n\ntype Callbacks = {\n onReady?: (() => void) | null;\n onAnimationFinished?: (() => void) | null;\n onMount?: (() => void) | null;\n onError?: (() => void) | null;\n};\n\nexport type AnimationManagerNode = [\n key: string,\n renderFunction: (\n renderProps: NonNullable<Callbacks>\n ) => (className: string, style: React.CSSProperties) => React.ReactElement\n];\n\nconst SUPPORTED_ANIMATIONS: ImageTransition['effect'][] = [\n 'cross-dissolve',\n 'flip-from-left',\n 'flip-from-right',\n 'flip-from-top',\n 'flip-from-bottom',\n];\n\ntype NodeStatus = 'mounted' | 'in' | 'active' | 'out' | 'errored';\n\nfunction useAnimationManagerNode(node: AnimationManagerNode | null, initialStatus?: NodeStatus) {\n const newNode = React.useMemo(() => {\n if (!node) {\n return null;\n }\n const [animationKey, renderFunction] = node;\n // key, ReactElement, ref, callbacks\n return {\n animationKey,\n persistedElement: renderFunction,\n status: (initialStatus || 'mounted') as NodeStatus,\n };\n }, [node?.[0]]);\n return newNode;\n}\n\nfunction validateTimingFunctionForAnimation(\n animationClass: ImageTransition['effect'],\n timingFunction: ImageTransition['timing']\n) {\n if (animationClass?.includes('flip')) {\n if (timingFunction?.includes('ease')) {\n return 'ease-in-out';\n }\n return 'linear';\n }\n return timingFunction || null;\n}\n\nfunction validateAnimationClass(effect: ImageTransition['effect']) {\n if (SUPPORTED_ANIMATIONS.includes(effect)) {\n return effect;\n }\n return 'cross-dissolve';\n}\n\nexport function getAnimatorFromTransition(transition: ImageTransition | null | undefined) {\n if (!transition?.duration) {\n return null;\n }\n const animationClass = validateAnimationClass(transition.effect);\n if (!animationClass) {\n return {\n startingClass: '',\n animateInClass: '',\n animateOutClass: '',\n containerClass: '',\n timingFunction: 'linear',\n animationClass: '',\n duration: 0,\n };\n }\n\n const timingFunction = validateTimingFunctionForAnimation(animationClass, transition.timing);\n const timingClass = `image-timing-${timingFunction}`;\n\n return {\n startingClass: `${animationClass}-start`,\n animateInClass: [animationClass, 'transitioning', `${animationClass}-active`, timingClass].join(\n ' '\n ),\n animateOutClass: [animationClass, `${animationClass}-end`, timingClass].join(' '),\n containerClass: `${animationClass}-container`,\n timingFunction,\n animationClass,\n duration: transition?.duration || 0,\n };\n}\n\ntype MountedAnimationNode = {\n animationKey: string;\n persistedElement: (\n renderProps: Callbacks\n ) => (className: string, style: React.CSSProperties) => React.ReactElement;\n status: NodeStatus;\n};\n\nexport default function AnimationManager({\n children: renderFunction,\n initial,\n transition,\n recyclingKey,\n}: {\n children: AnimationManagerNode;\n initial: AnimationManagerNode | null;\n transition: ImageTransition | null | undefined;\n recyclingKey?: string | null | undefined;\n}) {\n const animation = getAnimatorFromTransition(transition);\n\n const initialNode = useAnimationManagerNode(initial, 'active');\n\n const [nodes, setNodes] = React.useState<MountedAnimationNode[]>(\n initialNode ? [initialNode] : []\n );\n\n const [prevRecyclingKey, setPrevRecyclingKey] = React.useState<string>(recyclingKey ?? '');\n if (prevRecyclingKey !== (recyclingKey ?? '')) {\n setPrevRecyclingKey(recyclingKey ?? '');\n setNodes(initialNode ? [initialNode] : []);\n }\n\n const removeAllNodesOfKeyExceptShowing = (key?: string) => {\n setNodes((n) =>\n n.filter(\n (node) =>\n (key ? node.animationKey !== key : false) ||\n node.status === 'in' ||\n node.status === 'active'\n )\n );\n };\n\n const newNode = useAnimationManagerNode(renderFunction);\n\n React.useEffect(() => {\n setNodes((n) => {\n if (!newNode) {\n return n;\n }\n const existingNodeIndex = n.findIndex((node) => node.animationKey === newNode.animationKey);\n if (existingNodeIndex >= 0) {\n const copy = [...n];\n copy.splice(existingNodeIndex, 1, newNode);\n return copy;\n }\n return [...n, newNode];\n });\n }, [newNode]);\n\n function wrapNodeWithCallbacks(node: MountedAnimationNode) {\n if (renderFunction[0] === node.animationKey) {\n return renderFunction[1]({\n onReady: () => {\n if (animation) {\n setNodes((nodes) =>\n nodes.map((n) => (n === newNode ? { ...n, status: 'in' } : { ...n, status: 'out' }))\n );\n } else {\n setNodes([{ ...node, status: 'in' }]);\n }\n },\n onAnimationFinished: () => {\n setNodes([{ ...node, status: 'in' }]);\n },\n onError: () => {\n setNodes((nodes) => nodes.map((n) => (n === node ? { ...n, status: 'errored' } : n)));\n },\n });\n }\n if (initial?.[0] === node.animationKey) {\n return initial[1]({\n onAnimationFinished: () => {\n if (node.status === 'out') {\n removeAllNodesOfKeyExceptShowing(node.animationKey);\n }\n },\n onError: () => {\n setNodes((nodes) => nodes.map((n) => (n === node ? { ...n, status: 'errored' } : n)));\n },\n });\n }\n return node.persistedElement({\n onAnimationFinished: () => {\n removeAllNodesOfKeyExceptShowing(node.animationKey);\n },\n });\n }\n const styles = {\n transitionDuration: `${animation?.duration || 0}ms`,\n transitionTimingFunction: animation?.timingFunction || 'linear',\n };\n const classes = {\n in: animation?.animateInClass,\n out: animation?.animateOutClass,\n mounted: animation?.startingClass,\n };\n\n return (\n <>\n {[...nodes]\n .filter((n) => n.status !== 'errored')\n .map((n) => (\n <div className={animation?.containerClass} key={n.animationKey}>\n {wrapNodeWithCallbacks(n)(classes[n.status], styles)}\n </div>\n ))}\n </>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"AnimationManager.js","sourceRoot":"","sources":["../../src/web/AnimationManager.tsx"],"names":[],"mappings":"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","sourcesContent":["import React from 'react';\n\nimport { ImageTransition } from '../Image.types';\n\ntype Callbacks = {\n onReady?: (() => void) | null;\n onAnimationFinished?: (() => void) | null;\n onMount?: (() => void) | null;\n onError?: (() => void) | null;\n};\n\nexport type AnimationManagerNode = [\n key: string,\n renderFunction: (\n renderProps: NonNullable<Callbacks>\n ) => (className: string, style: React.CSSProperties) => React.ReactElement\n];\n\nconst SUPPORTED_ANIMATIONS: ImageTransition['effect'][] = [\n 'cross-dissolve',\n 'flip-from-left',\n 'flip-from-right',\n 'flip-from-top',\n 'flip-from-bottom',\n];\n\ntype NodeStatus = 'mounted' | 'in' | 'active' | 'out' | 'errored';\n\nfunction useAnimationManagerNode(node: AnimationManagerNode | null, initialStatus?: NodeStatus) {\n const newNode = React.useMemo(() => {\n if (!node) {\n return null;\n }\n const [animationKey, renderFunction] = node;\n // key, ReactElement, ref, callbacks\n return {\n animationKey,\n persistedElement: renderFunction,\n status: (initialStatus || 'mounted') as NodeStatus,\n };\n }, [node?.[0]]);\n return newNode;\n}\n\nfunction validateTimingFunctionForAnimation(\n animationClass: ImageTransition['effect'],\n timingFunction: ImageTransition['timing']\n) {\n if (animationClass?.includes('flip')) {\n if (timingFunction?.includes('ease')) {\n return 'ease-in-out';\n }\n return 'linear';\n }\n return timingFunction || null;\n}\n\nfunction validateAnimationClass(effect: ImageTransition['effect']) {\n if (SUPPORTED_ANIMATIONS.includes(effect)) {\n return effect;\n }\n return 'cross-dissolve';\n}\n\nexport function getAnimatorFromTransition(transition: ImageTransition | null | undefined) {\n if (!transition?.duration) {\n return null;\n }\n const animationClass = validateAnimationClass(transition.effect);\n if (!animationClass) {\n return {\n startingClass: '',\n animateInClass: '',\n animateOutClass: '',\n containerClass: '',\n timingFunction: 'linear',\n animationClass: '',\n duration: 0,\n };\n }\n\n const timingFunction = validateTimingFunctionForAnimation(animationClass, transition.timing);\n const timingClass = `image-timing-${timingFunction}`;\n\n return {\n startingClass: `${animationClass}-start`,\n animateInClass: [animationClass, 'transitioning', `${animationClass}-active`, timingClass].join(\n ' '\n ),\n animateOutClass: [animationClass, `${animationClass}-end`, timingClass].join(' '),\n containerClass: `${animationClass}-container`,\n timingFunction,\n animationClass,\n duration: transition?.duration || 0,\n };\n}\n\ntype MountedAnimationNode = {\n animationKey: string;\n persistedElement: (\n renderProps: Callbacks\n ) => (className: string, style: React.CSSProperties) => React.ReactElement;\n status: NodeStatus;\n};\n\nexport default function AnimationManager({\n children: renderFunction,\n initial,\n transition,\n recyclingKey,\n}: {\n children: AnimationManagerNode;\n initial: AnimationManagerNode | null;\n transition: ImageTransition | null | undefined;\n recyclingKey?: string | null | undefined;\n}) {\n const animation = getAnimatorFromTransition(transition);\n\n const initialNode = useAnimationManagerNode(initial, 'active');\n\n const [nodes, setNodes] = React.useState<MountedAnimationNode[]>(\n initialNode ? [initialNode] : []\n );\n\n const [prevRecyclingKey, setPrevRecyclingKey] = React.useState<string>(recyclingKey ?? '');\n if (prevRecyclingKey !== (recyclingKey ?? '')) {\n setPrevRecyclingKey(recyclingKey ?? '');\n setNodes(initialNode ? [initialNode] : []);\n }\n\n const removeAllNodesOfKeyExceptShowing = (key?: string) => {\n setNodes((n) =>\n n.filter(\n (node) =>\n (key ? node.animationKey !== key : false) ||\n node.status === 'in' ||\n node.status === 'active'\n )\n );\n };\n\n const newNode = useAnimationManagerNode(renderFunction);\n\n React.useEffect(() => {\n setNodes((n) => {\n if (!newNode) {\n return n;\n }\n const existingNodeIndex = n.findIndex((node) => node.animationKey === newNode.animationKey);\n if (existingNodeIndex >= 0) {\n if (animation) {\n return n.map((n2) =>\n n2.animationKey === newNode.animationKey\n ? { ...newNode, status: 'in' }\n : { ...n2, status: 'out' }\n );\n } else {\n return [{ ...newNode, status: 'in' }];\n }\n }\n return [...n, newNode];\n });\n }, [newNode]);\n\n function wrapNodeWithCallbacks(node: MountedAnimationNode) {\n if (renderFunction[0] === node.animationKey) {\n return renderFunction[1]({\n onReady: () => {\n if (animation) {\n setNodes((nodes) =>\n nodes.map((n) => (n === newNode ? { ...n, status: 'in' } : { ...n, status: 'out' }))\n );\n } else {\n setNodes([{ ...node, status: 'in' }]);\n }\n },\n onAnimationFinished: () => {\n setNodes([{ ...node, status: 'in' }]);\n },\n onError: () => {\n setNodes((nodes) => nodes.map((n) => (n === node ? { ...n, status: 'errored' } : n)));\n },\n });\n }\n if (initial?.[0] === node.animationKey) {\n return initial[1]({\n onAnimationFinished: () => {\n if (node.status === 'out') {\n removeAllNodesOfKeyExceptShowing(node.animationKey);\n }\n },\n onError: () => {\n setNodes((nodes) => nodes.map((n) => (n === node ? { ...n, status: 'errored' } : n)));\n },\n });\n }\n return node.persistedElement({\n onAnimationFinished: () => {\n removeAllNodesOfKeyExceptShowing(node.animationKey);\n },\n });\n }\n const styles = {\n transitionDuration: `${animation?.duration || 0}ms`,\n transitionTimingFunction: animation?.timingFunction || 'linear',\n };\n const classes = {\n in: animation?.animateInClass,\n out: animation?.animateOutClass,\n mounted: animation?.startingClass,\n };\n\n return (\n <>\n {[...nodes]\n .filter((n) => n.status !== 'errored')\n .map((n) => (\n <div className={animation?.containerClass} key={n.animationKey}>\n {wrapNodeWithCallbacks(n)(classes[n.status], styles)}\n </div>\n ))}\n </>\n );\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ImageWrapper.d.ts","sourceRoot":"","sources":["../../src/web/ImageWrapper.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,aAAa,EAAE,cAAc,EAA2B,MAAM,OAAO,CAAC;AAEtF,OAAO,EACL,0BAA0B,EAG1B,WAAW,EACZ,MAAM,gBAAgB,CAAC;AAgDxB,QAAA,MAAM,YAAY;;;2BAeS,eAAe,gBAAgB,EAAE,KAAK,CAAC,KAAK,IAAI;;oBAChC,WAAW,GAAG,IAAI;cAAO,IAAI;kCACtC,IAAI;0BACZ,IAAI;;;;;WAKjB,aAAa;;;;
|
|
1
|
+
{"version":3,"file":"ImageWrapper.d.ts","sourceRoot":"","sources":["../../src/web/ImageWrapper.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,aAAa,EAAE,cAAc,EAA2B,MAAM,OAAO,CAAC;AAEtF,OAAO,EACL,0BAA0B,EAG1B,WAAW,EACZ,MAAM,gBAAgB,CAAC;AAgDxB,QAAA,MAAM,YAAY;;;2BAeS,eAAe,gBAAgB,EAAE,KAAK,CAAC,KAAK,IAAI;;oBAChC,WAAW,GAAG,IAAI;cAAO,IAAI;kCACtC,IAAI;0BACZ,IAAI;;;;;WAKjB,aAAa;;;;0CA+DzB,CAAC;AACF,eAAe,YAAY,CAAC"}
|
|
@@ -60,7 +60,15 @@ const ImageWrapper = React.forwardRef(({ source, events, contentPosition, hashPl
|
|
|
60
60
|
// @ts-ignore
|
|
61
61
|
// eslint-disable-next-line react/no-unknown-property
|
|
62
62
|
fetchpriority: getFetchPriorityFromImagePriority(priority || 'normal'), onLoad: (event) => {
|
|
63
|
-
|
|
63
|
+
if (typeof window !== 'undefined') {
|
|
64
|
+
// this ensures the animation will run, since the starting class is applied at least 1 frame before the target class set in the onLoad event callback
|
|
65
|
+
window.requestAnimationFrame(() => {
|
|
66
|
+
events?.onLoad?.forEach((e) => e?.(event));
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
events?.onLoad?.forEach((e) => e?.(event));
|
|
71
|
+
}
|
|
64
72
|
}, onTransitionEnd: () => events?.onTransitionEnd?.forEach((e) => e?.()), onError: () => events?.onError?.forEach((e) => e?.({ source: source || null })) }));
|
|
65
73
|
});
|
|
66
74
|
export default ImageWrapper;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ImageWrapper.js","sourceRoot":"","sources":["../../src/web/ImageWrapper.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"ImageWrapper.js","sourceRoot":"","sources":["../../src/web/ImageWrapper.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { CSSProperties, SyntheticEvent, useEffect, Ref, useMemo } from 'react';\n\nimport {\n ImageContentPositionObject,\n ImageContentPositionValue,\n ImageNativeProps,\n ImageSource,\n} from '../Image.types';\nimport { useBlurhash } from '../utils/blurhash/useBlurhash';\nimport { isBlurhashString, isThumbhashString } from '../utils/resolveSources';\nimport { thumbHashStringToDataURL } from '../utils/thumbhash/thumbhash';\n\nfunction ensureUnit(value: string | number) {\n const trimmedValue = String(value).trim();\n if (trimmedValue.endsWith('%')) {\n return trimmedValue;\n }\n return `${trimmedValue}px`;\n}\n\ntype KeysOfUnion<T> = T extends T ? keyof T : never;\n\nfunction getObjectPositionFromContentPositionObject(\n contentPosition?: ImageContentPositionObject\n): string {\n const resolvedPosition = { ...contentPosition } as Record<\n KeysOfUnion<ImageContentPositionObject>,\n ImageContentPositionValue\n >;\n if (!resolvedPosition) {\n return '50% 50%';\n }\n if (resolvedPosition.top == null && resolvedPosition.bottom == null) {\n resolvedPosition.top = '50%';\n }\n if (resolvedPosition.left == null && resolvedPosition.right == null) {\n resolvedPosition.left = '50%';\n }\n\n return (\n ['top', 'bottom', 'left', 'right']\n .map((key) => {\n if (key in resolvedPosition) {\n return `${key} ${ensureUnit(resolvedPosition[key])}`;\n }\n return '';\n })\n .join(' ') || '50% 50%'\n );\n}\n\nfunction getFetchPriorityFromImagePriority(priority: ImageNativeProps['priority'] = 'normal') {\n return priority && ['low', 'high'].includes(priority) ? priority : 'auto';\n}\n\nconst ImageWrapper = React.forwardRef(\n (\n {\n source,\n events,\n contentPosition,\n hashPlaceholderContentPosition,\n priority,\n style,\n hashPlaceholderStyle,\n className,\n accessibilityLabel,\n }: {\n source?: ImageSource | null;\n events?: {\n onLoad?: (((event: SyntheticEvent<HTMLImageElement, Event>) => void) | undefined | null)[];\n onError?: ((({ source }: { source: ImageSource | null }) => void) | undefined | null)[];\n onTransitionEnd?: ((() => void) | undefined | null)[];\n onMount?: ((() => void) | undefined | null)[];\n };\n contentPosition?: ImageContentPositionObject;\n hashPlaceholderContentPosition?: ImageContentPositionObject;\n priority?: string | null;\n style: CSSProperties;\n hashPlaceholderStyle?: CSSProperties;\n className?: string;\n accessibilityLabel?: string;\n },\n ref: Ref<HTMLImageElement>\n ) => {\n useEffect(() => {\n events?.onMount?.forEach((e) => e?.());\n }, []);\n const isBlurhash = isBlurhashString(source?.uri || '');\n const isThumbhash = isThumbhashString(source?.uri || '');\n const isHash = isBlurhash || isThumbhash;\n\n // Thumbhash uri always has to start with 'thumbhash:/'\n const thumbhash = source?.uri?.replace(/thumbhash:\\//, '');\n const thumbhashUri = useMemo(\n () => (isThumbhash ? thumbHashStringToDataURL(thumbhash ?? '') : null),\n [thumbhash]\n );\n\n const blurhashUri = useBlurhash(isBlurhash ? source?.uri : null, source?.width, source?.height);\n const objectPosition = getObjectPositionFromContentPositionObject(\n isHash ? hashPlaceholderContentPosition : contentPosition\n );\n\n const uri = isHash ? blurhashUri ?? thumbhashUri : source?.uri;\n if (!uri) return null;\n return (\n <img\n ref={ref}\n alt={accessibilityLabel}\n className={className}\n src={uri || undefined}\n key={source?.uri}\n style={{\n width: '100%',\n height: '100%',\n position: 'absolute',\n left: 0,\n right: 0,\n objectPosition,\n ...style,\n ...(isHash ? hashPlaceholderStyle : {}),\n }}\n // @ts-ignore\n // eslint-disable-next-line react/no-unknown-property\n fetchpriority={getFetchPriorityFromImagePriority(priority || 'normal')}\n onLoad={(event) => {\n if (typeof window !== 'undefined') {\n // this ensures the animation will run, since the starting class is applied at least 1 frame before the target class set in the onLoad event callback\n window.requestAnimationFrame(() => {\n events?.onLoad?.forEach((e) => e?.(event));\n });\n } else {\n events?.onLoad?.forEach((e) => e?.(event));\n }\n }}\n onTransitionEnd={() => events?.onTransitionEnd?.forEach((e) => e?.())}\n onError={() => events?.onError?.forEach((e) => e?.({ source: source || null }))}\n />\n );\n }\n);\nexport default ImageWrapper;\n"]}
|
|
@@ -78,7 +78,8 @@ private func assetLocalIdentifier(fromUrl url: URL) -> String? {
|
|
|
78
78
|
*/
|
|
79
79
|
private func isPhotoLibraryStatusAuthorized() -> Bool {
|
|
80
80
|
if #available(iOS 14, *) {
|
|
81
|
-
|
|
81
|
+
let status = PHPhotoLibrary.authorizationStatus(for: .readWrite)
|
|
82
|
+
return status == .authorized || status == .limited
|
|
82
83
|
} else {
|
|
83
84
|
return PHPhotoLibrary.authorizationStatus() == .authorized
|
|
84
85
|
}
|
|
@@ -21,9 +21,10 @@ class ThumbhashLoader: NSObject, SDImageLoader {
|
|
|
21
21
|
return nil
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
// The URI looks like this: thumbhash:/3OcRJYB4d3h
|
|
25
|
-
//
|
|
26
|
-
|
|
24
|
+
// The URI looks like this: thumbhash:/3OcRJYB4d3h\iIeHeEh3eIhw+j2w
|
|
25
|
+
// ThumbHash may include slashes which could break the structure of the URL, so we replace them
|
|
26
|
+
// with backslashes on the JS side and revert them back to slashes here, before generating the image.
|
|
27
|
+
var thumbhash = (url.pathComponents[1] ?? "").replacingOccurrences(of: "\\", with: "/")
|
|
27
28
|
|
|
28
29
|
// Thumbhashes with transparency cause the conversion to data to fail, padding the thumbhash string to correct length fixes that
|
|
29
30
|
let remainder = thumbhash.count % 4
|
|
@@ -31,7 +32,7 @@ class ThumbhashLoader: NSObject, SDImageLoader {
|
|
|
31
32
|
thumbhash = thumbhash.padding(toLength: thumbhash.count + 4 - remainder, withPad: "=", startingAt: 0)
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
guard let thumbhashData = Data(base64Encoded: thumbhash, options: .ignoreUnknownCharacters) else {
|
|
35
|
+
guard !thumbhash.isEmpty, let thumbhashData = Data(base64Encoded: thumbhash, options: .ignoreUnknownCharacters) else {
|
|
35
36
|
let error = makeNSError(description: "URL provided to ThumbhashLoader is invalid")
|
|
36
37
|
completedBlock?(nil, nil, error, false)
|
|
37
38
|
return nil
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-image",
|
|
3
3
|
"title": "Expo Image",
|
|
4
|
-
"version": "1.2.
|
|
4
|
+
"version": "1.2.2",
|
|
5
5
|
"description": "A cross-platform, performant image component for React Native and Expo with Web support",
|
|
6
6
|
"main": "build/index.js",
|
|
7
7
|
"types": "build/index.d.ts",
|
|
@@ -33,5 +33,5 @@
|
|
|
33
33
|
"peerDependencies": {
|
|
34
34
|
"expo": "*"
|
|
35
35
|
},
|
|
36
|
-
"gitHead": "
|
|
36
|
+
"gitHead": "e7ef246d8e7c274cc6ce67eee4d0d764bd3f042f"
|
|
37
37
|
}
|
|
@@ -27,7 +27,9 @@ export function resolveBlurhashString(str: string): ImageSource {
|
|
|
27
27
|
* @return An ImageSource representing the provided thumbhash.
|
|
28
28
|
* */
|
|
29
29
|
export function resolveThumbhashString(str: string): ImageSource {
|
|
30
|
-
|
|
30
|
+
// ThumbHash may contain slashes that could break the url when the slash is at the beginning.
|
|
31
|
+
// We replace slashes with backslashes to make sure we don't break the url's path.
|
|
32
|
+
const thumbhash = str.replace(/^thumbhash:\//, '').replace(/\//g, '\\');
|
|
31
33
|
return {
|
|
32
34
|
uri: hashToUri('thumbhash', thumbhash),
|
|
33
35
|
};
|
|
@@ -148,9 +148,15 @@ export default function AnimationManager({
|
|
|
148
148
|
}
|
|
149
149
|
const existingNodeIndex = n.findIndex((node) => node.animationKey === newNode.animationKey);
|
|
150
150
|
if (existingNodeIndex >= 0) {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
151
|
+
if (animation) {
|
|
152
|
+
return n.map((n2) =>
|
|
153
|
+
n2.animationKey === newNode.animationKey
|
|
154
|
+
? { ...newNode, status: 'in' }
|
|
155
|
+
: { ...n2, status: 'out' }
|
|
156
|
+
);
|
|
157
|
+
} else {
|
|
158
|
+
return [{ ...newNode, status: 'in' }];
|
|
159
|
+
}
|
|
154
160
|
}
|
|
155
161
|
return [...n, newNode];
|
|
156
162
|
});
|
package/src/web/ImageWrapper.tsx
CHANGED
|
@@ -125,7 +125,14 @@ const ImageWrapper = React.forwardRef(
|
|
|
125
125
|
// eslint-disable-next-line react/no-unknown-property
|
|
126
126
|
fetchpriority={getFetchPriorityFromImagePriority(priority || 'normal')}
|
|
127
127
|
onLoad={(event) => {
|
|
128
|
-
|
|
128
|
+
if (typeof window !== 'undefined') {
|
|
129
|
+
// this ensures the animation will run, since the starting class is applied at least 1 frame before the target class set in the onLoad event callback
|
|
130
|
+
window.requestAnimationFrame(() => {
|
|
131
|
+
events?.onLoad?.forEach((e) => e?.(event));
|
|
132
|
+
});
|
|
133
|
+
} else {
|
|
134
|
+
events?.onLoad?.forEach((e) => e?.(event));
|
|
135
|
+
}
|
|
129
136
|
}}
|
|
130
137
|
onTransitionEnd={() => events?.onTransitionEnd?.forEach((e) => e?.())}
|
|
131
138
|
onError={() => events?.onError?.forEach((e) => e?.({ source: source || null }))}
|