feat(smart-app): implement complete mobile app MVP

- App.tsx: full navigation (Auth stack + Main tabs with 5 screens)
- Auth: LoginScreen, RegisterScreen, ForgotPasswordScreen
- HomeScreen: dashboard with IoT metrics, weather widget, alerts, quick actions, sensors
- MapScreen: interactive map with layer toggles (6 layers)
- MarketplaceScreen: categories (6), products (5), search
- ChatScreen: AI chat with quick prompts (4), bot responses
- ProfileScreen: user info, stats, menu (9 items), logout
- AlertsScreen: alert list with severity, acknowledge
- SensorsScreen: sensor list with type filters (6 types), search
- ZonesScreen: zone cards with stats
- SettingsScreen: language picker (FR/EN/ES/DE), privacy, about
- Stores: iotStore (sensors, zones, alerts), notificationStore, uiStore + i18n
- Hooks: useSensors, useAlerts, useNotifications, useLocation
- Components: Card, Button, LoadingSpinner, ErrorBoundary, Header
- Services: iotService, notificationService (with axios API client)
- Utils: formatters (temp, AQI, noise, dates), validators (email, password, IBAN)
- Theme: colors.ts with full design system (Blue Ocean palette)
- Ditto: fixed MongoDB connection, new JWT secrets, official gateway image
This commit is contained in:
Eric FELIXINE
2026-06-01 18:00:35 -04:00
parent 08ca495bde
commit e30ae8ed09
35578 changed files with 3703534 additions and 43 deletions

View File

