diff --git a/.github/workflows/reusable-build.yml b/.github/workflows/reusable-build.yml index aed7afc8c..b334e6eca 100644 --- a/.github/workflows/reusable-build.yml +++ b/.github/workflows/reusable-build.yml @@ -162,7 +162,10 @@ jobs: pushd $ANDROID_SDK_ROOT/ndk ln -s 28.2.13676358 25.1.8937393 popd - flutter build apk --dart-define=hasDevOptions=true --release --split-per-abi + flutter build apk --dart-define=hasDevOptions=true \ + --dart-define=CI_COMMIT="$(git rev-parse HEAD)" \ + --dart-define=CI_BRANCH="$BRANCH_NAME" \ + --release --split-per-abi - name: Rename apk file run: | diff --git a/lib/ci_build_overlay.dart b/lib/ci_build_overlay.dart new file mode 100644 index 000000000..0e2a4357c --- /dev/null +++ b/lib/ci_build_overlay.dart @@ -0,0 +1,60 @@ +import "package:cake_wallet/store/settings_store.dart"; +import "package:flutter/material.dart"; +import "package:flutter_mobx/flutter_mobx.dart"; + +class CiBuildOverlay extends StatelessWidget { + const CiBuildOverlay({required this.child, this.settingsStore, super.key}); + + static const _commit = String.fromEnvironment("CI_COMMIT"); + static const _branch = String.fromEnvironment("CI_BRANCH"); + static const isCiBuild = _commit != "" || _branch != ""; + + final Widget child; + final SettingsStore? settingsStore; + + @override + Widget build(BuildContext context) { + if (!isCiBuild) { + return child; + } + + final shortCommit = _commit.length > 7 ? _commit.substring(0, 7) : _commit; + final label = [shortCommit, _branch].where((part) => part.isNotEmpty).join(" ยท "); + + return Stack( + fit: StackFit.passthrough, + textDirection: TextDirection.ltr, + children: [ + child, + Positioned( + top: 0, + left: 0, + right: 0, + child: Observer( + builder: (_) => settingsStore?.showCiBuildOverlay ?? true + ? IgnorePointer( + child: ExcludeSemantics( + child: SafeArea( + bottom: false, + child: ColoredBox( + color: Colors.black54, + child: Text( + label, + textAlign: TextAlign.center, + textDirection: TextDirection.ltr, + textScaler: TextScaler.noScaling, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: const TextStyle(color: Colors.white, fontSize: 10), + ), + ), + ), + ), + ) + : const SizedBox.shrink(), + ), + ), + ], + ); + } +} diff --git a/lib/entities/preferences_key.dart b/lib/entities/preferences_key.dart index d35c010f5..09a7bd24c 100644 --- a/lib/entities/preferences_key.dart +++ b/lib/entities/preferences_key.dart @@ -147,4 +147,5 @@ class PreferencesKey { static const mwebAdDismissed = "mweb_ad_dismissed"; static const balanceHideCounter = "balance_hide_counter"; static const zcashMigrationModalViewed = "zcash_migration_modal_viewed"; + static const showCiBuildOverlay = "show_ci_build_overlay"; } diff --git a/lib/main.dart b/lib/main.dart index fb9c3665b..0420a765f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,6 +4,7 @@ import 'dart:ui'; import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart'; import 'package:cake_wallet/app_scroll_behavior.dart'; +import "package:cake_wallet/ci_build_overlay.dart"; import 'package:cake_wallet/core/auth_service.dart'; import 'package:cake_wallet/core/background_sync.dart'; import 'package:cake_wallet/core/node_switching_service.dart'; @@ -189,17 +190,22 @@ Future runAppWithZone({Key? topLevelKey}) async { ), ); } else { - runApp(App( - key: topLevelKey, - initialQuickAction: initialQuickAction, - quickActionsStream: quickActionsStream.stream)); + runApp( + CiBuildOverlay( + settingsStore: getIt.get(), + child: App( + key: topLevelKey, + initialQuickAction: initialQuickAction, + quickActionsStream: quickActionsStream.stream), + ), + ); } isAppRunning = true; }, (error, stackTrace) async { if (!isAppRunning) { runApp( - TopLevelErrorWidget(error: error, stackTrace: stackTrace), + CiBuildOverlay(child: TopLevelErrorWidget(error: error, stackTrace: stackTrace)), ); } diff --git a/lib/src/screens/settings/other_settings_page.dart b/lib/src/screens/settings/other_settings_page.dart index 512f71984..887693c6d 100644 --- a/lib/src/screens/settings/other_settings_page.dart +++ b/lib/src/screens/settings/other_settings_page.dart @@ -1,8 +1,10 @@ import 'dart:io'; import 'package:cake_wallet/bitcoin/bitcoin.dart'; +import "package:cake_wallet/ci_build_overlay.dart"; import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_regular_row.dart'; import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_selector.dart'; +import "package:cake_wallet/entities/new_ui_entities/list_item/list_item_toggle.dart"; import 'package:cake_wallet/entities/priority_for_wallet_type.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/new-ui/widgets/modal_page_wrapper.dart'; @@ -10,6 +12,7 @@ import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/dev/moneroc_cache_debug.dart'; +import "package:cake_wallet/src/widgets/alert_with_one_action.dart"; import 'package:cake_wallet/src/widgets/new_list_row/new_list_section.dart'; import 'package:cake_wallet/src/widgets/picker.dart'; import 'package:cake_wallet/utils/feature_flag.dart'; @@ -22,6 +25,7 @@ import 'package:cw_core/transaction_priority.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/db/sqlite.dart'; import 'package:flutter/material.dart'; +import "package:flutter_mobx/flutter_mobx.dart"; import 'package:path/path.dart' as p; class OtherSettingsPage extends BasePage { @@ -49,7 +53,7 @@ class OtherSettingsPage extends BasePage { // iconPath: "assets/new-ui/settings_row_icons/other.svg", // message: "Other settings", // title: S.of(context).other_settings), - content: NewListSections(sections: { + content: Observer(builder: (_) => NewListSections(sections: { "": [ if (_otherSettingsViewModel.displayTransactionPriority) _otherSettingsViewModel.walletType == WalletType.bitcoin @@ -167,6 +171,24 @@ class OtherSettingsPage extends BasePage { "dev": FeatureFlag.hasDevOptions == false ? [] : [ + if (CiBuildOverlay.isCiBuild) + ListItemToggle( + keyValue: "[dev] show commit/branch overlay", + label: "[dev] show commit/branch overlay", + value: _otherSettingsViewModel.showCiBuildOverlay, + onChanged: (val){ + if(!val) { + showPopUp( + context: context, + builder: (context) => AlertWithOneAction( + alertTitle: "overlay disabled", + alertContent: + "don't send issue reports without the overlay, pretty please", + buttonText: "okay", + buttonAction: Navigator.of(context).pop, + )); + } + _otherSettingsViewModel.setShowCiBuildOverlay(val);}), if (_otherSettingsViewModel.walletType == WalletType.monero) ListItemRegularRow( keyValue: "[dev] monero background sync", @@ -231,7 +253,7 @@ class OtherSettingsPage extends BasePage { dbDebugMarker.create(); }), ] - }), + })), ); } diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index bbbffa0c0..ff9f41dc0 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -59,6 +59,7 @@ abstract class SettingsStoreBase with Store { required SharedPreferences sharedPreferences, required bool initialShouldShowMarketPlaceInDashboard, required bool initialShowAddressBookPopupEnabled, + required bool initialShowCiBuildOverlay, required SyncStatusDisplayMode initialSyncStatusDisplayMode, required FiatCurrency initialFiatCurrency, required BalanceDisplayMode initialBalanceDisplayMode, @@ -199,6 +200,7 @@ abstract class SettingsStoreBase with Store { contactListAscending = initialContactListAscending, shouldShowMarketPlaceInDashboard = initialShouldShowMarketPlaceInDashboard, showAddressBookPopupEnabled = initialShowAddressBookPopupEnabled, + showCiBuildOverlay = initialShowCiBuildOverlay, syncStatusDisplayMode = initialSyncStatusDisplayMode, exchangeStatus = initialExchangeStatus, pinCodeLength = initialPinLength, @@ -452,6 +454,10 @@ abstract class SettingsStoreBase with Store { (bool value) => sharedPreferences.setBool(PreferencesKey.showAddressBookPopupEnabled, value)); + reaction( + (_) => showCiBuildOverlay, + (bool value) => sharedPreferences.setBool(PreferencesKey.showCiBuildOverlay, value)); + reaction( (_) => syncStatusDisplayMode, (SyncStatusDisplayMode value) => @@ -822,6 +828,9 @@ abstract class SettingsStoreBase with Store { @observable bool showAddressBookPopupEnabled; + @observable + bool showCiBuildOverlay; + @observable SyncStatusDisplayMode syncStatusDisplayMode; @@ -1300,6 +1309,7 @@ abstract class SettingsStoreBase with Store { sharedPreferences.getBool(PreferencesKey.shouldShowMarketPlaceInDashboard) ?? true; final showAddressBookPopupEnabled = sharedPreferences.getBool(PreferencesKey.showAddressBookPopupEnabled) ?? true; + final showCiBuildOverlay = sharedPreferences.getBool(PreferencesKey.showCiBuildOverlay) ?? true; final forceDecentralizedExchanges = await sharedPreferences.getBool(PreferencesKey.forceDecentralizedExchanges) ?? false; final decentralizedExchangesPromptDismissed = @@ -1671,6 +1681,7 @@ abstract class SettingsStoreBase with Store { sharedPreferences: sharedPreferences, initialShouldShowMarketPlaceInDashboard: shouldShowMarketPlaceInDashboard, initialShowAddressBookPopupEnabled: showAddressBookPopupEnabled, + initialShowCiBuildOverlay: showCiBuildOverlay, initialSyncStatusDisplayMode: syncStatusDisplayMode, nodes: nodes, powNodes: powNodes, @@ -1919,6 +1930,8 @@ abstract class SettingsStoreBase with Store { showAddressBookPopupEnabled = sharedPreferences.getBool(PreferencesKey.showAddressBookPopupEnabled) ?? showAddressBookPopupEnabled; + showCiBuildOverlay = + sharedPreferences.getBool(PreferencesKey.showCiBuildOverlay) ?? showCiBuildOverlay; syncStatusDisplayMode = SyncStatusDisplayModeExtension.fromString( sharedPreferences.getString(PreferencesKey.syncStatusDisplayMode) ?? SyncStatusDisplayMode.blocksRemaining.name); diff --git a/lib/view_model/settings/other_settings_view_model.dart b/lib/view_model/settings/other_settings_view_model.dart index fa3a274ae..35fa34ea2 100644 --- a/lib/view_model/settings/other_settings_view_model.dart +++ b/lib/view_model/settings/other_settings_view_model.dart @@ -161,6 +161,12 @@ abstract class OtherSettingsViewModelBase with Store { void setShouldSaveRecipientAddress(bool value) => _settingsStore.shouldSaveRecipientAddress = value; + @computed + bool get showCiBuildOverlay => _settingsStore.showCiBuildOverlay; + + @action + void setShowCiBuildOverlay(bool value) => _settingsStore.showCiBuildOverlay = value; + int? get customPriorityItemIndex { final priorities = priorityForWalletType(walletType); final customItem = priorities