Mobile calls: keep call memberships in memory; answering keeps the call screen

Found testing calls between two phones on the Compose call stack:

- The famedly SDK keeps only "important" state in memory for a room that
  hasn't been opened, so call memberships of other rooms were invisible:
  no ring (it was cleared at once, the call looking empty), no room-list
  marker. org.matrix.msc3401.call.member is now important state.
- The incoming-call card closed itself with Navigator.pop when the ring
  cleared, which popped the call screen pushed above it on Answer. The
  buttons now close the card first, and it closes itself only while it
  is the top route.

Tested: a voice call in a room of two (ring, answer, both connected
through LiveKit), and a video call in an encrypted room (to-device media
keys, frame cryptors OK on both sides, each phone showing the other's
video). The web client sees a phone's membership and rings for its
notification.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
tajniak81andClaude Opus 5.5 committed 2026-10-19 09:08:01 +02:00
1 parent 41f800ab0b
commit b8ddbaacf3
2 files changed
+15 -3

No files matched your search

+5 -1
View File
@@ -9,6 +9,7 @@ import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart' as sqflite;
import 'calls.dart';
import 'sliding_sync.dart';
/// The name the session's device gets on the homeserver.
@@ -60,7 +61,10 @@ Future<Client> createClient({bool startSync = true}) async {
vodozemacInit: () => vod.init(),
),
logLevel: kReleaseMode ? Level.warning : Level.info,
importantStateEvents: {'im.ponies.room_emotes'},
// Kept in memory for every room, not just opened ones: the room list
// marks rooms with a call going on, and a ring needs to know who is in
// the call.
importantStateEvents: {'im.ponies.room_emotes', Calls.memberType},
// Refresh tokens: a soft logout (an expired access token) is answered by
// refreshing it rather than by signing the user out.
onSoftLogout: (client) => client.refreshAccessToken(),
+10 -2
View File
@@ -427,8 +427,11 @@ Future<void> showIncomingCall(
final call = calls.incoming;
if (call == null) {
// Answered elsewhere, declined, or it stopped ringing.
// Only while the sheet is on top: after Answer, the call screen
// is, and popping would close it instead.
WidgetsBinding.instance.addPostFrameCallback((_) {
if (Navigator.of(sheet).canPop()) Navigator.of(sheet).pop();
final route = ModalRoute.of(sheet);
if (route != null && route.isCurrent) Navigator.of(sheet).pop();
});
return const SizedBox(height: 200);
}
@@ -462,7 +465,10 @@ Future<void> showIncomingCall(
icon: Icons.call_end,
label: 'Decline',
colour: c.danger,
onTap: () => calls.decline(),
onTap: () {
Navigator.of(sheet).pop();
calls.decline();
},
),
if (call.video)
_RoundButton(
@@ -470,6 +476,7 @@ Future<void> showIncomingCall(
label: 'Voice only',
colour: c.tealStrong,
onTap: () {
Navigator.of(sheet).pop();
calls.answer(withVideo: false);
navigator.push(CallScreen.route());
},
@@ -479,6 +486,7 @@ Future<void> showIncomingCall(
label: 'Answer',
colour: c.tealStrong,
onTap: () {
Navigator.of(sheet).pop();
calls.answer();
navigator.push(CallScreen.route());
},