@@ -0,0 +1,40 @@
# 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.
cmake_minimum_required(VERSION 3.13)
set(CMAKE_VERBOSE_MAKEFILE on)
add_compile_options(
-fexceptions
-frtti
-std=c++20
-Wall
-Wpedantic
-DLOG_TAG=\"Fabric\")
file(GLOB react_render_imagemanager_SRC CONFIGURE_DEPENDS
*.cpp
platform/cxx/react/renderer/imagemanager/*.cpp)
add_library(react_render_imagemanager
SHARED
${react_render_imagemanager_SRC})
target_include_directories(react_render_imagemanager
PUBLIC
${REACT_COMMON_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/platform/cxx/
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
)
target_link_libraries(react_render_imagemanager
folly_runtime
react_debug
react_render_core
react_render_debug
react_render_graphics
react_render_mounting
yoga)

View File

@@ -0,0 +1,40 @@
/*
* 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 <memory>
#include <react/renderer/core/ReactPrimitives.h>
#include <react/renderer/imagemanager/ImageRequest.h>
#include <react/renderer/imagemanager/primitives.h>
#include <react/utils/ContextContainer.h>
namespace facebook::react {
class ImageManager;
using SharedImageManager = std::shared_ptr<ImageManager>;
/*
* Cross platform facade for image management (e.g. iOS-specific
* RCTImageManager)
*/
class ImageManager {
public:
ImageManager(const ContextContainer::Shared& contextContainer);
virtual ~ImageManager();
virtual ImageRequest requestImage(
const ImageSource& imageSource,
SurfaceId surfaceId) const;
private:
void* self_{};
};
} // namespace facebook::react

View File

@@ -0,0 +1,45 @@
/*
* 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.
*/
#include <react/renderer/imagemanager/ImageRequest.h>
namespace facebook::react {
ImageRequest::ImageRequest(
ImageSource imageSource,
std::shared_ptr<const ImageTelemetry> telemetry,
SharedFunction<> cancelationFunction)
: imageSource_(std::move(imageSource)),
telemetry_(std::move(telemetry)),
cancelRequest_(std::move(cancelationFunction)) {
coordinator_ = std::make_shared<ImageResponseObserverCoordinator>();
}
void ImageRequest::cancel() const {
cancelRequest_();
}
const ImageSource& ImageRequest::getImageSource() const {
return imageSource_;
}
const std::shared_ptr<const ImageTelemetry>& ImageRequest::getSharedTelemetry()
const {
return telemetry_;
}
const ImageResponseObserverCoordinator& ImageRequest::getObserverCoordinator()
const {
return *coordinator_;
}
const std::shared_ptr<const ImageResponseObserverCoordinator>&
ImageRequest::getSharedObserverCoordinator() const {
return coordinator_;
}
} // namespace facebook::react

View File

@@ -0,0 +1,99 @@
/*
* 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 <react/renderer/imagemanager/ImageResponse.h>
#include <react/renderer/imagemanager/ImageResponseObserver.h>
#include <react/renderer/imagemanager/ImageResponseObserverCoordinator.h>
#include <react/renderer/imagemanager/ImageTelemetry.h>
#include <react/renderer/imagemanager/primitives.h>
#include <react/utils/SharedFunction.h>
namespace facebook::react {
/*
* Represents ongoing request for an image resource.
* The separate object must be constructed for every single separate
* image request. The object cannot be copied because it would make managing of
* event listeners hard and inefficient; the object can be moved though.
* Destroy to cancel the underlying request.
*/
class ImageRequest final {
public:
/*
* The default constructor
*/
ImageRequest(
ImageSource imageSource,
std::shared_ptr<const ImageTelemetry> telemetry,
SharedFunction<> cancelationFunction);
/*
* The move constructor.
*/
ImageRequest(ImageRequest&& other) noexcept = default;
/*
* `ImageRequest` does not support copying by design.
*/
ImageRequest(const ImageRequest& other) = delete;
/*
* Calls cancel function if one is defined. Should be when downloading
* image isn't needed anymore. E.g. <ImageView /> was removed.
*/
void cancel() const;
/*
* Returns the Image Source associated with the request.
*/
const ImageSource& getImageSource() const;
/*
* Returns stored observer coordinator as a shared pointer.
* Retain this *or* `ImageRequest` to ensure a correct lifetime of the object.
*/
const std::shared_ptr<const ImageResponseObserverCoordinator>&
getSharedObserverCoordinator() const;
/*
* Returns stored observer coordinator as a reference.
* Use this if a correct lifetime of the object is ensured in some other way
* (e.g. by retaining an `ImageRequest`).
*/
const ImageResponseObserverCoordinator& getObserverCoordinator() const;
/*
* Returns stored image telemetry object as a shared pointer.
* Retain this *or* `ImageRequest` to ensure a correct lifetime of the object.
*/
const std::shared_ptr<const ImageTelemetry>& getSharedTelemetry() const;
private:
/*
* Image source associated with the request.
*/
ImageSource imageSource_;
/*
* Image telemetry associated with the request.
*/
std::shared_ptr<const ImageTelemetry> telemetry_{};
/*
* Event coordinator associated with the request.
*/
std::shared_ptr<const ImageResponseObserverCoordinator> coordinator_{};
/*
* Function we can call to cancel image request.
*/
SharedFunction<> cancelRequest_;
};
} // namespace facebook::react

View File

@@ -0,0 +1,27 @@
/*
* 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.
*/
#include "ImageResponse.h"
#include <utility>
namespace facebook::react {
ImageResponse::ImageResponse(
std::shared_ptr<void> image,
std::shared_ptr<void> metadata)
: image_(std::move(image)), metadata_(std::move(metadata)) {}
std::shared_ptr<void> ImageResponse::getImage() const {
return image_;
}
std::shared_ptr<void> ImageResponse::getMetadata() const {
return metadata_;
}
} // namespace facebook::react

View File

@@ -0,0 +1,37 @@
/*
* 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 <memory>
namespace facebook::react {
/*
* Represents retrieved image bitmap and any associated platform-specific info.
*/
class ImageResponse final {
public:
enum class Status {
Loading,
Completed,
Failed,
};
ImageResponse(std::shared_ptr<void> image, std::shared_ptr<void> metadata);
std::shared_ptr<void> getImage() const;
std::shared_ptr<void> getMetadata() const;
private:
std::shared_ptr<void> image_{};
std::shared_ptr<void> metadata_{};
};
} // namespace facebook::react

