Skip to content

Commit ffa9af2

Browse files
alextoudicHayesGordon
authored andcommitted
feat: add source prop
1 parent 636736c commit ffa9af2

6 files changed

Lines changed: 119 additions & 4 deletions

File tree

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { SafeAreaView, StyleSheet, ScrollView, Text } from 'react-native';
2+
import Rive, { Alignment, Fit } from 'rive-react-native';
3+
4+
export default function SourceProp() {
5+
return (
6+
<SafeAreaView style={styles.safeAreaViewContainer}>
7+
<ScrollView contentContainerStyle={styles.container}>
8+
<Text>Require</Text>
9+
<Rive
10+
fit={Fit.Contain}
11+
alignment={Alignment.Center}
12+
style={styles.animation}
13+
artboardName={'Avatar 3'}
14+
autoplay={true}
15+
source={require('../../assets/rive/avatars.riv')}
16+
/>
17+
<Text>HTTP URI</Text>
18+
<Rive
19+
fit={Fit.Contain}
20+
alignment={Alignment.Center}
21+
style={styles.animation}
22+
artboardName={'Avatar 3'}
23+
autoplay={true}
24+
source={{
25+
uri: 'https://public.rive.app/community/runtime-files/2195-4346-avatar-pack-use-case.riv',
26+
}}
27+
/>
28+
<Text>Resource name URI</Text>
29+
<Rive
30+
fit={Fit.Contain}
31+
alignment={Alignment.Center}
32+
style={styles.animation}
33+
artboardName={'Avatar 3'}
34+
autoplay={true}
35+
source={{
36+
uri: 'avatars',
37+
}}
38+
/>
39+
</ScrollView>
40+
</SafeAreaView>
41+
);
42+
}
43+
44+
const styles = StyleSheet.create({
45+
safeAreaViewContainer: {
46+
flex: 1,
47+
},
48+
container: {
49+
flexGrow: 1,
50+
alignItems: 'center',
51+
justifyContent: 'center',
52+
},
53+
animation: {
54+
width: '100%',
55+
height: 300,
56+
},
57+
});

example/app/_layout.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ function RootLayoutNav() {
116116
name="(examples)/ErrorHandledManually"
117117
options={{ title: 'Error Handled Manually' }}
118118
/>
119+
<Stack.Screen
120+
name="(examples)/SourceProp"
121+
options={{ title: 'SourceProp' }}
122+
/>
119123
</Stack>
120124
</ThemeProvider>
121125
</GestureHandlerRootView>

example/app/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export default function Home() {
4848
<StyledLink href="/(examples)/ErrorHandledManually">
4949
Error Handled Manually
5050
</StyledLink>
51+
<StyledLink href="/(examples)/SourceProp">SourceProp</StyledLink>
5152
</ScrollView>
5253
);
5354
}

example/assets/rive/avatars.riv

14.4 KB
Binary file not shown.

example/metro.config.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ config.resolver.nodeModulesPaths = [
1616
path.resolve(monorepoRoot, 'node_modules'),
1717
];
1818

19+
config.resolver.assetExts.push('riv');
20+
1921
module.exports = config;
2022

2123
// const path = require('path');

src/Rive.tsx

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
import React, { useCallback, useImperativeHandle, useRef } from 'react';
1+
import React, {
2+
useCallback,
3+
useImperativeHandle,
4+
useMemo,
5+
useRef,
6+
} from 'react';
7+
// This import path isn't handled by @types/react-native
8+
// @ts-ignore
9+
import resolveAssetSource from 'react-native/Libraries/Image/resolveAssetSource';
210
import {
311
requireNativeComponent,
412
UIManager,
@@ -116,7 +124,10 @@ type Props = {
116124
stateMachineName?: string;
117125
autoplay?: boolean;
118126
children?: React.ReactNode;
119-
} & XOR<{ resourceName: string }, { url: string }>;
127+
} & XOR<
128+
XOR<{ resourceName: string }, { url: string }>,
129+
{ source: number | { uri: string } }
130+
>;
120131

121132
export const RiveViewManager = requireNativeComponent<RiveProps>(VIEW_NAME);
122133

@@ -133,19 +144,59 @@ const RiveContainer = React.forwardRef<RiveRef, Props>(
133144
onError,
134145
style,
135146
autoplay = true,
136-
resourceName,
137-
url,
147+
resourceName: resourceNameProp,
148+
url: urlProp,
138149
alignment = Alignment.Center,
139150
fit = Fit.Contain,
140151
layoutScaleFactor,
141152
artboardName,
142153
referencedAssets: referencedAssets,
143154
animationName,
155+
source,
144156
stateMachineName,
145157
testID,
146158
},
147159
ref
148160
) => {
161+
const assetID = typeof source === 'number' ? source : null;
162+
const sourceURI = typeof source === 'object' ? source.uri : null;
163+
const { resourceName, url } = useMemo(() => {
164+
if (resourceNameProp) {
165+
return { resourceName: resourceNameProp };
166+
}
167+
168+
if (urlProp) {
169+
return { url: urlProp };
170+
}
171+
172+
const assetURI = assetID ? resolveAssetSource(assetID)?.uri : sourceURI;
173+
174+
if (!assetURI) {
175+
return {};
176+
}
177+
178+
// handle http address and dev server
179+
if (assetURI.match(/https?:\/\//)) {
180+
return { url: assetURI };
181+
}
182+
183+
// handle iOS bundled asset
184+
if (assetURI.match(/file:\/\//)) {
185+
// strip resource name from file path
186+
return { resourceName: assetURI.match(/file:\/\/(.*\/)+(.*)\.riv/)[2] };
187+
}
188+
189+
// handle Android bundled asset or resource name uri
190+
return {
191+
resourceName: assetURI,
192+
};
193+
}, [assetID, sourceURI, resourceNameProp, urlProp]);
194+
if (!resourceName && !url) {
195+
throw new Error(
196+
'Invalid Rive resource. Please provide a valid resource.'
197+
);
198+
}
199+
149200
const riveRef = useRef(null);
150201

151202
const isUserHandlingErrors = onError !== undefined;

0 commit comments

Comments
 (0)