Comment history

Versions of a comment on Push-to-Talk Functioning Incorrectly, newest first.

Current version | Edited by Rex
Changes
Bug 2 (HIGH): getMuteReason always returns 'self' instead of 'push_to_talk'In VoiceAudioManager.ts line 115-123:Removed: tsexport function getMuteReason(voiceState: VoiceState | null) {Added: ```tsAdded: export function getMuteReason(voiceState: VoiceState | null) { const selfMuted = voiceState?.self_mute ?? LocalVoiceStateStore.getSelfMute(); if (KeybindStore.isPushToTalkEffective() && KeybindStore.isPushToTalkMuted(selfMuted)) return 'push_to_talk'; if (selfMuted) return 'self'; return null;}Added: ```Trace the logic: when PTT mutes the user, applyPushToTalkHold(false) → applyLocalMuteState(true, ...) → LocalVoiceStateStore.updateSelfMute(true) → syncVoiceState({self_mute: true}). So selfMuted becomes true. Then isPushToTalkMuted(true) hits line 567 in KeybindStore: if (userMuted) return false — because the parameter is intended to mean "user manually muted," but it's being passed the raw self_mute which includes PTT-induced muting.Result: mute reason is always 'self', never 'push_to_talk'. The UI shows "Unmute" instead of "Push-to-talk enabled — hold [key] to speak."
Show
found a few bugs with the PTT and here are some potential fixes for the issues: Bug 1 (CRITICAL): KeybindRecorder is disabled on web browsers — users can never set a PTT key In VoiceTab.tsx line 88 and 251:
const isPttLimited = !isNativeDesktop || (isNativeMac && !inputMonitoringGranted);
// ...
<KeybindRecorder ... disabled={isPttLimited} />
On a web browser, !isNativeDesktop is true, so isPttLimited is always true, and the KeybindRecorder is disabled. The user can switch to PTT mode (the radio group still works), but they physically cannot record a keybind. Combined with the fact that the default PTT combo is {key: '', enabled: false, global: false} (an empty key), PTT is completely non-functional on web browsers because no key is ever bound. The disabled flag conflates two things: "can't set ANY key" and "can't use global shortcuts." On web, local (in-focus) PTT should still work — the warning banner already tells users it only works when the tab has focus. The recorder should be enabled. Fix in VoiceTab.tsx:
// Line 251: only disable the recorder when PTT is entirely impossible,
// not merely limited. On web, local PTT works fine.
<KeybindRecorder
  action="push_to_talk"
  value={pttKeybind.combo}
  defaultValue={defaultPttCombo}
  disabled={false}  // ← always allow key recording
  onChange={(combo) => {
    KeybindStore.setKeybind('push_to_talk', {
      ...combo,
      global: isNativeDesktop ? pttKeybind.combo.global : false,
    });
  }}
  // ...
/>
Bug 2 (HIGH): getMuteReason always returns 'self' instead of 'push_to_talk' In VoiceAudioManager.ts line 115-123:
export function getMuteReason(voiceState: VoiceState | null) {
  const selfMuted = voiceState?.self_mute ?? LocalVoiceStateStore.getSelfMute();
  if (KeybindStore.isPushToTalkEffective() && KeybindStore.isPushToTalkMuted(selfMuted))
    return 'push_to_talk';
  if (selfMuted) return 'self';
  return null;
}
Trace the logic: when PTT mutes the user, applyPushToTalkHold(false) → applyLocalMuteState(true, ...) → LocalVoiceStateStore.updateSelfMute(true) → syncVoiceState({self_mute: true}). So selfMuted becomes true. Then isPushToTalkMuted(true) hits line 567 in KeybindStore: if (userMuted) return false — because the parameter is intended to mean "user manually muted," but it's being passed the raw self_mute which includes PTT-induced muting. Result: mute reason is always 'self', never 'push_to_talk'. The UI shows "Unmute" instead of "Push-to-talk enabled — hold [key] to speak." Fix in VoiceAudioManager.ts: tsexport function getMuteReason(voiceState: VoiceState | null) { const isGuildMuted = voiceState?.mute ?? false; if (isGuildMuted) return 'guild'; const selfMuted = voiceState?.self_mute ?? LocalVoiceStateStore.getSelfMute(); // Use the manual-mute flag, not raw self_mute, because PTT also sets self_mute const userManuallyMuted = LocalVoiceStateStore.getHasUserSetMute() && LocalVoiceStateStore.getSelfMute(); if (KeybindStore.isPushToTalkEffective() && KeybindStore.isPushToTalkMuted(userManuallyMuted)) return 'push_to_talk'; if (selfMuted) return 'self'; return null; } The hasUserSetMute flag in LocalVoiceStateStore is exactly the distinction needed — it's only set to true when the user explicitly toggles mute via toggleSelfMute(), never when PTT calls updateSelfMute(). Bug 3 (MEDIUM): pushToTalkHeld is set to false immediately on key release, before the release delay fires In KeybindStore.ts line 613-627, handlePushToTalkRelease sets this.pushToTalkHeld = false immediately. But in KeybindManager.ts line 566-572, applyPushToTalkHold(false) (which actually mutes the mic) runs after pushToTalkReleaseDelay ms. During that delay window, the MobX-observable pushToTalkHeld is false while the mic is still live. Any UI reading isPushToTalkMuted() sees the user as PTT-muted when they're actually still transmitting. This causes a visual flicker in the mute indicator during the release delay. Fix — option A (in KeybindStore.ts): Don't set pushToTalkHeld = false in handlePushToTalkRelease. Let applyPushToTalkHold(false) (called after the delay) handle it via setPushToTalkHeld(false): tshandlePushToTalkRelease(nowMs: number = Date.now()): boolean { const pressDuration = nowMs - this.pushToTalkPressTime; if (this.pushToTalkLatching && pressDuration < LATCH_TAP_THRESHOLD_MS && !this.pushToTalkLatched) { runInAction(() => { this.pushToTalkLatched = true; }); return false; } // Don't set pushToTalkHeld = false here — let the delayed // applyPushToTalkHold(false) do it so UI stays consistent // during the release delay window. return true; } This works because applyPushToTalkHold in VoiceAudioManager.ts line 84 already calls KeybindStore.setPushToTalkHeld(held), so the state will be set to false when the delay fires. Bug 4 (MEDIUM): No UI to enable global PTT on desktop The PTT keybind defaults to global: false. VoiceTab preserves whatever global was previously set (global: pttKeybind.combo.global). refreshGlobalKeyHook in KeybindManager line 330 requires pttKeybind.combo.global to be true to start the system-wide key hook. But there's no toggle in VoiceTab to flip global to true. This means even on the native desktop app, PTT is local-only (only works when the app has focus) unless something else sets global: true. I don't see that happening anywhere. Fix in VoiceTab.tsx: Add a global shortcut toggle for desktop users, or auto-enable global for PTT on desktop: tsx{isPushToTalk && isNativeDesktop && !isPttLimited && ( <Switch label={<Trans>System-wide Push-to-Talk</Trans>} description={<Trans>Works even when Fluxer is not focused</Trans>} value={pttKeybind.combo.global ?? false} onChange={(value) => { KeybindStore.setKeybind('push_to_talk', { ...pttKeybind.combo, global: value, }); }} /> )} Alternatively, if you want it on by default for desktop, set it in the onChange when recording a new key: tsxonChange={(combo) => { KeybindStore.setKeybind('push_to_talk', { ...combo, global: isNativeDesktop && !isPttLimited, // auto-enable on capable desktops }); }} Bug 5 (LOW): PTT sound effects exist but are never played You have ptt-active.mp3 and ptt-inactive.mp3 in the sounds directory, but the PTT handler in KeybindManager.ts (line 554-573) never plays them. If you want audio feedback: ts// In the push_to_talk handler in KeybindManager.ts this.register('push_to_talk', ({type}) => { if (type === 'press') { // ... existing press logic ... if (shouldUnmute) { SoundActionCreators.playSound(SoundType.PttActive); // add this MediaEngineStore.applyPushToTalkHold(true); } } else { const shouldMute = KeybindStore.handlePushToTalkRelease(); if (shouldMute) { const delay = KeybindStore.pushToTalkReleaseDelay; this.pttReleaseTimer = setTimeout(() => { this.pttReleaseTimer = null; SoundActionCreators.playSound(SoundType.PttInactive); // add this MediaEngineStore.applyPushToTalkHold(false); }, delay); } } }); (You'd need to add PttActive/PttInactive to the SoundType enum and register the sound files.)
Edited by Rex
Changes
The disabled flag conflates two things: "can't set ANY key" and "can't use global shortcuts." On web, local (in-focus) PTT should still work — the warning banner already tells users it only works when the tab has focus. The recorder should be enabled.Fix in VoiceTab.tsx:Removed: tsx// Line 251: only disable the recorder when PTT is entirely impossible,Added: ```tsxAdded: // Line 251: only disable the recorder when PTT is entirely impossible,// not merely limited. On web, local PTT works fine.<KeybindRecorder action="push_to_talk" value={pttKeybind.combo} defaultValue={defaultPttCombo} disabled={false} // ← always allow key recording onChange={(combo) => { KeybindStore.setKeybind('push_to_talk', { ...combo, global: isNativeDesktop ? pttKeybind.combo.global : false, }); }} // .../>Added: ```Bug 2 (HIGH): getMuteReason always returns 'self' instead of 'push_to_talk'
Show
found a few bugs with the PTT and here are some potential fixes for the issues: Bug 1 (CRITICAL): KeybindRecorder is disabled on web browsers — users can never set a PTT key In VoiceTab.tsx line 88 and 251:
const isPttLimited = !isNativeDesktop || (isNativeMac && !inputMonitoringGranted);
// ...
<KeybindRecorder ... disabled={isPttLimited} />
On a web browser, !isNativeDesktop is true, so isPttLimited is always true, and the KeybindRecorder is disabled. The user can switch to PTT mode (the radio group still works), but they physically cannot record a keybind. Combined with the fact that the default PTT combo is {key: '', enabled: false, global: false} (an empty key), PTT is completely non-functional on web browsers because no key is ever bound. The disabled flag conflates two things: "can't set ANY key" and "can't use global shortcuts." On web, local (in-focus) PTT should still work — the warning banner already tells users it only works when the tab has focus. The recorder should be enabled. Fix in VoiceTab.tsx:
// Line 251: only disable the recorder when PTT is entirely impossible,
// not merely limited. On web, local PTT works fine.
<KeybindRecorder
  action="push_to_talk"
  value={pttKeybind.combo}
  defaultValue={defaultPttCombo}
  disabled={false}  // ← always allow key recording
  onChange={(combo) => {
    KeybindStore.setKeybind('push_to_talk', {
      ...combo,
      global: isNativeDesktop ? pttKeybind.combo.global : false,
    });
  }}
  // ...
/>
Bug 2 (HIGH): getMuteReason always returns 'self' instead of 'push_to_talk' In VoiceAudioManager.ts line 115-123: tsexport function getMuteReason(voiceState: VoiceState | null) { const selfMuted = voiceState?.self_mute ?? LocalVoiceStateStore.getSelfMute(); if (KeybindStore.isPushToTalkEffective() && KeybindStore.isPushToTalkMuted(selfMuted)) return 'push_to_talk'; if (selfMuted) return 'self'; return null; } Trace the logic: when PTT mutes the user, applyPushToTalkHold(false) → applyLocalMuteState(true, ...) → LocalVoiceStateStore.updateSelfMute(true) → syncVoiceState({self_mute: true}). So selfMuted becomes true. Then isPushToTalkMuted(true) hits line 567 in KeybindStore: if (userMuted) return false — because the parameter is intended to mean "user manually muted," but it's being passed the raw self_mute which includes PTT-induced muting. Result: mute reason is always 'self', never 'push_to_talk'. The UI shows "Unmute" instead of "Push-to-talk enabled — hold [key] to speak." Fix in VoiceAudioManager.ts: tsexport function getMuteReason(voiceState: VoiceState | null) { const isGuildMuted = voiceState?.mute ?? false; if (isGuildMuted) return 'guild'; const selfMuted = voiceState?.self_mute ?? LocalVoiceStateStore.getSelfMute(); // Use the manual-mute flag, not raw self_mute, because PTT also sets self_mute const userManuallyMuted = LocalVoiceStateStore.getHasUserSetMute() && LocalVoiceStateStore.getSelfMute(); if (KeybindStore.isPushToTalkEffective() && KeybindStore.isPushToTalkMuted(userManuallyMuted)) return 'push_to_talk'; if (selfMuted) return 'self'; return null; } The hasUserSetMute flag in LocalVoiceStateStore is exactly the distinction needed — it's only set to true when the user explicitly toggles mute via toggleSelfMute(), never when PTT calls updateSelfMute(). Bug 3 (MEDIUM): pushToTalkHeld is set to false immediately on key release, before the release delay fires In KeybindStore.ts line 613-627, handlePushToTalkRelease sets this.pushToTalkHeld = false immediately. But in KeybindManager.ts line 566-572, applyPushToTalkHold(false) (which actually mutes the mic) runs after pushToTalkReleaseDelay ms. During that delay window, the MobX-observable pushToTalkHeld is false while the mic is still live. Any UI reading isPushToTalkMuted() sees the user as PTT-muted when they're actually still transmitting. This causes a visual flicker in the mute indicator during the release delay. Fix — option A (in KeybindStore.ts): Don't set pushToTalkHeld = false in handlePushToTalkRelease. Let applyPushToTalkHold(false) (called after the delay) handle it via setPushToTalkHeld(false): tshandlePushToTalkRelease(nowMs: number = Date.now()): boolean { const pressDuration = nowMs - this.pushToTalkPressTime; if (this.pushToTalkLatching && pressDuration < LATCH_TAP_THRESHOLD_MS && !this.pushToTalkLatched) { runInAction(() => { this.pushToTalkLatched = true; }); return false; } // Don't set pushToTalkHeld = false here — let the delayed // applyPushToTalkHold(false) do it so UI stays consistent // during the release delay window. return true; } This works because applyPushToTalkHold in VoiceAudioManager.ts line 84 already calls KeybindStore.setPushToTalkHeld(held), so the state will be set to false when the delay fires. Bug 4 (MEDIUM): No UI to enable global PTT on desktop The PTT keybind defaults to global: false. VoiceTab preserves whatever global was previously set (global: pttKeybind.combo.global). refreshGlobalKeyHook in KeybindManager line 330 requires pttKeybind.combo.global to be true to start the system-wide key hook. But there's no toggle in VoiceTab to flip global to true. This means even on the native desktop app, PTT is local-only (only works when the app has focus) unless something else sets global: true. I don't see that happening anywhere. Fix in VoiceTab.tsx: Add a global shortcut toggle for desktop users, or auto-enable global for PTT on desktop: tsx{isPushToTalk && isNativeDesktop && !isPttLimited && ( <Switch label={<Trans>System-wide Push-to-Talk</Trans>} description={<Trans>Works even when Fluxer is not focused</Trans>} value={pttKeybind.combo.global ?? false} onChange={(value) => { KeybindStore.setKeybind('push_to_talk', { ...pttKeybind.combo, global: value, }); }} /> )} Alternatively, if you want it on by default for desktop, set it in the onChange when recording a new key: tsxonChange={(combo) => { KeybindStore.setKeybind('push_to_talk', { ...combo, global: isNativeDesktop && !isPttLimited, // auto-enable on capable desktops }); }} Bug 5 (LOW): PTT sound effects exist but are never played You have ptt-active.mp3 and ptt-inactive.mp3 in the sounds directory, but the PTT handler in KeybindManager.ts (line 554-573) never plays them. If you want audio feedback: ts// In the push_to_talk handler in KeybindManager.ts this.register('push_to_talk', ({type}) => { if (type === 'press') { // ... existing press logic ... if (shouldUnmute) { SoundActionCreators.playSound(SoundType.PttActive); // add this MediaEngineStore.applyPushToTalkHold(true); } } else { const shouldMute = KeybindStore.handlePushToTalkRelease(); if (shouldMute) { const delay = KeybindStore.pushToTalkReleaseDelay; this.pttReleaseTimer = setTimeout(() => { this.pttReleaseTimer = null; SoundActionCreators.playSound(SoundType.PttInactive); // add this MediaEngineStore.applyPushToTalkHold(false); }, delay); } } }); (You'd need to add PttActive/PttInactive to the SoundType enum and register the sound files.)
Edited by Rex
Changes
Bug 1 (CRITICAL): KeybindRecorder is disabled on web browsers — users can never set a PTT keyIn VoiceTab.tsx line 88 and 251:Removed: tsxconst isPttLimited = !isNativeDesktop || (isNativeMac && !inputMonitoringGranted);Added: ```tsxAdded: const isPttLimited = !isNativeDesktop || (isNativeMac && !inputMonitoringGranted);// ...<KeybindRecorder ... disabled={isPttLimited} />Added: ```On a web browser, !isNativeDesktop is true, so isPttLimited is always true, and the KeybindRecorder is disabled. The user can switch to PTT mode (the radio group still works), but they physically cannot record a keybind. Combined with the fact that the default PTT combo is {key: '', enabled: false, global: false} (an empty key), PTT is completely non-functional on web browsers because no key is ever bound.The disabled flag conflates two things: "can't set ANY key" and "can't use global shortcuts." On web, local (in-focus) PTT should still work — the warning banner already tells users it only works when the tab has focus. The recorder should be enabled.
Show
found a few bugs with the PTT and here are some potential fixes for the issues: Bug 1 (CRITICAL): KeybindRecorder is disabled on web browsers — users can never set a PTT key In VoiceTab.tsx line 88 and 251:
const isPttLimited = !isNativeDesktop || (isNativeMac && !inputMonitoringGranted);
// ...
<KeybindRecorder ... disabled={isPttLimited} />
On a web browser, !isNativeDesktop is true, so isPttLimited is always true, and the KeybindRecorder is disabled. The user can switch to PTT mode (the radio group still works), but they physically cannot record a keybind. Combined with the fact that the default PTT combo is {key: '', enabled: false, global: false} (an empty key), PTT is completely non-functional on web browsers because no key is ever bound. The disabled flag conflates two things: "can't set ANY key" and "can't use global shortcuts." On web, local (in-focus) PTT should still work — the warning banner already tells users it only works when the tab has focus. The recorder should be enabled. Fix in VoiceTab.tsx: tsx// Line 251: only disable the recorder when PTT is entirely impossible, // not merely limited. On web, local PTT works fine. <KeybindRecorder action="push_to_talk" value={pttKeybind.combo} defaultValue={defaultPttCombo} disabled={false} // ← always allow key recording onChange={(combo) => { KeybindStore.setKeybind('push_to_talk', { ...combo, global: isNativeDesktop ? pttKeybind.combo.global : false, }); }} // ... /> Bug 2 (HIGH): getMuteReason always returns 'self' instead of 'push_to_talk' In VoiceAudioManager.ts line 115-123: tsexport function getMuteReason(voiceState: VoiceState | null) { const selfMuted = voiceState?.self_mute ?? LocalVoiceStateStore.getSelfMute(); if (KeybindStore.isPushToTalkEffective() && KeybindStore.isPushToTalkMuted(selfMuted)) return 'push_to_talk'; if (selfMuted) return 'self'; return null; } Trace the logic: when PTT mutes the user, applyPushToTalkHold(false) → applyLocalMuteState(true, ...) → LocalVoiceStateStore.updateSelfMute(true) → syncVoiceState({self_mute: true}). So selfMuted becomes true. Then isPushToTalkMuted(true) hits line 567 in KeybindStore: if (userMuted) return false — because the parameter is intended to mean "user manually muted," but it's being passed the raw self_mute which includes PTT-induced muting. Result: mute reason is always 'self', never 'push_to_talk'. The UI shows "Unmute" instead of "Push-to-talk enabled — hold [key] to speak." Fix in VoiceAudioManager.ts: tsexport function getMuteReason(voiceState: VoiceState | null) { const isGuildMuted = voiceState?.mute ?? false; if (isGuildMuted) return 'guild'; const selfMuted = voiceState?.self_mute ?? LocalVoiceStateStore.getSelfMute(); // Use the manual-mute flag, not raw self_mute, because PTT also sets self_mute const userManuallyMuted = LocalVoiceStateStore.getHasUserSetMute() && LocalVoiceStateStore.getSelfMute(); if (KeybindStore.isPushToTalkEffective() && KeybindStore.isPushToTalkMuted(userManuallyMuted)) return 'push_to_talk'; if (selfMuted) return 'self'; return null; } The hasUserSetMute flag in LocalVoiceStateStore is exactly the distinction needed — it's only set to true when the user explicitly toggles mute via toggleSelfMute(), never when PTT calls updateSelfMute(). Bug 3 (MEDIUM): pushToTalkHeld is set to false immediately on key release, before the release delay fires In KeybindStore.ts line 613-627, handlePushToTalkRelease sets this.pushToTalkHeld = false immediately. But in KeybindManager.ts line 566-572, applyPushToTalkHold(false) (which actually mutes the mic) runs after pushToTalkReleaseDelay ms. During that delay window, the MobX-observable pushToTalkHeld is false while the mic is still live. Any UI reading isPushToTalkMuted() sees the user as PTT-muted when they're actually still transmitting. This causes a visual flicker in the mute indicator during the release delay. Fix — option A (in KeybindStore.ts): Don't set pushToTalkHeld = false in handlePushToTalkRelease. Let applyPushToTalkHold(false) (called after the delay) handle it via setPushToTalkHeld(false): tshandlePushToTalkRelease(nowMs: number = Date.now()): boolean { const pressDuration = nowMs - this.pushToTalkPressTime; if (this.pushToTalkLatching && pressDuration < LATCH_TAP_THRESHOLD_MS && !this.pushToTalkLatched) { runInAction(() => { this.pushToTalkLatched = true; }); return false; } // Don't set pushToTalkHeld = false here — let the delayed // applyPushToTalkHold(false) do it so UI stays consistent // during the release delay window. return true; } This works because applyPushToTalkHold in VoiceAudioManager.ts line 84 already calls KeybindStore.setPushToTalkHeld(held), so the state will be set to false when the delay fires. Bug 4 (MEDIUM): No UI to enable global PTT on desktop The PTT keybind defaults to global: false. VoiceTab preserves whatever global was previously set (global: pttKeybind.combo.global). refreshGlobalKeyHook in KeybindManager line 330 requires pttKeybind.combo.global to be true to start the system-wide key hook. But there's no toggle in VoiceTab to flip global to true. This means even on the native desktop app, PTT is local-only (only works when the app has focus) unless something else sets global: true. I don't see that happening anywhere. Fix in VoiceTab.tsx: Add a global shortcut toggle for desktop users, or auto-enable global for PTT on desktop: tsx{isPushToTalk && isNativeDesktop && !isPttLimited && ( <Switch label={<Trans>System-wide Push-to-Talk</Trans>} description={<Trans>Works even when Fluxer is not focused</Trans>} value={pttKeybind.combo.global ?? false} onChange={(value) => { KeybindStore.setKeybind('push_to_talk', { ...pttKeybind.combo, global: value, }); }} /> )} Alternatively, if you want it on by default for desktop, set it in the onChange when recording a new key: tsxonChange={(combo) => { KeybindStore.setKeybind('push_to_talk', { ...combo, global: isNativeDesktop && !isPttLimited, // auto-enable on capable desktops }); }} Bug 5 (LOW): PTT sound effects exist but are never played You have ptt-active.mp3 and ptt-inactive.mp3 in the sounds directory, but the PTT handler in KeybindManager.ts (line 554-573) never plays them. If you want audio feedback: ts// In the push_to_talk handler in KeybindManager.ts this.register('push_to_talk', ({type}) => { if (type === 'press') { // ... existing press logic ... if (shouldUnmute) { SoundActionCreators.playSound(SoundType.PttActive); // add this MediaEngineStore.applyPushToTalkHold(true); } } else { const shouldMute = KeybindStore.handlePushToTalkRelease(); if (shouldMute) { const delay = KeybindStore.pushToTalkReleaseDelay; this.pttReleaseTimer = setTimeout(() => { this.pttReleaseTimer = null; SoundActionCreators.playSound(SoundType.PttInactive); // add this MediaEngineStore.applyPushToTalkHold(false); }, delay); } } }); (You'd need to add PttActive/PttInactive to the SoundType enum and register the sound files.)
Original by Rex
Show
found a few bugs with the PTT and here are some potential fixes for the issues: Bug 1 (CRITICAL): KeybindRecorder is disabled on web browsers — users can never set a PTT key In VoiceTab.tsx line 88 and 251: tsxconst isPttLimited = !isNativeDesktop || (isNativeMac && !inputMonitoringGranted); // ... <KeybindRecorder ... disabled={isPttLimited} /> On a web browser, !isNativeDesktop is true, so isPttLimited is always true, and the KeybindRecorder is disabled. The user can switch to PTT mode (the radio group still works), but they physically cannot record a keybind. Combined with the fact that the default PTT combo is {key: '', enabled: false, global: false} (an empty key), PTT is completely non-functional on web browsers because no key is ever bound. The disabled flag conflates two things: "can't set ANY key" and "can't use global shortcuts." On web, local (in-focus) PTT should still work — the warning banner already tells users it only works when the tab has focus. The recorder should be enabled. Fix in VoiceTab.tsx: tsx// Line 251: only disable the recorder when PTT is entirely impossible, // not merely limited. On web, local PTT works fine. <KeybindRecorder action="push_to_talk" value={pttKeybind.combo} defaultValue={defaultPttCombo} disabled={false} // ← always allow key recording onChange={(combo) => { KeybindStore.setKeybind('push_to_talk', { ...combo, global: isNativeDesktop ? pttKeybind.combo.global : false, }); }} // ... /> Bug 2 (HIGH): getMuteReason always returns 'self' instead of 'push_to_talk' In VoiceAudioManager.ts line 115-123: tsexport function getMuteReason(voiceState: VoiceState | null) { const selfMuted = voiceState?.self_mute ?? LocalVoiceStateStore.getSelfMute(); if (KeybindStore.isPushToTalkEffective() && KeybindStore.isPushToTalkMuted(selfMuted)) return 'push_to_talk'; if (selfMuted) return 'self'; return null; } Trace the logic: when PTT mutes the user, applyPushToTalkHold(false) → applyLocalMuteState(true, ...) → LocalVoiceStateStore.updateSelfMute(true) → syncVoiceState({self_mute: true}). So selfMuted becomes true. Then isPushToTalkMuted(true) hits line 567 in KeybindStore: if (userMuted) return false — because the parameter is intended to mean "user manually muted," but it's being passed the raw self_mute which includes PTT-induced muting. Result: mute reason is always 'self', never 'push_to_talk'. The UI shows "Unmute" instead of "Push-to-talk enabled — hold [key] to speak." Fix in VoiceAudioManager.ts: tsexport function getMuteReason(voiceState: VoiceState | null) { const isGuildMuted = voiceState?.mute ?? false; if (isGuildMuted) return 'guild'; const selfMuted = voiceState?.self_mute ?? LocalVoiceStateStore.getSelfMute(); // Use the manual-mute flag, not raw self_mute, because PTT also sets self_mute const userManuallyMuted = LocalVoiceStateStore.getHasUserSetMute() && LocalVoiceStateStore.getSelfMute(); if (KeybindStore.isPushToTalkEffective() && KeybindStore.isPushToTalkMuted(userManuallyMuted)) return 'push_to_talk'; if (selfMuted) return 'self'; return null; } The hasUserSetMute flag in LocalVoiceStateStore is exactly the distinction needed — it's only set to true when the user explicitly toggles mute via toggleSelfMute(), never when PTT calls updateSelfMute(). Bug 3 (MEDIUM): pushToTalkHeld is set to false immediately on key release, before the release delay fires In KeybindStore.ts line 613-627, handlePushToTalkRelease sets this.pushToTalkHeld = false immediately. But in KeybindManager.ts line 566-572, applyPushToTalkHold(false) (which actually mutes the mic) runs after pushToTalkReleaseDelay ms. During that delay window, the MobX-observable pushToTalkHeld is false while the mic is still live. Any UI reading isPushToTalkMuted() sees the user as PTT-muted when they're actually still transmitting. This causes a visual flicker in the mute indicator during the release delay. Fix — option A (in KeybindStore.ts): Don't set pushToTalkHeld = false in handlePushToTalkRelease. Let applyPushToTalkHold(false) (called after the delay) handle it via setPushToTalkHeld(false): tshandlePushToTalkRelease(nowMs: number = Date.now()): boolean { const pressDuration = nowMs - this.pushToTalkPressTime; if (this.pushToTalkLatching && pressDuration < LATCH_TAP_THRESHOLD_MS && !this.pushToTalkLatched) { runInAction(() => { this.pushToTalkLatched = true; }); return false; } // Don't set pushToTalkHeld = false here — let the delayed // applyPushToTalkHold(false) do it so UI stays consistent // during the release delay window. return true; } This works because applyPushToTalkHold in VoiceAudioManager.ts line 84 already calls KeybindStore.setPushToTalkHeld(held), so the state will be set to false when the delay fires. Bug 4 (MEDIUM): No UI to enable global PTT on desktop The PTT keybind defaults to global: false. VoiceTab preserves whatever global was previously set (global: pttKeybind.combo.global). refreshGlobalKeyHook in KeybindManager line 330 requires pttKeybind.combo.global to be true to start the system-wide key hook. But there's no toggle in VoiceTab to flip global to true. This means even on the native desktop app, PTT is local-only (only works when the app has focus) unless something else sets global: true. I don't see that happening anywhere. Fix in VoiceTab.tsx: Add a global shortcut toggle for desktop users, or auto-enable global for PTT on desktop: tsx{isPushToTalk && isNativeDesktop && !isPttLimited && ( <Switch label={<Trans>System-wide Push-to-Talk</Trans>} description={<Trans>Works even when Fluxer is not focused</Trans>} value={pttKeybind.combo.global ?? false} onChange={(value) => { KeybindStore.setKeybind('push_to_talk', { ...pttKeybind.combo, global: value, }); }} /> )} Alternatively, if you want it on by default for desktop, set it in the onChange when recording a new key: tsxonChange={(combo) => { KeybindStore.setKeybind('push_to_talk', { ...combo, global: isNativeDesktop && !isPttLimited, // auto-enable on capable desktops }); }} Bug 5 (LOW): PTT sound effects exist but are never played You have ptt-active.mp3 and ptt-inactive.mp3 in the sounds directory, but the PTT handler in KeybindManager.ts (line 554-573) never plays them. If you want audio feedback: ts// In the push_to_talk handler in KeybindManager.ts this.register('push_to_talk', ({type}) => { if (type === 'press') { // ... existing press logic ... if (shouldUnmute) { SoundActionCreators.playSound(SoundType.PttActive); // add this MediaEngineStore.applyPushToTalkHold(true); } } else { const shouldMute = KeybindStore.handlePushToTalkRelease(); if (shouldMute) { const delay = KeybindStore.pushToTalkReleaseDelay; this.pttReleaseTimer = setTimeout(() => { this.pttReleaseTimer = null; SoundActionCreators.playSound(SoundType.PttInactive); // add this MediaEngineStore.applyPushToTalkHold(false); }, delay); } } }); (You'd need to add PttActive/PttInactive to the SoundType enum and register the sound files.)