View File

@@ -0,0 +1,27 @@
/*
* 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 <react/renderer/imagemanager/ImageResponse.h>
namespace facebook::react {
/*
* Represents any observer of ImageResponse progression, completion, or failure.
* All methods must be thread-safe.
*/
class ImageResponseObserver {
public:
virtual ~ImageResponseObserver() noexcept = default;
virtual void didReceiveProgress(float progress) const = 0;
virtual void didReceiveImage(const ImageResponse& imageResponse) const = 0;
virtual void didReceiveFailure() const = 0;
};
} // namespace facebook::react

View File

@@ -0,0 +1,90 @@
/*
* 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.
*/
#include "ImageResponseObserverCoordinator.h"
#include <algorithm>
#include <react/debug/react_native_assert.h>
namespace facebook::react {
void ImageResponseObserverCoordinator::addObserver(
const ImageResponseObserver& observer) const {
mutex_.lock();
switch (status_) {
case ImageResponse::Status::Loading: {
observers_.push_back(&observer);
mutex_.unlock();
break;
}
case ImageResponse::Status::Completed: {
auto imageData = imageData_;
auto imageMetadata = imageMetadata_;
mutex_.unlock();
observer.didReceiveImage(ImageResponse{imageData, imageMetadata});
break;
}
case ImageResponse::Status::Failed: {
mutex_.unlock();
observer.didReceiveFailure();
break;
}
}
}
void ImageResponseObserverCoordinator::removeObserver(
const ImageResponseObserver& observer) const {
std::scoped_lock lock(mutex_);
// We remove only one element to maintain a balance between add/remove calls.
auto position = std::find(observers_.begin(), observers_.end(), &observer);
if (position != observers_.end()) {
observers_.erase(position, observers_.end());
}
}
void ImageResponseObserverCoordinator::nativeImageResponseProgress(
float progress) const {
mutex_.lock();
auto observers = observers_;
react_native_assert(status_ == ImageResponse::Status::Loading);
mutex_.unlock();
for (auto observer : observers) {
observer->didReceiveProgress(progress);
}
}
void ImageResponseObserverCoordinator::nativeImageResponseComplete(
const ImageResponse& imageResponse) const {
mutex_.lock();
imageData_ = imageResponse.getImage();
imageMetadata_ = imageResponse.getMetadata();
react_native_assert(status_ == ImageResponse::Status::Loading);
status_ = ImageResponse::Status::Completed;
auto observers = observers_;
mutex_.unlock();
for (auto observer : observers_) {
observer->didReceiveImage(imageResponse);
}
}
void ImageResponseObserverCoordinator::nativeImageResponseFailed() const {
mutex_.lock();
react_native_assert(status_ == ImageResponse::Status::Loading);
status_ = ImageResponse::Status::Failed;
auto observers = observers_;
mutex_.unlock();
for (auto observer : observers) {
observer->didReceiveFailure();
}
}
} // namespace facebook::react

View File

@@ -0,0 +1,86 @@
/*
* 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 <react/renderer/imagemanager/ImageResponse.h>
#include <react/renderer/imagemanager/ImageResponseObserver.h>
#include <mutex>
#include <vector>
namespace facebook::react {
/*
* The ImageResponseObserverCoordinator receives events and completed image
* data from native image loaders and sends events to any observers attached
* to the coordinator. The Coordinator also keeps track of response status
* and caches completed images.
*/
class ImageResponseObserverCoordinator {
public:
/*
* Interested parties may observe the image response.
* If the current image request status is not equal to `Loading`, the observer
* will be called immediately.
*/
void addObserver(const ImageResponseObserver& observer) const;
/*
* Interested parties may stop observing the image response.
*/
void removeObserver(const ImageResponseObserver& observer) const;
/*
* Platform-specific image loader will call this method with progress updates.
*/
void nativeImageResponseProgress(float progress) const;
/*
* Platform-specific image loader will call this method with a completed image
* response.
*/
void nativeImageResponseComplete(const ImageResponse& imageResponse) const;
/*
* Platform-specific image loader will call this method in case of any
* failures.
*/
void nativeImageResponseFailed() const;
private:
/*
* List of observers.
* Mutable: protected by mutex_.
*/
mutable std::vector<const ImageResponseObserver*> observers_;
/*
* Current status of image loading.
* Mutable: protected by mutex_.
*/
mutable ImageResponse::Status status_{ImageResponse::Status::Loading};
/*
* Cache image data.
* Mutable: protected by mutex_.
*/
mutable std::shared_ptr<void> imageData_;
/*
* Cache image metadata.
* Mutable: protected by mutex_.
*/
mutable std::shared_ptr<void> imageMetadata_;
/*
* Observer and data mutex.
*/
mutable std::mutex mutex_;
};
} // namespace facebook::react

