Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: bump native dependencies to support Rive Events #203

Merged
merged 3 commits into from
Sep 21, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ dependencies {
implementation 'androidx.core:core-ktx:1.10.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1'
implementation 'app.rive:rive-android:8.2.1'
implementation 'app.rive:rive-android:8.4.0'
implementation "androidx.startup:startup-runtime:1.1.1"
implementation 'com.android.volley:volley:1.2.0'
}
56 changes: 53 additions & 3 deletions android/src/main/java/com/rivereactnative/RiveReactNativeView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import android.widget.FrameLayout
import app.rive.runtime.kotlin.PointerEvents
import app.rive.runtime.kotlin.RiveAnimationView
import app.rive.runtime.kotlin.controllers.RiveFileController
import app.rive.runtime.kotlin.RiveArtboardRenderer
import app.rive.runtime.kotlin.core.*
import app.rive.runtime.kotlin.core.errors.*
import com.android.volley.NetworkResponse
Expand All @@ -13,14 +12,14 @@ import com.android.volley.Request
import com.android.volley.Response
import com.android.volley.toolbox.HttpHeaderParser
import com.android.volley.toolbox.Volley
import com.facebook.react.uimanager.events.RCTEventEmitter
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.WritableMap
import com.facebook.react.bridge.ReactContext
import com.facebook.react.bridge.ReadableArray
import com.facebook.react.modules.core.ExceptionsManagerModule
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.events.RCTEventEmitter
import java.io.UnsupportedEncodingException
import kotlin.IllegalStateException


class RiveReactNativeView(private val context: ThemedReactContext) : FrameLayout(context) {
Expand All @@ -43,6 +42,7 @@ class RiveReactNativeView(private val context: ThemedReactContext) : FrameLayout
STOP("onStop"),
LOOP_END("onLoopEnd"),
STATE_CHANGED("onStateChanged"),
RIVE_EVENT("onRiveEventReceived"),
ERROR("onError");

override fun toString(): String {
Expand Down Expand Up @@ -92,7 +92,18 @@ class RiveReactNativeView(private val context: ThemedReactContext) : FrameLayout
}

}

val eventListener = object : RiveFileController.RiveEventListener {
override fun notifyEvent(event: RiveEvent) {
when (event) {
is RiveGeneralEvent -> onRiveEventReceived(event as RiveGeneralEvent)
is RiveOpenURLEvent -> onRiveEventReceived(event as RiveOpenURLEvent)
}
}
}

riveAnimationView.registerListener(listener)
riveAnimationView.addEventListener(eventListener)
autoplay = false
addView(riveAnimationView)
}
Expand Down Expand Up @@ -146,6 +157,45 @@ class RiveReactNativeView(private val context: ThemedReactContext) : FrameLayout
reactContext.getJSModule(RCTEventEmitter::class.java).receiveEvent(id, Events.STATE_CHANGED.toString(), data)
}

private fun convertHashMapToWritableMap(hashMap: HashMap<String, Any>): WritableMap {
val writableMap = Arguments.createMap()

for ((key, value) in hashMap) {
when (value) {
is String -> writableMap.putString(key, value)
is Int -> writableMap.putInt(key, value)
is Float -> writableMap.putDouble(key, value.toDouble())
is Double -> writableMap.putDouble(key, value)
is Boolean -> writableMap.putBoolean(key, value)
}
}

return writableMap
}

fun onRiveEventReceived(event: RiveEvent) {
val reactContext = context as ReactContext
val topLevelDict = Arguments.createMap()

val eventProperties = Arguments.createMap().apply {
putString("name", event.name)
putDouble("delay", event.delay.toDouble())
putMap("properties", convertHashMapToWritableMap(event.properties))
}

if (event is RiveOpenURLEvent) {
eventProperties.putString("url", event.url)
eventProperties.putString("target", event.target)
}

topLevelDict.putMap(
"riveEvent",
eventProperties
)

reactContext.getJSModule(RCTEventEmitter::class.java).receiveEvent(id, Events.RIVE_EVENT.toString(), topLevelDict)
}

