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