View File

@@ -0,0 +1,20 @@
/*
* 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.
*/
#include "ImageTelemetry.h"
namespace facebook::react {
SurfaceId ImageTelemetry::getSurfaceId() const {
return surfaceId_;
}
TelemetryTimePoint ImageTelemetry::getWillRequestUrlTime() const {
return willRequestUrlTime_;
}
} // namespace facebook::react

View File

@@ -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 <react/renderer/core/ReactPrimitives.h>
#include <react/utils/Telemetry.h>
namespace facebook::react {
/*
* Represents telemetry data associated with a image request
* where the willRequestUrlTime is the time at ImageTelemetry's creation.
*/
class ImageTelemetry final {
public:
ImageTelemetry(const SurfaceId surfaceId) : surfaceId_(surfaceId) {
willRequestUrlTime_ = telemetryTimePointNow();
}
TelemetryTimePoint getWillRequestUrlTime() const;
SurfaceId getSurfaceId() const;
private:
TelemetryTimePoint willRequestUrlTime_;
const SurfaceId surfaceId_;
};
} // namespace facebook::react

View File

@@ -0,0 +1,30 @@
/*
* 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.
*/
#include "ImageManager.h"
namespace facebook::react {
ImageManager::ImageManager(
const ContextContainer::Shared& /*contextContainer*/) {
// Silence unused-private-field warning.
(void)self_;
// Not implemented.
}
ImageManager::~ImageManager() {
// Not implemented.
}
ImageRequest ImageManager::requestImage(
const ImageSource& imageSource,
SurfaceId /*surfaceId*/) const {
// Not implemented.
return {imageSource, nullptr, {}};
}
} // namespace facebook::react

View File

@@ -0,0 +1,67 @@
# 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.
require "json"
package = JSON.parse(File.read(File.join(__dir__, "..", "..", "..", "..", "..", "..", "package.json")))
version = package['version']
source = { :git => 'https://github.com/facebook/react-native.git' }
if version == '1000.0.0'
# This is an unpublished version, use the latest commit hash of the react-native repo, which were presumably in.
source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1")
else
source[:tag] = "v#{version}"
end
folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -DFOLLY_CFG_NO_COROUTINES=1 -DFOLLY_HAVE_CLOCK_GETTIME=1 -Wno-comma -Wno-shorten-64-to-32'
boost_compiler_flags = '-Wno-documentation'
Pod::Spec.new do |s|
source_files = "**/*.{m,mm,cpp,h}"
header_search_paths = [
"\"$(PODS_ROOT)/boost\"",
"\"$(PODS_TARGET_SRCROOT)/../../../\"",
"\"$(PODS_TARGET_SRCROOT)\"",
"\"$(PODS_ROOT)/RCT-Folly\"",
"\"$(PODS_ROOT)/DoubleConversion\"",
"\"$(PODS_ROOT)/fmt/include\"",
].join(" ")
s.name = "React-ImageManager"
s.version = version
s.summary = "Fabric for React Native."
s.homepage = "https://reactnative.dev/"
s.license = package["license"]
s.author = "Meta Platforms, Inc. and its affiliates"
s.platforms = min_supported_versions
s.source = source
s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags
s.source_files = source_files
s.header_dir = "react/renderer/imagemanager"
if ENV['USE_FRAMEWORKS']
s.module_name = "React_ImageManager"
s.header_mappings_dir = "./"
end
s.pod_target_xcconfig = {
"USE_HEADERMAP" => "NO",
"HEADER_SEARCH_PATHS" => header_search_paths,
"CLANG_CXX_LANGUAGE_STANDARD" => "c++20",
"DEFINES_MODULE" => "YES",
}
s.dependency "RCT-Folly/Fabric"
s.dependency "React-Core/Default"
s.dependency "glog"
add_dependency(s, "React-Fabric")
add_dependency(s, "React-graphics", :additional_framework_paths => ["react/renderer/graphics/platform/ios"])
add_dependency(s, "React-debug")
add_dependency(s, "React-utils")
add_dependency(s, "React-rendererdebug")
end

