Fabric: Migrate Image to State from LocalData

Summary:
Changelog: [Internal]

We are moving away from LocalData in favour of State.

Reviewed By: shergin

Differential Revision: D19247031

fbshipit-source-id: 8884133b13dd111e8d9e2cd4936bf90bfc5b4932
This commit is contained in:
Samuel Susla
2020-01-06 05:25:23 -08:00
committed by Facebook Github Bot
parent 8a4f9b31b3
commit 3d6f5f50a7
7 changed files with 112 additions and 140 deletions
@@ -11,7 +11,6 @@
#import <React/RCTImageResponseObserverProxy.h>
#import <react/components/image/ImageComponentDescriptor.h>
#import <react/components/image/ImageEventEmitter.h>
#import <react/components/image/ImageLocalData.h>
#import <react/components/image/ImageProps.h>
#import <react/imagemanager/ImageInstrumentation.h>
#import <react/imagemanager/ImageRequest.h>
@@ -26,7 +25,7 @@
@implementation RCTImageComponentView {
UIImageView *_imageView;
SharedImageLocalData _imageLocalData;
ImageShadowNode::ConcreteState::Shared _state;
ImageResponseObserverCoordinator const *_coordinator;
RCTImageResponseObserverProxy _imageResponseObserverProxy;
}
@@ -80,34 +79,28 @@
[super updateProps:props oldProps:oldProps];
}
- (void)updateLocalData:(SharedLocalData)localData oldLocalData:(SharedLocalData)oldLocalData
- (void)updateState:(facebook::react::State::Shared const &)state
oldState:(facebook::react::State::Shared const &)oldState
{
auto imageLocalData = std::static_pointer_cast<ImageLocalData const>(localData);
_state = std::static_pointer_cast<ImageShadowNode::ConcreteState const>(state);
auto _oldState = std::static_pointer_cast<ImageShadowNode::ConcreteState const>(oldState);
auto data = _state->getData();
// This call (setting `coordinator`) must be unconditional (at the same block as setting `LocalData`)
// because the setter stores a raw pointer to object that `LocalData` owns.
self.coordinator = imageLocalData ? &imageLocalData->getImageRequest().getObserverCoordinator() : nullptr;
// This call (setting `coordinator`) must be unconditional (at the same block as setting `State`)
// because the setter stores a raw pointer to object that `State` owns.
self.coordinator = &data.getImageRequest().getObserverCoordinator();
auto previousData = _imageLocalData;
_imageLocalData = imageLocalData;
bool havePreviousData = _oldState && _oldState->getData().getImageSource() != ImageSource{};
if (!_imageLocalData) {
// This might happen in very rare cases (e.g. inside a subtree inside a node with `display: none`).
// That's quite normal.
return;
}
bool havePreviousData = previousData != nullptr;
if (!havePreviousData || _imageLocalData->getImageSource() != previousData->getImageSource()) {
if (!havePreviousData || data.getImageSource() != _oldState->getData().getImageSource()) {
// Loading actually starts a little before this, but this is the first time we know
// the image is loading and can fire an event from this component
std::static_pointer_cast<ImageEventEmitter const>(_eventEmitter)->onLoadStart();
// TODO (T58941612): Tracking for visibility should be done directly on this class.
// For now, we consolidate instrumentation logic in the image loader, so that pre-Fabric gets the same treatment.
auto instrumentation = std::static_pointer_cast<const RCTImageInstrumentationProxy>(
_imageLocalData->getImageRequest().getSharedImageInstrumentation());
auto instrumentation = std::static_pointer_cast<RCTImageInstrumentationProxy const>(
data.getImageRequest().getSharedImageInstrumentation());
instrumentation->trackNativeImageView(self);
}
}
@@ -128,7 +121,7 @@
[super prepareForRecycle];
self.coordinator = nullptr;
_imageView.image = nil;
_imageLocalData.reset();
_state.reset();
}
- (void)dealloc
@@ -171,7 +164,7 @@
self->_imageView.layer.minificationFilter = kCAFilterTrilinear;
self->_imageView.layer.magnificationFilter = kCAFilterTrilinear;
_imageLocalData->getImageRequest().getImageInstrumentation().didSetImage();
_state->getData().getImageRequest().getImageInstrumentation().didSetImage();
}
- (void)didReceiveProgress:(float)progress fromObserver:(void const *)observer
@@ -1,37 +0,0 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "ImageLocalData.h"
#include <react/components/image/conversions.h>
#include <react/debug/debugStringConvertibleUtils.h>
namespace facebook {
namespace react {
ImageSource ImageLocalData::getImageSource() const {
return imageSource_;
}
const ImageRequest &ImageLocalData::getImageRequest() const {
return imageRequest_;
}
#pragma mark - DebugStringConvertible
#if RN_DEBUG_STRING_CONVERTIBLE
std::string ImageLocalData::getDebugName() const {
return "ImageLocalData";
}
SharedDebugStringConvertibleList ImageLocalData::getDebugProps() const {
return {debugStringConvertibleItem("imageSource", imageSource_)};
}
#endif
} // namespace react
} // namespace facebook
@@ -1,54 +0,0 @@
/*
* Copyright (c) Facebook, Inc. and its 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/core/LocalData.h>
#include <react/imagemanager/ImageRequest.h>
#include <react/imagemanager/primitives.h>
namespace facebook {
namespace react {
class ImageLocalData;
using SharedImageLocalData = std::shared_ptr<const ImageLocalData>;
/*
* LocalData for <Image> component.
* Represents the image request state and (possible) retrieved image bitmap.
*/
class ImageLocalData : public LocalData {
public:
ImageLocalData(const ImageSource &imageSource, ImageRequest imageRequest)
: imageSource_(imageSource), imageRequest_(std::move(imageRequest)){};
/*
* Returns stored ImageSource object.
*/
ImageSource getImageSource() const;
/*
* Exposes for reading stored `ImageRequest` object.
* `ImageRequest` object cannot be copied or moved from `ImageLocalData`.
*/
const ImageRequest &getImageRequest() const;
#pragma mark - DebugStringConvertible
#if RN_DEBUG_STRING_CONVERTIBLE
std::string getDebugName() const override;
SharedDebugStringConvertibleList getDebugProps() const override;
#endif
private:
ImageSource imageSource_;
ImageRequest imageRequest_;
};
} // namespace react
} // namespace facebook
@@ -8,9 +8,9 @@
#include <cstdlib>
#include <limits>
#include <react/components/image/ImageLocalData.h>
#include <react/components/image/ImageShadowNode.h>
#include <react/core/LayoutContext.h>
#include "ImageState.h"
namespace facebook {
namespace react {
@@ -22,27 +22,19 @@ void ImageShadowNode::setImageManager(const SharedImageManager &imageManager) {
imageManager_ = imageManager;
}
void ImageShadowNode::updateLocalData() {
const auto &imageSource = getImageSource();
const auto &currentLocalData = getLocalData();
if (currentLocalData) {
assert(std::dynamic_pointer_cast<const ImageLocalData>(currentLocalData));
auto currentImageLocalData =
std::static_pointer_cast<const ImageLocalData>(currentLocalData);
if (currentImageLocalData->getImageSource() == imageSource) {
// Same `imageSource` is already in `localData`,
// no need to (re)request an image resource.
return;
}
}
// Now we are about to mutate the Shadow Node.
void ImageShadowNode::updateStateIfNeeded() {
ensureUnsealed();
auto imageRequest = imageManager_->requestImage(imageSource, getSurfaceId());
auto imageLocalData =
std::make_shared<ImageLocalData>(imageSource, std::move(imageRequest));
setLocalData(imageLocalData);
auto const &imageSource = getImageSource();
auto const &currentState = getStateData();
if (currentState.getImageSource() == imageSource) {
return;
}
auto state = ImageState{
imageSource, imageManager_->requestImage(imageSource, getSurfaceId())};
setStateData(std::move(state));
}
ImageSource ImageShadowNode::getImageSource() const {
@@ -86,7 +78,7 @@ ImageSource ImageShadowNode::getImageSource() const {
#pragma mark - LayoutableShadowNode
void ImageShadowNode::layout(LayoutContext layoutContext) {
updateLocalData();
updateStateIfNeeded();
ConcreteViewShadowNode::layout(layoutContext);
}
@@ -9,6 +9,7 @@
#include <react/components/image/ImageEventEmitter.h>
#include <react/components/image/ImageProps.h>
#include <react/components/image/ImageState.h>
#include <react/components/view/ConcreteViewShadowNode.h>
#include <react/imagemanager/ImageManager.h>
#include <react/imagemanager/primitives.h>
@@ -24,7 +25,8 @@ extern const char ImageComponentName[];
class ImageShadowNode final : public ConcreteViewShadowNode<
ImageComponentName,
ImageProps,
ImageEventEmitter> {
ImageEventEmitter,
ImageState> {
public:
using ConcreteViewShadowNode::ConcreteViewShadowNode;
@@ -38,14 +40,11 @@ class ImageShadowNode final : public ConcreteViewShadowNode<
void layout(LayoutContext layoutContext) override;
private:
/*
* (Re)Creates a `LocalData` object (with `ImageRequest`) if needed.
*/
void updateLocalData();
ImageSource getImageSource() const;
SharedImageManager imageManager_;
void updateStateIfNeeded();
};
} // namespace react
@@ -0,0 +1,22 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "ImageState.h"
namespace facebook {
namespace react {
ImageSource ImageState::getImageSource() const {
return imageSource_;
}
ImageRequest const &ImageState::getImageRequest() const {
return *imageRequest_;
}
} // namespace react
} // namespace facebook
@@ -0,0 +1,57 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <folly/dynamic.h>
#include <react/imagemanager/ImageRequest.h>
#include <react/imagemanager/primitives.h>
namespace facebook {
namespace react {
/*
* State for <Image> component.
*/
class ImageState final {
public:
ImageState(ImageSource const &imageSource, ImageRequest imageRequest)
: imageSource_(imageSource),
imageRequest_(
std::make_shared<ImageRequest>(std::move(imageRequest))){};
ImageState() = default;
/*
* Returns stored ImageSource object.
*/
ImageSource getImageSource() const;
/*
* Exposes for reading stored `ImageRequest` object.
* `ImageRequest` object cannot be copied or moved from `ImageLocalData`.
*/
ImageRequest const &getImageRequest() const;
#ifdef ANDROID
ImageState(ImageState const &previousState, folly::dynamic data){};
/*
* Empty implementation for Android because it doesn't use this class.
*/
folly::dynamic getDynamic() const {
return {};
};
#endif
private:
ImageSource imageSource_;
std::shared_ptr<ImageRequest> imageRequest_;
};
} // namespace react
} // namespace facebook