fun play(animationName: String, rnLoopMode: RNLoopMode, rnDirection: RNDirection, isStateMachine: Boolean) {
val loop = RNLoopMode.mapToRiveLoop(rnLoopMode)
val direction = RNDirection.mapToRiveDirection(rnDirection)
Expand Down
Binary file added example/android/app/src/main/res/raw/rating.riv
Binary file not shown.
Binary file added example/ios/Assets/rating.riv
Binary file not shown.
16 changes: 8 additions & 8 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -489,8 +489,8 @@ PODS:
- React-perflogger (= 0.72.3)
- rive-react-native (6.0.4):
- React-Core
- RiveRuntime (= 5.1.6)
- RiveRuntime (5.1.6)
- RiveRuntime (= 5.3.1)
- RiveRuntime (5.3.1)
- RNCMaskedView (0.2.9):
- React-Core
- RNCPicker (1.16.8):
Expand Down Expand Up @@ -693,9 +693,9 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/yoga"

SPEC CHECKSUMS:
boost: a7c83b31436843459a1961bfd74b96033dc77234
boost: 57d2868c099736d80fcd648bf211b4431e51a558
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
DoubleConversion: 831926d9b8bf8166fd87886c4abab286c2422662
DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
FBLazyVector: 4cce221dd782d3ff7c4172167bba09d58af67ccb
FBReactNativeSpec: c6bd9e179757b3c0ecf815864fae8032377903ef
Flipper: 6edb735e6c3e332975d1b17956bcc584eccf5818
Expand All @@ -707,7 +707,7 @@ SPEC CHECKSUMS:
Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9
FlipperKit: 2efad7007d6745a3f95e4034d547be637f89d3f6
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
glog: 5337263514dd6f09803962437687240c5dc39aa4
glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b
hermes-engine: 10fbd3f62405c41ea07e71973ea61e1878d07322
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c
Expand Down Expand Up @@ -744,8 +744,8 @@ SPEC CHECKSUMS:
React-runtimescheduler: 837c1bebd2f84572db17698cd702ceaf585b0d9a
React-utils: bcb57da67eec2711f8b353f6e3d33bd8e4b2efa3
ReactCommon: 3ccb8fb14e6b3277e38c73b0ff5e4a1b8db017a9
rive-react-native: 2bbbdb03edbb7f1f9ffda06bf7bcd0a2f1c2a4d5
RiveRuntime: b019064a8668431b80389f83b5a2eb012e034c01
rive-react-native: a932751960b201581fd0dd91371ac400991e6201
RiveRuntime: 63fe0407ab96ab1796c492d1e584a3b766e80693
RNCMaskedView: 949696f25ec596bfc697fc88e6f95cf0c79669b6
RNCPicker: 0991c56da7815c0cf946d6f63cf920b25296e5f6
RNGestureHandler: a479ebd5ed4221a810967000735517df0d2db211
Expand All @@ -757,4 +757,4 @@ SPEC CHECKSUMS:

PODFILE CHECKSUM: 5ef3edaac0021600a1e0462aa4c2c8c364c536b3

COCOAPODS: 1.11.3
COCOAPODS: 1.11.2
4 changes: 4 additions & 0 deletions example/ios/RiveReactNativeExample.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
E5637D7A292BD27F000CBC1E /* skills_listener.riv in Resources */ = {isa = PBXBuildFile; fileRef = E5637D79292BD26D000CBC1E /* skills_listener.riv */; };
E5A17A90299AA0F5008CC433 /* avatars.riv in Resources */ = {isa = PBXBuildFile; fileRef = E5A17A8F299AA0F5008CC433 /* avatars.riv */; };
E5F3FC4029B2661500D6D265 /* switch.riv in Resources */ = {isa = PBXBuildFile; fileRef = E5F3FC3F29B2661500D6D265 /* switch.riv */; };
E5FC4EAA2ABB975100D98158 /* rating.riv in Resources */ = {isa = PBXBuildFile; fileRef = E5FC4EA92ABB975100D98158 /* rating.riv */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -95,6 +96,7 @@
E5637D79292BD26D000CBC1E /* skills_listener.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = skills_listener.riv; sourceTree = "<group>"; };
E5A17A8F299AA0F5008CC433 /* avatars.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = avatars.riv; sourceTree = "<group>"; };
E5F3FC3F29B2661500D6D265 /* switch.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = switch.riv; sourceTree = "<group>"; };
E5FC4EA92ABB975100D98158 /* rating.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = rating.riv; sourceTree = "<group>"; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
ED2971642150620600B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.0.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; };
/* End PBXFileReference section */
Expand Down Expand Up @@ -250,6 +252,7 @@
9DBF1CC42684937E0008391A /* v6_file.riv */,
E5F3FC3F29B2661500D6D265 /* switch.riv */,
9D879D0C265BF2A400D01424 /* ui_swipe_left_to_delete.riv */,
E5FC4EA92ABB975100D98158 /* rating.riv */,
E5A17A8F299AA0F5008CC433 /* avatars.riv */,
E5637D79292BD26D000CBC1E /* skills_listener.riv */,
9D879D0A26578A5E00D01424 /* artboard_animations.riv */,
Expand Down Expand Up @@ -400,6 +403,7 @@
buildActionMask = 2147483647;
files = (
9D879D00265642BA00D01424 /* truck_v7.riv in Resources */,
E5FC4EAA2ABB975100D98158 /* rating.riv in Resources */,
042FD22726B81BD1004556A3 /* constrained.riv in Resources */,
9D879D0B26578A5E00D01424 /* artboard_animations.riv in Resources */,
E5637D7A292BD27F000CBC1E /* skills_listener.riv in Resources */,
Expand Down
2 changes: 2 additions & 0 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import ErrorNotHandled from './ErrorNotHandled';
import ErrorHandledManually from './ErrorHandledManually';
import MeshExample from './MeshExample';
import DynamicText from './DynamicText';
import Events from './Events';

const Stack = createStackNavigator();

Expand All @@ -33,6 +34,7 @@ export default function App() {
<Stack.Screen name="Http" component={Http} />
<Stack.Screen name="MeshExample" component={MeshExample} />
<Stack.Screen name="StateMachine" component={StateMachine} />
<Stack.Screen name="Events" component={Events} />
<Stack.Screen name="DynamicText" component={DynamicText} />
<Stack.Screen
name="MultipleArtboards"
Expand Down
56 changes: 56 additions & 0 deletions example/src/Events.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React, { useRef, useState } from 'react';
import {
SafeAreaView,
ScrollView,
StyleSheet,
Linking,
Text,
} from 'react-native';
import Rive, { Fit, RiveOpenUrlEvent, RiveRef } from 'rive-react-native';

export default function Events() {
const riveRef = useRef<RiveRef>(null);
const [eventMessage, setEventMessage] = useState('');

return (
<SafeAreaView style={styles.safeAreaViewContainer}>
<ScrollView contentContainerStyle={styles.container}>
<Rive
ref={riveRef}
autoplay={true}
fit={Fit.Cover}
style={styles.box}
stateMachineName="State Machine 1"
onRiveEventReceived={(event) => {
const eventProperties = event.properties;
if (eventProperties?.message) {
setEventMessage(eventProperties.message as string);
}
if ('url' in event) {
Linking.openURL((event as RiveOpenUrlEvent).url || '');
}
}}
resourceName={'rating'}
/>
<Text>{eventMessage}</Text>
</ScrollView>
</SafeAreaView>
);
}

const styles = StyleSheet.create({
safeAreaViewContainer: {
flex: 1,
},
container: {
flexGrow: 1,
alignItems: 'center',
justifyContent: 'center',
marginBottom: 150,
},
box: {
width: '100%',
height: 500,
marginVertical: 20,
},
});
8 changes: 8 additions & 0 deletions example/src/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@ export default function Home({ navigation }) {
State Machine
</Button>

<Button
mode="contained"
onPress={() => navigation.navigate('Events')}
style={styles.buttonStyle}
>
Events
</Button>

<Button
mode="contained"
onPress={() => navigation.navigate('DynamicText')}
Expand Down
17 changes: 17 additions & 0 deletions ios/RiveReactNativeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class RiveReactNativeView: UIView, RivePlayerDelegate, RiveStateMachineDelegate
@objc var onStop: RCTDirectEventBlock?
@objc var onLoopEnd: RCTDirectEventBlock?
@objc var onStateChanged: RCTDirectEventBlock?
@objc var onRiveEventReceived: RCTDirectEventBlock?
@objc var onError: RCTDirectEventBlock?
@objc var isUserHandlingErrors: Bool

Expand Down Expand Up @@ -242,6 +243,22 @@ class RiveReactNativeView: UIView, RivePlayerDelegate, RiveStateMachineDelegate
@objc func stateMachine(_ stateMachine: RiveStateMachineInstance, receivedInput input: StateMachineInput) {
}

@objc func onRiveEventReceived(onRiveEvent riveEvent: RiveEvent) {
// Need to convert NSObject to Dictionary so React Native can support the serialization to JS
// Might be a better way to convert NSObject -> Dictionary in the future
var eventDict = [
"name": riveEvent.name(),
"type": riveEvent.type(),
"delay": riveEvent.delay(),
"properties": riveEvent.properties(),
] as [String : Any]
if let openUrlEvent = riveEvent as? RiveOpenUrlEvent {
eventDict["url"] = openUrlEvent.url()
eventDict["target"] = openUrlEvent.target()
}
onRiveEventReceived?(["riveEvent": eventDict])
}

// MARK: - PlayerDelegate

func player(playedWithModel riveModel: RiveModel?) {
Expand Down
1 change: 1 addition & 0 deletions ios/RiveReactNativeViewManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ @interface RCT_EXTERN_MODULE(RiveReactNativeViewManager, RCTViewManager)
RCT_EXPORT_VIEW_PROPERTY(onLoopEnd, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onStateChanged, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onError, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onRiveEventReceived, RCTDirectEventBlock)

RCT_EXTERN_METHOD(play:(nonnull NSNumber *)node animationName:(nonnull NSString)animationName loop:(NSString)loopMode direction:(NSString)direction isStateMachine:(BOOL)isStateMachine)
RCT_EXTERN_METHOD(pause:(nonnull NSNumber *)node)
Expand Down
2 changes: 1 addition & 1 deletion rive-react-native.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ Pod::Spec.new do |s|
s.source_files = "ios/**/*.{h,m,mm,swift}"

s.dependency "React-Core"
s.dependency "RiveRuntime", "5.1.6"
s.dependency "RiveRuntime", "5.3.1"
end
22 changes: 22 additions & 0 deletions src/Rive.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import {
LoopMode,
RNRiveError,
ViewManagerMethod,
RiveGeneralEvent,
RiveOpenUrlEvent,
} from './types';
import { convertErrorFromNativeToRN, XOR } from './helpers';

Expand Down Expand Up @@ -53,6 +55,11 @@ type RiveProps = {
stateName: string;
}>
) => void;
onRiveEventReceived?: (
event: NativeSyntheticEvent<{
riveEvent: RiveGeneralEvent | RiveOpenUrlEvent;
}>
) => void;
onError?: (
event: NativeSyntheticEvent<{
type: string;
Expand Down Expand Up @@ -81,6 +88,7 @@ type Props = {
onStop?: (animationName: string, isStateMachine: boolean) => void;
onLoopEnd?: (animationName: string, loopMode: LoopMode) => void;
onStateChanged?: (stateMachineName: string, stateName: string) => void;
onRiveEventReceived?: (event: RiveGeneralEvent | RiveOpenUrlEvent) => void;
onError?: (rnRiveError: RNRiveError) => void;
fit?: Fit;
style?: ViewStyle;
Expand All @@ -104,6 +112,7 @@ const RiveContainer = React.forwardRef<RiveRef, Props>(
onStop,
onLoopEnd,
onStateChanged,
onRiveEventReceived,
onError,
style,
autoplay = true,
Expand Down Expand Up @@ -192,6 +201,18 @@ const RiveContainer = React.forwardRef<RiveRef, Props>(
[onStateChanged]
);

const onRiveEventReceivedHandler = useCallback(
(
event: NativeSyntheticEvent<{
riveEvent: RiveGeneralEvent | RiveOpenUrlEvent;
}>
) => {
const { riveEvent } = event.nativeEvent;
onRiveEventReceived?.(riveEvent);
},
[onRiveEventReceived]
);

const onErrorHandler = useCallback(
(event: NativeSyntheticEvent<{ type: string; message: string }>) => {
const { type, message } = event.nativeEvent;
Expand Down Expand Up @@ -362,6 +383,7 @@ const RiveContainer = React.forwardRef<RiveRef, Props>(
onStop={onStopHandler}
onLoopEnd={onLoopEndHandler}
onStateChanged={onStateChangedHandler}
onRiveEventReceived={onRiveEventReceivedHandler}
onError={onErrorHandler}
alignment={alignment}
artboardName={artboardName}
Expand Down
Loading
Loading