View File

@@ -0,0 +1,43 @@
/*
* 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.
*/
#import <react/renderer/imagemanager/ImageManager.h>
#import <React/RCTImageLoaderWithAttributionProtocol.h>
#import <React/RCTUtils.h>
#import <react/utils/ManagedObjectWrapper.h>
#import "RCTImageManager.h"
#import "RCTSyncImageManager.h"
namespace facebook::react {
ImageManager::ImageManager(const ContextContainer::Shared &contextContainer)
{
id<RCTImageLoaderWithAttributionProtocol> imageLoader =
(id<RCTImageLoaderWithAttributionProtocol>)unwrapManagedObject(
contextContainer->at<std::shared_ptr<void>>("RCTImageLoader"));
if (RCTRunningInTestEnvironment()) {
self_ = (__bridge_retained void *)[[RCTSyncImageManager alloc] initWithImageLoader:imageLoader];
} else {
self_ = (__bridge_retained void *)[[RCTImageManager alloc] initWithImageLoader:imageLoader];
}
}
ImageManager::~ImageManager()
{
CFRelease(self_);
self_ = nullptr;
}
ImageRequest ImageManager::requestImage(const ImageSource &imageSource, SurfaceId surfaceId) const
{
RCTImageManager *imageManager = (__bridge RCTImageManager *)self_;
return [imageManager requestImage:imageSource surfaceId:surfaceId];
}
} // namespace facebook::react

View File

