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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user