fix(audio,voice,log): six P0 issues from Korean Windows test
1. Voice Bar 'Leave voice' button removed entirely. TeamSpeak users are always in some channel; Discord/Mumble-style leave is the wrong model. To stop being heard / hearing, mute mic / speaker. To physically move, tap a different channel. The voiceLeave bridge call + _onLeaveVoice stay as dead code for now (marked unused) so existing tests/integrations don't break. 2. voice_join now confirms the move actually applied server-side by polling the snapshot for up to 1.5 s and matching our own client's channel against the requested one. If the server rejected the move (no permission, wrong password, channel full), voice_join rolls the selector back to in_channel=false and returns Err so the UI surfaces the failure instead of showing a fake 'joined' state. 3. ServerSnapshot + BridgeSnapshot gain own_client_id so the UI can identify our row without name-matching. find_own_in reads it directly. 4. set_self_muted now also clamps the TransmitModeSelector's hard_mute when input is muted server-side. Without this, the Opus encoder kept producing frames after setInputMuted(true), tsclientlib refused each one with 'Sending audio while muted', and the log grew to 200 MB on the Korean host. 5. tsclientlib WARN spam suppressed via tracing filter (tsclientlib=error). Belt-and-braces on top of fix 4. 6. Log file is now rotated at every launch (not just when >4 MiB). Two generations kept: chanora.log.1 (previous) and chanora.log.2 (the one before). The bug that produced 200 MB files was a chatty subsystem flooding a single session; the per-launch rotate keeps disk use bounded by what one session can produce in its lifetime. Bonus Windows fix (separate from the six but found in the same log): the Raw Input + Hook backends now signal readiness BEFORE blocking on GetMessageW. Previously init_tx.send was called after the loop returned (i.e. on WM_QUIT, which never happens during arming), so the main thread's 2 s readiness probe always timed out and the backend reported L0Focused even when registration succeeded. Both run_raw_input_loop and run_hook_loop now take an init_tx parameter and call report!(true) right after a successful registration, and report!(false) on every early-fail return. cargo check --workspace: clean. cargo test --workspace --lib: 80 passed / 0 failed / 1 ignored. flutter analyze: clean (6 pre-existing Radio.groupValue infos).
This commit is contained in:
@@ -373,6 +373,7 @@ class _BetaHomeState extends State<_BetaHome> {
|
||||
}
|
||||
}
|
||||
|
||||
// ignore: unused_element
|
||||
Future<void> _onLeaveVoice() async {
|
||||
try {
|
||||
await rust.voiceLeave();
|
||||
@@ -867,7 +868,6 @@ class _BetaHomeState extends State<_BetaHome> {
|
||||
onToggleMute: _onToggleHardMute,
|
||||
onToggleOutputMute: _toggleOutputMute,
|
||||
onConfigure: _onOpenVoiceSettings,
|
||||
onLeave: _onLeaveVoice,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Expanded(
|
||||
|
||||
@@ -475,6 +475,11 @@ class BridgeSnapshot {
|
||||
/// Clients currently known.
|
||||
final List<BridgeClient> clients;
|
||||
|
||||
/// Our own client id. Useful for the UI to highlight our row
|
||||
/// in the client list and to know which channel we are in
|
||||
/// without trusting the optimistic local state.
|
||||
final BigInt ownClientId;
|
||||
|
||||
const BridgeSnapshot({
|
||||
required this.serverName,
|
||||
required this.welcomeMessage,
|
||||
@@ -482,6 +487,7 @@ class BridgeSnapshot {
|
||||
required this.version,
|
||||
required this.channels,
|
||||
required this.clients,
|
||||
required this.ownClientId,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -491,7 +497,8 @@ class BridgeSnapshot {
|
||||
platform.hashCode ^
|
||||
version.hashCode ^
|
||||
channels.hashCode ^
|
||||
clients.hashCode;
|
||||
clients.hashCode ^
|
||||
ownClientId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
@@ -503,7 +510,8 @@ class BridgeSnapshot {
|
||||
platform == other.platform &&
|
||||
version == other.version &&
|
||||
channels == other.channels &&
|
||||
clients == other.clients;
|
||||
clients == other.clients &&
|
||||
ownClientId == other.ownClientId;
|
||||
}
|
||||
|
||||
/// Voice transmit mode mirror (SDD-095). Schema-controlled enum;
|
||||
|
||||
@@ -1172,8 +1172,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
BridgeSnapshot dco_decode_bridge_snapshot(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
final arr = raw as List<dynamic>;
|
||||
if (arr.length != 6)
|
||||
throw Exception('unexpected arr length: expect 6 but see ${arr.length}');
|
||||
if (arr.length != 7)
|
||||
throw Exception('unexpected arr length: expect 7 but see ${arr.length}');
|
||||
return BridgeSnapshot(
|
||||
serverName: dco_decode_String(arr[0]),
|
||||
welcomeMessage: dco_decode_String(arr[1]),
|
||||
@@ -1181,6 +1181,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
version: dco_decode_String(arr[3]),
|
||||
channels: dco_decode_list_bridge_channel(arr[4]),
|
||||
clients: dco_decode_list_bridge_client(arr[5]),
|
||||
ownClientId: dco_decode_u_64(arr[6]),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1483,6 +1484,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
var var_version = sse_decode_String(deserializer);
|
||||
var var_channels = sse_decode_list_bridge_channel(deserializer);
|
||||
var var_clients = sse_decode_list_bridge_client(deserializer);
|
||||
var var_ownClientId = sse_decode_u_64(deserializer);
|
||||
return BridgeSnapshot(
|
||||
serverName: var_serverName,
|
||||
welcomeMessage: var_welcomeMessage,
|
||||
@@ -1490,6 +1492,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
version: var_version,
|
||||
channels: var_channels,
|
||||
clients: var_clients,
|
||||
ownClientId: var_ownClientId,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1809,6 +1812,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
sse_encode_String(self.version, serializer);
|
||||
sse_encode_list_bridge_channel(self.channels, serializer);
|
||||
sse_encode_list_bridge_client(self.clients, serializer);
|
||||
sse_encode_u_64(self.ownClientId, serializer);
|
||||
}
|
||||
|
||||
@protected
|
||||
|
||||
@@ -29,7 +29,6 @@ class VoiceBar extends StatelessWidget {
|
||||
required this.onToggleMute,
|
||||
required this.onToggleOutputMute,
|
||||
required this.onConfigure,
|
||||
required this.onLeave,
|
||||
});
|
||||
|
||||
/// True when the session is currently joined to a voice channel.
|
||||
@@ -85,9 +84,6 @@ class VoiceBar extends StatelessWidget {
|
||||
/// and intentionally does NOT have its own configure affordance.
|
||||
final VoidCallback onConfigure;
|
||||
|
||||
/// Leave the voice channel.
|
||||
final VoidCallback onLeave;
|
||||
|
||||
String _modeLabel(AppL10n l10n) {
|
||||
switch (transmitMode) {
|
||||
case rust.BridgeTransmitMode.ptt:
|
||||
@@ -236,17 +232,12 @@ class VoiceBar extends StatelessWidget {
|
||||
backendId: pttBackendId,
|
||||
boundInputClass: pttBoundInputClass,
|
||||
),
|
||||
if (inChannel) ...[
|
||||
const SizedBox(height: 6),
|
||||
Align(
|
||||
alignment: AlignmentDirectional.centerEnd,
|
||||
child: TextButton.icon(
|
||||
icon: const Icon(Icons.call_end),
|
||||
label: Text(l10n.voiceLeaveAction),
|
||||
onPressed: onLeave,
|
||||
),
|
||||
),
|
||||
],
|
||||
// Leave-voice button intentionally absent: TeamSpeak's
|
||||
// model is "user is always in some channel", not
|
||||
// Discord's join/leave-voice. To stop being heard /
|
||||
// hearing others, mute mic and/or speaker via the
|
||||
// icons at the top of the bar. To physically move,
|
||||
// tap a different channel in the tree below.
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user