getTurboModule(
27 | const std::string name,
28 | const JavaTurboModule::InitParams ¶ms) override;
29 |
30 | /**
31 | * Test-only method. Allows user to verify whether a TurboModule can be
32 | * created by instances of this class.
33 | */
34 | bool canCreateTurboModule(std::string name);
35 | };
36 |
37 | } // namespace react
38 | } // namespace facebook
39 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/com/reactions_example/newarchitecture/components/MainComponentsRegistry.java:
--------------------------------------------------------------------------------
1 | package com.reactions_example.newarchitecture.components;
2 |
3 | import com.facebook.jni.HybridData;
4 | import com.facebook.proguard.annotations.DoNotStrip;
5 | import com.facebook.react.fabric.ComponentFactory;
6 | import com.facebook.soloader.SoLoader;
7 |
8 | /**
9 | * Class responsible to load the custom Fabric Components. This class has native methods and needs a
10 | * corresponding C++ implementation/header file to work correctly (already placed inside the jni/
11 | * folder for you).
12 | *
13 | * Please note that this class is used ONLY if you opt-in for the New Architecture (see the
14 | * `newArchEnabled` property). Is ignored otherwise.
15 | */
16 | @DoNotStrip
17 | public class MainComponentsRegistry {
18 | static {
19 | SoLoader.loadLibrary("fabricjni");
20 | }
21 |
22 | @DoNotStrip private final HybridData mHybridData;
23 |
24 | @DoNotStrip
25 | private native HybridData initHybrid(ComponentFactory componentFactory);
26 |
27 | @DoNotStrip
28 | private MainComponentsRegistry(ComponentFactory componentFactory) {
29 | mHybridData = initHybrid(componentFactory);
30 | }
31 |
32 | @DoNotStrip
33 | public static MainComponentsRegistry register(ComponentFactory componentFactory) {
34 | return new MainComponentsRegistry(componentFactory);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/example/metro.config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Metro configuration for React Native
3 | * https://github.com/facebook/react-native
4 | *
5 | * @format
6 | */
7 | const path = require('path');
8 | const rootPackage = require('../package.json');
9 | const blacklist = require('metro-config/src/defaults/exclusionList');
10 | const rootModules = Object.keys({
11 | ...rootPackage.peerDependencies,
12 | });
13 | const moduleRoot = path.resolve(__dirname, '..');
14 | /**
15 | * Only load one version for peerDependencies and alias them to the versions in example's node_modules"
16 | */
17 | module.exports = {
18 | watchFolders: [moduleRoot],
19 | resolver: {
20 | blacklistRE: blacklist([
21 | ...rootModules.map(
22 | m =>
23 | new RegExp(
24 | `^${escape(path.join(moduleRoot, 'node_modules', m))}\\/.*$`
25 | )
26 | ),
27 | /^((?!example).)+[\/\\]node_modules[/\\]react[/\\].*/,
28 | /^((?!example).)+[\/\\]node_modules[/\\]react-native[/\\].*/,
29 | ]),
30 | extraNodeModules: {
31 | ...rootModules.reduce((acc, name) => {
32 | acc[name] = path.join(__dirname, 'node_modules', name);
33 | return acc;
34 | }, {}),
35 | },
36 | },
37 | transformer: {
38 | getTransformOptions: async () => ({
39 | transform: {
40 | experimentalImportSupport: false,
41 | inlineRequires: true,
42 | },
43 | }),
44 | },
45 | };
46 |
--------------------------------------------------------------------------------
/example/ios/Podfile:
--------------------------------------------------------------------------------
1 | require_relative '../node_modules/react-native/scripts/react_native_pods'
2 | require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
3 |
4 | platform :ios, '12.4'
5 | install! 'cocoapods', :deterministic_uuids => false
6 |
7 | production = ENV["PRODUCTION"] == "1"
8 |
9 | target 'reactions_example' do
10 | config = use_native_modules!
11 |
12 | # Flags change depending on the env values.
13 | flags = get_default_flags()
14 |
15 | use_react_native!(
16 | :path => config[:reactNativePath],
17 | # to enable hermes on iOS, change `false` to `true` and then install pods
18 | :production => production,
19 | # :hermes_enabled => flags[:hermes_enabled],
20 | # :fabric_enabled => flags[:fabric_enabled],
21 | # :flipper_configuration => FlipperConfiguration.enabled,
22 | # An absolute path to your application root.
23 | :app_path => "#{Pod::Config.instance.installation_root}/.."
24 | )
25 |
26 | target 'reactions_exampleTests' do
27 | inherit! :complete
28 | # Pods for testing
29 | end
30 |
31 | post_install do |installer|
32 | react_native_post_install(installer)
33 | __apply_Xcode_12_5_M1_post_install_workaround(installer)
34 | installer.pods_project.targets.each do |target|
35 | target.build_configurations.each do |config|
36 | config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.4'
37 | end
38 | end
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/example/android/app/_BUCK:
--------------------------------------------------------------------------------
1 | # To learn about Buck see [Docs](https://buckbuild.com/).
2 | # To run your application with Buck:
3 | # - install Buck
4 | # - `npm start` - to start the packager
5 | # - `cd android`
6 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
7 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
8 | # - `buck install -r android/app` - compile, install and run application
9 | #
10 |
11 | load(":build_defs.bzl", "create_aar_targets", "create_jar_targets")
12 |
13 | lib_deps = []
14 |
15 | create_aar_targets(glob(["libs/*.aar"]))
16 |
17 | create_jar_targets(glob(["libs/*.jar"]))
18 |
19 | android_library(
20 | name = "all-libs",
21 | exported_deps = lib_deps,
22 | )
23 |
24 | android_library(
25 | name = "app-code",
26 | srcs = glob([
27 | "src/main/java/**/*.java",
28 | ]),
29 | deps = [
30 | ":all-libs",
31 | ":build_config",
32 | ":res",
33 | ],
34 | )
35 |
36 | android_build_config(
37 | name = "build_config",
38 | package = "com.reactions_example",
39 | )
40 |
41 | android_resource(
42 | name = "res",
43 | package = "com.reactions_example",
44 | res = "src/main/res",
45 | )
46 |
47 | android_binary(
48 | name = "app",
49 | keystore = "//android/keystores:debug",
50 | manifest = "src/main/AndroidManifest.xml",
51 | package_type = "debug",
52 | deps = [
53 | ":app-code",
54 | ],
55 | )
56 |
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "reactions_example",
3 | "version": "0.0.1",
4 | "private": true,
5 | "scripts": {
6 | "android": "react-native run-android",
7 | "ios": "react-native run-ios",
8 | "start": "react-native start",
9 | "test": "jest",
10 | "lint": "eslint . --ext .js,.jsx,.ts,.tsx"
11 | },
12 | "dependencies": {
13 | "lodash": "^4.17.21",
14 | "react": "18.0.0",
15 | "react-native": "0.69.5",
16 | "react-native-gesture-handler": "2.16.2",
17 | "react-native-reanimated": "3.8.1"
18 | },
19 | "devDependencies": {
20 | "@babel/core": "^7.12.9",
21 | "@babel/runtime": "^7.12.5",
22 | "@react-native-community/eslint-config": "^3.0.1",
23 | "@tsconfig/react-native": "^2.0.2",
24 | "@types/jest": "^27.4.0",
25 | "@types/lodash": "^4.14.186",
26 | "@types/react-native": "^0.69.5",
27 | "@types/react-test-renderer": "^18.0.0",
28 | "@typescript-eslint/eslint-plugin": "^5.29.0",
29 | "@typescript-eslint/parser": "^5.29.0",
30 | "babel-jest": "^27.4.6",
31 | "eslint": "^7.32.0",
32 | "eslint-plugin-simple-import-sort": "^7.0.0",
33 | "jest": "^27.4.7",
34 | "metro-react-native-babel-preset": "^0.70.3",
35 | "react-test-renderer": "18.0.0",
36 | "typescript": "^4.6.4"
37 | },
38 | "resolutions": {
39 | "@types/react": "*",
40 | "react-native-reanimated": "3.8.1"
41 | },
42 | "jest": {
43 | "preset": "react-native",
44 | "moduleFileExtensions": [
45 | "ts",
46 | "tsx",
47 | "js",
48 | "jsx",
49 | "json",
50 | "node"
51 | ]
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/example/android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.cpp:
--------------------------------------------------------------------------------
1 | #include "MainApplicationTurboModuleManagerDelegate.h"
2 | #include "MainApplicationModuleProvider.h"
3 |
4 | namespace facebook {
5 | namespace react {
6 |
7 | jni::local_ref
8 | MainApplicationTurboModuleManagerDelegate::initHybrid(
9 | jni::alias_ref) {
10 | return makeCxxInstance();
11 | }
12 |
13 | void MainApplicationTurboModuleManagerDelegate::registerNatives() {
14 | registerHybrid({
15 | makeNativeMethod(
16 | "initHybrid", MainApplicationTurboModuleManagerDelegate::initHybrid),
17 | makeNativeMethod(
18 | "canCreateTurboModule",
19 | MainApplicationTurboModuleManagerDelegate::canCreateTurboModule),
20 | });
21 | }
22 |
23 | std::shared_ptr
24 | MainApplicationTurboModuleManagerDelegate::getTurboModule(
25 | const std::string name,
26 | const std::shared_ptr jsInvoker) {
27 | // Not implemented yet: provide pure-C++ NativeModules here.
28 | return nullptr;
29 | }
30 |
31 | std::shared_ptr
32 | MainApplicationTurboModuleManagerDelegate::getTurboModule(
33 | const std::string name,
34 | const JavaTurboModule::InitParams ¶ms) {
35 | return MainApplicationModuleProvider(name, params);
36 | }
37 |
38 | bool MainApplicationTurboModuleManagerDelegate::canCreateTurboModule(
39 | std::string name) {
40 | return getTurboModule(name, nullptr) != nullptr ||
41 | getTurboModule(name, {.moduleName = name}) != nullptr;
42 | }
43 |
44 | } // namespace react
45 | } // namespace facebook
46 |
--------------------------------------------------------------------------------
/example/src/component/Card/Card.tsx:
--------------------------------------------------------------------------------
1 | import { Image, Text, View } from 'react-native';
2 | import React, { memo, useState } from 'react';
3 | import { styles } from './styles';
4 | import { Reaction } from 'react-native-reactions';
5 | import { CardEmojiList, Strings } from '../../constants';
6 | import { CardProps, EmojiItemProp } from './types';
7 | import _ from 'lodash';
8 |
9 | const Footer = ({
10 | index,
11 | selectedEmoji,
12 | setSelectedEmoji,
13 | onShowDismissCard,
14 | }: CardProps) => (
15 |
16 |
22 | {selectedEmoji ? selectedEmoji?.emoji : Strings?.like}
23 |
24 | {Strings?.share}
25 |
26 | );
27 |
28 | const Card = ({ index, onShowDismissCard, item }: CardProps) => {
29 | const [selectedEmoji, setSelectedEmoji] = useState();
30 | return (
31 |
32 |
33 |
34 |
35 |
36 |
42 |
43 | );
44 | };
45 |
46 | export default memo(Card, (prevProps, nextProps) =>
47 | _.isEqual(prevProps?.isScrollDisable, nextProps?.isScrollDisable)
48 | );
49 |
--------------------------------------------------------------------------------
/example/android/app/src/main/jni/Android.mk:
--------------------------------------------------------------------------------
1 | THIS_DIR := $(call my-dir)
2 |
3 | include $(REACT_ANDROID_DIR)/Android-prebuilt.mk
4 |
5 | # If you wish to add a custom TurboModule or Fabric component in your app you
6 | # will have to include the following autogenerated makefile.
7 | # include $(GENERATED_SRC_DIR)/codegen/jni/Android.mk
8 | include $(CLEAR_VARS)
9 |
10 | LOCAL_PATH := $(THIS_DIR)
11 |
12 | # You can customize the name of your application .so file here.
13 | LOCAL_MODULE := reactions_example_appmodules
14 |
15 | LOCAL_C_INCLUDES := $(LOCAL_PATH)
16 | LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp)
17 | LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
18 |
19 | # If you wish to add a custom TurboModule or Fabric component in your app you
20 | # will have to uncomment those lines to include the generated source
21 | # files from the codegen (placed in $(GENERATED_SRC_DIR)/codegen/jni)
22 | #
23 | # LOCAL_C_INCLUDES += $(GENERATED_SRC_DIR)/codegen/jni
24 | # LOCAL_SRC_FILES += $(wildcard $(GENERATED_SRC_DIR)/codegen/jni/*.cpp)
25 | # LOCAL_EXPORT_C_INCLUDES += $(GENERATED_SRC_DIR)/codegen/jni
26 |
27 | # Here you should add any native library you wish to depend on.
28 | LOCAL_SHARED_LIBRARIES := \
29 | libfabricjni \
30 | libfbjni \
31 | libfolly_runtime \
32 | libglog \
33 | libjsi \
34 | libreact_codegen_rncore \
35 | libreact_debug \
36 | libreact_nativemodule_core \
37 | libreact_render_componentregistry \
38 | libreact_render_core \
39 | libreact_render_debug \
40 | libreact_render_graphics \
41 | librrc_view \
42 | libruntimeexecutor \
43 | libturbomodulejsijni \
44 | libyoga
45 |
46 | LOCAL_CFLAGS := -DLOG_TAG=\"ReactNative\" -fexceptions -frtti -std=c++17 -Wall
47 |
48 | include $(BUILD_SHARED_LIBRARY)
49 |
--------------------------------------------------------------------------------
/example/ios/reactions_example/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | reactions_example
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1
25 | LSRequiresIPhoneOS
26 |
27 | NSAppTransportSecurity
28 |
29 | NSExceptionDomains
30 |
31 | localhost
32 |
33 | NSExceptionAllowsInsecureHTTPLoads
34 |
35 |
36 |
37 |
38 | NSLocationWhenInUseUsageDescription
39 |
40 | UILaunchStoryboardName
41 | LaunchScreen
42 | UIRequiredDeviceCapabilities
43 |
44 | armv7
45 |
46 | UISupportedInterfaceOrientations
47 |
48 | UIInterfaceOrientationPortrait
49 | UIInterfaceOrientationLandscapeLeft
50 | UIInterfaceOrientationLandscapeRight
51 |
52 | UIViewControllerBasedStatusBarAppearance
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/src/components/ReactionView/__tests__/__snapshots__/ReactionView.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`ReactionView component Match Snapshot 1`] = `
4 |
15 |
32 |
59 |
60 | Emoji Text
61 |
62 |
63 |
64 |
65 | `;
66 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/com/reactions_example/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.reactions_example;
2 |
3 | import com.facebook.react.ReactActivity;
4 | import com.facebook.react.ReactActivityDelegate;
5 | import com.facebook.react.ReactRootView;
6 |
7 | public class MainActivity extends ReactActivity {
8 |
9 | /**
10 | * Returns the name of the main component registered from JavaScript. This is used to schedule
11 | * rendering of the component.
12 | */
13 | @Override
14 | protected String getMainComponentName() {
15 | return "reactions_example";
16 | }
17 |
18 | /**
19 | * Returns the instance of the {@link ReactActivityDelegate}. There the RootView is created and
20 | * you can specify the renderer you wish to use - the new renderer (Fabric) or the old renderer
21 | * (Paper).
22 | */
23 | @Override
24 | protected ReactActivityDelegate createReactActivityDelegate() {
25 | return new MainActivityDelegate(this, getMainComponentName());
26 | }
27 |
28 | public static class MainActivityDelegate extends ReactActivityDelegate {
29 | public MainActivityDelegate(ReactActivity activity, String mainComponentName) {
30 | super(activity, mainComponentName);
31 | }
32 |
33 | @Override
34 | protected ReactRootView createRootView() {
35 | ReactRootView reactRootView = new ReactRootView(getContext());
36 | // If you opted-in for the New Architecture, we enable the Fabric Renderer.
37 | reactRootView.setIsFabric(BuildConfig.IS_NEW_ARCHITECTURE_ENABLED);
38 | return reactRootView;
39 | }
40 |
41 | @Override
42 | protected boolean isConcurrentRootEnabled() {
43 | // If you opted-in for the New Architecture, we enable Concurrent Root (i.e. React 18).
44 | // More on this on https://reactjs.org/blog/2022/03/29/react-v18.html
45 | return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx512m -XX:MaxMetaspaceSize=256m
13 | org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 |
20 | # AndroidX package structure to make it clearer which packages are bundled with the
21 | # Android operating system, and which are packaged with your app's APK
22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
23 | android.useAndroidX=true
24 | # Automatically convert third-party libraries to use AndroidX
25 | android.enableJetifier=true
26 |
27 | # Version of flipper SDK to use with React Native
28 | FLIPPER_VERSION=0.125.0
29 |
30 | # Use this property to specify which architecture you want to build.
31 | # You can also override it from the CLI using
32 | # ./gradlew -PreactNativeArchitectures=x86_64
33 | reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
34 |
35 | # Use this property to enable support to the new architecture.
36 | # This will allow you to use TurboModules and the Fabric render in
37 | # your application. You should enable this flag either if you want
38 | # to write custom TurboModules/Fabric components OR use libraries that
39 | # are providing them.
40 | newArchEnabled=false
41 |
--------------------------------------------------------------------------------
/example/android/build.gradle:
--------------------------------------------------------------------------------
1 | import org.apache.tools.ant.taskdefs.condition.Os
2 |
3 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
4 |
5 | buildscript {
6 | ext {
7 | buildToolsVersion = "31.0.0"
8 | minSdkVersion = 21
9 | compileSdkVersion = 31
10 | targetSdkVersion = 31
11 |
12 | if (System.properties['os.arch'] == "aarch64") {
13 | // For M1 Users we need to use the NDK 24 which added support for aarch64
14 | ndkVersion = "24.0.8215888"
15 | } else {
16 | // Otherwise we default to the side-by-side NDK version from AGP.
17 | ndkVersion = "21.4.7075529"
18 | }
19 | }
20 | repositories {
21 | google()
22 | mavenCentral()
23 | }
24 | dependencies {
25 | classpath("com.android.tools.build:gradle:7.1.1")
26 | classpath("com.facebook.react:react-native-gradle-plugin")
27 | classpath("de.undercouch:gradle-download-task:5.0.1")
28 | // NOTE: Do not place your application dependencies here; they belong
29 | // in the individual module build.gradle files
30 | }
31 | }
32 |
33 | allprojects {
34 | repositories {
35 | maven {
36 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
37 | url("$rootDir/../node_modules/react-native/android")
38 | }
39 | maven {
40 | // Android JSC is installed from npm
41 | url("$rootDir/../node_modules/jsc-android/dist")
42 | }
43 | mavenCentral {
44 | // We don't want to fetch react-native from Maven Central as there are
45 | // older versions over there.
46 | content {
47 | excludeGroup "com.facebook.react"
48 | }
49 | }
50 | google()
51 | maven { url 'https://www.jitpack.io' }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/example/src/component/Card/CustomCard.tsx:
--------------------------------------------------------------------------------
1 | import { Image, Text, TouchableOpacity, View } from 'react-native';
2 | import React, { memo, useState } from 'react';
3 | import { styles } from './styles';
4 | import { Reaction } from 'react-native-reactions';
5 | import { CardEmojiUrlList, Strings } from '../../constants';
6 | import { CardProps, EmojiItemProp } from './types';
7 | import _ from 'lodash';
8 |
9 | const Footer = ({
10 | index,
11 | selectedEmoji,
12 | setSelectedEmoji,
13 | onShowDismissCard,
14 | }: CardProps) => (
15 |
16 |
22 | {selectedEmoji ? (
23 |
27 | ) : (
28 |
29 | {Strings?.like}
30 |
31 | )}
32 |
33 | {Strings?.comment}
34 | {Strings?.share}
35 |
36 | );
37 |
38 | const CustomCard = ({ index, onShowDismissCard, item }: CardProps) => {
39 | const [selectedEmoji, setSelectedEmoji] = useState();
40 | return (
41 |
42 |
43 |
44 |
45 |
46 |
52 |
53 | );
54 | };
55 |
56 | export default memo(CustomCard, (prevProps, nextProps) =>
57 | _.isEqual(prevProps?.isScrollDisable, nextProps?.isScrollDisable)
58 | );
59 |
--------------------------------------------------------------------------------
/example/src/constants/emojiDataLists.ts:
--------------------------------------------------------------------------------
1 |
2 | export const CardEmojiList = [
3 | {
4 | id: 0, emoji: '😇', title: 'like'
5 | },
6 | {
7 | id: 1, emoji: '🥰', title: 'love'
8 | },
9 | {
10 | id: 2, emoji: '🤪', title: 'care'
11 | },
12 | {
13 | id: 3, emoji: '😘', title: 'kiss'
14 | },
15 | {
16 | id: 4, emoji: '😂', title: 'laugh'
17 | },
18 | {
19 | id: 5, emoji: '😎', title: 'cool'
20 | },
21 | ];
22 |
23 | export const CardEmojiUrlList = [
24 | {
25 | id: 0, emoji: 'https://cdn-icons-png.flaticon.com/128/2584/2584606.png', title: 'like'
26 | },
27 | {
28 | id: 1, emoji: 'https://cdn-icons-png.flaticon.com/128/1933/1933691.png', title: 'love'
29 | },
30 | {
31 | id: 2, emoji: 'https://cdn-icons-png.flaticon.com/128/1791/1791330.png', title: 'cry'
32 | },
33 | {
34 | id: 3, emoji: 'https://cdn-icons-png.flaticon.com/128/742/742751.png', title: 'smile'
35 | },
36 | {
37 | id: 4, emoji: 'https://cdn-icons-png.flaticon.com/128/742/742752.png', title: 'sad'
38 | },
39 | {
40 | id: 5, emoji: 'https://cdn-icons-png.flaticon.com/128/2274/2274543.png', title: 'happy'
41 | },
42 | ];
43 |
44 | export const PostItemList = [
45 | {
46 | id: 'bd7acbea-c1b1-46c2-aed5-3ad53abb28ba',
47 | title: 'First Item',
48 | image:'https://raw.githubusercontent.com/SimformSolutionsPvtLtd/react-native-story-view/main/assets/banner.png',
49 | },
50 | {
51 | id: '3ac68afc-c605-48d3-a4f8-fbd91aa97f63',
52 | title: 'Second Item',
53 | image:'https://raw.githubusercontent.com/SimformSolutionsPvtLtd/react-native-radial-slider/main/assets/banner.png',
54 | },
55 | {
56 | id: '58694a0f-3da1-471f-bd96-145571e29d72',
57 | title: 'Third Item',
58 | image:'https://raw.githubusercontent.com/SimformSolutionsPvtLtd/react-native-country-code-select/main/assets/banner.png',
59 | },
60 | ];
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable/rn_edit_text_material.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
21 |
22 |
23 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/com/reactions_example/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate.java:
--------------------------------------------------------------------------------
1 | package com.reactions_example.newarchitecture.modules;
2 |
3 | import com.facebook.jni.HybridData;
4 | import com.facebook.react.ReactPackage;
5 | import com.facebook.react.ReactPackageTurboModuleManagerDelegate;
6 | import com.facebook.react.bridge.ReactApplicationContext;
7 | import com.facebook.soloader.SoLoader;
8 | import java.util.List;
9 |
10 | /**
11 | * Class responsible to load the TurboModules. This class has native methods and needs a
12 | * corresponding C++ implementation/header file to work correctly (already placed inside the jni/
13 | * folder for you).
14 | *
15 | * Please note that this class is used ONLY if you opt-in for the New Architecture (see the
16 | * `newArchEnabled` property). Is ignored otherwise.
17 | */
18 | public class MainApplicationTurboModuleManagerDelegate
19 | extends ReactPackageTurboModuleManagerDelegate {
20 |
21 | private static volatile boolean sIsSoLibraryLoaded;
22 |
23 | protected MainApplicationTurboModuleManagerDelegate(
24 | ReactApplicationContext reactApplicationContext, List packages) {
25 | super(reactApplicationContext, packages);
26 | }
27 |
28 | protected native HybridData initHybrid();
29 |
30 | native boolean canCreateTurboModule(String moduleName);
31 |
32 | public static class Builder extends ReactPackageTurboModuleManagerDelegate.Builder {
33 | protected MainApplicationTurboModuleManagerDelegate build(
34 | ReactApplicationContext context, List packages) {
35 | return new MainApplicationTurboModuleManagerDelegate(context, packages);
36 | }
37 | }
38 |
39 | @Override
40 | protected synchronized void maybeLoadOtherSoLibraries() {
41 | if (!sIsSoLibraryLoaded) {
42 | // If you change the name of your application .so file in the Android.mk file,
43 | // make sure you update the name here as well.
44 | SoLoader.loadLibrary("reactions_example_appmodules");
45 | sIsSoLibraryLoaded = true;
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/components/ReactionModal/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { createRef, useImperativeHandle, useRef, useState } from 'react';
2 | import { Modal } from 'react-native';
3 | import EmojiView from '../EmojiView';
4 | import { getCoordinatesRef } from '../ReactionView';
5 | import type { ModalProps, RefProps } from './types';
6 |
7 | export const reactionModalRef = createRef();
8 |
9 | const ReactionModal = () => {
10 | const [isVisible, setIsVisible] = useState(false);
11 | const modalProps = useRef({});
12 | const [isCardAnimation, setIsCardAnimation] = useState(false);
13 | const [updatedPosition, setUpdatedPosition] = useState(0);
14 |
15 | useImperativeHandle(
16 | reactionModalRef,
17 | () => ({
18 | show: (props: ModalProps) => {
19 | modalProps.current = {};
20 | modalProps.current = props;
21 | setIsVisible(true);
22 | setIsCardAnimation(true);
23 | },
24 | hide: () => {
25 | modalProps.current = {};
26 | setIsVisible(false);
27 | },
28 | sendUpdatedValues: (props: ModalProps) => {
29 | modalProps.current = { ...modalProps.current, ...props };
30 | setUpdatedPosition(props.position ?? 0);
31 | },
32 | }),
33 | []
34 | );
35 |
36 | const onStartShouldSetResponder = () => {
37 | setIsVisible(false);
38 | setTimeout(() => {
39 | modalProps.current = {};
40 | setIsCardAnimation(false);
41 | }, 500);
42 | return true;
43 | };
44 |
45 | if (!isCardAnimation) {
46 | return null;
47 | }
48 | return (
49 |
50 | {
55 | getCoordinatesRef?.current?.sendCoordinates(coordinates);
56 | }}
57 | {...modalProps.current}
58 | position={updatedPosition}
59 | />
60 |
61 | );
62 | };
63 |
64 | export default ReactionModal;
65 |
--------------------------------------------------------------------------------
/example/ios/reactions_exampleTests/reactions_exampleTests.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | #import
5 | #import
6 |
7 | #define TIMEOUT_SECONDS 600
8 | #define TEXT_TO_LOOK_FOR @"Welcome to React"
9 |
10 | @interface reactions_exampleTests : XCTestCase
11 |
12 | @end
13 |
14 | @implementation reactions_exampleTests
15 |
16 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL (^)(UIView *view))test
17 | {
18 | if (test(view)) {
19 | return YES;
20 | }
21 | for (UIView *subview in [view subviews]) {
22 | if ([self findSubviewInView:subview matching:test]) {
23 | return YES;
24 | }
25 | }
26 | return NO;
27 | }
28 |
29 | - (void)testRendersWelcomeScreen
30 | {
31 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController];
32 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
33 | BOOL foundElement = NO;
34 |
35 | __block NSString *redboxError = nil;
36 | #ifdef DEBUG
37 | RCTSetLogFunction(
38 | ^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
39 | if (level >= RCTLogLevelError) {
40 | redboxError = message;
41 | }
42 | });
43 | #endif
44 |
45 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) {
46 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
47 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
48 |
49 | foundElement = [self findSubviewInView:vc.view
50 | matching:^BOOL(UIView *view) {
51 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) {
52 | return YES;
53 | }
54 | return NO;
55 | }];
56 | }
57 |
58 | #ifdef DEBUG
59 | RCTSetLogFunction(RCTDefaultLogFunction);
60 | #endif
61 |
62 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
63 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
64 | }
65 |
66 | @end
67 |
--------------------------------------------------------------------------------
/example/android/app/src/main/jni/MainComponentsRegistry.cpp:
--------------------------------------------------------------------------------
1 | #include "MainComponentsRegistry.h"
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | namespace facebook {
9 | namespace react {
10 |
11 | MainComponentsRegistry::MainComponentsRegistry(ComponentFactory *delegate) {}
12 |
13 | std::shared_ptr
14 | MainComponentsRegistry::sharedProviderRegistry() {
15 | auto providerRegistry = CoreComponentsRegistry::sharedProviderRegistry();
16 |
17 | // Custom Fabric Components go here. You can register custom
18 | // components coming from your App or from 3rd party libraries here.
19 | //
20 | // providerRegistry->add(concreteComponentDescriptorProvider<
21 | // AocViewerComponentDescriptor>());
22 | return providerRegistry;
23 | }
24 |
25 | jni::local_ref
26 | MainComponentsRegistry::initHybrid(
27 | jni::alias_ref,
28 | ComponentFactory *delegate) {
29 | auto instance = makeCxxInstance(delegate);
30 |
31 | auto buildRegistryFunction =
32 | [](EventDispatcher::Weak const &eventDispatcher,
33 | ContextContainer::Shared const &contextContainer)
34 | -> ComponentDescriptorRegistry::Shared {
35 | auto registry = MainComponentsRegistry::sharedProviderRegistry()
36 | ->createComponentDescriptorRegistry(
37 | {eventDispatcher, contextContainer});
38 |
39 | auto mutableRegistry =
40 | std::const_pointer_cast(registry);
41 |
42 | mutableRegistry->setFallbackComponentDescriptor(
43 | std::make_shared(
44 | ComponentDescriptorParameters{
45 | eventDispatcher, contextContainer, nullptr}));
46 |
47 | return registry;
48 | };
49 |
50 | delegate->buildRegistryFunction = buildRegistryFunction;
51 | return instance;
52 | }
53 |
54 | void MainComponentsRegistry::registerNatives() {
55 | registerHybrid({
56 | makeNativeMethod("initHybrid", MainComponentsRegistry::initHybrid),
57 | });
58 | }
59 |
60 | } // namespace react
61 | } // namespace facebook
62 |
--------------------------------------------------------------------------------
/src/theme/Metrics.ts:
--------------------------------------------------------------------------------
1 | import { Dimensions } from 'react-native';
2 |
3 | export const { width: windowWidth, height: windowHeight } =
4 | Dimensions.get('window');
5 | const isLessWidth = windowWidth < windowHeight;
6 | const shortDimension = isLessWidth ? windowWidth : windowHeight;
7 | const longDimension = isLessWidth ? windowHeight : windowWidth;
8 |
9 | const getNewSize = (size: number): number => {
10 | const aspectRatio = windowHeight / windowWidth;
11 | let newSize = 0;
12 | if (aspectRatio > 1.77) {
13 | newSize = size;
14 | } else if (aspectRatio > 1.6) {
15 | newSize = size * 0.97;
16 | } else if (aspectRatio > 1.55) {
17 | newSize = size * 0.95;
18 | } else if (aspectRatio > 1.5) {
19 | newSize = size * 0.93;
20 | } else if (aspectRatio > 1.45) {
21 | newSize = size * 0.91;
22 | } else if (aspectRatio > 1.4) {
23 | newSize = size * 0.89;
24 | } else if (aspectRatio > 1.35) {
25 | newSize = size * 0.87;
26 | } else if (aspectRatio > 1.329) {
27 | return size;
28 | } else if (aspectRatio > 1.3) {
29 | newSize = size * 0.85;
30 | } else if (aspectRatio > 1.2) {
31 | newSize = size * 0.84;
32 | } else if (aspectRatio > 1.185) {
33 | return size * 0.95;
34 | } else if (aspectRatio > 1.15) {
35 | return size * 0.82;
36 | } else {
37 | newSize = size * 0.6;
38 | }
39 | return newSize;
40 | };
41 |
42 | // Default guideline sizes are based on standard ~5" screen mobile device
43 | const guidelineBaseWidth: number = 375;
44 | const guidelineBaseHeight: number = 812;
45 |
46 | // Use for horizontal scaling
47 | export const scale = (
48 | size: number,
49 | skipAspectRatio: boolean = false
50 | ): number => {
51 | const changeSize = skipAspectRatio ? size : getNewSize(size);
52 | return (shortDimension / guidelineBaseWidth) * changeSize;
53 | };
54 |
55 | // Use for vertical scaling
56 | export const verticalScale = (
57 | size: number,
58 | skipAspectRatio: boolean = false
59 | ): number => {
60 | const changeSize = skipAspectRatio ? size : getNewSize(size);
61 | return (longDimension / guidelineBaseHeight) * changeSize;
62 | };
63 |
64 | // Use for horizontal & vertical scaling (example: Fonts)
65 | export const moderateScale = (
66 | size: number,
67 | skipAspectRatio: boolean = false,
68 | factor: number = 0.5
69 | ): number => {
70 | const changeSize = skipAspectRatio ? size : getNewSize(size);
71 | return (
72 | changeSize + (scale(changeSize, skipAspectRatio) - changeSize) * factor
73 | );
74 | };
75 |
--------------------------------------------------------------------------------
/example/src/theme/Metrics.ts:
--------------------------------------------------------------------------------
1 | import { Dimensions } from 'react-native';
2 |
3 | export const { width: windowWidth, height: windowHeight } =
4 | Dimensions.get('window');
5 | const isLessWidth = windowWidth < windowHeight;
6 | const shortDimension = isLessWidth ? windowWidth : windowHeight;
7 | const longDimension = isLessWidth ? windowHeight : windowWidth;
8 |
9 | const getNewSize = (size: number): number => {
10 | const aspectRatio = windowHeight / windowWidth;
11 | let newSize = 0;
12 | if (aspectRatio > 1.77) {
13 | newSize = size;
14 | } else if (aspectRatio > 1.6) {
15 | newSize = size * 0.97;
16 | } else if (aspectRatio > 1.55) {
17 | newSize = size * 0.95;
18 | } else if (aspectRatio > 1.5) {
19 | newSize = size * 0.93;
20 | } else if (aspectRatio > 1.45) {
21 | newSize = size * 0.91;
22 | } else if (aspectRatio > 1.4) {
23 | newSize = size * 0.89;
24 | } else if (aspectRatio > 1.35) {
25 | newSize = size * 0.87;
26 | } else if (aspectRatio > 1.329) {
27 | return size;
28 | } else if (aspectRatio > 1.3) {
29 | newSize = size * 0.85;
30 | } else if (aspectRatio > 1.2) {
31 | newSize = size * 0.84;
32 | } else if (aspectRatio > 1.185) {
33 | return size * 0.95;
34 | } else if (aspectRatio > 1.15) {
35 | return size * 0.82;
36 | } else {
37 | newSize = size * 0.6;
38 | }
39 | return newSize;
40 | };
41 |
42 | // Default guideline sizes are based on standard ~5" screen mobile device
43 | const guidelineBaseWidth: number = 375;
44 | const guidelineBaseHeight: number = 812;
45 |
46 | // Use for horizontal scaling
47 | export const scale = (
48 | size: number,
49 | skipAspectRatio: boolean = false
50 | ): number => {
51 | const changeSize = skipAspectRatio ? size : getNewSize(size);
52 | return (shortDimension / guidelineBaseWidth) * changeSize;
53 | };
54 |
55 | // Use for vertical scaling
56 | export const verticalScale = (
57 | size: number,
58 | skipAspectRatio: boolean = false
59 | ): number => {
60 | const changeSize = skipAspectRatio ? size : getNewSize(size);
61 | return (longDimension / guidelineBaseHeight) * changeSize;
62 | };
63 |
64 | // Use for horizontal & vertical scaling (example: Fonts)
65 | export const moderateScale = (
66 | size: number,
67 | skipAspectRatio: boolean = false,
68 | factor: number = 0.5
69 | ): number => {
70 | const changeSize = skipAspectRatio ? size : getNewSize(size);
71 | return (
72 | changeSize + (scale(changeSize, skipAspectRatio) - changeSize) * factor
73 | );
74 | };
75 |
--------------------------------------------------------------------------------
/src/components/EmojiView/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useRef, useState } from 'react';
2 | import { LayoutRectangle, StyleSheet, View } from 'react-native';
3 | import EmojiItem from '../EmojiItem';
4 | import { useEmojiView } from './hooks';
5 | import { styles } from './styles';
6 | import type { EmojiModalProps } from './types';
7 | import Animated from 'react-native-reanimated';
8 |
9 | const EmojiView = ({
10 | onStartShouldSetResponder,
11 | getEmojiViewCoordinates,
12 | ...props
13 | }: EmojiModalProps) => {
14 | const {
15 | cardStyle,
16 | y = 0,
17 | items,
18 | directTouchRelease,
19 | directTouchLoad,
20 | panResponder,
21 | } = props;
22 | const [touchRelease, setTouchRelease] = useState(false);
23 | const [loaded, setLoaded] = useState(false);
24 | const {
25 | currentEmoji,
26 | emojiSize,
27 | hitSlopHeigth,
28 | hitSlopWidth,
29 | subContainer,
30 | emojiPressHandler,
31 | container,
32 | } = useEmojiView(props);
33 | const measureRef = useRef(null);
34 |
35 | const emojiBox = StyleSheet.flatten([styles.emojiBox, cardStyle]);
36 |
37 | return (
38 | setTouchRelease(true)}>
42 |
51 | {
55 | measureRef.current?.measure((_x, _y, width, height, pageX, pageY) => {
56 | const layoutRectangle: LayoutRectangle = {
57 | x: pageX,
58 | y: pageY,
59 | width,
60 | height,
61 | };
62 | getEmojiViewCoordinates && getEmojiViewCoordinates(layoutRectangle);
63 | });
64 | }}
65 | onTouchStart={() => setLoaded(true)}
66 | onTouchEnd={() => setLoaded(false)}>
67 | {items?.map((item, index: number) => (
68 | emojiPressHandler(item)}
72 | key={item?.title}
73 | data={item}
74 | currentPosition={currentEmoji}
75 | iconSize={emojiSize}
76 | showTopEmojiCard={y > 150}
77 | loaded={directTouchLoad || loaded}
78 | {...{ setTouchRelease, ...props }}
79 | />
80 | ))}
81 |
82 |
83 | );
84 | };
85 |
86 | export default EmojiView;
87 |
--------------------------------------------------------------------------------
/example/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/src/components/ReactionView/hooks/useReaction.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef, useState } from 'react';
2 | import {
3 | GestureResponderEvent,
4 | PanResponder,
5 | useWindowDimensions,
6 | } from 'react-native';
7 | import { GlobalConstants } from '../../../constants';
8 | import type { ReactionViewProps } from '../types';
9 |
10 | const useReaction = (props: ReactionViewProps) => {
11 | const [currentEmoji, setCurrentEmoji] = useState(0);
12 | const { iconSize = 25, showPopupType = 'default' } = props;
13 | const [emojiSize, setEmojiSize] = useState(iconSize);
14 | const [mainViewY, setMainViewY] = useState(0);
15 | const [mainViewX, setMainViewX] = useState(0);
16 | const [mainViewWidth, setMainViewWidth] = useState(0);
17 | const { width } = useWindowDimensions();
18 | const [position, setPosition] = useState(0);
19 |
20 | const mainViewWidthX = width - (mainViewX + mainViewWidth);
21 |
22 | const showCardInCenter: boolean =
23 | mainViewWidthX > width / 4 && mainViewWidthX < width / 2;
24 |
25 | const showCardPosition = showCardInCenter
26 | ? width / 8
27 | : mainViewWidthX < 100
28 | ? -(width - mainViewX)
29 | : mainViewX;
30 |
31 | useEffect(() => {
32 | if (iconSize > GlobalConstants.max) {
33 | setEmojiSize(30);
34 | } else if (iconSize < GlobalConstants.min) {
35 | setEmojiSize(15);
36 | } else {
37 | setEmojiSize(iconSize);
38 | }
39 | }, [iconSize]);
40 |
41 | const showTopEmojiCard: boolean = mainViewY < 150 ? true : false;
42 |
43 | const isSinglePress = showPopupType === GlobalConstants.onPress;
44 |
45 | const isLongPress = showPopupType === GlobalConstants.default;
46 | const panResponder = useRef(
47 | PanResponder.create({
48 | onStartShouldSetPanResponder: () => true,
49 | onPanResponderGrant: () => {},
50 | onPanResponderMove: event => onGesture(event),
51 | onPanResponderEnd: () => {
52 | setPosition(0);
53 | },
54 | onMoveShouldSetPanResponder: (_, gestureState) => {
55 | const { dx, dy } = gestureState;
56 | return dx > 2 || dx < -2 || dy > 2 || dy < -2;
57 | },
58 | onMoveShouldSetPanResponderCapture: (_, gestureState) => {
59 | const { dx, dy } = gestureState;
60 | return dx > 2 || dx < -2 || dy > 2 || dy < -2;
61 | },
62 | })
63 | ).current;
64 |
65 | const onGesture = async (event: GestureResponderEvent) => {
66 | if (event.nativeEvent?.pageX <= 367) {
67 | const currentItem = Math.floor(event.nativeEvent?.pageX);
68 | setPosition(currentItem ?? 0);
69 | } else {
70 | setPosition(0);
71 | }
72 | };
73 | return {
74 | currentEmoji,
75 | setCurrentEmoji,
76 | emojiSize,
77 | mainViewY,
78 | setMainViewY,
79 | showTopEmojiCard,
80 | setMainViewX,
81 | mainViewX,
82 | showCardPosition,
83 | setMainViewWidth,
84 | isSinglePress,
85 | isLongPress,
86 | width,
87 | showCardInCenter,
88 | position,
89 | setPosition,
90 | panResponder,
91 | mainViewWidth,
92 | mainViewWidthX,
93 | };
94 | };
95 |
96 | export default useReaction;
97 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/com/reactions_example/MainApplication.java:
--------------------------------------------------------------------------------
1 | package com.reactions_example;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 | import com.facebook.react.PackageList;
6 | import com.facebook.react.ReactApplication;
7 | import com.facebook.react.ReactInstanceManager;
8 | import com.facebook.react.ReactNativeHost;
9 | import com.facebook.react.ReactPackage;
10 | import com.facebook.react.config.ReactFeatureFlags;
11 | import com.facebook.soloader.SoLoader;
12 | import com.reactions_example.newarchitecture.MainApplicationReactNativeHost;
13 | import java.lang.reflect.InvocationTargetException;
14 | import java.util.List;
15 |
16 | public class MainApplication extends Application implements ReactApplication {
17 |
18 | private final ReactNativeHost mReactNativeHost =
19 | new ReactNativeHost(this) {
20 | @Override
21 | public boolean getUseDeveloperSupport() {
22 | return BuildConfig.DEBUG;
23 | }
24 |
25 | @Override
26 | protected List getPackages() {
27 | @SuppressWarnings("UnnecessaryLocalVariable")
28 | List packages = new PackageList(this).getPackages();
29 | // Packages that cannot be autolinked yet can be added manually here, for example:
30 | // packages.add(new MyReactNativePackage());
31 | return packages;
32 | }
33 |
34 | @Override
35 | protected String getJSMainModuleName() {
36 | return "index";
37 | }
38 | };
39 |
40 | private final ReactNativeHost mNewArchitectureNativeHost =
41 | new MainApplicationReactNativeHost(this);
42 |
43 | @Override
44 | public ReactNativeHost getReactNativeHost() {
45 | if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
46 | return mNewArchitectureNativeHost;
47 | } else {
48 | return mReactNativeHost;
49 | }
50 | }
51 |
52 | @Override
53 | public void onCreate() {
54 | super.onCreate();
55 | // If you opted-in for the New Architecture, we enable the TurboModule system
56 | ReactFeatureFlags.useTurboModules = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
57 | SoLoader.init(this, /* native exopackage */ false);
58 | initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
59 | }
60 |
61 | /**
62 | * Loads Flipper in React Native templates. Call this in the onCreate method with something like
63 | * initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
64 | *
65 | * @param context
66 | * @param reactInstanceManager
67 | */
68 | private static void initializeFlipper(
69 | Context context, ReactInstanceManager reactInstanceManager) {
70 | if (BuildConfig.DEBUG) {
71 | try {
72 | /*
73 | We use reflection here to pick up the class that initializes Flipper,
74 | since Flipper library is not available in release mode
75 | */
76 | Class> aClass = Class.forName("com.reactions_example.ReactNativeFlipper");
77 | aClass
78 | .getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
79 | .invoke(null, context, reactInstanceManager);
80 | } catch (ClassNotFoundException e) {
81 | e.printStackTrace();
82 | } catch (NoSuchMethodException e) {
83 | e.printStackTrace();
84 | } catch (IllegalAccessException e) {
85 | e.printStackTrace();
86 | } catch (InvocationTargetException e) {
87 | e.printStackTrace();
88 | }
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/components/EmojiItem/hooks/useEmojiItem.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef, useState } from 'react';
2 | import type { LayoutChangeEvent, TouchableOpacity } from 'react-native';
3 | import {
4 | interpolate,
5 | useAnimatedStyle,
6 | useSharedValue,
7 | withDelay,
8 | withTiming,
9 | } from 'react-native-reanimated';
10 | import type { EmojiItemProps } from '../types';
11 |
12 | const useEmojiItem = (props: EmojiItemProps) => {
13 | const {
14 | currentPosition = 0,
15 | index,
16 | showPopUpCard,
17 | emojiDuration = 200,
18 | scaleDuration = 100,
19 | } = props;
20 |
21 | const [xValue, setXValue] = useState(0);
22 | const [titlePosition, setTitlePosition] = useState(0);
23 | //boolean flag to identify whether the emoji is pressed or not
24 | const scaled: boolean =
25 | currentPosition > xValue && currentPosition < xValue + 20;
26 | const scaleEmoji = useSharedValue(0);
27 | const waveAnim = useSharedValue(0);
28 |
29 | useEffect(() => {
30 | waveAnim.value = withDelay(
31 | index * 40,
32 | withTiming(showPopUpCard ? 1 : 0, {
33 | duration: emojiDuration,
34 | })
35 | );
36 | }, [waveAnim, index, showPopUpCard, emojiDuration]);
37 |
38 | const childRef = useRef(null);
39 |
40 | const onLayout = (e: LayoutChangeEvent) => {
41 | setTimeout(() => {
42 | childRef?.current &&
43 | childRef?.current.measureInWindow((x: number) => {
44 | setXValue(x);
45 | });
46 | }, 200);
47 | setTitlePosition(e.nativeEvent.layout.x - 4);
48 | };
49 |
50 | const reverseEnim = (scaleEmoji as any).value === 2 ? [2, 1, 1] : [1, 1, 2];
51 |
52 | useEffect(() => {
53 | scaleEmoji.value = withTiming(scaled ? 2 : 1, {
54 | duration: scaleDuration,
55 | });
56 | }, [emojiDuration, scaleDuration, scaleEmoji, scaled]);
57 |
58 | const emojiAnimatedScaled = useAnimatedStyle(() => {
59 | return {
60 | transform: [
61 | {
62 | translateY: interpolate(
63 | scaleEmoji.value,
64 | [0, 1, 2],
65 | scaled ? [1, -3, -5] : [1, 1, 1]
66 | ),
67 | },
68 | {
69 | scaleY: interpolate(
70 | scaleEmoji.value,
71 | [0, 1, 2],
72 | scaled ? [0, 1, 1.5] : reverseEnim
73 | ),
74 | },
75 | {
76 | scaleX: interpolate(
77 | scaleEmoji.value,
78 | [0, 1, 2],
79 | scaled ? [0, 1, 1.5] : reverseEnim
80 | ),
81 | },
82 | ],
83 | };
84 | }, [emojiDuration, scaleDuration, scaleEmoji, scaled]);
85 |
86 | const wavedEmoji = useAnimatedStyle(() => {
87 | return {
88 | opacity: interpolate(waveAnim.value, [0, 0.5, 1], [0, 0.5, 1]),
89 | transform: [
90 | {
91 | translateY: interpolate(
92 | waveAnim.value,
93 | [0, 0.5, 1],
94 | showPopUpCard ? [15, 10, 0] : [0, 10, -index * 4]
95 | ),
96 | },
97 | {
98 | scale: interpolate(
99 | waveAnim.value,
100 | [0, 0.5, 1],
101 | showPopUpCard ? [0.5, 0.5, 1] : [1, 1, 0.5]
102 | ),
103 | },
104 | ],
105 | };
106 | });
107 |
108 | return {
109 | onLayout,
110 | titlePosition,
111 | xValue,
112 | scaled,
113 | childRef,
114 | wavedEmoji,
115 | emojiAnimatedScaled,
116 | };
117 | };
118 |
119 | export default useEmojiItem;
120 |
--------------------------------------------------------------------------------
/example/android/app/src/debug/java/com/reactions_example/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.reactions_example;
8 |
9 | import android.content.Context;
10 | import com.facebook.flipper.android.AndroidFlipperClient;
11 | import com.facebook.flipper.android.utils.FlipperUtils;
12 | import com.facebook.flipper.core.FlipperClient;
13 | import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin;
14 | import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
15 | import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
16 | import com.facebook.flipper.plugins.inspector.DescriptorMapping;
17 | import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
18 | import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
19 | import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
20 | import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
21 | import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
22 | import com.facebook.react.ReactInstanceEventListener;
23 | import com.facebook.react.ReactInstanceManager;
24 | import com.facebook.react.bridge.ReactContext;
25 | import com.facebook.react.modules.network.NetworkingModule;
26 | import okhttp3.OkHttpClient;
27 |
28 | public class ReactNativeFlipper {
29 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
30 | if (FlipperUtils.shouldEnableFlipper(context)) {
31 | final FlipperClient client = AndroidFlipperClient.getInstance(context);
32 |
33 | client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
34 | client.addPlugin(new ReactFlipperPlugin());
35 | client.addPlugin(new DatabasesFlipperPlugin(context));
36 | client.addPlugin(new SharedPreferencesFlipperPlugin(context));
37 | client.addPlugin(CrashReporterPlugin.getInstance());
38 |
39 | NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
40 | NetworkingModule.setCustomClientBuilder(
41 | new NetworkingModule.CustomClientBuilder() {
42 | @Override
43 | public void apply(OkHttpClient.Builder builder) {
44 | builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
45 | }
46 | });
47 | client.addPlugin(networkFlipperPlugin);
48 | client.start();
49 |
50 | // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
51 | // Hence we run if after all native modules have been initialized
52 | ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
53 | if (reactContext == null) {
54 | reactInstanceManager.addReactInstanceEventListener(
55 | new ReactInstanceEventListener() {
56 | @Override
57 | public void onReactContextInitialized(ReactContext reactContext) {
58 | reactInstanceManager.removeReactInstanceEventListener(this);
59 | reactContext.runOnNativeModulesQueueThread(
60 | new Runnable() {
61 | @Override
62 | public void run() {
63 | client.addPlugin(new FrescoFlipperPlugin());
64 | }
65 | });
66 | }
67 | });
68 | } else {
69 | client.addPlugin(new FrescoFlipperPlugin());
70 | }
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/example/ios/reactions_example.xcodeproj/xcshareddata/xcschemes/reactions_example.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
53 |
55 |
61 |
62 |
63 |
64 |
70 |
72 |
78 |
79 |
80 |
81 |
83 |
84 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-reactions",
3 | "version": "1.0.0",
4 | "description": "A React Native component to provide animated reaction picker",
5 | "main": "lib/index",
6 | "types": "lib/index.d.ts",
7 | "contributors": [],
8 | "author": "Simform Solutions",
9 | "repository": {
10 | "type": "git",
11 | "url": "https://github.com/SimformSolutionsPvtLtd/react-native-reactions"
12 | },
13 | "keywords": [
14 | "react",
15 | "react-native",
16 | "typescript",
17 | "reactions",
18 | "animated-reactions",
19 | "rn",
20 | "react-native-reactions",
21 | "reanimated",
22 | "component",
23 | "react-component",
24 | "ios",
25 | "android",
26 | "react-native-reaction",
27 | "seamless-reactions",
28 | "seamless",
29 | "seamless-gesture-reaction",
30 | "emoji-reactions",
31 | "reactions-picker",
32 | "reactions-component",
33 | "animated-reactions-picker",
34 | "picker",
35 | "reaction",
36 | "animation",
37 | "react-reaction",
38 | "react-native-fb-reactions-animation"
39 | ],
40 | "license": "MIT",
41 | "files": [
42 | "/lib"
43 | ],
44 | "scripts": {
45 | "prepare": "husky install",
46 | "clean": "rm -rf node_modules",
47 | "build": "rm -rf lib && tsc -p .",
48 | "lint": "eslint . --ext .js,.jsx,.ts,.tsx",
49 | "lint:fix": "eslint 'src/**/*.{js,jsx,ts,tsx}' -c .eslintrc --fix ",
50 | "build_local": "yarn build && yarn pack",
51 | "test": "jest",
52 | "example": "yarn --cwd example"
53 | },
54 | "peerDependencies": {
55 | "react": "*",
56 | "react-native": "*",
57 | "react-native-gesture-handler": "2.16.2",
58 | "react-native-reanimated": "3.8.1"
59 | },
60 | "devDependencies": {
61 | "@commitlint/cli": "^16.1.0",
62 | "@commitlint/config-conventional": "^16.0.0",
63 | "@react-native-community/eslint-config": "^3.0.1",
64 | "@testing-library/react-native": "^9.0.0",
65 | "@types/jest": "^27.4.0",
66 | "babel-jest": "^27.4.6",
67 | "eslint-plugin-simple-import-sort": "^7.0.0",
68 | "react-native-gesture-handler": "2.16.2",
69 | "react-native-reanimated": "3.8.1",
70 | "husky": "^7.0.4",
71 | "jest": "^27.4.7",
72 | "lint-staged": "^11.1.2",
73 | "prettier": "^2.7.1",
74 | "react": "18.0.0",
75 | "react-native": "0.69.5",
76 | "typescript": "4.7.4",
77 | "@babel/core": "^7.12.9",
78 | "@babel/runtime": "^7.12.5",
79 | "@tsconfig/react-native": "^2.0.2",
80 | "@types/react-native": "^0.69.5",
81 | "@types/react-test-renderer": "^18.0.0",
82 | "@typescript-eslint/eslint-plugin": "^5.29.0",
83 | "@typescript-eslint/parser": "^5.29.0",
84 | "eslint": "^7.32.0",
85 | "metro-react-native-babel-preset": "^0.70.3",
86 | "react-test-renderer": "18.0.0"
87 | },
88 | "resolutions": {
89 | "@types/react": "*",
90 | "react-native-reanimated": "3.8.1"
91 | },
92 | "jest": {
93 | "preset": "react-native",
94 | "moduleFileExtensions": [
95 | "ts",
96 | "tsx",
97 | "js",
98 | "jsx",
99 | "json",
100 | "node"
101 | ],
102 | "setupFilesAfterEnv": [
103 | "./jest-setup.js"
104 | ],
105 | "modulePathIgnorePatterns": []
106 | },
107 | "lint-staged": {
108 | "src/**/*.{js,ts,tsx}": [
109 | "eslint"
110 | ]
111 | },
112 | "eslintIgnore": [
113 | "node_modules/",
114 | "lib/"
115 | ],
116 | "husky": {
117 | "hooks": {
118 | "pre-commit": "lint-staged",
119 | "pre-push": "yarn build && yarn test"
120 | }
121 | },
122 | "commitlint": {
123 | "extends": [
124 | "@commitlint/config-conventional"
125 | ]
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/src/components/EmojiItem/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { memo, useEffect } from 'react';
2 | import { StyleSheet, Text, TouchableOpacity } from 'react-native';
3 | import { moderateScale, verticalScale } from '../../theme';
4 | import { isValidUrl } from '../../utils';
5 | import EmojiImage from '../EmojiImage';
6 | import type { EmojiItemProp } from '../ReactionView/types';
7 | import { useEmojiItem } from './hooks';
8 | import styles from './styles';
9 | import type { emojiData, EmojiItemProps } from './types';
10 | import Animated from 'react-native-reanimated';
11 |
12 | const EmojiButton = ({
13 | emojiData,
14 | emojiStyle,
15 | emojiKey = 'emoji',
16 | emojiSize = 28,
17 | }: emojiData) => {
18 | const emoji = emojiData?.[emojiKey];
19 | const isNumber: boolean = typeof emoji === 'number';
20 | const isValidEmement = React.isValidElement(emoji);
21 | const emojiElementStyle = StyleSheet.flatten([
22 | { fontSize: moderateScale(emojiSize) },
23 | emojiStyle,
24 | emoji?.props?.style ?? {},
25 | ]);
26 |
27 | if (isValidEmement) {
28 | return React.cloneElement(emoji as React.ReactElement, {
29 | style: emojiElementStyle,
30 | });
31 | } else if (isValidUrl(emoji as string) || isNumber) {
32 | return (
33 |
37 | );
38 | } else {
39 | return {emoji};
40 | }
41 | };
42 |
43 | const EmojiItem = (props: EmojiItemProps) => {
44 | const {
45 | data,
46 | onEmojiPress,
47 | titleStyle,
48 | titleBoxStyle,
49 | emojiContainerStyle,
50 | showTopEmojiCard,
51 | emojiKey,
52 | emojiStyle,
53 | emojiSize,
54 | isTouchRelease,
55 | isModal = true,
56 | setShowPopUpCard = () => {},
57 | onTap = () => {},
58 | loaded,
59 | onEmojiCloseModal = () => {},
60 | setTouchRelease = () => {},
61 | } = props;
62 |
63 | const {
64 | titlePosition,
65 | onLayout,
66 | scaled,
67 | childRef,
68 | emojiAnimatedScaled,
69 | wavedEmoji,
70 | } = useEmojiItem(props);
71 |
72 | const labelStyle = StyleSheet.flatten([
73 | styles.titleBox,
74 | {
75 | transform: [
76 | { scale: scaled ? 1.0 : 0 },
77 | { translateX: titlePosition },
78 | { perspective: 1000 },
79 | ],
80 | opacity: scaled ? 1.0 : 0,
81 | top: showTopEmojiCard ? verticalScale(-30) : verticalScale(70),
82 | },
83 | titleBoxStyle,
84 | ]);
85 |
86 | useEffect(() => {
87 | const getEmoji: EmojiItemProp | null = scaled ? data : null;
88 | isTouchRelease && (isModal ? onEmojiCloseModal() : setShowPopUpCard(false));
89 |
90 | return () => {
91 | isTouchRelease && getEmoji && onTap(getEmoji);
92 | setTouchRelease(false);
93 | };
94 | // eslint-disable-next-line react-hooks/exhaustive-deps
95 | }, [data, scaled, isTouchRelease]);
96 |
97 | return (
98 | <>
99 | {scaled && data?.title && (
100 |
101 | {data?.title}
102 |
103 | )}
104 |
110 |
111 |
115 |
116 |
117 | >
118 | );
119 | };
120 |
121 | export default memo(EmojiItem);
122 |
--------------------------------------------------------------------------------
/src/components/ReactionView/ReactionViewModal.tsx:
--------------------------------------------------------------------------------
1 | import React, {
2 | createRef,
3 | useEffect,
4 | useImperativeHandle,
5 | useRef,
6 | useState,
7 | } from 'react';
8 | import { LayoutRectangle, Pressable, View } from 'react-native';
9 | import { reactionModalRef } from '../ReactionModal';
10 | import { useReaction } from './hooks';
11 | import type { GetCoordinateRef, ReactionViewProps } from './types';
12 |
13 | export const getCoordinatesRef = createRef();
14 |
15 | const ReactionViewModal = ({ touchableProps, ...props }: ReactionViewProps) => {
16 | const {
17 | children,
18 | onPress = () => {},
19 | disabled = false,
20 | onLongPress = () => {},
21 | } = props;
22 | const rootRef = useRef(null);
23 | const contentHeightRef = useRef(0);
24 | const contentyRef = useRef(0);
25 | const [touchRelease, setTouchRelease] = useState(false);
26 | const [loaded, setLoaded] = useState(false);
27 | const [emojiViewCoordinates, setEmojiViewCoordinates] =
28 | useState({
29 | width: 0,
30 | height: 0,
31 | x: 0,
32 | y: 0,
33 | });
34 | const { emojiSize, isLongPress, isSinglePress, panResponder, position } =
35 | useReaction(props);
36 |
37 | useImperativeHandle(getCoordinatesRef, () => ({
38 | sendCoordinates: coordinates => {
39 | setEmojiViewCoordinates(coordinates);
40 | },
41 | }));
42 |
43 | const onPressHandler = () => {
44 | rootRef?.current &&
45 | rootRef?.current.measureInWindow(
46 | (x: number, y: number, width: number) => {
47 | reactionModalRef.current &&
48 | reactionModalRef.current?.show({
49 | x,
50 | y,
51 | width,
52 | contentHeight: contentHeightRef.current,
53 | emojiSize,
54 | directTouchRelease: touchRelease,
55 | directTouchLoad: loaded,
56 | position,
57 | panResponder,
58 | ...props,
59 | });
60 | contentyRef.current = y;
61 | }
62 | );
63 | };
64 |
65 | useEffect(() => {
66 | reactionModalRef.current &&
67 | reactionModalRef.current?.sendUpdatedValues({
68 | directTouchRelease: touchRelease,
69 | directTouchLoad: loaded,
70 | position,
71 | panResponder,
72 | ...props,
73 | });
74 | }, [loaded, panResponder, position, props, touchRelease]);
75 |
76 | const checkTouchRelease =
77 | position &&
78 | position > emojiViewCoordinates.x &&
79 | position <= emojiViewCoordinates.width + emojiViewCoordinates.x;
80 |
81 | return (
82 | {
85 | const { height } = event.nativeEvent.layout;
86 | contentHeightRef.current = height;
87 | }}
88 | onTouchStart={() => {
89 | setLoaded(true);
90 | setTouchRelease(false);
91 | }}
92 | onTouchEnd={() => {
93 | setLoaded(false);
94 | checkTouchRelease && setTouchRelease(true);
95 | }}
96 | {...panResponder.panHandlers}>
97 | {React.isValidElement(children) && (
98 | {
103 | isLongPress ? onPressHandler() : !isSinglePress && onPress();
104 | onLongPress();
105 | }}
106 | onPress={() => {
107 | isSinglePress ? onPressHandler() : !isLongPress && onPress();
108 | onPress();
109 | }}>
110 | {children}
111 |
112 | )}
113 |
114 | );
115 | };
116 |
117 | export default ReactionViewModal;
118 |
--------------------------------------------------------------------------------
/example/ios/reactions_example/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
24 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/src/components/EmojiView/hooks/useEmojiView.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef, useState } from 'react';
2 | import {
3 | PanResponder,
4 | StyleSheet,
5 | useWindowDimensions,
6 | GestureResponderEvent,
7 | } from 'react-native';
8 | import type { EmojiItemProp } from '../../ReactionView/types';
9 | import { GlobalConstants } from '../../../constants';
10 | import type { EmojiModalProps } from '../types';
11 | import {
12 | interpolate,
13 | useAnimatedStyle,
14 | useSharedValue,
15 | withTiming,
16 | } from 'react-native-reanimated';
17 |
18 | const useEmojiView = (props: EmojiModalProps) => {
19 | const {
20 | y = 0,
21 | x = 0,
22 | width: mainViewWidth = 0,
23 | isModal = true,
24 | onTap = () => {},
25 | setShowPopUpCard = () => {},
26 | showPopUpCard,
27 | cardDuration = 150,
28 | opacityRange = [0, 0, 1],
29 | onEmojiCloseModal = () => {},
30 | onShowDismissCard = () => {},
31 | isShowCardInCenter = false,
32 | position = 0,
33 | } = props;
34 |
35 | const [currentEmoji, setCurrentEmoji] = useState(0);
36 | const { iconSize = 25 } = props;
37 | const [emojiSize, setEmojiSize] = useState(iconSize);
38 | const { width, height } = useWindowDimensions();
39 | const cardAnim = useSharedValue(0);
40 |
41 | // get child component position to how far is the end of x
42 | const mainViewWidthX = width - (x + mainViewWidth);
43 |
44 | // based on mainViewWidthX get center position to show card
45 | const showCardInCenter: boolean =
46 | mainViewWidthX > width / 4 && mainViewWidthX < width / 2;
47 |
48 | // set card position based on child component
49 | const showCardPosition = x < 100 ? x + 5 : width / (emojiSize / 8);
50 |
51 | useEffect(() => {
52 | onShowDismissCard(showPopUpCard);
53 | }, [onShowDismissCard, showPopUpCard]);
54 |
55 | useEffect(() => {
56 | return () => setCurrentEmoji(0);
57 | }, [showPopUpCard]);
58 |
59 | useEffect(() => {
60 | if (iconSize > GlobalConstants.max) {
61 | setEmojiSize(30);
62 | } else if (iconSize < GlobalConstants.min) {
63 | setEmojiSize(15);
64 | } else {
65 | setEmojiSize(iconSize);
66 | }
67 | }, [iconSize]);
68 |
69 | useEffect(() => {
70 | cardAnim.value = withTiming(showPopUpCard ? 1 : 0, {
71 | duration: cardDuration,
72 | });
73 | }, [cardAnim, cardDuration, showPopUpCard]);
74 |
75 | useEffect(() => {
76 | setCurrentEmoji(position);
77 | }, [position]);
78 | const panResponder = useRef(
79 | PanResponder.create({
80 | onMoveShouldSetPanResponder: () => true,
81 | onStartShouldSetPanResponder: () => true,
82 | onPanResponderGrant: () => {},
83 | onPanResponderMove: event => onGesture(event),
84 | onPanResponderRelease: () => {},
85 | })
86 | ).current;
87 |
88 | const onGesture = async (event: GestureResponderEvent) => {
89 | if (event.nativeEvent?.pageX >= 16 && event.nativeEvent?.pageX <= 367) {
90 | const currentItem = Math.floor(event.nativeEvent?.pageX);
91 | if (currentItem) {
92 | setCurrentEmoji(currentItem);
93 | } else {
94 | setCurrentEmoji(0);
95 | }
96 | } else {
97 | setCurrentEmoji(0);
98 | }
99 | };
100 |
101 | const subContainer = StyleSheet.create([
102 | {
103 | alignItems:
104 | showCardInCenter || isShowCardInCenter ? 'center' : 'baseline',
105 | },
106 | ]);
107 |
108 | const emojiPressHandler = (item: EmojiItemProp) => {
109 | onTap && onTap(item);
110 | isModal ? onEmojiCloseModal() : setShowPopUpCard(false);
111 | };
112 |
113 | const outputRange =
114 | y > 150
115 | ? [0, emojiSize - 10, emojiSize - 20]
116 | : [emojiSize - 40, emojiSize - 30, emojiSize - 20];
117 |
118 | const outputRangeModal = y > 150 ? [0, y - 50, y - 70] : [0, y + 10, y + 30];
119 |
120 | const container = useAnimatedStyle(() => {
121 | return {
122 | opacity: interpolate(cardAnim.value, [0, 0.5, 1], opacityRange),
123 | transform: [
124 | {
125 | translateY: interpolate(
126 | cardAnim.value,
127 | [0, 0.5, 1],
128 | isModal ? outputRangeModal : outputRange
129 | ),
130 | },
131 | {
132 | translateX: isModal
133 | ? showCardInCenter || isShowCardInCenter
134 | ? 0
135 | : showCardPosition
136 | : showCardPosition,
137 | },
138 | ],
139 | };
140 | });
141 |
142 | const hitSlopHeigth = showPopUpCard ? height : 0;
143 | const hitSlopWidth = showPopUpCard ? width : 0;
144 |
145 | return {
146 | onGesture,
147 | currentEmoji,
148 | setCurrentEmoji,
149 | emojiSize,
150 | showCardPosition,
151 | height,
152 | showCardInCenter,
153 | width,
154 | panResponder,
155 | subContainer,
156 | emojiPressHandler,
157 | container,
158 | hitSlopHeigth,
159 | hitSlopWidth,
160 | };
161 | };
162 |
163 | export default useEmojiView;
164 |
--------------------------------------------------------------------------------
/src/components/ReactionView/ReactionView.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useRef, useState } from 'react';
2 | import {
3 | LayoutRectangle,
4 | Pressable,
5 | SafeAreaView,
6 | StyleSheet,
7 | View,
8 | } from 'react-native';
9 | import EmojiView from '../EmojiView';
10 | import { useReaction } from './hooks';
11 | import styles from './styles';
12 | import type { ReactionViewProps } from './types';
13 |
14 | const ReactionView = (props: ReactionViewProps) => {
15 | const {
16 | children,
17 | touchableProps,
18 | itemIndex = 0,
19 | onPress = () => {},
20 | disabled = false,
21 | onLongPress = () => {},
22 | onShowDismissCard,
23 | } = props;
24 | const [showPopUpCard, setShowPopUpCard] = useState(false);
25 | const [viewHeight, setViewHeight] = useState(0);
26 | const [touchRelease, setTouchRelease] = useState(false);
27 | const [loaded, setLoaded] = useState(false);
28 | const [emojiViewCoordinates, setEmojiViewCoordinates] =
29 | useState({
30 | width: 0,
31 | height: 0,
32 | x: 0,
33 | y: 0,
34 | });
35 | const rootRef = useRef(null);
36 | const {
37 | setCurrentEmoji,
38 | showTopEmojiCard,
39 | setMainViewY,
40 | setMainViewX,
41 | mainViewX,
42 | showCardPosition,
43 | setMainViewWidth,
44 | mainViewY,
45 | emojiSize,
46 | isLongPress,
47 | isSinglePress,
48 | width: screenWidth,
49 | showCardInCenter,
50 | panResponder,
51 | position,
52 | } = useReaction(props);
53 |
54 | const onPressHandler = () => {
55 | rootRef?.current &&
56 | rootRef?.current.measureInWindow(
57 | (x: number, y: number, width: number) => {
58 | setMainViewX(prev => (prev === 0 ? x : prev));
59 | setMainViewY(y);
60 | setMainViewWidth(width);
61 | setCurrentEmoji(0);
62 | setShowPopUpCard(!showPopUpCard);
63 | }
64 | );
65 | };
66 |
67 | const subContainer = StyleSheet.flatten([
68 | [
69 | styles.subContainer,
70 | showTopEmojiCard ? { top: viewHeight } : { bottom: viewHeight - 10 },
71 | showCardPosition > 0
72 | ? { left: showCardInCenter ? -(screenWidth / 2) : 0 }
73 | : { right: mainViewX + showCardPosition },
74 | ],
75 | ]);
76 | const hoverIndex: number = showTopEmojiCard ? -itemIndex : 1;
77 | const checkTouchRelease =
78 | position &&
79 | position > emojiViewCoordinates.x &&
80 | position <= emojiViewCoordinates.width + emojiViewCoordinates.x;
81 | const onStartShouldSetResponder = () => {
82 | setShowPopUpCard(!showPopUpCard);
83 | return true;
84 | };
85 | const isCardOpen: boolean = mainViewX > 0 && showPopUpCard === true;
86 |
87 | useEffect(() => {
88 | onShowDismissCard && onShowDismissCard(showPopUpCard);
89 | }, [onShowDismissCard, showPopUpCard]);
90 |
91 | return (
92 |
95 | {isCardOpen && (
96 |
97 | {
100 | setEmojiViewCoordinates(coordinates);
101 | }}
102 | {...{
103 | x: mainViewX,
104 | y: mainViewY,
105 | isModal: false,
106 | setShowPopUpCard,
107 | showPopUpCard,
108 | emojiSize,
109 | position,
110 | panResponder,
111 | directTouchRelease: touchRelease,
112 | directTouchLoad: loaded,
113 | ...props,
114 | }}
115 | />
116 |
117 | )}
118 | {
120 | const { height } = event.nativeEvent.layout;
121 | setViewHeight(height);
122 | }}
123 | onTouchStart={() => {
124 | setLoaded(true);
125 | setTouchRelease(false);
126 | }}
127 | onTouchEnd={() => {
128 | setLoaded(false);
129 | checkTouchRelease && setTouchRelease(true);
130 | }}
131 | {...panResponder.panHandlers}>
132 | {React.isValidElement(children) && (
133 | {
138 | isLongPress ? onPressHandler() : !isSinglePress && onPress();
139 | onLongPress();
140 | }}
141 | onPress={() => {
142 | isSinglePress ? onPressHandler() : !isLongPress && onPress();
143 | onPress();
144 | }}>
145 | {children}
146 |
147 | )}
148 |
149 |
150 | );
151 | };
152 |
153 | export default ReactionView;
154 |
--------------------------------------------------------------------------------
/example/ios/reactions_example/AppDelegate.mm:
--------------------------------------------------------------------------------
1 | #import "AppDelegate.h"
2 |
3 | #import
4 | #import
5 | #import
6 |
7 | #import
8 |
9 | #if RCT_NEW_ARCH_ENABLED
10 | #import
11 | #import
12 | #import
13 | #import
14 | #import
15 | #import
16 |
17 | #import
18 |
19 | static NSString *const kRNConcurrentRoot = @"concurrentRoot";
20 |
21 | @interface AppDelegate () {
22 | RCTTurboModuleManager *_turboModuleManager;
23 | RCTSurfacePresenterBridgeAdapter *_bridgeAdapter;
24 | std::shared_ptr _reactNativeConfig;
25 | facebook::react::ContextContainer::Shared _contextContainer;
26 | }
27 | @end
28 | #endif
29 |
30 | @implementation AppDelegate
31 |
32 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
33 | {
34 | RCTAppSetupPrepareApp(application);
35 |
36 | RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
37 |
38 | #if RCT_NEW_ARCH_ENABLED
39 | _contextContainer = std::make_shared();
40 | _reactNativeConfig = std::make_shared();
41 | _contextContainer->insert("ReactNativeConfig", _reactNativeConfig);
42 | _bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer];
43 | bridge.surfacePresenter = _bridgeAdapter.surfacePresenter;
44 | #endif
45 |
46 | NSDictionary *initProps = [self prepareInitialProps];
47 | UIView *rootView = RCTAppSetupDefaultRootView(bridge, @"reactions_example", initProps);
48 |
49 | if (@available(iOS 13.0, *)) {
50 | rootView.backgroundColor = [UIColor systemBackgroundColor];
51 | } else {
52 | rootView.backgroundColor = [UIColor whiteColor];
53 | }
54 |
55 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
56 | UIViewController *rootViewController = [UIViewController new];
57 | rootViewController.view = rootView;
58 | self.window.rootViewController = rootViewController;
59 | [self.window makeKeyAndVisible];
60 | return YES;
61 | }
62 |
63 | /// This method controls whether the `concurrentRoot`feature of React18 is turned on or off.
64 | ///
65 | /// @see: https://reactjs.org/blog/2022/03/29/react-v18.html
66 | /// @note: This requires to be rendering on Fabric (i.e. on the New Architecture).
67 | /// @return: `true` if the `concurrentRoot` feture is enabled. Otherwise, it returns `false`.
68 | - (BOOL)concurrentRootEnabled
69 | {
70 | // Switch this bool to turn on and off the concurrent root
71 | return true;
72 | }
73 |
74 | - (NSDictionary *)prepareInitialProps
75 | {
76 | NSMutableDictionary *initProps = [NSMutableDictionary new];
77 |
78 | #ifdef RCT_NEW_ARCH_ENABLED
79 | initProps[kRNConcurrentRoot] = @([self concurrentRootEnabled]);
80 | #endif
81 |
82 | return initProps;
83 | }
84 |
85 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
86 | {
87 | #if DEBUG
88 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
89 | #else
90 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
91 | #endif
92 | }
93 |
94 | #if RCT_NEW_ARCH_ENABLED
95 |
96 | #pragma mark - RCTCxxBridgeDelegate
97 |
98 | - (std::unique_ptr)jsExecutorFactoryForBridge:(RCTBridge *)bridge
99 | {
100 | _turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge
101 | delegate:self
102 | jsInvoker:bridge.jsCallInvoker];
103 | return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager);
104 | }
105 |
106 | #pragma mark RCTTurboModuleManagerDelegate
107 |
108 | - (Class)getModuleClassFromName:(const char *)name
109 | {
110 | return RCTCoreModulesClassProvider(name);
111 | }
112 |
113 | - (std::shared_ptr)getTurboModule:(const std::string &)name
114 | jsInvoker:(std::shared_ptr)jsInvoker
115 | {
116 | return nullptr;
117 | }
118 |
119 | - (std::shared_ptr)getTurboModule:(const std::string &)name
120 | initParams:
121 | (const facebook::react::ObjCTurboModule::InitParams &)params
122 | {
123 | return nullptr;
124 | }
125 |
126 | - (id)getModuleInstanceFromClass:(Class)moduleClass
127 | {
128 | return RCTAppSetupDefaultModuleFromClass(moduleClass);
129 | }
130 |
131 | #endif
132 |
133 | @end
134 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/com/reactions_example/newarchitecture/MainApplicationReactNativeHost.java:
--------------------------------------------------------------------------------
1 | package com.reactions_example.newarchitecture;
2 |
3 | import android.app.Application;
4 | import androidx.annotation.NonNull;
5 | import com.facebook.react.PackageList;
6 | import com.facebook.react.ReactInstanceManager;
7 | import com.facebook.react.ReactNativeHost;
8 | import com.facebook.react.ReactPackage;
9 | import com.facebook.react.ReactPackageTurboModuleManagerDelegate;
10 | import com.facebook.react.bridge.JSIModulePackage;
11 | import com.facebook.react.bridge.JSIModuleProvider;
12 | import com.facebook.react.bridge.JSIModuleSpec;
13 | import com.facebook.react.bridge.JSIModuleType;
14 | import com.facebook.react.bridge.JavaScriptContextHolder;
15 | import com.facebook.react.bridge.ReactApplicationContext;
16 | import com.facebook.react.bridge.UIManager;
17 | import com.facebook.react.fabric.ComponentFactory;
18 | import com.facebook.react.fabric.CoreComponentsRegistry;
19 | import com.facebook.react.fabric.FabricJSIModuleProvider;
20 | import com.facebook.react.fabric.ReactNativeConfig;
21 | import com.facebook.react.uimanager.ViewManagerRegistry;
22 | import com.reactions_example.BuildConfig;
23 | import com.reactions_example.newarchitecture.components.MainComponentsRegistry;
24 | import com.reactions_example.newarchitecture.modules.MainApplicationTurboModuleManagerDelegate;
25 | import java.util.ArrayList;
26 | import java.util.List;
27 |
28 | /**
29 | * A {@link ReactNativeHost} that helps you load everything needed for the New Architecture, both
30 | * TurboModule delegates and the Fabric Renderer.
31 | *
32 | * Please note that this class is used ONLY if you opt-in for the New Architecture (see the
33 | * `newArchEnabled` property). Is ignored otherwise.
34 | */
35 | public class MainApplicationReactNativeHost extends ReactNativeHost {
36 | public MainApplicationReactNativeHost(Application application) {
37 | super(application);
38 | }
39 |
40 | @Override
41 | public boolean getUseDeveloperSupport() {
42 | return BuildConfig.DEBUG;
43 | }
44 |
45 | @Override
46 | protected List getPackages() {
47 | List packages = new PackageList(this).getPackages();
48 | // Packages that cannot be autolinked yet can be added manually here, for example:
49 | // packages.add(new MyReactNativePackage());
50 | // TurboModules must also be loaded here providing a valid TurboReactPackage implementation:
51 | // packages.add(new TurboReactPackage() { ... });
52 | // If you have custom Fabric Components, their ViewManagers should also be loaded here
53 | // inside a ReactPackage.
54 | return packages;
55 | }
56 |
57 | @Override
58 | protected String getJSMainModuleName() {
59 | return "index";
60 | }
61 |
62 | @NonNull
63 | @Override
64 | protected ReactPackageTurboModuleManagerDelegate.Builder
65 | getReactPackageTurboModuleManagerDelegateBuilder() {
66 | // Here we provide the ReactPackageTurboModuleManagerDelegate Builder. This is necessary
67 | // for the new architecture and to use TurboModules correctly.
68 | return new MainApplicationTurboModuleManagerDelegate.Builder();
69 | }
70 |
71 | @Override
72 | protected JSIModulePackage getJSIModulePackage() {
73 | return new JSIModulePackage() {
74 | @Override
75 | public List getJSIModules(
76 | final ReactApplicationContext reactApplicationContext,
77 | final JavaScriptContextHolder jsContext) {
78 | final List specs = new ArrayList<>();
79 |
80 | // Here we provide a new JSIModuleSpec that will be responsible of providing the
81 | // custom Fabric Components.
82 | specs.add(
83 | new JSIModuleSpec() {
84 | @Override
85 | public JSIModuleType getJSIModuleType() {
86 | return JSIModuleType.UIManager;
87 | }
88 |
89 | @Override
90 | public JSIModuleProvider getJSIModuleProvider() {
91 | final ComponentFactory componentFactory = new ComponentFactory();
92 | CoreComponentsRegistry.register(componentFactory);
93 |
94 | // Here we register a Components Registry.
95 | // The one that is generated with the template contains no components
96 | // and just provides you the one from React Native core.
97 | MainComponentsRegistry.register(componentFactory);
98 |
99 | final ReactInstanceManager reactInstanceManager = getReactInstanceManager();
100 |
101 | ViewManagerRegistry viewManagerRegistry =
102 | new ViewManagerRegistry(
103 | reactInstanceManager.getOrCreateViewManagers(reactApplicationContext));
104 |
105 | return new FabricJSIModuleProvider(
106 | reactApplicationContext,
107 | componentFactory,
108 | ReactNativeConfig.DEFAULT_CONFIG,
109 | viewManagerRegistry);
110 | }
111 | });
112 | return specs;
113 | }
114 | };
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/example/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
84 |
85 | APP_NAME="Gradle"
86 | APP_BASE_NAME=${0##*/}
87 |
88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
90 |
91 | # Use the maximum available, or set MAX_FD != -1 to use that value.
92 | MAX_FD=maximum
93 |
94 | warn () {
95 | echo "$*"
96 | } >&2
97 |
98 | die () {
99 | echo
100 | echo "$*"
101 | echo
102 | exit 1
103 | } >&2
104 |
105 | # OS specific support (must be 'true' or 'false').
106 | cygwin=false
107 | msys=false
108 | darwin=false
109 | nonstop=false
110 | case "$( uname )" in #(
111 | CYGWIN* ) cygwin=true ;; #(
112 | Darwin* ) darwin=true ;; #(
113 | MSYS* | MINGW* ) msys=true ;; #(
114 | NONSTOP* ) nonstop=true ;;
115 | esac
116 |
117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
118 |
119 |
120 | # Determine the Java command to use to start the JVM.
121 | if [ -n "$JAVA_HOME" ] ; then
122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
123 | # IBM's JDK on AIX uses strange locations for the executables
124 | JAVACMD=$JAVA_HOME/jre/sh/java
125 | else
126 | JAVACMD=$JAVA_HOME/bin/java
127 | fi
128 | if [ ! -x "$JAVACMD" ] ; then
129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
130 |
131 | Please set the JAVA_HOME variable in your environment to match the
132 | location of your Java installation."
133 | fi
134 | else
135 | JAVACMD=java
136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 |
142 | # Increase the maximum file descriptors if we can.
143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
144 | case $MAX_FD in #(
145 | max*)
146 | MAX_FD=$( ulimit -H -n ) ||
147 | warn "Could not query maximum file descriptor limit"
148 | esac
149 | case $MAX_FD in #(
150 | '' | soft) :;; #(
151 | *)
152 | ulimit -n "$MAX_FD" ||
153 | warn "Could not set maximum file descriptor limit to $MAX_FD"
154 | esac
155 | fi
156 |
157 | # Collect all arguments for the java command, stacking in reverse order:
158 | # * args from the command line
159 | # * the main class name
160 | # * -classpath
161 | # * -D...appname settings
162 | # * --module-path (only if needed)
163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
164 |
165 | # For Cygwin or MSYS, switch paths to Windows format before running java
166 | if "$cygwin" || "$msys" ; then
167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
169 |
170 | JAVACMD=$( cygpath --unix "$JAVACMD" )
171 |
172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
173 | for arg do
174 | if
175 | case $arg in #(
176 | -*) false ;; # don't mess with options #(
177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
178 | [ -e "$t" ] ;; #(
179 | *) false ;;
180 | esac
181 | then
182 | arg=$( cygpath --path --ignore --mixed "$arg" )
183 | fi
184 | # Roll the args list around exactly as many times as the number of
185 | # args, so each arg winds up back in the position where it started, but
186 | # possibly modified.
187 | #
188 | # NB: a `for` loop captures its iteration list before it begins, so
189 | # changing the positional parameters here affects neither the number of
190 | # iterations, nor the values presented in `arg`.
191 | shift # remove old arg
192 | set -- "$@" "$arg" # push replacement arg
193 | done
194 | fi
195 |
196 | # Collect all arguments for the java command;
197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
198 | # shell script including quotes and variable substitutions, so put them in
199 | # double quotes to make sure that they get re-expanded; and
200 | # * put everything else in single quotes, so that it's not re-expanded.
201 |
202 | set -- \
203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
204 | -classpath "$CLASSPATH" \
205 | org.gradle.wrapper.GradleWrapperMain \
206 | "$@"
207 |
208 | # Use "xargs" to parse quoted args.
209 | #
210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
211 | #
212 | # In Bash we could simply go:
213 | #
214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
215 | # set -- "${ARGS[@]}" "$@"
216 | #
217 | # but POSIX shell has neither arrays nor command substitution, so instead we
218 | # post-process each arg (as a line of input to sed) to backslash-escape any
219 | # character that might be a shell metacharacter, then use eval to reverse
220 | # that process (while maintaining the separation between arguments), and wrap
221 | # the whole thing up as a single "set" statement.
222 | #
223 | # This will of course break if any of these variables contains a newline or
224 | # an unmatched quote.
225 | #
226 |
227 | eval "set -- $(
228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
229 | xargs -n1 |
230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
231 | tr '\n' ' '
232 | )" '"$@"'
233 |
234 | exec "$JAVACMD" "$@"
235 |
--------------------------------------------------------------------------------
/example/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: "com.android.application"
2 |
3 | import com.android.build.OutputFile
4 |
5 | /**
6 | * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
7 | * and bundleReleaseJsAndAssets).
8 | * These basically call `react-native bundle` with the correct arguments during the Android build
9 | * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
10 | * bundle directly from the development server. Below you can see all the possible configurations
11 | * and their defaults. If you decide to add a configuration block, make sure to add it before the
12 | * `apply from: "../../node_modules/react-native/react.gradle"` line.
13 | *
14 | * project.ext.react = [
15 | * // the name of the generated asset file containing your JS bundle
16 | * bundleAssetName: "index.android.bundle",
17 | *
18 | * // the entry file for bundle generation. If none specified and
19 | * // "index.android.js" exists, it will be used. Otherwise "index.js" is
20 | * // default. Can be overridden with ENTRY_FILE environment variable.
21 | * entryFile: "index.android.js",
22 | *
23 | * // https://reactnative.dev/docs/performance#enable-the-ram-format
24 | * bundleCommand: "ram-bundle",
25 | *
26 | * // whether to bundle JS and assets in debug mode
27 | * bundleInDebug: false,
28 | *
29 | * // whether to bundle JS and assets in release mode
30 | * bundleInRelease: true,
31 | *
32 | * // whether to bundle JS and assets in another build variant (if configured).
33 | * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
34 | * // The configuration property can be in the following formats
35 | * // 'bundleIn${productFlavor}${buildType}'
36 | * // 'bundleIn${buildType}'
37 | * // bundleInFreeDebug: true,
38 | * // bundleInPaidRelease: true,
39 | * // bundleInBeta: true,
40 | *
41 | * // whether to disable dev mode in custom build variants (by default only disabled in release)
42 | * // for example: to disable dev mode in the staging build type (if configured)
43 | * devDisabledInStaging: true,
44 | * // The configuration property can be in the following formats
45 | * // 'devDisabledIn${productFlavor}${buildType}'
46 | * // 'devDisabledIn${buildType}'
47 | *
48 | * // the root of your project, i.e. where "package.json" lives
49 | * root: "../../",
50 | *
51 | * // where to put the JS bundle asset in debug mode
52 | * jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
53 | *
54 | * // where to put the JS bundle asset in release mode
55 | * jsBundleDirRelease: "$buildDir/intermediates/assets/release",
56 | *
57 | * // where to put drawable resources / React Native assets, e.g. the ones you use via
58 | * // require('./image.png')), in debug mode
59 | * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
60 | *
61 | * // where to put drawable resources / React Native assets, e.g. the ones you use via
62 | * // require('./image.png')), in release mode
63 | * resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
64 | *
65 | * // by default the gradle tasks are skipped if none of the JS files or assets change; this means
66 | * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
67 | * // date; if you have any other folders that you want to ignore for performance reasons (gradle
68 | * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
69 | * // for example, you might want to remove it from here.
70 | * inputExcludes: ["android/**", "ios/**"],
71 | *
72 | * // override which node gets called and with what additional arguments
73 | * nodeExecutableAndArgs: ["node"],
74 | *
75 | * // supply additional arguments to the packager
76 | * extraPackagerArgs: []
77 | * ]
78 | */
79 |
80 | project.ext.react = [
81 | enableHermes: false, // clean and rebuild if changing
82 | ]
83 |
84 | apply from: "../../node_modules/react-native/react.gradle"
85 |
86 | /**
87 | * Set this to true to create two separate APKs instead of one:
88 | * - An APK that only works on ARM devices
89 | * - An APK that only works on x86 devices
90 | * The advantage is the size of the APK is reduced by about 4MB.
91 | * Upload all the APKs to the Play Store and people will download
92 | * the correct one based on the CPU architecture of their device.
93 | */
94 | def enableSeparateBuildPerCPUArchitecture = false
95 |
96 | /**
97 | * Run Proguard to shrink the Java bytecode in release builds.
98 | */
99 | def enableProguardInReleaseBuilds = false
100 |
101 | /**
102 | * The preferred build flavor of JavaScriptCore.
103 | *
104 | * For example, to use the international variant, you can use:
105 | * `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
106 | *
107 | * The international variant includes ICU i18n library and necessary data
108 | * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
109 | * give correct results when using with locales other than en-US. Note that
110 | * this variant is about 6MiB larger per architecture than default.
111 | */
112 | def jscFlavor = 'org.webkit:android-jsc:+'
113 |
114 | /**
115 | * Whether to enable the Hermes VM.
116 | *
117 | * This should be set on project.ext.react and that value will be read here. If it is not set
118 | * on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
119 | * and the benefits of using Hermes will therefore be sharply reduced.
120 | */
121 | def enableHermes = project.ext.react.get("enableHermes", false);
122 |
123 | /**
124 | * Architectures to build native code for.
125 | */
126 | def reactNativeArchitectures() {
127 | def value = project.getProperties().get("reactNativeArchitectures")
128 | return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
129 | }
130 |
131 | android {
132 | ndkVersion rootProject.ext.ndkVersion
133 |
134 | compileSdkVersion rootProject.ext.compileSdkVersion
135 |
136 | defaultConfig {
137 | applicationId "com.reactions_example"
138 | minSdkVersion rootProject.ext.minSdkVersion
139 | targetSdkVersion rootProject.ext.targetSdkVersion
140 | versionCode 1
141 | versionName "1.0"
142 | buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
143 |
144 | if (isNewArchitectureEnabled()) {
145 | // We configure the NDK build only if you decide to opt-in for the New Architecture.
146 | externalNativeBuild {
147 | ndkBuild {
148 | arguments "APP_PLATFORM=android-21",
149 | "APP_STL=c++_shared",
150 | "NDK_TOOLCHAIN_VERSION=clang",
151 | "GENERATED_SRC_DIR=$buildDir/generated/source",
152 | "PROJECT_BUILD_DIR=$buildDir",
153 | "REACT_ANDROID_DIR=$rootDir/../node_modules/react-native/ReactAndroid",
154 | "REACT_ANDROID_BUILD_DIR=$rootDir/../node_modules/react-native/ReactAndroid/build",
155 | "NODE_MODULES_DIR=$rootDir/../node_modules"
156 | cFlags "-Wall", "-Werror", "-fexceptions", "-frtti", "-DWITH_INSPECTOR=1"
157 | cppFlags "-std=c++17"
158 | // Make sure this target name is the same you specify inside the
159 | // src/main/jni/Android.mk file for the `LOCAL_MODULE` variable.
160 | targets "reactions_example_appmodules"
161 | }
162 | }
163 | if (!enableSeparateBuildPerCPUArchitecture) {
164 | ndk {
165 | abiFilters (*reactNativeArchitectures())
166 | }
167 | }
168 | }
169 | }
170 |
171 | if (isNewArchitectureEnabled()) {
172 | // We configure the NDK build only if you decide to opt-in for the New Architecture.
173 | externalNativeBuild {
174 | ndkBuild {
175 | path "$projectDir/src/main/jni/Android.mk"
176 | }
177 | }
178 | def reactAndroidProjectDir = project(':ReactAndroid').projectDir
179 | def packageReactNdkDebugLibs = tasks.register("packageReactNdkDebugLibs", Copy) {
180 | dependsOn(":ReactAndroid:packageReactNdkDebugLibsForBuck")
181 | from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib")
182 | into("$buildDir/react-ndk/exported")
183 | }
184 | def packageReactNdkReleaseLibs = tasks.register("packageReactNdkReleaseLibs", Copy) {
185 | dependsOn(":ReactAndroid:packageReactNdkReleaseLibsForBuck")
186 | from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib")
187 | into("$buildDir/react-ndk/exported")
188 | }
189 | afterEvaluate {
190 | // If you wish to add a custom TurboModule or component locally,
191 | // you should uncomment this line.
192 | // preBuild.dependsOn("generateCodegenArtifactsFromSchema")
193 | preDebugBuild.dependsOn(packageReactNdkDebugLibs)
194 | preReleaseBuild.dependsOn(packageReactNdkReleaseLibs)
195 |
196 | // Due to a bug inside AGP, we have to explicitly set a dependency
197 | // between configureNdkBuild* tasks and the preBuild tasks.
198 | // This can be removed once this is solved: https://issuetracker.google.com/issues/207403732
199 | configureNdkBuildRelease.dependsOn(preReleaseBuild)
200 | configureNdkBuildDebug.dependsOn(preDebugBuild)
201 | reactNativeArchitectures().each { architecture ->
202 | tasks.findByName("configureNdkBuildDebug[${architecture}]")?.configure {
203 | dependsOn("preDebugBuild")
204 | }
205 | tasks.findByName("configureNdkBuildRelease[${architecture}]")?.configure {
206 | dependsOn("preReleaseBuild")
207 | }
208 | }
209 | }
210 | }
211 |
212 | splits {
213 | abi {
214 | reset()
215 | enable enableSeparateBuildPerCPUArchitecture
216 | universalApk false // If true, also generate a universal APK
217 | include (*reactNativeArchitectures())
218 | }
219 | }
220 | signingConfigs {
221 | debug {
222 | storeFile file('debug.keystore')
223 | storePassword 'android'
224 | keyAlias 'androiddebugkey'
225 | keyPassword 'android'
226 | }
227 | }
228 | buildTypes {
229 | debug {
230 | signingConfig signingConfigs.debug
231 | }
232 | release {
233 | // Caution! In production, you need to generate your own keystore file.
234 | // see https://reactnative.dev/docs/signed-apk-android.
235 | signingConfig signingConfigs.debug
236 | minifyEnabled enableProguardInReleaseBuilds
237 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
238 | }
239 | }
240 |
241 | // applicationVariants are e.g. debug, release
242 | applicationVariants.all { variant ->
243 | variant.outputs.each { output ->
244 | // For each separate APK per architecture, set a unique version code as described here:
245 | // https://developer.android.com/studio/build/configure-apk-splits.html
246 | // Example: versionCode 1 will generate 1001 for armeabi-v7a, 1002 for x86, etc.
247 | def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
248 | def abi = output.getFilter(OutputFile.ABI)
249 | if (abi != null) { // null for the universal-debug, universal-release variants
250 | output.versionCodeOverride =
251 | defaultConfig.versionCode * 1000 + versionCodes.get(abi)
252 | }
253 |
254 | }
255 | }
256 | }
257 |
258 | dependencies {
259 | implementation fileTree(dir: "libs", include: ["*.jar"])
260 |
261 | //noinspection GradleDynamicVersion
262 | implementation "com.facebook.react:react-native:+" // From node_modules
263 |
264 | implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
265 |
266 | debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
267 | exclude group:'com.facebook.fbjni'
268 | }
269 |
270 | debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
271 | exclude group:'com.facebook.flipper'
272 | exclude group:'com.squareup.okhttp3', module:'okhttp'
273 | }
274 |
275 | debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
276 | exclude group:'com.facebook.flipper'
277 | }
278 |
279 | if (enableHermes) {
280 | //noinspection GradleDynamicVersion
281 | implementation("com.facebook.react:hermes-engine:+") { // From node_modules
282 | exclude group:'com.facebook.fbjni'
283 | }
284 | } else {
285 | implementation jscFlavor
286 | }
287 | }
288 |
289 | if (isNewArchitectureEnabled()) {
290 | // If new architecture is enabled, we let you build RN from source
291 | // Otherwise we fallback to a prebuilt .aar bundled in the NPM package.
292 | // This will be applied to all the imported transtitive dependency.
293 | configurations.all {
294 | resolutionStrategy.dependencySubstitution {
295 | substitute(module("com.facebook.react:react-native"))
296 | .using(project(":ReactAndroid"))
297 | .because("On New Architecture we're building React Native from source")
298 | substitute(module("com.facebook.react:hermes-engine"))
299 | .using(project(":ReactAndroid:hermes-engine"))
300 | .because("On New Architecture we're building Hermes from source")
301 | }
302 | }
303 | }
304 |
305 | // Run this once to be able to run the application with BUCK
306 | // puts all compile dependencies into folder libs for BUCK to use
307 | task copyDownloadableDepsToLibs(type: Copy) {
308 | from configurations.implementation
309 | into 'libs'
310 | }
311 |
312 | apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
313 |
314 | def isNewArchitectureEnabled() {
315 | // To opt-in for the New Architecture, you can either:
316 | // - Set `newArchEnabled` to true inside the `gradle.properties` file
317 | // - Invoke gradle with `-newArchEnabled=true`
318 | // - Set an environment variable `ORG_GRADLE_PROJECT_newArchEnabled=true`
319 | return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
320 | }
321 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # react-native-reactions
4 |
5 | [](https://www.npmjs.com/package/react-native-reactions) [](https://www.npmtrends.com/react-native-reactions) [](https://packagephobia.com/result?p=react-native-reactions) [](https://www.android.com) [](https://developer.apple.com/ios) [](https://opensource.org/licenses/MIT)
6 |
7 | ---
8 |
9 | This is a pure javascript and react-native-reanimated based library that provides reaction feature like Instagram/WhatsApp or other social media.
10 |
11 | It is simple to use and fully customizable. It works on both android and iOS platforms.
12 |
13 | ---
14 |
15 | ## 🎬 Preview
16 |
17 | ---
18 |
19 | | Default | Custom |
20 | | ------------------------------------ | --------------------------------- |
21 | |  |  |
22 |
23 | ---
24 |
25 | ## Quick Access
26 |
27 | [Installation](#installation) | [Reaction](#reaction) | [Properties](#properties) | [Example](#example) | [License](#license)
28 |
29 | # Installation
30 |
31 | ##### 1. Install library and react-native-reanimated
32 |
33 | ```bash
34 | npm install react-native-reactions react-native-reanimated
35 | ```
36 |
37 | ###### --- or ---
38 |
39 | ```bash
40 | yarn add react-native-reactions react-native-reanimated
41 | ```
42 |
43 | ##### 2. Install cocoapods in the ios project
44 |
45 | ```bash
46 | cd ios && pod install
47 | ```
48 |
49 | > Note: Make sure to add Reanimated's babel plugin to your `babel.config.js`
50 |
51 | ```
52 | module.exports = {
53 | ...
54 | plugins: [
55 | ...
56 | 'react-native-reanimated/plugin',
57 | ],
58 | };
59 | ```
60 |
61 | ##### Know more about [react-native-reanimated](https://www.npmjs.com/package/react-native-reanimated)
62 |
63 | ---
64 |
65 | # Reaction
66 |
67 | - Reaction component has two different variants
68 | - Default reaction: This variant of reaction is based on an absolute view
69 | - Modal reaction: This variant of reaction is based on a Modal view
70 | - To avoid the zIndex/Overlap issue, you can use modal variant of reaction component instead of the default
71 |
72 | #### 🎬 Preview
73 |
74 | ## 
75 |
76 | ### ReactionItems
77 |
78 | ```jsx
79 | const ReactionItems = [
80 | {
81 | id: string,
82 | emoji: element | string | url,
83 | title: string,
84 | },
85 | ];
86 | ```
87 |
88 | ### Emoji Data Format
89 |
90 | ```jsx
91 | const ReactionItems = [
92 | {
93 | id: 0,
94 | emoji: '😇',
95 | title: 'like',
96 | },
97 | {
98 | id: 1,
99 | emoji: '🥰',
100 | title: 'love',
101 | },
102 | {
103 | id: 2,
104 | emoji: '🤗',
105 | title: 'care',
106 | },
107 | {
108 | id: 3,
109 | emoji: '😘',
110 | title: 'kiss',
111 | },
112 | {
113 | id: 4,
114 | emoji: '😂',
115 | title: 'laugh',
116 | },
117 | {
118 | id: 5,
119 | emoji: '😎',
120 | title: 'cool',
121 | },
122 | ];
123 | ```
124 |
125 | ## Default Reaction
126 |
127 | ---
128 |
129 | #### 🎬 Preview
130 |
131 | ## 
132 |
133 | #### Usage
134 |
135 | ```jsx
136 | const ReactionItem = () => {
137 | const [selectedEmoji, setSelectedEmoji] = useState();
138 | return (
139 |
140 |
141 | {selectedEmoji ? selectedEmoji?.emoji : 'Like'}
142 |
143 |
144 | );
145 | };
146 | ```
147 |
148 | ---
149 |
150 | > Note: To improve the performance with Flatlist, consider wrapping your renderItem with memo. Additionally, pass state variable that will be used to manage the scroll enabled property of the Flatlist. This can further optimize the rendering of entire list.
151 | >
152 | > You're not sure what this means, take a look at this [example card](./example/src/component/Card/Card.tsx)
153 |
154 | ---
155 |
156 | ##### App
157 |
158 | ```jsx
159 | import React, { useState } from 'react';
160 | import { FlatList, SafeAreaView, StyleSheet } from 'react-native';
161 | import { Card } from './component';
162 |
163 | const PostItemList = [
164 | {
165 | id: 'bd7acbea-c1b1-46c2-aed5-3ad53abb28ba',
166 | title: 'First Item',
167 | image:
168 | 'https://raw.githubusercontent.com/SimformSolutionsPvtLtd/react-native-story-view/main/assets/banner.png',
169 | },
170 | {
171 | id: '3ac68afc-c605-48d3-a4f8-fbd91aa97f63',
172 | title: 'Second Item',
173 | image:
174 | 'https://raw.githubusercontent.com/SimformSolutionsPvtLtd/react-native-radial-slider/main/assets/banner.png',
175 | },
176 | {
177 | id: '58694a0f-3da1-471f-bd96-145571e29d72',
178 | title: 'Third Item',
179 | image:
180 | 'https://raw.githubusercontent.com/SimformSolutionsPvtLtd/react-native-country-code-select/main/assets/banner.png',
181 | },
182 | ];
183 |
184 | const App = () => {
185 | const [isScrollEnable, setIsScrollEnable] = useState(true);
186 | return (
187 |
188 | (
193 | setIsScrollEnable(!e)}
197 | isScrollEnable
198 | />
199 | )}
200 | keyExtractor={item => item?.id}
201 | />
202 |
203 | );
204 | };
205 |
206 | export default App;
207 |
208 | const styles = StyleSheet.create({
209 | mainStyle: {
210 | flex: 1,
211 | },
212 | flatlistStyle: {
213 | backgroundColor: '#c9cdd0',
214 | },
215 | });
216 | ```
217 |
218 | ##### Card
219 |
220 | ```jsx
221 |
222 | import {Image, StyleSheet, Text, View} from 'react-native';
223 | import React, {memo, useState} from 'react';
224 | import {Reaction} from 'react-native-reactions';
225 | import _ from 'lodash';
226 |
227 | interface EmojiItemProp {
228 | id: number;
229 | emoji: React.ReactNode | string | number;
230 | title: string;
231 | }
232 |
233 | interface CardProps extends CardItemsProps {
234 | index?: number;
235 | selectedEmoji?: EmojiItemProp;
236 | setSelectedEmoji?: (e: EmojiItemProp | undefined) => void;
237 | onShowDismissCard?: (e?: boolean) => void;
238 | isScrollEnable?: boolean;
239 | }
240 |
241 | interface CardItemsProps {
242 | id?: string;
243 | image?: string;
244 | title?: string;
245 | }
246 |
247 | const ReactionItems = [
248 | {
249 | id: 0,
250 | emoji: '😇',
251 | title: 'like',
252 | },
253 | {
254 | id: 1,
255 | emoji: '🥰',
256 | title: 'love',
257 | },
258 | {
259 | id: 2,
260 | emoji: '🤗',
261 | title: 'care',
262 | },
263 | {
264 | id: 3,
265 | emoji: '😘',
266 | title: 'kiss',
267 | },
268 | {
269 | id: 4,
270 | emoji: '😂',
271 | title: 'laugh',
272 | },
273 | {
274 | id: 5,
275 | emoji: '😎',
276 | title: 'cool',
277 | },
278 | ];
279 |
280 | const Card = ({index, onShowDismissCard, ...item}: CardProps) => {
281 | const [selectedEmoji, setSelectedEmoji] = useState();
282 |
283 | return (
284 |
285 |
286 |
287 |
288 |
289 |
290 |
295 | {selectedEmoji ? selectedEmoji?.emoji : 'Like'}
296 |
297 | Share
298 |
299 |
300 | );
301 | };
302 |
303 | export default memo(Card, (prevProps, nextProps) =>
304 | _.isEqual(prevProps?.isScrollEnable, nextProps?.isScrollEnable),
305 | );
306 |
307 | const styles = StyleSheet.create({
308 | cardContainer: {
309 | marginVertical: 5,
310 | backgroundColor: '#FFFFFF',
311 | },
312 | postImageContainer: {
313 | alignItems: 'center',
314 | zIndex: -1,
315 | },
316 | postImage: {
317 | width: '100%',
318 | height: 200,
319 | zIndex: -1,
320 | resizeMode: 'center',
321 | },
322 | line: {
323 | borderWidth: 0.3,
324 | borderColor: '#c9cdd0',
325 | },
326 | bottomContainer: {
327 | flexDirection: 'row',
328 | justifyContent: 'space-between',
329 | margin: 10,
330 | marginHorizontal: 20,
331 | },
332 | });
333 |
334 | ```
335 |
336 | ---
337 |
338 | ## Modal Reaction
339 |
340 | - Modal reaction variant can be used to avoid the zIndex / Overlap issue on reaction popup
341 |
342 | > Note: Make sure to wrap your root component with ReactionProvider
343 |
344 | ```bash
345 | import { ReactionProvider } from 'react-native-reactions';
346 | export default const App = () => {
347 | return {/* content */};
348 | }
349 | ```
350 |
351 | #### 🎬 Preview
352 |
353 | ## 
354 |
355 | #### Usage
356 |
357 | ---
358 |
359 | ##### App.tsx
360 |
361 | Use the above [App](#app) example but the only change here is to wrap the root component with ReactionProvider.
362 |
363 | ```jsx
364 | import { ReactionProvider } from 'react-native-reactions';
365 |
366 |
367 |
368 | (
373 | setIsScrollEnable(!e)}
377 | isScrollEnable
378 | />
379 | )}
380 | keyExtractor={item => item?.id}
381 | />
382 |
383 | ;
384 | ```
385 |
386 | ##### Card.tsx
387 |
388 | Use the above [Card](#card) example but the only change here is to set type as modal.
389 |
390 | ```jsx
391 |
396 | {selectedEmoji ? selectedEmoji?.emoji : 'Like'}
397 |
398 | ```
399 |
400 | ---
401 |
402 | # Properties
403 |
404 | ---
405 |
406 | | Prop | Default | Type | Description |
407 | | :------------------ | :------------------------------ | :-------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
408 | | type | default | string | Different type of component like default and modal |
409 | | items | [ReactionItems](#reactionitems) | array | Array of reaction emojis |
410 | | disabled | false | boolean | If true, disable all interactions for this component |
411 | | showPopupType | default | string | Pressable showPopupType like default and onPress
- If showPopupType is default, then reaction popup will be shown on onLongPress only
- If showPopupType is onPress, then reaction popup will be shown on onPress only |
412 | | onPress | - | function | Callback function that triggers when the wrapped element is pressed |
413 | | onLongPress | - | function | Callback function that triggers when the wrapped element is long pressed |
414 | | onTap | - | function | Callback function that returns selected emoji |
415 | | cardStyle | {} | ViewStyle | Card modal style |
416 | | emojiStyle | {} | TextStyle | Emoji style |
417 | | onShowDismissCard | - | function | Callback function that returns reaction card popup status (true / false) |
418 | | isShowCardInCenter | false | boolean | If true, Show card in center |
419 | | iconSize | 25 | number | Size of emoji. It should be in between 15 to 30. |
420 | | titleStyle | {} | TextStyle | Title style for emoji |
421 | | titleBoxStyle | {} | ViewStyle | Title box style |
422 | | emojiContainerStyle | {} | ViewStyle | Emoji container style |
423 | | cardDuration | 150 | number | Card animation duration |
424 | | opacityRange | [0, 0, 1] | array | Opacity range for card container (`note`: opacity range must be in ascending order) |
425 | | emojiDuration | 200 | number | Emoji animation duration |
426 | | scaleDuration | 100 | number | Scale animation duration |
427 |
428 | ---
429 |
430 | # Example
431 |
432 | A full working example project is here [Example](./example/src/App.tsx)
433 |
434 | ```sh
435 | yarn
436 | yarn example ios // For ios
437 | yarn example android // For Android
438 | ```
439 |
440 | # TODO
441 |
442 | - [ ] Customize Emoji (Add more emoji options)
443 | - [ ] Landscape support
444 |
445 | ## Find this library useful? ❤️
446 |
447 | Support it by joining [stargazers](https://github.com/SimformSolutionsPvtLtd/react-native-reactions/stargazers) for this repository.⭐
448 |
449 | ## Bugs / Feature requests / Feedbacks
450 |
451 | For bugs, feature requests, and discussion please use [GitHub Issues](https://github.com/SimformSolutionsPvtLtd/react-native-reactions/issues/new?labels=bug&late=BUG_REPORT.md&title=%5BBUG%5D%3A), [GitHub New Feature](https://github.com/SimformSolutionsPvtLtd/react-native-reactions/issues/new?labels=enhancement&late=FEATURE_REQUEST.md&title=%5BFEATURE%5D%3A), [GitHub Feedback](https://github.com/SimformSolutionsPvtLtd/react-native-reactions/issues/new?labels=enhancement&late=FEATURE_REQUEST.md&title=%5BFEEDBACK%5D%3A)
452 |
453 | ## 🤝 How to Contribute
454 |
455 | We'd love to have you improve this library or fix a problem 💪
456 | Check out our [Contributing Guide](CONTRIBUTING.md) for ideas on contributing.
457 |
458 | ## Awesome Mobile Libraries
459 |
460 | - Check out our other [available awesome mobile libraries](https://github.com/SimformSolutionsPvtLtd/Awesome-Mobile-Libraries)
461 |
462 | ## License
463 |
464 | - [MIT License](./LICENSE)
465 |
--------------------------------------------------------------------------------
/example/ios/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - boost (1.76.0)
3 | - DoubleConversion (1.1.6)
4 | - FBLazyVector (0.69.5)
5 | - FBReactNativeSpec (0.69.5):
6 | - RCT-Folly (= 2021.06.28.00-v2)
7 | - RCTRequired (= 0.69.5)
8 | - RCTTypeSafety (= 0.69.5)
9 | - React-Core (= 0.69.5)
10 | - React-jsi (= 0.69.5)
11 | - ReactCommon/turbomodule/core (= 0.69.5)
12 | - fmt (6.2.1)
13 | - glog (0.3.5)
14 | - RCT-Folly (2021.06.28.00-v2):
15 | - boost
16 | - DoubleConversion
17 | - fmt (~> 6.2.1)
18 | - glog
19 | - RCT-Folly/Default (= 2021.06.28.00-v2)
20 | - RCT-Folly/Default (2021.06.28.00-v2):
21 | - boost
22 | - DoubleConversion
23 | - fmt (~> 6.2.1)
24 | - glog
25 | - RCTRequired (0.69.5)
26 | - RCTTypeSafety (0.69.5):
27 | - FBLazyVector (= 0.69.5)
28 | - RCTRequired (= 0.69.5)
29 | - React-Core (= 0.69.5)
30 | - React (0.69.5):
31 | - React-Core (= 0.69.5)
32 | - React-Core/DevSupport (= 0.69.5)
33 | - React-Core/RCTWebSocket (= 0.69.5)
34 | - React-RCTActionSheet (= 0.69.5)
35 | - React-RCTAnimation (= 0.69.5)
36 | - React-RCTBlob (= 0.69.5)
37 | - React-RCTImage (= 0.69.5)
38 | - React-RCTLinking (= 0.69.5)
39 | - React-RCTNetwork (= 0.69.5)
40 | - React-RCTSettings (= 0.69.5)
41 | - React-RCTText (= 0.69.5)
42 | - React-RCTVibration (= 0.69.5)
43 | - React-bridging (0.69.5):
44 | - RCT-Folly (= 2021.06.28.00-v2)
45 | - React-jsi (= 0.69.5)
46 | - React-callinvoker (0.69.5)
47 | - React-Codegen (0.69.5):
48 | - FBReactNativeSpec (= 0.69.5)
49 | - RCT-Folly (= 2021.06.28.00-v2)
50 | - RCTRequired (= 0.69.5)
51 | - RCTTypeSafety (= 0.69.5)
52 | - React-Core (= 0.69.5)
53 | - React-jsi (= 0.69.5)
54 | - React-jsiexecutor (= 0.69.5)
55 | - ReactCommon/turbomodule/core (= 0.69.5)
56 | - React-Core (0.69.5):
57 | - glog
58 | - RCT-Folly (= 2021.06.28.00-v2)
59 | - React-Core/Default (= 0.69.5)
60 | - React-cxxreact (= 0.69.5)
61 | - React-jsi (= 0.69.5)
62 | - React-jsiexecutor (= 0.69.5)
63 | - React-perflogger (= 0.69.5)
64 | - Yoga
65 | - React-Core/CoreModulesHeaders (0.69.5):
66 | - glog
67 | - RCT-Folly (= 2021.06.28.00-v2)
68 | - React-Core/Default
69 | - React-cxxreact (= 0.69.5)
70 | - React-jsi (= 0.69.5)
71 | - React-jsiexecutor (= 0.69.5)
72 | - React-perflogger (= 0.69.5)
73 | - Yoga
74 | - React-Core/Default (0.69.5):
75 | - glog
76 | - RCT-Folly (= 2021.06.28.00-v2)
77 | - React-cxxreact (= 0.69.5)
78 | - React-jsi (= 0.69.5)
79 | - React-jsiexecutor (= 0.69.5)
80 | - React-perflogger (= 0.69.5)
81 | - Yoga
82 | - React-Core/DevSupport (0.69.5):
83 | - glog
84 | - RCT-Folly (= 2021.06.28.00-v2)
85 | - React-Core/Default (= 0.69.5)
86 | - React-Core/RCTWebSocket (= 0.69.5)
87 | - React-cxxreact (= 0.69.5)
88 | - React-jsi (= 0.69.5)
89 | - React-jsiexecutor (= 0.69.5)
90 | - React-jsinspector (= 0.69.5)
91 | - React-perflogger (= 0.69.5)
92 | - Yoga
93 | - React-Core/RCTActionSheetHeaders (0.69.5):
94 | - glog
95 | - RCT-Folly (= 2021.06.28.00-v2)
96 | - React-Core/Default
97 | - React-cxxreact (= 0.69.5)
98 | - React-jsi (= 0.69.5)
99 | - React-jsiexecutor (= 0.69.5)
100 | - React-perflogger (= 0.69.5)
101 | - Yoga
102 | - React-Core/RCTAnimationHeaders (0.69.5):
103 | - glog
104 | - RCT-Folly (= 2021.06.28.00-v2)
105 | - React-Core/Default
106 | - React-cxxreact (= 0.69.5)
107 | - React-jsi (= 0.69.5)
108 | - React-jsiexecutor (= 0.69.5)
109 | - React-perflogger (= 0.69.5)
110 | - Yoga
111 | - React-Core/RCTBlobHeaders (0.69.5):
112 | - glog
113 | - RCT-Folly (= 2021.06.28.00-v2)
114 | - React-Core/Default
115 | - React-cxxreact (= 0.69.5)
116 | - React-jsi (= 0.69.5)
117 | - React-jsiexecutor (= 0.69.5)
118 | - React-perflogger (= 0.69.5)
119 | - Yoga
120 | - React-Core/RCTImageHeaders (0.69.5):
121 | - glog
122 | - RCT-Folly (= 2021.06.28.00-v2)
123 | - React-Core/Default
124 | - React-cxxreact (= 0.69.5)
125 | - React-jsi (= 0.69.5)
126 | - React-jsiexecutor (= 0.69.5)
127 | - React-perflogger (= 0.69.5)
128 | - Yoga
129 | - React-Core/RCTLinkingHeaders (0.69.5):
130 | - glog
131 | - RCT-Folly (= 2021.06.28.00-v2)
132 | - React-Core/Default
133 | - React-cxxreact (= 0.69.5)
134 | - React-jsi (= 0.69.5)
135 | - React-jsiexecutor (= 0.69.5)
136 | - React-perflogger (= 0.69.5)
137 | - Yoga
138 | - React-Core/RCTNetworkHeaders (0.69.5):
139 | - glog
140 | - RCT-Folly (= 2021.06.28.00-v2)
141 | - React-Core/Default
142 | - React-cxxreact (= 0.69.5)
143 | - React-jsi (= 0.69.5)
144 | - React-jsiexecutor (= 0.69.5)
145 | - React-perflogger (= 0.69.5)
146 | - Yoga
147 | - React-Core/RCTSettingsHeaders (0.69.5):
148 | - glog
149 | - RCT-Folly (= 2021.06.28.00-v2)
150 | - React-Core/Default
151 | - React-cxxreact (= 0.69.5)
152 | - React-jsi (= 0.69.5)
153 | - React-jsiexecutor (= 0.69.5)
154 | - React-perflogger (= 0.69.5)
155 | - Yoga
156 | - React-Core/RCTTextHeaders (0.69.5):
157 | - glog
158 | - RCT-Folly (= 2021.06.28.00-v2)
159 | - React-Core/Default
160 | - React-cxxreact (= 0.69.5)
161 | - React-jsi (= 0.69.5)
162 | - React-jsiexecutor (= 0.69.5)
163 | - React-perflogger (= 0.69.5)
164 | - Yoga
165 | - React-Core/RCTVibrationHeaders (0.69.5):
166 | - glog
167 | - RCT-Folly (= 2021.06.28.00-v2)
168 | - React-Core/Default
169 | - React-cxxreact (= 0.69.5)
170 | - React-jsi (= 0.69.5)
171 | - React-jsiexecutor (= 0.69.5)
172 | - React-perflogger (= 0.69.5)
173 | - Yoga
174 | - React-Core/RCTWebSocket (0.69.5):
175 | - glog
176 | - RCT-Folly (= 2021.06.28.00-v2)
177 | - React-Core/Default (= 0.69.5)
178 | - React-cxxreact (= 0.69.5)
179 | - React-jsi (= 0.69.5)
180 | - React-jsiexecutor (= 0.69.5)
181 | - React-perflogger (= 0.69.5)
182 | - Yoga
183 | - React-CoreModules (0.69.5):
184 | - RCT-Folly (= 2021.06.28.00-v2)
185 | - RCTTypeSafety (= 0.69.5)
186 | - React-Codegen (= 0.69.5)
187 | - React-Core/CoreModulesHeaders (= 0.69.5)
188 | - React-jsi (= 0.69.5)
189 | - React-RCTImage (= 0.69.5)
190 | - ReactCommon/turbomodule/core (= 0.69.5)
191 | - React-cxxreact (0.69.5):
192 | - boost (= 1.76.0)
193 | - DoubleConversion
194 | - glog
195 | - RCT-Folly (= 2021.06.28.00-v2)
196 | - React-callinvoker (= 0.69.5)
197 | - React-jsi (= 0.69.5)
198 | - React-jsinspector (= 0.69.5)
199 | - React-logger (= 0.69.5)
200 | - React-perflogger (= 0.69.5)
201 | - React-runtimeexecutor (= 0.69.5)
202 | - React-jsi (0.69.5):
203 | - boost (= 1.76.0)
204 | - DoubleConversion
205 | - glog
206 | - RCT-Folly (= 2021.06.28.00-v2)
207 | - React-jsi/Default (= 0.69.5)
208 | - React-jsi/Default (0.69.5):
209 | - boost (= 1.76.0)
210 | - DoubleConversion
211 | - glog
212 | - RCT-Folly (= 2021.06.28.00-v2)
213 | - React-jsiexecutor (0.69.5):
214 | - DoubleConversion
215 | - glog
216 | - RCT-Folly (= 2021.06.28.00-v2)
217 | - React-cxxreact (= 0.69.5)
218 | - React-jsi (= 0.69.5)
219 | - React-perflogger (= 0.69.5)
220 | - React-jsinspector (0.69.5)
221 | - React-logger (0.69.5):
222 | - glog
223 | - React-perflogger (0.69.5)
224 | - React-RCTActionSheet (0.69.5):
225 | - React-Core/RCTActionSheetHeaders (= 0.69.5)
226 | - React-RCTAnimation (0.69.5):
227 | - RCT-Folly (= 2021.06.28.00-v2)
228 | - RCTTypeSafety (= 0.69.5)
229 | - React-Codegen (= 0.69.5)
230 | - React-Core/RCTAnimationHeaders (= 0.69.5)
231 | - React-jsi (= 0.69.5)
232 | - ReactCommon/turbomodule/core (= 0.69.5)
233 | - React-RCTBlob (0.69.5):
234 | - RCT-Folly (= 2021.06.28.00-v2)
235 | - React-Codegen (= 0.69.5)
236 | - React-Core/RCTBlobHeaders (= 0.69.5)
237 | - React-Core/RCTWebSocket (= 0.69.5)
238 | - React-jsi (= 0.69.5)
239 | - React-RCTNetwork (= 0.69.5)
240 | - ReactCommon/turbomodule/core (= 0.69.5)
241 | - React-RCTImage (0.69.5):
242 | - RCT-Folly (= 2021.06.28.00-v2)
243 | - RCTTypeSafety (= 0.69.5)
244 | - React-Codegen (= 0.69.5)
245 | - React-Core/RCTImageHeaders (= 0.69.5)
246 | - React-jsi (= 0.69.5)
247 | - React-RCTNetwork (= 0.69.5)
248 | - ReactCommon/turbomodule/core (= 0.69.5)
249 | - React-RCTLinking (0.69.5):
250 | - React-Codegen (= 0.69.5)
251 | - React-Core/RCTLinkingHeaders (= 0.69.5)
252 | - React-jsi (= 0.69.5)
253 | - ReactCommon/turbomodule/core (= 0.69.5)
254 | - React-RCTNetwork (0.69.5):
255 | - RCT-Folly (= 2021.06.28.00-v2)
256 | - RCTTypeSafety (= 0.69.5)
257 | - React-Codegen (= 0.69.5)
258 | - React-Core/RCTNetworkHeaders (= 0.69.5)
259 | - React-jsi (= 0.69.5)
260 | - ReactCommon/turbomodule/core (= 0.69.5)
261 | - React-RCTSettings (0.69.5):
262 | - RCT-Folly (= 2021.06.28.00-v2)
263 | - RCTTypeSafety (= 0.69.5)
264 | - React-Codegen (= 0.69.5)
265 | - React-Core/RCTSettingsHeaders (= 0.69.5)
266 | - React-jsi (= 0.69.5)
267 | - ReactCommon/turbomodule/core (= 0.69.5)
268 | - React-RCTText (0.69.5):
269 | - React-Core/RCTTextHeaders (= 0.69.5)
270 | - React-RCTVibration (0.69.5):
271 | - RCT-Folly (= 2021.06.28.00-v2)
272 | - React-Codegen (= 0.69.5)
273 | - React-Core/RCTVibrationHeaders (= 0.69.5)
274 | - React-jsi (= 0.69.5)
275 | - ReactCommon/turbomodule/core (= 0.69.5)
276 | - React-runtimeexecutor (0.69.5):
277 | - React-jsi (= 0.69.5)
278 | - ReactCommon/turbomodule/core (0.69.5):
279 | - DoubleConversion
280 | - glog
281 | - RCT-Folly (= 2021.06.28.00-v2)
282 | - React-bridging (= 0.69.5)
283 | - React-callinvoker (= 0.69.5)
284 | - React-Core (= 0.69.5)
285 | - React-cxxreact (= 0.69.5)
286 | - React-jsi (= 0.69.5)
287 | - React-logger (= 0.69.5)
288 | - React-perflogger (= 0.69.5)
289 | - RNGestureHandler (2.9.0):
290 | - React-Core
291 | - RNReanimated (2.14.4):
292 | - DoubleConversion
293 | - FBLazyVector
294 | - FBReactNativeSpec
295 | - glog
296 | - RCT-Folly
297 | - RCTRequired
298 | - RCTTypeSafety
299 | - React-callinvoker
300 | - React-Core
301 | - React-Core/DevSupport
302 | - React-Core/RCTWebSocket
303 | - React-CoreModules
304 | - React-cxxreact
305 | - React-jsi
306 | - React-jsiexecutor
307 | - React-jsinspector
308 | - React-RCTActionSheet
309 | - React-RCTAnimation
310 | - React-RCTBlob
311 | - React-RCTImage
312 | - React-RCTLinking
313 | - React-RCTNetwork
314 | - React-RCTSettings
315 | - React-RCTText
316 | - ReactCommon/turbomodule/core
317 | - Yoga
318 | - Yoga (1.14.0)
319 |
320 | DEPENDENCIES:
321 | - boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`)
322 | - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
323 | - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
324 | - FBReactNativeSpec (from `../node_modules/react-native/React/FBReactNativeSpec`)
325 | - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
326 | - RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
327 | - RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`)
328 | - RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`)
329 | - React (from `../node_modules/react-native/`)
330 | - React-bridging (from `../node_modules/react-native/ReactCommon`)
331 | - React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`)
332 | - React-Codegen (from `build/generated/ios`)
333 | - React-Core (from `../node_modules/react-native/`)
334 | - React-Core/RCTWebSocket (from `../node_modules/react-native/`)
335 | - React-CoreModules (from `../node_modules/react-native/React/CoreModules`)
336 | - React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`)
337 | - React-jsi (from `../node_modules/react-native/ReactCommon/jsi`)
338 | - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
339 | - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
340 | - React-logger (from `../node_modules/react-native/ReactCommon/logger`)
341 | - React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
342 | - React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
343 | - React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`)
344 | - React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`)
345 | - React-RCTImage (from `../node_modules/react-native/Libraries/Image`)
346 | - React-RCTLinking (from `../node_modules/react-native/Libraries/LinkingIOS`)
347 | - React-RCTNetwork (from `../node_modules/react-native/Libraries/Network`)
348 | - React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`)
349 | - React-RCTText (from `../node_modules/react-native/Libraries/Text`)
350 | - React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`)
351 | - React-runtimeexecutor (from `../node_modules/react-native/ReactCommon/runtimeexecutor`)
352 | - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
353 | - RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
354 | - RNReanimated (from `../node_modules/react-native-reanimated`)
355 | - Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
356 |
357 | SPEC REPOS:
358 | trunk:
359 | - fmt
360 |
361 | EXTERNAL SOURCES:
362 | boost:
363 | :podspec: "../node_modules/react-native/third-party-podspecs/boost.podspec"
364 | DoubleConversion:
365 | :podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
366 | FBLazyVector:
367 | :path: "../node_modules/react-native/Libraries/FBLazyVector"
368 | FBReactNativeSpec:
369 | :path: "../node_modules/react-native/React/FBReactNativeSpec"
370 | glog:
371 | :podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec"
372 | RCT-Folly:
373 | :podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec"
374 | RCTRequired:
375 | :path: "../node_modules/react-native/Libraries/RCTRequired"
376 | RCTTypeSafety:
377 | :path: "../node_modules/react-native/Libraries/TypeSafety"
378 | React:
379 | :path: "../node_modules/react-native/"
380 | React-bridging:
381 | :path: "../node_modules/react-native/ReactCommon"
382 | React-callinvoker:
383 | :path: "../node_modules/react-native/ReactCommon/callinvoker"
384 | React-Codegen:
385 | :path: build/generated/ios
386 | React-Core:
387 | :path: "../node_modules/react-native/"
388 | React-CoreModules:
389 | :path: "../node_modules/react-native/React/CoreModules"
390 | React-cxxreact:
391 | :path: "../node_modules/react-native/ReactCommon/cxxreact"
392 | React-jsi:
393 | :path: "../node_modules/react-native/ReactCommon/jsi"
394 | React-jsiexecutor:
395 | :path: "../node_modules/react-native/ReactCommon/jsiexecutor"
396 | React-jsinspector:
397 | :path: "../node_modules/react-native/ReactCommon/jsinspector"
398 | React-logger:
399 | :path: "../node_modules/react-native/ReactCommon/logger"
400 | React-perflogger:
401 | :path: "../node_modules/react-native/ReactCommon/reactperflogger"
402 | React-RCTActionSheet:
403 | :path: "../node_modules/react-native/Libraries/ActionSheetIOS"
404 | React-RCTAnimation:
405 | :path: "../node_modules/react-native/Libraries/NativeAnimation"
406 | React-RCTBlob:
407 | :path: "../node_modules/react-native/Libraries/Blob"
408 | React-RCTImage:
409 | :path: "../node_modules/react-native/Libraries/Image"
410 | React-RCTLinking:
411 | :path: "../node_modules/react-native/Libraries/LinkingIOS"
412 | React-RCTNetwork:
413 | :path: "../node_modules/react-native/Libraries/Network"
414 | React-RCTSettings:
415 | :path: "../node_modules/react-native/Libraries/Settings"
416 | React-RCTText:
417 | :path: "../node_modules/react-native/Libraries/Text"
418 | React-RCTVibration:
419 | :path: "../node_modules/react-native/Libraries/Vibration"
420 | React-runtimeexecutor:
421 | :path: "../node_modules/react-native/ReactCommon/runtimeexecutor"
422 | ReactCommon:
423 | :path: "../node_modules/react-native/ReactCommon"
424 | RNGestureHandler:
425 | :path: "../node_modules/react-native-gesture-handler"
426 | RNReanimated:
427 | :path: "../node_modules/react-native-reanimated"
428 | Yoga:
429 | :path: "../node_modules/react-native/ReactCommon/yoga"
430 |
431 | SPEC CHECKSUMS:
432 | boost: a7c83b31436843459a1961bfd74b96033dc77234
433 | DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
434 | FBLazyVector: 0045cf98ca4a48af3bf7108d85b1c243740fa289
435 | FBReactNativeSpec: 82e74141263f8c962e288f5cd6b5d149cdc8afe1
436 | fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
437 | glog: 3d02b25ca00c2d456734d0bcff864cbc62f6ae1a
438 | RCT-Folly: b9d9fe1fc70114b751c076104e52f3b1b5e5a95a
439 | RCTRequired: 85c60c4bde8241278be2c93420de4c65475a2151
440 | RCTTypeSafety: 15990f289215eb0fc65c5eb6e2610faeeda8d5e1
441 | React: 6cfa9367042a85f6235740420df017d51efc6494
442 | React-bridging: bf49ea3fa02446c647748d33cc9cbc0f5509bba7
443 | React-callinvoker: 6b98a94d1f5063afe211379d061b01f40707394a
444 | React-Codegen: 2fe0ade7442acce0b729a228a2d9111b6ef294e2
445 | React-Core: ad82eacbe769f918b0d199df3cb7c780cd3f46ff
446 | React-CoreModules: 72b07fed89ab0e7f2600f9275ec9642130aa920c
447 | React-cxxreact: 2bba16be9eb4116bee86e3dfd85aeb67b2795eca
448 | React-jsi: 013de11039e08ae5d67868a72f1012794d34e72f
449 | React-jsiexecutor: e42f0b46de293a026c2fb20e524d4fe09f81f575
450 | React-jsinspector: e385fb7a1440ae3f3b2cd1a139ca5aadaab43c10
451 | React-logger: 15c734997c06fe9c9b88e528fb7757601e7a56df
452 | React-perflogger: 367418425c5e4a9f0f80385ee1eaacd2a7348f8e
453 | React-RCTActionSheet: e4885e7136f98ded1137cd3daccc05eaed97d5a6
454 | React-RCTAnimation: 7c5a74f301c9b763343ba98a3dd776ed2676993f
455 | React-RCTBlob: 5c294e0415b290b1b3b72ec454c43e3afcfab444
456 | React-RCTImage: e82034ab64dfbadd3e0b42d830a810702f59f758
457 | React-RCTLinking: f007e2b4094e1fd364f3bde8bbd94113d4e1e70f
458 | React-RCTNetwork: 72eaf2f4cbcb5105b2ef4ac6a987b51047d8835f
459 | React-RCTSettings: 61949292107ca7b6cf9601679e952b1b5a3546a7
460 | React-RCTText: 307181243987b73aaefc22afd0b57b10ef970429
461 | React-RCTVibration: 42b34fde72e42446d9b08d2b9a3ddc2fa9ac6189
462 | React-runtimeexecutor: c778439c3c430a5719d027d3c67423b390a221fe
463 | ReactCommon: ab1003b81be740fecd82509c370a45b1a7dda0c1
464 | RNGestureHandler: 071d7a9ad81e8b83fe7663b303d132406a7d8f39
465 | RNReanimated: ba021107f1dd90cb04c35c5d639392d5da27d065
466 | Yoga: c2b1f2494060865ac1f27e49639e72371b1205fa
467 |
468 | PODFILE CHECKSUM: 6b7c35fae4af5690c34d29ff4881225f346fbf51
469 |
470 | COCOAPODS: 1.11.3
471 |
--------------------------------------------------------------------------------