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