update permission shield behaviour
This commit is contained in:
+59
-39
@@ -27,13 +27,26 @@ import 'package:weblibre/features/geckoview/features/tabs/data/entities/tab_mode
|
||||
|
||||
part 'site_settings_badge_provider.g.dart';
|
||||
|
||||
/// Provider that determines whether to show the site settings badge on the tab icon.
|
||||
/// Returns true if any site-specific setting has been altered from defaults.
|
||||
/// Indicates whether the site settings badge should be shown on the tab icon
|
||||
/// and, if so, which direction settings have been changed.
|
||||
enum SiteSettingsBadgeState {
|
||||
/// No per-site settings differ from defaults.
|
||||
hidden,
|
||||
|
||||
/// At least one per-site setting is stricter than the default.
|
||||
improved,
|
||||
|
||||
/// At least one per-site setting is looser than the default.
|
||||
/// Tracking-protection exceptions also count as weakened.
|
||||
weakened,
|
||||
}
|
||||
|
||||
/// Provider that determines the site settings badge state on the tab icon.
|
||||
@Riverpod()
|
||||
Future<bool> showSiteSettingsBadge(Ref ref) async {
|
||||
Future<SiteSettingsBadgeState> showSiteSettingsBadge(Ref ref) async {
|
||||
final tabState = ref.watch(selectedTabStateProvider);
|
||||
if (tabState == null) {
|
||||
return false;
|
||||
return SiteSettingsBadgeState.hidden;
|
||||
}
|
||||
|
||||
// Check tracking protection exception
|
||||
@@ -42,7 +55,7 @@ Future<bool> showSiteSettingsBadge(Ref ref) async {
|
||||
);
|
||||
|
||||
if (!tabState.url.hasScheme || !tabState.url.isHttpOrHttps) {
|
||||
return false;
|
||||
return SiteSettingsBadgeState.hidden;
|
||||
}
|
||||
|
||||
// Watch site permissions
|
||||
@@ -54,27 +67,24 @@ Future<bool> showSiteSettingsBadge(Ref ref) async {
|
||||
);
|
||||
|
||||
if (hasTrackingException) {
|
||||
return true;
|
||||
}
|
||||
// Check for altered permissions
|
||||
if (_hasAlteredPermissions(permissions)) {
|
||||
return true;
|
||||
}
|
||||
// Check for altered autoplay settings
|
||||
if (_hasAlteredAutoplay(permissions)) {
|
||||
return true;
|
||||
return SiteSettingsBadgeState.weakened;
|
||||
}
|
||||
|
||||
return false;
|
||||
final badgeState = _evaluateBadgeState(permissions);
|
||||
return badgeState;
|
||||
}
|
||||
|
||||
/// Checks if any permission has been explicitly set to allowed or blocked
|
||||
bool _hasAlteredPermissions(SitePermissions? permissions) {
|
||||
/// Evaluates whether site permissions are default, improved, or weakened.
|
||||
/// Weakened takes precedence over improved.
|
||||
SiteSettingsBadgeState _evaluateBadgeState(SitePermissions? permissions) {
|
||||
if (permissions == null) {
|
||||
return false;
|
||||
return SiteSettingsBadgeState.hidden;
|
||||
}
|
||||
|
||||
// Check all permission fields
|
||||
var hasImproved = false;
|
||||
var hasWeakened = false;
|
||||
|
||||
// Evaluate permission-type fields
|
||||
final permissionStatuses = [
|
||||
permissions.camera,
|
||||
permissions.microphone,
|
||||
@@ -87,30 +97,40 @@ bool _hasAlteredPermissions(SitePermissions? permissions) {
|
||||
permissions.localNetworkAccess,
|
||||
];
|
||||
|
||||
return permissionStatuses.any(
|
||||
(status) =>
|
||||
status != null &&
|
||||
(status == SitePermissionStatus.allowed ||
|
||||
status == SitePermissionStatus.blocked),
|
||||
);
|
||||
}
|
||||
|
||||
/// Checks if autoplay settings differ from defaults
|
||||
/// Default: autoplayAudible = blocked (null), autoplayInaudible = allowed (null)
|
||||
bool _hasAlteredAutoplay(SitePermissions? permissions) {
|
||||
if (permissions == null) {
|
||||
return false;
|
||||
for (final status in permissionStatuses) {
|
||||
if (status == null) {
|
||||
continue;
|
||||
}
|
||||
if (status == SitePermissionStatus.blocked) {
|
||||
hasImproved = true;
|
||||
} else if (status == SitePermissionStatus.allowed) {
|
||||
hasWeakened = true;
|
||||
}
|
||||
// noDecision is the default -> no effect
|
||||
}
|
||||
|
||||
// Evaluate autoplay audible
|
||||
final audible = permissions.autoplayAudible;
|
||||
if (audible != null && audible != AutoplayStatus.blocked) {
|
||||
hasWeakened = true;
|
||||
}
|
||||
|
||||
// Evaluate autoplay inaudible
|
||||
final inaudible = permissions.autoplayInaudible;
|
||||
if (inaudible != null) {
|
||||
if (inaudible == AutoplayStatus.blocked) {
|
||||
hasImproved = true;
|
||||
} else if (inaudible != AutoplayStatus.allowed) {
|
||||
hasWeakened = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if audible differs from default (null or blocked)
|
||||
final audibleAltered = audible != null && audible != AutoplayStatus.blocked;
|
||||
if (hasWeakened) {
|
||||
return SiteSettingsBadgeState.weakened;
|
||||
}
|
||||
if (hasImproved) {
|
||||
return SiteSettingsBadgeState.improved;
|
||||
}
|
||||
|
||||
// Check if inaudible differs from default (null or allowed)
|
||||
final inaudibleAltered =
|
||||
inaudible != null && inaudible != AutoplayStatus.allowed;
|
||||
|
||||
return audibleAltered || inaudibleAltered;
|
||||
return SiteSettingsBadgeState.hidden;
|
||||
}
|
||||
|
||||
+17
-12
@@ -8,20 +8,24 @@ part of 'site_settings_badge_provider.dart';
|
||||
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
/// Provider that determines whether to show the site settings badge on the tab icon.
|
||||
/// Returns true if any site-specific setting has been altered from defaults.
|
||||
/// Provider that determines the site settings badge state on the tab icon.
|
||||
|
||||
@ProviderFor(showSiteSettingsBadge)
|
||||
final showSiteSettingsBadgeProvider = ShowSiteSettingsBadgeProvider._();
|
||||
|
||||
/// Provider that determines whether to show the site settings badge on the tab icon.
|
||||
/// Returns true if any site-specific setting has been altered from defaults.
|
||||
/// Provider that determines the site settings badge state on the tab icon.
|
||||
|
||||
final class ShowSiteSettingsBadgeProvider
|
||||
extends $FunctionalProvider<AsyncValue<bool>, bool, FutureOr<bool>>
|
||||
with $FutureModifier<bool>, $FutureProvider<bool> {
|
||||
/// Provider that determines whether to show the site settings badge on the tab icon.
|
||||
/// Returns true if any site-specific setting has been altered from defaults.
|
||||
extends
|
||||
$FunctionalProvider<
|
||||
AsyncValue<SiteSettingsBadgeState>,
|
||||
SiteSettingsBadgeState,
|
||||
FutureOr<SiteSettingsBadgeState>
|
||||
>
|
||||
with
|
||||
$FutureModifier<SiteSettingsBadgeState>,
|
||||
$FutureProvider<SiteSettingsBadgeState> {
|
||||
/// Provider that determines the site settings badge state on the tab icon.
|
||||
ShowSiteSettingsBadgeProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
@@ -38,14 +42,15 @@ final class ShowSiteSettingsBadgeProvider
|
||||
|
||||
@$internal
|
||||
@override
|
||||
$FutureProviderElement<bool> $createElement($ProviderPointer pointer) =>
|
||||
$FutureProviderElement(pointer);
|
||||
$FutureProviderElement<SiteSettingsBadgeState> $createElement(
|
||||
$ProviderPointer pointer,
|
||||
) => $FutureProviderElement(pointer);
|
||||
|
||||
@override
|
||||
FutureOr<bool> create(Ref ref) {
|
||||
FutureOr<SiteSettingsBadgeState> create(Ref ref) {
|
||||
return showSiteSettingsBadge(ref);
|
||||
}
|
||||
}
|
||||
|
||||
String _$showSiteSettingsBadgeHash() =>
|
||||
r'c68e3b37b6f25e02779123b3f642a58dc98ba3a7';
|
||||
r'fbfe409cdd6656d5cd26e377a68acde6ff4294fa';
|
||||
|
||||
+44
-30
@@ -46,8 +46,10 @@ class CompactAppBarTitle extends ConsumerWidget {
|
||||
final selectedTabType = ref.watch(selectedTabTypeProvider);
|
||||
final settings = ref.watch(generalSettingsWithDefaultsProvider);
|
||||
final isTabTuneledAsync = ref.watch(isTabTunneledProvider(tabState?.id));
|
||||
final showSiteSettingsBadge = ref.watch(
|
||||
showSiteSettingsBadgeProvider.select((value) => value.value == true),
|
||||
final siteSettingsBadgeState = ref.watch(
|
||||
showSiteSettingsBadgeProvider.select(
|
||||
(value) => value.value ?? SiteSettingsBadgeState.hidden,
|
||||
),
|
||||
);
|
||||
|
||||
if (tabState == null) {
|
||||
@@ -64,7 +66,7 @@ class CompactAppBarTitle extends ConsumerWidget {
|
||||
tabState: tabState,
|
||||
isTabTunneled:
|
||||
isTabTuneledAsync.hasValue && isTabTuneledAsync.value == true,
|
||||
showSiteSettingsBadge: showSiteSettingsBadge,
|
||||
siteSettingsBadgeState: siteSettingsBadgeState,
|
||||
longPressUrlCopy: settings.tabBarLongPressUrlCopy,
|
||||
onSiteSettingsTap: () {
|
||||
ref
|
||||
@@ -87,7 +89,7 @@ class CompactAppBarTitleView extends StatelessWidget {
|
||||
super.key,
|
||||
required this.tabState,
|
||||
required this.isTabTunneled,
|
||||
required this.showSiteSettingsBadge,
|
||||
required this.siteSettingsBadgeState,
|
||||
required this.onSiteSettingsTap,
|
||||
required this.onTitleTap,
|
||||
this.tabIcon,
|
||||
@@ -96,7 +98,7 @@ class CompactAppBarTitleView extends StatelessWidget {
|
||||
|
||||
final TabState tabState;
|
||||
final bool isTabTunneled;
|
||||
final bool showSiteSettingsBadge;
|
||||
final SiteSettingsBadgeState siteSettingsBadgeState;
|
||||
final VoidCallback onSiteSettingsTap;
|
||||
final VoidCallback onTitleTap;
|
||||
final Widget? tabIcon;
|
||||
@@ -117,17 +119,22 @@ class CompactAppBarTitleView extends StatelessWidget {
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
tabIcon ?? TabIcon(tabState: tabState, iconSize: 24),
|
||||
Positioned(
|
||||
top: -4,
|
||||
right: -4,
|
||||
child: Icon(
|
||||
MdiIcons.shieldHalfFull,
|
||||
size: 10,
|
||||
color: showSiteSettingsBadge
|
||||
? appColors.warningAmber
|
||||
: Colors.green,
|
||||
if (siteSettingsBadgeState != SiteSettingsBadgeState.hidden)
|
||||
Positioned(
|
||||
top: -4,
|
||||
right: -4,
|
||||
child: Icon(
|
||||
siteSettingsBadgeState == SiteSettingsBadgeState.improved
|
||||
? MdiIcons.shield
|
||||
: MdiIcons.shieldAlert,
|
||||
size: 10,
|
||||
color:
|
||||
siteSettingsBadgeState ==
|
||||
SiteSettingsBadgeState.improved
|
||||
? Colors.green
|
||||
: appColors.warningAmber,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -202,8 +209,10 @@ class AppBarTitle extends ConsumerWidget {
|
||||
final selectedTabType = ref.watch(selectedTabTypeProvider);
|
||||
final settings = ref.watch(generalSettingsWithDefaultsProvider);
|
||||
final isTabTuneledAsync = ref.watch(isTabTunneledProvider(tabState?.id));
|
||||
final showSiteSettingsBadge = ref.watch(
|
||||
showSiteSettingsBadgeProvider.select((value) => value.value == true),
|
||||
final siteSettingsBadgeState = ref.watch(
|
||||
showSiteSettingsBadgeProvider.select(
|
||||
(value) => value.value ?? SiteSettingsBadgeState.hidden,
|
||||
),
|
||||
);
|
||||
|
||||
if (tabState == null) {
|
||||
@@ -220,7 +229,7 @@ class AppBarTitle extends ConsumerWidget {
|
||||
tabState: tabState,
|
||||
isTabTunneled:
|
||||
isTabTuneledAsync.hasValue && isTabTuneledAsync.value == true,
|
||||
showSiteSettingsBadge: showSiteSettingsBadge,
|
||||
siteSettingsBadgeState: siteSettingsBadgeState,
|
||||
longPressUrlCopy: settings.tabBarLongPressUrlCopy,
|
||||
onSiteSettingsTap: () {
|
||||
ref
|
||||
@@ -243,7 +252,7 @@ class AppBarTitleView extends StatelessWidget {
|
||||
super.key,
|
||||
required this.tabState,
|
||||
required this.isTabTunneled,
|
||||
required this.showSiteSettingsBadge,
|
||||
required this.siteSettingsBadgeState,
|
||||
required this.onSiteSettingsTap,
|
||||
required this.onTitleTap,
|
||||
required this.longPressUrlCopy,
|
||||
@@ -252,7 +261,7 @@ class AppBarTitleView extends StatelessWidget {
|
||||
|
||||
final TabState tabState;
|
||||
final bool isTabTunneled;
|
||||
final bool showSiteSettingsBadge;
|
||||
final SiteSettingsBadgeState siteSettingsBadgeState;
|
||||
final VoidCallback onSiteSettingsTap;
|
||||
final VoidCallback onTitleTap;
|
||||
final Widget? tabIcon;
|
||||
@@ -273,17 +282,22 @@ class AppBarTitleView extends StatelessWidget {
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
tabIcon ?? TabIcon(tabState: tabState, iconSize: 24),
|
||||
Positioned(
|
||||
top: -4,
|
||||
right: -4,
|
||||
child: Icon(
|
||||
MdiIcons.shieldHalfFull,
|
||||
size: 10,
|
||||
color: showSiteSettingsBadge
|
||||
? appColors.warningAmber
|
||||
: Colors.green,
|
||||
if (siteSettingsBadgeState != SiteSettingsBadgeState.hidden)
|
||||
Positioned(
|
||||
top: -4,
|
||||
right: -4,
|
||||
child: Icon(
|
||||
siteSettingsBadgeState == SiteSettingsBadgeState.improved
|
||||
? MdiIcons.shield
|
||||
: MdiIcons.shieldAlert,
|
||||
size: 10,
|
||||
color:
|
||||
siteSettingsBadgeState ==
|
||||
SiteSettingsBadgeState.improved
|
||||
? Colors.green
|
||||
: appColors.warningAmber,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -24,6 +24,7 @@ import 'package:weblibre/features/geckoview/domain/entities/states/security.dart
|
||||
import 'package:weblibre/features/geckoview/domain/entities/states/tab.dart';
|
||||
import 'package:weblibre/features/geckoview/features/browser/features/contextual_toolbar/presentation/widgets/contextual_bar_buttons.dart';
|
||||
import 'package:weblibre/features/geckoview/features/browser/features/contextual_toolbar/presentation/widgets/contextual_toolbar.dart';
|
||||
import 'package:weblibre/features/geckoview/features/browser/presentation/providers/site_settings_badge_provider.dart';
|
||||
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/browser_modules/app_bar_title.dart';
|
||||
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/browser_modules/bottom_app_bar.dart';
|
||||
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/navigation_buttons.dart';
|
||||
@@ -351,7 +352,7 @@ class _RegularPreviewTitle extends StatelessWidget {
|
||||
return AppBarTitleView(
|
||||
tabState: tabState,
|
||||
isTabTunneled: false,
|
||||
showSiteSettingsBadge: false,
|
||||
siteSettingsBadgeState: SiteSettingsBadgeState.hidden,
|
||||
onSiteSettingsTap: _noop,
|
||||
onTitleTap: _noop,
|
||||
tabIcon: const Icon(MdiIcons.web, size: 24),
|
||||
@@ -370,7 +371,7 @@ class _CompactPreviewTitle extends StatelessWidget {
|
||||
return CompactAppBarTitleView(
|
||||
tabState: tabState,
|
||||
isTabTunneled: false,
|
||||
showSiteSettingsBadge: false,
|
||||
siteSettingsBadgeState: SiteSettingsBadgeState.hidden,
|
||||
onSiteSettingsTap: _noop,
|
||||
onTitleTap: _noop,
|
||||
tabIcon: const Icon(MdiIcons.web, size: 24),
|
||||
|
||||
Reference in New Issue
Block a user