feat: add participant to existing call + comprehensive call previews

Add ability to invite new users into an ongoing call:
- CallManager.invitePeer() sends a call offer to a new peer using
  the current callId, adding them to pendingPeerPubKeys
- CallController.invitePeer() exposes this to the UI layer
- Add PersonAdd button to connected call controls
- Add AddParticipantDialog with user search (reuses UserSuggestionState)
- New strings: call_add_participant, call_search_users

Rewrite CallScreenPreviews to showcase all call states:
1. Offering P2P call (Calling...)
2. Offering group call (3 avatars)
3. Connecting
4. Incoming voice call
5. Incoming video call
6. Incoming group call (4 avatars)
7. Connected P2P voice call with controls
8. Connected muted + video + speaker
9. Connected group call with pending peers
10. Connected with Bluetooth audio
11. Call ended
12. Large group (5+ members, +N badge)
13. PiP calling
14. PiP connected

https://claude.ai/code/session_01LR8NmFGdMoDTVcfKjL7HWR
This commit is contained in:
Claude
2026-04-03 01:07:14 +00:00
parent 0c7aa67dd8
commit a65e192f03
5 changed files with 574 additions and 188 deletions
@@ -301,6 +301,36 @@ class CallManager(
)
}
/** Invites a new peer into the current call by sending them an offer. */
suspend fun invitePeer(
peerPubKey: HexKey,
sdpOffer: String,
) {
val current = _state.value
val callId: String
val callType: CallType
when (current) {
is CallState.Connecting -> {
callId = current.callId
callType = current.callType
_state.value = current.copy(pendingPeerPubKeys = current.pendingPeerPubKeys + peerPubKey)
}
is CallState.Connected -> {
callId = current.callId
callType = current.callType
_state.value = current.copy(pendingPeerPubKeys = current.pendingPeerPubKeys + peerPubKey)
}
else -> {
return
}
}
val result = factory.createCallOffer(sdpOffer, peerPubKey, callId, callType, signer)
publishEvent(result.wrap)
}
suspend fun hangup() {
val peerPubKeys: Set<HexKey>
val callId: String