feat: add permission-denied dialog for macOS network/mic access

When macOS denies network access (PermissionDenied), show a localized
dialog explaining how to grant permission in System Settings, with
an 'Open System Settings' button that opens directly to the Local
Network privacy pane.

Also adds author info (Edison Jwa) to About dialog and moves
diagnostics button from AppBar into About dialog.
This commit is contained in:
Edison Jwa
2026-05-17 21:57:56 +09:00
parent b97cc9589b
commit 4ecea09bbc
8 changed files with 163 additions and 16 deletions
+52 -7
View File
@@ -11,7 +11,7 @@
import 'dart:async';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'dart:io' show Platform;
import 'dart:io' show Platform, Process;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
@@ -429,13 +429,54 @@ class _BetaHomeState extends State<_BetaHome> {
});
} catch (e) {
if (!mounted) return;
final errorStr = e.toString();
if (errorStr.contains('PermissionDenied') ||
errorStr.contains('Operation not permitted')) {
_showPermissionDeniedDialog(errorStr);
}
setState(() {
_phase = _Phase.idle;
_error = e.toString();
_error = errorStr;
});
}
}
void _showPermissionDeniedDialog(String rawError) {
final l10n = AppL10n.of(context);
final isNetwork =
rawError.contains('9987') || rawError.contains('connect');
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title:
Text(isNetwork ? l10n.networkPermissionTitle : l10n.permissionDenied),
content: Text(
isNetwork ? l10n.networkPermissionBody : l10n.microphonePermissionBody),
actions: [
if (Platform.isMacOS)
TextButton(
onPressed: () {
Navigator.pop(ctx);
try {
Process.run(
'open',
[
'x-apple.systempreferences:com.apple.preference.security?Privacy_LocalNetwork',
],
);
} catch (_) {}
},
child: Text(l10n.networkPermissionOpenSettings),
),
TextButton(
onPressed: () => Navigator.pop(ctx),
child: Text(MaterialLocalizations.of(context).okButtonLabel),
),
],
),
);
}
// ignore: unused_element
Future<void> _setPtt(bool active) async {
try {
@@ -853,6 +894,8 @@ class _BetaHomeState extends State<_BetaHome> {
l10n.aboutVersion(_kAppVersion),
style: theme.textTheme.bodySmall,
),
const SizedBox(height: 4),
Text(l10n.aboutAuthor, style: theme.textTheme.bodySmall),
const SizedBox(height: 16),
Text(l10n.aboutNonAffiliation, style: theme.textTheme.bodyMedium),
const SizedBox(height: 12),
@@ -870,6 +913,13 @@ class _BetaHomeState extends State<_BetaHome> {
),
),
actions: [
TextButton(
onPressed: () async {
Navigator.of(ctx).pop();
await _onShowDiagnostics(context);
},
child: Text(l10n.diagnosticsAction),
),
TextButton(
onPressed: () => Navigator.of(ctx).pop(),
child: Text(l10n.closeAction),
@@ -1001,11 +1051,6 @@ class _BetaHomeState extends State<_BetaHome> {
icon: const Icon(Icons.info_outline),
onPressed: () => _onShowAbout(context),
),
IconButton(
tooltip: l10n.diagnosticsAction,
icon: const Icon(Icons.bug_report_outlined),
onPressed: () => _onShowDiagnostics(context),
),
if (_phase == _Phase.connected) ...[
// The previous Refresh action (Icons.refresh + _onRefresh)
// was removed in v1.0.0-rc.8 — the snapshot stream the