@@ -0,0 +1,28 @@
/*
* 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.
*/
#import <UIKit/UIKit.h>
#import "RCTImageManagerProtocol.h"
NS_ASSUME_NONNULL_BEGIN
@protocol RCTImageLoaderWithAttributionProtocol;
/**
* iOS-specific ImageManager.
*/
@interface RCTImageManager : NSObject <RCTImageManagerProtocol>
- (instancetype)initWithImageLoader:(id<RCTImageLoaderWithAttributionProtocol>)imageLoader;
- (facebook::react::ImageRequest)requestImage:(facebook::react::ImageSource)imageSource
surfaceId:(facebook::react::SurfaceId)surfaceId;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,110 @@
/*
* 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.
*/
#import "RCTImageManager.h"
#import <react/renderer/debug/SystraceSection.h>
#import <react/utils/ManagedObjectWrapper.h>
#import <react/utils/SharedFunction.h>
#import <React/RCTImageLoaderWithAttributionProtocol.h>
#import <react/renderer/imagemanager/ImageResponse.h>
#import <react/renderer/imagemanager/ImageResponseObserver.h>
#import "RCTImagePrimitivesConversions.h"
using namespace facebook::react;
@implementation RCTImageManager {
id<RCTImageLoaderWithAttributionProtocol> _imageLoader;
dispatch_queue_t _backgroundSerialQueue;
}
- (instancetype)initWithImageLoader:(id<RCTImageLoaderWithAttributionProtocol>)imageLoader
{
if (self = [super init]) {
_imageLoader = imageLoader;
_backgroundSerialQueue =
dispatch_queue_create("com.facebook.react-native.image-manager-queue", DISPATCH_QUEUE_SERIAL);
}
return self;
}
- (ImageRequest)requestImage:(ImageSource)imageSource surfaceId:(SurfaceId)surfaceId
{
SystraceSection s("RCTImageManager::requestImage");
NSURLRequest *request = NSURLRequestFromImageSource(imageSource);
std::shared_ptr<ImageTelemetry> telemetry;
if ([self->_imageLoader shouldEnablePerfLoggingForRequestUrl:request.URL]) {
telemetry = std::make_shared<ImageTelemetry>(surfaceId);
} else {
telemetry = nullptr;
}
auto sharedCancelationFunction = SharedFunction<>();
auto imageRequest = ImageRequest(imageSource, telemetry, sharedCancelationFunction);
auto weakObserverCoordinator =
(std::weak_ptr<const ImageResponseObserverCoordinator>)imageRequest.getSharedObserverCoordinator();
/*
* Even if an image is being loaded asynchronously on some other background thread, some other preparation
* work (such as creating an `NSURLRequest` object and some obscure logic inside `RCTImageLoader`) can take a couple
* of milliseconds, so we have to offload this to a separate thread. `ImageRequest` can be created as part of the
* layout process, so it must be highly performant.
*
* Technically, we don't need to dispatch this to *serial* queue. The interface of `RCTImageLoader` promises to be
* fully thread-safe. However, in reality, it crashes when we request images on concurrently on different threads. See
* T46024425 for more details.
*/
dispatch_async(_backgroundSerialQueue, ^{
auto completionBlock = ^(NSError *error, UIImage *image, id metadata) {
auto observerCoordinator = weakObserverCoordinator.lock();
if (!observerCoordinator) {
return;
}
if (image && !error) {
auto wrappedMetadata = metadata ? wrapManagedObject(metadata) : nullptr;
observerCoordinator->nativeImageResponseComplete(ImageResponse(wrapManagedObject(image), wrappedMetadata));
} else {
observerCoordinator->nativeImageResponseFailed();
}
};
auto progressBlock = ^(int64_t progress, int64_t total) {
auto observerCoordinator = weakObserverCoordinator.lock();
if (!observerCoordinator) {
return;
}
observerCoordinator->nativeImageResponseProgress((float)progress / (float)total);
};
RCTImageURLLoaderRequest *loaderRequest =
[self->_imageLoader loadImageWithURLRequest:request
size:CGSizeMake(imageSource.size.width, imageSource.size.height)
scale:imageSource.scale
clipped:NO
resizeMode:RCTResizeModeStretch
priority:RCTImageLoaderPriorityImmediate
attribution:{
.surfaceId = surfaceId,
}
progressBlock:progressBlock
partialLoadBlock:nil
completionBlock:completionBlock];
RCTImageLoaderCancellationBlock cancelationBlock = loaderRequest.cancellationBlock;
sharedCancelationFunction.assign([cancelationBlock]() { cancelationBlock(); });
});
return imageRequest;
}
@end

View File

@@ -0,0 +1,16 @@
/*
* 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.
*/
#import <Foundation/Foundation.h>
#import <react/renderer/core/ReactPrimitives.h>
#import <react/renderer/imagemanager/ImageRequest.h>
@protocol RCTImageManagerProtocol <NSObject>
- (facebook::react::ImageRequest)requestImage:(facebook::react::ImageSource)imageSource
surfaceId:(facebook::react::SurfaceId)surfaceId;
@end

View File

