diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 4df215e13..282941365 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -16,6 +16,7 @@ jobs: env: STORE_PASS: test@cake_wallet KEY_PASS: test@cake_wallet + PR_NUMBER: ${{ github.event.number }} steps: - name: is pr @@ -150,10 +151,7 @@ jobs: - name: Rename app run: | - hash=`sha512sum <<<"${{ env.BRANCH_NAME }}"` - substring=${hash:0:15} - echo substring - echo -e "id=com.cakewallet.test_$(substring)\nname=${{ env.BRANCH_NAME }}" > /opt/android/cake_wallet/android/app.properties + echo -e "id=com.cakewallet.test_${{ env.PR_NUMBER }}\nname=${{ env.BRANCH_NAME }}" > /opt/android/cake_wallet/android/app.properties - name: Build run: | diff --git a/assets/images/notification_icon.svg b/assets/images/notification_icon.svg new file mode 100644 index 000000000..099039e67 --- /dev/null +++ b/assets/images/notification_icon.svg @@ -0,0 +1,69 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/assets/images/status_website_image.png b/assets/images/status_website_image.png new file mode 100644 index 000000000..017bb64e1 Binary files /dev/null and b/assets/images/status_website_image.png differ diff --git a/assets/text/Monerocom_Release_Notes.txt b/assets/text/Monerocom_Release_Notes.txt index 732e58a18..05f78b809 100644 --- a/assets/text/Monerocom_Release_Notes.txt +++ b/assets/text/Monerocom_Release_Notes.txt @@ -1,2 +1,2 @@ -New themes +In-App live status page for the app services Bug fixes and enhancements \ No newline at end of file diff --git a/assets/text/Release_Notes.txt b/assets/text/Release_Notes.txt index 1c2ec154c..05f78b809 100644 --- a/assets/text/Release_Notes.txt +++ b/assets/text/Release_Notes.txt @@ -1,6 +1,2 @@ -Add Solana wallet -Support ALL Bitcoin address types (Legacy, Segwit (both variants), Taproot) -Enhance Sending/Receiving flow for Bitcoin -Improve fee calculations in Bitcoin -New themes +In-App live status page for the app services Bug fixes and enhancements \ No newline at end of file diff --git a/cw_core/lib/crypto_currency.dart b/cw_core/lib/crypto_currency.dart index ce509015c..9cebce10a 100644 --- a/cw_core/lib/crypto_currency.dart +++ b/cw_core/lib/crypto_currency.dart @@ -216,7 +216,7 @@ class CryptoCurrency extends EnumerableItem with Serializable implemen static const usdcEPoly = CryptoCurrency(title: 'USDC.E', tag: 'POLY', fullName: 'USD Coin (PoS)', raw: 88, name: 'usdcepoly', iconPath: 'assets/images/usdc_icon.png', decimals: 6); static const kaspa = CryptoCurrency(title: 'KAS', fullName: 'Kaspa', raw: 89, name: 'kas', iconPath: 'assets/images/kaspa_icon.png', decimals: 8); static const digibyte = CryptoCurrency(title: 'DGB', fullName: 'DigiByte', raw: 90, name: 'dgb', iconPath: 'assets/images/digibyte.png', decimals: 8); - static const usdtSol = CryptoCurrency(title: 'USDT', tag: 'SOL', fullName: 'USDT Tether', raw: 90, name: 'usdtsol', iconPath: 'assets/images/usdt_icon.png', decimals: 6); + static const usdtSol = CryptoCurrency(title: 'USDT', tag: 'SOL', fullName: 'USDT Tether', raw: 91, name: 'usdtsol', iconPath: 'assets/images/usdt_icon.png', decimals: 6); static final Map _rawCurrencyMap = diff --git a/cw_core/lib/monero_transaction_priority.dart b/cw_core/lib/monero_transaction_priority.dart index f5c00ecc7..81058f336 100644 --- a/cw_core/lib/monero_transaction_priority.dart +++ b/cw_core/lib/monero_transaction_priority.dart @@ -1,7 +1,4 @@ import 'package:cw_core/transaction_priority.dart'; -import 'package:cw_core/wallet_type.dart'; -//import 'package:cake_wallet/generated/i18n.dart'; -import 'package:cw_core/enumerable_item.dart'; class MoneroTransactionPriority extends TransactionPriority { const MoneroTransactionPriority({required String title, required int raw}) @@ -12,21 +9,20 @@ class MoneroTransactionPriority extends TransactionPriority { MoneroTransactionPriority.automatic, MoneroTransactionPriority.medium, MoneroTransactionPriority.fast, - MoneroTransactionPriority.fastest + MoneroTransactionPriority.fastest, ]; - static const slow = MoneroTransactionPriority(title: 'Slow', raw: 0); - static const automatic = MoneroTransactionPriority(title: 'Automatic', raw: 1); + static const automatic = MoneroTransactionPriority(title: 'Automatic', raw: 0); + static const slow = MoneroTransactionPriority(title: 'Slow', raw: 1); static const medium = MoneroTransactionPriority(title: 'Medium', raw: 2); static const fast = MoneroTransactionPriority(title: 'Fast', raw: 3); static const fastest = MoneroTransactionPriority(title: 'Fastest', raw: 4); - static const standard = slow; static MoneroTransactionPriority deserialize({required int raw}) { switch (raw) { case 0: - return slow; - case 1: return automatic; + case 1: + return slow; case 2: return medium; case 3: diff --git a/cw_monero/lib/monero_wallet.dart b/cw_monero/lib/monero_wallet.dart index a3c665277..db386837c 100644 --- a/cw_monero/lib/monero_wallet.dart +++ b/cw_monero/lib/monero_wallet.dart @@ -191,13 +191,8 @@ abstract class MoneroWalletBase extends WalletBase setup({ yatStore: getIt.get(), ordersStore: getIt.get(), anonpayTransactionsStore: getIt.get(), + sharedPreferences: getIt.get(), keyService: getIt.get())); getIt.registerFactory( diff --git a/lib/entities/default_settings_migration.dart b/lib/entities/default_settings_migration.dart index 59b8d5d5b..c3ebde94c 100644 --- a/lib/entities/default_settings_migration.dart +++ b/lib/entities/default_settings_migration.dart @@ -201,6 +201,9 @@ Future defaultSettingsMigration( await changeSolanaCurrentNodeToDefault( sharedPreferences: sharedPreferences, nodes: nodes); break; + case 28: + await _updateMoneroPriority(sharedPreferences); + break; default: break; } @@ -215,6 +218,18 @@ Future defaultSettingsMigration( await sharedPreferences.setInt(PreferencesKey.currentDefaultSettingsMigrationVersion, version); } +Future _updateMoneroPriority(SharedPreferences sharedPreferences) async { + final currentPriority = + await sharedPreferences.getInt(PreferencesKey.moneroTransactionPriority) ?? + monero!.getDefaultTransactionPriority().serialize(); + + // was set to automatic but automatic should be 0 + if (currentPriority == 1) { + sharedPreferences.setInt(PreferencesKey.moneroTransactionPriority, + monero!.getDefaultTransactionPriority().serialize()); // 0 + } +} + Future _validateWalletInfoBoxData(Box walletInfoSource) async { try { final root = await getAppDir(); diff --git a/lib/entities/preferences_key.dart b/lib/entities/preferences_key.dart index 0dd251aaa..1808be97c 100644 --- a/lib/entities/preferences_key.dart +++ b/lib/entities/preferences_key.dart @@ -72,4 +72,5 @@ class PreferencesKey { static const lastSeenAppVersion = 'last_seen_app_version'; static const shouldShowMarketPlaceInDashboard = 'should_show_marketplace_in_dashboard'; static const isNewInstall = 'is_new_install'; + static const serviceStatusShaKey = 'service_status_sha_key'; } diff --git a/lib/entities/service_status.dart b/lib/entities/service_status.dart new file mode 100644 index 000000000..392c8072d --- /dev/null +++ b/lib/entities/service_status.dart @@ -0,0 +1,41 @@ +class ServiceStatus { + final String title; + final String description; + final String? image; + final String? status; + final DateTime date; + + ServiceStatus( + {required this.title, + required this.description, + required this.date, + this.image, + this.status}); + + factory ServiceStatus.fromJson(Map json) => ServiceStatus( + title: json['title'] as String? ?? '', + description: json['description'] as String? ?? '', + date: DateTime.tryParse(json['date'] as String? ?? '') ?? DateTime.now(), + image: json['image'] as String?, + status: json['status'] as String?, + ); +} + +class ServicesResponse { + final List servicesStatus; + final bool hasUpdates; + final String currentSha; + + ServicesResponse(this.servicesStatus, this.hasUpdates, this.currentSha); + + factory ServicesResponse.fromJson( + Map json, bool hasUpdates, String currentSha) { + return ServicesResponse( + (json['notices'] as List? ?? []) + .map((e) => ServiceStatus.fromJson(e as Map)) + .toList(), + hasUpdates, + currentSha, + ); + } +} diff --git a/lib/main.dart b/lib/main.dart index 0bd6b472d..bdcb1e7d3 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -165,7 +165,7 @@ Future initializeAppConfigs() async { transactionDescriptions: transactionDescriptions, secureStorage: secureStorage, anonpayInvoiceInfo: anonpayInvoiceInfo, - initialMigrationVersion: 27); + initialMigrationVersion: 28); } Future initialSetup( diff --git a/lib/solana/cw_solana.dart b/lib/solana/cw_solana.dart index cdbf5bca3..7b805c1d5 100644 --- a/lib/solana/cw_solana.dart +++ b/lib/solana/cw_solana.dart @@ -111,7 +111,7 @@ class CWSolana extends Solana { @override List? getValidationLength(CryptoCurrency type) { if (type is SPLToken) { - return [44]; + return [32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44]; } return null; diff --git a/lib/src/screens/dashboard/dashboard_page.dart b/lib/src/screens/dashboard/dashboard_page.dart index 61e7d6176..3806dff91 100644 --- a/lib/src/screens/dashboard/dashboard_page.dart +++ b/lib/src/screens/dashboard/dashboard_page.dart @@ -7,6 +7,7 @@ import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sideba import 'package:cake_wallet/src/screens/dashboard/pages/market_place_page.dart'; import 'package:cake_wallet/src/screens/wallet_connect/widgets/modals/bottom_sheet_listener.dart'; import 'package:cake_wallet/src/widgets/gradient_background.dart'; +import 'package:cake_wallet/src/widgets/services_updates_widget.dart'; import 'package:cake_wallet/src/widgets/vulnerable_seeds_popup.dart'; import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart'; import 'package:cake_wallet/utils/device_info.dart'; @@ -101,6 +102,9 @@ class _DashboardPageView extends BasePage { @override Widget get endDrawer => MenuWidget(dashboardViewModel); + @override + Widget leading(BuildContext context) => ServicesUpdatesWidget(dashboardViewModel.getServicesStatus()); + @override Widget middle(BuildContext context) { return SyncIndicator( diff --git a/lib/src/screens/dashboard/desktop_widgets/desktop_sidebar/side_menu_item.dart b/lib/src/screens/dashboard/desktop_widgets/desktop_sidebar/side_menu_item.dart index 8119e9fd4..940ab54ca 100644 --- a/lib/src/screens/dashboard/desktop_widgets/desktop_sidebar/side_menu_item.dart +++ b/lib/src/screens/dashboard/desktop_widgets/desktop_sidebar/side_menu_item.dart @@ -7,13 +7,15 @@ class SideMenuItem extends StatelessWidget { required this.onTap, this.imagePath, this.icon, + this.widget, this.isSelected = false, - }) : assert((icon != null && imagePath == null) || (icon == null && imagePath != null)); + }) : assert(widget != null || icon != null || imagePath != null); final void Function() onTap; final String? imagePath; final IconData? icon; final bool isSelected; + final Widget? widget; Color _setColor(BuildContext context) { if (isSelected) { @@ -28,18 +30,7 @@ class SideMenuItem extends StatelessWidget { return InkWell( child: Padding( padding: EdgeInsets.all(20), - child: icon != null - ? Icon( - icon, - color: _setColor(context), - ) - : Image.asset( - imagePath ?? '', - fit: BoxFit.cover, - height: 30, - width: 30, - color: _setColor(context), - ), + child: widget ?? _getIcon(context), ), onTap: () => onTap.call(), highlightColor: Colors.transparent, @@ -48,4 +39,19 @@ class SideMenuItem extends StatelessWidget { splashColor: Colors.transparent, ); } + + Widget _getIcon(BuildContext context) { + return icon != null + ? Icon( + icon, + color: _setColor(context), + ) + : Image.asset( + imagePath ?? '', + fit: BoxFit.cover, + height: 30, + width: 30, + color: _setColor(context), + ); + } } diff --git a/lib/src/screens/dashboard/desktop_widgets/desktop_sidebar_wrapper.dart b/lib/src/screens/dashboard/desktop_widgets/desktop_sidebar_wrapper.dart index f73570048..d0ddb19e6 100644 --- a/lib/src/screens/dashboard/desktop_widgets/desktop_sidebar_wrapper.dart +++ b/lib/src/screens/dashboard/desktop_widgets/desktop_sidebar_wrapper.dart @@ -9,6 +9,7 @@ import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sideba import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator.dart'; import 'package:cake_wallet/src/screens/wallet_connect/widgets/modals/bottom_sheet_listener.dart'; +import 'package:cake_wallet/src/widgets/services_updates_widget.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/desktop_sidebar_view_model.dart'; import 'package:flutter/cupertino.dart'; @@ -105,12 +106,18 @@ class DesktopSidebarWrapper extends BasePage { ? selectedIconPath : unselectedIconPath, ), + SideMenuItem( + widget: ServicesUpdatesWidget(dashboardViewModel.getServicesStatus()), + isSelected: desktopSidebarViewModel.currentPage == SidebarItem.status, + onTap: () {}, + ), ], bottomItems: [ SideMenuItem( - imagePath: 'assets/images/support_icon.png', - isSelected: desktopSidebarViewModel.currentPage == SidebarItem.support, - onTap: () => desktopSidebarViewModel.onPageChange(SidebarItem.support)), + imagePath: 'assets/images/support_icon.png', + isSelected: desktopSidebarViewModel.currentPage == SidebarItem.support, + onTap: () => desktopSidebarViewModel.onPageChange(SidebarItem.support), + ), SideMenuItem( imagePath: 'assets/images/settings_outline.png', isSelected: desktopSidebarViewModel.currentPage == SidebarItem.settings, diff --git a/lib/src/widgets/base_alert_dialog.dart b/lib/src/widgets/base_alert_dialog.dart index b251e4b45..5c1111740 100644 --- a/lib/src/widgets/base_alert_dialog.dart +++ b/lib/src/widgets/base_alert_dialog.dart @@ -179,7 +179,7 @@ class BaseAlertDialog extends StatelessWidget { Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ - if (headerText != null) headerTitle(context), + if (headerText?.isNotEmpty ?? false) headerTitle(context), Padding( padding: EdgeInsets.fromLTRB(24, 20, 24, 0), child: title(context), diff --git a/lib/src/widgets/service_status_tile.dart b/lib/src/widgets/service_status_tile.dart new file mode 100644 index 000000000..f2a079841 --- /dev/null +++ b/lib/src/widgets/service_status_tile.dart @@ -0,0 +1,71 @@ +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:cake_wallet/entities/service_status.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; + +class ServiceStatusTile extends StatelessWidget { + final ServiceStatus status; + + const ServiceStatusTile(this.status, {super.key}); + + @override + Widget build(BuildContext context) { + return ListTile( + contentPadding: const EdgeInsets.all(8), + title: Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Expanded( + child: AutoSizeText( + "${status.title}${status.status != null ? " - ${status.status}" : ""}", + style: TextStyle( + fontSize: 16, + fontFamily: 'Lato', + fontWeight: FontWeight.w800, + height: 1, + ), + maxLines: 1, + textAlign: TextAlign.start, + ), + ), + Text( + _getTimeString(status.date), + style: TextStyle(fontSize: 12), + ), + ], + ), + ), + leading: RotatedBox( + child: Icon( + Icons.info, + color: status.status == "resolved" ? Colors.green : Colors.red, + ), + quarterTurns: 2, + ), + subtitle: Row( + children: [ + Expanded(child: Text(status.description)), + if (status.image != null) + SizedBox( + height: 50, + width: 50, + child: Image.network(status.image!), + ), + ], + ), + ); + } + + String _getTimeString(DateTime date) { + int difference = DateTime.now().difference(date).inHours; + if (difference == 0) { + return "few minutes ago"; + } + if (difference < 24) { + return DateFormat('h:mm a').format(date); + } + return DateFormat('d-MM-yyyy').format(date); + } +} diff --git a/lib/src/widgets/services_updates_widget.dart b/lib/src/widgets/services_updates_widget.dart new file mode 100644 index 000000000..9557ff6b9 --- /dev/null +++ b/lib/src/widgets/services_updates_widget.dart @@ -0,0 +1,122 @@ +import 'package:cake_wallet/di.dart'; +import 'package:cake_wallet/entities/preferences_key.dart'; +import 'package:cake_wallet/entities/service_status.dart'; +import 'package:cake_wallet/src/widgets/primary_button.dart'; +import 'package:cake_wallet/src/widgets/service_status_tile.dart'; +import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart'; +import 'package:cake_wallet/themes/extensions/wallet_list_theme.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class ServicesUpdatesWidget extends StatelessWidget { + final Future servicesResponse; + + const ServicesUpdatesWidget(this.servicesResponse, {super.key}); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(8.0), + child: FutureBuilder( + future: servicesResponse, + builder: (context, state) { + return InkWell( + onTap: state.hasData + ? () { + // save currentSha when the user see the status + getIt + .get() + .setString(PreferencesKey.serviceStatusShaKey, state.data!.currentSha); + + showModalBottomSheet( + context: context, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(50), + topRight: Radius.circular(50), + ), + ), + constraints: BoxConstraints( + maxHeight: MediaQuery.of(context).size.height / 2, + minHeight: MediaQuery.of(context).size.height / 4, + ), + builder: (context) { + Widget body; + if (state.data!.servicesStatus.isEmpty) { + body = Center( + child: Text("Everything is up and running as expected"), + ); + } else { + body = SingleChildScrollView( + child: Column( + children: state.data!.servicesStatus + .map((status) => ServiceStatusTile(status)) + .toList()), + ); + } + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 20), + child: Stack( + children: [ + body, + Align( + alignment: Alignment.bottomCenter, + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width / 8), + child: PrimaryImageButton( + onPressed: () { + try { + launchUrl(Uri.parse("https://status.cakewallet.com/")); + } catch (_) {} + }, + image: Image.asset( + "assets/images/status_website_image.png", + color: Theme.of(context).brightness == Brightness.light + ? Colors.white + : null, + ), + text: "Status Website", + color: Theme.of(context) + .extension()! + .createNewWalletButtonBackgroundColor, + textColor: Theme.of(context) + .extension()! + .restoreWalletButtonTextColor, + ), + ), + ) + ], + ), + ); + }, + ); + } + : null, + child: Stack( + children: [ + SvgPicture.asset( + "assets/images/notification_icon.svg", + color: Theme.of(context).extension()!.pageTitleTextColor, + width: 30, + ), + if (state.hasData && state.data!.hasUpdates) + Container( + height: 7, + width: 7, + margin: EdgeInsetsDirectional.only(start: 15), + decoration: BoxDecoration( + color: Colors.red, + shape: BoxShape.circle, + ), + ), + ], + ), + ); + }, + ), + ); + } +} diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index da5eb0373..b02c09f82 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -4,8 +4,10 @@ import 'package:cake_wallet/buy/buy_provider.dart'; import 'package:cake_wallet/core/key_service.dart'; import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart'; import 'package:cake_wallet/entities/balance_display_mode.dart'; +import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/entities/provider_types.dart'; import 'package:cake_wallet/entities/exchange_api_mode.dart'; +import 'package:cake_wallet/entities/service_status.dart'; import 'package:cake_wallet/exchange/exchange_provider_description.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/monero/monero.dart'; @@ -42,7 +44,8 @@ import 'package:cw_core/wallet_type.dart'; import 'package:eth_sig_util/util/utils.dart'; import 'package:flutter/services.dart'; import 'package:mobx/mobx.dart'; -import 'package:cake_wallet/entities/provider_types.dart'; +import 'package:http/http.dart' as http; +import 'package:shared_preferences/shared_preferences.dart'; part 'dashboard_view_model.g.dart'; @@ -59,6 +62,7 @@ abstract class DashboardViewModelBase with Store { required this.yatStore, required this.ordersStore, required this.anonpayTransactionsStore, + required this.sharedPreferences, required this.keyService}) : hasSellAction = false, hasBuyAction = false, @@ -280,6 +284,7 @@ abstract class DashboardViewModelBase with Store { bool get hasRescan => wallet.type == WalletType.monero || wallet.type == WalletType.haven; final KeyService keyService; + final SharedPreferences sharedPreferences; BalanceViewModel balanceViewModel; @@ -339,15 +344,13 @@ abstract class DashboardViewModelBase with Store { bool hasExchangeAction; @computed - bool get isEnabledBuyAction => - !settingsStore.disableBuy && availableBuyProviders.isNotEmpty; + bool get isEnabledBuyAction => !settingsStore.disableBuy && availableBuyProviders.isNotEmpty; @observable bool hasBuyAction; @computed - bool get isEnabledSellAction => - !settingsStore.disableSell && availableSellProviders.isNotEmpty; + bool get isEnabledSellAction => !settingsStore.disableSell && availableSellProviders.isNotEmpty; @observable bool hasSellAction; @@ -472,7 +475,8 @@ abstract class DashboardViewModelBase with Store { Future> checkAffectedWallets() async { // await load file - final vulnerableSeedsString = await rootBundle.loadString('assets/text/cakewallet_weak_bitcoin_seeds_hashed_sorted_version1.txt'); + final vulnerableSeedsString = await rootBundle + .loadString('assets/text/cakewallet_weak_bitcoin_seeds_hashed_sorted_version1.txt'); final vulnerableSeeds = vulnerableSeedsString.split("\n"); final walletInfoSource = await CakeHive.openBox(WalletInfo.boxName); @@ -497,4 +501,29 @@ abstract class DashboardViewModelBase with Store { return affectedWallets; } + + Future getServicesStatus() async { + try { + final res = await http.get(Uri.parse("https://service-api.cakewallet.com/v1/active-notices")); + + if (res.statusCode < 200 || res.statusCode >= 300) { + throw res.body; + } + + final oldSha = sharedPreferences.getString(PreferencesKey.serviceStatusShaKey); + + final hash = await Cryptography.instance.sha256().hash(utf8.encode(res.body)); + final currentSha = bytesToHex(hash.bytes); + + final hasUpdates = oldSha != currentSha; + + return ServicesResponse.fromJson( + json.decode(res.body) as Map, + hasUpdates, + currentSha, + ); + } catch (_) { + return ServicesResponse([], false, ''); + } + } } diff --git a/lib/view_model/dashboard/desktop_sidebar_view_model.dart b/lib/view_model/dashboard/desktop_sidebar_view_model.dart index 543b44b35..52a6dc49b 100644 --- a/lib/view_model/dashboard/desktop_sidebar_view_model.dart +++ b/lib/view_model/dashboard/desktop_sidebar_view_model.dart @@ -5,6 +5,7 @@ part 'desktop_sidebar_view_model.g.dart'; enum SidebarItem { dashboard, transactions, + status, support, settings, } diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index 772150368..507ed14cc 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -427,9 +427,10 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor ) { if (walletType == WalletType.ethereum || walletType == WalletType.polygon || + walletType == WalletType.solana || walletType == WalletType.haven) { if (error.contains('gas required exceeds allowance') || - error.contains('insufficient funds for')) { + error.contains('insufficient funds')) { return S.current.do_not_have_enough_gas_asset(currency.toString()); } diff --git a/scripts/android/app_env.sh b/scripts/android/app_env.sh index fa3701fa7..eb1aaaacc 100644 --- a/scripts/android/app_env.sh +++ b/scripts/android/app_env.sh @@ -15,15 +15,15 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN) APP_ANDROID_TYPE=$1 MONERO_COM_NAME="Monero.com" -MONERO_COM_VERSION="1.11.0" -MONERO_COM_BUILD_NUMBER=77 +MONERO_COM_VERSION="1.11.1" +MONERO_COM_BUILD_NUMBER=78 MONERO_COM_BUNDLE_ID="com.monero.app" MONERO_COM_PACKAGE="com.monero.app" MONERO_COM_SCHEME="monero.com" CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="4.14.0" -CAKEWALLET_BUILD_NUMBER=196 +CAKEWALLET_VERSION="4.14.1" +CAKEWALLET_BUILD_NUMBER=197 CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet" CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet" CAKEWALLET_SCHEME="cakewallet" diff --git a/scripts/android/build_monero.sh b/scripts/android/build_monero.sh index e0010b351..fb596e452 100755 --- a/scripts/android/build_monero.sh +++ b/scripts/android/build_monero.sh @@ -1,13 +1,12 @@ #!/bin/sh . ./config.sh -MONERO_BRANCH=release-v0.18.2.2-android +MONERO_BRANCH=release-v0.18.3.2-android MONERO_SRC_DIR=${WORKDIR}/monero git clone https://github.com/cake-tech/monero.git ${MONERO_SRC_DIR} --branch ${MONERO_BRANCH} cd $MONERO_SRC_DIR -git submodule init -git submodule update +git submodule update --init --force for arch in "aarch" "aarch64" "i686" "x86_64" do @@ -59,7 +58,7 @@ cd $MONERO_SRC_DIR rm -rf ./build/release mkdir -p ./build/release cd ./build/release -CC=${CLANG} CXX=${CXXLANG} cmake -D USE_DEVICE_TREZOR=OFF -D BUILD_GUI_DEPS=1 -D BUILD_TESTS=OFF -D ARCH=${ARCH} -D STATIC=ON -D BUILD_64=${BUILD_64} -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D INSTALL_VENDORED_LIBUNBOUND=ON -D BUILD_TAG=${TAG} -D CMAKE_SYSTEM_NAME="Android" -D CMAKE_ANDROID_STANDALONE_TOOLCHAIN="${ANDROID_STANDALONE_TOOLCHAIN_PATH}" -D CMAKE_ANDROID_ARCH_ABI=${ARCH_ABI} $FLAGS ../.. +CC=${CLANG} CXX=${CXXLANG} cmake -D USE_DEVICE_TREZOR=OFF -D BUILD_GUI_DEPS=1 -D BUILD_TESTS=OFF -D ARCH=${ARCH} -D STATIC=ON -D BUILD_64=${BUILD_64} -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D INSTALL_VENDORED_LIBUNBOUND=ON -D BUILD_TAG=${TAG} -D CMAKE_SYSTEM_NAME="Android" -D CMAKE_ANDROID_STANDALONE_TOOLCHAIN="${ANDROID_STANDALONE_TOOLCHAIN_PATH}" -D CMAKE_ANDROID_ARCH_ABI=${ARCH_ABI} -D MANUAL_SUBMODULES=1 $FLAGS ../.. make wallet_api -j$THREADS find . -path ./lib -prune -o -name '*.a' -exec cp '{}' lib \; diff --git a/scripts/ios/app_env.sh b/scripts/ios/app_env.sh index 31f0b9548..1c778063d 100644 --- a/scripts/ios/app_env.sh +++ b/scripts/ios/app_env.sh @@ -13,13 +13,13 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN) APP_IOS_TYPE=$1 MONERO_COM_NAME="Monero.com" -MONERO_COM_VERSION="1.11.0" -MONERO_COM_BUILD_NUMBER=75 +MONERO_COM_VERSION="1.11.1" +MONERO_COM_BUILD_NUMBER=76 MONERO_COM_BUNDLE_ID="com.cakewallet.monero" CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="4.14.0" -CAKEWALLET_BUILD_NUMBER=215 +CAKEWALLET_VERSION="4.14.1" +CAKEWALLET_BUILD_NUMBER=216 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" HAVEN_NAME="Haven" diff --git a/scripts/ios/build_monero.sh b/scripts/ios/build_monero.sh index 54dda546f..6f06f22c1 100755 --- a/scripts/ios/build_monero.sh +++ b/scripts/ios/build_monero.sh @@ -4,7 +4,7 @@ MONERO_URL="https://github.com/cake-tech/monero.git" MONERO_DIR_PATH="${EXTERNAL_IOS_SOURCE_DIR}/monero" -MONERO_VERSION=release-v0.18.2.2 +MONERO_VERSION=release-v0.18.3.2 BUILD_TYPE=release PREFIX=${EXTERNAL_IOS_DIR} DEST_LIB_DIR=${EXTERNAL_IOS_LIB_DIR}/monero @@ -52,6 +52,7 @@ cmake -D IOS=ON \ -DUNBOUND_INCLUDE_DIR=${EXTERNAL_IOS_INCLUDE_DIR} \ -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} \ -DUSE_DEVICE_TREZOR=OFF \ + -DMANUAL_SUBMODULES=1 \ ../.. make wallet_api -j4 find . -path ./lib -prune -o -name '*.a' -exec cp '{}' lib \; diff --git a/scripts/ios/build_sodium.sh b/scripts/ios/build_sodium.sh index 66f383fb4..5632719ad 100755 --- a/scripts/ios/build_sodium.sh +++ b/scripts/ios/build_sodium.sh @@ -8,9 +8,10 @@ SODIUM_URL="https://github.com/jedisct1/libsodium.git" echo "============================ SODIUM ============================" echo "Cloning SODIUM from - $SODIUM_URL" -git clone $SODIUM_URL $SODIUM_PATH --branch stable +git clone $SODIUM_URL $SODIUM_PATH cd $SODIUM_PATH -./dist-build/ios.sh +git checkout 443617d7507498f7477703f0b51cb596d4539262 +./dist-build/apple-xcframework.sh -mv ${SODIUM_PATH}/libsodium-ios/include/* $EXTERNAL_IOS_INCLUDE_DIR -mv ${SODIUM_PATH}/libsodium-ios/lib/* $EXTERNAL_IOS_LIB_DIR \ No newline at end of file +mv ${SODIUM_PATH}/libsodium-apple/ios/include/* $EXTERNAL_IOS_INCLUDE_DIR +mv ${SODIUM_PATH}/libsodium-apple/ios/lib/* $EXTERNAL_IOS_LIB_DIR \ No newline at end of file diff --git a/scripts/macos/app_env.sh b/scripts/macos/app_env.sh index 4dec47f40..4c84832e6 100755 --- a/scripts/macos/app_env.sh +++ b/scripts/macos/app_env.sh @@ -16,13 +16,13 @@ if [ -n "$1" ]; then fi MONERO_COM_NAME="Monero.com" -MONERO_COM_VERSION="1.1.0" -MONERO_COM_BUILD_NUMBER=7 +MONERO_COM_VERSION="1.1.1" +MONERO_COM_BUILD_NUMBER=9 MONERO_COM_BUNDLE_ID="com.cakewallet.monero" CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="1.7.0" -CAKEWALLET_BUILD_NUMBER=55 +CAKEWALLET_VERSION="1.7.1" +CAKEWALLET_BUILD_NUMBER=56 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" if ! [[ " ${TYPES[*]} " =~ " ${APP_MACOS_TYPE} " ]]; then diff --git a/scripts/macos/build_monero.sh b/scripts/macos/build_monero.sh index c1ee0e5d3..1af7ae0e3 100755 --- a/scripts/macos/build_monero.sh +++ b/scripts/macos/build_monero.sh @@ -4,7 +4,7 @@ MONERO_URL="https://github.com/cake-tech/monero.git" MONERO_DIR_PATH="${EXTERNAL_MACOS_SOURCE_DIR}/monero" -MONERO_VERSION=release-v0.18.2.2 +MONERO_VERSION=release-v0.18.3.2 BUILD_TYPE=release PREFIX=${EXTERNAL_MACOS_DIR} DEST_LIB_DIR=${EXTERNAL_MACOS_LIB_DIR}/monero @@ -46,6 +46,7 @@ cmake -DARCH=${ARCH} \ -DUNBOUND_INCLUDE_DIR=${EXTERNAL_MACOS_INCLUDE_DIR} \ -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} \ -DUSE_DEVICE_TREZOR=OFF \ + -DMANUAL_SUBMODULES=1 \ ../.. make wallet_api -j4 find . -path ./lib -prune -o -name '*.a' -exec cp '{}' lib \;