optimize permission section

This commit is contained in:
Fabian Freund
2026-01-19 11:12:39 +01:00
parent 8c7fe372df
commit 1dc609f4e4
2 changed files with 90 additions and 93 deletions
@@ -0,0 +1,47 @@
import 'package:flutter/material.dart';
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:weblibre/features/geckoview/features/browser/domain/entities/site_permissions.dart';
/// Permission types with their display configuration
enum PermissionType {
camera(Icons.videocam, 'Camera'),
microphone(Icons.mic, 'Microphone'),
location(Icons.location_on, 'Location'),
notification(Icons.notifications, 'Notifications'),
persistentStorage(Icons.storage, 'Persistent Storage'),
crossOriginStorage(Icons.cookie, 'Cross-Origin Storage'),
mediaKeySystem(Icons.key, 'Media Key System (DRM)');
const PermissionType(this.icon, this.label);
final IconData icon;
final String label;
}
extension SitePermissionsGetter on SitePermissions? {
SitePermissionStatus? getStatus(PermissionType type) => switch (type) {
PermissionType.camera => this?.camera,
PermissionType.microphone => this?.microphone,
PermissionType.location => this?.location,
PermissionType.notification => this?.notification,
PermissionType.persistentStorage => this?.persistentStorage,
PermissionType.crossOriginStorage => this?.crossOriginStorageAccess,
PermissionType.mediaKeySystem => this?.mediaKeySystemAccess,
};
}
extension SitePermissionsUpdater on SitePermissionsWrapper {
SitePermissions withStatus(
PermissionType type,
SitePermissionStatus status,
) => switch (type) {
PermissionType.camera => copyWith.camera(status),
PermissionType.microphone => copyWith.microphone(status),
PermissionType.location => copyWith.location(status),
PermissionType.notification => copyWith.notification(status),
PermissionType.persistentStorage => copyWith.persistentStorage(status),
PermissionType.crossOriginStorage => copyWith.crossOriginStorageAccess(
status,
),
PermissionType.mediaKeySystem => copyWith.mediaKeySystemAccess(status),
};
}
@@ -22,6 +22,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart'; import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:weblibre/features/geckoview/domain/providers/tab_session.dart'; import 'package:weblibre/features/geckoview/domain/providers/tab_session.dart';
import 'package:weblibre/features/geckoview/features/browser/domain/entities/permission_type.dart';
import 'package:weblibre/features/geckoview/features/browser/domain/entities/site_permissions.dart'; import 'package:weblibre/features/geckoview/features/browser/domain/entities/site_permissions.dart';
import 'package:weblibre/features/geckoview/features/browser/domain/repositories/site_permissions.dart'; import 'package:weblibre/features/geckoview/features/browser/domain/repositories/site_permissions.dart';
@@ -75,81 +76,48 @@ class _PermissionsList extends HookConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final showAll = useState(false); final showAll = useState(false);
// Build list of permission entries with their current status // Memoize the update callback to avoid recreating closures
final allPermissions = [ final updatePermission = useCallback((
_PermissionEntry( SitePermissions Function(SitePermissionsWrapper) updater,
icon: Icons.videocam, ) async {
label: 'Camera', await ref
status: permissions?.camera, .read(
onChanged: (status) => _updatePermission( sitePermissionsRepositoryProvider(
ref, origin: origin,
(permissions) => permissions.copyWith.camera(status), isPrivate: isPrivate,
), ).notifier,
), )
_PermissionEntry( .updatePermission(updater);
icon: Icons.mic, await ref.read(selectedTabSessionProvider).reload();
label: 'Microphone', }, [origin, isPrivate]);
status: permissions?.microphone,
onChanged: (status) => _updatePermission( // Build permission entries - only recalculates when permissions change
ref, final allPermissions = useMemoized(
(permissions) => permissions.copyWith.microphone(status), () => PermissionType.values
), .map(
), (type) => _PermissionEntry(
_PermissionEntry( icon: type.icon,
icon: Icons.location_on, label: type.label,
label: 'Location', status: permissions.getStatus(type),
status: permissions?.location, onChanged: (status) =>
onChanged: (status) => _updatePermission( updatePermission((w) => w.withStatus(type, status)),
ref, ),
(permissions) => permissions.copyWith.location(status), )
), .toList(),
), [permissions, updatePermission],
_PermissionEntry( );
icon: Icons.notifications,
label: 'Notifications',
status: permissions?.notification,
onChanged: (status) => _updatePermission(
ref,
(permissions) => permissions.copyWith.notification(status),
),
),
_PermissionEntry(
icon: Icons.storage,
label: 'Persistent Storage',
status: permissions?.persistentStorage,
onChanged: (status) => _updatePermission(
ref,
(permissions) => permissions.copyWith.persistentStorage(status),
),
),
_PermissionEntry(
icon: Icons.cookie,
label: 'Cross-Origin Storage',
status: permissions?.crossOriginStorageAccess,
onChanged: (status) => _updatePermission(
ref,
(permissions) =>
permissions.copyWith.crossOriginStorageAccess(status),
),
),
_PermissionEntry(
icon: Icons.key,
label: 'Media Key System (DRM)',
status: permissions?.mediaKeySystemAccess,
onChanged: (status) => _updatePermission(
ref,
(permissions) => permissions.copyWith.mediaKeySystemAccess(status),
),
),
];
// Filter to only show permissions that have been explicitly set (not noDecision) // Filter to only show permissions that have been explicitly set (not noDecision)
final setPermissions = allPermissions // Only recalculates when allPermissions changes
.where( final setPermissions = useMemoized(
(p) => () => allPermissions
p.status != null && p.status != SitePermissionStatus.noDecision, .where(
) (p) =>
.toList(); p.status != null && p.status != SitePermissionStatus.noDecision,
)
.toList(),
[allPermissions],
);
final permissionsToShow = showAll.value ? allPermissions : setPermissions; final permissionsToShow = showAll.value ? allPermissions : setPermissions;
final hiddenCount = allPermissions.length - setPermissions.length; final hiddenCount = allPermissions.length - setPermissions.length;
@@ -202,9 +170,8 @@ class _PermissionsList extends HookConsumerWidget {
_AutoplayTile( _AutoplayTile(
audibleStatus: permissions?.autoplayAudible, audibleStatus: permissions?.autoplayAudible,
inaudibleStatus: permissions?.autoplayInaudible, inaudibleStatus: permissions?.autoplayInaudible,
onChanged: (audible, inaudible) => _updatePermission( onChanged: (audible, inaudible) => updatePermission(
ref, (p) => p.copyWith(
(permissions) => permissions.copyWith(
autoplayAudible: audible, autoplayAudible: audible,
autoplayInaudible: inaudible, autoplayInaudible: inaudible,
), ),
@@ -213,23 +180,6 @@ class _PermissionsList extends HookConsumerWidget {
], ],
); );
} }
Future<void> _updatePermission(
WidgetRef ref,
SitePermissions Function(SitePermissionsWrapper) updater,
) async {
await ref
.read(
sitePermissionsRepositoryProvider(
origin: origin,
isPrivate: isPrivate,
).notifier,
)
.updatePermission(updater);
// Reload the tab to apply changes
await ref.read(selectedTabSessionProvider).reload();
}
} }
class _PermissionEntry { class _PermissionEntry {