@poolse/react-native 2.1.6 → 2.2.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 +89 -89
- package/README.md +238 -238
- package/dist/index.cjs +290 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +82 -2
- package/dist/index.d.ts +82 -2
- package/dist/index.js +288 -2
- package/dist/index.js.map +1 -1
- package/package.json +18 -11
- package/LICENSE +0 -21
package/dist/index.cjs
CHANGED
|
@@ -4941,9 +4941,296 @@ function useAppStateLifecycle() {
|
|
|
4941
4941
|
}, [poolse]);
|
|
4942
4942
|
}
|
|
4943
4943
|
|
|
4944
|
+
// src/voice/webrtc-native.ts
|
|
4945
|
+
var cached;
|
|
4946
|
+
function loadNativeWebRtc() {
|
|
4947
|
+
if (cached !== void 0) return cached;
|
|
4948
|
+
try {
|
|
4949
|
+
cached = __require("react-native-webrtc");
|
|
4950
|
+
} catch {
|
|
4951
|
+
cached = null;
|
|
4952
|
+
}
|
|
4953
|
+
return cached;
|
|
4954
|
+
}
|
|
4955
|
+
function isNativeWebRtcAvailable() {
|
|
4956
|
+
return loadNativeWebRtc() != null;
|
|
4957
|
+
}
|
|
4958
|
+
function createNativeWebRtcAdapter() {
|
|
4959
|
+
const native = loadNativeWebRtc();
|
|
4960
|
+
if (!native) {
|
|
4961
|
+
throw new Error(
|
|
4962
|
+
"react-native-webrtc is not installed. Add it to enable poolse voice calls:\n npx expo install react-native-webrtc\nIt contains native code, so it needs a development build \u2014 it will not run in Expo Go."
|
|
4963
|
+
);
|
|
4964
|
+
}
|
|
4965
|
+
return {
|
|
4966
|
+
createPeerConnection(iceServers) {
|
|
4967
|
+
return new native.RTCPeerConnection({ iceServers });
|
|
4968
|
+
},
|
|
4969
|
+
getUserMedia() {
|
|
4970
|
+
return native.mediaDevices.getUserMedia({
|
|
4971
|
+
audio: { echoCancellation: true, noiseSuppression: true },
|
|
4972
|
+
video: false
|
|
4973
|
+
});
|
|
4974
|
+
},
|
|
4975
|
+
playRemoteStream() {
|
|
4976
|
+
return () => {
|
|
4977
|
+
};
|
|
4978
|
+
}
|
|
4979
|
+
};
|
|
4980
|
+
}
|
|
4981
|
+
function VoiceRoomBar({ conversationId, labelFor, unavailableSlot }) {
|
|
4982
|
+
const theme = usePoolseTheme();
|
|
4983
|
+
const available = isNativeWebRtcAvailable();
|
|
4984
|
+
const opts = react.useMemo(
|
|
4985
|
+
() => available ? { webrtc: createNativeWebRtcAdapter() } : {},
|
|
4986
|
+
[available]
|
|
4987
|
+
);
|
|
4988
|
+
const { status, participants, muted, join, leave, toggleMute, error } = react$1.useVoiceRoom(
|
|
4989
|
+
available ? conversationId : null,
|
|
4990
|
+
opts
|
|
4991
|
+
);
|
|
4992
|
+
const label = labelFor ?? ((id) => `User ${id.slice(0, 6)}`);
|
|
4993
|
+
const connected = status === "connected";
|
|
4994
|
+
if (!available) {
|
|
4995
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4996
|
+
reactNative.View,
|
|
4997
|
+
{
|
|
4998
|
+
style: [
|
|
4999
|
+
styles23.bar,
|
|
5000
|
+
{ backgroundColor: theme.colors.surface, borderBottomColor: theme.colors.border }
|
|
5001
|
+
],
|
|
5002
|
+
children: unavailableSlot ?? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles23.hint, { color: theme.colors.ink3 }], children: "Voice needs react-native-webrtc and a development build." })
|
|
5003
|
+
}
|
|
5004
|
+
);
|
|
5005
|
+
}
|
|
5006
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5007
|
+
reactNative.View,
|
|
5008
|
+
{
|
|
5009
|
+
style: [
|
|
5010
|
+
styles23.bar,
|
|
5011
|
+
{ backgroundColor: theme.colors.surface, borderBottomColor: theme.colors.border }
|
|
5012
|
+
],
|
|
5013
|
+
children: [
|
|
5014
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles23.row, children: [
|
|
5015
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5016
|
+
reactNative.View,
|
|
5017
|
+
{
|
|
5018
|
+
style: [
|
|
5019
|
+
styles23.dot,
|
|
5020
|
+
{ backgroundColor: connected ? theme.colors.brand : theme.colors.ink3 }
|
|
5021
|
+
]
|
|
5022
|
+
}
|
|
5023
|
+
),
|
|
5024
|
+
connected ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5025
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles23.people, children: participants.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles23.hint, { color: theme.colors.ink3 }], children: "Waiting for others\u2026" }) : participants.map((p) => /* @__PURE__ */ jsxRuntime.jsx(ParticipantChip, { participant: p, label: label(p.userId) }, p.userId)) }),
|
|
5026
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5027
|
+
reactNative.Pressable,
|
|
5028
|
+
{
|
|
5029
|
+
onPress: toggleMute,
|
|
5030
|
+
accessibilityRole: "button",
|
|
5031
|
+
accessibilityState: { selected: muted },
|
|
5032
|
+
style: [
|
|
5033
|
+
styles23.btn,
|
|
5034
|
+
{
|
|
5035
|
+
borderColor: muted ? theme.colors.brand : theme.colors.border,
|
|
5036
|
+
backgroundColor: muted ? theme.colors.brandSoft : theme.colors.paper
|
|
5037
|
+
}
|
|
5038
|
+
],
|
|
5039
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { color: theme.colors.ink2 }, children: muted ? "Unmute" : "Mute" })
|
|
5040
|
+
}
|
|
5041
|
+
),
|
|
5042
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5043
|
+
reactNative.Pressable,
|
|
5044
|
+
{
|
|
5045
|
+
onPress: leave,
|
|
5046
|
+
accessibilityRole: "button",
|
|
5047
|
+
style: [styles23.btn, { borderColor: theme.colors.error }],
|
|
5048
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { color: theme.colors.error }, children: "Leave" })
|
|
5049
|
+
}
|
|
5050
|
+
)
|
|
5051
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5052
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles23.hint, { color: theme.colors.ink3 }], children: status === "connecting" ? "Connecting\u2026" : "Talk to everyone here" }),
|
|
5053
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5054
|
+
reactNative.Pressable,
|
|
5055
|
+
{
|
|
5056
|
+
onPress: () => void join(),
|
|
5057
|
+
disabled: status === "connecting",
|
|
5058
|
+
accessibilityRole: "button",
|
|
5059
|
+
style: [styles23.btn, styles23.btnPrimary, { backgroundColor: theme.colors.brand }],
|
|
5060
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { color: theme.colors.onBrand, fontWeight: "600" }, children: status === "connecting" ? "Joining\u2026" : "Join Voice" })
|
|
5061
|
+
}
|
|
5062
|
+
)
|
|
5063
|
+
] })
|
|
5064
|
+
] }),
|
|
5065
|
+
error && /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles23.error, { color: theme.colors.error }], children: error.message })
|
|
5066
|
+
]
|
|
5067
|
+
}
|
|
5068
|
+
);
|
|
5069
|
+
}
|
|
5070
|
+
function ParticipantChip({ participant, label }) {
|
|
5071
|
+
const theme = usePoolseTheme();
|
|
5072
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5073
|
+
reactNative.View,
|
|
5074
|
+
{
|
|
5075
|
+
style: [
|
|
5076
|
+
styles23.chip,
|
|
5077
|
+
{
|
|
5078
|
+
backgroundColor: theme.colors.paper,
|
|
5079
|
+
// The speaking ring is a border colour swap rather than an
|
|
5080
|
+
// extra element, so the row never reflows mid-sentence.
|
|
5081
|
+
borderColor: participant.speaking ? theme.colors.brand : theme.colors.border,
|
|
5082
|
+
opacity: participant.muted ? 0.62 : 1
|
|
5083
|
+
}
|
|
5084
|
+
],
|
|
5085
|
+
children: [
|
|
5086
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { style: { color: theme.colors.ink2, fontSize: 13 }, numberOfLines: 1, children: [
|
|
5087
|
+
label,
|
|
5088
|
+
participant.isSelf ? " (you)" : ""
|
|
5089
|
+
] }),
|
|
5090
|
+
participant.muted && /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { color: theme.colors.error, fontSize: 11, fontWeight: "700" }, children: "\u2715" })
|
|
5091
|
+
]
|
|
5092
|
+
}
|
|
5093
|
+
);
|
|
5094
|
+
}
|
|
5095
|
+
var styles23 = reactNative.StyleSheet.create({
|
|
5096
|
+
bar: { paddingHorizontal: 14, paddingVertical: 8, borderBottomWidth: reactNative.StyleSheet.hairlineWidth },
|
|
5097
|
+
row: { flexDirection: "row", alignItems: "center", gap: 8, flexWrap: "wrap" },
|
|
5098
|
+
dot: { width: 8, height: 8, borderRadius: 4 },
|
|
5099
|
+
people: { flexDirection: "row", alignItems: "center", gap: 6, flexShrink: 1, flexWrap: "wrap" },
|
|
5100
|
+
chip: {
|
|
5101
|
+
flexDirection: "row",
|
|
5102
|
+
alignItems: "center",
|
|
5103
|
+
gap: 5,
|
|
5104
|
+
paddingHorizontal: 10,
|
|
5105
|
+
paddingVertical: 4,
|
|
5106
|
+
borderRadius: 999,
|
|
5107
|
+
borderWidth: 1
|
|
5108
|
+
},
|
|
5109
|
+
btn: { paddingHorizontal: 12, paddingVertical: 6, borderRadius: 999, borderWidth: 1 },
|
|
5110
|
+
btnPrimary: { borderWidth: 0 },
|
|
5111
|
+
hint: { fontSize: 13, flexShrink: 1 },
|
|
5112
|
+
error: { fontSize: 12, marginTop: 6 }
|
|
5113
|
+
});
|
|
5114
|
+
function CallScreen({ userId, labelFor, calls: external }) {
|
|
5115
|
+
const theme = usePoolseTheme();
|
|
5116
|
+
const own = react$1.useCalls(external ? null : userId);
|
|
5117
|
+
const calls = external ?? own;
|
|
5118
|
+
const available = isNativeWebRtcAvailable();
|
|
5119
|
+
const opts = react.useMemo(
|
|
5120
|
+
() => available ? { webrtc: createNativeWebRtcAdapter() } : {},
|
|
5121
|
+
[available]
|
|
5122
|
+
);
|
|
5123
|
+
const voice = react$1.useVoiceRoom(
|
|
5124
|
+
calls.phase === "active" && available ? calls.activeConversationId : null,
|
|
5125
|
+
opts
|
|
5126
|
+
);
|
|
5127
|
+
react.useEffect(() => {
|
|
5128
|
+
if (calls.phase === "active" && voice.status === "idle") void voice.join();
|
|
5129
|
+
}, [calls.phase, voice]);
|
|
5130
|
+
const label = labelFor ?? ((id) => `User ${id.slice(0, 6)}`);
|
|
5131
|
+
const visible = calls.phase !== "idle";
|
|
5132
|
+
const hangUp = () => {
|
|
5133
|
+
voice.leave();
|
|
5134
|
+
calls.reset();
|
|
5135
|
+
};
|
|
5136
|
+
return /* @__PURE__ */ jsxRuntime.jsx(reactNative.Modal, { visible, animationType: "slide", transparent: false, onRequestClose: hangUp, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles24.screen, { backgroundColor: theme.colors.paper }], children: [
|
|
5137
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles24.identity, children: [
|
|
5138
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles24.who, { color: theme.colors.ink }], children: calls.phase === "ringing-in" && calls.incoming ? label(calls.incoming.callerUserId) : calls.outgoing ? calls.outgoing.calleeUserIds.map(label).join(", ") : "" }),
|
|
5139
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles24.state, { color: theme.colors.ink3 }], children: statusLine(calls, voice.status) })
|
|
5140
|
+
] }),
|
|
5141
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles24.actions, children: calls.phase === "ringing-in" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5142
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5143
|
+
CallButton,
|
|
5144
|
+
{
|
|
5145
|
+
label: "Decline",
|
|
5146
|
+
color: theme.colors.error,
|
|
5147
|
+
onPress: () => void calls.decline()
|
|
5148
|
+
}
|
|
5149
|
+
),
|
|
5150
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5151
|
+
CallButton,
|
|
5152
|
+
{
|
|
5153
|
+
label: "Accept",
|
|
5154
|
+
color: theme.colors.brand,
|
|
5155
|
+
onPress: () => void calls.accept()
|
|
5156
|
+
}
|
|
5157
|
+
)
|
|
5158
|
+
] }) : calls.phase === "ringing-out" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
5159
|
+
CallButton,
|
|
5160
|
+
{
|
|
5161
|
+
label: "Cancel",
|
|
5162
|
+
color: theme.colors.error,
|
|
5163
|
+
onPress: () => void calls.cancel()
|
|
5164
|
+
}
|
|
5165
|
+
) : calls.phase === "declined" || calls.phase === "busy" || calls.phase === "timed-out" ? /* @__PURE__ */ jsxRuntime.jsx(CallButton, { label: "Close", color: theme.colors.ink3, onPress: calls.reset }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5166
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5167
|
+
CallButton,
|
|
5168
|
+
{
|
|
5169
|
+
label: voice.muted ? "Unmute" : "Mute",
|
|
5170
|
+
color: theme.colors.ink2,
|
|
5171
|
+
onPress: voice.toggleMute
|
|
5172
|
+
}
|
|
5173
|
+
),
|
|
5174
|
+
/* @__PURE__ */ jsxRuntime.jsx(CallButton, { label: "Hang up", color: theme.colors.error, onPress: hangUp })
|
|
5175
|
+
] }) }),
|
|
5176
|
+
!available && /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles24.warn, { color: theme.colors.error }], children: "react-native-webrtc is not installed \u2014 this call has no audio." }),
|
|
5177
|
+
(calls.error ?? voice.error) && /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles24.warn, { color: theme.colors.error }], children: (calls.error ?? voice.error)?.message })
|
|
5178
|
+
] }) });
|
|
5179
|
+
}
|
|
5180
|
+
function statusLine(calls, voiceStatus) {
|
|
5181
|
+
switch (calls.phase) {
|
|
5182
|
+
case "ringing-in":
|
|
5183
|
+
return "Incoming call\u2026";
|
|
5184
|
+
case "ringing-out":
|
|
5185
|
+
return "Ringing\u2026";
|
|
5186
|
+
case "declined":
|
|
5187
|
+
return "Call declined";
|
|
5188
|
+
case "busy":
|
|
5189
|
+
return "Already on another call";
|
|
5190
|
+
case "timed-out":
|
|
5191
|
+
return "No answer";
|
|
5192
|
+
case "active":
|
|
5193
|
+
return voiceStatus === "connected" ? "Connected" : "Connecting\u2026";
|
|
5194
|
+
default:
|
|
5195
|
+
return "";
|
|
5196
|
+
}
|
|
5197
|
+
}
|
|
5198
|
+
function CallButton({
|
|
5199
|
+
label,
|
|
5200
|
+
color,
|
|
5201
|
+
onPress
|
|
5202
|
+
}) {
|
|
5203
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
5204
|
+
reactNative.Pressable,
|
|
5205
|
+
{
|
|
5206
|
+
onPress,
|
|
5207
|
+
accessibilityRole: "button",
|
|
5208
|
+
style: [styles24.callBtn, { backgroundColor: color }],
|
|
5209
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles24.callBtnText, children: label })
|
|
5210
|
+
}
|
|
5211
|
+
);
|
|
5212
|
+
}
|
|
5213
|
+
var styles24 = reactNative.StyleSheet.create({
|
|
5214
|
+
screen: { flex: 1, justifyContent: "space-between", paddingVertical: 72, paddingHorizontal: 24 },
|
|
5215
|
+
identity: { alignItems: "center", gap: 8, marginTop: 48 },
|
|
5216
|
+
who: { fontSize: 28, fontWeight: "600", textAlign: "center" },
|
|
5217
|
+
state: { fontSize: 15 },
|
|
5218
|
+
actions: { flexDirection: "row", justifyContent: "center", gap: 16 },
|
|
5219
|
+
callBtn: {
|
|
5220
|
+
paddingHorizontal: 26,
|
|
5221
|
+
paddingVertical: 16,
|
|
5222
|
+
borderRadius: 999,
|
|
5223
|
+
minWidth: 128,
|
|
5224
|
+
alignItems: "center"
|
|
5225
|
+
},
|
|
5226
|
+
callBtnText: { color: "#ffffff", fontSize: 16, fontWeight: "600" },
|
|
5227
|
+
warn: { fontSize: 12, textAlign: "center", marginTop: 12 }
|
|
5228
|
+
});
|
|
5229
|
+
|
|
4944
5230
|
exports.AttachmentPicker = AttachmentPicker;
|
|
4945
5231
|
exports.AttachmentPreview = AttachmentPreview;
|
|
4946
5232
|
exports.Avatar = Avatar;
|
|
5233
|
+
exports.CallScreen = CallScreen;
|
|
4947
5234
|
exports.ChatHeader = ChatHeader;
|
|
4948
5235
|
exports.ConversationList = ConversationList;
|
|
4949
5236
|
exports.ConversationView = ConversationView;
|
|
@@ -4968,8 +5255,11 @@ exports.ThreadView = ThreadView;
|
|
|
4968
5255
|
exports.TypingIndicator = TypingIndicator;
|
|
4969
5256
|
exports.UploadQueueStrip = UploadQueueStrip;
|
|
4970
5257
|
exports.UserName = UserName;
|
|
5258
|
+
exports.VoiceRoomBar = VoiceRoomBar;
|
|
5259
|
+
exports.createNativeWebRtcAdapter = createNativeWebRtcAdapter;
|
|
4971
5260
|
exports.defaultDarkTheme = defaultDarkTheme;
|
|
4972
5261
|
exports.defaultLightTheme = defaultLightTheme;
|
|
5262
|
+
exports.isNativeWebRtcAvailable = isNativeWebRtcAvailable;
|
|
4973
5263
|
exports.useAppStateLifecycle = useAppStateLifecycle;
|
|
4974
5264
|
exports.useDisplayName = useDisplayName;
|
|
4975
5265
|
exports.usePoolseTheme = usePoolseTheme;
|