`
261 |
262 | Clear all images from disk cache.
263 |
264 | ## Troubleshooting
265 |
266 | If you have any problems using this library try the steps in [troubleshooting](docs/troubleshooting.md) and see if they fix it.
267 |
268 | ## Development
269 |
270 | [Follow these instructions to get the example app running.](docs/development.md)
271 |
272 | ## Supported React Native Versions
273 |
274 | This project only aims to support the latest version of React Native.\
275 | This simplifies the development and the testing of the project.
276 |
277 | If you require new features or bug fixes for older versions you can fork this project.
278 |
279 | ## Credits
280 |
281 | The idea for this modules came from
282 | [vovkasm's](https://github.com/vovkasm)
283 | [react-native-web-image](https://github.com/vovkasm/react-native-web-image)
284 | package.
285 | It also uses Glide and SDWebImage, but didn't have some features I needed (priority, headers).
286 |
287 | Thanks to [@mobinni](https://github.com/mobinni) for helping with the conceptualization
288 |
289 | ## Licenses
290 |
291 | - FastImage - MIT © [DylanVann](https://github.com/DylanVann)
292 | - SDWebImage - `MIT`
293 | - Glide - BSD, part MIT and Apache 2.0. See the [LICENSE](https://github.com/bumptech/glide/blob/master/license) file for details.
294 |
295 | [build-badge]: https://github.com/dylanvann/react-native-fast-image/workflows/CI/badge.svg
296 | [build]: https://github.com/DylanVann/react-native-fast-image/actions?query=workflow%3ACI
297 | [coverage-badge]: https://img.shields.io/codecov/c/github/dylanvann/react-native-fast-image.svg
298 | [coverage]: https://codecov.io/github/dylanvann/react-native-fast-image
299 | [downloads-badge]: https://img.shields.io/npm/dm/react-native-fast-image.svg
300 | [npmtrends]: http://www.npmtrends.com/react-native-fast-image
301 | [package]: https://www.npmjs.com/package/react-native-fast-image
302 | [version-badge]: https://img.shields.io/npm/v/react-native-fast-image.svg
303 | [twitter]: https://twitter.com/home?status=Check%20out%20react-native-fast-image%20by%20%40atomarranger%20https%3A//github.com/DylanVann/react-native-fast-image
304 | [twitter-badge]: https://img.shields.io/twitter/url/https/github.com/DylanVann/react-native-fast-image.svg?style=social
305 | [github-watch-badge]: https://img.shields.io/github/watchers/dylanvann/react-native-fast-image.svg?style=social
306 | [github-watch]: https://github.com/dylanvann/react-native-fast-image/watchers
307 | [github-star-badge]: https://img.shields.io/github/stars/dylanvann/react-native-fast-image.svg?style=social
308 | [github-star]: https://github.com/dylanvann/react-native-fast-image/stargazers
309 |
--------------------------------------------------------------------------------
/RNFastImage.podspec:
--------------------------------------------------------------------------------
1 | require 'json'
2 |
3 | Pod::Spec.new do |s|
4 | package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
5 |
6 | s.name = "RNFastImage"
7 | s.version = package['version']
8 | s.summary = package['description']
9 | s.authors = { "Dylan Vann" => "dylan@dylanvann.com" }
10 | s.homepage = "https://github.com/DylanVann/react-native-fast-image#readme"
11 | s.license = "MIT"
12 | s.platforms = { :ios => "8.0", :tvos => "9.0" }
13 | s.framework = 'UIKit'
14 | s.requires_arc = true
15 | s.source = { :git => "https://github.com/DylanVann/react-native-fast-image.git", :tag => "v#{s.version}" }
16 | s.source_files = "ios/**/*.{h,m}"
17 |
18 | s.dependency 'React-Core'
19 | s.dependency 'SDWebImage', '~> 5.11.1'
20 | s.dependency 'SDWebImageWebPCoder', '~> 0.8.4'
21 | s.dependency 'SDWebImagePhotosPlugin'
22 | end
23 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/.buckconfig:
--------------------------------------------------------------------------------
1 |
2 | [android]
3 | target = Google Inc.:Google APIs:23
4 |
5 | [maven_repositories]
6 | central = https://repo1.maven.org/maven2
7 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/.editorconfig:
--------------------------------------------------------------------------------
1 | # Windows files
2 | [*.bat]
3 | end_of_line = crlf
4 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | extends: '@react-native-community',
4 | rules: {
5 | semi: ['error', 'never'],
6 | 'react-native/no-inline-styles': 'off',
7 | },
8 | }
9 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/.gitattributes:
--------------------------------------------------------------------------------
1 | # Windows files should use crlf line endings
2 | # https://help.github.com/articles/dealing-with-line-endings/
3 | *.bat text eol=crlf
4 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Xcode
6 | #
7 | build/
8 | *.pbxuser
9 | !default.pbxuser
10 | *.mode1v3
11 | !default.mode1v3
12 | *.mode2v3
13 | !default.mode2v3
14 | *.perspectivev3
15 | !default.perspectivev3
16 | xcuserdata
17 | *.xccheckout
18 | *.moved-aside
19 | DerivedData
20 | *.hmap
21 | *.ipa
22 | *.xcuserstate
23 |
24 | # Android/IntelliJ
25 | #
26 | build/
27 | .idea
28 | .gradle
29 | local.properties
30 | *.iml
31 | *.hprof
32 |
33 | # node.js
34 | #
35 | node_modules/
36 | npm-debug.log
37 | yarn-error.log
38 |
39 | # BUCK
40 | buck-out/
41 | \.buckd/
42 | *.keystore
43 | !debug.keystore
44 |
45 | # fastlane
46 | #
47 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
48 | # screenshots whenever they are needed.
49 | # For more information about the recommended setup visit:
50 | # https://docs.fastlane.tools/best-practices/source-control/
51 |
52 | */fastlane/report.xml
53 | */fastlane/Preview.html
54 | */fastlane/screenshots
55 |
56 | # Bundle artifact
57 | *.jsbundle
58 |
59 | # CocoaPods
60 | /ios/Pods/
61 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | semi: false,
3 | singleQuote: true,
4 | trailingComma: 'all',
5 | tabWidth: 4,
6 | }
7 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/__tests__/App-test.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * @format
3 | */
4 |
5 | import 'react-native'
6 | import React from 'react'
7 | import App from '../src'
8 |
9 | // Note: test renderer must be required after react-native.
10 | import renderer from 'react-test-renderer'
11 |
12 | it('renders correctly', () => {
13 | renderer.create()
14 | })
15 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/android/app/_BUCK:
--------------------------------------------------------------------------------
1 | # To learn about Buck see [Docs](https://buckbuild.com/).
2 | # To run your application with Buck:
3 | # - install Buck
4 | # - `npm start` - to start the packager
5 | # - `cd android`
6 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
7 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
8 | # - `buck install -r android/app` - compile, install and run application
9 | #
10 |
11 | load(":build_defs.bzl", "create_aar_targets", "create_jar_targets")
12 |
13 | lib_deps = []
14 |
15 | create_aar_targets(glob(["libs/*.aar"]))
16 |
17 | create_jar_targets(glob(["libs/*.jar"]))
18 |
19 | android_library(
20 | name = "all-libs",
21 | exported_deps = lib_deps,
22 | )
23 |
24 | android_library(
25 | name = "app-code",
26 | srcs = glob([
27 | "src/main/java/**/*.java",
28 | ]),
29 | deps = [
30 | ":all-libs",
31 | ":build_config",
32 | ":res",
33 | ],
34 | )
35 |
36 | android_build_config(
37 | name = "build_config",
38 | package = "com.reactnativefastimageexample",
39 | )
40 |
41 | android_resource(
42 | name = "res",
43 | package = "com.reactnativefastimageexample",
44 | res = "src/main/res",
45 | )
46 |
47 | android_binary(
48 | name = "app",
49 | keystore = "//android/keystores:debug",
50 | manifest = "src/main/AndroidManifest.xml",
51 | package_type = "debug",
52 | deps = [
53 | ":app-code",
54 | ],
55 | )
56 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: "com.android.application"
2 |
3 | import com.android.build.OutputFile
4 |
5 | /**
6 | * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
7 | * and bundleReleaseJsAndAssets).
8 | * These basically call `react-native bundle` with the correct arguments during the Android build
9 | * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
10 | * bundle directly from the development server. Below you can see all the possible configurations
11 | * and their defaults. If you decide to add a configuration block, make sure to add it before the
12 | * `apply from: "../../node_modules/react-native/react.gradle"` line.
13 | *
14 | * project.ext.react = [
15 | * // the name of the generated asset file containing your JS bundle
16 | * bundleAssetName: "index.android.bundle",
17 | *
18 | * // the entry file for bundle generation. If none specified and
19 | * // "index.android.js" exists, it will be used. Otherwise "index.js" is
20 | * // default. Can be overridden with ENTRY_FILE environment variable.
21 | * entryFile: "index.android.js",
22 | *
23 | * // https://reactnative.dev/docs/performance#enable-the-ram-format
24 | * bundleCommand: "ram-bundle",
25 | *
26 | * // whether to bundle JS and assets in debug mode
27 | * bundleInDebug: false,
28 | *
29 | * // whether to bundle JS and assets in release mode
30 | * bundleInRelease: true,
31 | *
32 | * // whether to bundle JS and assets in another build variant (if configured).
33 | * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
34 | * // The configuration property can be in the following formats
35 | * // 'bundleIn${productFlavor}${buildType}'
36 | * // 'bundleIn${buildType}'
37 | * // bundleInFreeDebug: true,
38 | * // bundleInPaidRelease: true,
39 | * // bundleInBeta: true,
40 | *
41 | * // whether to disable dev mode in custom build variants (by default only disabled in release)
42 | * // for example: to disable dev mode in the staging build type (if configured)
43 | * devDisabledInStaging: true,
44 | * // The configuration property can be in the following formats
45 | * // 'devDisabledIn${productFlavor}${buildType}'
46 | * // 'devDisabledIn${buildType}'
47 | *
48 | * // the root of your project, i.e. where "package.json" lives
49 | * root: "../../",
50 | *
51 | * // where to put the JS bundle asset in debug mode
52 | * jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
53 | *
54 | * // where to put the JS bundle asset in release mode
55 | * jsBundleDirRelease: "$buildDir/intermediates/assets/release",
56 | *
57 | * // where to put drawable resources / React Native assets, e.g. the ones you use via
58 | * // require('./image.png')), in debug mode
59 | * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
60 | *
61 | * // where to put drawable resources / React Native assets, e.g. the ones you use via
62 | * // require('./image.png')), in release mode
63 | * resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
64 | *
65 | * // by default the gradle tasks are skipped if none of the JS files or assets change; this means
66 | * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
67 | * // date; if you have any other folders that you want to ignore for performance reasons (gradle
68 | * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
69 | * // for example, you might want to remove it from here.
70 | * inputExcludes: ["android/**", "ios/**"],
71 | *
72 | * // override which node gets called and with what additional arguments
73 | * nodeExecutableAndArgs: ["node"],
74 | *
75 | * // supply additional arguments to the packager
76 | * extraPackagerArgs: []
77 | * ]
78 | */
79 |
80 | project.ext.react = [
81 | enableHermes: true, // clean and rebuild if changing
82 | ]
83 |
84 | apply from: "../../node_modules/react-native/react.gradle"
85 |
86 | /**
87 | * Set this to true to create two separate APKs instead of one:
88 | * - An APK that only works on ARM devices
89 | * - An APK that only works on x86 devices
90 | * The advantage is the size of the APK is reduced by about 4MB.
91 | * Upload all the APKs to the Play Store and people will download
92 | * the correct one based on the CPU architecture of their device.
93 | */
94 | def enableSeparateBuildPerCPUArchitecture = false
95 |
96 | /**
97 | * Run Proguard to shrink the Java bytecode in release builds.
98 | */
99 | def enableProguardInReleaseBuilds = false
100 |
101 | /**
102 | * The preferred build flavor of JavaScriptCore.
103 | *
104 | * For example, to use the international variant, you can use:
105 | * `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
106 | *
107 | * The international variant includes ICU i18n library and necessary data
108 | * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
109 | * give correct results when using with locales other than en-US. Note that
110 | * this variant is about 6MiB larger per architecture than default.
111 | */
112 | def jscFlavor = 'org.webkit:android-jsc:+'
113 |
114 | /**
115 | * Whether to enable the Hermes VM.
116 | *
117 | * This should be set on project.ext.react and mirrored here. If it is not set
118 | * on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
119 | * and the benefits of using Hermes will therefore be sharply reduced.
120 | */
121 | def enableHermes = project.ext.react.get("enableHermes", false);
122 |
123 | /**
124 | * Architectures to build native code for in debug.
125 | */
126 | def nativeArchitectures = project.getProperties().get("reactNativeDebugArchitectures")
127 |
128 |
129 | android {
130 | ndkVersion rootProject.ext.ndkVersion
131 |
132 | compileSdkVersion rootProject.ext.compileSdkVersion
133 |
134 | defaultConfig {
135 | applicationId "com.reactnativefastimageexample"
136 | minSdkVersion rootProject.ext.minSdkVersion
137 | targetSdkVersion rootProject.ext.targetSdkVersion
138 | versionCode 1
139 | versionName "1.0"
140 | }
141 | splits {
142 | abi {
143 | reset()
144 | enable enableSeparateBuildPerCPUArchitecture
145 | universalApk false // If true, also generate a universal APK
146 | include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
147 | }
148 | }
149 | signingConfigs {
150 | debug {
151 | storeFile file('debug.keystore')
152 | storePassword 'android'
153 | keyAlias 'androiddebugkey'
154 | keyPassword 'android'
155 | }
156 | }
157 | buildTypes {
158 | debug {
159 | signingConfig signingConfigs.debug
160 | if (nativeArchitectures) {
161 | ndk {
162 | abiFilters nativeArchitectures.split(',')
163 | }
164 | }
165 | }
166 | release {
167 | // Caution! In production, you need to generate your own keystore file.
168 | // see https://reactnative.dev/docs/signed-apk-android.
169 | signingConfig signingConfigs.debug
170 | minifyEnabled enableProguardInReleaseBuilds
171 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
172 | }
173 | }
174 |
175 | // applicationVariants are e.g. debug, release
176 | applicationVariants.all { variant ->
177 | variant.outputs.each { output ->
178 | // For each separate APK per architecture, set a unique version code as described here:
179 | // https://developer.android.com/studio/build/configure-apk-splits.html
180 | // Example: versionCode 1 will generate 1001 for armeabi-v7a, 1002 for x86, etc.
181 | def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
182 | def abi = output.getFilter(OutputFile.ABI)
183 | if (abi != null) { // null for the universal-debug, universal-release variants
184 | output.versionCodeOverride =
185 | defaultConfig.versionCode * 1000 + versionCodes.get(abi)
186 | }
187 |
188 | }
189 | }
190 | }
191 |
192 | dependencies {
193 | implementation fileTree(dir: "libs", include: ["*.jar"])
194 | //noinspection GradleDynamicVersion
195 | implementation "com.facebook.react:react-native:+" // From node_modules
196 |
197 | implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
198 |
199 | debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
200 | exclude group:'com.facebook.fbjni'
201 | }
202 |
203 | debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
204 | exclude group:'com.facebook.flipper'
205 | exclude group:'com.squareup.okhttp3', module:'okhttp'
206 | }
207 |
208 | debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
209 | exclude group:'com.facebook.flipper'
210 | }
211 |
212 | if (enableHermes) {
213 | def hermesPath = "../../node_modules/hermes-engine/android/";
214 | debugImplementation files(hermesPath + "hermes-debug.aar")
215 | releaseImplementation files(hermesPath + "hermes-release.aar")
216 | } else {
217 | implementation jscFlavor
218 | }
219 |
220 | implementation project(':rnfastimage')
221 | }
222 |
223 | // Run this once to be able to run the application with BUCK
224 | // puts all compile dependencies into folder libs for BUCK to use
225 | task copyDownloadableDepsToLibs(type: Copy) {
226 | from configurations.implementation
227 | into 'libs'
228 | }
229 |
230 | apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
231 | project.ext.vectoricons = [
232 | iconFontNames: [ 'Ionicons.ttf'] // Name of the font files you want to copy
233 | ]
234 |
235 | apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"
236 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/android/app/build_defs.bzl:
--------------------------------------------------------------------------------
1 | """Helper definitions to glob .aar and .jar targets"""
2 |
3 | def create_aar_targets(aarfiles):
4 | for aarfile in aarfiles:
5 | name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")]
6 | lib_deps.append(":" + name)
7 | android_prebuilt_aar(
8 | name = name,
9 | aar = aarfile,
10 | )
11 |
12 | def create_jar_targets(jarfiles):
13 | for jarfile in jarfiles:
14 | name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")]
15 | lib_deps.append(":" + name)
16 | prebuilt_jar(
17 | name = name,
18 | binary_jar = jarfile,
19 | )
20 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/android/app/debug.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuvent/react-native-fast-image/a661be6bb65029b3f1750fed9b2f793949df6152/ReactNativeFastImageExample/android/app/debug.keystore
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/android/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/android/app/src/debug/java/com/reactnativefastimageexample/ReactNativeFlipper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Facebook, Inc. and its affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the LICENSE file in the root
5 | * directory of this source tree.
6 | */
7 | package com.reactnativefastimageexample;
8 |
9 | import android.content.Context;
10 | import com.facebook.flipper.android.AndroidFlipperClient;
11 | import com.facebook.flipper.android.utils.FlipperUtils;
12 | import com.facebook.flipper.core.FlipperClient;
13 | import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin;
14 | import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
15 | import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
16 | import com.facebook.flipper.plugins.inspector.DescriptorMapping;
17 | import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
18 | import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
19 | import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
20 | import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
21 | import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
22 | import com.facebook.react.ReactInstanceManager;
23 | import com.facebook.react.bridge.ReactContext;
24 | import com.facebook.react.modules.network.NetworkingModule;
25 | import okhttp3.OkHttpClient;
26 |
27 | public class ReactNativeFlipper {
28 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
29 | if (FlipperUtils.shouldEnableFlipper(context)) {
30 | final FlipperClient client = AndroidFlipperClient.getInstance(context);
31 |
32 | client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
33 | client.addPlugin(new ReactFlipperPlugin());
34 | client.addPlugin(new DatabasesFlipperPlugin(context));
35 | client.addPlugin(new SharedPreferencesFlipperPlugin(context));
36 | client.addPlugin(CrashReporterPlugin.getInstance());
37 |
38 | NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
39 | NetworkingModule.setCustomClientBuilder(
40 | new NetworkingModule.CustomClientBuilder() {
41 | @Override
42 | public void apply(OkHttpClient.Builder builder) {
43 | builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
44 | }
45 | });
46 | client.addPlugin(networkFlipperPlugin);
47 | client.start();
48 |
49 | // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
50 | // Hence we run if after all native modules have been initialized
51 | ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
52 | if (reactContext == null) {
53 | reactInstanceManager.addReactInstanceEventListener(
54 | new ReactInstanceManager.ReactInstanceEventListener() {
55 | @Override
56 | public void onReactContextInitialized(ReactContext reactContext) {
57 | reactInstanceManager.removeReactInstanceEventListener(this);
58 | reactContext.runOnNativeModulesQueueThread(
59 | new Runnable() {
60 | @Override
61 | public void run() {
62 | client.addPlugin(new FrescoFlipperPlugin());
63 | }
64 | });
65 | }
66 | });
67 | } else {
68 | client.addPlugin(new FrescoFlipperPlugin());
69 | }
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
13 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/android/app/src/main/java/com/reactnativefastimageexample/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.reactnativefastimageexample;
2 |
3 | import com.facebook.react.ReactActivity;
4 |
5 | public class MainActivity extends ReactActivity {
6 |
7 | /**
8 | * Returns the name of the main component registered from JavaScript. This is used to schedule
9 | * rendering of the component.
10 | */
11 | @Override
12 | protected String getMainComponentName() {
13 | return "ReactNativeFastImageExample";
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/android/app/src/main/java/com/reactnativefastimageexample/MainApplication.java:
--------------------------------------------------------------------------------
1 | package com.reactnativefastimageexample;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 |
6 | import com.dylanvann.fastimage.FastImageViewPackage;
7 | import com.dylanvann.fastimage.custom.persistence.ObjectBox;
8 | import com.facebook.react.PackageList;
9 | import com.facebook.react.ReactApplication;
10 | import com.facebook.react.ReactInstanceManager;
11 | import com.facebook.react.ReactNativeHost;
12 | import com.facebook.react.ReactPackage;
13 | import com.facebook.react.bridge.JSIModulePackage;
14 | import com.facebook.soloader.SoLoader;
15 | import com.swmansion.reanimated.ReanimatedJSIModulePackage;
16 |
17 | import java.lang.reflect.InvocationTargetException;
18 | import java.util.List;
19 |
20 | public class MainApplication extends Application implements ReactApplication {
21 |
22 | private final ReactNativeHost mReactNativeHost =
23 | new ReactNativeHost(this) {
24 | @Override
25 | public boolean getUseDeveloperSupport() {
26 | return BuildConfig.DEBUG;
27 | }
28 |
29 | @Override
30 | protected List getPackages() {
31 | @SuppressWarnings("UnnecessaryLocalVariable")
32 | List packages = new PackageList(this).getPackages();
33 | // Packages that cannot be autolinked yet can be added manually here, for example:
34 | // packages.add(new MyReactNativePackage());
35 | packages.add(new FastImageViewPackage());
36 | return packages;
37 | }
38 |
39 | @Override
40 | protected String getJSMainModuleName() {
41 | return "index";
42 | }
43 |
44 | @Override
45 | protected JSIModulePackage getJSIModulePackage() {
46 | return new ReanimatedJSIModulePackage();
47 | }
48 | };
49 |
50 | @Override
51 | public ReactNativeHost getReactNativeHost() {
52 | return mReactNativeHost;
53 | }
54 |
55 | @Override
56 | public void onCreate() {
57 | super.onCreate();
58 | SoLoader.init(this, /* native exopackage */ false);
59 | initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
60 | ObjectBox.init(this);
61 | }
62 |
63 | /**
64 | * Loads Flipper in React Native templates. Call this in the onCreate method with something like
65 | * initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
66 | *
67 | * @param context
68 | * @param reactInstanceManager
69 | */
70 | private static void initializeFlipper(
71 | Context context, ReactInstanceManager reactInstanceManager) {
72 | if (BuildConfig.DEBUG) {
73 | try {
74 | /*
75 | We use reflection here to pick up the class that initializes Flipper,
76 | since Flipper library is not available in release mode
77 | */
78 | Class> aClass = Class.forName("com.reactnativefastimageexample.ReactNativeFlipper");
79 | aClass
80 | .getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
81 | .invoke(null, context, reactInstanceManager);
82 | } catch (ClassNotFoundException e) {
83 | e.printStackTrace();
84 | } catch (NoSuchMethodException e) {
85 | e.printStackTrace();
86 | } catch (IllegalAccessException e) {
87 | e.printStackTrace();
88 | } catch (InvocationTargetException e) {
89 | e.printStackTrace();
90 | }
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuvent/react-native-fast-image/a661be6bb65029b3f1750fed9b2f793949df6152/ReactNativeFastImageExample/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuvent/react-native-fast-image/a661be6bb65029b3f1750fed9b2f793949df6152/ReactNativeFastImageExample/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuvent/react-native-fast-image/a661be6bb65029b3f1750fed9b2f793949df6152/ReactNativeFastImageExample/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuvent/react-native-fast-image/a661be6bb65029b3f1750fed9b2f793949df6152/ReactNativeFastImageExample/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuvent/react-native-fast-image/a661be6bb65029b3f1750fed9b2f793949df6152/ReactNativeFastImageExample/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuvent/react-native-fast-image/a661be6bb65029b3f1750fed9b2f793949df6152/ReactNativeFastImageExample/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuvent/react-native-fast-image/a661be6bb65029b3f1750fed9b2f793949df6152/ReactNativeFastImageExample/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuvent/react-native-fast-image/a661be6bb65029b3f1750fed9b2f793949df6152/ReactNativeFastImageExample/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuvent/react-native-fast-image/a661be6bb65029b3f1750fed9b2f793949df6152/ReactNativeFastImageExample/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuvent/react-native-fast-image/a661be6bb65029b3f1750fed9b2f793949df6152/ReactNativeFastImageExample/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | ReactNativeFastImageExample
3 |
4 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/android/app/src/main/res/xml/network_security_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | s3.nl-ams.scw.cloud
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/android/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | ext {
5 | buildToolsVersion = "30.0.2"
6 | minSdkVersion = 21
7 | compileSdkVersion = 30
8 | targetSdkVersion = 30
9 | ndkVersion = "21.4.7075529"
10 | }
11 | repositories {
12 | google()
13 | mavenCentral()
14 | }
15 | dependencies {
16 | classpath("com.android.tools.build:gradle:4.2.2")
17 | // NOTE: Do not place your application dependencies here; they belong
18 | // in the individual module build.gradle files
19 | }
20 | }
21 |
22 | allprojects {
23 | repositories {
24 | maven {
25 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
26 | url("$rootDir/../node_modules/react-native/android")
27 | }
28 | maven {
29 | // Android JSC is installed from npm
30 | url("$rootDir/../node_modules/jsc-android/dist")
31 | }
32 | mavenCentral {
33 | // We don't want to fetch react-native from Maven Central as there are
34 | // older versions over there.
35 | content {
36 | excludeGroup "com.facebook.react"
37 | }
38 | }
39 |
40 | google()
41 | maven { url 'https://www.jitpack.io' }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/android/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 |
20 | # AndroidX package structure to make it clearer which packages are bundled with the
21 | # Android operating system, and which are packaged with your app's APK
22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
23 | android.useAndroidX=true
24 | # Automatically convert third-party libraries to use AndroidX
25 | android.enableJetifier=true
26 |
27 | # Version of flipper SDK to use with React Native
28 | FLIPPER_VERSION=0.93.0
29 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuvent/react-native-fast-image/a661be6bb65029b3f1750fed9b2f793949df6152/ReactNativeFastImageExample/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-all.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'ReactNativeFastImageExample'
2 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
3 | include ':app'
4 |
5 | include ':rnfastimage'
6 | project(':rnfastimage').projectDir = new File(rootProject.projectDir, '../../android')
7 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ReactNativeFastImageExample",
3 | "displayName": "ReactNativeFastImageExample"
4 | }
5 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/babel.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const pak = require('../package.json')
3 |
4 | module.exports = {
5 | presets: ['module:metro-react-native-babel-preset'],
6 | plugins: [
7 | [
8 | 'module-resolver',
9 | {
10 | extensions: ['.tsx', '.ts', '.js', '.json'],
11 | alias: {
12 | [pak.name]: path.join(__dirname, '..', pak.source),
13 | },
14 | },
15 | ],
16 | ],
17 | }
18 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @format
3 | */
4 |
5 | import { AppRegistry } from 'react-native'
6 | import App from './src'
7 | import { name as appName } from './app.json'
8 |
9 | AppRegistry.registerComponent(appName, () => App)
10 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/ios/Podfile:
--------------------------------------------------------------------------------
1 | require_relative '../node_modules/react-native/scripts/react_native_pods'
2 | require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
3 |
4 | platform :ios, '11.0'
5 |
6 | target 'ReactNativeFastImageExample' do
7 | config = use_native_modules!
8 |
9 | use_react_native!(
10 | :path => config[:reactNativePath],
11 | # to enable hermes on iOS, change `false` to `true` and then install pods
12 | :hermes_enabled => false
13 | )
14 |
15 | pod 'RNFastImage', :path => '../..'
16 |
17 | target 'ReactNativeFastImageExampleTests' do
18 | inherit! :complete
19 | # Pods for testing
20 | end
21 |
22 | # Enables Flipper.
23 | #
24 | # Note that if you have use_frameworks! enabled, Flipper will not work and
25 | # you should disable the next line.
26 | use_flipper!()
27 |
28 | post_install do |installer|
29 | react_native_post_install(installer)
30 | __apply_Xcode_12_5_M1_post_install_workaround(installer)
31 | end
32 | end
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/ios/ReactNativeFastImageExample.xcodeproj/xcshareddata/xcschemes/ReactNativeFastImageExample.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
53 |
55 |
61 |
62 |
63 |
64 |
70 |
72 |
78 |
79 |
80 |
81 |
83 |
84 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/ios/ReactNativeFastImageExample.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/ios/ReactNativeFastImageExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/ios/ReactNativeFastImageExample/AppDelegate.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | @interface AppDelegate : UIResponder
5 |
6 | @property (nonatomic, strong) UIWindow *window;
7 |
8 | @end
9 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/ios/ReactNativeFastImageExample/AppDelegate.m:
--------------------------------------------------------------------------------
1 | #import "AppDelegate.h"
2 |
3 | #import
4 | #import
5 | #import
6 |
7 | #ifdef FB_SONARKIT_ENABLED
8 | #import
9 | #import
10 | #import
11 | #import
12 | #import
13 | #import
14 |
15 | static void InitializeFlipper(UIApplication *application) {
16 | FlipperClient *client = [FlipperClient sharedClient];
17 | SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults];
18 | [client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]];
19 | [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]];
20 | [client addPlugin:[FlipperKitReactPlugin new]];
21 | [client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]];
22 | [client start];
23 | }
24 | #endif
25 |
26 | @implementation AppDelegate
27 |
28 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
29 | {
30 | #ifdef FB_SONARKIT_ENABLED
31 | InitializeFlipper(application);
32 | #endif
33 |
34 | RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
35 | RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
36 | moduleName:@"ReactNativeFastImageExample"
37 | initialProperties:nil];
38 |
39 | if (@available(iOS 13.0, *)) {
40 | rootView.backgroundColor = [UIColor systemBackgroundColor];
41 | } else {
42 | rootView.backgroundColor = [UIColor whiteColor];
43 | }
44 |
45 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
46 | UIViewController *rootViewController = [UIViewController new];
47 | rootViewController.view = rootView;
48 | self.window.rootViewController = rootViewController;
49 | [self.window makeKeyAndVisible];
50 | return YES;
51 | }
52 |
53 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
54 | {
55 | #if DEBUG
56 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
57 | #else
58 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
59 | #endif
60 | }
61 |
62 | @end
63 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/ios/ReactNativeFastImageExample/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | }
33 | ],
34 | "info" : {
35 | "version" : 1,
36 | "author" : "xcode"
37 | }
38 | }
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/ios/ReactNativeFastImageExample/Images.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/ios/ReactNativeFastImageExample/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | ReactNativeFastImageExample
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1
25 | LSRequiresIPhoneOS
26 |
27 | NSAppTransportSecurity
28 |
29 | NSExceptionDomains
30 |
31 | localhost
32 |
33 | NSExceptionAllowsInsecureHTTPLoads
34 |
35 |
36 |
37 |
38 | NSLocationWhenInUseUsageDescription
39 |
40 | NSPhotoLibraryUsageDescription
41 | Need your photos.
42 | UIAppFonts
43 |
44 | Ionicons.ttf
45 |
46 | UILaunchStoryboardName
47 | LaunchScreen
48 | UIRequiredDeviceCapabilities
49 |
50 | armv7
51 |
52 | UISupportedInterfaceOrientations
53 |
54 | UIInterfaceOrientationPortrait
55 | UIInterfaceOrientationLandscapeLeft
56 | UIInterfaceOrientationLandscapeRight
57 |
58 | UIViewControllerBasedStatusBarAppearance
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/ios/ReactNativeFastImageExample/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
24 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/ios/ReactNativeFastImageExample/main.m:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | #import "AppDelegate.h"
4 |
5 | int main(int argc, char * argv[]) {
6 | @autoreleasepool {
7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/ios/ReactNativeFastImageExampleTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/ios/ReactNativeFastImageExampleTests/ReactNativeFastImageExampleTests.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | #import
5 | #import
6 |
7 | #define TIMEOUT_SECONDS 600
8 | #define TEXT_TO_LOOK_FOR @"Welcome to React"
9 |
10 | @interface ReactNativeFastImageExampleTests : XCTestCase
11 |
12 | @end
13 |
14 | @implementation ReactNativeFastImageExampleTests
15 |
16 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test
17 | {
18 | if (test(view)) {
19 | return YES;
20 | }
21 | for (UIView *subview in [view subviews]) {
22 | if ([self findSubviewInView:subview matching:test]) {
23 | return YES;
24 | }
25 | }
26 | return NO;
27 | }
28 |
29 | - (void)testRendersWelcomeScreen
30 | {
31 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController];
32 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
33 | BOOL foundElement = NO;
34 |
35 | __block NSString *redboxError = nil;
36 | #ifdef DEBUG
37 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
38 | if (level >= RCTLogLevelError) {
39 | redboxError = message;
40 | }
41 | });
42 | #endif
43 |
44 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) {
45 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
46 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
47 |
48 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) {
49 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) {
50 | return YES;
51 | }
52 | return NO;
53 | }];
54 | }
55 |
56 | #ifdef DEBUG
57 | RCTSetLogFunction(RCTDefaultLogFunction);
58 | #endif
59 |
60 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
61 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
62 | }
63 |
64 |
65 | @end
66 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/metro.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const blacklist = require('metro-config/src/defaults/exclusionList')
3 | const escape = require('escape-string-regexp')
4 | const pak = require('../package.json')
5 |
6 | const root = path.resolve(__dirname, '..')
7 |
8 | const modules = Object.keys({
9 | ...pak.peerDependencies,
10 | })
11 |
12 | module.exports = {
13 | projectRoot: __dirname,
14 | watchFolders: [root],
15 |
16 | // We need to make sure that only one version is loaded for peerDependencies
17 | // So we blacklist them at the root, and alias them to the versions in example's node_modules
18 | resolver: {
19 | blacklistRE: blacklist(
20 | modules.map(
21 | (m) =>
22 | new RegExp(
23 | `^${escape(path.join(root, 'node_modules', m))}\\/.*$`,
24 | ),
25 | ),
26 | ),
27 |
28 | extraNodeModules: modules.reduce((acc, name) => {
29 | acc[name] = path.join(__dirname, 'node_modules', name)
30 | return acc
31 | }, {}),
32 | },
33 |
34 | transformer: {
35 | getTransformOptions: async () => ({
36 | transform: {
37 | experimentalImportSupport: false,
38 | inlineRequires: true,
39 | },
40 | }),
41 | },
42 | }
43 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "reactnativefastimageexample",
3 | "version": "0.0.1",
4 | "private": true,
5 | "scripts": {
6 | "android": "react-native run-android",
7 | "ios": "react-native run-ios",
8 | "start": "react-native start",
9 | "test": "jest",
10 | "lint": "eslint . --ext .js,.jsx,.ts,.tsx",
11 | "ts": "tsc --skipLibCheck"
12 | },
13 | "jest": {
14 | "moduleFileExtensions": [
15 | "ts",
16 | "tsx",
17 | "js",
18 | "jsx",
19 | "json",
20 | "node"
21 | ],
22 | "preset": "react-native"
23 | },
24 | "resolutions": {
25 | "@types/react": "^17"
26 | },
27 | "dependencies": {
28 | "@react-native-community/masked-view": "^0.1.11",
29 | "@react-navigation/bottom-tabs": "^6.2.0",
30 | "@react-navigation/native": "^6.0.8",
31 | "@react-navigation/stack": "^6.1.1",
32 | "react": "17.0.2",
33 | "react-native": "0.67.2",
34 | "react-native-gesture-handler": "^2.2.0",
35 | "react-native-image-picker": "^4.7.3",
36 | "react-native-image-progress": "^1.2.0",
37 | "react-native-reanimated": "2.3.0",
38 | "react-native-safe-area-context": "^3.3.2",
39 | "react-native-screens": "^3.10.2",
40 | "react-native-status-bar-height": "^2.6.0",
41 | "react-native-vector-icons": "^8.1.0"
42 | },
43 | "devDependencies": {
44 | "@babel/core": "^7.12.9",
45 | "@babel/runtime": "^7.12.5",
46 | "@react-native-community/eslint-config": "^2.0.0",
47 | "@types/jest": "^26.0.23",
48 | "@types/react-native": "^0.66.15",
49 | "@types/react-test-renderer": "^17.0.1",
50 | "babel-jest": "^26.6.3",
51 | "babel-plugin-module-resolver": "^4.1.0",
52 | "eslint": "^7.14.0",
53 | "jest": "^26.6.3",
54 | "metro-react-native-babel-preset": "^0.66.2",
55 | "react-test-renderer": "17.0.2",
56 | "typescript": "^4.5.5"
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/src/AutoSizeExample.tsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useMemo, useState } from 'react'
2 | import { StyleSheet, View } from 'react-native'
3 | import SectionFlex from './SectionFlex'
4 | import FastImage, { FastImageProps } from '@cuvent/react-native-fast-image'
5 | import Section from './Section'
6 | import FeatureText from './FeatureText'
7 | import { useCacheBust } from './useCacheBust'
8 |
9 | const GIF_URL =
10 | 'https://cdn-images-1.medium.com/max/1600/1*-CY5bU4OqiJRox7G00sftw.gif'
11 |
12 | interface AutoSizingImageProps extends FastImageProps {
13 | onLoad?: (event: any) => void
14 | defaultHeight?: number
15 | width: number
16 | style?: any
17 | }
18 |
19 | const AutoSizingImage = (props: AutoSizingImageProps) => {
20 | const [dimensions, setDimensions] = useState({
21 | height: 0,
22 | width: 0,
23 | })
24 |
25 | const propsOnLoad = props.onLoad
26 | const onLoad = useCallback(
27 | (e: any) => {
28 | const {
29 | nativeEvent: { width, height },
30 | } = e
31 | setDimensions({ width, height })
32 | if (propsOnLoad) {
33 | propsOnLoad(e)
34 | }
35 | },
36 | [propsOnLoad],
37 | )
38 |
39 | const height = useMemo(() => {
40 | if (!dimensions.height) {
41 | return props.defaultHeight === undefined ? 300 : props.defaultHeight
42 | }
43 | const ratio = dimensions.height / dimensions.width
44 | return props.width * ratio
45 | }, [dimensions.height, dimensions.width, props.defaultHeight, props.width])
46 | return (
47 |
52 | )
53 | }
54 |
55 | export const AutoSizeExample = () => {
56 | const { bust, url } = useCacheBust(GIF_URL)
57 | return (
58 |
59 |
62 |
63 |
68 |
69 |
70 | )
71 | }
72 |
73 | const styles = StyleSheet.create({
74 | image: {
75 | backgroundColor: '#ddd',
76 | margin: 20,
77 | flex: 0,
78 | },
79 | })
80 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/src/BorderRadiusExample.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { StyleSheet, View } from 'react-native'
3 | import SectionFlex from './SectionFlex'
4 | import FastImage from '@cuvent/react-native-fast-image'
5 | import Section from './Section'
6 | import FeatureText from './FeatureText'
7 | import { useCacheBust } from './useCacheBust'
8 |
9 | const IMAGE_URL = 'https://media.giphy.com/media/GEsoqZDGVoisw/giphy.gif'
10 |
11 | export const BorderRadiusExample = () => {
12 | const { query, bust } = useCacheBust('')
13 | return (
14 |
15 |
18 |
19 |
25 |
31 |
32 |
33 | )
34 | }
35 |
36 | const styles = StyleSheet.create({
37 | imageSquare: {
38 | borderRadius: 50,
39 | height: 100,
40 | backgroundColor: '#ddd',
41 | margin: 20,
42 | width: 100,
43 | flex: 0,
44 | },
45 | imageRectangular: {
46 | borderRadius: 50,
47 | borderTopLeftRadius: 10,
48 | borderBottomRightRadius: 10,
49 | height: 100,
50 | backgroundColor: '#ddd',
51 | margin: 20,
52 | flex: 1,
53 | },
54 | plus: {
55 | width: 30,
56 | height: 30,
57 | position: 'absolute',
58 | bottom: 0,
59 | right: 0,
60 | },
61 | })
62 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/src/BulletText.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import FeatureText from './FeatureText'
3 |
4 | interface BulletTextProps {
5 | text?: string
6 | children?: any
7 | }
8 |
9 | const BulletText = ({ text, children }: BulletTextProps) => (
10 |
11 | )
12 |
13 | export default BulletText
14 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/src/Button.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { StyleSheet, Text, TouchableOpacity, View } from 'react-native'
3 |
4 | interface ButtonProps {
5 | text: string
6 | onPress: () => void
7 | }
8 |
9 | const Button = ({ text, onPress }: ButtonProps) => (
10 |
11 |
12 | {text}
13 |
14 |
15 | )
16 |
17 | const styles = StyleSheet.create({
18 | button: {
19 | backgroundColor: 'black',
20 | margin: 10,
21 | height: 44,
22 | paddingLeft: 10,
23 | paddingRight: 10,
24 | borderRadius: 10,
25 | alignItems: 'center',
26 | justifyContent: 'center',
27 | },
28 | text: {
29 | color: 'white',
30 | },
31 | })
32 |
33 | export default Button
34 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/src/DefaultImageGrid.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Image } from 'react-native'
3 | import { ImageGrid } from './ImageGrid'
4 |
5 | const DefaultImageGrid = () =>
6 |
7 | export default DefaultImageGrid
8 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/src/ETAGExample.tsx:
--------------------------------------------------------------------------------
1 | import { Button, Dimensions, StatusBar, StyleSheet, View } from 'react-native'
2 | import React, { useRef } from 'react'
3 | import FastImage from '@cuvent/react-native-fast-image'
4 |
5 | export const ETAGExample = () => {
6 | const ref = useRef(null)
7 |
8 | return (
9 |
10 |
15 |
16 |
24 |
25 |
32 | )
33 | }
34 |
35 | const styles = StyleSheet.create({
36 | container: {
37 | flex: 1,
38 | alignItems: 'stretch',
39 | backgroundColor: '#fff',
40 | },
41 | image: {
42 | height: 300,
43 | width: Dimensions.get('window').width,
44 | },
45 | })
46 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/src/FastImageExamples.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { ScrollView, StatusBar, StyleSheet, Text, View } from 'react-native'
3 | import Section from './Section'
4 | import FeatureText from './FeatureText'
5 | import StatusBarUnderlay, { STATUS_BAR_HEIGHT } from './StatusBarUnderlay'
6 | import { PriorityExample } from './PriorityExample'
7 | import { GifExample } from './GifExample'
8 | import { BorderRadiusExample } from './BorderRadiusExample'
9 | import { ProgressExample } from './ProgressExample'
10 | import { PreloadExample } from './PreloadExample'
11 | import { ResizeModeExample } from './ResizeModeExample'
12 | import { TintColorExample } from './TintColorExample'
13 | import { LocalImagesExample } from './LocalImagesExample'
14 | import { AutoSizeExample } from './AutoSizeExample'
15 |
16 | const FastImageExample = () => (
17 |
18 |
23 |
27 |
28 |
29 | 🚩 FastImage
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | )
46 |
47 | const styles = StyleSheet.create({
48 | titleText: {
49 | fontWeight: '900',
50 | marginBottom: 20,
51 | color: '#222',
52 | },
53 | contentContainer: {
54 | marginTop: 20,
55 | },
56 | image: {
57 | flex: 1,
58 | height: 100,
59 | backgroundColor: '#ddd',
60 | margin: 10,
61 | },
62 | container: {
63 | flex: 1,
64 | alignItems: 'stretch',
65 | backgroundColor: '#fff',
66 | },
67 | scrollContainer: {
68 | marginTop: STATUS_BAR_HEIGHT,
69 | },
70 | scrollContentContainer: {
71 | alignItems: 'stretch',
72 | flex: 0,
73 | },
74 | })
75 |
76 | export default FastImageExample
77 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/src/FastImageGrid.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import FastImage from '@cuvent/react-native-fast-image'
3 | import { ImageGrid } from './ImageGrid'
4 |
5 | // @ts-ignore
6 | const FastImageGrid = () =>
7 |
8 | export default FastImageGrid
9 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/src/FeatureText.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { StyleSheet, Text } from 'react-native'
3 |
4 | interface FeatureTextProps {
5 | text?: string
6 | style?: any
7 | children?: any
8 | }
9 |
10 | export default function FeatureText({
11 | text,
12 | style,
13 | children,
14 | }: FeatureTextProps) {
15 | return {text || children}
16 | }
17 |
18 | const styles = StyleSheet.create({
19 | style: {
20 | color: '#222',
21 | },
22 | })
23 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/src/GifExample.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { StyleSheet, View } from 'react-native'
3 | import SectionFlex from './SectionFlex'
4 | import FastImage from '@cuvent/react-native-fast-image'
5 | import Section from './Section'
6 | import FeatureText from './FeatureText'
7 | import { useCacheBust } from './useCacheBust'
8 |
9 | const GIF_URL =
10 | 'https://cdn-images-1.medium.com/max/1600/1*-CY5bU4OqiJRox7G00sftw.gif'
11 |
12 | export const GifExample = () => {
13 | const { url, bust } = useCacheBust(GIF_URL)
14 | return (
15 |
16 |
19 |
20 |
21 |
22 |
23 | )
24 | }
25 |
26 | const styles = StyleSheet.create({
27 | image: {
28 | backgroundColor: '#ddd',
29 | margin: 20,
30 | height: 100,
31 | width: 100,
32 | flex: 0,
33 | },
34 | })
35 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/src/Icon.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | // @ts-ignore
3 | import Base from 'react-native-vector-icons/Ionicons'
4 |
5 | interface IconProps {
6 | size?: number
7 | name: string
8 | color: string
9 | }
10 |
11 | export function Icon({ size, name, color }: IconProps) {
12 | return (
13 |
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/src/ImageGrid.tsx:
--------------------------------------------------------------------------------
1 | import React, { memo, useCallback, useEffect, useState } from 'react'
2 | import { FlatList, Text, View, LayoutChangeEvent } from 'react-native'
3 | import StatusBarUnderlay, { STATUS_BAR_HEIGHT } from './StatusBarUnderlay'
4 |
5 | const getImageUrl = (id: string, width: number, height: number) =>
6 | `https://unsplash.it/${width}/${height}?image=${id}`
7 |
8 | const MARGIN = 2
9 |
10 | export interface ImageGridItemProps {
11 | id: string
12 | ImageComponent: any
13 | }
14 |
15 | export const ImageGridItem = memo(
16 | ({ id, ImageComponent }: ImageGridItemProps) => {
17 | const uri = getImageUrl(id, 100, 100)
18 | return (
19 |
25 |
35 |
36 | )
37 | },
38 | )
39 |
40 | export interface ImageGridProps {
41 | ImageComponent: React.ComponentType
42 | }
43 |
44 | export const ImageGrid = (props: ImageGridProps) => {
45 | const [images, setImages] = useState([])
46 | const [itemHeight, setItemHeight] = useState(0)
47 | const [error, setError] = useState(null)
48 |
49 | useEffect(() => {
50 | fetch('https://unsplash.it/list')
51 | .then((res) => res.json())
52 | .then((d) => setImages(d))
53 | .catch((e) => setError(e))
54 | }, [])
55 |
56 | const onLayout = useCallback((e: LayoutChangeEvent) => {
57 | const width = e.nativeEvent.layout.width
58 | setItemHeight(width / 4)
59 | }, [])
60 |
61 | const getItemLayout = useCallback(
62 | (_: any, index: number) => {
63 | return { length: itemHeight, offset: itemHeight * index, index }
64 | },
65 | [itemHeight],
66 | )
67 |
68 | const { ImageComponent } = props
69 |
70 | const renderItem = useCallback(
71 | ({ item }: { item: any }) => {
72 | return (
73 |
74 | )
75 | },
76 | [ImageComponent],
77 | )
78 |
79 | const extractKey = useCallback((item: any) => {
80 | return item.id
81 | }, [])
82 |
83 | if (error) {
84 | return (
85 |
93 |
98 | Error fetching images.
99 |
100 |
101 | )
102 | }
103 |
104 | return (
105 |
113 |
134 |
135 |
136 | )
137 | }
138 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/src/LocalImagesExample.tsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import {
3 | StyleSheet,
4 | View,
5 | Text,
6 | TouchableOpacity,
7 | ViewProps,
8 | } from 'react-native'
9 | import FastImage, {
10 | FastImageProps,
11 | Source,
12 | } from '@cuvent/react-native-fast-image'
13 | import Section from './Section'
14 | import FeatureText from './FeatureText'
15 | import FieldsBase64 from './images/fields'
16 | import { launchImageLibrary } from 'react-native-image-picker'
17 | import BulletText from './BulletText'
18 |
19 | // @ts-ignore
20 | import FieldsImage from './images/fields.jpg'
21 | // @ts-ignore
22 | import FieldsWebP from './images/fields.webp'
23 | // @ts-ignore
24 | import JellyfishGIF from './images/jellyfish.gif'
25 | // @ts-ignore
26 | import JellyfishWebP from './images/jellyfish.webp'
27 |
28 | const Image = ({ source, ...p }: FastImageProps) => (
29 |
30 | )
31 |
32 | const Row: React.ComponentType = (p: ViewProps) => (
33 |
34 | )
35 |
36 | interface ExampleProps {
37 | name: string
38 | source: any
39 | }
40 |
41 | const Example = ({ name, source }: ExampleProps) => (
42 |
43 | {name}
44 |
45 |
46 | )
47 |
48 | interface PhotoExampleState {
49 | image?: Source
50 | }
51 |
52 | class PhotoExample extends Component<{}, PhotoExampleState> {
53 | state: PhotoExampleState = {}
54 |
55 | pick = () => {
56 | launchImageLibrary({ mediaType: 'photo' }, (response) => {
57 | if (response.didCancel) {
58 | console.log('ImagePicker - User cancelled.')
59 | } else if (response.errorCode) {
60 | console.log(`ImagePicker - Error ${response.errorMessage}.`)
61 | } else {
62 | const uri = response?.assets?.[0]?.uri
63 | if (uri) {
64 | this.setState({
65 | image: { uri: uri },
66 | })
67 | }
68 | }
69 | })
70 | }
71 |
72 | render() {
73 | return (
74 |
75 | photo library
76 |
77 |
81 | Pick Photo
82 |
83 |
84 |
85 | )
86 | }
87 | }
88 |
89 | export const LocalImagesExample = () => (
90 |
91 |
92 | • Local images.
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 | )
105 |
106 | const styles = StyleSheet.create({
107 | pickPhoto: { color: 'white', fontWeight: '900' },
108 | row: {
109 | justifyContent: 'center',
110 | alignItems: 'center',
111 | marginBottom: 20,
112 | },
113 | container: {
114 | backgroundColor: '#eee',
115 | justifyContent: 'center',
116 | alignItems: 'center',
117 | paddingTop: 10,
118 | paddingBottom: 10,
119 | },
120 | imageSquare: {
121 | alignItems: 'center',
122 | justifyContent: 'center',
123 | height: 100,
124 | backgroundColor: '#ddd',
125 | margin: 20,
126 | marginTop: 10,
127 | width: 100,
128 | flex: 0,
129 | },
130 | plus: {
131 | width: 30,
132 | height: 30,
133 | position: 'absolute',
134 | bottom: 0,
135 | right: 0,
136 | },
137 | })
138 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/src/PreloadExample.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import { StyleSheet, View } from 'react-native'
3 | import SectionFlex from './SectionFlex'
4 | import FastImage from '@cuvent/react-native-fast-image'
5 | import Section from './Section'
6 | import FeatureText from './FeatureText'
7 | import Button from './Button'
8 | // @ts-ignore
9 | import { createImageProgress } from 'react-native-image-progress'
10 | import { useCacheBust } from './useCacheBust'
11 |
12 | const IMAGE_URL =
13 | 'https://cdn-images-1.medium.com/max/1600/1*-CY5bU4OqiJRox7G00sftw.gif'
14 |
15 | // @ts-ignore
16 | const Image = createImageProgress(FastImage)
17 |
18 | export const PreloadExample = () => {
19 | const [show, setShow] = useState(false)
20 | const { url, bust } = useCacheBust(IMAGE_URL)
21 |
22 | const preload = () => {
23 | FastImage.preload([{ uri: url }])
24 | }
25 |
26 | return (
27 |
28 |
32 |
33 | {show ? (
34 |
35 | ) : (
36 |
37 | )}
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
51 |
52 |
53 |
54 | )
55 | }
56 |
57 | const styles = StyleSheet.create({
58 | buttonView: { flex: 1 },
59 | section: {
60 | flexDirection: 'column',
61 | alignItems: 'center',
62 | },
63 | buttons: {
64 | flexDirection: 'row',
65 | marginHorizontal: 20,
66 | marginBottom: 10,
67 | },
68 | image: {
69 | backgroundColor: '#ddd',
70 | margin: 20,
71 | marginBottom: 10,
72 | height: 100,
73 | width: 100,
74 | },
75 | })
76 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/src/PriorityExample.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { PixelRatio, StyleSheet, View } from 'react-native'
3 | import FastImage from '@cuvent/react-native-fast-image'
4 | import Section from './Section'
5 | import SectionFlex from './SectionFlex'
6 | import FeatureText from './FeatureText'
7 | import { useCacheBust } from './useCacheBust'
8 |
9 | const getImageUrl = (id: string, width: number, height: number) =>
10 | `https://source.unsplash.com/${id}/${width}x${height}`
11 | const IMAGE_SIZE = 1024
12 | const IMAGE_SIZE_PX = PixelRatio.getPixelSizeForLayoutSize(IMAGE_SIZE)
13 | const IMAGE_URLS = [
14 | getImageUrl('x58soEovG_M', IMAGE_SIZE_PX, IMAGE_SIZE_PX),
15 | getImageUrl('yPI7myL5eWY', IMAGE_SIZE_PX, IMAGE_SIZE_PX),
16 | getImageUrl('S7VCcp6KCKE', IMAGE_SIZE, IMAGE_SIZE),
17 | ]
18 |
19 | export const PriorityExample = () => {
20 | const { query, bust } = useCacheBust('')
21 | return (
22 |
23 |
26 |
27 |
34 |
41 |
48 |
49 |
50 | )
51 | }
52 |
53 | const styles = StyleSheet.create({
54 | image: {
55 | flex: 1,
56 | height: 100,
57 | backgroundColor: '#ddd',
58 | margin: 10,
59 | marginVertical: 20,
60 | },
61 | })
62 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/src/ProgressExample.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import { StyleSheet, View, Text } from 'react-native'
3 | import SectionFlex from './SectionFlex'
4 | import FastImage from '@cuvent/react-native-fast-image'
5 | import Section from './Section'
6 | import FeatureText from './FeatureText'
7 | import { useCacheBust } from './useCacheBust'
8 |
9 | const IMAGE_URL = 'https://media.giphy.com/media/GEsoqZDGVoisw/giphy.gif'
10 |
11 | export const ProgressExample = () => {
12 | const [state, setState] = useState<{
13 | mount: number
14 | start?: number
15 | progress?: number
16 | end?: number
17 | }>({
18 | mount: Date.now(),
19 | start: undefined,
20 | progress: undefined,
21 | end: undefined,
22 | })
23 |
24 | const { url, bust } = useCacheBust(IMAGE_URL)
25 | const { mount, start, progress, end } = state
26 | return (
27 |
28 |
31 |
32 |
38 | setState((s) => ({ ...s, start: Date.now() }))
39 | }
40 | onProgress={(e) => {
41 | const p = Math.round(
42 | 100 * (e.nativeEvent.loaded / e.nativeEvent.total),
43 | )
44 | setState((s) => ({
45 | ...s,
46 | progress: p,
47 | }))
48 | }}
49 | onLoad={() => setState((s) => ({ ...s, end: Date.now() }))}
50 | onLoadEnd={() => {}}
51 | />
52 |
53 | onLoadStart
54 | {start !== undefined && ` - ${start - mount} ms`}
55 |
56 |
57 | onProgress
58 | {progress !== undefined && ` - ${progress} %`}
59 |
60 |
61 | onLoad
62 | {end !== undefined && ` - ${end - mount} ms`}
63 |
64 |
65 |
66 | )
67 | }
68 |
69 | const styles = StyleSheet.create({
70 | section: {
71 | flexDirection: 'column',
72 | alignItems: 'center',
73 | paddingBottom: 20,
74 | },
75 | image: {
76 | height: 100,
77 | backgroundColor: '#ddd',
78 | margin: 20,
79 | width: 100,
80 | flex: 0,
81 | },
82 | })
83 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/src/ResizeModeExample.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { StyleSheet, View } from 'react-native'
3 | import SectionFlex from './SectionFlex'
4 | import FastImage from '@cuvent/react-native-fast-image'
5 | import Section from './Section'
6 | import FeatureText from './FeatureText'
7 | import BulletText from './BulletText'
8 |
9 | const IMAGE_URL = 'https://media.giphy.com/media/GEsoqZDGVoisw/giphy.gif'
10 |
11 | const Col = (p: any) =>
12 |
13 | export const ResizeModeExample = () => (
14 |
15 |
18 |
19 |
20 |
25 | contain
26 |
27 |
28 |
33 | center
34 |
35 |
36 |
41 | stretch
42 |
43 |
44 |
49 | cover
50 |
51 |
52 |
53 | )
54 |
55 | const styles = StyleSheet.create({
56 | image: {
57 | height: 100,
58 | width: 50,
59 | backgroundColor: '#ddd',
60 | margin: 20,
61 | marginTop: 0,
62 | marginBottom: 10,
63 | flex: 0,
64 | },
65 | container: {
66 | padding: 20,
67 | },
68 | col: {
69 | alignItems: 'center',
70 | },
71 | })
72 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/src/Section.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { StyleSheet, View } from 'react-native'
3 |
4 | interface SectionProps {
5 | children?: any
6 | }
7 |
8 | export default function Section({ children }: SectionProps) {
9 | return {children}
10 | }
11 |
12 | const styles = StyleSheet.create({
13 | section: {
14 | marginTop: 10,
15 | marginBottom: 10,
16 | marginLeft: 40,
17 | marginRight: 40,
18 | },
19 | })
20 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/src/SectionFlex.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { StyleSheet, TouchableOpacity, View } from 'react-native'
3 |
4 | interface SectionFlexProps {
5 | style?: any
6 | onPress?: () => void
7 | children?: any
8 | }
9 |
10 | export default function SectionFlex({
11 | children,
12 | onPress,
13 | style,
14 | }: SectionFlexProps) {
15 | return onPress ? (
16 |
17 | {children}
18 |
19 | ) : (
20 | {children}
21 | )
22 | }
23 |
24 | const styles = StyleSheet.create({
25 | sectionFlex: {
26 | backgroundColor: '#eee',
27 | flexDirection: 'row',
28 | justifyContent: 'center',
29 | marginLeft: -10,
30 | marginRight: -10,
31 | },
32 | })
33 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/src/StatusBarUnderlay.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { StyleSheet, View } from 'react-native'
3 | import { getStatusBarHeight } from 'react-native-status-bar-height'
4 |
5 | export const STATUS_BAR_HEIGHT = getStatusBarHeight()
6 |
7 | export default () =>
8 |
9 | const styles = StyleSheet.create({
10 | statusBarUnderlay: {
11 | position: 'absolute',
12 | top: 0,
13 | left: 0,
14 | right: 0,
15 | height: STATUS_BAR_HEIGHT,
16 | backgroundColor: 'white',
17 | },
18 | })
19 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/src/TintColorExample.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { StyleSheet, View } from 'react-native'
3 | import FastImage from '@cuvent/react-native-fast-image'
4 | import Section from './Section'
5 | import SectionFlex from './SectionFlex'
6 | import FeatureText from './FeatureText'
7 |
8 | // @ts-ignore
9 | import LogoImage from './images/logo.png'
10 |
11 | export const TintColorExample = () => {
12 | return (
13 |
14 |
18 |
19 |
24 |
29 |
34 |
35 |
36 | )
37 | }
38 |
39 | const styles = StyleSheet.create({
40 | image: {
41 | flex: 1,
42 | height: 100,
43 | margin: 10,
44 | },
45 | })
46 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/src/images/fields.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuvent/react-native-fast-image/a661be6bb65029b3f1750fed9b2f793949df6152/ReactNativeFastImageExample/src/images/fields.jpg
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/src/images/fields.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuvent/react-native-fast-image/a661be6bb65029b3f1750fed9b2f793949df6152/ReactNativeFastImageExample/src/images/fields.webp
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/src/images/jellyfish.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuvent/react-native-fast-image/a661be6bb65029b3f1750fed9b2f793949df6152/ReactNativeFastImageExample/src/images/jellyfish.gif
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/src/images/jellyfish.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuvent/react-native-fast-image/a661be6bb65029b3f1750fed9b2f793949df6152/ReactNativeFastImageExample/src/images/jellyfish.webp
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/src/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuvent/react-native-fast-image/a661be6bb65029b3f1750fed9b2f793949df6152/ReactNativeFastImageExample/src/images/logo.png
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { LogBox } from 'react-native'
3 | import { NavigationContainer } from '@react-navigation/native'
4 | import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'
5 | import { Icon } from './Icon'
6 | import FastImageExamples from './FastImageExamples'
7 | import FastImageGrid from './FastImageGrid'
8 | import DefaultImageGrid from './DefaultImageGrid'
9 | import { ETAGExample } from './ETAGExample'
10 |
11 | const Tab = createBottomTabNavigator()
12 |
13 | LogBox.ignoreLogs([
14 | 'Warning: isMounted(...) is deprecated',
15 | 'Module RCTImageLoader',
16 | ])
17 |
18 | export default function App() {
19 | return (
20 |
21 |
22 | (
27 |
28 | ),
29 | }}
30 | />
31 | (
36 |
37 | ),
38 | }}
39 | />
40 | (
45 |
46 | ),
47 | }}
48 | />
49 | (
54 |
55 | ),
56 | }}
57 | />
58 |
59 |
60 | )
61 | }
62 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/src/useCacheBust.tsx:
--------------------------------------------------------------------------------
1 | import { useCallback, useState } from 'react'
2 |
3 | const getNewKey = () => Math.random().toString()
4 |
5 | export const useCacheBust = (
6 | url: string,
7 | ): { bust: () => void; url: string; query: string } => {
8 | const [key, setKey] = useState(getNewKey())
9 | const bust = useCallback(() => {
10 | setKey(getNewKey())
11 | }, [])
12 | const query = `?bust=${key}`
13 | return {
14 | url: `${url}${query}`,
15 | query,
16 | bust,
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExample/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Basic Options */
4 | "target": "esnext" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */,
5 | "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,
6 | "lib": [
7 | "es2017"
8 | ] /* Specify library files to be included in the compilation. */,
9 | "allowJs": true /* Allow javascript files to be compiled. */,
10 | // "checkJs": true, /* Report errors in .js files. */
11 | "jsx": "react-native" /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */,
12 | // "declaration": true, /* Generates corresponding '.d.ts' file. */
13 | // "sourceMap": true, /* Generates corresponding '.map' file. */
14 | // "outFile": "./", /* Concatenate and emit output to single file. */
15 | // "outDir": "./", /* Redirect output structure to the directory. */
16 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
17 | // "removeComments": true, /* Do not emit comments to output. */
18 | "noEmit": true /* Do not emit outputs. */,
19 | // "incremental": true, /* Enable incremental compilation */
20 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */
21 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
22 | "isolatedModules": true /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */,
23 |
24 | /* Strict Type-Checking Options */
25 | "strict": true /* Enable all strict type-checking options. */,
26 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
27 | // "strictNullChecks": true, /* Enable strict null checks. */
28 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */
29 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
30 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
31 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
32 |
33 | /* Additional Checks */
34 | // "noUnusedLocals": true, /* Report errors on unused locals. */
35 | // "noUnusedParameters": true, /* Report errors on unused parameters. */
36 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
37 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
38 |
39 | /* Module Resolution Options */
40 | "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */,
41 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
42 | /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
43 | "paths": {
44 | "@cuvent/react-native-fast-image": [
45 | "../src/index"
46 | ]
47 | },
48 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
49 | // "typeRoots": [], /* List of folders to include type definitions from. */
50 | // "types": [], /* Type declaration files to be included in compilation. */
51 | "allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */,
52 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
53 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
54 | "skipLibCheck": false /* Skip type checking of declaration files. */,
55 | "resolveJsonModule": true /* Allows importing modules with a ‘.json’ extension, which is a common practice in node projects. */
56 |
57 | /* Source Map Options */
58 | // "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
59 | // "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */
60 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
61 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
62 |
63 | /* Experimental Options */
64 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
65 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
66 | },
67 | "exclude": [
68 | "node_modules",
69 | "babel.config.js",
70 | "metro.config.js",
71 | "jest.config.js"
72 | ]
73 | }
74 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExampleServer/index.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const express = require('express')
3 | const bodyParser = require('body-parser')
4 | const morgan = require('morgan')
5 |
6 | const app = express()
7 |
8 | const port = process.env.PORT || 8080
9 | const welcome = 'Test images API at http://localhost:' + port
10 | console.log(welcome)
11 |
12 | app.use(bodyParser.urlencoded({ extended: false }))
13 | app.use(bodyParser.json())
14 | app.use(morgan('dev'))
15 | app.get('/', (req, res) => res.send(welcome))
16 | app.listen(port)
17 |
18 | const staticPictures = express.static(path.join(__dirname, 'pictures'))
19 |
20 | app.use('/pictures', staticPictures)
21 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExampleServer/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ReactNativeFastImageExampleServer",
3 | "version": "0.0.1",
4 | "private": true,
5 | "license": "MIT",
6 | "main": "index.js",
7 | "scripts": {
8 | "start": "nodemon ./index",
9 | "cat:swap": "mv pictures/cat.jpg pictures/_cat2.jpg && mv pictures/cat2.jpg pictures/cat.jpg && mv pictures/_cat2.jpg pictures/cat2.jpg"
10 | },
11 | "dependencies": {
12 | "body-parser": "^1.19.0",
13 | "express": "^4.17.1",
14 | "morgan": "^1.9.1",
15 | "nodemon": "^2.0.2"
16 | },
17 | "devDependencies": {
18 | "ts-node": "^10.4.0"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/ReactNativeFastImageExampleServer/pictures/cat.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuvent/react-native-fast-image/a661be6bb65029b3f1750fed9b2f793949df6152/ReactNativeFastImageExampleServer/pictures/cat.jpg
--------------------------------------------------------------------------------
/ReactNativeFastImageExampleServer/pictures/cat2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuvent/react-native-fast-image/a661be6bb65029b3f1750fed9b2f793949df6152/ReactNativeFastImageExampleServer/pictures/cat2.jpg
--------------------------------------------------------------------------------
/ReactNativeFastImageExampleServer/pictures/fields.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuvent/react-native-fast-image/a661be6bb65029b3f1750fed9b2f793949df6152/ReactNativeFastImageExampleServer/pictures/fields.jpg
--------------------------------------------------------------------------------
/ReactNativeFastImageExampleServer/pictures/forest.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuvent/react-native-fast-image/a661be6bb65029b3f1750fed9b2f793949df6152/ReactNativeFastImageExampleServer/pictures/forest.jpg
--------------------------------------------------------------------------------
/ReactNativeFastImageExampleServer/pictures/harbor.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuvent/react-native-fast-image/a661be6bb65029b3f1750fed9b2f793949df6152/ReactNativeFastImageExampleServer/pictures/harbor.jpg
--------------------------------------------------------------------------------
/ReactNativeFastImageExampleServer/pictures/jellyfish.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuvent/react-native-fast-image/a661be6bb65029b3f1750fed9b2f793949df6152/ReactNativeFastImageExampleServer/pictures/jellyfish.gif
--------------------------------------------------------------------------------
/ReactNativeFastImageExampleServer/pictures/jellyfish.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuvent/react-native-fast-image/a661be6bb65029b3f1750fed9b2f793949df6152/ReactNativeFastImageExampleServer/pictures/jellyfish.webp
--------------------------------------------------------------------------------
/ReactNativeFastImageExampleServer/pictures/plankton.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuvent/react-native-fast-image/a661be6bb65029b3f1750fed9b2f793949df6152/ReactNativeFastImageExampleServer/pictures/plankton.gif
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | jcenter()
4 | google()
5 | }
6 | dependencies {
7 | classpath "com.android.tools.build:gradle:3.5.3"
8 | classpath "io.objectbox:objectbox-gradle-plugin:2.9.1"
9 | }
10 | }
11 |
12 | apply plugin: "com.android.library"
13 | apply plugin: 'io.objectbox'
14 |
15 | def safeExtGet(prop, fallback) {
16 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
17 | }
18 |
19 | android {
20 | compileSdkVersion safeExtGet('compileSdkVersion', 28)
21 | buildToolsVersion safeExtGet('buildToolsVersion', '28.0.3')
22 | defaultConfig {
23 | minSdkVersion safeExtGet('minSdkVersion', 16)
24 | targetSdkVersion safeExtGet('targetSdkVersion', 28)
25 | versionCode 1
26 | versionName "1.0"
27 | }
28 | sourceSets {
29 | main {
30 | java {
31 | if (safeExtGet('excludeAppGlideModule', false)) {
32 | srcDir "src"
33 | exclude "**/FastImageGlideModule.java"
34 | }
35 | }
36 | }
37 | }
38 | lintOptions {
39 | abortOnError false
40 | }
41 | }
42 |
43 | repositories {
44 | mavenLocal()
45 | maven {
46 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
47 | url "$rootDir/../node_modules/react-native/android"
48 | }
49 | maven {
50 | // Android JSC is installed from npm
51 | url "$rootDir/../node_modules/jsc-android/dist"
52 | }
53 | google()
54 | mavenCentral()
55 | }
56 |
57 |
58 | def glideVersion = safeExtGet('glideVersion', '4.13.1')
59 |
60 | dependencies {
61 | //noinspection GradleDynamicVersion
62 | implementation 'com.facebook.react:react-native:+' // From node_modules
63 | implementation "com.github.bumptech.glide:glide:${glideVersion}"
64 | implementation "com.github.bumptech.glide:okhttp3-integration:${glideVersion}"
65 | implementation "com.github.bumptech.glide:glide:${glideVersion}"
66 | annotationProcessor "com.github.bumptech.glide:compiler:${glideVersion}"
67 | }
68 |
69 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuvent/react-native-fast-image/a661be6bb65029b3f1750fed9b2f793949df6152/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Nov 25 15:58:32 CET 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/android/objectbox-models/default.json:
--------------------------------------------------------------------------------
1 | {
2 | "_note1": "KEEP THIS FILE! Check it into a version control system (VCS) like git.",
3 | "_note2": "ObjectBox manages crucial IDs for your object model. See docs for details.",
4 | "_note3": "If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.",
5 | "entities": [
6 | {
7 | "id": "1:5479488966980118382",
8 | "lastPropertyId": "3:6722197042630209543",
9 | "name": "EntityEtagCache",
10 | "properties": [
11 | {
12 | "id": "1:3311350697302057773",
13 | "name": "id",
14 | "type": 6,
15 | "flags": 1
16 | },
17 | {
18 | "id": "2:8905752283444122403",
19 | "name": "url",
20 | "type": 9
21 | },
22 | {
23 | "id": "3:6722197042630209543",
24 | "name": "etag",
25 | "type": 9
26 | }
27 | ],
28 | "relations": []
29 | }
30 | ],
31 | "lastEntityId": "1:5479488966980118382",
32 | "lastIndexId": "0:0",
33 | "lastRelationId": "0:0",
34 | "lastSequenceId": "0:0",
35 | "modelVersion": 5,
36 | "modelVersionParserMinimum": 5,
37 | "retiredEntityUids": [],
38 | "retiredIndexUids": [],
39 | "retiredPropertyUids": [],
40 | "retiredRelationUids": [],
41 | "version": 1
42 | }
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/android/src/main/java/com/dylanvann/fastimage/FastImageCacheControl.java:
--------------------------------------------------------------------------------
1 | package com.dylanvann.fastimage;
2 |
3 | public enum FastImageCacheControl {
4 | IMMUTABLE,
5 | WEB,
6 | CACHE_ONLY
7 | }
8 |
9 |
--------------------------------------------------------------------------------
/android/src/main/java/com/dylanvann/fastimage/FastImageGlideModule.java:
--------------------------------------------------------------------------------
1 | package com.dylanvann.fastimage;
2 |
3 | import android.content.Context;
4 | import android.util.Log;
5 |
6 | import androidx.annotation.NonNull;
7 |
8 | import com.bumptech.glide.Glide;
9 | import com.bumptech.glide.GlideBuilder;
10 | import com.bumptech.glide.Registry;
11 | import com.bumptech.glide.annotation.GlideModule;
12 | import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader;
13 | import com.bumptech.glide.load.engine.cache.InternalCacheDiskCacheFactory;
14 | import com.bumptech.glide.load.model.GlideUrl;
15 | import com.bumptech.glide.module.AppGlideModule;
16 | import com.dylanvann.fastimage.custom.SharedOkHttpClient;
17 |
18 | import java.io.InputStream;
19 |
20 | // We need an AppGlideModule to be present for progress events to work.
21 | @GlideModule
22 | public final class FastImageGlideModule extends AppGlideModule {
23 | final long ONE_GB = 1024L * 1024L * 1000L; // 1000 MB
24 |
25 | //this overwrites the default okhttp client with a client that has caching enabled.
26 | @Override
27 | public void registerComponents(Context context, Glide glide, Registry registry) {
28 | OkHttpUrlLoader.Factory factory = new OkHttpUrlLoader.Factory(SharedOkHttpClient.getInstance(context).getClient());
29 |
30 | glide.getRegistry().replace(GlideUrl.class, InputStream.class, factory);
31 | }
32 |
33 | @Override
34 | public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
35 | super.applyOptions(context, builder);
36 | builder.setLogLevel(Log.VERBOSE);
37 | builder.setDiskCache(new InternalCacheDiskCacheFactory(context, ONE_GB));
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/android/src/main/java/com/dylanvann/fastimage/FastImageOkHttpProgressGlideModule.java:
--------------------------------------------------------------------------------
1 | package com.dylanvann.fastimage;
2 |
3 | import android.content.Context;
4 | import androidx.annotation.NonNull;
5 |
6 | import com.bumptech.glide.Glide;
7 | import com.bumptech.glide.Registry;
8 | import com.bumptech.glide.annotation.GlideModule;
9 | import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader;
10 | import com.bumptech.glide.load.model.GlideUrl;
11 | import com.bumptech.glide.module.LibraryGlideModule;
12 | import com.facebook.react.modules.network.OkHttpClientProvider;
13 |
14 | import java.io.IOException;
15 | import java.io.InputStream;
16 | import java.util.HashMap;
17 | import java.util.Map;
18 | import java.util.WeakHashMap;
19 |
20 | import okhttp3.Interceptor;
21 | import okhttp3.MediaType;
22 | import okhttp3.OkHttpClient;
23 | import okhttp3.Request;
24 | import okhttp3.Response;
25 | import okhttp3.ResponseBody;
26 | import okio.Buffer;
27 | import okio.BufferedSource;
28 | import okio.ForwardingSource;
29 | import okio.Okio;
30 | import okio.Source;
31 |
32 | @GlideModule
33 | public class FastImageOkHttpProgressGlideModule extends LibraryGlideModule {
34 |
35 | private static DispatchingProgressListener progressListener = new DispatchingProgressListener();
36 |
37 | @Override
38 | public void registerComponents(
39 | @NonNull Context context,
40 | @NonNull Glide glide,
41 | @NonNull Registry registry
42 | ) {
43 | OkHttpClient client = OkHttpClientProvider
44 | .getOkHttpClient()
45 | .newBuilder()
46 | .addInterceptor(createInterceptor(progressListener))
47 | .build();
48 | OkHttpUrlLoader.Factory factory = new OkHttpUrlLoader.Factory(client);
49 | registry.replace(GlideUrl.class, InputStream.class, factory);
50 | }
51 |
52 | private static Interceptor createInterceptor(final ResponseProgressListener listener) {
53 | return new Interceptor() {
54 | @Override
55 | public Response intercept(Chain chain) throws IOException {
56 | Request request = chain.request();
57 | Response response = chain.proceed(request);
58 | final String key = request.url().toString();
59 | return response
60 | .newBuilder()
61 | .body(new OkHttpProgressResponseBody(key, response.body(), listener))
62 | .build();
63 | }
64 | };
65 | }
66 |
67 | static void forget(String key) {
68 | progressListener.forget(key);
69 | }
70 |
71 | static void expect(String key, FastImageProgressListener listener) {
72 | progressListener.expect(key, listener);
73 | }
74 |
75 | private interface ResponseProgressListener {
76 | void update(String key, long bytesRead, long contentLength);
77 | }
78 |
79 | private static class DispatchingProgressListener implements ResponseProgressListener {
80 | private final Map LISTENERS = new WeakHashMap<>();
81 | private final Map PROGRESSES = new HashMap<>();
82 |
83 | void forget(String key) {
84 | LISTENERS.remove(key);
85 | PROGRESSES.remove(key);
86 | }
87 |
88 | void expect(String key, FastImageProgressListener listener) {
89 | LISTENERS.put(key, listener);
90 | }
91 |
92 | @Override
93 | public void update(final String key, final long bytesRead, final long contentLength) {
94 | final FastImageProgressListener listener = LISTENERS.get(key);
95 | if (listener == null) {
96 | return;
97 | }
98 | if (contentLength <= bytesRead) {
99 | forget(key);
100 | }
101 | if (needsDispatch(key, bytesRead, contentLength, listener.getGranularityPercentage())) {
102 | listener.onProgress(key, bytesRead, contentLength);
103 | }
104 | }
105 |
106 | private boolean needsDispatch(String key, long current, long total, float granularity) {
107 | if (granularity == 0 || current == 0 || total == current) {
108 | return true;
109 | }
110 | float percent = 100f * current / total;
111 | long currentProgress = (long) (percent / granularity);
112 | Long lastProgress = PROGRESSES.get(key);
113 | if (lastProgress == null || currentProgress != lastProgress) {
114 | PROGRESSES.put(key, currentProgress);
115 | return true;
116 | } else {
117 | return false;
118 | }
119 | }
120 | }
121 |
122 | private static class OkHttpProgressResponseBody extends ResponseBody {
123 | private final String key;
124 | private final ResponseBody responseBody;
125 | private final ResponseProgressListener progressListener;
126 | private BufferedSource bufferedSource;
127 |
128 | OkHttpProgressResponseBody(
129 | String key,
130 | ResponseBody responseBody,
131 | ResponseProgressListener progressListener
132 | ) {
133 | this.key = key;
134 | this.responseBody = responseBody;
135 | this.progressListener = progressListener;
136 | }
137 |
138 | @Override
139 | public MediaType contentType() {
140 | return responseBody.contentType();
141 | }
142 |
143 | @Override
144 | public long contentLength() {
145 | return responseBody.contentLength();
146 | }
147 |
148 | @Override
149 | public BufferedSource source() {
150 | if (bufferedSource == null) {
151 | bufferedSource = Okio.buffer(source(responseBody.source()));
152 | }
153 | return bufferedSource;
154 | }
155 |
156 | private Source source(Source source) {
157 | return new ForwardingSource(source) {
158 | long totalBytesRead = 0L;
159 |
160 | @Override
161 | public long read(Buffer sink, long byteCount) throws IOException {
162 | long bytesRead = super.read(sink, byteCount);
163 | long fullLength = responseBody.contentLength();
164 | if (bytesRead == -1) {
165 | // this source is exhausted
166 | totalBytesRead = fullLength;
167 | } else {
168 | totalBytesRead += bytesRead;
169 | }
170 | progressListener.update(key, totalBytesRead, fullLength);
171 | return bytesRead;
172 | }
173 | };
174 | }
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/android/src/main/java/com/dylanvann/fastimage/FastImageProgressListener.java:
--------------------------------------------------------------------------------
1 | package com.dylanvann.fastimage;
2 |
3 | public interface FastImageProgressListener {
4 |
5 | void onProgress(String key, long bytesRead, long expectedLength);
6 |
7 | /**
8 | * Control how often the listener needs an update. 0% and 100% will always be dispatched.
9 | *
10 | * @return in percentage (0.2 = call {@link #onProgress} around every 0.2 percent of progress)
11 | */
12 | float getGranularityPercentage();
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/android/src/main/java/com/dylanvann/fastimage/FastImageRequestListener.java:
--------------------------------------------------------------------------------
1 | package com.dylanvann.fastimage;
2 |
3 | import android.graphics.drawable.Drawable;
4 |
5 | import com.bumptech.glide.load.DataSource;
6 | import com.bumptech.glide.load.engine.GlideException;
7 | import com.bumptech.glide.request.RequestListener;
8 | import com.bumptech.glide.request.target.ImageViewTarget;
9 | import com.bumptech.glide.request.target.Target;
10 | import com.facebook.react.bridge.WritableMap;
11 | import com.facebook.react.bridge.WritableNativeMap;
12 | import com.facebook.react.uimanager.ThemedReactContext;
13 | import com.facebook.react.uimanager.events.RCTEventEmitter;
14 |
15 | public class FastImageRequestListener implements RequestListener {
16 | static final String REACT_ON_ERROR_EVENT = "onFastImageError";
17 | static final String REACT_ON_LOAD_EVENT = "onFastImageLoad";
18 | static final String REACT_ON_LOAD_END_EVENT = "onFastImageLoadEnd";
19 |
20 | private String key;
21 |
22 | FastImageRequestListener(String key) {
23 | this.key = key;
24 | }
25 |
26 | private static WritableMap mapFromResource(Drawable resource) {
27 | WritableMap resourceData = new WritableNativeMap();
28 | resourceData.putInt("width", resource.getIntrinsicWidth());
29 | resourceData.putInt("height", resource.getIntrinsicHeight());
30 | return resourceData;
31 | }
32 |
33 | @Override
34 | public boolean onLoadFailed(@androidx.annotation.Nullable GlideException e, Object model, Target target, boolean isFirstResource) {
35 | FastImageOkHttpProgressGlideModule.forget(key);
36 | if (!(target instanceof ImageViewTarget)) {
37 | return false;
38 | }
39 | FastImageViewWithUrl view = (FastImageViewWithUrl) ((ImageViewTarget) target).getView();
40 | ThemedReactContext context = (ThemedReactContext) view.getContext();
41 | RCTEventEmitter eventEmitter = context.getJSModule(RCTEventEmitter.class);
42 | int viewId = view.getId();
43 | eventEmitter.receiveEvent(viewId, REACT_ON_ERROR_EVENT, new WritableNativeMap());
44 | eventEmitter.receiveEvent(viewId, REACT_ON_LOAD_END_EVENT, new WritableNativeMap());
45 | return false;
46 | }
47 |
48 | @Override
49 | public boolean onResourceReady(Drawable resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) {
50 | if (!(target instanceof ImageViewTarget)) {
51 | return false;
52 | }
53 | FastImageViewWithUrl view = (FastImageViewWithUrl) ((ImageViewTarget) target).getView();
54 | ThemedReactContext context = (ThemedReactContext) view.getContext();
55 | RCTEventEmitter eventEmitter = context.getJSModule(RCTEventEmitter.class);
56 | int viewId = view.getId();
57 | eventEmitter.receiveEvent(viewId, REACT_ON_LOAD_EVENT, mapFromResource(resource));
58 | eventEmitter.receiveEvent(viewId, REACT_ON_LOAD_END_EVENT, new WritableNativeMap());
59 | return false;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/android/src/main/java/com/dylanvann/fastimage/FastImageSource.java:
--------------------------------------------------------------------------------
1 | package com.dylanvann.fastimage;
2 |
3 | import android.content.Context;
4 | import android.content.res.Resources;
5 | import android.net.Uri;
6 | import android.text.TextUtils;
7 |
8 | import com.bumptech.glide.load.model.GlideUrl;
9 | import com.bumptech.glide.load.model.Headers;
10 | import com.facebook.react.views.imagehelper.ImageSource;
11 |
12 | import javax.annotation.Nullable;
13 |
14 | public class FastImageSource extends ImageSource {
15 | private static final String DATA_SCHEME = "data";
16 | private static final String LOCAL_RESOURCE_SCHEME = "res";
17 | private static final String ANDROID_RESOURCE_SCHEME = "android.resource";
18 | private static final String ANDROID_CONTENT_SCHEME = "content";
19 | private static final String LOCAL_FILE_SCHEME = "file";
20 | private static final String Glide = "Glide";
21 | private Headers mHeaders;
22 | private Uri mUri;
23 |
24 | public static boolean isBase64Uri(Uri uri) {
25 | return DATA_SCHEME.equals(uri.getScheme());
26 | }
27 |
28 | public static boolean isLocalResourceUri(Uri uri) {
29 | return LOCAL_RESOURCE_SCHEME.equals(uri.getScheme());
30 | }
31 |
32 | public static boolean isResourceUri(Uri uri) {
33 | return ANDROID_RESOURCE_SCHEME.equals(uri.getScheme());
34 | }
35 |
36 | public static boolean isContentUri(Uri uri) {
37 | return ANDROID_CONTENT_SCHEME.equals(uri.getScheme());
38 | }
39 |
40 | public static boolean isLocalFileUri(Uri uri) {
41 | return LOCAL_FILE_SCHEME.equals(uri.getScheme());
42 | }
43 |
44 | public FastImageSource(Context context, String source) {
45 | this(context, source, null);
46 | }
47 |
48 | public FastImageSource(Context context, String source, @Nullable Headers headers) {
49 | this(context, source, 0.0d, 0.0d, headers);
50 | }
51 |
52 | public FastImageSource(Context context, String source, double width, double height, @Nullable Headers headers) {
53 | super(context, source, width, height);
54 | mHeaders = headers == null ? Headers.DEFAULT : headers;
55 | mUri = super.getUri();
56 |
57 | if (isResource() && TextUtils.isEmpty(mUri.toString())) {
58 | throw new Resources.NotFoundException("Local Resource Not Found. Resource: '" + getSource() + "'.");
59 | }
60 |
61 | if (isLocalResourceUri(mUri)) {
62 | // Convert res:/ scheme to android.resource:// so
63 | // glide can understand the uri.
64 | mUri = Uri.parse(mUri.toString().replace("res:/", ANDROID_RESOURCE_SCHEME + "://" + context.getPackageName() + "/"));
65 | }
66 | }
67 |
68 |
69 | public boolean isBase64Resource() {
70 | return mUri != null && FastImageSource.isBase64Uri(mUri);
71 | }
72 |
73 | public boolean isResource() {
74 | return mUri != null && FastImageSource.isResourceUri(mUri);
75 | }
76 |
77 | public boolean isLocalFile() {
78 | return mUri != null && FastImageSource.isLocalFileUri(mUri);
79 | }
80 |
81 | public boolean isContentUri() {
82 | return mUri != null && FastImageSource.isContentUri(mUri);
83 | }
84 |
85 | public Object getSourceForLoad() {
86 | if (isContentUri()) {
87 | return getSource();
88 | }
89 | if (isBase64Resource()) {
90 | return getSource();
91 | }
92 | if (isResource()) {
93 | return getUri();
94 | }
95 | if (isLocalFile()) {
96 | return getUri().toString();
97 | }
98 | return getGlideUrl();
99 | }
100 |
101 | @Override
102 | @Nullable
103 | public Uri getUri() {
104 | return mUri;
105 | }
106 |
107 | public Headers getHeaders() {
108 | return mHeaders;
109 | }
110 |
111 | public GlideUrl getGlideUrl() {
112 | Uri uri = getUri();
113 | try {
114 | if (uri != null && !getUri().toString().equals("")) {
115 | return new GlideUrl(getUri().toString(), getHeaders());
116 | } else {
117 | return new GlideUrl(Glide, getHeaders());
118 | }
119 | } catch (NullPointerException e) {
120 | return new GlideUrl(Glide, getHeaders());
121 | }
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/android/src/main/java/com/dylanvann/fastimage/FastImageViewConverter.java:
--------------------------------------------------------------------------------
1 | package com.dylanvann.fastimage;
2 |
3 | import android.content.Context;
4 | import android.graphics.Color;
5 | import android.graphics.drawable.ColorDrawable;
6 | import android.graphics.drawable.Drawable;
7 | import android.widget.ImageView;
8 | import android.widget.ImageView.ScaleType;
9 |
10 | import com.bumptech.glide.Priority;
11 | import com.bumptech.glide.load.engine.DiskCacheStrategy;
12 | import com.bumptech.glide.load.model.Headers;
13 | import com.bumptech.glide.load.model.LazyHeaders;
14 | import com.bumptech.glide.load.resource.bitmap.CenterCrop;
15 | import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
16 | import com.bumptech.glide.request.RequestOptions;
17 | import com.bumptech.glide.signature.ApplicationVersionSignature;
18 | import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
19 | import com.facebook.react.bridge.NoSuchKeyException;
20 | import com.facebook.react.bridge.ReadableMap;
21 | import com.facebook.react.bridge.ReadableMapKeySetIterator;
22 |
23 | import java.util.HashMap;
24 | import java.util.Map;
25 |
26 | import static com.bumptech.glide.request.RequestOptions.signatureOf;
27 |
28 | class FastImageViewConverter {
29 | private static final Drawable TRANSPARENT_DRAWABLE = new ColorDrawable(Color.TRANSPARENT);
30 |
31 | private static final Map FAST_IMAGE_CACHE_CONTROL_MAP =
32 | new HashMap() {{
33 | put("immutable", FastImageCacheControl.IMMUTABLE);
34 | put("web", FastImageCacheControl.WEB);
35 | put("cacheOnly", FastImageCacheControl.CACHE_ONLY);
36 | }};
37 |
38 | private static final Map FAST_IMAGE_PRIORITY_MAP =
39 | new HashMap() {{
40 | put("low", Priority.LOW);
41 | put("normal", Priority.NORMAL);
42 | put("high", Priority.HIGH);
43 | }};
44 |
45 | private static final Map FAST_IMAGE_RESIZE_MODE_MAP =
46 | new HashMap() {{
47 | put("contain", ScaleType.FIT_CENTER);
48 | put("cover", ScaleType.CENTER_CROP);
49 | put("stretch", ScaleType.FIT_XY);
50 | put("center", ScaleType.CENTER_INSIDE);
51 | }};
52 |
53 | // Resolve the source uri to a file path that android understands.
54 | static FastImageSource getImageSource(Context context, ReadableMap source) {
55 | return new FastImageSource(context, source.getString("uri"), getHeaders(source));
56 | }
57 |
58 | static Headers getHeaders(ReadableMap source) {
59 | Headers headers = Headers.DEFAULT;
60 |
61 | if (source.hasKey("headers")) {
62 | ReadableMap headersMap = source.getMap("headers");
63 | ReadableMapKeySetIterator iterator = headersMap.keySetIterator();
64 | LazyHeaders.Builder builder = new LazyHeaders.Builder();
65 |
66 | while (iterator.hasNextKey()) {
67 | String header = iterator.nextKey();
68 | String value = headersMap.getString(header);
69 |
70 | builder.addHeader(header, value);
71 | }
72 |
73 | headers = builder.build();
74 | }
75 |
76 | return headers;
77 | }
78 |
79 | static RequestOptions getOptions(Context context, FastImageSource imageSource, ReadableMap source) {
80 | // Get priority.
81 | final Priority priority = FastImageViewConverter.getPriority(source);
82 | // Get cache control method.
83 | final FastImageCacheControl cacheControl = FastImageViewConverter.getCacheControl(source);
84 | DiskCacheStrategy diskCacheStrategy = DiskCacheStrategy.AUTOMATIC;
85 | boolean onlyFromCache = false;
86 | switch (cacheControl) {
87 | case CACHE_ONLY:
88 | onlyFromCache = true;
89 | break;
90 | case WEB:
91 | case IMMUTABLE:
92 | // Use defaults.
93 | break;
94 | }
95 |
96 | RequestOptions options = new RequestOptions()
97 | .diskCacheStrategy(diskCacheStrategy)
98 | .onlyRetrieveFromCache(onlyFromCache)
99 | .priority(priority)
100 | .placeholder(TRANSPARENT_DRAWABLE);
101 |
102 | if (imageSource.isResource()) {
103 | // Every local resource (drawable) in Android has its own unique numeric id, which are
104 | // generated at build time. Although these ids are unique, they are not guaranteed unique
105 | // across builds. The underlying glide implementation caches these resources. To make
106 | // sure the cache does not return the wrong image, we should clear the cache when the
107 | // application version changes. Adding a cache signature for only these local resources
108 | // solves this issue: https://github.com/DylanVann/react-native-fast-image/issues/402
109 | options = options.apply(signatureOf(ApplicationVersionSignature.obtain(context)));
110 | }
111 |
112 | if (source.hasKey("borderRadius")) {
113 | int borderRadius = source.getInt("borderRadius");
114 | options = options.transforms(new CenterCrop(), new RoundedCorners(borderRadius));
115 | }
116 |
117 | return options;
118 | }
119 |
120 | private static FastImageCacheControl getCacheControl(ReadableMap source) {
121 | return getValueFromSource("cache", "immutable", FAST_IMAGE_CACHE_CONTROL_MAP, source);
122 | }
123 |
124 | private static Priority getPriority(ReadableMap source) {
125 | return getValueFromSource("priority", "normal", FAST_IMAGE_PRIORITY_MAP, source);
126 | }
127 |
128 | static ScaleType getScaleType(String propValue) {
129 | return getValue("resizeMode", "cover", FAST_IMAGE_RESIZE_MODE_MAP, propValue);
130 | }
131 |
132 | private static T getValue(String propName, String defaultPropValue, Map map, String propValue) {
133 | if (propValue == null) propValue = defaultPropValue;
134 | T value = map.get(propValue);
135 | if (value == null)
136 | throw new JSApplicationIllegalArgumentException("FastImage, invalid " + propName + " : " + propValue);
137 | return value;
138 | }
139 |
140 | private static T getValueFromSource(String propName, String defaultProp, Map map, ReadableMap source) {
141 | String propValue;
142 | try {
143 | propValue = source != null ? source.getString(propName) : null;
144 | } catch (NoSuchKeyException e) {
145 | propValue = null;
146 | }
147 | return getValue(propName, defaultProp, map, propValue);
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/android/src/main/java/com/dylanvann/fastimage/FastImageViewModule.java:
--------------------------------------------------------------------------------
1 | package com.dylanvann.fastimage;
2 |
3 | import android.app.Activity;
4 |
5 | import com.bumptech.glide.Glide;
6 | import com.bumptech.glide.load.model.GlideUrl;
7 | import com.facebook.react.bridge.Promise;
8 | import com.facebook.react.bridge.ReactApplicationContext;
9 | import com.facebook.react.bridge.ReactContextBaseJavaModule;
10 | import com.facebook.react.bridge.ReactMethod;
11 | import com.facebook.react.bridge.ReadableArray;
12 | import com.facebook.react.bridge.ReadableMap;
13 | import com.facebook.react.views.imagehelper.ImageSource;
14 |
15 | class FastImageViewModule extends ReactContextBaseJavaModule {
16 |
17 | private static final String REACT_CLASS = "FastImageView";
18 |
19 | FastImageViewModule(ReactApplicationContext reactContext) {
20 | super(reactContext);
21 | }
22 |
23 | @Override
24 | public String getName() {
25 | return REACT_CLASS;
26 | }
27 |
28 | @ReactMethod
29 | public void preload(final ReadableArray sources) {
30 | final Activity activity = getCurrentActivity();
31 | if (activity == null) return;
32 | activity.runOnUiThread(new Runnable() {
33 | @Override
34 | public void run() {
35 | for (int i = 0; i < sources.size(); i++) {
36 | final ReadableMap source = sources.getMap(i);
37 | final FastImageSource imageSource = FastImageViewConverter.getImageSource(activity, source);
38 |
39 | Glide
40 | .with(activity.getApplicationContext())
41 | // This will make this work for remote and local images. e.g.
42 | // - file:///
43 | // - content://
44 | // - res:/
45 | // - android.resource://
46 | // - data:image/png;base64
47 | .load(
48 | imageSource.isBase64Resource() ? imageSource.getSource() :
49 | imageSource.isResource() ? imageSource.getUri() : imageSource.getGlideUrl()
50 | )
51 | .apply(FastImageViewConverter.getOptions(activity, imageSource, source))
52 | .preload();
53 | }
54 | }
55 | });
56 | }
57 |
58 | @ReactMethod
59 | public void clearMemoryCache(final Promise promise) {
60 | final Activity activity = getCurrentActivity();
61 | if (activity == null) {
62 | promise.resolve(null);
63 | return;
64 | }
65 |
66 | activity.runOnUiThread(new Runnable() {
67 | @Override
68 | public void run() {
69 | Glide.get(activity.getApplicationContext()).clearMemory();
70 | promise.resolve(null);
71 | }
72 | });
73 | }
74 |
75 | @ReactMethod
76 | public void clearDiskCache(Promise promise) {
77 | final Activity activity = getCurrentActivity();
78 | if (activity == null) {
79 | promise.resolve(null);
80 | return;
81 | }
82 |
83 | Glide.get(activity.getApplicationContext()).clearDiskCache();
84 | promise.resolve(null);
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/android/src/main/java/com/dylanvann/fastimage/FastImageViewPackage.java:
--------------------------------------------------------------------------------
1 | package com.dylanvann.fastimage;
2 |
3 | import com.facebook.react.ReactPackage;
4 | import com.facebook.react.bridge.NativeModule;
5 | import com.facebook.react.bridge.ReactApplicationContext;
6 | import com.facebook.react.uimanager.ViewManager;
7 |
8 | import java.util.Collections;
9 | import java.util.List;
10 |
11 | public class FastImageViewPackage implements ReactPackage {
12 | @Override
13 | public List createNativeModules(ReactApplicationContext reactContext) {
14 | return Collections.singletonList(new FastImageViewModule(reactContext));
15 | }
16 |
17 | @Override
18 | public List createViewManagers(ReactApplicationContext reactContext) {
19 | return Collections.singletonList(new FastImageViewManager());
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/android/src/main/java/com/dylanvann/fastimage/FastImageViewWithUrl.java:
--------------------------------------------------------------------------------
1 | package com.dylanvann.fastimage;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.appcompat.widget.AppCompatImageView;
6 |
7 | import com.bumptech.glide.load.model.GlideUrl;
8 | import com.facebook.react.bridge.ReadableMap;
9 |
10 | class FastImageViewWithUrl extends AppCompatImageView {
11 | public GlideUrl glideUrl;
12 | public ReadableMap source;
13 |
14 | public FastImageViewWithUrl(Context context) {
15 | super(context);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/android/src/main/java/com/dylanvann/fastimage/custom/EtagCallback.java:
--------------------------------------------------------------------------------
1 | package com.dylanvann.fastimage.custom;
2 |
3 | public interface EtagCallback {
4 | void onEtag(String etag);
5 | void onError(String error);
6 | }
7 |
--------------------------------------------------------------------------------
/android/src/main/java/com/dylanvann/fastimage/custom/EtagRequester.java:
--------------------------------------------------------------------------------
1 | package com.dylanvann.fastimage.custom;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import com.dylanvann.fastimage.custom.persistence.ObjectBox;
6 |
7 | import java.io.IOException;
8 |
9 | import javax.annotation.Nullable;
10 |
11 | import okhttp3.Call;
12 | import okhttp3.Callback;
13 | import okhttp3.OkHttpClient;
14 | import okhttp3.Request;
15 | import okhttp3.Response;
16 |
17 | public class EtagRequester {
18 | /**
19 | * Requests etag at the server. Won't call callback on failure or if
20 | * etas hasn't changed.
21 | * @param url
22 | * @param callback
23 | */
24 | public static void requestEtag(@NonNull final String url, @Nullable final String prevEtag, @NonNull final EtagCallback callback) {
25 | OkHttpClient client = SharedOkHttpClient.getInstance(null).getClient();
26 | Request.Builder request = new Request.Builder()
27 | .url(url)
28 | .head();
29 |
30 | if (prevEtag != null) {
31 | request.addHeader("If-None-Match", prevEtag);
32 | }
33 |
34 | client.newCall(request.build()).enqueue(new Callback() {
35 | @Override
36 | public void onFailure(Call call, IOException e) {
37 | e.printStackTrace();
38 | // we only want to report an error when we have no etag
39 | // and the etag request failed
40 | if (prevEtag == null) {
41 | callback.onError("Failure when requesting etag: " + e.getMessage());
42 | } else {
43 | callback.onEtag(prevEtag);
44 | }
45 | }
46 |
47 | @Override
48 | public void onResponse(Call call, Response response) {
49 | if (response.code() == 200) {
50 | String etag = response.header("etag");
51 | callback.onEtag(etag);
52 | } else if (response.code() > 308) {
53 | callback.onError("Unexpected http code: " + response.code());
54 | } else {
55 | callback.onEtag(prevEtag);
56 | }
57 | }
58 | });
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/android/src/main/java/com/dylanvann/fastimage/custom/SharedOkHttpClient.java:
--------------------------------------------------------------------------------
1 | package com.dylanvann.fastimage.custom;
2 |
3 | import android.content.Context;
4 | import android.util.Log;
5 |
6 | import com.dylanvann.fastimage.custom.persistence.ObjectBox;
7 |
8 | import java.io.File;
9 |
10 | import okhttp3.Cache;
11 | import okhttp3.OkHttpClient;
12 | import okhttp3.Request;
13 | import okhttp3.Response;
14 |
15 | public class SharedOkHttpClient {
16 | private static SharedOkHttpClient instance;
17 |
18 | private final okhttp3.OkHttpClient client;
19 |
20 | private SharedOkHttpClient(Context context) {
21 | this.client = new okhttp3.OkHttpClient.Builder()
22 | .cache(new Cache(
23 | new File(context.getCacheDir(), "http_cache"),
24 | 50L * 1024L * 1024L // 50 MiB
25 | ))
26 | // Add an interceptor that will keep our etag up2date
27 | .addInterceptor(chain -> {
28 | Request request = chain.request();
29 |
30 | // Note: we don't add the the etag to the request
31 | // Cache invalidation is handled on Glides signature level
32 | // (See FastImageViewManager)
33 | Response response = chain.proceed(request);
34 |
35 | String url = request.url().toString();
36 | String prevEtag = ObjectBox.getEtagByUrl(url);
37 |
38 |
39 | // update etag if changes
40 | String responseEtag = response.header("etag");
41 | if (responseEtag != null && !responseEtag.equals(prevEtag)) {
42 | ObjectBox.putOrUpdateEtag(url, responseEtag);
43 | }
44 |
45 | return response;
46 | })
47 | .build();
48 | }
49 |
50 | public static SharedOkHttpClient getInstance(Context context) {
51 | if (instance == null) {
52 | instance = new SharedOkHttpClient(context);
53 | }
54 | return instance;
55 | }
56 |
57 | public OkHttpClient getClient() {
58 | return client;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/android/src/main/java/com/dylanvann/fastimage/custom/persistence/EntityEtagCache.java:
--------------------------------------------------------------------------------
1 | package com.dylanvann.fastimage.custom.persistence;
2 |
3 | import io.objectbox.annotation.Entity;
4 | import io.objectbox.annotation.Id;
5 |
6 | @Entity
7 | public class EntityEtagCache {
8 | @Id
9 | public long id;
10 |
11 | public String url;
12 | public String etag;
13 | }
14 |
--------------------------------------------------------------------------------
/android/src/main/java/com/dylanvann/fastimage/custom/persistence/ObjectBox.java:
--------------------------------------------------------------------------------
1 | package com.dylanvann.fastimage.custom.persistence;
2 |
3 | import android.content.Context;
4 |
5 | import io.objectbox.Box;
6 | import io.objectbox.BoxStore;
7 |
8 | public class ObjectBox {
9 | private static BoxStore boxStore;
10 |
11 | public static void init(Context context) {
12 | if (boxStore == null) {
13 | boxStore = MyObjectBox.builder()
14 | .androidContext(context.getApplicationContext())
15 | .build();
16 | }
17 | }
18 |
19 | public static BoxStore get() { return boxStore; }
20 |
21 | public static Box getBoxEtagCache() {
22 | return get().boxFor(EntityEtagCache.class);
23 | }
24 |
25 | public static EntityEtagCache getEtagCacheEntityByUrl(String url) {
26 | return getBoxEtagCache().query().equal(EntityEtagCache_.url, url).build().findFirst();
27 | }
28 |
29 | public static String getEtagByUrl(String url) {
30 | EntityEtagCache entry = getEtagCacheEntityByUrl(url);
31 | if (entry != null) {
32 | return entry.etag;
33 | }
34 | return null;
35 | }
36 |
37 | public static void putOrUpdateEtag(String url, String etag) {
38 | EntityEtagCache entry = getEtagCacheEntityByUrl(url);
39 | if (entry != null) {
40 | entry.etag = etag;
41 | } else {
42 | entry = new EntityEtagCache();
43 | entry.etag = etag;
44 | entry.url = url;
45 | }
46 | getBoxEtagCache().put(entry);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['module:metro-react-native-babel-preset'],
3 | }
4 |
--------------------------------------------------------------------------------
/docs/app-glide-module.md:
--------------------------------------------------------------------------------
1 | # Removing MyAppGlideModule from react-native-fast-image
2 |
3 | If you are using Glide within your application using an `AppGlideModule` then you will
4 | need to prevent the inclusion of the `AppGlideModule` in this package.
5 |
6 | To accomplish this you can add to `android/build.gradle`:
7 |
8 | ```gradle
9 | project.ext {
10 | excludeAppGlideModule = true
11 | }
12 | ```
--------------------------------------------------------------------------------
/docs/assets/priority.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuvent/react-native-fast-image/a661be6bb65029b3f1750fed9b2f793949df6152/docs/assets/priority.gif
--------------------------------------------------------------------------------
/docs/assets/scroll.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuvent/react-native-fast-image/a661be6bb65029b3f1750fed9b2f793949df6152/docs/assets/scroll.gif
--------------------------------------------------------------------------------
/docs/development.md:
--------------------------------------------------------------------------------
1 | # Development
2 |
3 | For now this uses a modified cli to work around issues with symlinked packages.
4 |
5 | This is how to start the example app so you can test code with it.
6 |
7 | ```bash
8 | # In the repo root folder.
9 | # Install dependencies.
10 | yarn
11 |
12 | # Link module.
13 | yarn link
14 |
15 | # Move to example folder.
16 | cd ReactNativeFastImageExample
17 |
18 | # Install dependencies.
19 | yarn
20 |
21 | # Link module.
22 | yarn link react-native-fast-image
23 |
24 | # Start packager.
25 | yarn start
26 |
27 | # Start the iOS app.
28 | yarn react-native run-ios
29 | # Start the android app.
30 | yarn react-native run-android
31 | # You will need to re-run those commands to re-compile native code.
32 | ```
--------------------------------------------------------------------------------
/docs/how-is-caching-handled.md:
--------------------------------------------------------------------------------
1 | # How is caching handled?
2 |
3 | In the readme it says "Aggressively cache images.". What does this mean?
4 |
5 | This library treats image urls as immutable.
6 | That means it assumes the data located at a given url will not change.
7 | This is ideal for performance.
8 |
9 | The way this would work in practice for something like a user profile picture is:
10 |
11 | - Request user from API.
12 | - Receive JSON representing the user containing a `profilePicture` property that is the url of the profile picture.
13 | - Display the profile picture.
14 |
15 | So what happens if the user wants to change their profile picture?
16 |
17 | - User uploads a new profile picture, it gets a new url on the backend.
18 | - Update a field in a database.
19 |
20 | Next time the app is opened:
21 |
22 | - Display the cached profile picture immediately.
23 | - Request the user json again (this time it will have the new profile picture url).
24 | - Display the new profile picture.
25 |
26 | ## How is the cache cleared?
27 |
28 | As the app is used the cache fills up. When the cache reaches its maximum size the least frequently used images will be purged from the cache. You generally do not need to manually manage the cache.
29 |
--------------------------------------------------------------------------------
/docs/other-android-versions.md:
--------------------------------------------------------------------------------
1 | # Other Android Versions
2 |
3 | If you are not on a clean React Native install you may need to change the versions of some things this library uses.
4 |
5 | If you've defined
6 | _[project-wide-properties](https://developer.android.com/studio/build/gradle-tips.html)_(**recommended**)
7 | in your root `build.gradle`, this library will detect the presence of the following properties:
8 |
9 | ```groovy
10 | buildscript {...}
11 | allprojects {...}
12 |
13 | /**
14 | + Project-wide Gradle configuration properties
15 | */
16 | ext {
17 | // You can use any of these to change project wide versions:
18 | // compileSdkVersion = 26
19 | // targetSdkVersion = 26
20 | // minSdkVersion = 16
21 | // buildToolsVersion = "26.0.3"
22 | // supportLibVersion = "27.1.1"
23 | // glideVersion = "4.7.1"
24 | }
25 | ```
26 |
--------------------------------------------------------------------------------
/docs/roadmap.md:
--------------------------------------------------------------------------------
1 | # Roadmap / Ideas
2 |
3 | ## Add `onProgress` and `onComplete` to preload.
4 |
5 | - [Add onProgress and onComplete to preload.](https://github.com/DylanVann/react-native-fast-image/pull/268)
6 | - Blocked by: [Consider switching to a different iOS image loading library.](https://github.com/DylanVann/react-native-fast-image/issues/13)
7 | - Preload API should include returning the cache path of the images.
8 | - Something like `.preload(images: {uri, ...otherOptions}[]): Promise<[{path: string}]>`
9 | - Maybe something like: [Make it possible to obtain the cache path of an image.](https://github.com/DylanVann/react-native-fast-image/pull/351)
10 |
11 | ## Add `blurRadius` prop.
12 |
13 | - [Add blurRadius property.](https://github.com/DylanVann/react-native-fast-image/pull/157)
14 | - Blocked by: needing an Android implementation that works the same.
15 |
16 | ## Add more information to `onError` callback.
17 |
18 | - We need standardized errors across iOS and Android.
19 |
--------------------------------------------------------------------------------
/docs/troubleshooting.md:
--------------------------------------------------------------------------------
1 | # Troubleshooting
2 |
3 | If you have problems you can try:
4 |
5 | - Running Clean in Xcode.
6 | - Deleting Xcode's derived data.
7 | - Removing `node_modules` then reinstalling dependencies. (`rm -rf node_modules && yarn`)
8 | - Clearing watchman's watches. (`watchman watch-del-all`)
9 | - Clearing React Native's packager cache. (`react-native start --reset-cache`)
10 | - Clearing React Native's iOS build folder. (`rm -rf ios/build`)
11 | - Updating Pod repos. (`cd ios && pod repo update`)
12 | - Reinstalling Pods. (`cd ios && pod install`)
13 |
--------------------------------------------------------------------------------
/ios/FastImage.xcodeproj/xcshareddata/xcschemes/FastImage-tvOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
34 |
35 |
45 |
46 |
52 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
70 |
71 |
72 |
73 |
75 |
76 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/ios/FastImage/FFFastImageSource.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | typedef NS_ENUM(NSInteger, FFFPriority) {
5 | FFFPriorityLow,
6 | FFFPriorityNormal,
7 | FFFPriorityHigh
8 | };
9 |
10 | typedef NS_ENUM(NSInteger, FFFCacheControl) {
11 | FFFCacheControlImmutable,
12 | FFFCacheControlWeb,
13 | FFFCacheControlCacheOnly
14 | };
15 |
16 | // Object containing an image uri and metadata.
17 | @interface FFFastImageSource : NSObject
18 |
19 | // uri for image, or base64
20 | @property (nonatomic) NSURL* url;
21 | // priority for image request
22 | @property (nonatomic) FFFPriority priority;
23 | // headers for the image request
24 | @property (nonatomic) NSDictionary *headers;
25 | // cache control mode
26 | @property (nonatomic) FFFCacheControl cacheControl;
27 |
28 | - (instancetype)initWithURL:(NSURL *)url
29 | priority:(FFFPriority)priority
30 | headers:(NSDictionary *)headers
31 | cacheControl:(FFFCacheControl)cacheControl;
32 |
33 | @end
34 |
--------------------------------------------------------------------------------
/ios/FastImage/FFFastImageSource.m:
--------------------------------------------------------------------------------
1 | #import "FFFastImageSource.h"
2 |
3 | @implementation FFFastImageSource
4 |
5 | - (instancetype)initWithURL:(NSURL *)url
6 | priority:(FFFPriority)priority
7 | headers:(NSDictionary *)headers
8 | cacheControl:(FFFCacheControl)cacheControl
9 | {
10 | self = [super init];
11 | if (self) {
12 | _url = url;
13 | _priority = priority;
14 | _headers = headers;
15 | _cacheControl = cacheControl;
16 | }
17 | return self;
18 | }
19 |
20 | @end
21 |
--------------------------------------------------------------------------------
/ios/FastImage/FFFastImageView.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | #import
4 | #import
5 |
6 | #import
7 | #import
8 |
9 | #import "FFFastImageSource.h"
10 |
11 | @interface FFFastImageView : SDAnimatedImageView
12 |
13 | @property (nonatomic, copy) RCTDirectEventBlock onFastImageLoadStart;
14 | @property (nonatomic, copy) RCTDirectEventBlock onFastImageProgress;
15 | @property (nonatomic, copy) RCTDirectEventBlock onFastImageError;
16 | @property (nonatomic, copy) RCTDirectEventBlock onFastImageLoad;
17 | @property (nonatomic, copy) RCTDirectEventBlock onFastImageLoadEnd;
18 | @property (nonatomic, assign) RCTResizeMode resizeMode;
19 | @property (nonatomic, strong) FFFastImageSource *source;
20 | @property (nonatomic, strong) UIColor *imageColor;
21 |
22 | - (id)initWithManager:(SDWebImageManager*)manager;
23 | - (void)reloadImage;
24 |
25 | @end
26 |
27 |
--------------------------------------------------------------------------------
/ios/FastImage/FFFastImageView.m:
--------------------------------------------------------------------------------
1 | #import "FFFastImageView.h"
2 | #import
3 | #import
4 | #import
5 | #import
6 | #import
7 |
8 | @interface FFFastImageView()
9 |
10 | @property (nonatomic, assign) BOOL hasSentOnLoadStart;
11 | @property (nonatomic, assign) BOOL hasCompleted;
12 | @property (nonatomic, assign) BOOL hasErrored;
13 | // Whether the latest change of props requires the image to be reloaded
14 | @property (nonatomic, assign) BOOL needsReload;
15 |
16 | @property (nonatomic, strong) NSDictionary* onLoadEvent;
17 | @property (nonatomic, strong) SDWebImageManager* manager;
18 |
19 | @end
20 |
21 | @implementation FFFastImageView
22 |
23 | - (id) initWithManager:(SDWebImageManager*)manager {
24 | self = [super init];
25 | self.resizeMode = RCTResizeModeCover;
26 | self.clipsToBounds = YES;
27 | self.manager = manager;
28 | return self;
29 | }
30 |
31 | - (void)setResizeMode:(RCTResizeMode)resizeMode {
32 | if (_resizeMode != resizeMode) {
33 | _resizeMode = resizeMode;
34 | self.contentMode = (UIViewContentMode)resizeMode;
35 | }
36 | }
37 |
38 | - (void)setOnFastImageLoadEnd:(RCTDirectEventBlock)onFastImageLoadEnd {
39 | _onFastImageLoadEnd = onFastImageLoadEnd;
40 | if (self.hasCompleted && _onFastImageLoadEnd != NULL) {
41 | _onFastImageLoadEnd(@{});
42 | }
43 | }
44 |
45 | - (void)setOnFastImageLoad:(RCTDirectEventBlock)onFastImageLoad {
46 | _onFastImageLoad = onFastImageLoad;
47 | if (self.hasCompleted) {
48 | _onFastImageLoad(self.onLoadEvent);
49 | }
50 | }
51 |
52 | - (void)setOnFastImageError:(RCTDirectEventBlock)onFastImageError {
53 | _onFastImageError = onFastImageError;
54 | if (self.hasErrored) {
55 | _onFastImageError(@{});
56 | }
57 | }
58 |
59 | - (void)setOnFastImageLoadStart:(RCTDirectEventBlock)onFastImageLoadStart {
60 | if (_source && !self.hasSentOnLoadStart) {
61 | _onFastImageLoadStart = onFastImageLoadStart;
62 | onFastImageLoadStart(@{});
63 | self.hasSentOnLoadStart = YES;
64 | } else {
65 | _onFastImageLoadStart = onFastImageLoadStart;
66 | self.hasSentOnLoadStart = NO;
67 | }
68 | }
69 |
70 | - (void)setImageColor:(UIColor *)imageColor {
71 | if (imageColor != nil) {
72 | _imageColor = imageColor;
73 | super.image = [self makeImage:super.image withTint:self.imageColor];
74 | }
75 | }
76 |
77 | - (UIImage*)makeImage:(UIImage *)image withTint:(UIColor *)color {
78 | UIImage *newImage = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
79 | UIGraphicsBeginImageContextWithOptions(image.size, NO, newImage.scale);
80 | [color set];
81 | [newImage drawInRect:CGRectMake(0, 0, image.size.width, newImage.size.height)];
82 | newImage = UIGraphicsGetImageFromCurrentImageContext();
83 | UIGraphicsEndImageContext();
84 | return newImage;
85 | }
86 |
87 | - (void)setImage:(UIImage *)image {
88 | if (self.imageColor != nil) {
89 | super.image = [self makeImage:image withTint:self.imageColor];
90 | } else {
91 | super.image = image;
92 | }
93 | }
94 |
95 | - (void)sendOnLoad:(UIImage *)image {
96 | self.onLoadEvent = @{
97 | @"width":[NSNumber numberWithDouble:image.size.width],
98 | @"height":[NSNumber numberWithDouble:image.size.height]
99 | };
100 | if (self.onFastImageLoad) {
101 | self.onFastImageLoad(self.onLoadEvent);
102 | }
103 | }
104 |
105 | - (void)setSource:(FFFastImageSource *)source {
106 | if (_source != source) {
107 | _source = source;
108 | _needsReload = YES;
109 | }
110 | }
111 |
112 | - (void)didSetProps:(NSArray *)changedProps
113 | {
114 | if (_needsReload) {
115 | [self reloadImage];
116 | }
117 | }
118 |
119 |
120 | - (void)reloadImage
121 | {
122 | _needsReload = NO;
123 |
124 | if (_source) {
125 |
126 | // Load base64 images.
127 | NSString* url = [_source.url absoluteString];
128 | if (url && [url hasPrefix:@"data:image"]) {
129 | if (self.onFastImageLoadStart) {
130 | self.onFastImageLoadStart(@{});
131 | self.hasSentOnLoadStart = YES;
132 | } {
133 | self.hasSentOnLoadStart = NO;
134 | }
135 | // Use SDWebImage API to support external format like WebP images
136 | UIImage *image = [UIImage sd_imageWithData:[NSData dataWithContentsOfURL:_source.url]];
137 | [self setImage:image];
138 | if (self.onFastImageProgress) {
139 | self.onFastImageProgress(@{
140 | @"loaded": @(1),
141 | @"total": @(1)
142 | });
143 | }
144 | self.hasCompleted = YES;
145 | [self sendOnLoad:image];
146 |
147 | if (self.onFastImageLoadEnd) {
148 | self.onFastImageLoadEnd(@{});
149 | }
150 | return;
151 | }
152 |
153 | // Set headers.
154 | NSDictionary *headers = _source.headers;
155 | SDWebImageDownloaderRequestModifier *requestModifier = [SDWebImageDownloaderRequestModifier requestModifierWithBlock:^NSURLRequest * _Nullable(NSURLRequest * _Nonnull request) {
156 | NSMutableURLRequest *mutableRequest = [request mutableCopy];
157 | for (NSString *header in headers) {
158 | NSString *value = headers[header];
159 | [mutableRequest setValue:value forHTTPHeaderField:header];
160 | }
161 | return [mutableRequest copy];
162 | }];
163 | SDWebImageContext *context = @{SDWebImageContextDownloadRequestModifier : requestModifier};
164 |
165 | // Set priority.
166 | SDWebImageOptions options = SDWebImageRetryFailed | SDWebImageHandleCookies;
167 | switch (_source.priority) {
168 | case FFFPriorityLow:
169 | options |= SDWebImageLowPriority;
170 | break;
171 | case FFFPriorityNormal:
172 | // Priority is normal by default.
173 | break;
174 | case FFFPriorityHigh:
175 | options |= SDWebImageHighPriority;
176 | break;
177 | }
178 |
179 | switch (_source.cacheControl) {
180 | case FFFCacheControlWeb:
181 | options |= SDWebImageRefreshCached;
182 | break;
183 | case FFFCacheControlCacheOnly:
184 | options |= SDWebImageFromCacheOnly;
185 | break;
186 | case FFFCacheControlImmutable:
187 | break;
188 | }
189 |
190 | if (self.onFastImageLoadStart) {
191 | self.onFastImageLoadStart(@{});
192 | self.hasSentOnLoadStart = YES;
193 | } {
194 | self.hasSentOnLoadStart = NO;
195 | }
196 | self.hasCompleted = NO;
197 | self.hasErrored = NO;
198 |
199 | [self downloadImage:_source options:options context:context];
200 | }
201 | }
202 |
203 | - (void)downloadImage:(FFFastImageSource *) source options:(SDWebImageOptions) options context:(SDWebImageContext *)context {
204 | __weak typeof(self) weakSelf = self; // Always use a weak reference to self in blocks
205 | NSLog(@"%@", [NSString stringWithFormat:@"REQUEST FOR %@", _source.url.absoluteString]);
206 | BOOL isPhotoAsset = [_source.url.absoluteString.lowercaseString hasPrefix:@"ph://"];
207 | [self sd_setImageWithURL:_source.url
208 | placeholderImage:nil
209 | //the following two properties are loading a photo asset according to the image's view size: https://github.com/SDWebImage/SDWebImagePhotosPlugin#control-query-image-size
210 | options:(isPhotoAsset) ? SDWebImageRefreshCached : options
211 | context:(isPhotoAsset) ? @{SDWebImageContextImageThumbnailPixelSize: @(self.bounds.size), SDWebImageContextCustomManager: self.manager} : context
212 | progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) {
213 | if (weakSelf.onFastImageProgress) {
214 | weakSelf.onFastImageProgress(@{
215 | @"loaded": @(receivedSize),
216 | @"total": @(expectedSize)
217 | });
218 | }
219 | } completed:^(UIImage * _Nullable image,
220 | NSError * _Nullable error,
221 | SDImageCacheType cacheType,
222 | NSURL * _Nullable imageURL) {
223 | if (error) {
224 | weakSelf.hasErrored = YES;
225 | if (weakSelf.onFastImageError) {
226 | weakSelf.onFastImageError(@{});
227 | }
228 | if (weakSelf.onFastImageLoadEnd) {
229 | weakSelf.onFastImageLoadEnd(@{});
230 | }
231 | } else {
232 | weakSelf.hasCompleted = YES;
233 | [weakSelf sendOnLoad:image];
234 | if (weakSelf.onFastImageLoadEnd) {
235 | weakSelf.onFastImageLoadEnd(@{});
236 | }
237 | }
238 | }];
239 | }
240 |
241 | - (void)dealloc {
242 | [self sd_cancelCurrentImageLoad];
243 | }
244 |
245 | @end
246 |
--------------------------------------------------------------------------------
/ios/FastImage/FFFastImageViewManager.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface FFFastImageViewManager : RCTViewManager
4 |
5 | @end
6 |
--------------------------------------------------------------------------------
/ios/FastImage/FFFastImageViewManager.m:
--------------------------------------------------------------------------------
1 | #import "FFFastImageViewManager.h"
2 | #import "FFFastImageView.h"
3 |
4 | #import
5 | #import
6 | #import
7 | #import
8 | #import
9 | #import
10 | #import
11 |
12 | @interface FFFastImageViewManager()
13 |
14 | @property (nonatomic, strong) SDWebImageManager* manager;
15 |
16 | @end
17 |
18 | @implementation FFFastImageViewManager
19 |
20 | RCT_EXPORT_MODULE(FastImageView)
21 |
22 | - (instancetype)init {
23 | self = [super init];
24 | self.manager = [[SDWebImageManager alloc] initWithCache:SDImageCache.sharedImageCache loader:SDImagePhotosLoader.sharedLoader];
25 | return self;
26 | }
27 |
28 | - (FFFastImageView*)view {
29 | return [[FFFastImageView alloc] initWithManager:self.manager];
30 | }
31 |
32 | + (BOOL)requiresMainQueueSetup {
33 | return YES;
34 | }
35 |
36 | RCT_EXPORT_VIEW_PROPERTY(source, FFFastImageSource)
37 | RCT_EXPORT_VIEW_PROPERTY(resizeMode, RCTResizeMode)
38 | RCT_EXPORT_VIEW_PROPERTY(onFastImageLoadStart, RCTDirectEventBlock)
39 | RCT_EXPORT_VIEW_PROPERTY(onFastImageProgress, RCTDirectEventBlock)
40 | RCT_EXPORT_VIEW_PROPERTY(onFastImageError, RCTDirectEventBlock)
41 | RCT_EXPORT_VIEW_PROPERTY(onFastImageLoad, RCTDirectEventBlock)
42 | RCT_EXPORT_VIEW_PROPERTY(onFastImageLoadEnd, RCTDirectEventBlock)
43 | RCT_REMAP_VIEW_PROPERTY(tintColor, imageColor, UIColor)
44 |
45 | RCT_EXPORT_METHOD(preload:(nonnull NSArray *)sources)
46 | {
47 | NSMutableArray *urls = [NSMutableArray arrayWithCapacity:sources.count];
48 |
49 | [sources enumerateObjectsUsingBlock:^(FFFastImageSource * _Nonnull source, NSUInteger idx, BOOL * _Nonnull stop) {
50 | [source.headers enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString* header, BOOL *stop) {
51 | [[SDWebImageDownloader sharedDownloader] setValue:header forHTTPHeaderField:key];
52 | }];
53 | [urls setObject:source.url atIndexedSubscript:idx];
54 | }];
55 |
56 | [[SDWebImagePrefetcher sharedImagePrefetcher] prefetchURLs:urls];
57 | }
58 |
59 | RCT_EXPORT_METHOD(clearMemoryCache:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
60 | {
61 | [SDImageCache.sharedImageCache clearMemory];
62 | resolve(NULL);
63 | }
64 |
65 | RCT_EXPORT_METHOD(clearDiskCache:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
66 | {
67 | [SDImageCache.sharedImageCache clearDiskOnCompletion:^(){
68 | resolve(NULL);
69 | }];
70 | }
71 |
72 | RCT_EXPORT_METHOD(forceRefreshImage:(nonnull NSNumber*) reactTag)
73 | {
74 | [self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary *viewRegistry) {
75 | UIView* view = viewRegistry[reactTag];
76 | if (!view || ![view isKindOfClass:[FFFastImageView class]]) {
77 | RCTLogError(@"Cannot find NativeView with tag #%@", reactTag);
78 | return;
79 | }
80 | [(FFFastImageView*)view reloadImage];
81 | }];
82 | }
83 |
84 | @end
85 |
--------------------------------------------------------------------------------
/ios/FastImage/RCTConvert+FFFastImage.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @class FFFastImageSource;
4 |
5 | @interface RCTConvert (FFFastImage)
6 |
7 | + (FFFastImageSource *)FFFastImageSource:(id)json;
8 |
9 | @end
10 |
--------------------------------------------------------------------------------
/ios/FastImage/RCTConvert+FFFastImage.m:
--------------------------------------------------------------------------------
1 | #import "RCTConvert+FFFastImage.h"
2 | #import "FFFastImageSource.h"
3 |
4 | @implementation RCTConvert (FFFastImage)
5 |
6 | RCT_ENUM_CONVERTER(FFFPriority, (@{
7 | @"low": @(FFFPriorityLow),
8 | @"normal": @(FFFPriorityNormal),
9 | @"high": @(FFFPriorityHigh),
10 | }), FFFPriorityNormal, integerValue);
11 |
12 | RCT_ENUM_CONVERTER(FFFCacheControl, (@{
13 | @"immutable": @(FFFCacheControlImmutable),
14 | @"web": @(FFFCacheControlWeb),
15 | @"cacheOnly": @(FFFCacheControlCacheOnly),
16 | }), FFFCacheControlImmutable, integerValue);
17 |
18 | + (FFFastImageSource *)FFFastImageSource:(id)json {
19 | if (!json) {
20 | return nil;
21 | }
22 |
23 | NSString *uriString = json[@"uri"];
24 | NSURL *uri = [self NSURL:uriString];
25 |
26 | FFFPriority priority = [self FFFPriority:json[@"priority"]];
27 | FFFCacheControl cacheControl = [self FFFCacheControl:json[@"cache"]];
28 |
29 | NSDictionary *headers = [self NSDictionary:json[@"headers"]];
30 | if (headers) {
31 | __block BOOL allHeadersAreStrings = YES;
32 | [headers enumerateKeysAndObjectsUsingBlock:^(NSString *key, id header, BOOL *stop) {
33 | if (![header isKindOfClass:[NSString class]]) {
34 | RCTLogError(@"Values of HTTP headers passed must be of type string. "
35 | "Value of header '%@' is not a string.", key);
36 | allHeadersAreStrings = NO;
37 | *stop = YES;
38 | }
39 | }];
40 | if (!allHeadersAreStrings) {
41 | // Set headers to nil here to avoid crashing later.
42 | headers = nil;
43 | }
44 | }
45 |
46 | FFFastImageSource *imageSource = [[FFFastImageSource alloc] initWithURL:uri priority:priority headers:headers cacheControl:cacheControl];
47 |
48 | return imageSource;
49 | }
50 |
51 | RCT_ARRAY_CONVERTER(FFFastImageSource);
52 |
53 | @end
54 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@cuvent/react-native-fast-image",
3 | "version": "8.6.0",
4 | "description": "🚩 FastImage, performant React Native image component. Cuvent fork.",
5 | "keywords": [
6 | "cache",
7 | "cached",
8 | "fastimage",
9 | "image",
10 | "priority"
11 | ],
12 | "homepage": "https://github.com/cuvent/react-native-fast-image#readme",
13 | "bugs": {
14 | "url": "https://github.com/cuvent/react-native-fast-image/issues"
15 | },
16 | "repository": {
17 | "type": "git",
18 | "url": "https://github.com/cuvent/react-native-fast-image.git"
19 | },
20 | "license": "(MIT AND Apache-2.0)",
21 | "author": "Dylan Vann (https://dylanvann.com)",
22 | "main": "dist/index.cjs.js",
23 | "module": "dist/index.js",
24 | "typings": "dist/index.d.ts",
25 | "source": "src/index",
26 | "react-native": "src/index",
27 | "files": [
28 | "android",
29 | "src",
30 | "!android/build",
31 | "ios",
32 | "!ios/build",
33 | "dist",
34 | "RNFastImage.podspec",
35 | "!**/*.test.*"
36 | ],
37 | "scripts": {
38 | "build": "dv-scripts build && cp src/index.js.flow dist/index.js.flow && cp src/index.js.flow dist/index.cjs.js.flow",
39 | "lint": "dv-scripts lint",
40 | "release": "dv-scripts release",
41 | "test": "dv-scripts test"
42 | },
43 | "prettier": {
44 | "semi": false,
45 | "singleQuote": true,
46 | "tabWidth": 4,
47 | "trailingComma": "all"
48 | },
49 | "eslintConfig": {
50 | "extends": "dv-scripts"
51 | },
52 | "jest": {
53 | "coveragePathIgnorePatterns": [
54 | "ReactNativeFastImageExample*",
55 | "ReactNativeFastImageExampleServer*"
56 | ],
57 | "modulePathIgnorePatterns": [
58 | "ReactNativeFastImageExample*",
59 | "ReactNativeFastImageExampleServer*"
60 | ],
61 | "preset": "react-native"
62 | },
63 | "resolutions": {
64 | "@jest/create-cache-key-function": "^27"
65 | },
66 | "devDependencies": {
67 | "@babel/core": "^7.14.6",
68 | "@babel/runtime": "^7.14.6",
69 | "@types/jest": "^26.0.24",
70 | "@types/react": "^17.0.14",
71 | "@types/react-native": "^0.64.12",
72 | "@types/react-test-renderer": "^17.0.1",
73 | "dv-scripts": "^1.6.0",
74 | "eslint-config-dv-scripts": "^1.1.1",
75 | "metro-react-native-babel-preset": "^0.66.1",
76 | "prettier": "^2.3.2",
77 | "react": "17.0.2",
78 | "react-native": "0.64.2",
79 | "react-test-renderer": "17.0.2",
80 | "typescript": "^4.3.5"
81 | },
82 | "peerDependencies": {
83 | "react": "^16.8.6 || ^17.0.0",
84 | "react-native": ">=0.60.0"
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/__snapshots__/index.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`FastImage renders correctly. 1`] = `
4 |
17 |
38 |
39 | `;
40 |
41 | exports[`Renders Image with fallback prop. 1`] = `
42 |
55 |
72 |
73 | `;
74 |
75 | exports[`Renders a normal Image when not passed a uri. 1`] = `
76 |
89 |
106 |
107 | `;
108 |
--------------------------------------------------------------------------------
/src/index.js.flow:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | import type { ViewProps } from 'react-native/Libraries/Components/View/ViewPropTypes'
4 | import type { SyntheticEvent } from 'react-native/Libraries/Types/CoreEventTypes'
5 |
6 | export type OnLoadEvent = SyntheticEvent<
7 | $ReadOnly<{
8 | width: number,
9 | height: number,
10 | }>,
11 | >
12 |
13 | export type OnProgressEvent = SyntheticEvent<
14 | $ReadOnly<{|
15 | loaded: number,
16 | total: number,
17 | |}>,
18 | >
19 |
20 | export type ResizeMode = $ReadOnly<{|
21 | contain: 'contain',
22 | cover: 'cover',
23 | stretch: 'stretch',
24 | center: 'center',
25 | |}>
26 |
27 | export type Priority = $ReadOnly<{|
28 | low: 'low',
29 | normal: 'normal',
30 | high: 'high',
31 | |}>
32 |
33 | export type CacheControl = $ReadOnly<{|
34 | immutable: 'immutable',
35 | web: 'web',
36 | cacheOnly: 'cacheOnly',
37 | |}>
38 |
39 | export type ResizeModes = $Values
40 | export type Priorities = $Values
41 | export type CacheControls = $Values
42 |
43 | export type PreloadFn = (sources: Array) => void
44 | export type FastImageSource = {
45 | uri?: string,
46 | headers?: Object,
47 | priority?: Priorities,
48 | cache?: CacheControls,
49 | }
50 |
51 | export type FastImageProps = $ReadOnly<{|
52 | ...ViewProps,
53 | onError?: ?() => void,
54 | onLoad?: ?(event: OnLoadEvent) => void,
55 | onLoadEnd?: ?() => void,
56 | onLoadStart?: ?() => void,
57 | onProgress?: ?(event: OnProgressEvent) => void,
58 |
59 | source: FastImageSource | number,
60 |
61 | resizeMode?: ?ResizeModes,
62 | fallback?: ?boolean,
63 | testID?: ?string,
64 | |}>
65 |
66 | declare export default class FastImage extends React$Component {
67 | static resizeMode: ResizeMode;
68 | static priority: Priority;
69 | static cacheControl: CacheControl;
70 | static preload: PreloadFn;
71 | static clearMemoryCache: () => Promise;
72 | static clearDiskCache: () => Promise;
73 | }
74 |
--------------------------------------------------------------------------------
/src/index.test.tsx:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from 'react-native'
2 | import React from 'react'
3 | import renderer from 'react-test-renderer'
4 | import FastImage from './index'
5 |
6 | const style = StyleSheet.create({ image: { width: 44, height: 44 } })
7 |
8 | test('FastImage renders correctly.', () => {
9 | const tree = renderer
10 | .create(
11 | ,
21 | )
22 | .toJSON()
23 |
24 | expect(tree).toMatchSnapshot()
25 | })
26 |
27 | test('Renders a normal Image when not passed a uri.', () => {
28 | const tree = renderer
29 | .create(
30 | ,
34 | )
35 | .toJSON()
36 |
37 | expect(tree).toMatchSnapshot()
38 | })
39 |
40 | test('Renders Image with fallback prop.', () => {
41 | const tree = renderer
42 | .create(
43 | ,
48 | )
49 | .toJSON()
50 |
51 | expect(tree).toMatchSnapshot()
52 | })
53 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "module": "ESNext",
5 | "composite": true,
6 | "incremental": true,
7 | "lib": ["ESNext"],
8 | "importHelpers": true,
9 | "declarationMap": true,
10 | "declaration": true,
11 | "emitDeclarationOnly": true,
12 | "sourceMap": false,
13 | "strict": true,
14 | "noImplicitAny": true,
15 | "skipLibCheck": true,
16 | "skipDefaultLibCheck": true,
17 | "isolatedModules": true,
18 | "noImplicitThis": true,
19 | "alwaysStrict": true,
20 | "noUnusedLocals": true,
21 | "noImplicitReturns": true,
22 | "noFallthroughCasesInSwitch": true,
23 | "forceConsistentCasingInFileNames": true,
24 | "moduleResolution": "Node",
25 | "jsx": "react",
26 | "allowSyntheticDefaultImports": true,
27 | "esModuleInterop": true,
28 | "resolveJsonModule": true,
29 | "declarationDir": "./dist",
30 | "outDir": "./dist",
31 | "rootDir": "./src"
32 | },
33 | "include": ["./src"]
34 | }
35 |
--------------------------------------------------------------------------------