diff --git a/packages/react-native/ReactCxxPlatform/react/io/IImageLoader.h b/packages/react-native/ReactCxxPlatform/react/io/IImageLoader.h new file mode 100644 index 00000000000..c1a4bcec769 --- /dev/null +++ b/packages/react-native/ReactCxxPlatform/react/io/IImageLoader.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include + +namespace facebook::react { + +using IImageLoaderOnLoadCallback = std::function< + void(double imageWidth, double imageHeight, const char* errorMessage)>; + +class IImageLoader { + public: + enum class CacheStatus { + None = 0, + Disk = 0x1, + Memory = 0x1 << 1, + }; + + virtual ~IImageLoader() = default; + + virtual void loadImage( + const std::string& uri, + const IImageLoaderOnLoadCallback&& onLoad) = 0; + + virtual CacheStatus getCacheStatus(const std::string& uri) = 0; +}; + +} // namespace facebook::react diff --git a/packages/react-native/ReactCxxPlatform/react/io/ImageLoaderModule.cpp b/packages/react-native/ReactCxxPlatform/react/io/ImageLoaderModule.cpp index 1265eb259e3..c8bb167924c 100644 --- a/packages/react-native/ReactCxxPlatform/react/io/ImageLoaderModule.cpp +++ b/packages/react-native/ReactCxxPlatform/react/io/ImageLoaderModule.cpp @@ -6,11 +6,10 @@ */ #include "ImageLoaderModule.h" +#include "IImageLoader.h" namespace facebook::react { -// TODO: T170340321 - actual implementation - jsi::Object ImageLoaderModule::getConstants(jsi::Runtime& rt) { return jsi::Object(rt); } @@ -19,34 +18,87 @@ void ImageLoaderModule::abortRequest(jsi::Runtime& rt, int32_t requestId) {} AsyncPromise ImageLoaderModule::getSize( jsi::Runtime& rt, - const std::string& /*uri*/) { + const std::string& uri) { auto promise = AsyncPromise(rt, jsInvoker_); - promise.resolve({.width = 0.0, .height = 0.0}); + if (auto imageLoader = imageLoader_.lock()) { + imageLoader->loadImage( + uri, + [promise]( + double imageWidth, + double imageHeight, + const char* errorMessage) mutable { + if (errorMessage == nullptr) { + promise.resolve({.width = imageWidth, .height = imageHeight}); + } else { + promise.reject(errorMessage); + } + }); + } else { + promise.reject("Failed to get image size: image loader is not available."); + } return promise; } AsyncPromise ImageLoaderModule::getSizeWithHeaders( jsi::Runtime& rt, - const std::string& /*uri*/, + const std::string& uri, jsi::Object /*headers*/) { - auto promise = AsyncPromise(rt, jsInvoker_); - promise.resolve({.width = 0.0, .height = 0.0}); - return promise; + return getSize(rt, uri); } AsyncPromise ImageLoaderModule::prefetchImage( jsi::Runtime& rt, - const std::string& /*uri*/, + const std::string& uri, int32_t /*requestId*/) { auto promise = AsyncPromise(rt, jsInvoker_); - promise.resolve(false); + if (auto imageLoader = imageLoader_.lock()) { + imageLoader->loadImage( + uri, + [promise]( + double /*imageWidth*/, + double /*imageHeight*/, + const char* errorMessage) mutable { + if (errorMessage == nullptr) { + promise.resolve(true); + } else { + promise.reject(errorMessage); + } + }); + } else { + promise.reject("Failed to get image size: image loader is not available."); + } return promise; } jsi::Object ImageLoaderModule::queryCache( jsi::Runtime& rt, - const std::vector& /*uris*/) { - return jsi::Object(rt); + const std::vector& uris) { + auto result = jsi::Object(rt); + if (auto imageLoader = imageLoader_.lock()) { + for (const auto& uri : uris) { + auto cacheStatus = static_cast(imageLoader->getCacheStatus(uri)); + if (cacheStatus == static_cast(IImageLoader::CacheStatus::None)) { + continue; + } + const bool isOnDisk = + (cacheStatus & static_cast(IImageLoader::CacheStatus::Disk)) != + 0; + const bool isInMemory = + (cacheStatus & static_cast(IImageLoader::CacheStatus::Memory)) != + 0; + + std::string cacheStatusString; + if (isOnDisk && isInMemory) { + cacheStatusString = "disk/memory"; + } else if (isInMemory) { + cacheStatusString = "memory"; + } else if (isOnDisk) { + cacheStatusString = "disk"; + } + result.setProperty(rt, uri.c_str(), cacheStatusString.c_str()); + } + } + return result; } } // namespace facebook::react diff --git a/packages/react-native/ReactCxxPlatform/react/io/ImageLoaderModule.h b/packages/react-native/ReactCxxPlatform/react/io/ImageLoaderModule.h index 932ce9d1870..209564efc45 100644 --- a/packages/react-native/ReactCxxPlatform/react/io/ImageLoaderModule.h +++ b/packages/react-native/ReactCxxPlatform/react/io/ImageLoaderModule.h @@ -11,10 +11,13 @@ #include #include #include +#include #include namespace facebook::react { +class IImageLoader; + using ImageSize = NativeImageLoaderAndroidImageSize; template <> @@ -24,8 +27,11 @@ struct Bridging class ImageLoaderModule : public NativeImageLoaderAndroidCxxSpec { public: - explicit ImageLoaderModule(std::shared_ptr jsInvoker) - : NativeImageLoaderAndroidCxxSpec(jsInvoker) {} + explicit ImageLoaderModule( + std::shared_ptr jsInvoker, + std::weak_ptr imageLoader = std::weak_ptr()) + : NativeImageLoaderAndroidCxxSpec(jsInvoker), + imageLoader_(std::move(imageLoader)) {} jsi::Object getConstants(jsi::Runtime& rt); void abortRequest(jsi::Runtime& rt, int32_t requestId); @@ -43,6 +49,9 @@ class ImageLoaderModule jsi::Object queryCache( jsi::Runtime& rt, const std::vector& uris); + + private: + std::weak_ptr imageLoader_; }; } // namespace facebook::react