windowIndexRange);
28 |
29 | void registerBindings();
30 | void unregisterBindings();
31 |
32 | virtual ~MGDataBindingImpl();
33 | };
34 |
35 | }; // namespace Wishlist
36 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "./",
4 | "paths": {
5 | "react-native-wishlist": ["./src/index"]
6 | },
7 | "allowUnreachableCode": false,
8 | "allowUnusedLabels": false,
9 | "esModuleInterop": true,
10 | "forceConsistentCasingInFileNames": true,
11 | "jsx": "react",
12 | "lib": ["esnext"],
13 | "module": "esnext",
14 | "moduleResolution": "node",
15 | "noFallthroughCasesInSwitch": true,
16 | "noImplicitReturns": true,
17 | "noImplicitUseStrict": false,
18 | "noStrictGenericChecks": false,
19 | "noUncheckedIndexedAccess": false,
20 | "noUnusedLocals": true,
21 | "noUnusedParameters": true,
22 | "resolveJsonModule": true,
23 | "skipLibCheck": true,
24 | "strict": true,
25 | "target": "esnext"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Components/WishlistImage.tsx:
--------------------------------------------------------------------------------
1 | import { Image, Platform } from 'react-native';
2 | import { createTemplateComponent } from '../createTemplateComponent';
3 |
4 | export const WishlistImage = createTemplateComponent(Image, {
5 | addProps: (item, props) => {
6 | 'worklet';
7 | if (Platform.OS === 'android') {
8 | const { source, ...rest } = props;
9 | const src = source != null && !Array.isArray(source) ? [source] : source;
10 | item.addProps({
11 | src,
12 | ...rest,
13 | });
14 | } else {
15 | item.addProps(props);
16 | }
17 | },
18 | additionalTemplateProps: [
19 | 'style.borderRadius',
20 | 'style.borderTopLeftRadius',
21 | 'style.borderTopRightRadius',
22 | 'style.borderBottomLeftRadius',
23 | 'style.borderBottomRightRadius',
24 | ],
25 | });
26 |
--------------------------------------------------------------------------------
/example/android/app/src/release/java/com/wishlistexample/ReactNativeFlipper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Meta Platforms, Inc. and 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.wishlistexample;
8 |
9 | import android.content.Context;
10 | import com.facebook.react.ReactInstanceManager;
11 |
12 | /**
13 | * Class responsible of loading Flipper inside your React Native application. This is the release
14 | * flavor of it so it's empty as we don't want to load Flipper.
15 | */
16 | public class ReactNativeFlipper {
17 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
18 | // Do nothing as we don't want to initialize Flipper on Release.
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/example/ios/WishlistExampleTests/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 |
--------------------------------------------------------------------------------
/example/src/AssetList/Header.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, View, Image } from 'react-native';
3 |
4 | type HeaderProps = {};
5 |
6 | const VITALIK_AVATAR =
7 | 'https://gateway.ipfs.io/ipfs/QmSP4nq9fnN9dAiCj42ug9Wa79rqmQerZXZch82VqpiH7U/image.gif';
8 |
9 | export function Header({}: HeaderProps) {
10 | return (
11 |
12 |
13 |
14 | );
15 | }
16 |
17 | const styles = StyleSheet.create({
18 | container: {
19 | height: 108,
20 | paddingTop: 58,
21 | paddingHorizontal: 19,
22 | flexDirection: 'row',
23 | alignItems: 'center',
24 | justifyContent: 'space-between',
25 | },
26 | avatar: {
27 | width: 34,
28 | height: 34,
29 | borderRadius: 17,
30 | backgroundColor: '#9DA0A8',
31 | },
32 | });
33 |
--------------------------------------------------------------------------------
/android/src/main/java/com/wishlist/TemplateInterceptor.kt:
--------------------------------------------------------------------------------
1 | package com.wishlist
2 |
3 | import android.content.Context
4 | import android.view.View
5 | import android.view.ViewGroup
6 | import com.facebook.react.views.view.ReactViewGroup
7 |
8 | class TemplateInterceptor(reactContext: Context) :
9 | ReactViewGroup(reactContext), ViewGroup.OnHierarchyChangeListener {
10 | private var wishlist: Wishlist? = null
11 |
12 | init {
13 | setOnHierarchyChangeListener(this)
14 | }
15 |
16 | override fun onChildViewAdded(parent: View, child: View) {
17 | if (child is Wishlist) {
18 | wishlist = child
19 | } else if (child is TemplateContainer && wishlist != null) {
20 | child.wishlist = wishlist
21 | }
22 | }
23 |
24 | override fun onChildViewRemoved(parent: View, child: View) {
25 | if (child is Wishlist) {
26 | wishlist = null
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/example/src/AssetList/AssetListHeader.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, Text, View } from 'react-native';
3 |
4 | export function AssetListHeader() {
5 | return (
6 |
7 | vitalik.eth
8 |
9 | $2,773,494.65
10 |
11 | );
12 | }
13 |
14 | const styles = StyleSheet.create({
15 | container: {
16 | paddingHorizontal: 19,
17 | paddingVertical: 5,
18 | height: 50,
19 | flexDirection: 'row',
20 | justifyContent: 'space-between',
21 | alignItems: 'center',
22 | },
23 | name: {
24 | fontSize: 24,
25 | fontWeight: '800',
26 | letterSpacing: 0.4,
27 | textAlign: 'left',
28 | },
29 | balance: {
30 | fontSize: 24,
31 | fontWeight: '600',
32 | letterSpacing: 0.4,
33 | textAlign: 'right',
34 | },
35 | });
36 |
--------------------------------------------------------------------------------
/scripts/bootstrap.js:
--------------------------------------------------------------------------------
1 | const os = require('os');
2 | const path = require('path');
3 | const child_process = require('child_process');
4 |
5 | const root = path.resolve(__dirname, '..');
6 | const args = process.argv.slice(2);
7 | const options = {
8 | cwd: process.cwd(),
9 | env: process.env,
10 | stdio: 'inherit',
11 | encoding: 'utf-8',
12 | };
13 |
14 | if (os.type() === 'Windows_NT') {
15 | options.shell = true;
16 | }
17 |
18 | let result;
19 |
20 | if (process.cwd() !== root || args.length) {
21 | // We're not in the root of the project, or additional arguments were passed
22 | // In this case, forward the command to `yarn`
23 | result = child_process.spawnSync('yarn', args, options);
24 | } else {
25 | // If `yarn` is run without arguments, perform bootstrap
26 | result = child_process.spawnSync('yarn', ['bootstrap'], options);
27 | }
28 |
29 | process.exitCode = result.status;
30 |
--------------------------------------------------------------------------------
/cpp/ContentContainer/MGContentContainerState.cpp:
--------------------------------------------------------------------------------
1 | #include "MGContentContainerState.h"
2 |
3 | namespace facebook {
4 | namespace react {
5 |
6 | MGContentContainerState::MGContentContainerState()
7 | : wishlistChildren(nullptr){};
8 |
9 | MGContentContainerState::MGContentContainerState(
10 | const ShadowNode::SharedListOfShared &wishlistChildren)
11 | : wishlistChildren(wishlistChildren) {}
12 |
13 | #ifdef ANDROID
14 |
15 | MGContentContainerState::MGContentContainerState(
16 | MGContentContainerState const &previousState,
17 | folly::dynamic data)
18 | : wishlistChildren(previousState.wishlistChildren){};
19 |
20 | folly::dynamic MGContentContainerState::getDynamic() const {
21 | return folly::dynamic::object;
22 | };
23 |
24 | MapBuffer MGContentContainerState::getMapBuffer() const {
25 | return MapBufferBuilder::EMPTY();
26 | };
27 |
28 | #endif
29 |
30 | } // namespace react
31 | } // namespace facebook
32 |
--------------------------------------------------------------------------------
/ios/Orchestrator/MGOrchestratorCppAdapter.hpp:
--------------------------------------------------------------------------------
1 | //
2 | // MGOrchestratorCppAdapter.hpp
3 | // CocoaAsyncSocket
4 | //
5 | // Created by Szymon on 14/01/2023.
6 | //
7 |
8 | #pragma once
9 |
10 | #include
11 | #include
12 | #include "MGVSyncRequester.hpp"
13 | #include "MGViewportCarerListener.hpp"
14 |
15 | namespace Wishlist {
16 |
17 | class MGOrchestratorCppAdapter final : public MGVSyncRequester,
18 | public MGViewportCarerListener {
19 | public:
20 | MGOrchestratorCppAdapter(
21 | std::function onRequestVSync,
22 | std::function items)> didPushChildren);
23 |
24 | private:
25 | void didPushChildren(std::vector- newWindow) override;
26 | void requestVSync() override;
27 |
28 | std::function onRequestVSync_;
29 | std::function items)> didPushChildren_;
30 | };
31 |
32 | }; // namespace Wishlist
33 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # XDE
6 | .expo/
7 |
8 | # VSCode
9 | .vscode/
10 | jsconfig.json
11 |
12 | # Xcode
13 | #
14 | build/
15 | *.pbxuser
16 | !default.pbxuser
17 | *.mode1v3
18 | !default.mode1v3
19 | *.mode2v3
20 | !default.mode2v3
21 | *.perspectivev3
22 | !default.perspectivev3
23 | xcuserdata
24 | *.xccheckout
25 | *.moved-aside
26 | DerivedData
27 | *.hmap
28 | *.ipa
29 | *.xcuserstate
30 | project.xcworkspace
31 |
32 | # Android/IJ
33 | #
34 | .classpath
35 | .cxx
36 | .gradle
37 | .idea
38 | .project
39 | .settings
40 | local.properties
41 | android.iml
42 |
43 | # Cocoapods
44 | #
45 | example/ios/Pods
46 |
47 | # Ruby
48 | example/vendor/
49 |
50 | # node.js
51 | #
52 | node_modules/
53 | npm-debug.log
54 | yarn-debug.log
55 | yarn-error.log
56 |
57 | # BUCK
58 | buck-out/
59 | \.buckd/
60 | android/app/libs
61 | android/keystores/debug.keystore
62 |
63 | # Expo
64 | .expo/*
65 |
66 | # generated by bob
67 | lib/
68 |
--------------------------------------------------------------------------------
/MGWishList.podspec:
--------------------------------------------------------------------------------
1 | require "json"
2 |
3 | package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4 |
5 | Pod::Spec.new do |s|
6 | s.name = "MGWishList"
7 | s.version = package["version"]
8 | s.summary = package["description"]
9 | s.description = <<-DESC
10 | The fastest List component for React Native.
11 | DESC
12 | s.homepage = "https://github.com/margelo/react-native-wishlist"
13 | s.license = "MIT"
14 | # s.license = { :type => "MIT", :file => "FILE_LICENSE" }
15 | s.author = { "author" => "author@domain.cn" }
16 | s.platforms = { :ios => "13.4", :tvos => "13.4" }
17 | s.source = { :git => "https://github.com/margelo/react-native-wishlist.git", :tag => "#{s.version}" }
18 |
19 | s.source_files = [
20 | "ios/**/*.{mm,h,m,cpp,hpp}",
21 | "cpp/**/*.{cpp,h,m,mm,hpp}",
22 | ]
23 |
24 | install_modules_dependencies(s)
25 | end
26 |
27 |
--------------------------------------------------------------------------------
/cpp/DependencyInjection/MGDI.hpp:
--------------------------------------------------------------------------------
1 | //
2 | // MGDI.hpp
3 | // MGWishList
4 | //
5 | // Created by Szymon on 13/01/2023.
6 | //
7 |
8 | #pragma once
9 |
10 | #include
11 | #include
12 |
13 | #include "MGBindingProvider.hpp"
14 | #include "MGDataBinding.hpp"
15 | #include "MGErrorHandler.h"
16 | #include "MGUIScheduler.hpp"
17 | #include "MGVSyncRequester.hpp"
18 | #include "MGViewportCarer.hpp"
19 | #include "MGViewportCarerListener.hpp"
20 |
21 | namespace Wishlist {
22 |
23 | struct MGDI {
24 | virtual std::shared_ptr getDataBinding() = 0;
25 | virtual std::shared_ptr getVSyncRequester() = 0;
26 | virtual std::shared_ptr getViewportCarer() = 0;
27 | virtual std::shared_ptr getUIScheduler() = 0;
28 | virtual std::shared_ptr getErrorHandler() = 0;
29 |
30 | virtual ~MGDI() {}
31 | };
32 |
33 | }; // namespace Wishlist
34 |
--------------------------------------------------------------------------------
/cpp/ContentContainer/MGContentContainerShadowNode.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include "MGContentContainerState.h"
7 |
8 | namespace facebook {
9 | namespace react {
10 |
11 | JSI_EXPORT extern const char MGContentContainerComponentName[];
12 |
13 | /*
14 | * `ShadowNode` for component.
15 | */
16 | class JSI_EXPORT MGContentContainerShadowNode final
17 | : public ConcreteViewShadowNode<
18 | MGContentContainerComponentName,
19 | MGContentContainerProps,
20 | ViewEventEmitter,
21 | MGContentContainerState> {
22 | using ConcreteViewShadowNode::ConcreteViewShadowNode;
23 |
24 | public:
25 | void setWishlistChildren(
26 | const ShadowNode::SharedListOfShared &wishlistChildren);
27 | };
28 |
29 | } // namespace react
30 | } // namespace facebook
31 |
--------------------------------------------------------------------------------
/lefthook.yml:
--------------------------------------------------------------------------------
1 | # EXAMPLE USAGE
2 | # Refer for explanation to following link:
3 | # https://github.com/evilmartians/lefthook/blob/master/docs/full_guide.md
4 | #
5 | # pre-push:
6 | # commands:
7 | # packages-audit:
8 | # tags: frontend security
9 | # run: yarn audit
10 | # gems-audit:
11 | # tags: backend security
12 | # run: bundle audit
13 | #
14 | # pre-commit:
15 | # parallel: true
16 | # commands:
17 | # eslint:
18 | # glob: "*.{js,ts}"
19 | # run: yarn eslint {staged_files}
20 | # rubocop:
21 | # tags: backend style
22 | # glob: "*.rb"
23 | # exclude: "application.rb|routes.rb"
24 | # run: bundle exec rubocop --force-exclusion {all_files}
25 | # govet:
26 | # tags: backend style
27 | # files: git ls-files -m
28 | # glob: "*.go"
29 | # run: go vet {files}
30 | # scripts:
31 | # "hello.js":
32 | # runner: node
33 | # "any.go":
34 | # runner: go run
35 |
--------------------------------------------------------------------------------
/cpp/ContentContainer/MGContentContainerState.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #ifdef ANDROID
6 | #include
7 | #include
8 | #include
9 | #endif
10 |
11 | namespace facebook {
12 | namespace react {
13 |
14 | /*
15 | * State for component.
16 | */
17 | class JSI_EXPORT MGContentContainerState final {
18 | public:
19 | ShadowNode::SharedListOfShared wishlistChildren;
20 |
21 | MGContentContainerState();
22 | MGContentContainerState(
23 | const ShadowNode::SharedListOfShared &wishlistChildren);
24 |
25 | #ifdef ANDROID
26 | MGContentContainerState(
27 | MGContentContainerState const &previousState,
28 | folly::dynamic data);
29 | folly::dynamic getDynamic() const;
30 | MapBuffer getMapBuffer() const;
31 | #endif
32 | };
33 |
34 | } // namespace react
35 | } // namespace facebook
36 |
--------------------------------------------------------------------------------
/android/src/main/java/com/wishlist/ContentContainer.kt:
--------------------------------------------------------------------------------
1 | package com.wishlist
2 |
3 | import com.facebook.react.module.annotations.ReactModule
4 | import com.facebook.react.uimanager.ThemedReactContext
5 | import com.facebook.react.uimanager.ViewGroupManager
6 | import com.facebook.react.viewmanagers.MGContentContainerManagerDelegate
7 | import com.facebook.react.viewmanagers.MGContentContainerManagerInterface
8 | import com.facebook.react.views.view.ReactViewGroup
9 |
10 | @ReactModule(name = ContentContainerViewManager.REACT_CLASS)
11 | class ContentContainerViewManager :
12 | ViewGroupManager(), MGContentContainerManagerInterface {
13 | companion object {
14 | const val REACT_CLASS = "MGContentContainer"
15 | }
16 |
17 | override fun getName() = REACT_CLASS
18 |
19 | override fun createViewInstance(reactContext: ThemedReactContext) = ReactViewGroup(reactContext)
20 |
21 | override fun getDelegate() = MGContentContainerManagerDelegate(this)
22 | }
23 |
--------------------------------------------------------------------------------
/android/src/main/java/com/wishlist/TemplateInterceptorViewManager.kt:
--------------------------------------------------------------------------------
1 | package com.wishlist
2 |
3 | import com.facebook.react.module.annotations.ReactModule
4 | import com.facebook.react.uimanager.ThemedReactContext
5 | import com.facebook.react.uimanager.ViewGroupManager
6 | import com.facebook.react.viewmanagers.MGTemplateInterceptorManagerDelegate
7 | import com.facebook.react.viewmanagers.MGTemplateInterceptorManagerInterface
8 |
9 | @ReactModule(name = TemplateInterceptorViewManager.REACT_CLASS)
10 | class TemplateInterceptorViewManager :
11 | ViewGroupManager(),
12 | MGTemplateInterceptorManagerInterface {
13 | companion object {
14 | const val REACT_CLASS = "MGTemplateInterceptor"
15 | }
16 |
17 | override fun getName() = REACT_CLASS
18 |
19 | override fun createViewInstance(reactContext: ThemedReactContext) =
20 | TemplateInterceptor(reactContext)
21 |
22 | override fun getDelegate() = MGTemplateInterceptorManagerDelegate(this)
23 | }
24 |
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "WishlistExample",
3 | "version": "0.0.1",
4 | "private": true,
5 | "scripts": {
6 | "android": "react-native run-android --active-arch-only",
7 | "ios": "react-native run-ios",
8 | "start": "react-native start",
9 | "pods": "pod-install --quiet",
10 | "postinstall": "patch-package"
11 | },
12 | "dependencies": {
13 | "react": "^18.2.0",
14 | "react-native": "^0.72.3",
15 | "react-native-gesture-handler": "^2.12.0",
16 | "react-native-worklets": "janicduplessis/react-native-worklets#react-native-worklets-v0.1.0-alpha.4-gitpkg"
17 | },
18 | "devDependencies": {
19 | "@babel/core": "^7.20.0",
20 | "@babel/runtime": "^7.20.0",
21 | "@react-native/metro-config": "^0.72.9",
22 | "metro-react-native-babel-preset": "0.76.7",
23 | "string-hash-64": "^1.0.3",
24 | "babel-plugin-module-resolver": "^4.1.0",
25 | "patch-package": "^6.4.7",
26 | "postinstall-postinstall": "^2.1.0"
27 | },
28 | "engines": {
29 | "node": ">=16"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/cpp/MGViewportCarer/MGViewportCarer.hpp:
--------------------------------------------------------------------------------
1 | //
2 | // MGViewportCarer.hpp
3 | // MGWishList
4 | //
5 | // Created by Szymon on 13/01/2023.
6 | //
7 |
8 | #pragma once
9 |
10 | #include
11 | #include
12 |
13 | namespace Wishlist {
14 |
15 | using namespace facebook::react;
16 |
17 | static float MG_NO_OFFSET = std::numeric_limits::min();
18 |
19 | struct MGDims {
20 | float width;
21 | float height;
22 | };
23 |
24 | class MGViewportCarer {
25 | public:
26 | virtual void initialRenderAsync(
27 | MGDims dimensions,
28 | float initialContentSize,
29 | int originItem,
30 | const std::vector> ®isteredViews,
31 | const std::vector &names,
32 | const std::string &inflatorId) = 0;
33 |
34 | virtual void didScrollAsync(
35 | MGDims dimensions,
36 | float contentOffset,
37 | const std::string &inflatorId) = 0;
38 |
39 | virtual void didUpdateContentOffset() = 0;
40 | };
41 |
42 | }; // namespace Wishlist
43 |
--------------------------------------------------------------------------------
/ios/MGWishlistQueue.m:
--------------------------------------------------------------------------------
1 | #import "MGWishlistQueue.h"
2 |
3 | dispatch_queue_t MGGetWishlistQueue(void)
4 | {
5 | static dispatch_queue_t wishlistQueue;
6 | static dispatch_once_t onceToken;
7 | dispatch_once(&onceToken, ^{
8 | dispatch_queue_attr_t qos =
9 | dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, -1);
10 | wishlistQueue = dispatch_queue_create("wishlistqueue", qos);
11 | });
12 | return wishlistQueue;
13 | }
14 |
15 | BOOL MGIsWishlistQueue(void)
16 | {
17 | static void *wishlistQueueKey = &wishlistQueueKey;
18 | static dispatch_once_t onceToken;
19 | dispatch_once(&onceToken, ^{
20 | dispatch_queue_set_specific(MGGetWishlistQueue(), wishlistQueueKey, wishlistQueueKey, NULL);
21 | });
22 | return dispatch_get_specific(wishlistQueueKey) == wishlistQueueKey;
23 | }
24 |
25 | void MGExecuteOnWishlistQueue(dispatch_block_t block)
26 | {
27 | if (MGIsWishlistQueue()) {
28 | block();
29 | } else {
30 | dispatch_async(MGGetWishlistQueue(), ^{
31 | block();
32 | });
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/ios/MGContentContainerComponent.mm:
--------------------------------------------------------------------------------
1 | #import "MGContentContainerComponent.h"
2 |
3 | #import
4 | #import
5 | #import "MGContentContainerComponentDescriptor.h"
6 | #import "RCTFabricComponentsPlugins.h"
7 |
8 | using namespace facebook::react;
9 |
10 | @implementation MGContentContainerComponent
11 |
12 | - (instancetype)initWithFrame:(CGRect)frame
13 | {
14 | if (self = [super initWithFrame:frame]) {
15 | static const auto defaultProps = std::make_shared();
16 | _props = defaultProps;
17 | }
18 |
19 | return self;
20 | }
21 |
22 | #pragma mark - RCTComponentViewProtocol
23 |
24 | + (facebook::react::ComponentDescriptorProvider)componentDescriptorProvider
25 | {
26 | return facebook::react::concreteComponentDescriptorProvider();
27 | }
28 |
29 | @end
30 |
31 | Class MGContentContainerCls(void)
32 | {
33 | return MGContentContainerComponent.class;
34 | }
35 |
--------------------------------------------------------------------------------
/cpp/Wishlist/MGWishlistState.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | #ifdef ANDROID
7 | #include
8 | #include
9 | #include
10 | #endif
11 |
12 | #include "ComponentsPool.h"
13 | #include "MGViewportCarerImpl.h"
14 |
15 | using namespace Wishlist;
16 |
17 | namespace facebook {
18 | namespace react {
19 |
20 | // class WishlistShadowNode;
21 |
22 | /*
23 | * State for component.
24 | */
25 | class JSI_EXPORT MGWishlistState final {
26 | public:
27 | bool initialised;
28 | std::shared_ptr viewportCarer;
29 | Rect contentBoundingRect;
30 | float contentOffset;
31 |
32 | MGWishlistState();
33 |
34 | #ifdef ANDROID
35 | MGWishlistState(MGWishlistState const &previousState, folly::dynamic data);
36 | folly::dynamic getDynamic() const;
37 | MapBuffer getMapBuffer() const;
38 | #endif
39 | };
40 |
41 | } // namespace react
42 | } // namespace facebook
43 |
--------------------------------------------------------------------------------
/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
12 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/ComponentPool.ts:
--------------------------------------------------------------------------------
1 | import { TemplateItem } from './TemplateItem';
2 |
3 | export type NodeType = 'View' | 'Text' | 'Paragraph' | 'RawText';
4 |
5 | export type NativeComponentPool = {
6 | getComponent: (id: string) => TemplateItem | undefined;
7 | };
8 |
9 | export type ComponentPool = NativeComponentPool & {
10 | createNode(type: NodeType): TemplateItem;
11 | };
12 |
13 | export function wrapComponentPool(pool: NativeComponentPool): ComponentPool {
14 | 'worklet';
15 |
16 | return {
17 | getComponent: pool.getComponent,
18 | createNode(type) {
19 | switch (type) {
20 | case 'Paragraph':
21 | return pool.getComponent('__paragraphComponent')!.Paragraph!;
22 | case 'Text':
23 | return pool.getComponent('__textComponent')!.Paragraph!.Text!;
24 | case 'RawText':
25 | return pool.getComponent('__paragraphComponent')!.Paragraph!.RawText!;
26 | case 'View':
27 | return pool.getComponent('__viewComponent')!;
28 | default:
29 | throw new Error(`Unknown node type ${type}`);
30 | }
31 | },
32 | };
33 | }
34 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Margelo GmbH
4 | Permission is hereby granted, free of charge, to any person obtaining a copy
5 | of this software and associated documentation files (the "Software"), to deal
6 | in the Software without restriction, including without limitation the rights
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the Software is
9 | furnished to do so, subject to the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included in all
12 | copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 | SOFTWARE.
21 |
--------------------------------------------------------------------------------
/cpp/Wishlist/MGWishlistShadowNode.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include "MGWishlistState.h"
9 |
10 | namespace facebook {
11 | namespace react {
12 |
13 | JSI_EXPORT extern const char MGWishlistComponentName[];
14 |
15 | /*
16 | * `ShadowNode` for component.
17 | */
18 | class JSI_EXPORT MGWishlistShadowNode
19 | : public ConcreteViewShadowNode<
20 | MGWishlistComponentName,
21 | MGWishlistProps,
22 | MGWishlistEventEmitter,
23 | MGWishlistState>,
24 | public std::enable_shared_from_this {
25 | using ConcreteViewShadowNode::ConcreteViewShadowNode;
26 |
27 | public:
28 | void layout(LayoutContext layoutContext) override;
29 |
30 | void updateContentOffset(float contentOffset);
31 |
32 | private:
33 | void updateStateIfNeeded();
34 | };
35 |
36 | } // namespace react
37 | } // namespace facebook
38 |
--------------------------------------------------------------------------------
/example/ios/WishlistExample/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ios-marketing",
45 | "scale" : "1x",
46 | "size" : "1024x1024"
47 | }
48 | ],
49 | "info" : {
50 | "author" : "xcode",
51 | "version" : 1
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/cpp/TemplateContainer/MGTemplateContainerState.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | #ifdef ANDROID
7 | #include
8 | #include
9 | #include
10 | #endif
11 |
12 | namespace facebook {
13 | namespace react {
14 |
15 | class JSI_EXPORT MGTemplateContainerState final {
16 | public:
17 | MGTemplateContainerState(){};
18 | MGTemplateContainerState(
19 | std::vector> const &templates)
20 | : templates_(templates){};
21 |
22 | const std::vector> &getTemplates() const;
23 |
24 | #ifdef ANDROID
25 | MGTemplateContainerState(
26 | MGTemplateContainerState const &previousState,
27 | folly::dynamic data){};
28 |
29 | folly::dynamic getDynamic() const;
30 |
31 | MapBuffer getMapBuffer() const {
32 | return MapBufferBuilder::EMPTY();
33 | };
34 | #endif
35 |
36 | private:
37 | std::vector> templates_;
38 | };
39 |
40 | } // namespace react
41 | } // namespace facebook
42 |
--------------------------------------------------------------------------------
/example/ios/WishlistExample/AppDelegate.mm:
--------------------------------------------------------------------------------
1 | #import "AppDelegate.h"
2 |
3 | #import
4 | #import
5 | #import "MGWishlistManager.h"
6 |
7 | @implementation AppDelegate
8 |
9 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
10 | {
11 | self.moduleName = @"WishlistExample";
12 | // You can add your custom initial props in the dictionary below.
13 | // They will be passed down to the ViewController used by React Native.
14 | self.initialProps = @{};
15 |
16 | return [super application:application didFinishLaunchingWithOptions:launchOptions];
17 | }
18 |
19 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
20 | {
21 | #if DEBUG
22 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
23 | #else
24 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
25 | #endif
26 | }
27 |
28 | - (Class)getModuleClassFromName:(const char *)name
29 | {
30 | if (strcmp(name, "WishlistManager") == 0) {
31 | return MGWishlistManager.class;
32 | }
33 | return RCTCoreModulesClassProvider(name);
34 | }
35 |
36 | @end
37 |
--------------------------------------------------------------------------------
/android/src/main/java/com/wishlist/TemplateContainer.kt:
--------------------------------------------------------------------------------
1 | package com.wishlist
2 |
3 | import android.content.Context
4 | import com.facebook.react.uimanager.FabricViewStateManager
5 | import com.facebook.react.uimanager.FabricViewStateManager.HasFabricViewStateManager
6 | import com.facebook.react.views.view.ReactViewGroup
7 |
8 | class TemplateContainer(reactContext: Context) :
9 | ReactViewGroup(reactContext), HasFabricViewStateManager {
10 | var inflatorId: String? = null
11 | var wishlistId: String? = null
12 | var names: List? = null
13 | var wishlist: Wishlist? = null
14 | set(value) {
15 | field = value
16 | updateWishlist()
17 | }
18 | private val fabricViewStateManager: FabricViewStateManager = FabricViewStateManager()
19 |
20 | override fun getFabricViewStateManager() = fabricViewStateManager
21 |
22 | fun updateWishlist() {
23 | wishlist?.let {
24 | it.wishlistId = wishlistId
25 | it.inflatorId = inflatorId
26 | val templatesRef = fabricViewStateManager.stateData?.getInt("templates")
27 | if (templatesRef != null) {
28 | it.setTemplates(templatesRef, names ?: listOf())
29 | }
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-native-wishlist
2 |
3 | The fastest List component for React Native.
4 |
5 |
6 | ```jsx
7 | function ChatRoom({ room }) {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | )
18 | }
19 | ```
20 |
21 | ## State of WishList
22 |
23 | WishList is an archived, read-only repository, and should probably not be used in production. It's a good proof of concept, and a pretty impressive experiment. See [this Notion document](https://margelo.notion.site/WishList-Summit-b20c24d1f0da4889a0513dfa929be5ed?pvs=74) for more details.
24 |
25 | ## Installation
26 |
27 | ```sh
28 | yarn add react-native-worklets # still private
29 | yarn add react-native-wishlist
30 | cd ios && pod install
31 | ```
32 |
33 | ## Usage
34 |
35 | See [USAGE.md](./USAGE.md)
36 |
37 | ## Contributing
38 |
39 | See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.
40 |
41 | ## License
42 |
43 | MIT
44 |
--------------------------------------------------------------------------------
/example/src/Chat/ReactionPicker.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
3 | import { emoticons } from './Data';
4 |
5 | type ReactionPickerProps = {
6 | onPickReaction: (emoji: string) => void;
7 | };
8 |
9 | export function ReactionPicker({ onPickReaction }: ReactionPickerProps) {
10 | return (
11 |
12 |
13 | {emoticons.map((emoji, index) => (
14 | onPickReaction(emoji)}
16 | key={String(index)}
17 | >
18 | {emoji}
19 |
20 | ))}
21 |
22 |
23 | );
24 | }
25 |
26 | const styles = StyleSheet.create({
27 | backdrop: {
28 | ...StyleSheet.absoluteFillObject,
29 | backgroundColor: 'rgba(0,0,0,0.5)',
30 | justifyContent: 'center',
31 | alignItems: 'center',
32 | },
33 | container: {
34 | flexDirection: 'row',
35 | padding: 24,
36 | backgroundColor: 'white',
37 | borderRadius: 24,
38 | },
39 | emoji: {
40 | fontSize: 38,
41 | paddingHorizontal: 8,
42 | },
43 | });
44 |
--------------------------------------------------------------------------------
/android/src/main/java/com/wishlist/Orchestrator.kt:
--------------------------------------------------------------------------------
1 | package com.wishlist
2 |
3 | import com.facebook.jni.HybridData
4 | import com.facebook.proguard.annotations.DoNotStrip
5 | import com.facebook.react.uimanager.PixelUtil
6 |
7 | class Orchestrator(private val mWishlist: Wishlist, wishlistId: String, viewportCarerRef: Int) {
8 | companion object {
9 | init {
10 | WishlistSoLoader.staticInit()
11 | }
12 | }
13 |
14 | @field:DoNotStrip private val mHybridData = initHybrid(wishlistId, viewportCarerRef)
15 |
16 | private external fun initHybrid(wishlistId: String, viewportCarerRef: Int): HybridData
17 |
18 | external fun renderAsync(
19 | width: Float,
20 | height: Float,
21 | initialContentSize: Float,
22 | originItem: Int,
23 | templatesRef: Int,
24 | names: List,
25 | inflatorId: String
26 | )
27 |
28 | external fun didScrollAsync(width: Float, height: Float, contentOffset: Float, inflatorId: String)
29 |
30 | external fun scrollToItem(index: Int)
31 |
32 | external fun didUpdateContentOffset()
33 |
34 | @DoNotStrip
35 | private fun scrollToOffset(offset: Float, animated: Boolean) {
36 | mWishlist.reactSmoothScrollTo(0, PixelUtil.toPixelFromDIP(offset).toInt())
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/cpp/WishlistJsRuntime.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | using namespace facebook;
11 |
12 | namespace Wishlist {
13 |
14 | class WishlistJsRuntime {
15 | public:
16 | static WishlistJsRuntime &getInstance();
17 |
18 | void initialize(
19 | jsi::Runtime *runtime,
20 | std::function &&)> jsCallInvoker,
21 | std::function &&)> workletCallInvoker);
22 |
23 | jsi::Runtime &getRuntime() const;
24 | void accessRuntime(std::function &&f) const;
25 | void accessRuntimeSync(std::function &&f) const;
26 |
27 | private:
28 | WishlistJsRuntime();
29 | WishlistJsRuntime(const WishlistJsRuntime &) = delete;
30 | WishlistJsRuntime &operator=(const WishlistJsRuntime &) = delete;
31 |
32 | class Decorator : public RNWorklet::JsiBaseDecorator {
33 | void decorateRuntime(jsi::Runtime &runtime) override;
34 | };
35 |
36 | std::shared_ptr workletContext_;
37 | };
38 |
39 | }; // namespace Wishlist
40 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/com/wishlistexample/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.wishlistexample;
2 |
3 | import com.facebook.react.ReactActivity;
4 | import com.facebook.react.ReactActivityDelegate;
5 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
6 | import com.facebook.react.defaults.DefaultReactActivityDelegate;
7 |
8 | public class MainActivity extends ReactActivity {
9 |
10 | /**
11 | * Returns the name of the main component registered from JavaScript. This is used to schedule
12 | * rendering of the component.
13 | */
14 | @Override
15 | protected String getMainComponentName() {
16 | return "WishlistExample";
17 | }
18 |
19 | /**
20 | * Returns the instance of the {@link ReactActivityDelegate}. Here we use a util class {@link
21 | * DefaultReactActivityDelegate} which allows you to easily enable Fabric and Concurrent React
22 | * (aka React 18) with two boolean flags.
23 | */
24 | @Override
25 | protected ReactActivityDelegate createReactActivityDelegate() {
26 | return new DefaultReactActivityDelegate(
27 | this,
28 | getMainComponentName(),
29 | // If you opted-in for the New Architecture, we enable the Fabric Renderer.
30 | DefaultNewArchitectureEntryPoint.getFabricEnabled());
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Specs/NativeWishlist.ts:
--------------------------------------------------------------------------------
1 | import type * as React from 'react';
2 | import type { ViewProps } from 'react-native';
3 | import type {
4 | DirectEventHandler,
5 | Double,
6 | Int32,
7 | } from 'react-native/Libraries/Types/CodegenTypes';
8 | import codegenNativeCommands from 'react-native/Libraries/Utilities/codegenNativeCommands';
9 | import codegenNativeComponent, {
10 | NativeComponentType,
11 | } from 'react-native/Libraries/Utilities/codegenNativeComponent';
12 |
13 | export type EventInFile = Readonly<{
14 | value: Double;
15 | }>;
16 |
17 | export interface WishlistProps extends ViewProps {
18 | inflatorId: string;
19 | initialIndex: Int32;
20 | onStartReached?: DirectEventHandler>;
21 | onEndReached?: DirectEventHandler>;
22 | }
23 |
24 | type NativeType = NativeComponentType;
25 |
26 | export type ScrollToItem = (
27 | viewRef: React.ElementRef,
28 | index: Int32,
29 | animated: boolean,
30 | ) => void;
31 |
32 | interface NativeCommands {
33 | readonly scrollToItem: ScrollToItem;
34 | }
35 |
36 | export const Commands = codegenNativeCommands({
37 | supportedCommands: ['scrollToItem'],
38 | });
39 |
40 | export default codegenNativeComponent('MGWishlist', {
41 | interfaceOnly: true,
42 | });
43 |
--------------------------------------------------------------------------------
/cpp/Wishlist/MGWishlistState.cpp:
--------------------------------------------------------------------------------
1 | #include "MGWishlistState.h"
2 |
3 | #ifdef ANDROID
4 | #include "JNIStateRegistry.h"
5 | #endif
6 |
7 | namespace facebook {
8 | namespace react {
9 |
10 | MGWishlistState::MGWishlistState()
11 | : initialised(false),
12 | viewportCarer(std::make_shared()),
13 | contentBoundingRect({}),
14 | contentOffset(MG_NO_OFFSET){};
15 |
16 | #ifdef ANDROID
17 |
18 | MGWishlistState::MGWishlistState(
19 | MGWishlistState const &previousState,
20 | folly::dynamic data)
21 | : initialised(previousState.initialised),
22 | viewportCarer(previousState.viewportCarer),
23 | contentBoundingRect(previousState.contentBoundingRect),
24 | contentOffset(MG_NO_OFFSET){};
25 |
26 | folly::dynamic MGWishlistState::getDynamic() const {
27 | auto viewportCarerRef = Wishlist::JNIStateRegistry::getInstance().addValue(
28 | (void *)&viewportCarer);
29 | folly::dynamic result = folly::dynamic::object();
30 | result["viewportCarer"] = viewportCarerRef;
31 | if (contentOffset != MG_NO_OFFSET) {
32 | result["contentOffset"] = contentOffset;
33 | }
34 | return result;
35 | };
36 |
37 | MapBuffer MGWishlistState::getMapBuffer() const {
38 | return MapBufferBuilder::EMPTY();
39 | };
40 |
41 | #endif
42 |
43 | } // namespace react
44 | } // namespace facebook
45 |
--------------------------------------------------------------------------------
/ios/Orchestrator/MGOrchestrator.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #import
4 | #import
5 | #import
6 | #import
7 | #import "MGDI.hpp"
8 | #import "MGViewportCarerImpl.h"
9 |
10 | NS_ASSUME_NONNULL_BEGIN
11 |
12 | using namespace Wishlist;
13 |
14 | @class MGWishListComponent;
15 |
16 | @interface MGOrchestrator : NSObject
17 |
18 | - (instancetype)initWith:(MGWishListComponent *)wishlist
19 | wishlistId:(std::string)wishlistId
20 | viewportCarer:(std::shared_ptr)viewportCarer;
21 |
22 | - (void)renderAsyncWithDimensions:(MGDims)dimensions
23 | initialContentSize:(CGFloat)initialContentSize
24 | initialIndex:(NSInteger)initialIndex
25 | templates:(std::vector>)templates
26 | names:(std::vector)names
27 | inflatorId:(std::string)inflatorId;
28 | - (void)didScrollAsyncWithDimensions:(MGDims)dimensions
29 | contentOffset:(float)contentOffset
30 | inflatorId:(std::string)inflatorId;
31 | - (void)scrollToItem:(int)index;
32 |
33 | @end
34 |
35 | NS_ASSUME_NONNULL_END
36 |
--------------------------------------------------------------------------------
/.github/workflows/build-android.yml:
--------------------------------------------------------------------------------
1 | name: Build Android
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | paths:
8 | - '.github/workflows/build-android.yml'
9 | - 'android/**'
10 | - 'example/android/**'
11 | pull_request:
12 | paths:
13 | - '.github/workflows/build-android.yml'
14 | - 'android/**'
15 | - 'example/android/**'
16 |
17 | jobs:
18 | build:
19 | name: Build Android Example App
20 | runs-on: ubuntu-latest
21 | defaults:
22 | run:
23 | working-directory: example/android
24 | steps:
25 | - uses: actions/checkout@v2
26 |
27 | - name: Get yarn cache directory path
28 | id: yarn-cache-dir-path
29 | run: echo "::set-output name=dir::$(yarn cache dir)"
30 | - name: Restore node_modules from cache
31 | uses: actions/cache@v2
32 | id: yarn-cache
33 | with:
34 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
35 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
36 | restore-keys: |
37 | ${{ runner.os }}-yarn-
38 | - name: Install node_modules
39 | run: yarn install --frozen-lockfile --cwd ../..
40 | - name: Install node_modules for example/
41 | run: yarn install --frozen-lockfile --cwd ..
42 |
43 | - name: Build App
44 | run: ./gradlew assembleDebug
45 |
--------------------------------------------------------------------------------
/android/src/main/jni/WishlistManagerModule.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include "ErrorHandlerAndroid.h"
10 |
11 | using namespace facebook::react;
12 | using namespace facebook::jni;
13 |
14 | namespace Wishlist {
15 |
16 | class WishlistManagerModule : public HybridClass {
17 | public:
18 | WishlistManagerModule();
19 | ~WishlistManagerModule();
20 |
21 | static constexpr auto kJavaDescriptor =
22 | "Lcom/wishlist/WishlistManagerModule;";
23 |
24 | static void registerNatives();
25 |
26 | private:
27 | static local_ref initHybrid(alias_ref);
28 |
29 | void nativeInstall(
30 | jlong jsiRuntimeRef,
31 | alias_ref jsCallInvokerHolder,
32 | alias_ref fabricUIManager);
33 |
34 | private:
35 | friend HybridBase;
36 |
37 | std::shared_ptr wishlistQueue_;
38 | std::shared_ptr scheduler_;
39 | std::shared_ptr eventListener_;
40 | std::shared_ptr errorHandler_;
41 | };
42 |
43 | } // namespace Wishlist
44 |
--------------------------------------------------------------------------------
/cpp/TemplateContainer/MGTemplateContainerShadowNode.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include "MGTemplateContainerState.h"
9 |
10 | namespace facebook {
11 | namespace react {
12 |
13 | JSI_EXPORT extern const char MGTemplateContainerComponentName[];
14 |
15 | /*
16 | * `ShadowNode` for component.
17 | */
18 | class JSI_EXPORT MGTemplateContainerShadowNode final
19 | : public ConcreteViewShadowNode<
20 | MGTemplateContainerComponentName,
21 | MGTemplateContainerProps,
22 | MGTemplateContainerEventEmitter,
23 | MGTemplateContainerState> {
24 | using ConcreteViewShadowNode::ConcreteViewShadowNode;
25 |
26 | public:
27 | MGTemplateContainerShadowNode(
28 | ShadowNode const &sourceShadowNode,
29 | ShadowNodeFragment const &fragment);
30 |
31 | std::vector> templates;
32 |
33 | void appendChild(ShadowNode::Shared const &childNode) override;
34 |
35 | void layout(LayoutContext layoutContext) override;
36 |
37 | private:
38 | void updateStateIfNeeded();
39 | };
40 |
41 | } // namespace react
42 | } // namespace facebook
43 |
--------------------------------------------------------------------------------
/src/EventHandler.ts:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react';
2 | import { createRunInWishlistFn } from './WishlistJsRuntime';
3 |
4 | let done = false;
5 | const maybeInit = () => {
6 | if (!done) {
7 | done = true;
8 | createRunInWishlistFn(() => {
9 | 'worklet';
10 | global.handlers = {};
11 |
12 | global.handleEvent = (type: string, tag: number, event: any) => {
13 | // Events are prefixed with top sometimes.
14 | const key = tag.toString() + type.replace(/^topOn/, 'on');
15 | const callback = global.handlers[key];
16 | if (callback) {
17 | callback(event);
18 | }
19 | };
20 | })();
21 | }
22 | };
23 |
24 | export type TemplateCallbackWorklet = (
25 | nativeEvent: any,
26 | value: any,
27 | rootValue: any,
28 | ) => unknown;
29 |
30 | export class TemplateCallback {
31 | worklet: TemplateCallbackWorklet;
32 | eventName: string | undefined;
33 |
34 | constructor(worklet: TemplateCallbackWorklet, eventName?: string) {
35 | this.worklet = worklet;
36 | this.eventName = eventName;
37 | }
38 | }
39 |
40 | export function useTemplateCallback(
41 | worklet: (nativeEvent: any, value: any, rootValue: any) => unknown,
42 | eventName?: string,
43 | ) {
44 | return useMemo(() => {
45 | return new TemplateCallback(worklet, eventName);
46 | }, [worklet, eventName]);
47 | }
48 |
49 | export function initEventHandler() {
50 | maybeInit();
51 | }
52 |
--------------------------------------------------------------------------------
/cpp/Wishlist/MGWishlistShadowNode.cpp:
--------------------------------------------------------------------------------
1 | #include "MGWishlistShadowNode.h"
2 |
3 | namespace facebook {
4 | namespace react {
5 |
6 | extern const char MGWishlistComponentName[] = "MGWishlist";
7 |
8 | void MGWishlistShadowNode::layout(LayoutContext layoutContext) {
9 | auto state = getStateData();
10 | if (!state.initialised) {
11 | state.initialised = true;
12 | state.viewportCarer->setInitialValues(shared_from_this(), layoutContext);
13 |
14 | setStateData(std::move(state));
15 | }
16 |
17 | ConcreteViewShadowNode::layout(layoutContext);
18 |
19 | // TODO update viewportObserver if needed
20 |
21 | updateStateIfNeeded();
22 | }
23 |
24 | void MGWishlistShadowNode::updateStateIfNeeded() {
25 | ensureUnsealed();
26 |
27 | auto contentBoundingRect = Rect{};
28 | for (const auto &childNode : getLayoutableChildNodes()) {
29 | contentBoundingRect.unionInPlace(childNode->getLayoutMetrics().frame);
30 | }
31 |
32 | auto state = getStateData();
33 |
34 | if (state.contentBoundingRect != contentBoundingRect) {
35 | state.contentBoundingRect = contentBoundingRect;
36 | setStateData(std::move(state));
37 | }
38 | }
39 |
40 | void MGWishlistShadowNode::updateContentOffset(float contentOffset) {
41 | ensureUnsealed();
42 |
43 | auto state = getStateData();
44 | state.contentOffset = contentOffset;
45 | setStateData(std::move(state));
46 | }
47 |
48 | } // namespace react
49 | } // namespace facebook
50 |
--------------------------------------------------------------------------------
/cpp/ItemProvider/ComponentsPool.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include