Minimal implementation for ImageLoaderModule in ReactCxxPlatform (#52198)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/52198

# Changelog:
[Internal] -

This provides an implementation of what was the RnCxx ImageLoaderModule stub inside ReactCxxPlatform, allowing the clients use dependency injection to provide the actual platform specific image loading functionality.

Reviewed By: javache

Differential Revision: D77015269

fbshipit-source-id: 7355dd75692c1f564de8c3daffd6c8a79182dc09
This commit is contained in:
Ruslan Shestopalyuk
2025-06-23 07:44:36 -07:00
committed by Facebook GitHub Bot
parent e69f0726cd
commit 27c97ac942
3 changed files with 110 additions and 14 deletions
@@ -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 <functional>
#include <string>
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
@@ -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<ImageSize> ImageLoaderModule::getSize(
jsi::Runtime& rt,
const std::string& /*uri*/) {
const std::string& uri) {
auto promise = AsyncPromise<ImageSize>(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<ImageSize> ImageLoaderModule::getSizeWithHeaders(
jsi::Runtime& rt,
const std::string& /*uri*/,
const std::string& uri,
jsi::Object /*headers*/) {
auto promise = AsyncPromise<ImageSize>(rt, jsInvoker_);
promise.resolve({.width = 0.0, .height = 0.0});
return promise;
return getSize(rt, uri);
}
AsyncPromise<bool> ImageLoaderModule::prefetchImage(
jsi::Runtime& rt,
const std::string& /*uri*/,
const std::string& uri,
int32_t /*requestId*/) {
auto promise = AsyncPromise<bool>(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<std::string>& /*uris*/) {
return jsi::Object(rt);
const std::vector<std::string>& uris) {
auto result = jsi::Object(rt);
if (auto imageLoader = imageLoader_.lock()) {
for (const auto& uri : uris) {
auto cacheStatus = static_cast<int>(imageLoader->getCacheStatus(uri));
if (cacheStatus == static_cast<int>(IImageLoader::CacheStatus::None)) {
continue;
}
const bool isOnDisk =
(cacheStatus & static_cast<int>(IImageLoader::CacheStatus::Disk)) !=
0;
const bool isInMemory =
(cacheStatus & static_cast<int>(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
@@ -11,10 +11,13 @@
#include <react/bridging/Promise.h>
#include <memory>
#include <string>
#include <utility>
#include <vector>
namespace facebook::react {
class IImageLoader;
using ImageSize = NativeImageLoaderAndroidImageSize<double, double>;
template <>
@@ -24,8 +27,11 @@ struct Bridging<ImageSize>
class ImageLoaderModule
: public NativeImageLoaderAndroidCxxSpec<ImageLoaderModule> {
public:
explicit ImageLoaderModule(std::shared_ptr<CallInvoker> jsInvoker)
: NativeImageLoaderAndroidCxxSpec(jsInvoker) {}
explicit ImageLoaderModule(
std::shared_ptr<CallInvoker> jsInvoker,
std::weak_ptr<IImageLoader> imageLoader = std::weak_ptr<IImageLoader>())
: 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<std::string>& uris);
private:
std::weak_ptr<IImageLoader> imageLoader_;
};
} // namespace facebook::react