@@ -0,0 +1,114 @@
/*
* 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.
*/
#import <UIKit/UIKit.h>
#import <React/RCTImageLoader.h>
#import <react/renderer/imagemanager/primitives.h>
inline static UIViewContentMode RCTContentModeFromImageResizeMode(facebook::react::ImageResizeMode imageResizeMode)
{
switch (imageResizeMode) {
case facebook::react::ImageResizeMode::Cover:
return UIViewContentModeScaleAspectFill;
case facebook::react::ImageResizeMode::Contain:
return UIViewContentModeScaleAspectFit;
case facebook::react::ImageResizeMode::Stretch:
return UIViewContentModeScaleToFill;
case facebook::react::ImageResizeMode::Center:
return UIViewContentModeCenter;
case facebook::react::ImageResizeMode::Repeat:
// Repeat resize mode is handled by the UIImage. Use scale to fill
// so the repeated image fills the UIImageView.
return UIViewContentModeScaleToFill;
}
}
inline std::string toString(const facebook::react::ImageResizeMode &value)
{
switch (value) {
case facebook::react::ImageResizeMode::Cover:
return "cover";
case facebook::react::ImageResizeMode::Contain:
return "contain";
case facebook::react::ImageResizeMode::Stretch:
return "stretch";
case facebook::react::ImageResizeMode::Center:
return "center";
case facebook::react::ImageResizeMode::Repeat:
return "repeat";
}
}
inline static NSURL *NSURLFromImageSource(const facebook::react::ImageSource &imageSource)
{
// `NSURL` has a history of crashing with bad input, so let's be safe.
@try {
NSString *urlString = [NSString stringWithUTF8String:imageSource.uri.c_str()];
if (!imageSource.bundle.empty()) {
NSString *bundle = [NSString stringWithUTF8String:imageSource.bundle.c_str()];
urlString = [NSString stringWithFormat:@"%@.bundle/%@", bundle, urlString];
}
NSURL *url = [[NSURL alloc] initWithString:urlString];
if (url.scheme) {
// Well-formed absolute URL.
return url;
}
if ([urlString rangeOfString:@":"].location != NSNotFound) {
// The URL has a scheme.
urlString =
[urlString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
url = [NSURL URLWithString:urlString];
return url;
}
// Assume that it's a local path.
urlString = [urlString stringByRemovingPercentEncoding];
if ([urlString hasPrefix:@"~"]) {
// Path is inside user directory.
urlString = [urlString stringByExpandingTildeInPath];
} else {
if (![urlString isAbsolutePath]) {
// Assume it's a resource path.
urlString = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:urlString];
}
}
url = [NSURL fileURLWithPath:urlString];
return url;
} @catch (__unused NSException *exception) {
return nil;
}
}
inline static NSURLRequest *NSURLRequestFromImageSource(const facebook::react::ImageSource &imageSource)
{
NSURL *url = NSURLFromImageSource(imageSource);
if (!url) {
RCTLogError(@"URI parsing error.");
return nil;
}
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
/*
// TODO(shergin): To be implemented.
request.HTTPBody = ...;
request.HTTPMethod = ...;
request.cachePolicy = ...;
request.allHTTPHeaderFields = ...;
*/
return [request copy];
}

View File

