From 342d40222173d5ef50ae106332a31a32f4079f8b Mon Sep 17 00:00:00 2001 From: Kongara Meghana <154802977+meghanakongara0429@users.noreply.github.com> Date: Fri, 15 May 2026 01:15:53 +0530 Subject: [PATCH] streamoji avatars react native sdk --- App.tsx | 414 +- README.md | 89 +- dist/ExampleUsage.d.ts | 10 + dist/ExampleUsage.d.ts.map | 1 + dist/ExampleUsage.js | 100 + dist/ExampleUsage.js.map | 1 + dist/StreamojiAvatarCreator.d.ts | 13 + dist/StreamojiAvatarCreator.d.ts.map | 1 + dist/StreamojiAvatarCreator.js | 114 + dist/StreamojiAvatarCreator.js.map | 1 + dist/events/avatar-exported.event.d.ts | 9 + dist/events/avatar-exported.event.d.ts.map | 1 + dist/events/avatar-exported.event.js | 3 + dist/events/avatar-exported.event.js.map | 1 + dist/events/index.d.ts | 4 + dist/events/index.d.ts.map | 1 + dist/events/index.js | 3 + dist/events/index.js.map | 1 + dist/hooks/use-streamoji-auth.d.ts | 10 + dist/hooks/use-streamoji-auth.d.ts.map | 1 + dist/hooks/use-streamoji-auth.js | 84 + dist/hooks/use-streamoji-auth.js.map | 1 + dist/hooks/use-streamoji-url.d.ts | 3 + dist/hooks/use-streamoji-url.d.ts.map | 1 + dist/hooks/use-streamoji-url.js | 30 + dist/hooks/use-streamoji-url.js.map | 1 + dist/index.d.ts | 6 + dist/index.d.ts.map | 1 + dist/index.js | 10 + dist/index.js.map | 1 + dist/types.d.ts | 27 + dist/types.d.ts.map | 1 + dist/types.js | 3 + dist/types.js.map | 1 + package-lock.json | 23418 ++++++++++++++----- package.json | 45 +- src/ExampleUsage.tsx | 100 + src/StreamojiAvatar.tsx | 88 + src/StreamojiAvatarCreator.tsx | 137 + src/events/asset-unlocked.event.ts | 8 - src/events/avatar-exported.event.ts | 1 + src/events/index.ts | 18 +- src/events/user-authorized.event.ts | 7 - src/events/user-logged-out.event.ts | 3 - src/events/user-set.event.ts | 7 - src/events/user-updated.event.ts | 7 - src/hooks/use-2d-image-url.ts | 16 - src/hooks/use-avatar-creator-url.ts | 20 - src/hooks/use-streamoji-auth.ts | 102 + src/hooks/use-streamoji-url.ts | 32 + src/index.ts | 12 +- src/pages/avatar.tsx | 40 - src/types.ts | 57 +- tsconfig.build.json | 13 + tsconfig.json | 16 +- 55 files changed, 19175 insertions(+), 5920 deletions(-) create mode 100644 dist/ExampleUsage.d.ts create mode 100644 dist/ExampleUsage.d.ts.map create mode 100644 dist/ExampleUsage.js create mode 100644 dist/ExampleUsage.js.map create mode 100644 dist/StreamojiAvatarCreator.d.ts create mode 100644 dist/StreamojiAvatarCreator.d.ts.map create mode 100644 dist/StreamojiAvatarCreator.js create mode 100644 dist/StreamojiAvatarCreator.js.map create mode 100644 dist/events/avatar-exported.event.d.ts create mode 100644 dist/events/avatar-exported.event.d.ts.map create mode 100644 dist/events/avatar-exported.event.js create mode 100644 dist/events/avatar-exported.event.js.map create mode 100644 dist/events/index.d.ts create mode 100644 dist/events/index.d.ts.map create mode 100644 dist/events/index.js create mode 100644 dist/events/index.js.map create mode 100644 dist/hooks/use-streamoji-auth.d.ts create mode 100644 dist/hooks/use-streamoji-auth.d.ts.map create mode 100644 dist/hooks/use-streamoji-auth.js create mode 100644 dist/hooks/use-streamoji-auth.js.map create mode 100644 dist/hooks/use-streamoji-url.d.ts create mode 100644 dist/hooks/use-streamoji-url.d.ts.map create mode 100644 dist/hooks/use-streamoji-url.js create mode 100644 dist/hooks/use-streamoji-url.js.map create mode 100644 dist/index.d.ts create mode 100644 dist/index.d.ts.map create mode 100644 dist/index.js create mode 100644 dist/index.js.map create mode 100644 dist/types.d.ts create mode 100644 dist/types.d.ts.map create mode 100644 dist/types.js create mode 100644 dist/types.js.map create mode 100644 src/ExampleUsage.tsx create mode 100644 src/StreamojiAvatar.tsx create mode 100644 src/StreamojiAvatarCreator.tsx delete mode 100644 src/events/asset-unlocked.event.ts delete mode 100644 src/events/user-authorized.event.ts delete mode 100644 src/events/user-logged-out.event.ts delete mode 100644 src/events/user-set.event.ts delete mode 100644 src/events/user-updated.event.ts delete mode 100644 src/hooks/use-2d-image-url.ts delete mode 100644 src/hooks/use-avatar-creator-url.ts create mode 100644 src/hooks/use-streamoji-auth.ts create mode 100644 src/hooks/use-streamoji-url.ts delete mode 100644 src/pages/avatar.tsx create mode 100644 tsconfig.build.json diff --git a/App.tsx b/App.tsx index b561504..ea4f309 100644 --- a/App.tsx +++ b/App.tsx @@ -1,94 +1,358 @@ -import React, { useRef, useState } from 'react'; -import WebView, { WebViewMessageEvent } from 'react-native-webview'; -import { useAvatarCreatorUrl } from './src/hooks/use-avatar-creator-url'; -import { - AssetUnlockedEvent, - AvatarCreatorEvent, +import React, { useState } from 'react'; +import { StyleSheet, View, Text, TextInput, TouchableOpacity, ScrollView, SafeAreaView, Alert, Image } from 'react-native'; +import { + StreamojiAvatarCreator, + StreamojiAvatar, + useStreamojiAuth, AvatarExportedEvent, - UserAuthorizedEvent, - UserSetEvent, - UserUpdatedEvent, - UserLoggedOutEvent + StreamojiAuthParams, + StreamojiConfig, + AvatarExportedEventPayload } from './src'; -import { Alert } from 'react-native'; -import AvatarPage from './src/pages/avatar'; - -const RPM_TARGET = 'readyplayerme'; - -// Replace with your custom subdomain -const subdomain = 'demo'; export default function App() { - const webView = useRef(); - const url = useAvatarCreatorUrl(subdomain, {}); - - const [avatarId, setAvatarId] = useState(); - - const supportedEvents = { - 'v1.avatar.exported': onAvatarExported, - 'v1.user.set': onUserSet, - 'v1.user.authorized': onUserAuthorized, - 'v1.asset.unlock': onAssetUnlocked, - 'v1.user.updated': onUserUpdated, - 'v1.user.logout': onUserLoggedOut - } as Record; - - function onAvatarExported(message: AvatarExportedEvent) { - setAvatarId(message.data.avatarId); - } + const tokenEndpoint = 'https://us-central1-streamoji-265f4.cloudfunctions.net/getAuthToken'; + const [clientId, setClientId] = useState(''); + const [clientSecret, setClientSecret] = useState(''); - function onAssetUnlocked(message: AssetUnlockedEvent) { - Alert.alert(`Asset Unlocked | Asset ID = ${message.data?.assetId}`); - } + const { authToken, isFetching, error, getAuthToken } = useStreamojiAuth({ + tokenEndpoint, + headers: { + "Client-Id": clientId, + "Client-Secret": clientSecret, + } + }); - function onUserAuthorized(message: UserAuthorizedEvent) { - Alert.alert(`User Authorized | User ID = ${message.data?.id}`); - } + const [authParams, setAuthParams] = useState({ + userId: 'user_rn_123', + userName: 'RN User', + expiresIn: 3600 + }); - function onUserSet(message: UserSetEvent) { - Alert.alert(`User Set | User ID = ${message.data?.id}`); - } + const [config, setConfig] = useState({ + bodyType: 'Full', + thumbnail: true, + whiteLabelTitle: 'My Mobile App', + whiteLabelColor: '#0F766E' + }); - function onUserUpdated(message: UserUpdatedEvent) { - Alert.alert(`User Updated | User ID = ${message.data?.id}`); - } + const [exportedAvatar, setExportedAvatar] = useState(null); + const [showCreator, setShowCreator] = useState(false); - function onUserLoggedOut(message: UserLoggedOutEvent) { - Alert.alert(`User Logged Out`); - } + const handleGetToken = async () => { + const token = await getAuthToken(authParams); + if (token) { + Alert.alert('Success', 'Auth token generated successfully'); + } else if (error) { + Alert.alert('Error', error); + } + }; - function onWebViewLoaded() { - webView.current?.postMessage( - JSON.stringify({ - target: 'readyplayerme', - type: 'subscribe', - eventName: 'v1.**' - }) + const onAvatarExported = (event: AvatarExportedEvent) => { + setExportedAvatar(event.data); + setShowCreator(false); + }; + + if (showCreator && authToken) { + return ( + + + Streamoji Creator + setShowCreator(false)}> + X + + + + ); } - function onMessageReceived(message: WebViewMessageEvent) { - const data = message.nativeEvent.data; - const event = JSON.parse(data) as AvatarCreatorEvent; + return ( + + + Streamoji SDK Demo - if (event?.source !== RPM_TARGET || !event.eventName) { - return; - } + + 1. Authentication + + This demo expects your backend to create the Streamoji auth token. Keep Streamoji client secrets on the server. + + - supportedEvents[event.eventName]?.(event); - } - if (avatarId) { - return setAvatarId('')} avatarId={avatarId}>; - } + + + Client ID + + + + Client Secret + + + - return ( - + + + User ID + setAuthParams({ ...authParams, userId: text })} + /> + + + User Name + setAuthParams({ ...authParams, userName: text })} + /> + + + + + {isFetching ? 'Fetching...' : 'Generate Token'} + + + {authToken ? ( + + Token: {authToken} + + ) : null} + + + + 2. Configuration + + White Label Title + setConfig({ ...config, whiteLabelTitle: text })} + /> + + White Label Color (Hex) + setConfig({ ...config, whiteLabelColor: text })} + /> + + setShowCreator(true)} + disabled={!authToken} + > + Open Avatar Creator + + + + {exportedAvatar && ( + + 3. Result + + + + Interactive 3D Avatar (ID: {exportedAvatar.avatarId}) + + + + GLB URL: + + {exportedAvatar.url} + + + + + User ID: + {exportedAvatar.userId} + + + {exportedAvatar.thumbnailUrl && ( + + Thumbnail URL: + {exportedAvatar.thumbnailUrl} + + )} + + )} + + ); } + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: '#121212', + }, + scrollContent: { + padding: 20, + }, + title: { + fontSize: 24, + fontWeight: 'bold', + color: '#FFFFFF', + marginBottom: 20, + textAlign: 'center', + }, + header: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + padding: 15, + backgroundColor: '#1E1E1E', + }, + headerTitle: { + color: '#FFFFFF', + fontSize: 18, + fontWeight: '600', + }, + closeBtn: { + color: '#0F766E', + fontSize: 20, + fontWeight: 'bold', + padding: 5, + }, + section: { + backgroundColor: '#1E1E1E', + borderRadius: 12, + padding: 16, + marginBottom: 20, + }, + sectionTitle: { + fontSize: 18, + fontWeight: 'bold', + color: '#0F766E', + marginBottom: 15, + }, + helperText: { + color: '#D4D4D4', + fontSize: 13, + lineHeight: 20, + marginBottom: 10, + }, + label: { + color: '#AAAAAA', + fontSize: 12, + marginBottom: 4, + marginTop: 8, + }, + input: { + backgroundColor: '#2C2C2C', + color: '#FFFFFF', + borderRadius: 8, + padding: 12, + fontSize: 14, + }, + row: { + flexDirection: 'row', + justifyContent: 'space-between', + }, + button: { + backgroundColor: '#0F766E', + borderRadius: 8, + padding: 15, + alignItems: 'center', + marginTop: 20, + }, + secondaryButton: { + backgroundColor: '#03DAC6', + }, + buttonDisabled: { + opacity: 0.5, + }, + buttonText: { + color: '#000000', + fontWeight: 'bold', + fontSize: 16, + }, + tokenBox: { + backgroundColor: '#2C2C2C', + padding: 10, + borderRadius: 8, + marginTop: 15, + }, + tokenText: { + color: '#03DAC6', + fontFamily: 'monospace', + fontSize: 12, + }, + urlContainer: { + backgroundColor: '#2C2C2C', + padding: 8, + borderRadius: 6, + marginTop: 4, + borderWidth: 1, + borderColor: '#3F3F3F', + }, + urlText: { + color: '#03DAC6', + fontSize: 11, + fontFamily: 'monospace', + }, + infoRow: { + marginTop: 12, + }, + avatarPreviewContainer: { + alignItems: 'center', + justifyContent: 'center', + backgroundColor: '#2C2C2C', + borderRadius: 12, + padding: 20, + marginBottom: 10, + }, + avatarPreview: { + width: 200, + height: 200, + borderRadius: 100, + backgroundColor: '#1E1E1E', + }, + avatarIdText: { + color: '#03DAC6', + fontSize: 12, + marginTop: 10, + fontWeight: 'bold', + }, + avatarPlaceholder: { + height: 150, + alignItems: 'center', + justifyContent: 'center', + backgroundColor: '#2C2C2C', + borderRadius: 12, + marginBottom: 10, + }, + avatarPlaceholderText: { + color: '#03DAC6', + fontSize: 18, + fontWeight: 'bold', + }, + avatar3D: { + width: '100%', + height: 350, + }, +}); diff --git a/README.md b/README.md index e677e2e..54e9c9e 100644 --- a/README.md +++ b/README.md @@ -1,49 +1,70 @@ -# Ready Player Me embedded in a React Native WebView +# Streamoji Avatar SDK for React Native -This example showcases embedding [Ready Player Me](https://readyplayer.me) avatar creator as a `WebView` to React Native mobile applications and retrieving the URL to the 3D model of the avatar. +This SDK helps React Native apps launch the [Streamoji Avatar Creator](https://avatars.streamoji.com), apply white-label options, and receive exported avatar events. -The example uses the cross-platform (iOS, Android, MacOS, Windows) [react-native-webview](https://github.com/react-native-webview/react-native-webview) module to load and interact with Ready Player Me inside a WebView. +The SDK is designed for a backend-issued token flow. Your app should request a short-lived Streamoji auth token from your own server, then pass that token into the creator. Do not ship a Streamoji client secret inside a mobile app. -## Demo +## Features +- **Backend-first auth flow**: Request a session token from your backend instead of exposing secrets on-device. +- **Custom Branding**: Configure white-labeling options directly from your app. +- **Easy Integration**: High-level `StreamojiAvatarCreator` component built on top of `react-native-webview`. +- **Event Handling**: Listen for avatar export events to receive GLB and thumbnail URLs. -Try this example [live on Snack](https://snack.expo.io/@rainerwolf3d/readyplayerme-with-react-native-expo). +## Quick Start -## How to use - -This example uses [expo.io](https://expo.io) for bootstrapping the React Native project, however it should be easily adaptable for any React Native project using the [react-native-webview](https://github.com/react-native-webview/react-native-webview) package. - -1. Install [expo-cli](https://docs.expo.io/get-started/installation/). - -``` -npm install --global expo-cli +1. Install dependencies: +```bash +npm install react-native-webview ``` -2. Run the project in development mode - -``` -npm install -expo start +2. Expose a backend endpoint that returns an auth token: +```json +{ + "authToken": "streamoji-session-token" +} ``` ---- - -## About Ready Player Me +3. Generate a session token from your app: +```tsx +import { useStreamojiAuth } from '@streamoji/react-native-avatar-sdk'; -[Ready Player Me](https://readyplayer.me/developers) is a cross-game avatar platform for Unity, Unreal Engine, and all web-based stacks. +const { getAuthToken } = useStreamojiAuth({ + tokenEndpoint: 'https://your-backend.example.com/streamoji/token' +}); -We give users an interoperable personal avatar that travels with them across many virtual experiences. Developers use our plug-and-play avatar creator so they wouldn’t have to spend months on building their own. - - -### Getting Started - -Visit [docs.readyplayer.me](https://docs.readyplayer.me/) to get started with integrating Ready Player Me or explore the [examples](https://github.com/readyplayerme). - -### Community +const token = await getAuthToken({ + userId: 'user_12345', + userName: 'Jane Doe', + expiresIn: 1800 +}); +``` -Get in touch with the Ready Player Me community over at our [Forums](https://forum.readyplayer.me/). +4. Launch the creator: +```tsx +import { StreamojiAvatarCreator } from '@streamoji/react-native-avatar-sdk'; + + { + console.log('Avatar URL:', event.data.url); + }} +/> +``` -### Becoming a partner +## Token Endpoint Contract +Your backend endpoint should: +- accept the SDK request body with `userId`, `userName`, and optional Streamoji auth parameters +- attach your private Streamoji credentials server-side +- return JSON with either `authToken` or `token` -Ready Player Me is already used in [thousands of apps and games](https://readyplayer.me/partners). +## Demo Integration +The [App.tsx](/abs/c:/Example-React-Native/App.tsx:1) file in this repository is a demo app that exercises the SDK with a configurable backend token endpoint. -Want to add Ready Player Me avatars to your own app or game? Apply [here](http://readyplayer.me/become-a-partner) or let us know at [support@readyplayer.me](mailto:support@readyplayer.me). +## About Streamoji +Streamoji provides a powerful avatar creation platform that can be integrated into any application. For more information, contact support at Streamoji. diff --git a/dist/ExampleUsage.d.ts b/dist/ExampleUsage.d.ts new file mode 100644 index 0000000..4f27e0b --- /dev/null +++ b/dist/ExampleUsage.d.ts @@ -0,0 +1,10 @@ +import React from 'react'; +/** + * A simple example of how to use the Streamoji Avatar SDK. + * This component demonstrates the two-step process: + * 1. Authenticating to get a session token. + * 2. Launching the Avatar Creator. + */ +declare const StreamojiExample: () => React.JSX.Element; +export default StreamojiExample; +//# sourceMappingURL=ExampleUsage.d.ts.map \ No newline at end of file diff --git a/dist/ExampleUsage.d.ts.map b/dist/ExampleUsage.d.ts.map new file mode 100644 index 0000000..6150223 --- /dev/null +++ b/dist/ExampleUsage.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"ExampleUsage.d.ts","sourceRoot":"","sources":["../src/ExampleUsage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAIxC;;;;;GAKG;AACH,QAAA,MAAM,gBAAgB,yBAoErB,CAAC;AAqBF,eAAe,gBAAgB,CAAC"} \ No newline at end of file diff --git a/dist/ExampleUsage.js b/dist/ExampleUsage.js new file mode 100644 index 0000000..c72b4b6 --- /dev/null +++ b/dist/ExampleUsage.js @@ -0,0 +1,100 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const react_1 = __importStar(require("react")); +const react_native_1 = require("react-native"); +const index_1 = require("./index"); +/** + * A simple example of how to use the Streamoji Avatar SDK. + * This component demonstrates the two-step process: + * 1. Authenticating to get a session token. + * 2. Launching the Avatar Creator. + */ +const StreamojiExample = () => { + const { getAuthToken, isFetching, error } = (0, index_1.useStreamojiAuth)({ + tokenEndpoint: 'https://your-backend.example.com/streamoji/token' + }); + const [token, setToken] = (0, react_1.useState)(null); + const [isCreatorOpen, setIsCreatorOpen] = (0, react_1.useState)(false); + // Step 1: Get the Auth Token + const handleLogin = async () => { + const params = { + userId: 'user_12345', + userName: 'Jane Doe', + expiresIn: 3600 // Token valid for 1 hour + }; + const authToken = await getAuthToken(params); + if (authToken) { + setToken(authToken); + react_native_1.Alert.alert('Authenticated', 'Token generated successfully. You can now open the creator.'); + } + else { + react_native_1.Alert.alert('Error', error || 'Failed to generate auth token.'); + } + }; + // Step 2: Handle the exported avatar + const handleAvatarExported = (event) => { + const { url, thumbnailUrl } = event.data; + console.log('Avatar GLB URL:', url); + console.log('Thumbnail URL:', thumbnailUrl); + react_native_1.Alert.alert('Success', 'Avatar created and exported!'); + setIsCreatorOpen(false); // Close the creator + }; + return ( + {!isCreatorOpen ? ( + + + setIsCreatorOpen(true)} disabled={!token}/> + ) : ( + setIsCreatorOpen(false)}/> + + )} + ); +}; +const styles = react_native_1.StyleSheet.create({ + container: { + flex: 1, + backgroundColor: '#fff', + }, + centered: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + padding: 20, + }, + creatorContainer: { + flex: 1, + }, + creator: { + flex: 1, + }, +}); +exports.default = StreamojiExample; +//# sourceMappingURL=ExampleUsage.js.map \ No newline at end of file diff --git a/dist/ExampleUsage.js.map b/dist/ExampleUsage.js.map new file mode 100644 index 0000000..bc60f00 --- /dev/null +++ b/dist/ExampleUsage.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ExampleUsage.js","sourceRoot":"","sources":["../src/ExampleUsage.tsx"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/StreamojiAvatarCreator.d.ts b/dist/StreamojiAvatarCreator.d.ts new file mode 100644 index 0000000..f649218 --- /dev/null +++ b/dist/StreamojiAvatarCreator.d.ts @@ -0,0 +1,13 @@ +import React from 'react'; +import { StreamojiConfig } from './types'; +import { AvatarExportedEvent } from './events'; +export interface StreamojiAvatarCreatorProps { + authToken: string; + config?: StreamojiConfig; + onAvatarExported: (event: AvatarExportedEvent) => void; + onReady?: () => void; + onError?: (error: any) => void; + style?: any; +} +export declare const StreamojiAvatarCreator: React.FC; +//# sourceMappingURL=StreamojiAvatarCreator.d.ts.map \ No newline at end of file diff --git a/dist/StreamojiAvatarCreator.d.ts.map b/dist/StreamojiAvatarCreator.d.ts.map new file mode 100644 index 0000000..9db9dbe --- /dev/null +++ b/dist/StreamojiAvatarCreator.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"StreamojiAvatarCreator.d.ts","sourceRoot":"","sources":["../src/StreamojiAvatarCreator.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAInD,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAE/C,MAAM,WAAW,2BAA2B;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,gBAAgB,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,CAAC;IACvD,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAC/B,KAAK,CAAC,EAAE,GAAG,CAAC;CACb;AAID,eAAO,MAAM,sBAAsB,EAAE,KAAK,CAAC,EAAE,CAAC,2BAA2B,CAsHxE,CAAC"} \ No newline at end of file diff --git a/dist/StreamojiAvatarCreator.js b/dist/StreamojiAvatarCreator.js new file mode 100644 index 0000000..be4dcda --- /dev/null +++ b/dist/StreamojiAvatarCreator.js @@ -0,0 +1,114 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.StreamojiAvatarCreator = void 0; +const react_1 = __importStar(require("react")); +const react_native_1 = require("react-native"); +const react_native_webview_1 = __importDefault(require("react-native-webview")); +const use_streamoji_url_1 = require("./hooks/use-streamoji-url"); +const STREAMOJI_TARGET = 'streamojiavatars'; +const StreamojiAvatarCreator = ({ authToken, config, onAvatarExported, onReady, onError, style }) => { + const webViewRef = (0, react_1.useRef)(null); + const iframeRef = (0, react_1.useRef)(null); + const url = (0, use_streamoji_url_1.useStreamojiUrl)(authToken, config); + const allowedOrigin = url ? new URL(url).origin : ''; + const subscribeToCreatorEvents = (0, react_1.useCallback)(() => { + const payload = JSON.stringify({ + target: STREAMOJI_TARGET, + type: 'subscribe', + eventName: 'v1.**' + }); + if (react_native_1.Platform.OS === 'web') { + iframeRef.current?.contentWindow?.postMessage(payload, allowedOrigin || '*'); + return; + } + webViewRef.current?.postMessage(payload); + }, [allowedOrigin]); + const onWebViewLoaded = (0, react_1.useCallback)(() => { + subscribeToCreatorEvents(); + onReady?.(); + }, [onReady, subscribeToCreatorEvents]); + const onMessageReceived = (0, react_1.useCallback)((message) => { + try { + let data; + // Handle both Native WebView and Web PostMessage + if ('nativeEvent' in message && message.nativeEvent) { + data = message.nativeEvent.data; + } + else if ('data' in message) { + data = message.data; + } + if (!data) + return; + const event = typeof data === 'string' ? JSON.parse(data) : data; + if (event?.source !== STREAMOJI_TARGET) { + return; + } + if (event.eventName === 'v1.avatar.exported') { + onAvatarExported(event); + } + else if (event.eventName === 'v1.frame.ready' && react_native_1.Platform.OS === 'web') { + subscribeToCreatorEvents(); + onReady?.(); + } + } + catch (err) { + onError?.(err); + } + }, [onAvatarExported, onError, onReady, subscribeToCreatorEvents]); + // Set up message listener for Web + react_1.default.useEffect(() => { + if (react_native_1.Platform.OS === 'web') { + const listener = (event) => { + if (allowedOrigin && event.origin !== allowedOrigin) { + return; + } + onMessageReceived(event); + }; + window.addEventListener('message', listener); + return () => window.removeEventListener('message', listener); + } + }, [allowedOrigin, onMessageReceived]); + if (!url) { + return null; + } + // Web implementation using iframe + if (react_native_1.Platform.OS === 'web') { + const IFrame = 'iframe'; + return (