@robono/messaging-react-native 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -0
- package/LICENSE +130 -0
- package/README.md +165 -2
- package/SBOM.spdx.json +1127 -0
- package/app.plugin.cjs +5 -0
- package/dist/call-message.d.ts +6 -0
- package/dist/call-message.js +109 -0
- package/dist/calling.d.ts +15 -0
- package/dist/calling.js +22 -0
- package/dist/expo-calling.d.ts +6 -0
- package/dist/expo-calling.js +7 -0
- package/dist/expo-media.d.ts +19 -0
- package/dist/expo-media.js +388 -0
- package/dist/expo.d.ts +36 -0
- package/dist/expo.js +429 -0
- package/dist/icons.d.ts +7 -0
- package/dist/icons.js +10 -0
- package/dist/index.d.ts +101 -0
- package/dist/index.js +433 -0
- package/dist/media-controller.d.ts +98 -0
- package/dist/media-controller.js +257 -0
- package/dist/media-readiness.d.ts +4 -0
- package/dist/media-readiness.js +4 -0
- package/dist/media-ui.d.ts +51 -0
- package/dist/media-ui.js +42 -0
- package/dist/polls.d.ts +21 -0
- package/dist/polls.js +54 -0
- package/dist/recent-media.d.ts +21 -0
- package/dist/recent-media.js +122 -0
- package/dist/safety.d.ts +11 -0
- package/dist/safety.js +47 -0
- package/dist/use-quote-gesture.d.ts +24 -0
- package/dist/use-quote-gesture.js +127 -0
- package/dist/visual/RobonoIcon.d.ts +232 -0
- package/dist/visual/RobonoIcon.js +20 -0
- package/dist/visual/icon-nodes.d.ts +3 -0
- package/dist/visual/icon-nodes.js +2 -0
- package/dist/visual/icon-paths.d.ts +221 -0
- package/dist/visual/icon-paths.js +221 -0
- package/dist/voice-composer.d.ts +37 -0
- package/dist/voice-composer.js +20 -0
- package/licenses/robono-icons-lucide-license.txt +43 -0
- package/licenses/robono-icons-weather-attribution.txt +1 -0
- package/licenses/robono-icons-weather-license.txt +21 -0
- package/package.json +110 -4
- package/plugin/preserve-call-audio.cjs +22 -0
package/CHANGELOG.md
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
ROBONO SDK LICENSE AGREEMENT
|
|
2
|
+
Version 1.0, July 20, 2026
|
|
3
|
+
|
|
4
|
+
Copyright (c) 2026 Add to Loop LLC. All rights reserved.
|
|
5
|
+
|
|
6
|
+
This Robono SDK License Agreement (the "Agreement") is between Add to Loop LLC,
|
|
7
|
+
operating the Robono service ("Robono"), and the person or entity that downloads,
|
|
8
|
+
installs, copies, or uses the Software ("You"). By downloading, installing,
|
|
9
|
+
copying, or using the Software, You accept this Agreement. If You do not accept
|
|
10
|
+
it, do not use the Software.
|
|
11
|
+
|
|
12
|
+
1. Definitions
|
|
13
|
+
|
|
14
|
+
"Software" means this Robono software development kit, its documentation, and
|
|
15
|
+
any updates Robono provides under this Agreement.
|
|
16
|
+
|
|
17
|
+
"Robono Service" means the messaging interoperability, routing, policy,
|
|
18
|
+
conversion, delivery, and related services operated or expressly authorized by
|
|
19
|
+
Robono.
|
|
20
|
+
|
|
21
|
+
"Application" means a product or service owned or controlled by You that is
|
|
22
|
+
authorized to connect to the Robono Service.
|
|
23
|
+
|
|
24
|
+
2. Limited License
|
|
25
|
+
|
|
26
|
+
Subject to this Agreement, Robono grants You a limited, non-exclusive,
|
|
27
|
+
non-transferable, non-sublicensable, revocable license to:
|
|
28
|
+
|
|
29
|
+
(a) install, copy, and use the Software internally to develop, test, and
|
|
30
|
+
operate an Application that communicates with the Robono Service; and
|
|
31
|
+
|
|
32
|
+
(b) reproduce and distribute the portions of the Software that are necessarily
|
|
33
|
+
included in Your Application, solely in compiled, minified, bundled, or object
|
|
34
|
+
code form and solely as needed for that Application to communicate with the
|
|
35
|
+
Robono Service.
|
|
36
|
+
|
|
37
|
+
No right is granted to distribute the Software as a standalone product or SDK.
|
|
38
|
+
|
|
39
|
+
3. Restrictions
|
|
40
|
+
|
|
41
|
+
You may not, and may not permit another person to:
|
|
42
|
+
|
|
43
|
+
(a) sell, rent, lease, sublicense, publish, or redistribute the Software as a
|
|
44
|
+
standalone product, library, SDK, or service;
|
|
45
|
+
|
|
46
|
+
(b) use the Software or a modified or derivative version of it to develop,
|
|
47
|
+
provide, or support a product or service that competes with the Robono Service;
|
|
48
|
+
|
|
49
|
+
(c) use the Software to bypass Robono, avoid applicable usage controls or fees,
|
|
50
|
+
or access the Robono Service without authorization;
|
|
51
|
+
|
|
52
|
+
(d) remove or alter copyright, trademark, attribution, or proprietary notices;
|
|
53
|
+
|
|
54
|
+
(e) reverse engineer, decompile, or disassemble the Software except to the
|
|
55
|
+
limited extent applicable law expressly prohibits this restriction; or
|
|
56
|
+
|
|
57
|
+
(f) use the Software in violation of applicable law, the rights of another
|
|
58
|
+
person, or the terms governing access to the Robono Service.
|
|
59
|
+
|
|
60
|
+
4. Ownership and Reserved Rights
|
|
61
|
+
|
|
62
|
+
The Software is licensed, not sold. Robono and its licensors retain all right,
|
|
63
|
+
title, and interest in the Software and all related intellectual property.
|
|
64
|
+
Except for the limited rights expressly granted in Section 2, all rights are
|
|
65
|
+
reserved.
|
|
66
|
+
|
|
67
|
+
No patent license is granted except the minimum license, if any, necessarily
|
|
68
|
+
required to exercise the rights expressly granted in Section 2, and that
|
|
69
|
+
limited patent license applies only while the Software is used with an
|
|
70
|
+
authorized Robono Service.
|
|
71
|
+
|
|
72
|
+
5. Service Access
|
|
73
|
+
|
|
74
|
+
This Agreement does not itself grant an account, API credentials, service
|
|
75
|
+
availability, support, or a right to access the Robono Service. Service access
|
|
76
|
+
may be governed by additional terms, plans, limits, and policies. Robono may
|
|
77
|
+
suspend or terminate access to protect users, systems, legal compliance, or the
|
|
78
|
+
integrity of the Robono Service.
|
|
79
|
+
|
|
80
|
+
6. Updates and Support
|
|
81
|
+
|
|
82
|
+
Robono is not required to provide maintenance, updates, support, or continued
|
|
83
|
+
availability of any version of the Software unless separately agreed in
|
|
84
|
+
writing.
|
|
85
|
+
|
|
86
|
+
7. Term and Termination
|
|
87
|
+
|
|
88
|
+
This Agreement continues until terminated. Your rights terminate automatically
|
|
89
|
+
if You breach this Agreement. On termination, You must stop using the Software
|
|
90
|
+
and delete copies under Your control, except for copies necessarily contained
|
|
91
|
+
in previously distributed Applications. Sections 3, 4, 8, 9, 10, and 11
|
|
92
|
+
survive termination.
|
|
93
|
+
|
|
94
|
+
8. Disclaimer of Warranties
|
|
95
|
+
|
|
96
|
+
TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS" AND
|
|
97
|
+
"AS AVAILABLE," WITHOUT WARRANTIES OF ANY KIND, EXPRESS, IMPLIED, OR
|
|
98
|
+
STATUTORY, INCLUDING WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
99
|
+
PURPOSE, TITLE, AND NON-INFRINGEMENT.
|
|
100
|
+
|
|
101
|
+
9. Limitation of Liability
|
|
102
|
+
|
|
103
|
+
TO THE MAXIMUM EXTENT PERMITTED BY LAW, ROBONO WILL NOT BE LIABLE FOR INDIRECT,
|
|
104
|
+
INCIDENTAL, SPECIAL, CONSEQUENTIAL, EXEMPLARY, OR PUNITIVE DAMAGES, OR FOR LOSS
|
|
105
|
+
OF DATA, REVENUE, PROFITS, GOODWILL, OR BUSINESS OPPORTUNITY, ARISING FROM OR
|
|
106
|
+
RELATING TO THE SOFTWARE. ROBONO'S TOTAL LIABILITY RELATING TO THE SOFTWARE
|
|
107
|
+
WILL NOT EXCEED THE GREATER OF ONE HUNDRED U.S. DOLLARS (US $100) OR THE AMOUNT
|
|
108
|
+
YOU PAID ROBONO FOR THE SOFTWARE DURING THE TWELVE MONTHS BEFORE THE EVENT
|
|
109
|
+
GIVING RISE TO THE CLAIM. THESE LIMITATIONS APPLY TO THE MAXIMUM EXTENT
|
|
110
|
+
PERMITTED BY LAW.
|
|
111
|
+
|
|
112
|
+
10. Governing Law
|
|
113
|
+
|
|
114
|
+
This Agreement is governed by the laws of the State of Florida, excluding its
|
|
115
|
+
conflict-of-laws rules. The state and federal courts located in Palm Beach
|
|
116
|
+
County, Florida have exclusive jurisdiction, and each party consents to their
|
|
117
|
+
jurisdiction and venue.
|
|
118
|
+
|
|
119
|
+
11. General
|
|
120
|
+
|
|
121
|
+
You may not assign this Agreement without Robono's prior written consent.
|
|
122
|
+
Robono may assign this Agreement in connection with a reorganization, transfer
|
|
123
|
+
of intellectual property, financing, merger, acquisition, or sale of all or
|
|
124
|
+
substantially all of the relevant assets. If a provision is unenforceable, it
|
|
125
|
+
will be enforced to the maximum extent permitted and the remaining provisions
|
|
126
|
+
will remain effective. A waiver must be in writing. This Agreement is the
|
|
127
|
+
entire agreement concerning the Software unless You and Robono sign a separate
|
|
128
|
+
written agreement that expressly replaces it.
|
|
129
|
+
|
|
130
|
+
Questions about this license may be submitted at https://robono.com/contact.
|
package/README.md
CHANGED
|
@@ -1,3 +1,166 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Optional Robono messaging screens
|
|
2
2
|
|
|
3
|
-
This
|
|
3
|
+
Use a ready-made direct or group conversation screen, replace its sections, or compose the exported components into your own layout. This package is optional and adds no navigation, global audio configuration or push handlers to your app. The shared icon controls require `react-native-svg` (install it with your framework’s native dependency installer).
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import {MessagingScreen} from '@robono/messaging-react-native';
|
|
7
|
+
|
|
8
|
+
<MessagingScreen
|
|
9
|
+
client={messagingClient}
|
|
10
|
+
userId={session.userId}
|
|
11
|
+
contact={{name: contact.displayName, avatarUrl: contact.avatarUrl}}
|
|
12
|
+
active={isConversationFocused}
|
|
13
|
+
onBack={navigation.goBack}
|
|
14
|
+
onMessageOptions={message => navigation.openMessageActions(message)}
|
|
15
|
+
pickAttachment={(kind, limits) => media.pickAndUpload(kind, limits)}
|
|
16
|
+
openAttachment={message => media.openAuthorized(message.media!.assetId)}
|
|
17
|
+
/>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The client is from `@robono/messaging`; initialize it with the authenticated backend transport and open the conversation before rendering. Your existing navigation, account/contact services and private media store supply the referenced callbacks. Mount inside your safe area. Pause polling in the background and dispose the client when the user signs out. No separate Robono end-user account is needed.
|
|
21
|
+
|
|
22
|
+
Enabled message kinds come from the server. In text-only mode there are no attachment controls. Voice-only mode has no text field. `pickAttachment(kind, {maxDurationMs})` receives the configured voice limit. Stop recording at that limit on both iOS and Android and show the remaining time. Cancel without sending when recording fails. Your backend must check the actual file duration too. The recorder displays the limit, and the client rejects oversized recordings before sending. `pickAttachment` handles the device picker or recording flow, uploads privately, and returns an asset reference or `null` on cancellation. `openAttachment` checks access through your backend and uses your media viewer/player. A missing callback never sends a fabricated attachment. Connect safety callbacks to your app's existing report/block actions.
|
|
23
|
+
|
|
24
|
+
## Message presentation and camera
|
|
25
|
+
|
|
26
|
+
Text uses a send icon; `appearance.labels.send` remains its accessible label. Voice messages show a play control, waveform area and duration. Photos and videos use thumbnail bubbles, and files show a document icon, filename, type and size. Sent and received messages use the corresponding colors and alignment.
|
|
27
|
+
|
|
28
|
+
Supply `attachmentPreview(message)` to resolve `{uri?, waveform?}` after checking the current user's media access. `uri` is an image thumbnail (a poster frame for video); `waveform` contains 32 normalized amplitude values between 0 and 1. These stay in component state, not in the message payload. Without metadata the SDK shows a neutral media tile or waveform track. `openAttachment` remains responsible for authorized media access. The Expo screen supplies inline voice playback and the shared full-screen media viewer. To connect a different audio engine, supply `components.VoiceMessage` and render the exported `VoicePlayback` with its playback state and callbacks. `MediaViewer`, `ZoomablePhoto` and `MediaPlaybackControls` are also exported for hosts with their own media engines.
|
|
29
|
+
|
|
30
|
+
Photos, videos and files share an Attach menu. Set `appearance.showCamera` to offer Camera alongside them; photo messaging must also be enabled. The option defaults off for existing integrations. Camera calls `pickAttachment('image', {...limits, source: 'camera'})`, so captures follow the same photo restrictions and review/upload process. Hosts supplying their own media callback must handle that source explicitly. The Expo adapter requests camera permission only on selection and opens the device camera on iOS and Android. Configure the `expo-image-picker` plugin's `cameraPermission` explanation in your native build. Cancelling or refusing permission sends nothing.
|
|
31
|
+
|
|
32
|
+
## Reuse or replace components
|
|
33
|
+
|
|
34
|
+
The screen accepts `components={{Header, Message, List, Composer}}`. Replacements read the same client state, theme, selected reply and callbacks through `useMessagingUI()`.
|
|
35
|
+
|
|
36
|
+
For complete layout control, wrap `MessagingHeader`, `MessagingList` and `MessagingComposer` in `MessagingProvider`. Use `MessagingBubble` for the standard message view. Supply only the components you want. The headless client works without this package.
|
|
37
|
+
|
|
38
|
+
Appearance comes from the selected integration's Messaging page, with an optional per-screen `appearance` override. The service still enforces message permissions even when your UI is fully custom. A local appearance override cannot change permissions.
|
|
39
|
+
|
|
40
|
+
The same React Native components support iOS and Android. Keyboard handling uses iOS padding and Android's host window configuration. Your host app owns microphone/library permissions, media audio focus, foreground notification behavior and navigation. Report component inventory through `client.reportUsage`; it is distinct from accepted-message usage.
|
|
41
|
+
|
|
42
|
+
## Recording and attachments with Expo
|
|
43
|
+
|
|
44
|
+
The optional `@robono/messaging-react-native/expo` entry supplies recording, review, voice playback and seeking, photo/video picking and preview, document selection, and private upload/download progress. Users explicitly send after review. A failed upload can retry with the same upload identity; cancelling removes the adapter's cache copy, never the original library item. Viewer copies are removed when the viewer closes. Managed voice and video play directly from private storage using native streaming. By default, `ExpoMessagingScreen` preloads up to six recent photos in the open conversation (2 MiB per file, 6 MiB total, two background transfers at a time). Custom adapters without streaming can also preload small voice/video attachments within those limits. Each transfer uses the authenticated storage adapter and counts as normal transfer usage. The cache is cleared when the app backgrounds, the conversation changes, access fails, or messages disappear; cached files require a fresh download on opening if their message has not been refreshed from the conversation history within 60 seconds. Voice/video streams and document downloads start only on tap. Set `preloadRecentMedia={false}` to keep all media transfers on demand. The recorder reserves 100 milliseconds at the duration limit for encoder padding; storage completion verifies the finished file. Recording stops when the app backgrounds or a call takes audio priority, leaving a review rather than sending automatically.
|
|
45
|
+
|
|
46
|
+
Install native dependencies with your Expo project's installer so their versions match your SDK:
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
npx expo install expo-audio expo-image-picker expo-document-picker expo-file-system expo-sharing expo-video expo-crypto react-native-safe-area-context react-native-svg
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
This entry targets Expo SDK 55–56; the core components do not require Expo. Configure the `expo-audio` plugin's `microphonePermission` with your app's explanation (for example, “Allow microphone access to record voice messages”). Keep background recording disabled. Native dependency or permission changes need a new development/store build; screen changes do not. The standard system photo picker does not require full photo-library access. Documents use the platform's file picker, without broad storage access.
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
import {
|
|
56
|
+
ExpoMessagingScreen, createExpoMessagingStorage,
|
|
57
|
+
createExpoMessagingAudioSession,
|
|
58
|
+
} from '@robono/messaging-react-native/expo';
|
|
59
|
+
|
|
60
|
+
const storage = createExpoMessagingStorage({
|
|
61
|
+
url: appConfig.messagingMediaEndpoint,
|
|
62
|
+
getAccessToken: () => sessions.accessToken(),
|
|
63
|
+
});
|
|
64
|
+
const audioSession = createExpoMessagingAudioSession({
|
|
65
|
+
isCallActive: () => calls.isActive(),
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
<ExpoMessagingScreen
|
|
69
|
+
client={messagingClient}
|
|
70
|
+
userId={session.userId}
|
|
71
|
+
contact={{name: contact.displayName, avatarUrl: contact.avatarUrl}}
|
|
72
|
+
active={isConversationFocused}
|
|
73
|
+
onBack={navigation.goBack}
|
|
74
|
+
storage={storage}
|
|
75
|
+
audioSession={audioSession}
|
|
76
|
+
onMessageOptions={message => navigation.openMessageActions(message)}
|
|
77
|
+
/>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Open the conversation with `await messagingClient.open(conversationId)`, then call `messagingClient.start()` while it is active. Call `stop()` when it backgrounds and `start()` when it returns; call `dispose()` when the owning client is no longer used. The screen subscribes to the client but does not own its polling lifecycle.
|
|
81
|
+
|
|
82
|
+
Managed attachment history includes short-lived download authorization. The Expo adapter uses it to begin a fresh transfer without another authorization request; absent, mismatched or expired grants use the authenticated media endpoint. Keep grants in memory only and out of logs and persisted chat history. The bucket remains private. As with other issued transfer links, revocation can leave an already issued link usable for up to 60 seconds, unless the object has already been removed.
|
|
83
|
+
|
|
84
|
+
Managed voice messages and videos use progressive streaming: the native player can start before the complete original file has downloaded and request byte ranges when seeking. No lower-resolution copy or new streaming subscription is required. Photos and documents retain verified whole-file downloads. Set `streaming: false` in `createExpoMessagingStorage` to use whole-file downloads for voice/video too. Custom `MessagingMediaStorage` adapters may implement optional `stream(message, context)` returning `{...metadata, uri, stream: {headers?}}`; omit it or return `null` to use `download`.
|
|
85
|
+
|
|
86
|
+
If native streaming fails or a later range request encounters an expired link, the screen makes one recovery attempt through the existing authorized download path, preserving playback position and whether playback was paused. Denied access still fails; it does not bypass authorization or loop retries. Native buffers are temporary; streaming URLs and headers must never be persisted. Usage counts bytes served, including additional ranges or recovery downloads, rather than treating each range as a whole-file download.
|
|
87
|
+
|
|
88
|
+
The adapter prepares private file storage alongside download authorization, and prepares voice audio output alongside native file loading. Transfers still require authorization; playback waits for native media readiness and the audio session. Video rendering can prepare while audio initializes, with playback controls enabled only after audio is ready.
|
|
89
|
+
|
|
90
|
+
For performance measurement, `onMediaReady` reports `{messageId, kind, stage, elapsedMs}` from a photo's load, a video's first frame or advancing voice playback. Disable preloading to measure on-demand media startup. Streaming readiness means the player has usable data, not that it downloaded the whole file. This callback does not measure the exact audible onset at the speaker.
|
|
91
|
+
|
|
92
|
+
Create stable storage and audio-session instances outside rendering or with `useMemo`. Mount within your existing `SafeAreaProvider`. If your app already coordinates calls and media, supply its own `MessagingAudioSession.acquire()` lease instead. A lease returns an idempotent release function. `isAvailable()` must return false while a call owns audio. The supplied coordinator stops media when that guard changes and avoids overwriting the call's audio mode. Your call integration remains responsible for acquiring its own native audio session.
|
|
93
|
+
|
|
94
|
+
Photos, Videos and Files each have an allowed MIME-type list and maximum file size. Videos also have a duration limit. Files cannot be used to bypass image, video or voice restrictions. A phone may list more formats than the app allows; selection is checked before upload and again against verified metadata on the backend. Allowing a container format does not guarantee that every codec in it plays on every device. Choose formats compatible with your supported phones, or convert media in your storage pipeline. Documents open the native share/open menu; installed document viewers vary by phone.
|
|
95
|
+
|
|
96
|
+
The bundled adapter uses your private storage endpoint, described below. You can instead implement `MessagingMediaStorage` with your existing upload/download service, or use the core `MessagingScreen` callbacks. None of these screens or adapters is mandatory.
|
|
97
|
+
|
|
98
|
+
## Private media endpoint
|
|
99
|
+
|
|
100
|
+
Mount `createMessagingMediaBackendAdapter` from `@robono/server` behind your existing session authentication. Supply the same `RobonoMessagingBackend` as your messaging endpoint, your authorization checks, and a `MessagingPrivateStorage` implementation for your storage provider. The adapter checks current conversation access and policy before asking storage for a transfer. Phone requests never contain Robono API keys or storage credentials.
|
|
101
|
+
|
|
102
|
+
The storage implementation has four operations:
|
|
103
|
+
|
|
104
|
+
- `prepare`: persist upload ID, actor, conversation, message kind and proposed metadata together; return a short-lived private HTTPS PUT URL and required headers. Retrying the same upload ID must resolve the same upload. Reject conflicting reuse.
|
|
105
|
+
- `complete`: verify that actor/conversation own the upload; inspect the stored bytes to determine the actual format, size and recording/video duration. Return the persisted kind and verified metadata. A filename, extension or phone-provided MIME type is not verification. Repeated completion must return the same asset.
|
|
106
|
+
- `download`: return a short-lived private HTTPS URL for the authorized message's asset. Robono checks that the message still exists; storage also verifies its asset belongs to that message/conversation.
|
|
107
|
+
- `discard`: revoke/delete only that actor's uncommitted upload. It must be safe to repeat, including after a lost completion response, and must never delete an asset already referenced by an accepted message.
|
|
108
|
+
|
|
109
|
+
The HTTP protocol uses POST with `{operation, ...}`. `prepare` takes `uploadId`, `conversationId`, `kind`, and `file` metadata; it returns `{url, headers?}` or `{media}` for an already verified upload. `complete` takes `uploadId` and `conversationId`, returning `{media}`. `download` takes `conversationId` and `messageId`, returning `{url, headers?, media}`. `discard` takes `uploadId`, returning `{ok:true}`. Errors use `{error:{code,message}}` and an appropriate HTTP status.
|
|
110
|
+
|
|
111
|
+
Also supply `resolveMedia` to `createMessagingBackendAdapter`, resolving the authenticated actor, conversation and asset ID to your verified stored metadata. This replaces untrusted phone claims before sending. Keep asset IDs opaque and unique per message. Check blocks/permissions in both endpoints. Restrict signed uploads to the prepared object and size where supported; delete rejected objects. Expire abandoned uploads and private cache files through your storage lifecycle. Your app owns storage and any malware/content checks appropriate to its policy; the SDK does not upload files to public storage or scan private conversations.
|
|
112
|
+
|
|
113
|
+
Handle signed `messaging.message_removed` events to revoke and delete assets when history expires. Account deletion must also erase associated storage. A terminated app cannot run cache cleanup; the adapter's temporary files live under the OS cache directory, which can be purged by the OS. Do not treat device cache eviction as a fixed retention guarantee.
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
### Design messaging and calling together
|
|
117
|
+
|
|
118
|
+
The website Experience builder renders these messaging components and the optional calling components, with a local conversation transport. Saving a draft creates an immutable design version without changing active settings. Review and apply updates messaging and calling atomically, with revision checks against the existing editors. Use your existing settings/appearance fetches to receive the applied design. Service access and per-contact eligibility still apply.
|
|
119
|
+
|
|
120
|
+
For a conversation header with an optional voice-call action:
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
import { MessagingCallingScreen } from '@robono/messaging-react-native/calling';
|
|
124
|
+
|
|
125
|
+
<MessagingCallingScreen
|
|
126
|
+
client={messagingClient}
|
|
127
|
+
userId={signedInUserId}
|
|
128
|
+
contact={contact}
|
|
129
|
+
onCall={voiceAvailable ? () => callingController.call(connectionId, contact) : undefined}
|
|
130
|
+
/>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
The `/calling` entry additionally needs the optional `@robono/calling-react-native` peer. Mount the calling overlay and configure native audio/notifications through the existing calling integration. This entry creates no call client and requests no permission. Omit `onCall` to hide the action; backend admission remains authoritative. `MessagingHeader` also accepts children for custom header actions.
|
|
134
|
+
|
|
135
|
+
The website preview renders the same bubbles, inline voice controls, full-screen photo/file viewer and video transport controls through React Native Web. Phone/tablet and portrait/landscape choices resize the real layout viewport without reloading the conversation. Browser media elements and Expo players drive the shared controls; the browser preview does not substitute native browser player controls or a generic dialog. Camera/library/file selection and OS permission prompts still follow the platform. Preview interactions never contact real users or send billable events.
|
|
136
|
+
|
|
137
|
+
The standard chat uses compact grouped bubbles, timestamps at the lower right, inline voice controls and an expandable attachment tray below the composer. Attach dismisses the keyboard; tap it again to return to typing. Android Back closes the tray first. The microphone remains available beside the text field, including while text is typed; Send is enabled when there is text or a finished voice recording. Camera remains available when capture is enabled, including with typed text or a reviewed voice recording. Captures join the existing pending attachments. New designs start with a light chat palette; existing saved colors remain unchanged. The builder’s “Use classic chat colors” action applies the default palette to the draft without changing enabled features.
|
|
138
|
+
|
|
139
|
+
Voice-only conversations use Robono Messenger’s compact composer: the duration-limit placeholder, embedded microphone and adjacent Send control. The Expo adapter records and reviews audio inline, with elapsed time, microphone metering, playback and removal before sending. The builder uses the same controls with a local browser microphone recording; no audio is uploaded from the preview. When texting is enabled, tap the microphone to record in the composer and tap it again to stop. Each completed recording becomes a separate playable attachment in the shared horizontal row, and the microphone is available for another recording. Typed text is preserved; Send submits the voice message and any accompanying text as separate messages. Custom media adapters can use `components.VoiceComposer`, `VoiceMessageComposer` and `VoiceDraftPlayback`; `renderTextComposer` restores the text controls alongside the reviewed recording.
|
|
140
|
+
|
|
141
|
+
Recent attachments can appear directly beneath the attachment buttons. Photos, Videos and Document filter this strip; tapping the selected category again shows all enabled types. Tapping a thumbnail adds it above the composer as a pending attachment; its magnifying-glass button opens a preview instead. Remove unwanted attachments or press Send to submit the selection with any typed text. Camera starts capture. When no recent-media source is supplied, the category buttons open the device picker. Supply `attachmentChoices` with an ID, kind, name, and optional thumbnail URI/duration. Only enabled attachment types appear. With `MessagingScreen`, selecting a tile calls `pickAttachment` with `options.attachmentId`; use `options.intent` to distinguish selection (`send`, retained for adapter compatibility) from preview. Returning photo, video or document media stages it in the composer; returning null cancels without selecting it. Voice recordings also become pending attachments after recording stops. With `ExpoMessagingScreen`, supply `resolveAttachmentChoice(id, signal)` to return an authorized cache copy; the standard preview, validation, send and cleanup flow handles the rest. Keep originals in the user's library untouched. The host controls library access and which recent items it supplies.
|
|
142
|
+
|
|
143
|
+
Camera capture and library selection are separate controls. `appearance.showPhotoLibrary` and `showVideoLibrary` hide library choices without removing the corresponding message-type permission needed for camera captures. Both default to true for existing integrations. The builder manages these together with Camera, so enabling capture does not select Photos/Videos or insert example media. Parent and network message-type permissions remain enforced.
|
|
144
|
+
|
|
145
|
+
The Expo adapter prepares voice assets in private storage when recording stops, retaining local playback copies until removal, submission or disposal. This does not send a chat message. Send submits all selected attachments in order, followed by any typed text. Custom adapters can supply components.VoiceAttachment for draft playback and releaseAttachment for local cleanup.
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
## Group conversations, reactions and polls
|
|
149
|
+
|
|
150
|
+
The same `MessagingScreen` opens direct or group conversations. Supply `participants: {[userId]: {name, avatarUrl}}` from your own authorized profile data. The service supplies the group name, membership and `canSend`; read-only members see the conversation without a composer. Your backend manages group membership through `RobonoMessagingBackend.setGroup`, never a mobile API key.
|
|
151
|
+
|
|
152
|
+
Enable reactions and polls in Experience builder. The screen includes the long-press reaction picker, reaction counts, poll composer and voting/results controls. These components share your configured message colors. `PollMessage` and `PollComposer` can also be used separately. Voting is visible to other participants; only a poll's creator can close it.
|
|
153
|
+
|
|
154
|
+
## Optional message after no answer
|
|
155
|
+
|
|
156
|
+
Use `ExpoMessagingCallingScreen` from `@robono/messaging-react-native/expo-calling` for the supplied native media tools together with calling. It accepts the same `storage` and `audioSession` as `ExpoMessagingScreen`. For your own media adapter, use `MessagingCallingScreen` from the `/calling` entry to combine the optional call button and message composer. Connect the calling controller's `onUnansweredCall` callback to your existing conversation navigation, then pass `unansweredCallId` and `onDismissCallMessage`. The callback only runs for an outgoing call made in the current controller session that ended without an answer; opening old missed-call history does not trigger it.
|
|
157
|
+
|
|
158
|
+
`UnansweredCallMessage` is also exported separately for a custom composer. Place it inside `MessagingProvider` and supply your existing `pickAttachment` adapter. The adapter must support recording, review and private upload. Eligibility comes from your authenticated backend, and sending rechecks permissions. Recording is optional and starts only when the caller chooses it. An ordinary message is left in the chat, with normal history, retry and media rules.
|
|
159
|
+
|
|
160
|
+
### Blocking, reporting, reactions and language tools
|
|
161
|
+
|
|
162
|
+
The default header’s three-dot menu provides Block/Unblock and Report when the authenticated backend advertises safety support. Hold a received bubble to report that message. `onContactOptions` optionally replaces the supplied contact menu with your existing safety flow. The exported `MessagingSafety` component can also be composed into your screen.
|
|
163
|
+
|
|
164
|
+
Blocking retains history and stops direct messages and calls for that pair in your app. Shared group messages remain visible. Reporting is separate: it shares the selected message, or up to five recent incoming messages, and the reason with your app’s moderators. It confirms success only after storage acknowledges the report. The SDK reuses the request ID on retry. Owners and administrators review reports in the Messaging workspace; private media is reviewed through your app’s authorized storage tools.
|
|
165
|
+
|
|
166
|
+
Enable Reactions to show an emoji ribbon beside the long-pressed bubble, or the hover smile icon on desktop. Selected reactions stay attached below the message. Polls are interactive chat messages created from the attachment menu. Transcript and Translate toggle small text immediately under the original; the original timestamp stays with the source message.
|