@@ -0,0 +1,28 @@
/*
* 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.
*/
#import <UIKit/UIKit.h>
#import "RCTImageManagerProtocol.h"
NS_ASSUME_NONNULL_BEGIN
@protocol RCTImageLoaderWithAttributionProtocol;
/**
* iOS-specific ImageManager.
*/
@interface RCTSyncImageManager : NSObject <RCTImageManagerProtocol>
- (instancetype)initWithImageLoader:(id<RCTImageLoaderWithAttributionProtocol>)imageLoader;
- (facebook::react::ImageRequest)requestImage:(facebook::react::ImageSource)imageSource
surfaceId:(facebook::react::SurfaceId)surfaceId;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,98 @@
/*
* 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.
*/
#import "RCTSyncImageManager.h"
#import <react/utils/ManagedObjectWrapper.h>
#import <react/utils/SharedFunction.h>
#import <React/RCTAssert.h>
#import <React/RCTImageLoaderWithAttributionProtocol.h>
#import <React/RCTLog.h>
#import <react/renderer/imagemanager/ImageResponse.h>
#import <react/renderer/imagemanager/ImageResponseObserver.h>
#import "RCTImagePrimitivesConversions.h"
using namespace facebook::react;
@implementation RCTSyncImageManager {
id<RCTImageLoaderWithAttributionProtocol> _imageLoader;
}
- (instancetype)initWithImageLoader:(id<RCTImageLoaderWithAttributionProtocol>)imageLoader
{
if (self = [super init]) {
RCTAssert(RCTRunningInTestEnvironment(), @"This class is only meant to be used in test environment");
_imageLoader = imageLoader;
}
return self;
}
- (ImageRequest)requestImage:(ImageSource)imageSource surfaceId:(SurfaceId)surfaceId
{
auto telemetry = std::make_shared<ImageTelemetry>(surfaceId);
auto sharedCancelationFunction = SharedFunction<>();
auto imageRequest = ImageRequest(imageSource, telemetry, sharedCancelationFunction);
auto weakObserverCoordinator =
(std::weak_ptr<const ImageResponseObserverCoordinator>)imageRequest.getSharedObserverCoordinator();
dispatch_group_t imageWaitGroup = dispatch_group_create();
dispatch_group_enter(imageWaitGroup);
NSURLRequest *request = NSURLRequestFromImageSource(imageSource);
auto completionBlock = ^(NSError *error, UIImage *image, id metadata) {
auto observerCoordinator = weakObserverCoordinator.lock();
if (!observerCoordinator) {
return;
}
if (image && !error) {
auto wrappedMetadata = metadata ? wrapManagedObject(metadata) : nullptr;
observerCoordinator->nativeImageResponseComplete(ImageResponse(wrapManagedObject(image), wrappedMetadata));
} else {
observerCoordinator->nativeImageResponseFailed();
}
dispatch_group_leave(imageWaitGroup);
};
auto progressBlock = ^(int64_t progress, int64_t total) {
auto observerCoordinator = weakObserverCoordinator.lock();
if (!observerCoordinator) {
return;
}
observerCoordinator->nativeImageResponseProgress((float)progress / (float)total);
};
RCTImageURLLoaderRequest *loaderRequest =
[self->_imageLoader loadImageWithURLRequest:request
size:CGSizeMake(imageSource.size.width, imageSource.size.height)
scale:imageSource.scale
clipped:YES
resizeMode:RCTResizeModeStretch
priority:RCTImageLoaderPriorityImmediate
attribution:{
.surfaceId = surfaceId,
}
progressBlock:progressBlock
partialLoadBlock:nil
completionBlock:completionBlock];
RCTImageLoaderCancellationBlock cancelationBlock = loaderRequest.cancellationBlock;
sharedCancelationFunction.assign([cancelationBlock]() { cancelationBlock(); });
auto result = dispatch_group_wait(imageWaitGroup, dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC));
if (result != 0) {
RCTLogError(@"Image timed out in test environment for url: %@", loaderRequest.imageURL);
}
return imageRequest;
}
@end

View File

@@ -0,0 +1,47 @@
/*
* 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 <string>
#include <vector>
#include <react/renderer/graphics/Float.h>
#include <react/renderer/graphics/Size.h>
namespace facebook::react {
class ImageSource {
public:
enum class Type { Invalid, Remote, Local };
Type type{};
std::string uri{};
std::string bundle{};
Float scale{3};
Size size{0};
bool operator==(const ImageSource& rhs) const {
return std::tie(this->type, this->uri) == std::tie(rhs.type, rhs.uri);
}
bool operator!=(const ImageSource& rhs) const {
return !(*this == rhs);
}
};
using ImageSources = std::vector<ImageSource>;
enum class ImageResizeMode {
Cover,
Contain,
Stretch,
Center,
Repeat,
};
} // namespace facebook::react

View File

@@ -0,0 +1,18 @@
/*
* 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.
*/
#include <memory>
#include <gtest/gtest.h>
#include <react/renderer/imagemanager/ImageManager.h>
using namespace facebook::react;
TEST(ImageManagerTest, testSomething) {
// TODO:
}