hide/disable shopinbit/cakepay based on app features flags
What changed, and why it matters
This commit simply hides or shows two app features—ShopinBit and CakePay gift cards—based on configuration flags. It is a normal UI customization change, not a security fix or vulnerability.
No security action needed. Review as a routine feature-gating change.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch wraps existing ShopinBit and CakePay UI entries with AppConfig.hasFeature() checks. It refactors the desktop services view to build its menu and content maps dynamically from the same feature flags. No security-sensitive logic, input handling, cryptography, or network code is modified.
Changed components
Global settings view UIWallet view bottom navigation UIDesktop menu UIDesktop services view UIDesktop settings view UIDesktop settings menu UIInspect captured patch +105 / −81
diff --git a/lib/pages/settings_views/global_settings_view/global_settings_view.dart b/lib/pages/settings_views/global_settings_view/global_settings_view.dart
index 40b5319..729709f 100644
--- a/lib/pages/settings_views/global_settings_view/global_settings_view.dart
+++ b/lib/pages/settings_views/global_settings_view/global_settings_view.dart
@@ -247,33 +247,37 @@ class GlobalSettingsView extends StatelessWidget {
);
},
),
- Consumer(
- builder: (_, ref, __) {
- final familiarity = ref.watch(
- prefsChangeNotifierProvider.select(
- (v) => v.familiarity,
- ),
- );
- if (familiarity < 6) {
- return const SizedBox.shrink();
- }
- return Column(
- children: [
- const SizedBox(height: 8),
- SettingsListButton(
- iconAssetName: Assets.svg.key,
- iconSize: 16,
- title: "ShopinBit",
- onPressed: () {
- Navigator.of(context).pushNamed(
- ShopInBitSettingsView.routeName,
- );
- },
+ if (AppConfig.hasFeature(
+ AppFeature.shopinBit,
+ ))
+ Consumer(
+ builder: (_, ref, __) {
+ final familiarity = ref.watch(
+ prefsChangeNotifierProvider.select(
+ (v) => v.familiarity,
),
- ],
- );
- },
- ),
+ );
+ if (familiarity < 6) {
+ return const SizedBox.shrink();
+ }
+ return Column(
+ children: [
+ const SizedBox(height: 8),
+ SettingsListButton(
+ iconAssetName: Assets.svg.key,
+ iconSize: 16,
+ title: "ShopinBit",
+ onPressed: () {
+ Navigator.of(context).pushNamed(
+ ShopInBitSettingsView
+ .routeName,
+ );
+ },
+ ),
+ ],
+ );
+ },
+ ),
const SizedBox(height: 8),
SettingsListButton(
iconAssetName: Assets.svg.questionMessage,
diff --git a/lib/pages/wallet_view/wallet_view.dart b/lib/pages/wallet_view/wallet_view.dart
index 0dc8d17..04c0888 100644
--- a/lib/pages/wallet_view/wallet_view.dart
+++ b/lib/pages/wallet_view/wallet_view.dart
@@ -1348,7 +1348,7 @@ class _WalletViewState extends ConsumerState<WalletView> {
);
},
),
- if (!viewOnly)
+ if (!viewOnly && AppConfig.hasFeature(.shopinBit))
WalletNavigationBarItemData(
label: "Services",
icon: SvgPicture.asset(
@@ -1365,21 +1365,22 @@ class _WalletViewState extends ConsumerState<WalletView> {
).pushNamed(ServicesView.routeName);
},
),
- WalletNavigationBarItemData(
- label: "Gift cards",
- icon: CreditCardIcon(
- height: 20,
- width: 20,
- color: Theme.of(
- context,
- ).extension<StackColors>()!.bottomNavIconIcon,
+ if (AppConfig.hasFeature(.shopinBit))
+ WalletNavigationBarItemData(
+ label: "Gift cards",
+ icon: CreditCardIcon(
+ height: 20,
+ width: 20,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.bottomNavIconIcon,
+ ),
+ onTap: () {
+ Navigator.of(
+ context,
+ ).pushNamed(GiftCardsView.routeName);
+ },
),
- onTap: () {
- Navigator.of(
- context,
- ).pushNamed(GiftCardsView.routeName);
- },
- ),
],
),
),
diff --git a/lib/pages_desktop_specific/desktop_menu.dart b/lib/pages_desktop_specific/desktop_menu.dart
index 5ffe149..7602ca5 100644
--- a/lib/pages_desktop_specific/desktop_menu.dart
+++ b/lib/pages_desktop_specific/desktop_menu.dart
@@ -223,17 +223,20 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
isExpandedInitially: !_isMinimized,
),
],
- const SizedBox(height: 2),
- DesktopMenuItem(
- key: const ValueKey('services'),
- duration: duration,
- icon: const DesktopServicesIcon(),
- label: "Services",
- value: DesktopMenuItemId.services,
- onChanged: updateSelectedMenuItem,
- controller: controllers[3],
- isExpandedInitially: !_isMinimized,
- ),
+ if (AppConfig.hasFeature(.shopinBit) ||
+ AppConfig.hasFeature(.cakePay)) ...[
+ const SizedBox(height: 2),
+ DesktopMenuItem(
+ key: const ValueKey('services'),
+ duration: duration,
+ icon: const DesktopServicesIcon(),
+ label: "Services",
+ value: DesktopMenuItemId.services,
+ onChanged: updateSelectedMenuItem,
+ controller: controllers[3],
+ isExpandedInitially: !_isMinimized,
+ ),
+ ],
const SizedBox(height: 2),
DesktopMenuItem(
key: const ValueKey('notifications'),
diff --git a/lib/pages_desktop_specific/services/desktop_services_view.dart b/lib/pages_desktop_specific/services/desktop_services_view.dart
index f94f708..7a24d94 100644
--- a/lib/pages_desktop_specific/services/desktop_services_view.dart
+++ b/lib/pages_desktop_specific/services/desktop_services_view.dart
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
+import '../../app_config.dart';
import '../../route_generator.dart';
import '../../themes/stack_colors.dart';
import '../../utilities/assets.dart';
@@ -12,7 +13,22 @@ import '../settings/settings_menu_item.dart';
import 'cakepay/desktop_gift_cards_view.dart';
import 'shopin_bit/desktop_shopinbit_view.dart';
-final selectedServicesMenuItemStateProvider = StateProvider<int>((_) => 0);
+final _selectedServicesMenuItemStateProvider = StateProvider<_MenuItem?>(
+ (_) => _labels.firstOrNull,
+);
+
+enum _MenuItem {
+ shopinBit("Services"),
+ cakePay("Gift Cards");
+
+ final String value;
+ const _MenuItem(this.value);
+}
+
+final _labels = [
+ if (AppConfig.hasFeature(.shopinBit)) _MenuItem.shopinBit,
+ if (AppConfig.hasFeature(.cakePay)) _MenuItem.cakePay,
+];
class DesktopServicesView extends ConsumerStatefulWidget {
const DesktopServicesView({super.key});
@@ -25,22 +41,22 @@ class DesktopServicesView extends ConsumerStatefulWidget {
}
class _DesktopServicesViewState extends ConsumerState<DesktopServicesView> {
- final List<String> _labels = const ["Services", "Gift Cards"];
-
@override
Widget build(BuildContext context) {
- final List<Widget> contentViews = [
- const Navigator(
- key: Key("servicesShopInBitDesktopKey"),
- onGenerateRoute: RouteGenerator.generateRoute,
- initialRoute: DesktopShopInBitView.routeName,
- ),
- const Navigator(
- key: Key("servicesGiftCardsDesktopKey"),
- onGenerateRoute: RouteGenerator.generateRoute,
- initialRoute: DesktopGiftCardsView.routeName,
- ),
- ];
+ final Map<_MenuItem, Widget> contentViews = {
+ if (AppConfig.hasFeature(.shopinBit))
+ .shopinBit: const Navigator(
+ key: Key("servicesShopInBitDesktopKey"),
+ onGenerateRoute: RouteGenerator.generateRoute,
+ initialRoute: DesktopShopInBitView.routeName,
+ ),
+ if (AppConfig.hasFeature(.cakePay))
+ .cakePay: const Navigator(
+ key: Key("servicesGiftCardsDesktopKey"),
+ onGenerateRoute: RouteGenerator.generateRoute,
+ initialRoute: DesktopGiftCardsView.routeName,
+ ),
+ };
return DesktopScaffold(
background: Theme.of(context).extension<StackColors>()!.background,
@@ -68,12 +84,11 @@ class _DesktopServicesViewState extends ConsumerState<DesktopServicesView> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
- for (int i = 0; i < _labels.length; i++)
- Column(
+ ..._labels.map(
+ (label) => Column(
mainAxisSize: MainAxisSize.min,
children: [
- if (i > 0) const SizedBox(height: 2),
- SettingsMenuItem<int>(
+ SettingsMenuItem<_MenuItem?>(
icon: SvgPicture.asset(
Assets.svg.polygon,
width: 11,
@@ -81,28 +96,28 @@ class _DesktopServicesViewState extends ConsumerState<DesktopServicesView> {
color:
ref
.watch(
- selectedServicesMenuItemStateProvider
+ _selectedServicesMenuItemStateProvider
.state,
)
.state ==
- i
+ label
? Theme.of(context)
.extension<StackColors>()!
.accentColorBlue
: Colors.transparent,
),
- label: _labels[i],
- value: i,
+ label: label.value,
+ value: label,
group: ref
.watch(
- selectedServicesMenuItemStateProvider
+ _selectedServicesMenuItemStateProvider
.state,
)
.state,
onChanged: (newValue) =>
ref
.read(
- selectedServicesMenuItemStateProvider
+ _selectedServicesMenuItemStateProvider
.state,
)
.state =
@@ -110,6 +125,7 @@ class _DesktopServicesViewState extends ConsumerState<DesktopServicesView> {
),
],
),
+ ),
],
),
),
@@ -121,8 +137,8 @@ class _DesktopServicesViewState extends ConsumerState<DesktopServicesView> {
Expanded(
child:
contentViews[ref
- .watch(selectedServicesMenuItemStateProvider.state)
- .state],
+ .watch(_selectedServicesMenuItemStateProvider.state)
+ .state]!,
),
],
),
diff --git a/lib/pages_desktop_specific/settings/desktop_settings_view.dart b/lib/pages_desktop_specific/settings/desktop_settings_view.dart
index ee2c423..6556989 100644
--- a/lib/pages_desktop_specific/settings/desktop_settings_view.dart
+++ b/lib/pages_desktop_specific/settings/desktop_settings_view.dart
@@ -94,7 +94,7 @@ class _DesktopSettingsViewState extends ConsumerState<DesktopSettingsView> {
onGenerateRoute: RouteGenerator.generateRoute,
initialRoute: AdvancedSettings.routeName,
), //advanced
- if (familiarity >= 6)
+ if (AppConfig.hasFeature(.shopinBit) && familiarity >= 6)
const Navigator(
key: Key("settingsShopInBitDesktopKey"),
onGenerateRoute: RouteGenerator.generateRoute,
diff --git a/lib/pages_desktop_specific/settings/settings_menu.dart b/lib/pages_desktop_specific/settings/settings_menu.dart
index a7f5129..be49fc2 100644
--- a/lib/pages_desktop_specific/settings/settings_menu.dart
+++ b/lib/pages_desktop_specific/settings/settings_menu.dart
@@ -46,7 +46,7 @@ class _SettingsMenuState extends ConsumerState<SettingsMenu> {
"Syncing preferences",
if (AppConfig.hasFeature(AppFeature.themeSelection)) "Appearance",
"Advanced",
- if (familiarity >= 6) "ShopinBit",
+ if (AppConfig.hasFeature(.shopinBit) && familiarity >= 6) "ShopinBit",
];
return Column(
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.