fix(voice): preserve mute owners and release touch ptt

This commit is contained in:
Edison Jwa
2026-06-08 18:00:32 +09:00
parent e4fdf8414a
commit d83539436e
7 changed files with 158 additions and 25 deletions
+26 -25
View File
@@ -25,6 +25,7 @@ import 'services/audio_lifecycle_service.dart';
import 'services/ios_audio_session_controller.dart';
import 'services/channel_join_error_mapper.dart';
import 'services/connection_phase_state.dart';
import 'services/hard_mute_owners.dart';
import 'services/ios_permissions_service.dart';
import 'services/macos_permissions_service.dart';
import 'services/prefetch_debouncer.dart';
@@ -339,8 +340,9 @@ class _BetaHomeState extends State<_BetaHome> with WidgetsBindingObserver {
bool _inChannel = false;
rust.BridgeTransmitMode _transmitMode = rust.BridgeTransmitMode.ptt;
bool _hardMute = false;
bool _hardMuteByPermission = false;
bool _hardMuteByTalkPower = false;
HardMuteOwners _hardMuteOwners = const HardMuteOwners();
bool get _hardMuteByPermission => _hardMuteOwners.permission;
bool get _hardMuteByTalkPower => _hardMuteOwners.talkPower;
bool _permissionHardMuteClearInFlight = false;
int _releaseTailMs = 200;
BigInt? _currentVoiceChannelId;
@@ -590,8 +592,8 @@ class _BetaHomeState extends State<_BetaHome> with WidgetsBindingObserver {
await rust.setHardMute(muted: false);
if (!mounted || !_hardMuteByPermission) return;
setState(() {
_hardMute = false;
_hardMuteByPermission = false;
_hardMuteOwners = _hardMuteOwners.copyWith(permission: false);
_hardMute = _hardMuteOwners.effective;
});
} catch (e) {
if (!mounted) return;
@@ -798,8 +800,8 @@ class _BetaHomeState extends State<_BetaHome> with WidgetsBindingObserver {
_voiceStateInitialized = true;
_inChannel = inChannel;
_transmitMode = transmitMode;
_hardMute = mute;
if (!mute) _hardMuteByPermission = false;
_hardMuteOwners = _hardMuteOwners.withBridgeManualMute(mute);
_hardMute = _hardMuteOwners.effective;
_releaseTailMs = releaseTailMs;
_currentVoiceChannelId = currentChannelId;
_pendingVoiceChannelId = pendingTargetChannelId;
@@ -1271,8 +1273,8 @@ class _BetaHomeState extends State<_BetaHome> with WidgetsBindingObserver {
await rust.setHardMute(muted: true);
if (mounted) {
setState(() {
_hardMute = true;
_hardMuteByPermission = true;
_hardMuteOwners = _hardMuteOwners.copyWith(permission: true);
_hardMute = _hardMuteOwners.effective;
});
}
} catch (_) {
@@ -1287,8 +1289,8 @@ class _BetaHomeState extends State<_BetaHome> with WidgetsBindingObserver {
await rust.setHardMute(muted: false);
if (mounted) {
setState(() {
_hardMute = false;
_hardMuteByPermission = false;
_hardMuteOwners = _hardMuteOwners.copyWith(permission: false);
_hardMute = _hardMuteOwners.effective;
});
}
}
@@ -1356,13 +1358,14 @@ class _BetaHomeState extends State<_BetaHome> with WidgetsBindingObserver {
final next = !_hardMute;
final previousInputMuted = _inputMuted;
final previousHardMute = _hardMute;
final previousPermissionMute = _hardMuteByPermission;
final previousHardMuteOwners = _hardMuteOwners;
setState(() {
_inputMuted = next;
_hardMute = next;
if (next) {
_hardMuteByPermission = false;
}
_hardMuteOwners = _hardMuteOwners.copyWith(
manual: next,
permission: next ? false : null,
);
_hardMute = _hardMuteOwners.effective;
});
try {
// Hard-mute is two coordinated effects:
@@ -1381,7 +1384,7 @@ class _BetaHomeState extends State<_BetaHome> with WidgetsBindingObserver {
setState(() {
_inputMuted = previousInputMuted;
_hardMute = previousHardMute;
_hardMuteByPermission = previousPermissionMute;
_hardMuteOwners = previousHardMuteOwners;
});
_showUiError('hard mute', e);
}
@@ -1651,8 +1654,7 @@ class _BetaHomeState extends State<_BetaHome> with WidgetsBindingObserver {
_inputMuted = false;
_outputMuted = false;
_hardMute = false;
_hardMuteByPermission = false;
_hardMuteByTalkPower = false;
_hardMuteOwners = const HardMuteOwners();
_inChannel = false;
_currentVoiceChannelId = null;
_pendingVoiceChannelId = null;
@@ -2083,8 +2085,8 @@ class _BetaHomeState extends State<_BetaHome> with WidgetsBindingObserver {
if (!own.talkPowerOk && !_hardMuteByTalkPower) {
final talkPowerEpoch = _connectionEpoch;
_hardMuteByTalkPower = true;
_hardMute = true;
_hardMuteOwners = _hardMuteOwners.copyWith(talkPower: true);
_hardMute = _hardMuteOwners.effective;
WidgetsBinding.instance.addPostFrameCallback((_) {
if (!mounted ||
_connectionEpoch != talkPowerEpoch ||
@@ -2097,15 +2099,14 @@ class _BetaHomeState extends State<_BetaHome> with WidgetsBindingObserver {
});
} else if (own.talkPowerOk && _hardMuteByTalkPower) {
final talkPowerEpoch = _connectionEpoch;
_hardMuteByTalkPower = false;
if (!_hardMuteByPermission) {
_hardMute = false;
_hardMuteOwners = _hardMuteOwners.copyWith(talkPower: false);
_hardMute = _hardMuteOwners.effective;
if (!_hardMuteOwners.effective) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (!mounted ||
_connectionEpoch != talkPowerEpoch ||
!_serverReachable ||
_hardMuteByTalkPower ||
_hardMuteByPermission) {
_hardMuteOwners.effective) {
return;
}
unawaited(rust.setHardMute(muted: false));