diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index fc8f9b8ae..076fc2ea1 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -99,6 +99,7 @@ jobs: echo "const backupSalt = '${{ secrets.BACKUP_SALT }}';" >> lib/.secrets.g.dart echo "const backupKeychainSalt = '${{ secrets.BACKUP_KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart echo "const changeNowApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart + echo "const changeNowApiKeyDesktop = '${{ secrets.CHANGE_NOW_API_KEY_DESKTOP }}';" >> lib/.secrets.g.dart echo "const wyreSecretKey = '${{ secrets.WYRE_SECRET_KEY }}';" >> lib/.secrets.g.dart echo "const wyreApiKey = '${{ secrets.WYRE_API_KEY }}';" >> lib/.secrets.g.dart echo "const wyreAccountId = '${{ secrets.WYRE_ACCOUNT_ID }}';" >> lib/.secrets.g.dart @@ -107,6 +108,7 @@ jobs: echo "const sideShiftAffiliateId = '${{ secrets.SIDE_SHIFT_AFFILIATE_ID }}';" >> lib/.secrets.g.dart echo "const sideShiftApiKey = '${{ secrets.SIDE_SHIFT_API_KEY }}';" >> lib/.secrets.g.dart echo "const simpleSwapApiKey = '${{ secrets.SIMPLE_SWAP_API_KEY }}';" >> lib/.secrets.g.dart + echo "const simpleSwapApiKeyDesktop = '${{ secrets.SIMPLE_SWAP_API_KEY_DESKTOP }}';" >> lib/.secrets.g.dart echo "const onramperApiKey = '${{ secrets.ONRAMPER_API_KEY }}';" >> lib/.secrets.g.dart echo "const anypayToken = '${{ secrets.ANY_PAY_TOKEN }}';" >> lib/.secrets.g.dart echo "const ioniaClientId = '${{ secrets.IONIA_CLIENT_ID }}';" >> lib/.secrets.g.dart diff --git a/.gitignore b/.gitignore index 7e3f38beb..9fb7fd204 100644 --- a/.gitignore +++ b/.gitignore @@ -135,3 +135,8 @@ assets/images/app_logo.png /pubspec.lock /android/app.properties /android/app/src/main/AndroidManifest.xml + + +macos/Runner/Info.plist +macos/Runner/DebugProfile.entitlements +macos/Runner/Release.entitlements \ No newline at end of file diff --git a/.metadata b/.metadata index e0236519d..cdddb9350 100644 --- a/.metadata +++ b/.metadata @@ -1,10 +1,30 @@ # This file tracks properties of this Flutter project. # Used by Flutter tool to assess capabilities and perform upgrades etc. # -# This file should be version controlled and should not be manually edited. +# This file should be version controlled. version: - revision: 20e59316b8b8474554b38493b8ca888794b0234a + revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 channel: stable project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 + base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 + - platform: macos + create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 + base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/android/app/src/main/java/com/cakewallet/cake_wallet/MainActivity.java b/android/app/src/main/java/com/cakewallet/cake_wallet/MainActivity.java index 19373aab2..1c1e42df8 100644 --- a/android/app/src/main/java/com/cakewallet/cake_wallet/MainActivity.java +++ b/android/app/src/main/java/com/cakewallet/cake_wallet/MainActivity.java @@ -41,14 +41,6 @@ public class MainActivity extends FlutterFragmentActivity { try { switch (call.method) { - case "enableWakeScreen": - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - handler.post(() -> result.success(true)); - break; - case "disableWakeScreen": - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - handler.post(() -> result.success(true)); - break; case "sec_random": int count = call.argument("count"); SecureRandom random = new SecureRandom(); diff --git a/android/app/src/main/java/com/cakewallet/haven/MainActivity.java b/android/app/src/main/java/com/cakewallet/haven/MainActivity.java index 065af9318..8c13d1f8d 100644 --- a/android/app/src/main/java/com/cakewallet/haven/MainActivity.java +++ b/android/app/src/main/java/com/cakewallet/haven/MainActivity.java @@ -40,14 +40,6 @@ public class MainActivity extends FlutterFragmentActivity { try { switch (call.method) { - case "enableWakeScreen": - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - handler.post(() -> result.success(true)); - break; - case "disableWakeScreen": - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - handler.post(() -> result.success(true)); - break; case "sec_random": int count = call.argument("count"); SecureRandom random = new SecureRandom(); diff --git a/android/app/src/main/java/com/monero/app/MainActivity.java b/android/app/src/main/java/com/monero/app/MainActivity.java index 385932b38..f9e4f0882 100644 --- a/android/app/src/main/java/com/monero/app/MainActivity.java +++ b/android/app/src/main/java/com/monero/app/MainActivity.java @@ -40,14 +40,6 @@ public class MainActivity extends FlutterFragmentActivity { try { switch (call.method) { - case "enableWakeScreen": - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - handler.post(() -> result.success(true)); - break; - case "disableWakeScreen": - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - handler.post(() -> result.success(true)); - break; case "sec_random": int count = call.argument("count"); SecureRandom random = new SecureRandom(); diff --git a/assets/images/desktop_transactions_outline_icon.png b/assets/images/desktop_transactions_outline_icon.png new file mode 100644 index 000000000..38fc38866 Binary files /dev/null and b/assets/images/desktop_transactions_outline_icon.png differ diff --git a/assets/images/desktop_transactions_solid_icon.png b/assets/images/desktop_transactions_solid_icon.png new file mode 100644 index 000000000..d793803da Binary files /dev/null and b/assets/images/desktop_transactions_solid_icon.png differ diff --git a/assets/images/settings_outline.png b/assets/images/settings_outline.png new file mode 100644 index 000000000..3230f00c4 Binary files /dev/null and b/assets/images/settings_outline.png differ diff --git a/assets/images/support_icon.png b/assets/images/support_icon.png new file mode 100644 index 000000000..0d6ec99fd Binary files /dev/null and b/assets/images/support_icon.png differ diff --git a/assets/images/wallet_outline.png b/assets/images/wallet_outline.png new file mode 100644 index 000000000..e75131b05 Binary files /dev/null and b/assets/images/wallet_outline.png differ diff --git a/assets/images/wallet_solid.png b/assets/images/wallet_solid.png new file mode 100644 index 000000000..0997221f2 Binary files /dev/null and b/assets/images/wallet_solid.png differ diff --git a/cw_monero/.gitignore b/cw_monero/.gitignore index c8bb78494..ebb19df82 100644 --- a/cw_monero/.gitignore +++ b/cw_monero/.gitignore @@ -8,4 +8,7 @@ build/ ios/External/ android/.externalNativeBuild/ -android/.cxx/ \ No newline at end of file +android/.cxx/ + +macos/cw_monero.podspec +macos/External/ \ No newline at end of file diff --git a/cw_monero/.metadata b/cw_monero/.metadata index 36ba765ff..46a2f7f6f 100644 --- a/cw_monero/.metadata +++ b/cw_monero/.metadata @@ -1,10 +1,30 @@ # This file tracks properties of this Flutter project. # Used by Flutter tool to assess capabilities and perform upgrades etc. # -# This file should be version controlled and should not be manually edited. +# This file should be version controlled. version: - revision: 798e4272a2e43d7daab75f225a13442e384ee0cd - channel: dev + revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 + channel: stable project_type: plugin + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 + base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 + - platform: macos + create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 + base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/cw_monero/analysis_options.yaml b/cw_monero/analysis_options.yaml new file mode 100644 index 000000000..a5744c1cf --- /dev/null +++ b/cw_monero/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/cw_monero/example/.gitignore b/cw_monero/example/.gitignore new file mode 100644 index 000000000..24476c5d1 --- /dev/null +++ b/cw_monero/example/.gitignore @@ -0,0 +1,44 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/cw_monero/example/README.md b/cw_monero/example/README.md new file mode 100644 index 000000000..18cf6d109 --- /dev/null +++ b/cw_monero/example/README.md @@ -0,0 +1,16 @@ +# cw_monero_example + +Demonstrates how to use the cw_monero plugin. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/cw_monero/example/analysis_options.yaml b/cw_monero/example/analysis_options.yaml new file mode 100644 index 000000000..61b6c4de1 --- /dev/null +++ b/cw_monero/example/analysis_options.yaml @@ -0,0 +1,29 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/cw_monero/example/lib/main.dart b/cw_monero/example/lib/main.dart new file mode 100644 index 000000000..e4374f097 --- /dev/null +++ b/cw_monero/example/lib/main.dart @@ -0,0 +1,63 @@ +import 'package:flutter/material.dart'; +import 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:cw_monero/cw_monero.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatefulWidget { + const MyApp({super.key}); + + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + String _platformVersion = 'Unknown'; + final _cwMoneroPlugin = CwMonero(); + + @override + void initState() { + super.initState(); + initPlatformState(); + } + + // Platform messages are asynchronous, so we initialize in an async method. + Future initPlatformState() async { + String platformVersion; + // Platform messages may fail, so we use a try/catch PlatformException. + // We also handle the message potentially returning null. + try { + platformVersion = + await _cwMoneroPlugin.getPlatformVersion() ?? 'Unknown platform version'; + } on PlatformException { + platformVersion = 'Failed to get platform version.'; + } + + // If the widget was removed from the tree while the asynchronous platform + // message was in flight, we want to discard the reply rather than calling + // setState to update our non-existent appearance. + if (!mounted) return; + + setState(() { + _platformVersion = platformVersion; + }); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('Plugin example app'), + ), + body: Center( + child: Text('Running on: $_platformVersion\n'), + ), + ), + ); + } +} diff --git a/cw_monero/example/macos/.gitignore b/cw_monero/example/macos/.gitignore new file mode 100644 index 000000000..746adbb6b --- /dev/null +++ b/cw_monero/example/macos/.gitignore @@ -0,0 +1,7 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ diff --git a/cw_monero/example/macos/Flutter/Flutter-Debug.xcconfig b/cw_monero/example/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 000000000..4b81f9b2d --- /dev/null +++ b/cw_monero/example/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/cw_monero/example/macos/Flutter/Flutter-Release.xcconfig b/cw_monero/example/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 000000000..5caa9d157 --- /dev/null +++ b/cw_monero/example/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/cw_monero/example/macos/Flutter/GeneratedPluginRegistrant.swift b/cw_monero/example/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 000000000..e25d64097 --- /dev/null +++ b/cw_monero/example/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,14 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + +import cw_monero +import path_provider_foundation + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + CwMoneroPlugin.register(with: registry.registrar(forPlugin: "CwMoneroPlugin")) + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) +} diff --git a/cw_monero/example/macos/Podfile b/cw_monero/example/macos/Podfile new file mode 100644 index 000000000..dade8dfad --- /dev/null +++ b/cw_monero/example/macos/Podfile @@ -0,0 +1,40 @@ +platform :osx, '10.11' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end diff --git a/cw_monero/example/macos/Podfile.lock b/cw_monero/example/macos/Podfile.lock new file mode 100644 index 000000000..692176b30 --- /dev/null +++ b/cw_monero/example/macos/Podfile.lock @@ -0,0 +1,22 @@ +PODS: + - FlutterMacOS (1.0.0) + - path_provider_macos (0.0.1): + - FlutterMacOS + +DEPENDENCIES: + - FlutterMacOS (from `Flutter/ephemeral`) + - path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`) + +EXTERNAL SOURCES: + FlutterMacOS: + :path: Flutter/ephemeral + path_provider_macos: + :path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos + +SPEC CHECKSUMS: + FlutterMacOS: ae6af50a8ea7d6103d888583d46bd8328a7e9811 + path_provider_macos: 3c0c3b4b0d4a76d2bf989a913c2de869c5641a19 + +PODFILE CHECKSUM: 6eac6b3292e5142cfc23bdeb71848a40ec51c14c + +COCOAPODS: 1.11.2 diff --git a/cw_monero/example/macos/Runner.xcodeproj/project.pbxproj b/cw_monero/example/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000..472859e8c --- /dev/null +++ b/cw_monero/example/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,632 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + 428E7496E2068D0AB138F295 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C29B2253BA962B7A415DBA77 /* Pods_Runner.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* cw_monero_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = cw_monero_example.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + A9CDA1605413332AB9056C23 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + C29B2253BA962B7A415DBA77 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + E434913D71DC2682EF8E9059 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + EEF09839C86335F78056F812 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 428E7496E2068D0AB138F295 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + 77870A4C94A9AB6EEC2EE261 /* Pods */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* cw_monero_example.app */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + 77870A4C94A9AB6EEC2EE261 /* Pods */ = { + isa = PBXGroup; + children = ( + EEF09839C86335F78056F812 /* Pods-Runner.debug.xcconfig */, + A9CDA1605413332AB9056C23 /* Pods-Runner.release.xcconfig */, + E434913D71DC2682EF8E9059 /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + C29B2253BA962B7A415DBA77 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 0A239C1738C005E3F6E4DFC6 /* [CP] Check Pods Manifest.lock */, + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + 0CEAA82AE8A029C31B39F234 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* cw_monero_example.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 0A239C1738C005E3F6E4DFC6 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 0CEAA82AE8A029C31B39F234 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/cw_monero/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/cw_monero/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/cw_monero/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/cw_monero/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/cw_monero/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000..4e44b7ced --- /dev/null +++ b/cw_monero/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cw_monero/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/cw_monero/example/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..21a3cc14c --- /dev/null +++ b/cw_monero/example/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/cw_monero/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/cw_monero/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/cw_monero/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/cw_monero/example/macos/Runner/AppDelegate.swift b/cw_monero/example/macos/Runner/AppDelegate.swift new file mode 100644 index 000000000..d53ef6437 --- /dev/null +++ b/cw_monero/example/macos/Runner/AppDelegate.swift @@ -0,0 +1,9 @@ +import Cocoa +import FlutterMacOS + +@NSApplicationMain +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} diff --git a/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..a2ec33f19 --- /dev/null +++ b/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 000000000..82b6f9d9a Binary files /dev/null and b/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ diff --git a/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 000000000..13b35eba5 Binary files /dev/null and b/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ diff --git a/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 000000000..0a3f5fa40 Binary files /dev/null and b/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ diff --git a/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png new file mode 100644 index 000000000..bdb57226d Binary files /dev/null and b/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ diff --git a/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png new file mode 100644 index 000000000..f083318e0 Binary files /dev/null and b/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ diff --git a/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png new file mode 100644 index 000000000..326c0e72c Binary files /dev/null and b/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ diff --git a/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 000000000..2f1632cfd Binary files /dev/null and b/cw_monero/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ diff --git a/cw_monero/example/macos/Runner/Base.lproj/MainMenu.xib b/cw_monero/example/macos/Runner/Base.lproj/MainMenu.xib new file mode 100644 index 000000000..80e867a4e --- /dev/null +++ b/cw_monero/example/macos/Runner/Base.lproj/MainMenu.xib @@ -0,0 +1,343 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cw_monero/example/macos/Runner/Configs/AppInfo.xcconfig b/cw_monero/example/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 000000000..a80a25602 --- /dev/null +++ b/cw_monero/example/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = cw_monero_example + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.cakewallet.cwMoneroExample + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2022 com.cakewallet. All rights reserved. diff --git a/cw_monero/example/macos/Runner/Configs/Debug.xcconfig b/cw_monero/example/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 000000000..36b0fd946 --- /dev/null +++ b/cw_monero/example/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/cw_monero/example/macos/Runner/Configs/Release.xcconfig b/cw_monero/example/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 000000000..dff4f4956 --- /dev/null +++ b/cw_monero/example/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/cw_monero/example/macos/Runner/Configs/Warnings.xcconfig b/cw_monero/example/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 000000000..42bcbf478 --- /dev/null +++ b/cw_monero/example/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/cw_monero/example/macos/Runner/DebugProfile.entitlements b/cw_monero/example/macos/Runner/DebugProfile.entitlements new file mode 100644 index 000000000..dddb8a30c --- /dev/null +++ b/cw_monero/example/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + + diff --git a/cw_monero/example/macos/Runner/Info.plist b/cw_monero/example/macos/Runner/Info.plist new file mode 100644 index 000000000..4789daa6a --- /dev/null +++ b/cw_monero/example/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/cw_monero/example/macos/Runner/MainFlutterWindow.swift b/cw_monero/example/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 000000000..2722837ec --- /dev/null +++ b/cw_monero/example/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController.init() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/cw_monero/example/macos/Runner/Release.entitlements b/cw_monero/example/macos/Runner/Release.entitlements new file mode 100644 index 000000000..852fa1a47 --- /dev/null +++ b/cw_monero/example/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/cw_monero/example/pubspec.lock b/cw_monero/example/pubspec.lock new file mode 100644 index 000000000..772ff47bd --- /dev/null +++ b/cw_monero/example/pubspec.lock @@ -0,0 +1,403 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + args: + dependency: transitive + description: + name: args + sha256: "139d809800a412ebb26a3892da228b2d0ba36f0ef5d9a82166e5e52ec8d61611" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + asn1lib: + dependency: transitive + description: + name: asn1lib + sha256: ab96a1cb3beeccf8145c52e449233fe68364c9641623acd3adad66f8184f1039 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + async: + dependency: transitive + description: + name: async + sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + url: "https://pub.dev" + source: hosted + version: "2.10.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + characters: + dependency: transitive + description: + name: characters + sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + url: "https://pub.dev" + source: hosted + version: "1.2.1" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + collection: + dependency: transitive + description: + name: collection + sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + url: "https://pub.dev" + source: hosted + version: "1.17.0" + convert: + dependency: transitive + description: + name: convert + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + crypto: + dependency: transitive + description: + name: crypto + sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67 + url: "https://pub.dev" + source: hosted + version: "3.0.2" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be + url: "https://pub.dev" + source: hosted + version: "1.0.5" + cw_core: + dependency: transitive + description: + path: "../../cw_core" + relative: true + source: path + version: "0.0.1" + cw_monero: + dependency: "direct main" + description: + path: ".." + relative: true + source: path + version: "0.0.1" + encrypt: + dependency: transitive + description: + name: encrypt + sha256: "4fd4e4fdc21b9d7d4141823e1e6515cd94e7b8d84749504c232999fba25d9bbb" + url: "https://pub.dev" + source: hosted + version: "5.0.1" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: "13a6ccf6a459a125b3fcdb6ec73bd5ff90822e071207c663bfd1f70062d51d18" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + file: + dependency: transitive + description: + name: file + sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + url: "https://pub.dev" + source: hosted + version: "6.1.4" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + url: "https://pub.dev" + source: hosted + version: "2.0.1" + flutter_mobx: + dependency: transitive + description: + name: flutter_mobx + sha256: "0da4add0016387a7bf309a0d0c41d36c6b3ae25ed7a176409267f166509e723e" + url: "https://pub.dev" + source: hosted + version: "2.0.6+5" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + http: + dependency: transitive + description: + name: http + sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482" + url: "https://pub.dev" + source: hosted + version: "0.13.5" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + intl: + dependency: transitive + description: + name: intl + sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91" + url: "https://pub.dev" + source: hosted + version: "0.17.0" + js: + dependency: transitive + description: + name: js + sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + url: "https://pub.dev" + source: hosted + version: "0.6.5" + lints: + dependency: transitive + description: + name: lints + sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + url: "https://pub.dev" + source: hosted + version: "2.0.1" + matcher: + dependency: transitive + description: + name: matcher + sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" + url: "https://pub.dev" + source: hosted + version: "0.12.13" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + url: "https://pub.dev" + source: hosted + version: "0.2.0" + meta: + dependency: transitive + description: + name: meta + sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + url: "https://pub.dev" + source: hosted + version: "1.8.0" + mobx: + dependency: transitive + description: + name: mobx + sha256: f1862bd92c6a903fab67338f27e2f731117c3cb9ea37cee1a487f9e4e0de314a + url: "https://pub.dev" + source: hosted + version: "2.1.3+1" + path: + dependency: transitive + description: + name: path + sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b + url: "https://pub.dev" + source: hosted + version: "1.8.2" + path_provider: + dependency: transitive + description: + name: path_provider + sha256: dcea5feb97d8abf90cab9e9030b497fb7c3cbf26b7a1fe9e3ef7dcb0a1ddec95 + url: "https://pub.dev" + source: hosted + version: "2.0.12" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: a776c088d671b27f6e3aa8881d64b87b3e80201c64e8869b811325de7a76c15e + url: "https://pub.dev" + source: hosted + version: "2.0.22" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "62a68e7e1c6c459f9289859e2fae58290c981ce21d1697faf54910fe1faa4c74" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: ab0987bf95bc591da42dffb38c77398fc43309f0b9b894dcc5d6f40c4b26c379 + url: "https://pub.dev" + source: hosted + version: "2.1.7" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: f0abc8ebd7253741f05488b4813d936b4d07c6bae3e86148a09e342ee4b08e76 + url: "https://pub.dev" + source: hosted + version: "2.0.5" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: a34ecd7fb548f8e57321fd8e50d865d266941b54e6c3b7758cf8f37c24116905 + url: "https://pub.dev" + source: hosted + version: "2.0.7" + platform: + dependency: transitive + description: + name: platform + sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a + url: "https://pub.dev" + source: hosted + version: "2.1.3" + pointycastle: + dependency: transitive + description: + name: pointycastle + sha256: db7306cf0249f838d1a24af52b5a5887c5bf7f31d8bb4e827d071dc0939ad346 + url: "https://pub.dev" + source: hosted + version: "3.6.2" + process: + dependency: transitive + description: + name: process + sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" + url: "https://pub.dev" + source: hosted + version: "4.2.4" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + url: "https://pub.dev" + source: hosted + version: "1.9.1" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + url: "https://pub.dev" + source: hosted + version: "1.11.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 + url: "https://pub.dev" + source: hosted + version: "0.4.16" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + win32: + dependency: transitive + description: + name: win32 + sha256: c0e3a4f7be7dae51d8f152230b86627e3397c1ba8c3fa58e63d44a9f3edc9cef + url: "https://pub.dev" + source: hosted + version: "2.6.1" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: bd512f03919aac5f1313eb8249f223bacf4927031bf60b02601f81f687689e86 + url: "https://pub.dev" + source: hosted + version: "0.2.0+3" +sdks: + dart: ">=2.18.1 <3.0.0" + flutter: ">=3.0.0" diff --git a/cw_monero/example/pubspec.yaml b/cw_monero/example/pubspec.yaml new file mode 100644 index 000000000..2dee5337f --- /dev/null +++ b/cw_monero/example/pubspec.yaml @@ -0,0 +1,84 @@ +name: cw_monero_example +description: Demonstrates how to use the cw_monero plugin. + +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +environment: + sdk: '>=2.18.1 <3.0.0' + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + cw_monero: + # When depending on this package from a real application you should use: + # cw_monero: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^2.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/cw_monero/example/test/widget_test.dart b/cw_monero/example/test/widget_test.dart new file mode 100644 index 000000000..b37e6313d --- /dev/null +++ b/cw_monero/example/test/widget_test.dart @@ -0,0 +1,27 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:cw_monero_example/main.dart'; + +void main() { + testWidgets('Verify Platform version', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that platform version is retrieved. + expect( + find.byWidgetPredicate( + (Widget widget) => widget is Text && + widget.data!.startsWith('Running on:'), + ), + findsOneWidget, + ); + }); +} diff --git a/cw_monero/lib/cw_monero.dart b/cw_monero/lib/cw_monero.dart new file mode 100644 index 000000000..7945a020e --- /dev/null +++ b/cw_monero/lib/cw_monero.dart @@ -0,0 +1,8 @@ + +import 'cw_monero_platform_interface.dart'; + +class CwMonero { + Future getPlatformVersion() { + return CwMoneroPlatform.instance.getPlatformVersion(); + } +} diff --git a/cw_monero/lib/cw_monero_method_channel.dart b/cw_monero/lib/cw_monero_method_channel.dart new file mode 100644 index 000000000..1cbca9f2c --- /dev/null +++ b/cw_monero/lib/cw_monero_method_channel.dart @@ -0,0 +1,17 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; + +import 'cw_monero_platform_interface.dart'; + +/// An implementation of [CwMoneroPlatform] that uses method channels. +class MethodChannelCwMonero extends CwMoneroPlatform { + /// The method channel used to interact with the native platform. + @visibleForTesting + final methodChannel = const MethodChannel('cw_monero'); + + @override + Future getPlatformVersion() async { + final version = await methodChannel.invokeMethod('getPlatformVersion'); + return version; + } +} diff --git a/cw_monero/lib/cw_monero_platform_interface.dart b/cw_monero/lib/cw_monero_platform_interface.dart new file mode 100644 index 000000000..6c9b20a25 --- /dev/null +++ b/cw_monero/lib/cw_monero_platform_interface.dart @@ -0,0 +1,29 @@ +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'cw_monero_method_channel.dart'; + +abstract class CwMoneroPlatform extends PlatformInterface { + /// Constructs a CwMoneroPlatform. + CwMoneroPlatform() : super(token: _token); + + static final Object _token = Object(); + + static CwMoneroPlatform _instance = MethodChannelCwMonero(); + + /// The default instance of [CwMoneroPlatform] to use. + /// + /// Defaults to [MethodChannelCwMonero]. + static CwMoneroPlatform get instance => _instance; + + /// Platform-specific implementations should set this with their own + /// platform-specific class that extends [CwMoneroPlatform] when + /// they register themselves. + static set instance(CwMoneroPlatform instance) { + PlatformInterface.verifyToken(instance, _token); + _instance = instance; + } + + Future getPlatformVersion() { + throw UnimplementedError('platformVersion() has not been implemented.'); + } +} diff --git a/cw_monero/macos/Classes/CwMoneroPlugin.swift b/cw_monero/macos/Classes/CwMoneroPlugin.swift new file mode 100644 index 000000000..d4ff81e1c --- /dev/null +++ b/cw_monero/macos/Classes/CwMoneroPlugin.swift @@ -0,0 +1,19 @@ +import Cocoa +import FlutterMacOS + +public class CwMoneroPlugin: NSObject, FlutterPlugin { + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel(name: "cw_monero", binaryMessenger: registrar.messenger) + let instance = CwMoneroPlugin() + registrar.addMethodCallDelegate(instance, channel: channel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + switch call.method { + case "getPlatformVersion": + result("macOS " + ProcessInfo.processInfo.operatingSystemVersionString) + default: + result(FlutterMethodNotImplemented) + } + } +} diff --git a/cw_monero/macos/Classes/CwWalletListener.h b/cw_monero/macos/Classes/CwWalletListener.h new file mode 100644 index 000000000..cbfcb0c4e --- /dev/null +++ b/cw_monero/macos/Classes/CwWalletListener.h @@ -0,0 +1,23 @@ +#include + +struct CWMoneroWalletListener; + +typedef int8_t (*on_new_block_callback)(uint64_t height); +typedef int8_t (*on_need_to_refresh_callback)(); + +typedef struct CWMoneroWalletListener +{ + // on_money_spent_callback *on_money_spent; + // on_money_received_callback *on_money_received; + // on_unconfirmed_money_received_callback *on_unconfirmed_money_received; + // on_new_block_callback *on_new_block; + // on_updated_callback *on_updated; + // on_refreshed_callback *on_refreshed; + + on_new_block_callback on_new_block; +} CWMoneroWalletListener; + +struct TestListener { + // int8_t x; + on_new_block_callback on_new_block; +}; \ No newline at end of file diff --git a/cw_monero/macos/Classes/monero_api.cpp b/cw_monero/macos/Classes/monero_api.cpp new file mode 100644 index 000000000..56548e79e --- /dev/null +++ b/cw_monero/macos/Classes/monero_api.cpp @@ -0,0 +1,798 @@ +#include +#include "cstdlib" +#include +#include +#include +#include +#include +#include "thread" +#include "CwWalletListener.h" +#if __APPLE__ +// Fix for randomx on ios +void __clear_cache(void* start, void* end) { } +#include "../External/macos/include/wallet2_api.h" +#else +#include "../External/android/include/wallet2_api.h" +#endif + +using namespace std::chrono_literals; +#ifdef __cplusplus +extern "C" +{ +#endif + const uint64_t MONERO_BLOCK_SIZE = 1000; + + struct Utf8Box + { + char *value; + + Utf8Box(char *_value) + { + value = _value; + } + }; + + struct SubaddressRow + { + uint64_t id; + char *address; + char *label; + + SubaddressRow(std::size_t _id, char *_address, char *_label) + { + id = static_cast(_id); + address = _address; + label = _label; + } + }; + + struct AccountRow + { + uint64_t id; + char *label; + + AccountRow(std::size_t _id, char *_label) + { + id = static_cast(_id); + label = _label; + } + }; + + struct MoneroWalletListener : Monero::WalletListener + { + uint64_t m_height; + bool m_need_to_refresh; + bool m_new_transaction; + + MoneroWalletListener() + { + m_height = 0; + m_need_to_refresh = false; + m_new_transaction = false; + } + + void moneySpent(const std::string &txId, uint64_t amount) + { + m_new_transaction = true; + } + + void moneyReceived(const std::string &txId, uint64_t amount) + { + m_new_transaction = true; + } + + void unconfirmedMoneyReceived(const std::string &txId, uint64_t amount) + { + m_new_transaction = true; + } + + void newBlock(uint64_t height) + { + m_height = height; + } + + void updated() + { + m_new_transaction = true; + } + + void refreshed() + { + m_need_to_refresh = true; + } + + void resetNeedToRefresh() + { + m_need_to_refresh = false; + } + + bool isNeedToRefresh() + { + return m_need_to_refresh; + } + + bool isNewTransactionExist() + { + return m_new_transaction; + } + + void resetIsNewTransactionExist() + { + m_new_transaction = false; + } + + uint64_t height() + { + return m_height; + } + }; + + struct TransactionInfoRow + { + uint64_t amount; + uint64_t fee; + uint64_t blockHeight; + uint64_t confirmations; + uint32_t subaddrAccount; + int8_t direction; + int8_t isPending; + uint32_t subaddrIndex; + + char *hash; + char *paymentId; + + int64_t datetime; + + TransactionInfoRow(Monero::TransactionInfo *transaction) + { + amount = transaction->amount(); + fee = transaction->fee(); + blockHeight = transaction->blockHeight(); + subaddrAccount = transaction->subaddrAccount(); + std::set::iterator it = transaction->subaddrIndex().begin(); + subaddrIndex = *it; + confirmations = transaction->confirmations(); + datetime = static_cast(transaction->timestamp()); + direction = transaction->direction(); + isPending = static_cast(transaction->isPending()); + std::string *hash_str = new std::string(transaction->hash()); + hash = strdup(hash_str->c_str()); + paymentId = strdup(transaction->paymentId().c_str()); + } + }; + + struct PendingTransactionRaw + { + uint64_t amount; + uint64_t fee; + char *hash; + char *hex; + char *txKey; + Monero::PendingTransaction *transaction; + + PendingTransactionRaw(Monero::PendingTransaction *_transaction) + { + transaction = _transaction; + amount = _transaction->amount(); + fee = _transaction->fee(); + hash = strdup(_transaction->txid()[0].c_str()); + hex = strdup(_transaction->hex()[0].c_str()); + txKey = strdup(_transaction->txKey()[0].c_str()); + } + }; + + Monero::Wallet *m_wallet; + Monero::TransactionHistory *m_transaction_history; + MoneroWalletListener *m_listener; + Monero::Subaddress *m_subaddress; + Monero::SubaddressAccount *m_account; + uint64_t m_last_known_wallet_height; + uint64_t m_cached_syncing_blockchain_height = 0; + std::mutex store_lock; + bool is_storing = false; + + void change_current_wallet(Monero::Wallet *wallet) + { + m_wallet = wallet; + m_listener = nullptr; + + + if (wallet != nullptr) + { + m_transaction_history = wallet->history(); + } + else + { + m_transaction_history = nullptr; + } + + if (wallet != nullptr) + { + m_account = wallet->subaddressAccount(); + } + else + { + m_account = nullptr; + } + + if (wallet != nullptr) + { + m_subaddress = wallet->subaddress(); + } + else + { + m_subaddress = nullptr; + } + } + + Monero::Wallet *get_current_wallet() + { + return m_wallet; + } + + bool create_wallet(char *path, char *password, char *language, int32_t networkType, char *error) + { + Monero::NetworkType _networkType = static_cast(networkType); + Monero::WalletManager *walletManager = Monero::WalletManagerFactory::getWalletManager(); + Monero::Wallet *wallet = walletManager->createWallet(path, password, language, _networkType); + + int status; + std::string errorString; + + wallet->statusWithErrorString(status, errorString); + + if (wallet->status() != Monero::Wallet::Status_Ok) + { + error = strdup(wallet->errorString().c_str()); + return false; + } + + change_current_wallet(wallet); + + return true; + } + + bool restore_wallet_from_seed(char *path, char *password, char *seed, int32_t networkType, uint64_t restoreHeight, char *error) + { + Monero::NetworkType _networkType = static_cast(networkType); + Monero::Wallet *wallet = Monero::WalletManagerFactory::getWalletManager()->recoveryWallet( + std::string(path), + std::string(password), + std::string(seed), + _networkType, + (uint64_t)restoreHeight); + + int status; + std::string errorString; + + wallet->statusWithErrorString(status, errorString); + + if (status != Monero::Wallet::Status_Ok || !errorString.empty()) + { + error = strdup(errorString.c_str()); + return false; + } + + change_current_wallet(wallet); + return true; + } + + bool restore_wallet_from_keys(char *path, char *password, char *language, char *address, char *viewKey, char *spendKey, int32_t networkType, uint64_t restoreHeight, char *error) + { + Monero::NetworkType _networkType = static_cast(networkType); + Monero::Wallet *wallet = Monero::WalletManagerFactory::getWalletManager()->createWalletFromKeys( + std::string(path), + std::string(password), + std::string(language), + _networkType, + (uint64_t)restoreHeight, + std::string(address), + std::string(viewKey), + std::string(spendKey)); + + int status; + std::string errorString; + + wallet->statusWithErrorString(status, errorString); + + if (status != Monero::Wallet::Status_Ok || !errorString.empty()) + { + error = strdup(errorString.c_str()); + return false; + } + + change_current_wallet(wallet); + return true; + } + + bool load_wallet(char *path, char *password, int32_t nettype) + { + nice(19); + Monero::NetworkType networkType = static_cast(nettype); + Monero::WalletManager *walletManager = Monero::WalletManagerFactory::getWalletManager(); + Monero::Wallet *wallet = walletManager->openWallet(std::string(path), std::string(password), networkType); + int status; + std::string errorString; + + wallet->statusWithErrorString(status, errorString); + change_current_wallet(wallet); + + return !(status != Monero::Wallet::Status_Ok || !errorString.empty()); + } + + char *error_string() { + return strdup(get_current_wallet()->errorString().c_str()); + } + + + bool is_wallet_exist(char *path) + { + return Monero::WalletManagerFactory::getWalletManager()->walletExists(std::string(path)); + } + + void close_current_wallet() + { + Monero::WalletManagerFactory::getWalletManager()->closeWallet(get_current_wallet()); + change_current_wallet(nullptr); + } + + char *get_filename() + { + return strdup(get_current_wallet()->filename().c_str()); + } + + char *secret_view_key() + { + return strdup(get_current_wallet()->secretViewKey().c_str()); + } + + char *public_view_key() + { + return strdup(get_current_wallet()->publicViewKey().c_str()); + } + + char *secret_spend_key() + { + return strdup(get_current_wallet()->secretSpendKey().c_str()); + } + + char *public_spend_key() + { + return strdup(get_current_wallet()->publicSpendKey().c_str()); + } + + char *get_address(uint32_t account_index, uint32_t address_index) + { + return strdup(get_current_wallet()->address(account_index, address_index).c_str()); + } + + + const char *seed() + { + return strdup(get_current_wallet()->seed().c_str()); + } + + uint64_t get_full_balance(uint32_t account_index) + { + return get_current_wallet()->balance(account_index); + } + + uint64_t get_unlocked_balance(uint32_t account_index) + { + return get_current_wallet()->unlockedBalance(account_index); + } + + uint64_t get_current_height() + { + return get_current_wallet()->blockChainHeight(); + } + + uint64_t get_node_height() + { + return get_current_wallet()->daemonBlockChainHeight(); + } + + bool connect_to_node(char *error) + { + nice(19); + bool is_connected = get_current_wallet()->connectToDaemon(); + + if (!is_connected) + { + error = strdup(get_current_wallet()->errorString().c_str()); + } + + return is_connected; + } + + bool setup_node(char *address, char *login, char *password, bool use_ssl, bool is_light_wallet, char *error) + { + nice(19); + Monero::Wallet *wallet = get_current_wallet(); + + std::string _login = ""; + std::string _password = ""; + + if (login != nullptr) + { + _login = std::string(login); + } + + if (password != nullptr) + { + _password = std::string(password); + } + + bool inited = wallet->init(std::string(address), 0, _login, _password, use_ssl, is_light_wallet); + + if (!inited) + { + error = strdup(wallet->errorString().c_str()); + } else if (!wallet->connectToDaemon()) { + error = strdup(wallet->errorString().c_str()); + } + + return inited; + } + + bool is_connected() + { + return get_current_wallet()->connected(); + } + + void start_refresh() + { + get_current_wallet()->refreshAsync(); + get_current_wallet()->startRefresh(); + } + + void set_refresh_from_block_height(uint64_t height) + { + get_current_wallet()->setRefreshFromBlockHeight(height); + } + + void set_recovering_from_seed(bool is_recovery) + { + get_current_wallet()->setRecoveringFromSeed(is_recovery); + } + + void store(char *path) + { + store_lock.lock(); + if (is_storing) { + return; + } + + is_storing = true; + get_current_wallet()->store(std::string(path)); + is_storing = false; + store_lock.unlock(); + } + + bool set_password(char *password, Utf8Box &error) { + bool is_changed = get_current_wallet()->setPassword(std::string(password)); + + if (!is_changed) { + error = Utf8Box(strdup(get_current_wallet()->errorString().c_str())); + } + + return is_changed; + } + + bool transaction_create(char *address, char *payment_id, char *amount, + uint8_t priority_raw, uint32_t subaddr_account, Utf8Box &error, PendingTransactionRaw &pendingTransaction) + { + nice(19); + + auto priority = static_cast(priority_raw); + std::string _payment_id; + Monero::PendingTransaction *transaction; + + if (payment_id != nullptr) + { + _payment_id = std::string(payment_id); + } + + if (amount != nullptr) + { + uint64_t _amount = Monero::Wallet::amountFromString(std::string(amount)); + transaction = m_wallet->createTransaction(std::string(address), _payment_id, _amount, m_wallet->defaultMixin(), priority, subaddr_account); + } + else + { + transaction = m_wallet->createTransaction(std::string(address), _payment_id, Monero::optional(), m_wallet->defaultMixin(), priority, subaddr_account); + } + + int status = transaction->status(); + + if (status == Monero::PendingTransaction::Status::Status_Error || status == Monero::PendingTransaction::Status::Status_Critical) + { + error = Utf8Box(strdup(transaction->errorString().c_str())); + return false; + } + + if (m_listener != nullptr) { + m_listener->m_new_transaction = true; + } + + pendingTransaction = PendingTransactionRaw(transaction); + return true; + } + + bool transaction_create_mult_dest(char **addresses, char *payment_id, char **amounts, uint32_t size, + uint8_t priority_raw, uint32_t subaddr_account, Utf8Box &error, PendingTransactionRaw &pendingTransaction) + { + nice(19); + + std::vector _addresses; + std::vector _amounts; + + for (int i = 0; i < size; i++) { + _addresses.push_back(std::string(*addresses)); + _amounts.push_back(Monero::Wallet::amountFromString(std::string(*amounts))); + addresses++; + amounts++; + } + + auto priority = static_cast(priority_raw); + std::string _payment_id; + Monero::PendingTransaction *transaction; + + if (payment_id != nullptr) + { + _payment_id = std::string(payment_id); + } + + transaction = m_wallet->createTransactionMultDest(_addresses, _payment_id, _amounts, m_wallet->defaultMixin(), priority, subaddr_account); + + int status = transaction->status(); + + if (status == Monero::PendingTransaction::Status::Status_Error || status == Monero::PendingTransaction::Status::Status_Critical) + { + error = Utf8Box(strdup(transaction->errorString().c_str())); + return false; + } + + if (m_listener != nullptr) { + m_listener->m_new_transaction = true; + } + + pendingTransaction = PendingTransactionRaw(transaction); + return true; + } + + bool transaction_commit(PendingTransactionRaw *transaction, Utf8Box &error) + { + bool committed = transaction->transaction->commit(); + + if (!committed) + { + error = Utf8Box(strdup(transaction->transaction->errorString().c_str())); + } else if (m_listener != nullptr) { + m_listener->m_new_transaction = true; + } + + return committed; + } + + uint64_t get_node_height_or_update(uint64_t base_eight) + { + if (m_cached_syncing_blockchain_height < base_eight) { + m_cached_syncing_blockchain_height = base_eight; + } + + return m_cached_syncing_blockchain_height; + } + + uint64_t get_syncing_height() + { + if (m_listener == nullptr) { + return 0; + } + + uint64_t height = m_listener->height(); + + if (height <= 1) { + return 0; + } + + if (height != m_last_known_wallet_height) + { + m_last_known_wallet_height = height; + } + + return height; + } + + uint64_t is_needed_to_refresh() + { + if (m_listener == nullptr) { + return false; + } + + bool should_refresh = m_listener->isNeedToRefresh(); + + if (should_refresh) { + m_listener->resetNeedToRefresh(); + } + + return should_refresh; + } + + uint8_t is_new_transaction_exist() + { + if (m_listener == nullptr) { + return false; + } + + bool is_new_transaction_exist = m_listener->isNewTransactionExist(); + + if (is_new_transaction_exist) + { + m_listener->resetIsNewTransactionExist(); + } + + return is_new_transaction_exist; + } + + void set_listener() + { + m_last_known_wallet_height = 0; + + if (m_listener != nullptr) + { + free(m_listener); + } + + m_listener = new MoneroWalletListener(); + get_current_wallet()->setListener(m_listener); + } + + int64_t *subaddrress_get_all() + { + std::vector _subaddresses = m_subaddress->getAll(); + size_t size = _subaddresses.size(); + int64_t *subaddresses = (int64_t *)malloc(size * sizeof(int64_t)); + + for (int i = 0; i < size; i++) + { + Monero::SubaddressRow *row = _subaddresses[i]; + SubaddressRow *_row = new SubaddressRow(row->getRowId(), strdup(row->getAddress().c_str()), strdup(row->getLabel().c_str())); + subaddresses[i] = reinterpret_cast(_row); + } + + return subaddresses; + } + + int32_t subaddrress_size() + { + std::vector _subaddresses = m_subaddress->getAll(); + return _subaddresses.size(); + } + + void subaddress_add_row(uint32_t accountIndex, char *label) + { + m_subaddress->addRow(accountIndex, std::string(label)); + } + + void subaddress_set_label(uint32_t accountIndex, uint32_t addressIndex, char *label) + { + m_subaddress->setLabel(accountIndex, addressIndex, std::string(label)); + } + + void subaddress_refresh(uint32_t accountIndex) + { + m_subaddress->refresh(accountIndex); + } + + int32_t account_size() + { + std::vector _accocunts = m_account->getAll(); + return _accocunts.size(); + } + + int64_t *account_get_all() + { + std::vector _accocunts = m_account->getAll(); + size_t size = _accocunts.size(); + int64_t *accocunts = (int64_t *)malloc(size * sizeof(int64_t)); + + for (int i = 0; i < size; i++) + { + Monero::SubaddressAccountRow *row = _accocunts[i]; + AccountRow *_row = new AccountRow(row->getRowId(), strdup(row->getLabel().c_str())); + accocunts[i] = reinterpret_cast(_row); + } + + return accocunts; + } + + void account_add_row(char *label) + { + m_account->addRow(std::string(label)); + } + + void account_set_label_row(uint32_t account_index, char *label) + { + m_account->setLabel(account_index, label); + } + + void account_refresh() + { + m_account->refresh(); + } + + int64_t *transactions_get_all() + { + std::vector transactions = m_transaction_history->getAll(); + size_t size = transactions.size(); + int64_t *transactionAddresses = (int64_t *)malloc(size * sizeof(int64_t)); + + for (int i = 0; i < size; i++) + { + Monero::TransactionInfo *row = transactions[i]; + TransactionInfoRow *tx = new TransactionInfoRow(row); + transactionAddresses[i] = reinterpret_cast(tx); + } + + return transactionAddresses; + } + + void transactions_refresh() + { + m_transaction_history->refresh(); + } + + int64_t transactions_count() + { + return m_transaction_history->count(); + } + + int LedgerExchange( + unsigned char *command, + unsigned int cmd_len, + unsigned char *response, + unsigned int max_resp_len) + { + return -1; + } + + int LedgerFind(char *buffer, size_t len) + { + return -1; + } + + void on_startup() + { + Monero::Utils::onStartup(); + Monero::WalletManagerFactory::setLogLevel(0); + } + + void rescan_blockchain() + { + m_wallet->rescanBlockchainAsync(); + } + + char * get_tx_key(char * txId) + { + return strdup(m_wallet->getTxKey(std::string(txId)).c_str()); + } + + char *get_subaddress_label(uint32_t accountIndex, uint32_t addressIndex) + { + return strdup(get_current_wallet()->getSubaddressLabel(accountIndex, addressIndex).c_str()); + } + + void set_trusted_daemon(bool arg) + { + m_wallet->setTrustedDaemon(arg); + } + + bool trusted_daemon() + { + return m_wallet->trustedDaemon(); + } + +#ifdef __cplusplus +} +#endif diff --git a/cw_monero/macos/Classes/monero_api.h b/cw_monero/macos/Classes/monero_api.h new file mode 100644 index 000000000..74258ba4c --- /dev/null +++ b/cw_monero/macos/Classes/monero_api.h @@ -0,0 +1,38 @@ +#include +#include +#include +#include "CwWalletListener.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool create_wallet(char *path, char *password, char *language, int32_t networkType, char *error); +bool restore_wallet_from_seed(char *path, char *password, char *seed, int32_t networkType, uint64_t restoreHeight, char *error); +bool restore_wallet_from_keys(char *path, char *password, char *language, char *address, char *viewKey, char *spendKey, int32_t networkType, uint64_t restoreHeight, char *error); +void load_wallet(char *path, char *password, int32_t nettype); +bool is_wallet_exist(char *path); + +char *get_filename(); +const char *seed(); +char *get_address(uint32_t account_index, uint32_t address_index); +uint64_t get_full_balance(uint32_t account_index); +uint64_t get_unlocked_balance(uint32_t account_index); +uint64_t get_current_height(); +uint64_t get_node_height(); + +bool is_connected(); + +bool setup_node(char *address, char *login, char *password, bool use_ssl, bool is_light_wallet, char *error); +bool connect_to_node(char *error); +void start_refresh(); +void set_refresh_from_block_height(uint64_t height); +void set_recovering_from_seed(bool is_recovery); +void store(char *path); + +void set_trusted_daemon(bool arg); +bool trusted_daemon(); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/cw_monero/macos/cw_monero_base.podspec b/cw_monero/macos/cw_monero_base.podspec new file mode 100644 index 000000000..aac972c0f --- /dev/null +++ b/cw_monero/macos/cw_monero_base.podspec @@ -0,0 +1,56 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint cw_monero.podspec` to validate before publishing. +# +Pod::Spec.new do |s| + s.name = 'cw_monero' + s.version = '0.0.1' + s.summary = 'CW Monero' + s.description = 'Cake Wallet wrapper over Monero project.' + s.homepage = 'http://cakewallet.com' + s.license = { :file => '../LICENSE' } + s.author = { 'CakeWallet' => 'support@cakewallet.com' } + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.dependency 'FlutterMacOS' + s.public_header_files = 'Classes/**/*.h, Classes/*.h, External/macos/libs/monero/include/External/ios/**/*.h' + s.platform = :osx, '10.11' + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS' => '#___VALID_ARCHS___#', 'ENABLE_BITCODE' => 'NO' } + s.swift_version = '5.0' + s.libraries = 'iconv' + + s.subspec 'OpenSSL' do |openssl| + openssl.preserve_paths = '../../../../../cw_shared_external/ios/External/macos/include/**/*.h' + openssl.vendored_libraries = '../../../../../cw_shared_external/ios/External/macos/lib/libcrypto.a', '../../../../../cw_shared_external/ios/External/ios/lib/libssl.a' + openssl.libraries = 'ssl', 'crypto' + openssl.xcconfig = { 'HEADER_SEARCH_PATHS' => "${PODS_ROOT}/#{s.name}/External/macos/include/**" } + end + + s.subspec 'Sodium' do |sodium| + sodium.preserve_paths = '../../../../../cw_shared_external/ios/External/macos/include/**/*.h' + sodium.vendored_libraries = '../../../../../cw_shared_external/ios/External/macos/lib/libsodium.a' + sodium.libraries = 'sodium' + sodium.xcconfig = { 'HEADER_SEARCH_PATHS' => "${PODS_ROOT}/#{s.name}/External/macos/include/**" } + end + + s.subspec 'Unbound' do |unbound| + unbound.preserve_paths = '../../../../../cw_shared_external/ios/External/macos/include/**/*.h' + unbound.vendored_libraries = '../../../../../cw_shared_external/ios/External/macos/lib/libunbound.a' + unbound.libraries = 'unbound' + unbound.xcconfig = { 'HEADER_SEARCH_PATHS' => "${PODS_ROOT}/#{s.name}/External/macos/include/**" } + end + + s.subspec 'Boost' do |boost| + boost.preserve_paths = '../../../../../cw_shared_external/ios/External/macos/include/**/*.h', + boost.vendored_libraries = '../../../../../cw_shared_external/ios/External/macos/lib/libboost.a', + boost.libraries = 'boost' + boost.xcconfig = { 'HEADER_SEARCH_PATHS' => "${PODS_ROOT}/#{s.name}/External/macos/include/**" } + end + + s.subspec 'Monero' do |monero| + monero.preserve_paths = 'External/macos/include/**/*.h' + monero.vendored_libraries = 'External/macos/lib/libmonero.a' + monero.libraries = 'monero' + monero.xcconfig = { 'HEADER_SEARCH_PATHS' => "${PODS_ROOT}/#{s.name}/External/macos/include" } + end +end diff --git a/cw_monero/pubspec.yaml b/cw_monero/pubspec.yaml index 6d5041dfa..066a0d4c3 100644 --- a/cw_monero/pubspec.yaml +++ b/cw_monero/pubspec.yaml @@ -40,8 +40,15 @@ flutter: # be modified. They are used by the tooling to maintain consistency when # adding or updating assets for this project. plugin: - androidPackage: com.cakewallet.monero - pluginClass: CwMoneroPlugin + platforms: + android: + package: com.cakewallet.monero + pluginClass: CwMoneroPlugin + ios: + pluginClass: CwMoneroPlugin + + macos: + pluginClass: CwMoneroPlugin # To add assets to your plugin package, add an assets section, like this: # assets: diff --git a/cw_monero/test/cw_monero_method_channel_test.dart b/cw_monero/test/cw_monero_method_channel_test.dart new file mode 100644 index 000000000..8c1f329f0 --- /dev/null +++ b/cw_monero/test/cw_monero_method_channel_test.dart @@ -0,0 +1,24 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:cw_monero/cw_monero_method_channel.dart'; + +void main() { + MethodChannelCwMonero platform = MethodChannelCwMonero(); + const MethodChannel channel = MethodChannel('cw_monero'); + + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() { + channel.setMockMethodCallHandler((MethodCall methodCall) async { + return '42'; + }); + }); + + tearDown(() { + channel.setMockMethodCallHandler(null); + }); + + test('getPlatformVersion', () async { + expect(await platform.getPlatformVersion(), '42'); + }); +} diff --git a/cw_monero/test/cw_monero_test.dart b/cw_monero/test/cw_monero_test.dart new file mode 100644 index 000000000..1eb8d6f79 --- /dev/null +++ b/cw_monero/test/cw_monero_test.dart @@ -0,0 +1,29 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:cw_monero/cw_monero.dart'; +import 'package:cw_monero/cw_monero_platform_interface.dart'; +import 'package:cw_monero/cw_monero_method_channel.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +class MockCwMoneroPlatform + with MockPlatformInterfaceMixin + implements CwMoneroPlatform { + + @override + Future getPlatformVersion() => Future.value('42'); +} + +void main() { + final CwMoneroPlatform initialPlatform = CwMoneroPlatform.instance; + + test('$MethodChannelCwMonero is the default instance', () { + expect(initialPlatform, isInstanceOf()); + }); + + test('getPlatformVersion', () async { + CwMonero cwMoneroPlugin = CwMonero(); + MockCwMoneroPlatform fakePlatform = MockCwMoneroPlatform(); + CwMoneroPlatform.instance = fakePlatform; + + expect(await cwMoneroPlugin.getPlatformVersion(), '42'); + }); +} diff --git a/ios/CakeWallet/wakeLock.swift b/ios/CakeWallet/wakeLock.swift deleted file mode 100644 index 35f23eafa..000000000 --- a/ios/CakeWallet/wakeLock.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// wakeLock.swift -// Runner -// -// Created by Godwin Asuquo on 1/21/22. -// - -import Foundation -import UIKit - -func enableWakeScreen() -> Bool{ - UIApplication.shared.isIdleTimerDisabled = true - - return true -} - -func disableWakeScreen() -> Bool{ - UIApplication.shared.isIdleTimerDisabled = false - return true -} diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 5148714e5..50b9da031 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -12,7 +12,6 @@ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 20ED0868E1BD7E12278C0CB3 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B26E3F56D69167FBB1DC160A /* Pods_Runner.framework */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 5AFFEBFD279AD49C00F906A4 /* wakeLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AFFEBFC279AD49C00F906A4 /* wakeLock.swift */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; @@ -282,7 +281,6 @@ files = ( 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, - 5AFFEBFD279AD49C00F906A4 /* wakeLock.swift in Sources */, 0C9D68C9264854B60011B691 /* secRandom.swift in Sources */, 0C44A71A2518EF8000B570ED /* decrypt.swift in Sources */, ); diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index e3f3da418..6d5f51aa3 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -96,11 +96,6 @@ import UnstoppableDomainsResolution result(address) } - case "enableWakeScreen": - result(enableWakeScreen()) - - case "disableWakeScreen": - result(disableWakeScreen()) default: result(FlutterMethodNotImplemented) diff --git a/lib/buy/moonpay/moonpay_buy_provider.dart b/lib/buy/moonpay/moonpay_buy_provider.dart index 4ff3fb04c..372b6d6cc 100644 --- a/lib/buy/moonpay/moonpay_buy_provider.dart +++ b/lib/buy/moonpay/moonpay_buy_provider.dart @@ -23,7 +23,7 @@ class MoonPaySellProvider { final bool isTest; final String baseUrl; - Future requestUrl({required CryptoCurrency currency, required String refundWalletAddress}) async { + Future requestUrl({required CryptoCurrency currency, required String refundWalletAddress}) async { final originalUri = Uri.https( baseUrl, '', { 'apiKey': _apiKey, @@ -37,13 +37,13 @@ class MoonPaySellProvider { final signature = base64.encode(digest.bytes); if (isTest) { - return originalUri.toString(); + return originalUri; } final query = Map.from(originalUri.queryParameters); query['signature'] = signature; final signedUri = originalUri.replace(queryParameters: query); - return signedUri.toString(); + return signedUri; } } diff --git a/lib/buy/onramper/onramper_buy_provider.dart b/lib/buy/onramper/onramper_buy_provider.dart new file mode 100644 index 000000000..a887f98dc --- /dev/null +++ b/lib/buy/onramper/onramper_buy_provider.dart @@ -0,0 +1,69 @@ +import 'package:cake_wallet/.secrets.g.dart' as secrets; +import 'package:cake_wallet/store/settings_store.dart'; +import 'package:cake_wallet/themes/theme_base.dart'; +import 'package:cw_core/wallet_base.dart'; + +class OnRamperBuyProvider { + OnRamperBuyProvider({required SettingsStore settingsStore, required WalletBase wallet}) + : this._settingsStore = settingsStore, + this._wallet = wallet; + + final SettingsStore _settingsStore; + final WalletBase _wallet; + + static const _baseUrl = 'buy.onramper.com'; + + static String get _apiKey => secrets.onramperApiKey; + + Uri requestUrl() { + String primaryColor, + secondaryColor, + primaryTextColor, + secondaryTextColor, + containerColor, + cardColor; + + switch (_settingsStore.currentTheme.type) { + case ThemeType.bright: + primaryColor = '815dfbff'; + secondaryColor = 'ffffff'; + primaryTextColor = '141519'; + secondaryTextColor = '6b6f80'; + containerColor = 'ffffff'; + cardColor = 'f2f0faff'; + break; + case ThemeType.light: + primaryColor = '2194ffff'; + secondaryColor = 'ffffff'; + primaryTextColor = '141519'; + secondaryTextColor = '6b6f80'; + containerColor = 'ffffff'; + cardColor = 'e5f7ff'; + break; + case ThemeType.dark: + primaryColor = '456effff'; + secondaryColor = '1b2747ff'; + primaryTextColor = 'ffffff'; + secondaryTextColor = 'ffffff'; + containerColor = '19233C'; + cardColor = '232f4fff'; + break; + } + + + return Uri.https(_baseUrl, '', { + 'apiKey': _apiKey, + 'defaultCrypto': _wallet.currency.title, + 'defaultFiat': _settingsStore.fiatCurrency.title, + 'wallets': '${_wallet.currency.title}:${_wallet.walletAddresses.address}', + 'supportSell': "false", + 'supportSwap': "false", + 'primaryColor': primaryColor, + 'secondaryColor': secondaryColor, + 'primaryTextColor': primaryTextColor, + 'secondaryTextColor': secondaryTextColor, + 'containerColor': containerColor, + 'cardColor': cardColor + }); + } +} diff --git a/lib/buy/payfura/payfura_buy_provider.dart b/lib/buy/payfura/payfura_buy_provider.dart new file mode 100644 index 000000000..eb9104df0 --- /dev/null +++ b/lib/buy/payfura/payfura_buy_provider.dart @@ -0,0 +1,24 @@ +import 'package:cake_wallet/.secrets.g.dart' as secrets; +import 'package:cake_wallet/store/settings_store.dart'; +import 'package:cw_core/wallet_base.dart'; + +class PayfuraBuyProvider { + PayfuraBuyProvider({required SettingsStore settingsStore, required WalletBase wallet}) + : this._settingsStore = settingsStore, + this._wallet = wallet; + + final SettingsStore _settingsStore; + final WalletBase _wallet; + + static const _baseUrl = 'exchange.payfura.com'; + + Uri requestUrl() { + return Uri.https(_baseUrl, '', { + 'apiKey': secrets.payfuraApiKey, + 'to': _wallet.currency.title, + 'from': _settingsStore.fiatCurrency.title, + 'walletAddress': '${_wallet.currency.title}:${_wallet.walletAddresses.address}', + 'mode': 'buy' + }); + } +} diff --git a/lib/di.dart b/lib/di.dart index 166fa218f..2d6e1ec34 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -1,17 +1,22 @@ import 'package:cake_wallet/anonpay/anonpay_api.dart'; import 'package:cake_wallet/anonpay/anonpay_info_base.dart'; import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart'; +import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart'; +import 'package:cake_wallet/buy/payfura/payfura_buy_provider.dart'; import 'package:cake_wallet/core/yat_service.dart'; import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/entities/parse_address_from_domain.dart'; import 'package:cake_wallet/entities/receive_page_option.dart'; -import 'package:cake_wallet/entities/wake_lock.dart'; import 'package:cake_wallet/ionia/ionia_anypay.dart'; import 'package:cake_wallet/ionia/ionia_gift_card.dart'; import 'package:cake_wallet/ionia/ionia_tip.dart'; import 'package:cake_wallet/src/screens/anonpay_details/anonpay_details_page.dart'; import 'package:cake_wallet/src/screens/buy/onramper_page.dart'; import 'package:cake_wallet/src/screens/buy/payfura_page.dart'; +import 'package:cake_wallet/src/screens/dashboard/desktop_dashboard_page.dart'; +import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sidebar_wrapper.dart'; +import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart'; +import 'package:cake_wallet/src/screens/dashboard/widgets/transactions_page.dart'; import 'package:cake_wallet/src/screens/receive/anonpay_invoice_page.dart'; import 'package:cake_wallet/src/screens/receive/anonpay_receive_page.dart'; import 'package:cake_wallet/src/screens/settings/display_settings_page.dart'; @@ -22,8 +27,11 @@ import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_redeem_page.dar import 'package:cake_wallet/src/screens/ionia/cards/ionia_gift_card_detail_page.dart'; import 'package:cake_wallet/src/screens/ionia/cards/ionia_more_options_page.dart'; import 'package:cake_wallet/src/screens/settings/connection_sync_page.dart'; +import 'package:cake_wallet/themes/theme_list.dart'; +import 'package:cake_wallet/utils/device_info.dart'; import 'package:cake_wallet/store/anonpay/anonpay_transactions_store.dart'; import 'package:cake_wallet/utils/payment_request.dart'; +import 'package:cake_wallet/view_model/dashboard/desktop_sidebar_view_model.dart'; import 'package:cake_wallet/view_model/anon_invoice_page_view_model.dart'; import 'package:cake_wallet/view_model/anonpay_details_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.dart'; @@ -143,7 +151,6 @@ import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart'; import 'package:cake_wallet/view_model/wallet_restore_view_model.dart'; import 'package:cake_wallet/view_model/wallet_seed_view_model.dart'; import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:get_it/get_it.dart'; import 'package:hive/hive.dart'; @@ -219,12 +226,16 @@ Future setup( () => SharedPreferences.getInstance()); } - final isBitcoinBuyEnabled = (secrets.wyreSecretKey?.isNotEmpty ?? false) && - (secrets.wyreApiKey?.isNotEmpty ?? false) && - (secrets.wyreAccountId?.isNotEmpty ?? false); + final isBitcoinBuyEnabled = (secrets.wyreSecretKey.isNotEmpty ?? false) && + (secrets.wyreApiKey.isNotEmpty ?? false) && + (secrets.wyreAccountId.isNotEmpty ?? false); final settingsStore = await SettingsStoreBase.load( - nodeSource: _nodeSource, isBitcoinBuyEnabled: isBitcoinBuyEnabled); + nodeSource: _nodeSource, + isBitcoinBuyEnabled: isBitcoinBuyEnabled, + // Enforce darkTheme on platforms other than mobile till the design for other themes is completed + initialTheme: DeviceInfo.instance.isMobile ? null : ThemeList.darkTheme, + ); if (_isSetupFinished) { return; @@ -375,42 +386,64 @@ Future setup( .registerFactoryParam( (onAuthFinished, closable) => AuthPage(getIt.get(), onAuthenticationFinished: onAuthFinished, - closable: closable ?? false)); + closable: closable)); getIt.registerFactory(() => BalancePage(dashboardViewModel: getIt.get(), settingsStore: getIt.get())); - getIt.registerFactory(() => DashboardPage( balancePage: getIt.get(), walletViewModel: getIt.get(), addressListViewModel: getIt.get())); - + getIt.registerFactory(() => DashboardPage( + balancePage: getIt.get(), + dashboardViewModel: getIt.get(), + addressListViewModel: getIt.get(), + )); + getIt.registerFactory(() { + final GlobalKey _navigatorKey = GlobalKey(); + return DesktopSidebarWrapper( + dashboardViewModel: getIt.get(), + desktopSidebarViewModel: getIt.get(), + child: getIt.get(param1: _navigatorKey), + desktopNavigatorKey: _navigatorKey, + ); + }); + getIt.registerFactoryParam, void>( + (desktopKey, _) => DesktopDashboardPage( + balancePage: getIt.get(), + dashboardViewModel: getIt.get(), + addressListViewModel: getIt.get(), + desktopKey: desktopKey, + )); + + getIt.registerFactory(() => TransactionsPage(dashboardViewModel: getIt.get())); + getIt.registerFactoryParam((pageOption, _) => ReceiveOptionViewModel( getIt.get().wallet!, pageOption)); - getIt.registerFactoryParam, void>((args, _) { + getIt.registerFactoryParam, void>((args, _) { final address = args.first as String; final pageOption = args.last as ReceivePageOption; return AnonInvoicePageViewModel( - getIt.get(), - address, - getIt.get(), - getIt.get().wallet!, - _anonpayInvoiceInfoSource, + getIt.get(), + address, + getIt.get(), + getIt.get().wallet!, + _anonpayInvoiceInfoSource, getIt.get(), pageOption, - ); + ); }); - getIt.registerFactoryParam, void>((List args, _) { + getIt.registerFactoryParam, void>((List args, _) { final pageOption = args.last as ReceivePageOption; return AnonPayInvoicePage( - getIt.get(param1: args), + getIt.get(param1: args), getIt.get(param1: pageOption)); - }); - + }); + getIt.registerFactory(() => ReceivePage( addressListViewModel: getIt.get())); getIt.registerFactory(() => AddressPage( addressListViewModel: getIt.get(), - walletViewModel: getIt.get(), + dashboardViewModel: getIt.get(), receiveOptionViewModel: getIt.get())); getIt.registerFactoryParam( @@ -445,13 +478,25 @@ Future setup( getIt.registerFactory(() => SendTemplatePage( sendTemplateViewModel: getIt.get())); - getIt.registerFactory(() => WalletListViewModel( - _walletInfoSource, - getIt.get(), - getIt.get(), - getIt.get(), - ), - ); + if (DeviceInfo.instance.isMobile) { + getIt.registerFactory(() => WalletListViewModel( + _walletInfoSource, + getIt.get(), + getIt.get(), + getIt.get(), + ), + ); + } else { + // register wallet list view model as singleton on desktop since it can be accessed + // from multiple places at the same time (Wallets DropDown, Wallets List in settings) + getIt.registerLazySingleton(() => WalletListViewModel( + _walletInfoSource, + getIt.get(), + getIt.get(), + getIt.get(), + ), + ); + } getIt.registerFactory(() => WalletListPage(walletListViewModel: getIt.get())); @@ -523,7 +568,7 @@ Future setup( isNewWalletCreated: isWalletCreated)); getIt - .registerFactory(() => WalletKeysViewModel(getIt.get().wallet!)); + .registerFactory(() => WalletKeysViewModel(getIt.get())); getIt.registerFactory(() => WalletKeysPage(getIt.get())); @@ -543,8 +588,7 @@ Future setup( getIt.registerFactory(() { final appStore = getIt.get(); - return NodeListViewModel( - _nodeSource, appStore.wallet!, appStore.settingsStore); + return NodeListViewModel(_nodeSource, appStore); }); getIt.registerFactory(() => ConnectionSyncPage(getIt.get(), getIt.get())); @@ -570,13 +614,19 @@ Future setup( editingNode: editingNode, isSelected: isSelected)); - getIt.registerFactory(() => OnRamperPage( + getIt.registerFactory(() => OnRamperBuyProvider( settingsStore: getIt.get().settingsStore, - wallet: getIt.get().wallet!)); + wallet: getIt.get().wallet!, + )); - getIt.registerFactory(() => PayFuraPage( - settingsStore: getIt.get().settingsStore, - wallet: getIt.get().wallet!)); + getIt.registerFactory(() => OnRamperPage(getIt.get())); + + getIt.registerFactory(() => PayfuraBuyProvider( + settingsStore: getIt.get().settingsStore, + wallet: getIt.get().wallet!, + )); + + getIt.registerFactory(() => PayFuraPage(getIt.get())); getIt.registerFactory(() => ExchangeViewModel( getIt.get().wallet!, @@ -759,8 +809,6 @@ Future setup( param1: item, param2: unspentCoinsListViewModel)); }); - getIt.registerFactory(() => WakeLock()); - getIt.registerFactory(() => YatService()); getIt.registerFactory(() => AddressResolver(yatService: getIt.get(), @@ -873,11 +921,15 @@ Future setup( getIt.registerFactory(() => IoniaAccountPage(getIt.get())); getIt.registerFactory(() => IoniaAccountCardsPage(getIt.get())); - + getIt.registerFactory(() => AnonPayApi(useTorOnly: getIt.get().exchangeStatus == ExchangeApiMode.torOnly, - wallet: getIt.get().wallet!) + wallet: getIt.get().wallet!) ); + getIt.registerFactory(() => DesktopWalletSelectionDropDown(getIt.get())); + + getIt.registerFactory(() => DesktopSidebarViewModel()); + getIt.registerFactoryParam( (AnonpayInvoiceInfo anonpayInvoiceInfo, _) => AnonpayDetailsViewModel( @@ -890,7 +942,7 @@ Future setup( (AnonpayInfoBase anonpayInvoiceInfo, _) => AnonPayReceivePage(invoiceInfo: anonpayInvoiceInfo)); getIt.registerFactoryParam( - (AnonpayInvoiceInfo anonpayInvoiceInfo, _) + (AnonpayInvoiceInfo anonpayInvoiceInfo, _) => AnonpayDetailsPage(anonpayDetailsViewModel: getIt.get(param1: anonpayInvoiceInfo))); getIt.registerFactoryParam( diff --git a/lib/entities/desktop_dropdown_item.dart b/lib/entities/desktop_dropdown_item.dart new file mode 100644 index 000000000..3a542f5e8 --- /dev/null +++ b/lib/entities/desktop_dropdown_item.dart @@ -0,0 +1,9 @@ +import 'package:flutter/material.dart'; + +class DesktopDropdownItem { + final Function() onSelected; + final Widget child; + final bool isSelected; + + DesktopDropdownItem({required this.onSelected, required this.child, this.isSelected = false}); +} diff --git a/lib/entities/main_actions.dart b/lib/entities/main_actions.dart new file mode 100644 index 000000000..ec70b95d9 --- /dev/null +++ b/lib/entities/main_actions.dart @@ -0,0 +1,142 @@ +import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart'; +import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart'; +import 'package:cake_wallet/buy/payfura/payfura_buy_provider.dart'; +import 'package:cake_wallet/di.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/routes.dart'; +import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; +import 'package:cake_wallet/utils/device_info.dart'; +import 'package:cake_wallet/utils/show_pop_up.dart'; +import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; +import 'package:cw_core/wallet_type.dart'; +import 'package:flutter/material.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class MainActions { + final String Function(BuildContext context) name; + final String image; + + final bool Function(DashboardViewModel viewModel)? isEnabled; + final bool Function(DashboardViewModel viewModel)? canShow; + final Future Function(BuildContext context, DashboardViewModel viewModel) onTap; + + MainActions._({ + required this.name, + required this.image, + this.isEnabled, + this.canShow, + required this.onTap, + }); + + static List all = [ + buyAction, + receiveAction, + exchangeAction, + sendAction, + sellAction, + ]; + + static MainActions buyAction = MainActions._( + name: (context) => S.of(context).buy, + image: 'assets/images/buy.png', + isEnabled: (viewModel) => viewModel.isEnabledBuyAction, + canShow: (viewModel) => viewModel.hasBuyAction, + onTap: (BuildContext context, DashboardViewModel viewModel) async { + final walletType = viewModel.type; + + switch (walletType) { + case WalletType.bitcoin: + case WalletType.litecoin: + if (DeviceInfo.instance.isMobile) { + Navigator.of(context).pushNamed(Routes.onramperPage); + } else { + final uri = getIt + .get() + .requestUrl(); + await launchUrl(uri); + } + break; + case WalletType.monero: + if (DeviceInfo.instance.isMobile) { + Navigator.of(context).pushNamed(Routes.payfuraPage); + } else { + final uri = getIt + .get() + .requestUrl(); + await launchUrl(uri); + } + break; + default: + await showPopUp( + context: context, + builder: (BuildContext context) { + return AlertWithOneAction( + alertTitle: S.of(context).buy, + alertContent: S.of(context).buy_alert_content, + buttonText: S.of(context).ok, + buttonAction: () => Navigator.of(context).pop()); + }); + } + }, + ); + + static MainActions receiveAction = MainActions._( + name: (context) => S.of(context).receive, + image: 'assets/images/received.png', + onTap: (BuildContext context, DashboardViewModel viewModel) async { + Navigator.pushNamed(context, Routes.addressPage); + }, + ); + + static MainActions exchangeAction = MainActions._( + name: (context) => S.of(context).exchange, + image: 'assets/images/transfer.png', + isEnabled: (viewModel) => viewModel.isEnabledExchangeAction, + canShow: (viewModel) => viewModel.hasExchangeAction, + onTap: (BuildContext context, DashboardViewModel viewModel) async { + if (viewModel.isEnabledExchangeAction) { + await Navigator.of(context).pushNamed(Routes.exchange); + } + }, + ); + + static MainActions sendAction = MainActions._( + name: (context) => S.of(context).send, + image: 'assets/images/upload.png', + onTap: (BuildContext context, DashboardViewModel viewModel) async { + Navigator.pushNamed(context, Routes.send); + }, + ); + + static MainActions sellAction = MainActions._( + name: (context) => S.of(context).sell, + image: 'assets/images/sell.png', + isEnabled: (viewModel) => viewModel.isEnabledSellAction, + canShow: (viewModel) => viewModel.hasSellAction, + onTap: (BuildContext context, DashboardViewModel viewModel) async { + final walletType = viewModel.type; + + switch (walletType) { + case WalletType.bitcoin: + final moonPaySellProvider = MoonPaySellProvider(); + final uri = await moonPaySellProvider.requestUrl( + currency: viewModel.wallet.currency, + refundWalletAddress: viewModel.wallet.walletAddresses.address, + ); + await launchUrl(uri); + break; + default: + await showPopUp( + context: context, + builder: (BuildContext context) { + return AlertWithOneAction( + alertTitle: S.of(context).sell, + alertContent: S.of(context).sell_alert_content, + buttonText: S.of(context).ok, + buttonAction: () => Navigator.of(context).pop()); + }, + ); + } + }, + ); +} diff --git a/lib/entities/unstoppable_domain_address.dart b/lib/entities/unstoppable_domain_address.dart index ab94e31fb..c5ec71ab5 100644 --- a/lib/entities/unstoppable_domain_address.dart +++ b/lib/entities/unstoppable_domain_address.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/utils/device_info.dart'; import 'package:flutter/services.dart'; const channel = MethodChannel('com.cake_wallet/native_utils'); @@ -6,13 +7,18 @@ Future fetchUnstoppableDomainAddress(String domain, String ticker) async var address = ''; try { - address = await channel.invokeMethod( - 'getUnstoppableDomainAddress', - { - 'domain' : domain, - 'ticker' : ticker - } - ) ?? ''; + if (DeviceInfo.instance.isMobile) { + address = await channel.invokeMethod( + 'getUnstoppableDomainAddress', + { + 'domain' : domain, + 'ticker' : ticker + } + ) ?? ''; + } else { + // TODO: Integrate with Unstoppable domains resolution API + return address; + } } catch (e) { print('Unstoppable domain error: ${e.toString()}'); address = ''; diff --git a/lib/entities/wake_lock.dart b/lib/entities/wake_lock.dart deleted file mode 100644 index 99acc65ee..000000000 --- a/lib/entities/wake_lock.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:flutter/services.dart'; - -class WakeLock { - static const _utils = const MethodChannel('com.cake_wallet/native_utils'); - - Future enableWake() async { - try { - await _utils.invokeMethod('enableWakeScreen'); - } on PlatformException catch (_) { - print('Failed enabling screen wakelock'); - } - } - - Future disableWake() async { - try { - await _utils.invokeMethod('disableWakeScreen'); - } on PlatformException catch (_) { - print('Failed enabling screen wakelock'); - } - } -} diff --git a/lib/exchange/changenow/changenow_exchange_provider.dart b/lib/exchange/changenow/changenow_exchange_provider.dart index e173dbdf6..89d32fa09 100644 --- a/lib/exchange/changenow/changenow_exchange_provider.dart +++ b/lib/exchange/changenow/changenow_exchange_provider.dart @@ -1,5 +1,6 @@ import 'dart:convert'; import 'package:cake_wallet/exchange/trade_not_found_exeption.dart'; +import 'package:cake_wallet/utils/device_info.dart'; import 'package:http/http.dart'; import 'package:cake_wallet/.secrets.g.dart' as secrets; import 'package:cw_core/crypto_currency.dart'; @@ -24,7 +25,7 @@ class ChangeNowExchangeProvider extends ExchangeProvider { .expand((i) => i) .toList()); - static const apiKey = secrets.changeNowApiKey; + static final apiKey = DeviceInfo.instance.isMobile ? secrets.changeNowApiKey : secrets.changeNowApiKeyDesktop; static const apiAuthority = 'api.changenow.io'; static const createTradePath = '/v2/exchange'; static const findTradeByIdPath = '/v2/exchange/by-id'; diff --git a/lib/exchange/simpleswap/simpleswap_exchange_provider.dart b/lib/exchange/simpleswap/simpleswap_exchange_provider.dart index 6c5c23460..4c1072d11 100644 --- a/lib/exchange/simpleswap/simpleswap_exchange_provider.dart +++ b/lib/exchange/simpleswap/simpleswap_exchange_provider.dart @@ -6,6 +6,7 @@ import 'package:cake_wallet/exchange/simpleswap/simpleswap_request.dart'; import 'package:cake_wallet/exchange/trade_not_created_exeption.dart'; import 'package:cake_wallet/exchange/trade_not_found_exeption.dart'; import 'package:cake_wallet/exchange/trade_state.dart'; +import 'package:cake_wallet/utils/device_info.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cake_wallet/exchange/trade_request.dart'; import 'package:cake_wallet/exchange/trade.dart'; @@ -29,7 +30,7 @@ class SimpleSwapExchangeProvider extends ExchangeProvider { static const rangePath = '/v1/get_ranges'; static const getExchangePath = '/v1/get_exchange'; static const createExchangePath = '/v1/create_exchange'; - static const apiKey = secrets.simpleSwapApiKey; + static final apiKey = DeviceInfo.instance.isMobile ? secrets.simpleSwapApiKey : secrets.simpleSwapApiKeyDesktop; @override ExchangeProviderDescription get description => diff --git a/lib/reactions/on_authentication_state_change.dart b/lib/reactions/on_authentication_state_change.dart index 7521170e6..5f1214b76 100644 --- a/lib/reactions/on_authentication_state_change.dart +++ b/lib/reactions/on_authentication_state_change.dart @@ -9,8 +9,8 @@ ReactionDisposer? _onAuthenticationStateChange; dynamic loginError; -void startAuthenticationStateChange(AuthenticationStore authenticationStore, - GlobalKey navigatorKey) { +void startAuthenticationStateChange( + AuthenticationStore authenticationStore, GlobalKey navigatorKey) { _onAuthenticationStateChange ??= autorun((_) async { final state = authenticationStore.state; diff --git a/lib/reactions/on_wallet_sync_status_change.dart b/lib/reactions/on_wallet_sync_status_change.dart index 68bd4b3c2..767bfd7e8 100644 --- a/lib/reactions/on_wallet_sync_status_change.dart +++ b/lib/reactions/on_wallet_sync_status_change.dart @@ -1,6 +1,4 @@ -import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/entities/update_haven_rate.dart'; -import 'package:cake_wallet/entities/wake_lock.dart'; import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:mobx/mobx.dart'; @@ -9,7 +7,7 @@ import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/balance.dart'; import 'package:cw_core/transaction_info.dart'; import 'package:cw_core/sync_status.dart'; -import 'package:flutter/services.dart'; +import 'package:wakelock/wakelock.dart'; ReactionDisposer? _onWalletSyncStatusChangeReaction; @@ -17,7 +15,6 @@ void startWalletSyncStatusChangeReaction( WalletBase, TransactionInfo> wallet, FiatConversionStore fiatConversionStore) { - final _wakeLock = getIt.get(); _onWalletSyncStatusChangeReaction?.reaction.dispose(); _onWalletSyncStatusChangeReaction = reaction((_) => wallet.syncStatus, (SyncStatus status) async { @@ -30,10 +27,10 @@ void startWalletSyncStatusChangeReaction( } } if (status is SyncingSyncStatus) { - await _wakeLock.enableWake(); + await Wakelock.enable(); } if (status is SyncedSyncStatus || status is FailedSyncStatus) { - await _wakeLock.disableWake(); + await Wakelock.disable(); } } catch(e) { print(e.toString()); diff --git a/lib/router.dart b/lib/router.dart index 634a69966..aebee0942 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -11,6 +11,9 @@ import 'package:cake_wallet/src/screens/buy/payfura_page.dart'; import 'package:cake_wallet/src/screens/buy/pre_order_page.dart'; import 'package:cake_wallet/src/screens/receive/anonpay_invoice_page.dart'; import 'package:cake_wallet/src/screens/receive/anonpay_receive_page.dart'; +import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_dashboard_actions.dart'; +import 'package:cake_wallet/src/screens/dashboard/widgets/transactions_page.dart'; +import 'package:cake_wallet/src/screens/settings/desktop_settings/desktop_settings_page.dart'; import 'package:cake_wallet/src/screens/settings/display_settings_page.dart'; import 'package:cake_wallet/src/screens/settings/other_settings_page.dart'; import 'package:cake_wallet/src/screens/settings/privacy_page.dart'; @@ -32,6 +35,7 @@ import 'package:cake_wallet/src/screens/support/support_page.dart'; import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_details_page.dart'; import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.dart'; import 'package:cake_wallet/utils/payment_request.dart'; +import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart'; import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart'; import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart'; @@ -295,22 +299,27 @@ Route createRoute(RouteSettings settings) { case Routes.connectionSync: return CupertinoPageRoute( + fullscreenDialog: true, builder: (_) => getIt.get()); case Routes.securityBackupPage: return CupertinoPageRoute( + fullscreenDialog: true, builder: (_) => getIt.get()); case Routes.privacyPage: return CupertinoPageRoute( + fullscreenDialog: true, builder: (_) => getIt.get()); case Routes.displaySettingsPage: return CupertinoPageRoute( + fullscreenDialog: true, builder: (_) => getIt.get()); case Routes.otherSettingsPage: return CupertinoPageRoute( + fullscreenDialog: true, builder: (_) => getIt.get()); case Routes.newNode: @@ -336,8 +345,8 @@ Route createRoute(RouteSettings settings) { case Routes.addressBook: return MaterialPageRoute( - builder: (_) => - getIt.get()); + fullscreenDialog: true, + builder: (_) => getIt.get()); case Routes.pickerAddressBook: final selectedCurrency = settings.arguments as CryptoCurrency?; @@ -394,6 +403,7 @@ Route createRoute(RouteSettings settings) { case Routes.exchange: return CupertinoPageRoute( + fullscreenDialog: true, builder: (_) => getIt.get()); case Routes.exchangeTemplate: @@ -425,6 +435,7 @@ Route createRoute(RouteSettings settings) { case Routes.support: return CupertinoPageRoute( + fullscreenDialog: true, builder: (_) => getIt.get()); case Routes.unspentCoinsList: @@ -447,11 +458,14 @@ Route createRoute(RouteSettings settings) { getIt.get( param1: args['qrData'] as String, param2: args['version'] as int?, - + )); case Routes.ioniaWelcomePage: - return CupertinoPageRoute(builder: (_) => getIt.get()); + return CupertinoPageRoute( + fullscreenDialog: true, + builder: (_) => getIt.get(), + ); case Routes.ioniaLoginPage: return CupertinoPageRoute( builder: (_) => getIt.get()); @@ -524,19 +538,39 @@ Route createRoute(RouteSettings settings) { getIt.get(param1: type), getIt.get(param1: type), )); - + case Routes.anonPayInvoicePage: final args = settings.arguments as List; return CupertinoPageRoute(builder: (_) => getIt.get(param1: args)); - + case Routes.anonPayReceivePage: final anonInvoiceViewData = settings.arguments as AnonpayInfoBase; return CupertinoPageRoute(builder: (_) => getIt.get(param1: anonInvoiceViewData)); - + case Routes.anonPayDetailsPage: final anonInvoiceViewData = settings.arguments as AnonpayInvoiceInfo; return CupertinoPageRoute(builder: (_) => getIt.get(param1: anonInvoiceViewData)); - + + case Routes.desktop_actions: + return PageRouteBuilder( + opaque: false, + pageBuilder: (_, __, ___) => DesktopDashboardActions(getIt()), + ); + + case Routes.desktop_settings_page: + return CupertinoPageRoute( + builder: (_) => DesktopSettingsPage()); + + case Routes.empty_no_route: + return MaterialPageRoute( + builder: (_) => SizedBox.shrink()); + + case Routes.transactionsPage: + return CupertinoPageRoute( + settings: settings, + fullscreenDialog: true, + builder: (_) => getIt.get()); + default: return MaterialPageRoute( builder: (_) => Scaffold( diff --git a/lib/routes.dart b/lib/routes.dart index 99d0f438e..295b55ae0 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -37,6 +37,8 @@ class Routes { static const restoreWalletFromSeedDetails = '/restore_from_seed_details'; static const exchange = '/exchange'; static const settings = '/settings'; + static const desktop_settings_page = '/desktop_settings_page'; + static const empty_no_route = '/empty_no_route'; static const unlock = '/auth_not_closable'; static const rescan = '/rescan'; static const faq = '/faq'; @@ -86,4 +88,6 @@ class Routes { static const anonPayReceivePage = '/anon_pay_receive_page'; static const anonPayDetailsPage = '/anon_pay_details_page'; static const payfuraPage = '/pay_fura_page'; + static const desktop_actions = '/desktop_actions'; + static const transactionsPage = '/transactions_page'; } diff --git a/lib/src/screens/backup/backup_page.dart b/lib/src/screens/backup/backup_page.dart index f819e88e5..966b289d1 100644 --- a/lib/src/screens/backup/backup_page.dart +++ b/lib/src/screens/backup/backup_page.dart @@ -1,6 +1,8 @@ import 'dart:io'; import 'package:cake_wallet/palette.dart'; +import 'package:cake_wallet/utils/exception_handler.dart'; import 'package:cake_wallet/utils/share_util.dart'; +import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -27,8 +29,7 @@ class BackupPage extends BasePage { @override Widget trailing(BuildContext context) => TrailButton( caption: S.of(context).change_password, - onPressed: () => - Navigator.of(context).pushNamed(Routes.editBackupPassword), + onPressed: () => Navigator.of(context).pushNamed(Routes.editBackupPassword), textColor: Palette.blueCraiola); @override @@ -51,9 +52,8 @@ class BackupPage extends BasePage { child: Observer( builder: (_) => GestureDetector( onTap: () { - Clipboard.setData(ClipboardData( - text: - backupViewModelBase.backupPassword)); + Clipboard.setData( + ClipboardData(text: backupViewModelBase.backupPassword)); showBar( context, S.of(context).transaction_details_copied( @@ -108,8 +108,10 @@ class BackupPage extends BasePage { if (Platform.isAndroid) { onExportAndroid(context, backup); - } else { + } else if (Platform.isIOS) { await share(backup, context); + } else { + await _saveFile(backup); } }, actionLeftButton: () => Navigator.of(dialogContext).pop()); @@ -133,8 +135,7 @@ class BackupPage extends BasePage { return; } - await backupViewModelBase.saveToDownload( - backup.name, backup.content); + await backupViewModelBase.saveToDownload(backup.name, backup.content); Navigator.of(dialogContext).pop(); }, actionLeftButton: () async { @@ -149,4 +150,20 @@ class BackupPage extends BasePage { await ShareUtil.shareFile(filePath: path, fileName: backup.name, context: context); await backupViewModelBase.removeBackupFileLocally(backup); } + + Future _saveFile(BackupExportFile backup) async { + String? outputFile = await FilePicker.platform + .saveFile(dialogTitle: 'Save Your File to desired location', fileName: backup.name); + + try { + File returnedFile = File(outputFile!); + await returnedFile.writeAsBytes(backup.content); + } catch (exception, stackTrace) { + ExceptionHandler.onError(FlutterErrorDetails( + exception: exception, + stack: stackTrace, + library: "Export Backup", + )); + } + } } diff --git a/lib/src/screens/backup/edit_backup_password_page.dart b/lib/src/screens/backup/edit_backup_password_page.dart index 47ae77c0e..f1ddaf4e1 100644 --- a/lib/src/screens/backup/edit_backup_password_page.dart +++ b/lib/src/screens/backup/edit_backup_password_page.dart @@ -66,7 +66,8 @@ class EditBackupPasswordPage extends BasePage { leftButtonText: S.of(context).cancel, actionRightButton: () async { await editBackupPasswordViewModel.save(); - Navigator.of(dialogContext)..pop()..pop(); + Navigator.of(dialogContext).pop(); + Navigator.of(context).pop(); }, actionLeftButton: () => Navigator.of(dialogContext).pop()); }); diff --git a/lib/src/screens/base_page.dart b/lib/src/screens/base_page.dart index 92bf56eaf..123eef65c 100644 --- a/lib/src/screens/base_page.dart +++ b/lib/src/screens/base_page.dart @@ -1,4 +1,5 @@ import 'package:cake_wallet/themes/theme_base.dart'; +import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/palette.dart'; @@ -20,7 +21,7 @@ abstract class BasePage extends StatelessWidget { String? get title => null; - bool get isModalBackButton => false; + bool get canUseCloseIcon => false; Color get backgroundLightColor => Colors.white; @@ -50,23 +51,27 @@ abstract class BasePage extends StatelessWidget { } final _backButton = Icon(Icons.arrow_back_ios, - color: titleColor ?? Theme.of(context).primaryTextTheme!.headline6!.color!, + color: titleColor ?? Theme.of(context).primaryTextTheme.headline6!.color!, size: 16,); final _closeButton = currentTheme.type == ThemeType.dark ? closeButtonImageDarkTheme : closeButtonImage; + bool isMobileView = ResponsiveLayoutUtil.instance.isMobile(context); + return SizedBox( - height: 37, - width: 37, + height: isMobileView ? 37 : 45, + width: isMobileView ? 37 : 45, child: ButtonTheme( minWidth: double.minPositive, child: TextButton( - // FIX-ME: Style - //highlightColor: Colors.transparent, - //splashColor: Colors.transparent, - //padding: EdgeInsets.all(0), + style: ButtonStyle( + overlayColor: MaterialStateColor.resolveWith((states) => Colors.transparent), + ), onPressed: () => onClose(context), - child: isModalBackButton ? _closeButton : _backButton), + child: canUseCloseIcon && !isMobileView + ? _closeButton + : _backButton, + ), ), ); } @@ -92,7 +97,7 @@ abstract class BasePage extends StatelessWidget { ObstructingPreferredSizeWidget appBar(BuildContext context) { final appBarColor = currentTheme.type == ThemeType.dark ? backgroundDarkColor : backgroundLightColor; - + switch (appBarStyle) { case AppBarStyle.regular: // FIX-ME: NavBar no context diff --git a/lib/src/screens/buy/onramper_page.dart b/lib/src/screens/buy/onramper_page.dart index 4973cef0b..cbdf9e1b1 100644 --- a/lib/src/screens/buy/onramper_page.dart +++ b/lib/src/screens/buy/onramper_page.dart @@ -1,60 +1,30 @@ +import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; -import 'package:cake_wallet/store/settings_store.dart'; -import 'package:cw_core/wallet_base.dart'; import 'package:flutter/material.dart'; -import 'package:cake_wallet/.secrets.g.dart' as secrets; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:permission_handler/permission_handler.dart'; class OnRamperPage extends BasePage { - OnRamperPage({required this.settingsStore, required this.wallet}); + OnRamperPage(this._onRamperBuyProvider); - final SettingsStore settingsStore; - final WalletBase wallet; + final OnRamperBuyProvider _onRamperBuyProvider; @override String get title => S.current.buy; @override Widget body(BuildContext context) { - final darkMode = Theme.of(context).brightness == Brightness.dark; - return OnRamperPageBody( - settingsStore: settingsStore, - wallet: wallet, - darkMode: darkMode, - backgroundColor: darkMode ? backgroundDarkColor : backgroundLightColor, - supportSell: false, - supportSwap: false); + return OnRamperPageBody(_onRamperBuyProvider); } } class OnRamperPageBody extends StatefulWidget { - OnRamperPageBody( - {required this.settingsStore, - required this.wallet, - required this.darkMode, - required this.supportSell, - required this.supportSwap, - required this.backgroundColor}); + OnRamperPageBody(this.onRamperBuyProvider); - static const baseUrl = 'widget.onramper.com'; - final SettingsStore settingsStore; - final WalletBase wallet; - final Color backgroundColor; - final bool darkMode; - final bool supportSell; - final bool supportSwap; + final OnRamperBuyProvider onRamperBuyProvider; - Uri get uri => Uri.https(baseUrl, '', { - 'apiKey': secrets.onramperApiKey, - 'defaultCrypto': wallet.currency.title, - 'defaultFiat': settingsStore.fiatCurrency.title, - 'wallets': '${wallet.currency.title}:${wallet.walletAddresses.address}', - 'darkMode': darkMode.toString(), - 'supportSell': supportSell.toString(), - 'supportSwap': supportSwap.toString() - }); + Uri get uri => onRamperBuyProvider.requestUrl(); @override OnRamperPageBodyState createState() => OnRamperPageBodyState(); diff --git a/lib/src/screens/buy/payfura_page.dart b/lib/src/screens/buy/payfura_page.dart index 4bada4a9e..a974aec25 100644 --- a/lib/src/screens/buy/payfura_page.dart +++ b/lib/src/screens/buy/payfura_page.dart @@ -1,45 +1,30 @@ +import 'package:cake_wallet/buy/payfura/payfura_buy_provider.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; -import 'package:cake_wallet/store/settings_store.dart'; -import 'package:cw_core/wallet_base.dart'; import 'package:flutter/material.dart'; -import 'package:cake_wallet/.secrets.g.dart' as secrets; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:permission_handler/permission_handler.dart'; class PayFuraPage extends BasePage { - PayFuraPage({required this.settingsStore, required this.wallet}); + PayFuraPage(this._PayfuraBuyProvider); - final SettingsStore settingsStore; - final WalletBase wallet; + final PayfuraBuyProvider _PayfuraBuyProvider; @override String get title => S.current.buy; @override Widget body(BuildContext context) { - return PayFuraPageBody( - settingsStore: settingsStore, - wallet: wallet); + return PayFuraPageBody(_PayfuraBuyProvider); } } class PayFuraPageBody extends StatefulWidget { - PayFuraPageBody( - {required this.settingsStore, - required this.wallet}); + PayFuraPageBody(this._PayfuraBuyProvider); - static const baseUrl = 'exchange.payfura.com'; - final SettingsStore settingsStore; - final WalletBase wallet; + final PayfuraBuyProvider _PayfuraBuyProvider; - Uri get uri => Uri.https(baseUrl, '', { - 'apiKey': secrets.payfuraApiKey, - 'to': wallet.currency.title, - 'from': settingsStore.fiatCurrency.title, - 'walletAddress': '${wallet.currency.title}:${wallet.walletAddresses.address}', - 'mode': 'buy' - }); + Uri get uri => _PayfuraBuyProvider.requestUrl(); @override PayFuraPageBodyState createState() => PayFuraPageBodyState(); diff --git a/lib/src/screens/contact/contact_list_page.dart b/lib/src/screens/contact/contact_list_page.dart index 0065b9281..f8ccf5807 100644 --- a/lib/src/screens/contact/contact_list_page.dart +++ b/lib/src/screens/contact/contact_list_page.dart @@ -24,10 +24,6 @@ class ContactListPage extends BasePage { @override Widget? trailing(BuildContext context) { - if (!contactListViewModel.isEditable) { - return null; - } - return Container( width: 32.0, height: 32.0, diff --git a/lib/src/screens/dashboard/dashboard_page.dart b/lib/src/screens/dashboard/dashboard_page.dart index b0df7f36c..d0e947508 100644 --- a/lib/src/screens/dashboard/dashboard_page.dart +++ b/lib/src/screens/dashboard/dashboard_page.dart @@ -1,12 +1,15 @@ import 'dart:async'; +import 'package:cake_wallet/di.dart'; +import 'package:cake_wallet/entities/main_actions.dart'; +import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sidebar_wrapper.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/market_place_page.dart'; import 'package:cake_wallet/wallet_type_utils.dart'; -import 'package:cw_core/wallet_type.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/screens/yat_emoji_id.dart'; import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; import 'package:cake_wallet/themes/theme_base.dart'; +import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; @@ -21,17 +24,41 @@ import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:mobx/mobx.dart'; import 'package:smooth_page_indicator/smooth_page_indicator.dart'; import 'package:cake_wallet/main.dart'; -import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart'; -import 'package:url_launcher/url_launcher.dart'; -class DashboardPage extends BasePage { +class DashboardPage extends StatelessWidget { DashboardPage({ required this.balancePage, - required this.walletViewModel, + required this.dashboardViewModel, required this.addressListViewModel, }); + final BalancePage balancePage; - + final DashboardViewModel dashboardViewModel; + final WalletAddressListViewModel addressListViewModel; + + @override + Widget build(BuildContext context) { + return Scaffold( + body: ResponsiveLayoutUtil.instance.isMobile(context) + ? _DashboardPageView( + balancePage: balancePage, + dashboardViewModel: dashboardViewModel, + addressListViewModel: addressListViewModel, + ) + : getIt.get(), + ); + } +} + +class _DashboardPageView extends BasePage { + _DashboardPageView({ + required this.balancePage, + required this.dashboardViewModel, + required this.addressListViewModel, + }); + + final BalancePage balancePage; + @override Color get backgroundLightColor => currentTheme.type == ThemeType.bright ? Colors.transparent : Colors.white; @@ -54,19 +81,19 @@ class DashboardPage extends BasePage { bool get resizeToAvoidBottomInset => false; @override - Widget get endDrawer => MenuWidget(walletViewModel); + Widget get endDrawer => MenuWidget(dashboardViewModel); @override Widget middle(BuildContext context) { - return SyncIndicator(dashboardViewModel: walletViewModel, - onTap: () => Navigator.of(context, rootNavigator: true) - .pushNamed(Routes.connectionSync)); + return SyncIndicator( + dashboardViewModel: dashboardViewModel, + onTap: () => Navigator.of(context, rootNavigator: true).pushNamed(Routes.connectionSync)); } @override Widget trailing(BuildContext context) { final menuButton = Image.asset('assets/images/menu.png', - color: Theme.of(context).accentTextTheme!.headline2!.backgroundColor!); + color: Theme.of(context).accentTextTheme.headline2!.backgroundColor!); return Container( alignment: Alignment.centerRight, @@ -80,7 +107,7 @@ class DashboardPage extends BasePage { child: menuButton)); } - final DashboardViewModel walletViewModel; + final DashboardViewModel dashboardViewModel; final WalletAddressListViewModel addressListViewModel; final controller = PageController(initialPage: 1); @@ -90,141 +117,97 @@ class DashboardPage extends BasePage { @override Widget body(BuildContext context) { - final sendImage = Image.asset('assets/images/upload.png', - height: 24, - width: 24, - color: Theme.of(context).accentTextTheme!.headline2!.backgroundColor!); - final receiveImage = Image.asset('assets/images/received.png', - height: 24, - width: 24, - color: Theme.of(context).accentTextTheme!.headline2!.backgroundColor!); _setEffects(context); return SafeArea( - minimum: EdgeInsets.only(bottom: 24), + minimum: EdgeInsets.only(bottom: 24), child: Column( - mainAxisSize: MainAxisSize.max, - children: [ - Expanded( - child: PageView.builder( - controller: controller, - itemCount: pages.length, - itemBuilder: (context, index) => pages[index])), - Padding( - padding: EdgeInsets.only(bottom: 24, top: 10), - child: SmoothPageIndicator( - controller: controller, - count: pages.length, - effect: ColorTransitionEffect( - spacing: 6.0, - radius: 6.0, - dotWidth: 6.0, - dotHeight: 6.0, - dotColor: Theme.of(context).indicatorColor, - activeDotColor: Theme.of(context) - .accentTextTheme! - .headline4! - .backgroundColor!), - )), - Observer(builder: (_) { - return ClipRect( - child:Container( - margin: const EdgeInsets.only(left: 16, right: 16), - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(50.0), - border: Border.all(color: currentTheme.type == ThemeType.bright ? Color.fromRGBO(255, 255, 255, 0.2): Colors.transparent, width: 1, ), - color:Theme.of(context).textTheme!.headline6!.backgroundColor!), + mainAxisSize: MainAxisSize.max, + children: [ + Expanded( + child: PageView.builder( + controller: controller, + itemCount: pages.length, + itemBuilder: (context, index) => pages[index])), + Padding( + padding: EdgeInsets.only(bottom: 24, top: 10), + child: SmoothPageIndicator( + controller: controller, + count: pages.length, + effect: ColorTransitionEffect( + spacing: 6.0, + radius: 6.0, + dotWidth: 6.0, + dotHeight: 6.0, + dotColor: Theme.of(context).indicatorColor, + activeDotColor: + Theme.of(context).accentTextTheme!.headline4!.backgroundColor!), + )), + Observer(builder: (_) { + return ClipRect( child: Container( - padding: EdgeInsets.only(left: 32, right: 32), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - if (walletViewModel.hasBuyAction) - ActionButton( - image: Image.asset('assets/images/buy.png', - height: 24, - width: 24, - color: !walletViewModel.isEnabledBuyAction - ? Theme.of(context) - .accentTextTheme! - .headline3! - .backgroundColor! - : Theme.of(context).accentTextTheme!.headline2!.backgroundColor!), - title: S.of(context).buy, - onClick: () async => await _onClickBuyButton(context), - textColor: !walletViewModel.isEnabledBuyAction - ? Theme.of(context) - .accentTextTheme! - .headline3! - .backgroundColor! - : null), - ActionButton( - image: receiveImage, - title: S.of(context).receive, - route: Routes.addressPage), - if (walletViewModel.hasExchangeAction) - ActionButton( - image: Image.asset('assets/images/transfer.png', - height: 24, - width: 24, - color: !walletViewModel.isEnabledExchangeAction - ? Theme.of(context) - .accentTextTheme! - .headline3! - .backgroundColor! - : Theme.of(context).accentTextTheme!.headline2!.backgroundColor!), - title: S.of(context).exchange, - onClick: () async => _onClickExchangeButton(context), - textColor: !walletViewModel.isEnabledExchangeAction - ? Theme.of(context) - .accentTextTheme! - .headline3! - .backgroundColor! - : null), - ActionButton( - image: sendImage, - title: S.of(context).send, - route: Routes.send), - if (walletViewModel.hasSellAction) - ActionButton( - image: Image.asset('assets/images/sell.png', - height: 24, - width: 24, - color: !walletViewModel.isEnabledSellAction - ? Theme.of(context) - .accentTextTheme! - .headline3! - .backgroundColor! - : Theme.of(context).accentTextTheme!.headline2!.backgroundColor!), - title: S.of(context).sell, - onClick: () async => await _onClickSellButton(context), - textColor: !walletViewModel.isEnabledSellAction - ? Theme.of(context) - .accentTextTheme! - .headline3! - .backgroundColor! - : null), - ], - ),), - ),),); - }), - - ], - )); + margin: const EdgeInsets.only(left: 16, right: 16), + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(50.0), + border: Border.all( + color: currentTheme.type == ThemeType.bright + ? Color.fromRGBO(255, 255, 255, 0.2) + : Colors.transparent, + width: 1, + ), + color: Theme.of(context).textTheme.headline6!.backgroundColor!, + ), + child: Container( + padding: EdgeInsets.only(left: 32, right: 32), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: MainActions.all + .where((element) => element.canShow?.call(dashboardViewModel) ?? true) + .map((action) => ActionButton( + image: Image.asset(action.image, + height: 24, + width: 24, + color: action.isEnabled?.call(dashboardViewModel) ?? true + ? Theme.of(context) + .accentTextTheme + .headline2! + .backgroundColor! + : Theme.of(context) + .accentTextTheme + .headline3! + .backgroundColor!), + title: action.name(context), + onClick: () async => await action.onTap(context, dashboardViewModel), + textColor: action.isEnabled?.call(dashboardViewModel) ?? true + ? null + : Theme.of(context) + .accentTextTheme + .headline3! + .backgroundColor!, + )) + .toList(), + ), + ), + ), + ), + ); + }), + ], + )); } void _setEffects(BuildContext context) async { if (_isEffectsInstalled) { return; } - pages.add(MarketPlacePage(dashboardViewModel: walletViewModel)); + pages.add(MarketPlacePage(dashboardViewModel: dashboardViewModel)); pages.add(balancePage); - pages.add(TransactionsPage(dashboardViewModel: walletViewModel)); + pages.add(TransactionsPage(dashboardViewModel: dashboardViewModel)); _isEffectsInstalled = true; autorun((_) async { - if (!walletViewModel.isOutdatedElectrumWallet) { + if (!dashboardViewModel.isOutdatedElectrumWallet) { return; } @@ -235,8 +218,7 @@ class DashboardPage extends BasePage { builder: (BuildContext context) { return AlertWithOneAction( alertTitle: S.of(context).pre_seed_title, - alertContent: - S.of(context).outdated_electrum_wallet_description, + alertContent: S.of(context).outdated_electrum_wallet_description, buttonText: S.of(context).understand, buttonAction: () => Navigator.of(context).pop()); }); @@ -253,13 +235,13 @@ class DashboardPage extends BasePage { Future.delayed(Duration(milliseconds: 500)).then((_) { showPopUp( context: navigatorKey.currentContext!, - builder: (_) => YatEmojiId(walletViewModel.yatStore.emoji)); + builder: (_) => YatEmojiId(dashboardViewModel.yatStore.emoji)); needToPresentYat = false; }); } }); - walletViewModel.yatStore.emojiIncommingStream.listen((String emoji) { + dashboardViewModel.yatStore.emojiIncommingStream.listen((String emoji) { if (!_isEffectsInstalled || emoji.isEmpty) { return; } @@ -267,62 +249,4 @@ class DashboardPage extends BasePage { needToPresentYat = true; }); } - - Future _onClickBuyButton(BuildContext context) async { - final walletType = walletViewModel.type; - - switch (walletType) { - case WalletType.bitcoin: - Navigator.of(context).pushNamed(Routes.onramperPage); - break; - case WalletType.litecoin: - Navigator.of(context).pushNamed(Routes.onramperPage); - break; - case WalletType.monero: - Navigator.of(context).pushNamed(Routes.payfuraPage); - break; - default: - await showPopUp( - context: context, - builder: (BuildContext context) { - return AlertWithOneAction( - alertTitle: S.of(context).buy, - alertContent: S.of(context).buy_alert_content, - buttonText: S.of(context).ok, - buttonAction: () => Navigator.of(context).pop()); - }); - } - } - - Future _onClickSellButton(BuildContext context) async { - final walletType = walletViewModel.type; - - switch (walletType) { - case WalletType.bitcoin: - final moonPaySellProvider = MoonPaySellProvider(); - final uri = await moonPaySellProvider.requestUrl( - currency: walletViewModel.wallet.currency, - refundWalletAddress: - walletViewModel.wallet.walletAddresses.address); - await launch(uri); - break; - default: - await showPopUp( - context: context, - builder: (BuildContext context) { - return AlertWithOneAction( - alertTitle: S.of(context).sell, - alertContent: isMoneroOnly ? S.of(context).sell_monero_com_alert_content - : S.of(context).sell_alert_content, - buttonText: S.of(context).ok, - buttonAction: () => Navigator.of(context).pop()); - }); - } - } - - Future _onClickExchangeButton(BuildContext context) async { - if (walletViewModel.isEnabledExchangeAction) { - await Navigator.of(context).pushNamed(Routes.exchange); - } - } } diff --git a/lib/src/screens/dashboard/desktop_dashboard_page.dart b/lib/src/screens/dashboard/desktop_dashboard_page.dart new file mode 100644 index 000000000..64f8a9aac --- /dev/null +++ b/lib/src/screens/dashboard/desktop_dashboard_page.dart @@ -0,0 +1,111 @@ +import 'dart:async'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/routes.dart'; +import 'package:cake_wallet/src/screens/yat_emoji_id.dart'; +import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; +import 'package:cake_wallet/utils/show_pop_up.dart'; +import 'package:flutter/material.dart'; +import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; +import 'package:cake_wallet/src/screens/dashboard/widgets/balance_page.dart'; +import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart'; +import 'package:mobx/mobx.dart'; +import 'package:cake_wallet/main.dart'; +import 'package:cake_wallet/router.dart' as Router; + +class DesktopDashboardPage extends StatelessWidget { + DesktopDashboardPage({ + required this.balancePage, + required this.dashboardViewModel, + required this.addressListViewModel, + required this.desktopKey, + }); + + final BalancePage balancePage; + final DashboardViewModel dashboardViewModel; + final WalletAddressListViewModel addressListViewModel; + final GlobalKey desktopKey; + + bool _isEffectsInstalled = false; + StreamSubscription? _onInactiveSub; + + @override + Widget build(BuildContext context) { + _setEffects(context); + + return Container( + color: Theme.of(context).backgroundColor, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 400, + child: balancePage, + ), + Flexible( + child: ConstrainedBox( + constraints: BoxConstraints(maxWidth: 500), + child: Navigator( + key: desktopKey, + initialRoute: Routes.desktop_actions, + onGenerateRoute: (settings) => Router.createRoute(settings), + onGenerateInitialRoutes: (NavigatorState navigator, String initialRouteName) { + return [ + navigator.widget.onGenerateRoute!(RouteSettings(name: initialRouteName))! + ]; + }, + ), + ), + ), + ], + ), + ); + } + + void _setEffects(BuildContext context) async { + if (_isEffectsInstalled) { + return; + } + _isEffectsInstalled = true; + + autorun((_) async { + if (!dashboardViewModel.isOutdatedElectrumWallet) { + return; + } + + await Future.delayed(Duration(seconds: 1)); + await showPopUp( + context: context, + builder: (BuildContext context) { + return AlertWithOneAction( + alertTitle: S.of(context).pre_seed_title, + alertContent: S.of(context).outdated_electrum_wallet_description, + buttonText: S.of(context).understand, + buttonAction: () => Navigator.of(context).pop()); + }); + }); + + var needToPresentYat = false; + var isInactive = false; + + _onInactiveSub = rootKey.currentState!.isInactive.listen((inactive) { + isInactive = inactive; + + if (needToPresentYat) { + Future.delayed(Duration(milliseconds: 500)).then((_) { + showPopUp( + context: navigatorKey.currentContext!, + builder: (_) => YatEmojiId(dashboardViewModel.yatStore.emoji)); + needToPresentYat = false; + }); + } + }); + + dashboardViewModel.yatStore.emojiIncommingStream.listen((String emoji) { + if (!_isEffectsInstalled || emoji.isEmpty) { + return; + } + + needToPresentYat = true; + }); + } +} diff --git a/lib/src/screens/dashboard/desktop_widgets/desktop_action_button.dart b/lib/src/screens/dashboard/desktop_widgets/desktop_action_button.dart new file mode 100644 index 000000000..0e3588a17 --- /dev/null +++ b/lib/src/screens/dashboard/desktop_widgets/desktop_action_button.dart @@ -0,0 +1,69 @@ +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:flutter/material.dart'; + +class DesktopActionButton extends StatelessWidget { + final String image; + final String title; + final bool canShow; + final bool isEnabled; + final Function() onTap; + + const DesktopActionButton({ + Key? key, + required this.title, + required this.image, + required this.onTap, + bool? canShow, + bool? isEnabled, + }) : this.isEnabled = isEnabled ?? true, + this.canShow = canShow ?? true, + super(key: key); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.fromLTRB(8, 0, 8, 8), + child: GestureDetector( + onTap: onTap, + child: Container( + padding: EdgeInsets.symmetric(vertical: 25), + width: double.infinity, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15.0), + color: Theme.of(context).textTheme.headline6!.backgroundColor!, + ), + child: Center( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Image.asset( + image, + height: 30, + width: 30, + color: isEnabled + ? Theme.of(context).accentTextTheme.headline2!.backgroundColor! + : Theme.of(context).accentTextTheme.headline3!.backgroundColor!, + ), + const SizedBox(width: 10), + AutoSizeText( + title, + style: TextStyle( + fontSize: 24, + fontFamily: 'Lato', + fontWeight: FontWeight.bold, + color: isEnabled + ? Theme.of(context).accentTextTheme.headline2!.backgroundColor! + : null, + height: 1, + ), + maxLines: 1, + textAlign: TextAlign.center, + ) + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/src/screens/dashboard/desktop_widgets/desktop_dashboard_actions.dart b/lib/src/screens/dashboard/desktop_widgets/desktop_dashboard_actions.dart new file mode 100644 index 000000000..e5fa42390 --- /dev/null +++ b/lib/src/screens/dashboard/desktop_widgets/desktop_dashboard_actions.dart @@ -0,0 +1,80 @@ +import 'package:cake_wallet/entities/main_actions.dart'; +import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_action_button.dart'; +import 'package:cake_wallet/src/screens/dashboard/widgets/market_place_page.dart'; +import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; + +class DesktopDashboardActions extends StatelessWidget { + final DashboardViewModel dashboardViewModel; + + const DesktopDashboardActions(this.dashboardViewModel, {Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Observer( + builder: (_) { + return Column( + children: [ + const SizedBox(height: 16), + DesktopActionButton( + title: MainActions.exchangeAction.name(context), + image: MainActions.exchangeAction.image, + canShow: MainActions.exchangeAction.canShow?.call(dashboardViewModel), + isEnabled: MainActions.exchangeAction.isEnabled?.call(dashboardViewModel), + onTap: () async => await MainActions.exchangeAction.onTap(context, dashboardViewModel), + ), + Row( + children: [ + Expanded( + child: DesktopActionButton( + title: MainActions.receiveAction.name(context), + image: MainActions.receiveAction.image, + canShow: MainActions.receiveAction.canShow?.call(dashboardViewModel), + isEnabled: MainActions.receiveAction.isEnabled?.call(dashboardViewModel), + onTap: () async => + await MainActions.receiveAction.onTap(context, dashboardViewModel), + ), + ), + Expanded( + child: DesktopActionButton( + title: MainActions.sendAction.name(context), + image: MainActions.sendAction.image, + canShow: MainActions.sendAction.canShow?.call(dashboardViewModel), + isEnabled: MainActions.sendAction.isEnabled?.call(dashboardViewModel), + onTap: () async => await MainActions.sendAction.onTap(context, dashboardViewModel), + ), + ), + ], + ), + Row( + children: [ + Expanded( + child: DesktopActionButton( + title: MainActions.buyAction.name(context), + image: MainActions.buyAction.image, + canShow: MainActions.buyAction.canShow?.call(dashboardViewModel), + isEnabled: MainActions.buyAction.isEnabled?.call(dashboardViewModel), + onTap: () async => await MainActions.buyAction.onTap(context, dashboardViewModel), + ), + ), + Expanded( + child: DesktopActionButton( + title: MainActions.sellAction.name(context), + image: MainActions.sellAction.image, + canShow: MainActions.sellAction.canShow?.call(dashboardViewModel), + isEnabled: MainActions.sellAction.isEnabled?.call(dashboardViewModel), + onTap: () async => await MainActions.sellAction.onTap(context, dashboardViewModel), + ), + ), + ], + ), + Expanded( + child: MarketPlacePage(dashboardViewModel: dashboardViewModel), + ), + ], + ); + } + ); + } +} diff --git a/lib/src/screens/dashboard/desktop_widgets/desktop_dashboard_navbar.dart b/lib/src/screens/dashboard/desktop_widgets/desktop_dashboard_navbar.dart new file mode 100644 index 000000000..b97def132 --- /dev/null +++ b/lib/src/screens/dashboard/desktop_widgets/desktop_dashboard_navbar.dart @@ -0,0 +1,48 @@ +import 'package:cake_wallet/di.dart'; +import 'package:cake_wallet/store/settings_store.dart'; +import 'package:cake_wallet/themes/theme_base.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +class DesktopDashboardNavbar extends StatelessWidget implements ObstructingPreferredSizeWidget { + final Widget leading; + final Widget middle; + final Widget trailing; + + DesktopDashboardNavbar({ + super.key, + required this.leading, + required this.middle, + required this.trailing, + }); + + ThemeBase get currentTheme => getIt.get().currentTheme; + + @override + Widget build(BuildContext context) { + final appBarColor = + currentTheme.type == ThemeType.dark ? Colors.black.withOpacity(0.1) : Colors.white; + + return Container( + padding: const EdgeInsetsDirectional.only(end: 24), + color: appBarColor, + child: Center( + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible(child: leading), + middle, + trailing, + ], + ), + ), + ); + } + + @override + Size get preferredSize => Size.fromHeight(60); + + @override + bool shouldFullyObstruct(BuildContext context) => false; +} diff --git a/lib/src/screens/dashboard/desktop_widgets/desktop_sidebar/side_menu.dart b/lib/src/screens/dashboard/desktop_widgets/desktop_sidebar/side_menu.dart new file mode 100644 index 000000000..bc7c0af84 --- /dev/null +++ b/lib/src/screens/dashboard/desktop_widgets/desktop_sidebar/side_menu.dart @@ -0,0 +1,32 @@ +import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sidebar/side_menu_item.dart'; +import 'package:flutter/material.dart'; + +class SideMenu extends StatelessWidget { + const SideMenu({ + super.key, + required this.topItems, + required this.bottomItems, + required this.width, + }); + + final List topItems; + final List bottomItems; + final double width; + + @override + Widget build(BuildContext context) { + return Container( + color: Colors.black.withOpacity(0.1), + width: width, + height: MediaQuery.of(context).size.height, + child: Column( + children: [ + ...topItems, + Spacer(), + ...bottomItems, + SizedBox(height: 30), + ], + ), + ); + } +} 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 new file mode 100644 index 000000000..42f4b0e4e --- /dev/null +++ b/lib/src/screens/dashboard/desktop_widgets/desktop_sidebar/side_menu_item.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; + +class SideMenuItem extends StatelessWidget { + const SideMenuItem({ + Key? key, + required this.onTap, + this.imagePath, + this.icon, + this.isSelected = false, + }) : assert((icon != null && imagePath == null) || (icon == null && imagePath != null)); + + final void Function() onTap; + final String? imagePath; + final IconData? icon; + final bool isSelected; + + Color _setColor(BuildContext context) { + if (isSelected) { + return Theme.of(context).primaryTextTheme.headline6!.color!; + } else { + return Theme.of(context).highlightColor; + } + } + + @override + Widget build(BuildContext context) { + 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), + ), + ), + onTap: () => onTap.call(), + highlightColor: Colors.transparent, + focusColor: Colors.transparent, + hoverColor: Colors.transparent, + splashColor: Colors.transparent, + ); + } +} diff --git a/lib/src/screens/dashboard/desktop_widgets/desktop_sidebar_wrapper.dart b/lib/src/screens/dashboard/desktop_widgets/desktop_sidebar_wrapper.dart new file mode 100644 index 000000000..72e65da34 --- /dev/null +++ b/lib/src/screens/dashboard/desktop_widgets/desktop_sidebar_wrapper.dart @@ -0,0 +1,175 @@ +import 'package:cake_wallet/di.dart'; +import 'package:cake_wallet/routes.dart'; +import 'package:cake_wallet/src/screens/auth/auth_page.dart'; +import 'package:cake_wallet/src/screens/base_page.dart'; +import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_dashboard_navbar.dart'; +import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sidebar/side_menu.dart'; +import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sidebar/side_menu_item.dart'; +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/view_model/dashboard/dashboard_view_model.dart'; +import 'package:cake_wallet/view_model/dashboard/desktop_sidebar_view_model.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:cake_wallet/router.dart' as Router; +import 'package:mobx/mobx.dart'; + +class DesktopSidebarWrapper extends BasePage { + final Widget child; + final DesktopSidebarViewModel desktopSidebarViewModel; + final DashboardViewModel dashboardViewModel; + final GlobalKey desktopNavigatorKey; + + DesktopSidebarWrapper({ + required this.child, + required this.desktopSidebarViewModel, + required this.dashboardViewModel, + required this.desktopNavigatorKey, + }); + + @override + ObstructingPreferredSizeWidget appBar(BuildContext context) => DesktopDashboardNavbar( + leading: Padding( + padding: EdgeInsets.only(left: sideMenuWidth), + child: getIt(), + ), + middle: SyncIndicator( + dashboardViewModel: dashboardViewModel, + onTap: () => Navigator.of(context, rootNavigator: true).pushNamed(Routes.connectionSync), + ), + trailing: InkWell( + onTap: () { + Navigator.of(context).pushNamed( + Routes.unlock, + arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) { + if (isAuthenticatedSuccessfully) { + auth.close(); + } + }, + ); + }, + child: Icon(Icons.lock_outline), + ), + ); + + @override + bool get resizeToAvoidBottomInset => false; + + final pageController = PageController(); + + final selectedIconPath = 'assets/images/desktop_transactions_solid_icon.png'; + final unselectedIconPath = 'assets/images/desktop_transactions_outline_icon.png'; + + double get sideMenuWidth => 76.0; + + @override + Widget body(BuildContext context) { + _setEffects(); + + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Observer(builder: (_) { + return SideMenu( + width: sideMenuWidth, + topItems: [ + SideMenuItem( + imagePath: 'assets/images/wallet_outline.png', + isSelected: desktopSidebarViewModel.currentPage == SidebarItem.dashboard, + onTap: () => desktopSidebarViewModel.onPageChange(SidebarItem.dashboard), + ), + SideMenuItem( + onTap: () { + String? currentPath; + + desktopNavigatorKey.currentState?.popUntil((route) { + currentPath = route.settings.name; + return true; + }); + + switch (currentPath) { + case Routes.transactionsPage: + desktopSidebarViewModel.resetSidebar(); + break; + default: + desktopSidebarViewModel.resetSidebar(); + Future.delayed(Duration(milliseconds: 10), () { + desktopSidebarViewModel.onPageChange(SidebarItem.transactions); + desktopNavigatorKey.currentState?.pushNamed(Routes.transactionsPage); + }); + } + }, + isSelected: desktopSidebarViewModel.currentPage == SidebarItem.transactions, + imagePath: desktopSidebarViewModel.currentPage == SidebarItem.transactions + ? selectedIconPath + : unselectedIconPath, + ), + ], + bottomItems: [ + SideMenuItem( + 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, + onTap: () => desktopSidebarViewModel.onPageChange(SidebarItem.settings), + ), + ], + ); + }), + Expanded( + child: PageView( + controller: pageController, + physics: NeverScrollableScrollPhysics(), + children: [ + child, + Container( + color: Theme.of(context).backgroundColor, + padding: EdgeInsets.all(20), + child: Navigator( + initialRoute: Routes.support, + onGenerateRoute: (settings) => Router.createRoute(settings), + onGenerateInitialRoutes: (NavigatorState navigator, String initialRouteName) { + return [ + navigator.widget.onGenerateRoute!(RouteSettings(name: initialRouteName))! + ]; + }, + ), + ), + Navigator( + initialRoute: Routes.desktop_settings_page, + onGenerateRoute: (settings) => Router.createRoute(settings), + onGenerateInitialRoutes: (NavigatorState navigator, String initialRouteName) { + return [ + navigator.widget.onGenerateRoute!(RouteSettings(name: initialRouteName))! + ]; + }, + ), + ], + ), + ), + ], + ); + } + + void _setEffects() async { + reaction((_) => desktopSidebarViewModel.currentPage, (page) { + String? currentPath; + + desktopNavigatorKey.currentState?.popUntil((route) { + currentPath = route.settings.name; + return true; + }); + if (page == SidebarItem.transactions) { + return; + } + + if (currentPath == Routes.transactionsPage) { + Navigator.of(desktopNavigatorKey.currentContext!).pop(); + } + pageController.jumpToPage(page.index); + }); + } +} diff --git a/lib/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart b/lib/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart new file mode 100644 index 000000000..1ad831b1b --- /dev/null +++ b/lib/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart @@ -0,0 +1,199 @@ +import 'package:another_flushbar/flushbar.dart'; +import 'package:cake_wallet/entities/desktop_dropdown_item.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/routes.dart'; +import 'package:cake_wallet/src/screens/auth/auth_page.dart'; +import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/dropdown_item_widget.dart'; +import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; +import 'package:cake_wallet/utils/show_bar.dart'; +import 'package:cake_wallet/utils/show_pop_up.dart'; +import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart'; +import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart'; +import 'package:cake_wallet/wallet_type_utils.dart'; +import 'package:cw_core/wallet_type.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; + +class DesktopWalletSelectionDropDown extends StatefulWidget { + final WalletListViewModel walletListViewModel; + + DesktopWalletSelectionDropDown(this.walletListViewModel, {Key? key}) : super(key: key); + + @override + State createState() => _DesktopWalletSelectionDropDownState(); +} + +class _DesktopWalletSelectionDropDownState extends State { + final moneroIcon = Image.asset('assets/images/monero_logo.png', height: 24, width: 24); + final bitcoinIcon = Image.asset('assets/images/bitcoin.png', height: 24, width: 24); + final litecoinIcon = Image.asset('assets/images/litecoin_icon.png', height: 24, width: 24); + final havenIcon = Image.asset('assets/images/haven_logo.png', height: 24, width: 24); + final nonWalletTypeIcon = Image.asset('assets/images/close.png', height: 24, width: 24); + + Image _newWalletImage(BuildContext context) => Image.asset( + 'assets/images/new_wallet.png', + height: 12, + width: 12, + color: Theme.of(context).primaryTextTheme.headline6!.color!, + ); + + Image _restoreWalletImage(BuildContext context) => Image.asset( + 'assets/images/restore_wallet.png', + height: 12, + width: 12, + color: Theme.of(context).primaryTextTheme.headline6!.color!, + ); + + Flushbar? _progressBar; + + @override + Widget build(BuildContext context) { + final themeData = Theme.of(context); + return Observer(builder: (context) { + final dropDownItems = [ + ...widget.walletListViewModel.wallets + .map((wallet) => DesktopDropdownItem( + isSelected: wallet.isCurrent, + child: ConstrainedBox( + constraints: BoxConstraints(maxWidth: 500), + child: DropDownItemWidget( + title: wallet.name, + image: wallet.isEnabled ? _imageFor(type: wallet.type) : nonWalletTypeIcon), + ), + onSelected: () => _onSelectedWallet(wallet), + )) + .toList(), + DesktopDropdownItem( + onSelected: () => _navigateToCreateWallet(), + child: DropDownItemWidget( + title: S.of(context).create_new, + image: _newWalletImage(context), + ), + ), + DesktopDropdownItem( + onSelected: () => _navigateToRestoreWallet(), + child: DropDownItemWidget( + title: S.of(context).restore_wallet, + image: _restoreWalletImage(context), + ), + ), + ]; + + return DropdownButton( + items: dropDownItems + .map( + (wallet) => DropdownMenuItem( + child: wallet.child, + value: wallet, + ), + ) + .toList(), + onChanged: (item) { + item?.onSelected(); + }, + dropdownColor: themeData.textTheme.bodyText1?.decorationColor, + style: TextStyle(color: themeData.primaryTextTheme.headline6?.color), + selectedItemBuilder: (context) => dropDownItems.map((item) => item.child).toList(), + value: dropDownItems.firstWhere((element) => element.isSelected), + underline: const SizedBox(), + focusColor: Colors.transparent, + borderRadius: BorderRadius.circular(15.0), + ); + }); + } + + void _onSelectedWallet(WalletListItem selectedWallet) async { + if (selectedWallet.isCurrent || !selectedWallet.isEnabled) { + return; + } + final confirmed = await showPopUp( + context: context, + builder: (dialogContext) { + return AlertWithTwoActions( + alertTitle: S.of(context).change_wallet_alert_title, + alertContent: S.of(context).change_wallet_alert_content(selectedWallet.name), + leftButtonText: S.of(context).cancel, + rightButtonText: S.of(context).change, + actionLeftButton: () => Navigator.of(context).pop(false), + actionRightButton: () => Navigator.of(context).pop(true)); + }) ?? + false; + + if (confirmed) { + await _loadWallet(selectedWallet); + } + } + + Image _imageFor({required WalletType type}) { + switch (type) { + case WalletType.bitcoin: + return bitcoinIcon; + case WalletType.monero: + return moneroIcon; + case WalletType.litecoin: + return litecoinIcon; + case WalletType.haven: + return havenIcon; + default: + return nonWalletTypeIcon; + } + } + + Future _loadWallet(WalletListItem wallet) async { + if (await widget.walletListViewModel.checkIfAuthRequired()) { + await Navigator.of(context).pushNamed(Routes.auth, + arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) async { + if (!isAuthenticatedSuccessfully) { + return; + } + + try { + auth.changeProcessText(S.of(context).wallet_list_loading_wallet(wallet.name)); + await widget.walletListViewModel.loadWallet(wallet); + auth.hideProgressText(); + auth.close(); + setState(() {}); + } catch (e) { + auth.changeProcessText( + S.of(context).wallet_list_failed_to_load(wallet.name, e.toString())); + } + }); + } else { + try { + changeProcessText(S.of(context).wallet_list_loading_wallet(wallet.name)); + await widget.walletListViewModel.loadWallet(wallet); + hideProgressText(); + setState(() {}); + } catch (e) { + changeProcessText(S.of(context).wallet_list_failed_to_load(wallet.name, e.toString())); + } + } + } + + void _navigateToCreateWallet() { + if (isSingleCoin) { + Navigator.of(context) + .pushNamed(Routes.newWallet, arguments: widget.walletListViewModel.currentWalletType); + } else { + Navigator.of(context).pushNamed(Routes.newWalletType); + } + } + + void _navigateToRestoreWallet() { + if (isSingleCoin) { + Navigator.of(context) + .pushNamed(Routes.restoreWallet, arguments: widget.walletListViewModel.currentWalletType); + } else { + Navigator.of(context).pushNamed(Routes.restoreWalletType); + } + } + + void changeProcessText(String text) { + _progressBar = createBar(text, duration: null)..show(context); + } + + void hideProgressText() { + _progressBar?.dismiss(); + _progressBar = null; + } +} diff --git a/lib/src/screens/dashboard/desktop_widgets/dropdown_item_widget.dart b/lib/src/screens/dashboard/desktop_widgets/dropdown_item_widget.dart new file mode 100644 index 000000000..47efd04a7 --- /dev/null +++ b/lib/src/screens/dashboard/desktop_widgets/dropdown_item_widget.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; + +class DropDownItemWidget extends StatelessWidget { + const DropDownItemWidget({super.key, required this.title, required this.image}); + final double tileHeight = 60; + final Image image; + final String title; + + @override + Widget build(BuildContext context) { + return Container( + height: tileHeight, + padding: EdgeInsets.symmetric(horizontal: 20), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + image, + SizedBox(width: 10), + Flexible( + child: Text( + title, + style: TextStyle( + fontSize: 22, + fontWeight: FontWeight.w500, + color: Theme.of(context).primaryTextTheme.headline6!.color!, + ), + overflow: TextOverflow.ellipsis, + maxLines: 1, + ), + ) + ], + ), + ); + } +} diff --git a/lib/src/screens/dashboard/wallet_menu.dart b/lib/src/screens/dashboard/wallet_menu.dart deleted file mode 100644 index 1638c0bc4..000000000 --- a/lib/src/screens/dashboard/wallet_menu.dart +++ /dev/null @@ -1,74 +0,0 @@ -import 'package:cake_wallet/palette.dart'; -import 'package:cake_wallet/src/screens/dashboard/wallet_menu_item.dart'; -import 'package:flutter/material.dart'; -import 'package:cake_wallet/routes.dart'; -import 'package:cake_wallet/generated/i18n.dart'; - -// FIXME: terrible design - -class WalletMenu { - WalletMenu(this.context, this.reconnect, this.hasRescan) : items = [] { - items.addAll([ - WalletMenuItem( - title: S.current.connection_sync, - image: Image.asset('assets/images/nodes_menu.png', - height: 16, width: 16), - handler: () => Navigator.of(context).pushNamed(Routes.connectionSync), - ), - WalletMenuItem( - title: S.current.wallets, - image: Image.asset('assets/images/wallet_menu.png', - height: 16, width: 16), - handler: () => Navigator.of(context).pushNamed(Routes.walletList), - ), - WalletMenuItem( - title: S.current.address_book_menu, - image: Image.asset('assets/images/open_book_menu.png', - height: 16, width: 16), - handler: () => Navigator.of(context).pushNamed(Routes.addressBook), - ), - WalletMenuItem( - title: S.current.security_and_backup, - image: - Image.asset('assets/images/key_menu.png', height: 16, width: 16), - handler: () { - Navigator.of(context).pushNamed(Routes.securityBackupPage); - }), - WalletMenuItem( - title: S.current.privacy_settings, - image: - Image.asset('assets/images/privacy_menu.png', height: 16, width: 16), - handler: () { - Navigator.of(context).pushNamed(Routes.privacyPage); - }), - WalletMenuItem( - title: S.current.display_settings, - image: Image.asset('assets/images/eye_menu.png', - height: 16, width: 16), - handler: () => Navigator.of(context).pushNamed(Routes.displaySettingsPage), - ), - WalletMenuItem( - title: S.current.other_settings, - image: Image.asset('assets/images/settings_menu.png', - height: 16, width: 16), - handler: () => Navigator.of(context).pushNamed(Routes.otherSettingsPage), - ), - WalletMenuItem( - title: S.current.settings_support, - image: Image.asset('assets/images/question_mark.png', - height: 16, width: 16, color: Palette.darkBlue), - handler: () => Navigator.of(context).pushNamed(Routes.support), - ), - ]); - } - - final List items; - final BuildContext context; - final Future Function() reconnect; - final bool hasRescan; - - void action(int index) { - final item = items[index]; - item.handler(); - } -} diff --git a/lib/src/screens/dashboard/wallet_menu_item.dart b/lib/src/screens/dashboard/wallet_menu_item.dart deleted file mode 100644 index 31a11f31e..000000000 --- a/lib/src/screens/dashboard/wallet_menu_item.dart +++ /dev/null @@ -1,12 +0,0 @@ -import 'package:flutter/material.dart'; - -class WalletMenuItem { - WalletMenuItem({ - required this.title, - required this.image, - required this.handler}); - - final String title; - final Image image; - final void Function() handler; -} \ No newline at end of file diff --git a/lib/src/screens/dashboard/widgets/address_page.dart b/lib/src/screens/dashboard/widgets/address_page.dart index 5de8124c4..ebfabcd02 100644 --- a/lib/src/screens/dashboard/widgets/address_page.dart +++ b/lib/src/screens/dashboard/widgets/address_page.dart @@ -24,12 +24,12 @@ import 'package:cake_wallet/di.dart'; class AddressPage extends BasePage { AddressPage({ required this.addressListViewModel, - required this.walletViewModel, + required this.dashboardViewModel, required this.receiveOptionViewModel, }) : _cryptoAmountFocus = FocusNode(); final WalletAddressListViewModel addressListViewModel; - final DashboardViewModel walletViewModel; + final DashboardViewModel dashboardViewModel; final ReceiveOptionViewModel receiveOptionViewModel; final FocusNode _cryptoAmountFocus; @@ -47,28 +47,10 @@ class AddressPage extends BasePage { bool effectsInstalled = false; @override - Widget leading(BuildContext context) { - final _backButton = Icon( - Icons.arrow_back_ios, - color: Theme.of(context).accentTextTheme.headline2!.backgroundColor!, - size: 16, - ); + Color get titleColor => Colors.white; - return SizedBox( - height: 37, - width: 37, - child: ButtonTheme( - minWidth: double.minPositive, - child: TextButton( - // FIX-ME: Style - //highlightColor: Colors.transparent, - //splashColor: Colors.transparent, - //padding: EdgeInsets.all(0), - onPressed: () => onClose(context), - child: _backButton), - ), - ); - } + @override + bool get canUseCloseIcon => true; @override Widget middle(BuildContext context) => @@ -116,8 +98,8 @@ class AddressPage extends BasePage { _setEffects(context); autorun((_) async { - if (!walletViewModel.isOutdatedElectrumWallet || - !walletViewModel.settingsStore.shouldShowReceiveWarning) { + if (!dashboardViewModel.isOutdatedElectrumWallet || + !dashboardViewModel.settingsStore.shouldShowReceiveWarning) { return; } @@ -133,7 +115,7 @@ class AddressPage extends BasePage { actionLeftButton: () => Navigator.of(context).pop(), rightButtonText: S.of(context).do_not_show_me, actionRightButton: () { - walletViewModel.settingsStore.setShouldShowReceiveWarning(false); + dashboardViewModel.settingsStore.setShouldShowReceiveWarning(false); Navigator.of(context).pop(); }); }); @@ -163,7 +145,7 @@ class AddressPage extends BasePage { addressListViewModel: addressListViewModel, amountTextFieldFocusNode: _cryptoAmountFocus, isAmountFieldShow: !addressListViewModel.hasAccounts, - isLight: walletViewModel.settingsStore.currentTheme.type == ThemeType.light)) + isLight: dashboardViewModel.settingsStore.currentTheme.type == ThemeType.light)) ), Observer(builder: (_) { return addressListViewModel.hasAddressList @@ -222,7 +204,6 @@ class AddressPage extends BasePage { } reaction((_) => receiveOptionViewModel.selectedReceiveOption, (ReceivePageOption option) { - Navigator.pop(context); switch (option) { case ReceivePageOption.anonPayInvoice: Navigator.pushReplacementNamed( diff --git a/lib/src/screens/dashboard/widgets/balance_page.dart b/lib/src/screens/dashboard/widgets/balance_page.dart index 48eaa0a3e..bf8b1ae17 100644 --- a/lib/src/screens/dashboard/widgets/balance_page.dart +++ b/lib/src/screens/dashboard/widgets/balance_page.dart @@ -1,10 +1,8 @@ -import 'package:cake_wallet/di.dart'; -import 'package:cake_wallet/src/widgets/standard_list.dart'; import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/themes/theme_base.dart'; +import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; -import 'package:flutter/scheduler.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:cake_wallet/src/widgets/introducing_card.dart'; @@ -16,9 +14,6 @@ class BalancePage extends StatelessWidget{ final DashboardViewModel dashboardViewModel; final SettingsStore settingsStore; - - Color get backgroundLightColor => - settingsStore.currentTheme.type == ThemeType.bright ? Colors.transparent : Colors.white; @override Widget build(BuildContext context) { @@ -29,7 +24,7 @@ class BalancePage extends StatelessWidget{ child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - SizedBox(height: 56), + SizedBox(height: ResponsiveLayoutUtil.instance.isMobile(context) ? 56 : 16), Container( margin: const EdgeInsets.only(left: 24, bottom: 16), child: Observer(builder: (_) { diff --git a/lib/src/screens/dashboard/widgets/menu_widget.dart b/lib/src/screens/dashboard/widgets/menu_widget.dart index 87bf956b5..17acdab87 100644 --- a/lib/src/screens/dashboard/widgets/menu_widget.dart +++ b/lib/src/screens/dashboard/widgets/menu_widget.dart @@ -1,10 +1,9 @@ -import 'dart:ui'; +import 'package:cake_wallet/src/widgets/setting_action_button.dart'; +import 'package:cake_wallet/src/widgets/setting_actions.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/palette.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:cw_core/wallet_type.dart'; -import 'package:cake_wallet/src/screens/dashboard/wallet_menu.dart'; -import 'package:flutter/rendering.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; // FIXME: terrible design. @@ -19,18 +18,18 @@ class MenuWidget extends StatefulWidget { } class MenuWidgetState extends State { - MenuWidgetState() - : this.menuWidth = 0, - this.screenWidth = 0, - this.screenHeight = 0, - this.headerHeight = 120, - this.tileHeight = 60, - this.fromTopEdge = 50, - this.fromBottomEdge = 25, - this.moneroIcon = Image.asset('assets/images/monero_menu.png'), - this.bitcoinIcon = Image.asset('assets/images/bitcoin_menu.png'), - this.litecoinIcon = Image.asset('assets/images/litecoin_menu.png'), - this.havenIcon = Image.asset('assets/images/haven_menu.png'); + MenuWidgetState() + : this.menuWidth = 0, + this.screenWidth = 0, + this.screenHeight = 0, + this.headerHeight = 120, + this.tileHeight = 60, + this.fromTopEdge = 50, + this.fromBottomEdge = 25, + this.moneroIcon = Image.asset('assets/images/monero_menu.png'), + this.bitcoinIcon = Image.asset('assets/images/bitcoin_menu.png'), + this.litecoinIcon = Image.asset('assets/images/litecoin_menu.png'), + this.havenIcon = Image.asset('assets/images/haven_menu.png'); final largeScreen = 731; @@ -82,16 +81,12 @@ class MenuWidgetState extends State { @override Widget build(BuildContext context) { - final walletMenu = WalletMenu( - context, - () async => widget.dashboardViewModel.reconnect(), - widget.dashboardViewModel.hasRescan); - final itemCount = walletMenu.items.length; + final itemCount = SettingActions.all.length; moneroIcon = Image.asset('assets/images/monero_menu.png', - color: Theme.of(context).accentTextTheme!.overline!.decorationColor!); + color: Theme.of(context).accentTextTheme.overline!.decorationColor!); bitcoinIcon = Image.asset('assets/images/bitcoin_menu.png', - color: Theme.of(context).accentTextTheme!.overline!.decorationColor!); + color: Theme.of(context).accentTextTheme.overline!.decorationColor!); litecoinIcon = Image.asset('assets/images/litecoin_menu.png'); havenIcon = Image.asset('assets/images/haven_menu.png'); @@ -105,17 +100,15 @@ class MenuWidgetState extends State { height: 60, width: 4, decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(2)), - color: PaletteDark.gray), + borderRadius: BorderRadius.all(Radius.circular(2)), color: PaletteDark.gray), )), SizedBox(width: 12), Expanded( child: ClipRRect( borderRadius: BorderRadius.only( - topLeft: Radius.circular(24), - bottomLeft: Radius.circular(24)), + topLeft: Radius.circular(24), bottomLeft: Radius.circular(24)), child: Container( - color: Theme.of(context).textTheme!.bodyText1!.decorationColor!, + color: Theme.of(context).textTheme.bodyText1!.decorationColor!, child: ListView.separated( padding: EdgeInsets.only(top: 0), itemBuilder: (_, index) { @@ -123,25 +116,13 @@ class MenuWidgetState extends State { return Container( height: headerHeight, decoration: BoxDecoration( - gradient: LinearGradient( - colors: [ - Theme.of(context) - .accentTextTheme! - .headline4! - .color!, - Theme.of(context) - .accentTextTheme! - .headline4! - .decorationColor!, - ], - begin: Alignment.topLeft, - end: Alignment.bottomRight), + gradient: LinearGradient(colors: [ + Theme.of(context).accentTextTheme.headline4!.color!, + Theme.of(context).accentTextTheme.headline4!.decorationColor!, + ], begin: Alignment.topLeft, end: Alignment.bottomRight), ), padding: EdgeInsets.only( - left: 24, - top: fromTopEdge, - right: 24, - bottom: fromBottomEdge), + left: 24, top: fromTopEdge, right: 24, bottom: fromBottomEdge), child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ @@ -150,13 +131,10 @@ class MenuWidgetState extends State { SingleChildScrollView( child: Container( child: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - mainAxisAlignment: - widget.dashboardViewModel.subname != - null - ? MainAxisAlignment.spaceBetween - : MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: widget.dashboardViewModel.subname.isNotEmpty + ? MainAxisAlignment.spaceBetween + : MainAxisAlignment.center, children: [ Text( widget.dashboardViewModel.name, @@ -165,19 +143,16 @@ class MenuWidgetState extends State { fontSize: 16, fontWeight: FontWeight.bold), ), - if (widget.dashboardViewModel.subname != - null) + if (widget.dashboardViewModel.subname.isNotEmpty) Observer( builder: (_) => Text( - widget.dashboardViewModel - .subname, + widget.dashboardViewModel.subname, style: TextStyle( color: Theme.of(context) - .accentTextTheme! + .accentTextTheme .overline! .decorationColor!, - fontWeight: - FontWeight.w500, + fontWeight: FontWeight.w500, fontSize: 12), )) ], @@ -190,58 +165,24 @@ class MenuWidgetState extends State { index--; - final item = walletMenu.items[index]; - final title = item.title; - final image = item.image ?? Offstage(); + final item = SettingActions.all[index]; + final isLastTile = index == itemCount - 1; - return GestureDetector( - onTap: () { - Navigator.of(context).pop(); - walletMenu.action(index); - }, - child: Container( - color: Theme.of(context) - .textTheme! - .bodyText1! - .decorationColor!, - height: isLastTile ? headerHeight : tileHeight, - padding: isLastTile - ? EdgeInsets.only( - left: 24, - right: 24, - top: fromBottomEdge, - //bottom: fromTopEdge - ) - : EdgeInsets.only(left: 24, right: 24), - alignment: isLastTile ? Alignment.topLeft : null, - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - image, - SizedBox(width: 16), - Expanded( - child: Text( - title, - style: TextStyle( - color: Theme.of(context) - .textTheme! - .headline3! - .color!, - fontSize: 16, - fontWeight: FontWeight.bold), - )) - ], - ), - )); + return SettingActionButton( + isLastTile: isLastTile, + tileHeight: tileHeight, + selectionActive: false, + fromBottomEdge: fromBottomEdge, + fromTopEdge: fromTopEdge, + onTap: () => item.onTap.call(context), + image: item.image, + title: item.name, + ); }, separatorBuilder: (_, index) => Container( height: 1, - color: Theme.of(context) - .primaryTextTheme! - .caption! - .decorationColor!, + color: Theme.of(context).primaryTextTheme.caption!.decorationColor!, ), itemCount: itemCount + 1), ))) diff --git a/lib/src/screens/dashboard/widgets/present_receive_option_picker.dart b/lib/src/screens/dashboard/widgets/present_receive_option_picker.dart index 2ca63dd82..8b3ffb894 100644 --- a/lib/src/screens/dashboard/widgets/present_receive_option_picker.dart +++ b/lib/src/screens/dashboard/widgets/present_receive_option_picker.dart @@ -86,7 +86,11 @@ class PresentReceiveOptionPicker extends StatelessWidget { itemBuilder: (_, index) { final option = receiveOptionViewModel.options[index]; return InkWell( - onTap: () => receiveOptionViewModel.selectReceiveOption(option), + onTap: () { + Navigator.pop(popUpContext); + + receiveOptionViewModel.selectReceiveOption(option); + }, child: Padding( padding: const EdgeInsets.only(left: 24, right: 24), child: Observer(builder: (_) { diff --git a/lib/src/screens/dashboard/widgets/transactions_page.dart b/lib/src/screens/dashboard/widgets/transactions_page.dart index f773fa8fc..de26d8da6 100644 --- a/lib/src/screens/dashboard/widgets/transactions_page.dart +++ b/lib/src/screens/dashboard/widgets/transactions_page.dart @@ -25,6 +25,7 @@ class TransactionsPage extends StatelessWidget { @override Widget build(BuildContext context) { return Container( + color: Theme.of(context).backgroundColor, padding: EdgeInsets.only(top: 24, bottom: 24), child: Column( children: [ diff --git a/lib/src/screens/exchange/exchange_page.dart b/lib/src/screens/exchange/exchange_page.dart index 1f17d38fd..99eeae7dc 100644 --- a/lib/src/screens/exchange/exchange_page.dart +++ b/lib/src/screens/exchange/exchange_page.dart @@ -1,13 +1,14 @@ -import 'dart:ui'; import 'package:cake_wallet/di.dart'; +import 'package:cake_wallet/src/screens/exchange/widgets/desktop_exchange_cards_section.dart'; +import 'package:cake_wallet/src/screens/exchange/widgets/mobile_exchange_cards_section.dart'; +import 'package:cake_wallet/src/widgets/add_template_button.dart'; import 'package:cake_wallet/utils/debounce.dart'; +import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cw_core/sync_status.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:cake_wallet/entities/parse_address_from_domain.dart'; import 'package:cake_wallet/src/screens/send/widgets/extract_address_from_parsed.dart'; import 'package:cake_wallet/src/widgets/standard_checkbox.dart'; -import 'package:dotted_border/dotted_border.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:keyboard_actions/keyboard_actions.dart'; @@ -35,7 +36,14 @@ import 'package:cake_wallet/src/screens/exchange/widgets/present_provider_picker import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator_icon.dart'; class ExchangePage extends BasePage { - ExchangePage(this.exchangeViewModel); + ExchangePage(this.exchangeViewModel) { + depositWalletName = exchangeViewModel.depositCurrency == CryptoCurrency.xmr + ? exchangeViewModel.wallet.name + : null; + receiveWalletName = exchangeViewModel.receiveCurrency == CryptoCurrency.xmr + ? exchangeViewModel.wallet.name + : null; + } final ExchangeViewModel exchangeViewModel; final depositKey = GlobalKey(); @@ -49,6 +57,20 @@ class ExchangePage extends BasePage { final _depositAmountDebounce = Debounce(Duration(milliseconds: 500)); var _isReactionsSet = false; + final arrowBottomPurple = Image.asset( + 'assets/images/arrow_bottom_purple_icon.png', + color: Colors.white, + height: 8, + ); + final arrowBottomCakeGreen = Image.asset( + 'assets/images/arrow_bottom_cake_green.png', + color: Colors.white, + height: 8, + ); + + late final String? depositWalletName; + late final String? receiveWalletName; + @override String get title => S.current.exchange; @@ -85,28 +107,11 @@ class ExchangePage extends BasePage { exchangeViewModel.reset(); }); + @override + bool get canUseCloseIcon => true; + @override Widget body(BuildContext context) { - final arrowBottomPurple = Image.asset( - 'assets/images/arrow_bottom_purple_icon.png', - color: Colors.white, - height: 8, - ); - final arrowBottomCakeGreen = Image.asset( - 'assets/images/arrow_bottom_cake_green.png', - color: Colors.white, - height: 8, - ); - - final depositWalletName = - exchangeViewModel.depositCurrency == CryptoCurrency.xmr - ? exchangeViewModel.wallet.name - : null; - final receiveWalletName = - exchangeViewModel.receiveCurrency == CryptoCurrency.xmr - ? exchangeViewModel.wallet.name - : null; - WidgetsBinding.instance .addPostFrameCallback((_) => _setReactions(context, exchangeViewModel)); @@ -137,201 +142,7 @@ class ExchangePage extends BasePage { contentPadding: EdgeInsets.only(bottom: 24), content: Observer(builder: (_) => Column( children: [ - Container( - padding: EdgeInsets.only(bottom: 32), - decoration: BoxDecoration( - borderRadius: BorderRadius.only( - bottomLeft: Radius.circular(24), - bottomRight: Radius.circular(24)), - gradient: LinearGradient( - colors: [ - Theme.of(context).primaryTextTheme!.bodyText2!.color!, - Theme.of(context) - .primaryTextTheme! - .bodyText2! - .decorationColor!, - ], - stops: [ - 0.35, - 1.0 - ], - begin: Alignment.topLeft, - end: Alignment.bottomRight), - ), - child: Column( - children: [ - Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.only( - bottomLeft: Radius.circular(24), - bottomRight: Radius.circular(24)), - gradient: LinearGradient( - colors: [ - Theme.of(context) - .primaryTextTheme! - .subtitle2! - .color!, - Theme.of(context) - .primaryTextTheme! - .subtitle2! - .decorationColor!, - ], - begin: Alignment.topLeft, - end: Alignment.bottomRight), - ), - padding: EdgeInsets.fromLTRB(24, 100, 24, 32), - child: Observer( - builder: (_) => ExchangeCard( - onDispose: disposeBestRateSync, - hasAllAmount: exchangeViewModel.hasAllAmount, - allAmount: exchangeViewModel.hasAllAmount - ? () => exchangeViewModel - .calculateDepositAllAmount() - : null, - amountFocusNode: _depositAmountFocus, - addressFocusNode: _depositAddressFocus, - key: depositKey, - title: S.of(context).you_will_send, - initialCurrency: - exchangeViewModel.depositCurrency, - initialWalletName: depositWalletName ?? '', - initialAddress: - exchangeViewModel.depositCurrency == - exchangeViewModel.wallet.currency - ? exchangeViewModel.wallet.walletAddresses.address - : exchangeViewModel.depositAddress, - initialIsAmountEditable: true, - initialIsAddressEditable: - exchangeViewModel.isDepositAddressEnabled, - isAmountEstimated: false, - hasRefundAddress: true, - isMoneroWallet: exchangeViewModel.isMoneroWallet, - currencies: exchangeViewModel.depositCurrencies, - onCurrencySelected: (currency) { - // FIXME: need to move it into view model - if (currency == CryptoCurrency.xmr && - exchangeViewModel.wallet.type != - WalletType.monero) { - showPopUp( - context: context, - builder: (dialogContext) { - return AlertWithOneAction( - alertTitle: S.of(context).error, - alertContent: S - .of(context) - .exchange_incorrect_current_wallet_for_xmr, - buttonText: S.of(context).ok, - buttonAction: () => - Navigator.of(dialogContext) - .pop()); - }); - return; - } - - exchangeViewModel.changeDepositCurrency( - currency: currency); - }, - imageArrow: arrowBottomPurple, - currencyButtonColor: Colors.transparent, - addressButtonsColor: - Theme.of(context).focusColor!, - borderColor: Theme.of(context) - .primaryTextTheme! - .bodyText1! - .color!, - currencyValueValidator: AmountValidator( - currency: exchangeViewModel.depositCurrency), - addressTextFieldValidator: AddressValidator( - type: exchangeViewModel.depositCurrency), - onPushPasteButton: (context) async { - final domain = - exchangeViewModel.depositAddress; - final ticker = exchangeViewModel - .depositCurrency.title.toLowerCase(); - exchangeViewModel.depositAddress = - await fetchParsedAddress( - context, domain, ticker); - }, - onPushAddressBookButton: (context) async { - final domain = - exchangeViewModel.depositAddress; - final ticker = exchangeViewModel - .depositCurrency.title.toLowerCase(); - exchangeViewModel.depositAddress = - await fetchParsedAddress( - context, domain, ticker); - }, - ), - ), - ), - Padding( - padding: - EdgeInsets.only(top: 29, left: 24, right: 24), - child: Observer( - builder: (_) => ExchangeCard( - onDispose: disposeBestRateSync, - amountFocusNode: _receiveAmountFocus, - addressFocusNode: _receiveAddressFocus, - key: receiveKey, - title: S.of(context).you_will_get, - initialCurrency: - exchangeViewModel.receiveCurrency, - initialWalletName: receiveWalletName ?? '', - initialAddress: exchangeViewModel - .receiveCurrency == - exchangeViewModel.wallet.currency - ? exchangeViewModel.wallet.walletAddresses.address - : exchangeViewModel.receiveAddress, - initialIsAmountEditable: exchangeViewModel - .isReceiveAmountEditable, - initialIsAddressEditable: - exchangeViewModel - .isReceiveAddressEnabled, - isAmountEstimated: true, - isMoneroWallet: exchangeViewModel.isMoneroWallet, - currencies: - exchangeViewModel.receiveCurrencies, - onCurrencySelected: (currency) => - exchangeViewModel - .changeReceiveCurrency( - currency: currency), - imageArrow: arrowBottomCakeGreen, - currencyButtonColor: Colors.transparent, - addressButtonsColor: - Theme.of(context).focusColor!, - borderColor: Theme.of(context) - .primaryTextTheme! - .bodyText1! - .decorationColor!, - currencyValueValidator: AmountValidator( - currency: exchangeViewModel.receiveCurrency), - addressTextFieldValidator: - AddressValidator( - type: exchangeViewModel - .receiveCurrency), - onPushPasteButton: (context) async { - final domain = - exchangeViewModel.receiveAddress; - final ticker = exchangeViewModel - .receiveCurrency.title.toLowerCase(); - exchangeViewModel.receiveAddress = - await fetchParsedAddress( - context, domain, ticker); - }, - onPushAddressBookButton: (context) async { - final domain = - exchangeViewModel.receiveAddress; - final ticker = exchangeViewModel - .receiveCurrency.title.toLowerCase(); - exchangeViewModel.receiveAddress = - await fetchParsedAddress( - context, domain, ticker); - }, - )), - ) - ], - ), - ), + _exchangeCardsSection(context), Padding( padding: EdgeInsets.only(top: 12, left: 24), child: Row( @@ -427,50 +238,9 @@ class ExchangePage extends BasePage { return Row( children: [ - GestureDetector( - onTap: () => - Navigator.of(context).pushNamed(Routes.exchangeTemplate), - child: Container( - padding: EdgeInsets.only(left: 1, right: 10), - child: DottedBorder( - borderType: BorderType.RRect, - dashPattern: [6, 4], - color: Theme.of(context) - .primaryTextTheme! - .headline3! - .decorationColor!, - strokeWidth: 2, - radius: Radius.circular(20), - child: Container( - height: 34, - padding: EdgeInsets.only(left: 10, right: 10), - alignment: Alignment.center, - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(20)), - color: Colors.transparent, - ), - child: templates.length >= 1 - ? Icon( - Icons.add, - color: Theme.of(context) - .primaryTextTheme! - .headline2! - .color!, - ) - : Text( - S.of(context).new_template, - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w600, - color: Theme.of(context) - .primaryTextTheme! - .headline2! - .color!, - ), - ), - ), - ), - ), + AddTemplateButton( + onTap: () => Navigator.of(context).pushNamed(Routes.exchangeTemplate), + currentTemplatesLength: templates.length, ), ListView.builder( scrollDirection: Axis.horizontal, @@ -808,5 +578,124 @@ class ExchangePage extends BasePage { } } - void disposeBestRateSync() => exchangeViewModel.bestRateSync?.cancel(); + void disposeBestRateSync() => exchangeViewModel.bestRateSync.cancel(); + + Widget _exchangeCardsSection(BuildContext context) { + final firstExchangeCard = Observer(builder: (_) => ExchangeCard( + onDispose: disposeBestRateSync, + hasAllAmount: exchangeViewModel.hasAllAmount, + allAmount: exchangeViewModel.hasAllAmount + ? () => exchangeViewModel.calculateDepositAllAmount() + : null, + amountFocusNode: _depositAmountFocus, + addressFocusNode: _depositAddressFocus, + key: depositKey, + title: S.of(context).you_will_send, + initialCurrency: exchangeViewModel.depositCurrency, + initialWalletName: depositWalletName ?? '', + initialAddress: + exchangeViewModel.depositCurrency == exchangeViewModel.wallet.currency + ? exchangeViewModel.wallet.walletAddresses.address + : exchangeViewModel.depositAddress, + initialIsAmountEditable: true, + initialIsAddressEditable: exchangeViewModel.isDepositAddressEnabled, + isAmountEstimated: false, + hasRefundAddress: true, + isMoneroWallet: exchangeViewModel.isMoneroWallet, + currencies: exchangeViewModel.depositCurrencies, + onCurrencySelected: (currency) { + // FIXME: need to move it into view model + if (currency == CryptoCurrency.xmr && + exchangeViewModel.wallet.type != WalletType.monero) { + showPopUp( + context: context, + builder: (dialogContext) { + return AlertWithOneAction( + alertTitle: S.of(context).error, + alertContent: + S.of(context).exchange_incorrect_current_wallet_for_xmr, + buttonText: S.of(context).ok, + buttonAction: () => Navigator.of(dialogContext).pop()); + }); + return; + } + + exchangeViewModel.changeDepositCurrency(currency: currency); + }, + imageArrow: arrowBottomPurple, + currencyButtonColor: Colors.transparent, + addressButtonsColor: Theme.of(context).focusColor!, + borderColor: Theme.of(context).primaryTextTheme!.bodyText1!.color!, + currencyValueValidator: + AmountValidator(currency: exchangeViewModel.depositCurrency), + addressTextFieldValidator: + AddressValidator(type: exchangeViewModel.depositCurrency), + onPushPasteButton: (context) async { + final domain = exchangeViewModel.depositAddress; + final ticker = exchangeViewModel.depositCurrency.title.toLowerCase(); + exchangeViewModel.depositAddress = + await fetchParsedAddress(context, domain, ticker); + }, + onPushAddressBookButton: (context) async { + final domain = exchangeViewModel.depositAddress; + final ticker = exchangeViewModel.depositCurrency.title.toLowerCase(); + exchangeViewModel.depositAddress = + await fetchParsedAddress(context, domain, ticker); + }, + )); + + final secondExchangeCard = Observer(builder: (_) => ExchangeCard( + onDispose: disposeBestRateSync, + amountFocusNode: _receiveAmountFocus, + addressFocusNode: _receiveAddressFocus, + key: receiveKey, + title: S.of(context).you_will_get, + initialCurrency: exchangeViewModel.receiveCurrency, + initialWalletName: receiveWalletName ?? '', + initialAddress: + exchangeViewModel.receiveCurrency == exchangeViewModel.wallet.currency + ? exchangeViewModel.wallet.walletAddresses.address + : exchangeViewModel.receiveAddress, + initialIsAmountEditable: exchangeViewModel.isReceiveAmountEditable, + initialIsAddressEditable: exchangeViewModel.isReceiveAddressEnabled, + isAmountEstimated: true, + isMoneroWallet: exchangeViewModel.isMoneroWallet, + currencies: exchangeViewModel.receiveCurrencies, + onCurrencySelected: (currency) => + exchangeViewModel.changeReceiveCurrency(currency: currency), + imageArrow: arrowBottomCakeGreen, + currencyButtonColor: Colors.transparent, + addressButtonsColor: Theme.of(context).focusColor!, + borderColor: + Theme.of(context).primaryTextTheme!.bodyText1!.decorationColor!, + currencyValueValidator: + AmountValidator(currency: exchangeViewModel.receiveCurrency), + addressTextFieldValidator: + AddressValidator(type: exchangeViewModel.receiveCurrency), + onPushPasteButton: (context) async { + final domain = exchangeViewModel.receiveAddress; + final ticker = exchangeViewModel.receiveCurrency.title.toLowerCase(); + exchangeViewModel.receiveAddress = + await fetchParsedAddress(context, domain, ticker); + }, + onPushAddressBookButton: (context) async { + final domain = exchangeViewModel.receiveAddress; + final ticker = exchangeViewModel.receiveCurrency.title.toLowerCase(); + exchangeViewModel.receiveAddress = + await fetchParsedAddress(context, domain, ticker); + }, + )); + + if (ResponsiveLayoutUtil.instance.isMobile(context)) { + return MobileExchangeCardsSection( + firstExchangeCard: firstExchangeCard, + secondExchangeCard: secondExchangeCard, + ); + } + + return DesktopExchangeCardsSection( + firstExchangeCard: firstExchangeCard, + secondExchangeCard: secondExchangeCard, + ); + } } diff --git a/lib/src/screens/exchange/exchange_template_page.dart b/lib/src/screens/exchange/exchange_template_page.dart index cacc52498..50faf7eb2 100644 --- a/lib/src/screens/exchange/exchange_template_page.dart +++ b/lib/src/screens/exchange/exchange_template_page.dart @@ -1,13 +1,9 @@ -import 'dart:ui'; import 'package:cake_wallet/exchange/exchange_provider.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/widgets/keyboard_done_button.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:keyboard_actions/keyboard_actions.dart'; -import 'package:keyboard_actions/keyboard_actions_config.dart'; -import 'package:keyboard_actions/keyboard_actions_item.dart'; import 'package:mobx/mobx.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cw_core/crypto_currency.dart'; @@ -78,7 +74,7 @@ class ExchangeTemplatePage extends BasePage { config: KeyboardActionsConfig( keyboardActionsPlatform: KeyboardActionsPlatform.IOS, keyboardBarColor: - Theme.of(context).accentTextTheme!.bodyText1!.backgroundColor!, + Theme.of(context).accentTextTheme.bodyText1!.backgroundColor!, nextFocus: false, actions: [ KeyboardActionsItem( @@ -103,115 +99,114 @@ class ExchangeTemplatePage extends BasePage { ), gradient: LinearGradient( colors: [ - Theme.of(context).primaryTextTheme!.bodyText2!.color!, - Theme.of(context).primaryTextTheme!.bodyText2!.decorationColor!, + Theme.of(context).primaryTextTheme.bodyText2!.color!, + Theme.of(context).primaryTextTheme.bodyText2!.decorationColor!, ], stops: [0.35, 1.0], begin: Alignment.topLeft, end: Alignment.bottomRight), ), - child: Column( - children: [ - Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.only( - bottomLeft: Radius.circular(24), - bottomRight: Radius.circular(24) + child: FocusTraversalGroup( + policy: OrderedTraversalPolicy(), + child: Column( + children: [ + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(24), + bottomRight: Radius.circular(24) + ), + gradient: LinearGradient( + colors: [ + Theme.of(context) + .primaryTextTheme.subtitle2! + .color!, + Theme.of(context) + .primaryTextTheme.subtitle2! + .decorationColor!, + ], + begin: Alignment.topLeft, + end: Alignment.bottomRight), ), - gradient: LinearGradient( - colors: [ - Theme.of(context) - .primaryTextTheme! - .subtitle2! - .color!, - Theme.of(context) - .primaryTextTheme! - .subtitle2! - .decorationColor!, - ], - begin: Alignment.topLeft, - end: Alignment.bottomRight), - ), - padding: EdgeInsets.fromLTRB(24, 100, 24, 32), - child: Observer( - builder: (_) => ExchangeCard( - amountFocusNode: _depositAmountFocus, - key: depositKey, - title: S.of(context).you_will_send, - initialCurrency: - exchangeViewModel.depositCurrency, - initialWalletName: depositWalletName ?? '', - initialAddress: exchangeViewModel - .depositCurrency == - exchangeViewModel.wallet.currency - ? exchangeViewModel.wallet.walletAddresses.address - : exchangeViewModel.depositAddress, - initialIsAmountEditable: true, - initialIsAddressEditable: exchangeViewModel - .isDepositAddressEnabled, - isAmountEstimated: false, - hasRefundAddress: true, - isMoneroWallet: exchangeViewModel.isMoneroWallet, - currencies: CryptoCurrency.all, - onCurrencySelected: (currency) => - exchangeViewModel.changeDepositCurrency( - currency: currency), - imageArrow: arrowBottomPurple, - currencyButtonColor: Colors.transparent, - addressButtonsColor: - Theme.of(context).focusColor!, - borderColor: Theme.of(context) - .primaryTextTheme! - .bodyText1! - .color!, - currencyValueValidator: AmountValidator( - currency: exchangeViewModel.depositCurrency), - //addressTextFieldValidator: AddressValidator( - // type: exchangeViewModel.depositCurrency), - ), - ), - ), - Padding( - padding: EdgeInsets.only(top: 29, left: 24, right: 24), - child: Observer( + padding: EdgeInsets.fromLTRB(24, 100, 24, 32), + child: Observer( builder: (_) => ExchangeCard( - amountFocusNode: _receiveAmountFocus, - key: receiveKey, - title: S.of(context).you_will_get, + amountFocusNode: _depositAmountFocus, + key: depositKey, + title: S.of(context).you_will_send, initialCurrency: - exchangeViewModel.receiveCurrency, - initialWalletName: receiveWalletName ?? '', - initialAddress: - exchangeViewModel.receiveCurrency == + exchangeViewModel.depositCurrency, + initialWalletName: depositWalletName ?? '', + initialAddress: exchangeViewModel + .depositCurrency == exchangeViewModel.wallet.currency ? exchangeViewModel.wallet.walletAddresses.address - : exchangeViewModel.receiveAddress, - initialIsAmountEditable: - exchangeViewModel.provider is - XMRTOExchangeProvider ? true : false, - initialIsAddressEditable: - exchangeViewModel.isReceiveAddressEnabled, - isAmountEstimated: true, + : exchangeViewModel.depositAddress, + initialIsAmountEditable: true, + initialIsAddressEditable: exchangeViewModel + .isDepositAddressEnabled, + isAmountEstimated: false, + hasRefundAddress: true, isMoneroWallet: exchangeViewModel.isMoneroWallet, - currencies: exchangeViewModel.receiveCurrencies, + currencies: CryptoCurrency.all, onCurrencySelected: (currency) => - exchangeViewModel.changeReceiveCurrency( + exchangeViewModel.changeDepositCurrency( currency: currency), - imageArrow: arrowBottomCakeGreen, + imageArrow: arrowBottomPurple, currencyButtonColor: Colors.transparent, addressButtonsColor: - Theme.of(context).focusColor!, + Theme.of(context).focusColor, borderColor: Theme.of(context) - .primaryTextTheme! - .bodyText1! - .decorationColor!, + .primaryTextTheme.bodyText1! + .color!, currencyValueValidator: AmountValidator( - currency: exchangeViewModel.receiveCurrency), + currency: exchangeViewModel.depositCurrency), //addressTextFieldValidator: AddressValidator( - // type: exchangeViewModel.receiveCurrency), - )), - ) - ], + // type: exchangeViewModel.depositCurrency), + ), + ), + ), + Padding( + padding: EdgeInsets.only(top: 29, left: 24, right: 24), + child: Observer( + builder: (_) => ExchangeCard( + amountFocusNode: _receiveAmountFocus, + key: receiveKey, + title: S.of(context).you_will_get, + initialCurrency: + exchangeViewModel.receiveCurrency, + initialWalletName: receiveWalletName ?? '', + initialAddress: + exchangeViewModel.receiveCurrency == + exchangeViewModel.wallet.currency + ? exchangeViewModel.wallet.walletAddresses.address + : exchangeViewModel.receiveAddress, + initialIsAmountEditable: + exchangeViewModel.provider is + XMRTOExchangeProvider ? true : false, + initialIsAddressEditable: + exchangeViewModel.isReceiveAddressEnabled, + isAmountEstimated: true, + isMoneroWallet: exchangeViewModel.isMoneroWallet, + currencies: exchangeViewModel.receiveCurrencies, + onCurrencySelected: (currency) => + exchangeViewModel.changeReceiveCurrency( + currency: currency), + imageArrow: arrowBottomCakeGreen, + currencyButtonColor: Colors.transparent, + addressButtonsColor: + Theme.of(context).focusColor, + borderColor: Theme.of(context) + .primaryTextTheme.bodyText1! + .decorationColor!, + currencyValueValidator: AmountValidator( + currency: exchangeViewModel.receiveCurrency), + //addressTextFieldValidator: AddressValidator( + // type: exchangeViewModel.receiveCurrency), + )), + ) + ], + ), ), ), bottomSectionPadding: @@ -230,8 +225,7 @@ class ExchangeTemplatePage extends BasePage { textAlign: TextAlign.center, style: TextStyle( color: Theme.of(context) - .primaryTextTheme! - .headline1! + .primaryTextTheme.headline1! .decorationColor!, fontWeight: FontWeight.w500, fontSize: 12), diff --git a/lib/src/screens/exchange/widgets/currency_picker.dart b/lib/src/screens/exchange/widgets/currency_picker.dart index 442ee1b24..5ed9c6f7d 100644 --- a/lib/src/screens/exchange/widgets/currency_picker.dart +++ b/lib/src/screens/exchange/widgets/currency_picker.dart @@ -1,9 +1,8 @@ -import 'dart:ui'; import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker_item_widget.dart'; import 'package:cake_wallet/src/screens/exchange/widgets/picker_item.dart'; import 'package:cake_wallet/src/widgets/alert_close_button.dart'; +import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cw_core/currency.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cake_wallet/src/widgets/alert_background.dart'; @@ -68,11 +67,9 @@ class CurrencyPickerState extends State { @override Widget build(BuildContext context) { return AlertBackground( - child: Stack( - alignment: Alignment.center, - children: [ - Column( + child: Column( mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, children: [ if (widget.title?.isNotEmpty ?? false) Container( @@ -94,10 +91,11 @@ class CurrencyPickerState extends State { child: ClipRRect( borderRadius: BorderRadius.all(Radius.circular(30)), child: Container( - color: Theme.of(context).accentTextTheme!.headline6!.color!, + color: Theme.of(context).accentTextTheme.headline6!.color!, child: ConstrainedBox( constraints: BoxConstraints( maxHeight: MediaQuery.of(context).size.height * 0.65, + maxWidth: ResponsiveLayoutUtil.kPopupWidth ), child: Column( mainAxisSize: MainAxisSize.min, @@ -133,7 +131,7 @@ class CurrencyPickerState extends State { ), ), Divider( - color: Theme.of(context).accentTextTheme!.headline6!.backgroundColor!, + color: Theme.of(context).accentTextTheme.headline6!.backgroundColor!, height: 1, ), if (widget.selectedAtIndex != -1) @@ -171,8 +169,7 @@ class CurrencyPickerState extends State { ), ), ), - ], - ), + SizedBox(height: ResponsiveLayoutUtil.kPopupSpaceHeight), AlertCloseButton(), ], ), diff --git a/lib/src/screens/exchange/widgets/desktop_exchange_cards_section.dart b/lib/src/screens/exchange/widgets/desktop_exchange_cards_section.dart new file mode 100644 index 000000000..0a97d7bad --- /dev/null +++ b/lib/src/screens/exchange/widgets/desktop_exchange_cards_section.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; + +class DesktopExchangeCardsSection extends StatelessWidget { + final Widget firstExchangeCard; + final Widget secondExchangeCard; + + const DesktopExchangeCardsSection({ + Key? key, + required this.firstExchangeCard, + required this.secondExchangeCard, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return FocusTraversalGroup( + policy: OrderedTraversalPolicy(), + child: Column( + children: [ + Padding( + padding: EdgeInsets.only(top: 55, left: 24, right: 24), + child: firstExchangeCard, + ), + Padding( + padding: EdgeInsets.only(top: 29, left: 24, right: 24), + child: secondExchangeCard, + ), + ], + ), + ); + } +} diff --git a/lib/src/screens/exchange/widgets/exchange_card.dart b/lib/src/screens/exchange/widgets/exchange_card.dart index 2bacbac3c..0ba112215 100644 --- a/lib/src/screens/exchange/widgets/exchange_card.dart +++ b/lib/src/screens/exchange/widgets/exchange_card.dart @@ -160,7 +160,7 @@ class ExchangeCardState extends State { final copyImage = Image.asset('assets/images/copy_content.png', height: 16, width: 16, - color: Theme.of(context).primaryTextTheme!.headline3!.color!); + color: Theme.of(context).primaryTextTheme.headline3!.color!); return Container( width: double.infinity, @@ -175,7 +175,7 @@ class ExchangeCardState extends State { style: TextStyle( fontSize: 18, fontWeight: FontWeight.w600, - color: Theme.of(context).textTheme!.headline5!.color!), + color: Theme.of(context).textTheme.headline5!.color!), ) ], ), @@ -210,7 +210,7 @@ class ExchangeCardState extends State { child: Container( height: 32, decoration: BoxDecoration( - color: widget.addressButtonsColor ?? Theme.of(context).primaryTextTheme!.headline4!.color!, + color: widget.addressButtonsColor ?? Theme.of(context).primaryTextTheme.headline4!.color!, borderRadius: BorderRadius.all(Radius.circular(6))), child: Center( @@ -221,8 +221,7 @@ class ExchangeCardState extends State { fontSize: 12, fontWeight: FontWeight.bold, color: Theme.of(context) - .primaryTextTheme! - .headline4! + .primaryTextTheme.headline4! .decorationColor!)), ), ), @@ -241,34 +240,36 @@ class ExchangeCardState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Flexible( - child: BaseTextFormField( - focusNode: widget.amountFocusNode, - controller: amountController, - enabled: _isAmountEditable, - textAlign: TextAlign.left, - keyboardType: TextInputType.numberWithOptions( - signed: false, decimal: true), - inputFormatters: [ - FilteringTextInputFormatter.deny( - RegExp('[\\-|\\ ]')) - ], - hintText: '0.0000', - borderColor: Colors.transparent, - //widget.borderColor, - textStyle: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, - color: Colors.white), - placeholderTextStyle: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, - color: Theme.of(context) - .accentTextTheme! - .headline1! - .decorationColor!), - validator: _isAmountEditable - ? widget.currencyValueValidator - : null), + child: FocusTraversalOrder( + order: NumericFocusOrder(1), + child: BaseTextFormField( + focusNode: widget.amountFocusNode, + controller: amountController, + enabled: _isAmountEditable, + textAlign: TextAlign.left, + keyboardType: TextInputType.numberWithOptions( + signed: false, decimal: true), + inputFormatters: [ + FilteringTextInputFormatter.deny( + RegExp('[\\-|\\ ]')) + ], + hintText: '0.0000', + borderColor: Colors.transparent, + //widget.borderColor, + textStyle: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Colors.white), + placeholderTextStyle: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Theme.of(context) + .accentTextTheme.headline1! + .decorationColor!), + validator: _isAmountEditable + ? widget.currencyValueValidator + : null), + ), ), if (widget.hasAllAmount) Container( @@ -276,8 +277,7 @@ class ExchangeCardState extends State { width: 32, decoration: BoxDecoration( color: Theme.of(context) - .primaryTextTheme! - .headline4! + .primaryTextTheme.headline4! .color!, borderRadius: BorderRadius.all(Radius.circular(6))), @@ -290,8 +290,7 @@ class ExchangeCardState extends State { fontSize: 12, fontWeight: FontWeight.bold, color: Theme.of(context) - .primaryTextTheme! - .headline4! + .primaryTextTheme.headline4! .decorationColor!)), ), ), @@ -302,8 +301,7 @@ class ExchangeCardState extends State { ], )), Divider(height: 1,color: Theme.of(context) - .primaryTextTheme! - .headline5! + .primaryTextTheme.headline5! .decorationColor!), Padding( padding: EdgeInsets.only(top: 5), @@ -321,8 +319,7 @@ class ExchangeCardState extends State { fontSize: 10, height: 1.2, color: Theme.of(context) - .accentTextTheme! - .headline1! + .accentTextTheme.headline1! .decorationColor!), ) : Offstage(), @@ -336,8 +333,7 @@ class ExchangeCardState extends State { fontSize: 10, height: 1.2, color: Theme.of(context) - .accentTextTheme! - .headline1! + .accentTextTheme.headline1! .decorationColor!)) : Offstage(), ])), @@ -351,71 +347,75 @@ class ExchangeCardState extends State { fontSize: 14, fontWeight: FontWeight.w500, color: Theme.of(context) - .accentTextTheme! - .headline1! + .accentTextTheme.headline1! .decorationColor!), )) : Offstage(), _isAddressEditable - ? Padding( - padding: EdgeInsets.only(top: 20), - child: AddressTextField( - focusNode: widget.addressFocusNode, - controller: addressController, - onURIScanned: (uri) { - final paymentRequest = PaymentRequest.fromUri(uri); - addressController.text = paymentRequest.address; - - if (amountController.text.isNotEmpty) { - _showAmountPopup(context, paymentRequest); - return; - } - widget.amountFocusNode?.requestFocus(); - amountController.text = paymentRequest.amount; - }, - placeholder: widget.hasRefundAddress - ? S.of(context).refund_address - : null, - options: [ - AddressTextFieldOption.paste, - AddressTextFieldOption.qrCode, - AddressTextFieldOption.addressBook, - ], - isBorderExist: false, - textStyle: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, - color: Colors.white), - hintStyle: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, - color: Theme.of(context) - .accentTextTheme! - .headline1! - .decorationColor!), - buttonColor: widget.addressButtonsColor, - validator: widget.addressTextFieldValidator, - onPushPasteButton: widget.onPushPasteButton, - onPushAddressBookButton: widget.onPushAddressBookButton, - selectedCurrency: _selectedCurrency + ? FocusTraversalOrder( + order: NumericFocusOrder(2), + child: Padding( + padding: EdgeInsets.only(top: 20), + child: AddressTextField( + focusNode: widget.addressFocusNode, + controller: addressController, + onURIScanned: (uri) { + final paymentRequest = PaymentRequest.fromUri(uri); + addressController.text = paymentRequest.address; + + if (amountController.text.isNotEmpty) { + _showAmountPopup(context, paymentRequest); + return; + } + widget.amountFocusNode?.requestFocus(); + amountController.text = paymentRequest.amount; + }, + placeholder: widget.hasRefundAddress + ? S.of(context).refund_address + : null, + options: [ + AddressTextFieldOption.paste, + AddressTextFieldOption.qrCode, + AddressTextFieldOption.addressBook, + ], + isBorderExist: false, + textStyle: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Colors.white), + hintStyle: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Theme.of(context) + .accentTextTheme.headline1! + .decorationColor!), + buttonColor: widget.addressButtonsColor, + validator: widget.addressTextFieldValidator, + onPushPasteButton: widget.onPushPasteButton, + onPushAddressBookButton: widget.onPushAddressBookButton, + selectedCurrency: _selectedCurrency + ), + ), - - ) + ) : Padding( padding: EdgeInsets.only(top: 10), child: Builder( builder: (context) => Stack(children: [ - BaseTextFormField( - controller: addressController, - readOnly: true, - borderColor: Colors.transparent, - suffixIcon: - SizedBox(width: _isMoneroWallet ? 80 : 36), - textStyle: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, - color: Colors.white), - validator: widget.addressTextFieldValidator), + FocusTraversalOrder( + order: NumericFocusOrder(3), + child: BaseTextFormField( + controller: addressController, + readOnly: true, + borderColor: Colors.transparent, + suffixIcon: + SizedBox(width: _isMoneroWallet ? 80 : 36), + textStyle: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Colors.white), + validator: widget.addressTextFieldValidator), + ), Positioned( top: 2, right: 0, @@ -432,10 +432,8 @@ class ExchangeCardState extends State { child: InkWell( onTap: () async { final contact = - await Navigator.of(context, - rootNavigator: true) - .pushNamed(Routes - .pickerAddressBook); + await Navigator.of(context) + .pushNamed(Routes.pickerAddressBook); if (contact is ContactBase && contact.address != null) { @@ -458,8 +456,7 @@ class ExchangeCardState extends State { child: Image.asset( 'assets/images/open_book.png', color: Theme.of(context) - .primaryTextTheme! - .headline4! + .primaryTextTheme.headline4! .decorationColor!, )), )), diff --git a/lib/src/screens/exchange/widgets/mobile_exchange_cards_section.dart b/lib/src/screens/exchange/widgets/mobile_exchange_cards_section.dart new file mode 100644 index 000000000..762c36a55 --- /dev/null +++ b/lib/src/screens/exchange/widgets/mobile_exchange_cards_section.dart @@ -0,0 +1,58 @@ +import 'package:flutter/material.dart'; + +class MobileExchangeCardsSection extends StatelessWidget { + final Widget firstExchangeCard; + final Widget secondExchangeCard; + + const MobileExchangeCardsSection({ + Key? key, + required this.firstExchangeCard, + required this.secondExchangeCard, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.only(bottom: 32), + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(24), + bottomRight: Radius.circular(24), + ), + gradient: LinearGradient( + colors: [ + Theme.of(context).primaryTextTheme.bodyText2!.color!, + Theme.of(context).primaryTextTheme.bodyText2!.decorationColor!, + ], + stops: [0.35, 1.0], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + ), + child: Column( + children: [ + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24)), + gradient: LinearGradient( + colors: [ + Theme.of(context).primaryTextTheme.subtitle2!.color!, + Theme.of(context).primaryTextTheme.subtitle2!.decorationColor!, + ], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + ), + padding: EdgeInsets.fromLTRB(24, 100, 24, 32), + child: firstExchangeCard, + ), + Padding( + padding: EdgeInsets.only(top: 29, left: 24, right: 24), + child: secondExchangeCard, + ) + ], + ), + ); + } +} diff --git a/lib/src/screens/ionia/auth/ionia_create_account_page.dart b/lib/src/screens/ionia/auth/ionia_create_account_page.dart index d2ced5dae..abdf3501c 100644 --- a/lib/src/screens/ionia/auth/ionia_create_account_page.dart +++ b/lib/src/screens/ionia/auth/ionia_create_account_page.dart @@ -66,6 +66,7 @@ class IoniaCreateAccountPage extends BasePage { validator: EmailValidator(), keyboardType: TextInputType.emailAddress, controller: _emailController, + onSubmit: (_) => _createAccount(), ), ), bottomSectionPadding: EdgeInsets.symmetric(vertical: 36, horizontal: 24), @@ -77,12 +78,7 @@ class IoniaCreateAccountPage extends BasePage { Observer( builder: (_) => LoadingPrimaryButton( text: S.of(context).create_account, - onPressed: () async { - if (_formKey.currentState != null && !_formKey.currentState!.validate()) { - return; - } - await _authViewModel.createUser(_emailController.text); - }, + onPressed: _createAccount, isLoading: _authViewModel.createUserState is IoniaCreateStateLoading, color: Theme.of(context).accentTextTheme!.bodyText1!.color!, textColor: Colors.white, @@ -151,4 +147,11 @@ class IoniaCreateAccountPage extends BasePage { Routes.ioniaVerifyIoniaOtpPage, arguments: [authViewModel.email, false], ); + + void _createAccount() async { + if (_formKey.currentState != null && !_formKey.currentState!.validate()) { + return; + } + await _authViewModel.createUser(_emailController.text); + } } diff --git a/lib/src/screens/ionia/auth/ionia_login_page.dart b/lib/src/screens/ionia/auth/ionia_login_page.dart index 6dc4aa6e5..e6e8680f3 100644 --- a/lib/src/screens/ionia/auth/ionia_login_page.dart +++ b/lib/src/screens/ionia/auth/ionia_login_page.dart @@ -57,6 +57,7 @@ class IoniaLoginPage extends BasePage { keyboardType: TextInputType.emailAddress, validator: EmailValidator(), controller: _emailController, + onSubmit: (text) => _login(), ), ), bottomSectionPadding: EdgeInsets.symmetric(vertical: 36, horizontal: 24), @@ -68,12 +69,7 @@ class IoniaLoginPage extends BasePage { Observer( builder: (_) => LoadingPrimaryButton( text: S.of(context).login, - onPressed: () async { - if (_formKey.currentState != null && !_formKey.currentState!.validate()) { - return; - } - await _authViewModel.signIn(_emailController.text); - }, + onPressed: _login, isLoading: _authViewModel.signInState is IoniaCreateStateLoading, color: Theme.of(context).accentTextTheme!.bodyText1!.color!, textColor: Colors.white, @@ -106,4 +102,11 @@ class IoniaLoginPage extends BasePage { Routes.ioniaVerifyIoniaOtpPage, arguments: [authViewModel.email, true], ); + + void _login() async { + if (_formKey.currentState != null && !_formKey.currentState!.validate()) { + return; + } + await _authViewModel.signIn(_emailController.text); + } } diff --git a/lib/src/screens/ionia/auth/ionia_verify_otp_page.dart b/lib/src/screens/ionia/auth/ionia_verify_otp_page.dart index 625bd36b0..e2123e164 100644 --- a/lib/src/screens/ionia/auth/ionia_verify_otp_page.dart +++ b/lib/src/screens/ionia/auth/ionia_verify_otp_page.dart @@ -82,6 +82,7 @@ class IoniaVerifyIoniaOtp extends BasePage { keyboardType: TextInputType.numberWithOptions(signed: false, decimal: true), focusNode: _codeFocus, controller: _codeController, + onSubmit: (_) => _verify(), ), SizedBox(height: 14), Text( @@ -116,7 +117,7 @@ class IoniaVerifyIoniaOtp extends BasePage { Observer( builder: (_) => LoadingPrimaryButton( text: S.of(context).continue_text, - onPressed: () async => await _authViewModel.verifyEmail(_codeController.text), + onPressed: _verify, isDisabled: _authViewModel.otpState is IoniaOtpSendDisabled, isLoading: _authViewModel.otpState is IoniaOtpValidating, color: Theme.of(context).accentTextTheme!.bodyText1!.color!, @@ -148,4 +149,6 @@ class IoniaVerifyIoniaOtp extends BasePage { void _onOtpSuccessful(BuildContext context) => Navigator.of(context) .pushNamedAndRemoveUntil(Routes.ioniaManageCardsPage, (route) => route.isFirst); + + void _verify() async => await _authViewModel.verifyEmail(_codeController.text); } diff --git a/lib/src/screens/ionia/cards/ionia_buy_gift_card.dart b/lib/src/screens/ionia/cards/ionia_buy_gift_card.dart index 3182d366f..a60b967f2 100644 --- a/lib/src/screens/ionia/cards/ionia_buy_gift_card.dart +++ b/lib/src/screens/ionia/cards/ionia_buy_gift_card.dart @@ -6,6 +6,7 @@ import 'package:cake_wallet/src/widgets/keyboard_done_button.dart'; import 'package:cake_wallet/src/widgets/primary_button.dart'; import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; import 'package:cake_wallet/themes/theme_base.dart'; +import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cake_wallet/view_model/ionia/ionia_buy_card_view_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -44,7 +45,6 @@ class IoniaBuyGiftCardPage extends BasePage { @override Widget body(BuildContext context) { - final _width = MediaQuery.of(context).size.width; final merchant = ioniaBuyCardViewModel.ioniaMerchant; return KeyboardActions( disableScroll: true, @@ -67,7 +67,10 @@ class IoniaBuyGiftCardPage extends BasePage { Container( padding: EdgeInsets.symmetric(horizontal: 25), decoration: BoxDecoration( - borderRadius: BorderRadius.only(bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24)), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(24), + bottomRight: Radius.circular(24), + ), gradient: LinearGradient(colors: [ Theme.of(context).primaryTextTheme!.subtitle1!.color!, Theme.of(context).primaryTextTheme!.subtitle1!.decorationColor!, @@ -75,35 +78,28 @@ class IoniaBuyGiftCardPage extends BasePage { ), child: Column( mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisAlignment: MainAxisAlignment.center, children: [ SizedBox(height: 150), - BaseTextFormField( - controller: _amountController, - focusNode: _amountFieldFocus, - keyboardType: TextInputType.numberWithOptions(signed: false, decimal: true), - inputFormatters: [ - FilteringTextInputFormatter.deny(RegExp('[\-|\ ]')), - FilteringTextInputFormatter.allow(RegExp(r'^\d+(\.|\,)?\d{0,2}'))], - hintText: '1000', - placeholderTextStyle: TextStyle( - color: Theme.of(context).primaryTextTheme!.headline5!.color!, - fontWeight: FontWeight.w600, - fontSize: 36, - ), - borderColor: Theme.of(context).primaryTextTheme!.headline5!.color!, - textColor: Colors.white, - textStyle: TextStyle( - color: Colors.white, - fontSize: 36, - ), - prefixIcon: Padding( - padding: EdgeInsets.only( - top: 5.0, - left: _width / 4, + SizedBox( + width: 200, + child: BaseTextFormField( + controller: _amountController, + focusNode: _amountFieldFocus, + keyboardType: TextInputType.numberWithOptions(signed: false, decimal: true), + inputFormatters: [ + FilteringTextInputFormatter.deny(RegExp('[\-|\ ]')), + FilteringTextInputFormatter.allow( + RegExp(r'^\d+(\.|\,)?\d{0,2}'), + ), + ], + hintText: '1000', + placeholderTextStyle: TextStyle( + color: Theme.of(context).primaryTextTheme.headline5!.color!, + fontWeight: FontWeight.w600, + fontSize: 36, ), - child: Text( + prefixIcon: Text( 'USD: ', style: TextStyle( color: Colors.white, @@ -111,8 +107,17 @@ class IoniaBuyGiftCardPage extends BasePage { fontSize: 36, ), ), + textColor: Colors.white, + textStyle: TextStyle( + color: Colors.white, + fontSize: 36, + ), ), ), + Divider( + color: Theme.of(context).primaryTextTheme.headline5!.color!, + height: 1, + ), SizedBox(height: 8), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -140,7 +145,11 @@ class IoniaBuyGiftCardPage extends BasePage { padding: const EdgeInsets.all(24.0), child: CardItem( title: merchant.legalName, - backgroundColor: Theme.of(context).accentTextTheme!.headline1!.backgroundColor!.withOpacity(0.1), + backgroundColor: Theme.of(context) + .accentTextTheme! + .headline1! + .backgroundColor! + .withOpacity(0.1), discount: merchant.discount, titleColor: Theme.of(context).accentTextTheme!.headline1!.backgroundColor!, subtitleColor: Theme.of(context).hintColor, diff --git a/lib/src/screens/new_wallet/new_wallet_page.dart b/lib/src/screens/new_wallet/new_wallet_page.dart index 9e0cd898c..0f15e23c5 100644 --- a/lib/src/screens/new_wallet/new_wallet_page.dart +++ b/lib/src/screens/new_wallet/new_wallet_page.dart @@ -1,11 +1,12 @@ import 'package:cake_wallet/entities/generate_name.dart'; +import 'package:cake_wallet/main.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/themes/theme_base.dart'; +import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:mobx/mobx.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/cupertino.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/core/wallet_name_validator.dart'; import 'package:cake_wallet/src/widgets/seed_language_selector.dart'; @@ -24,18 +25,14 @@ class NewWalletPage extends BasePage { final walletNameImage = Image.asset('assets/images/wallet_name.png'); - final walletNameLightImage = - Image.asset('assets/images/wallet_name_light.png'); + final walletNameLightImage = Image.asset('assets/images/wallet_name_light.png'); @override String get title => S.current.new_wallet; @override Widget body(BuildContext context) => WalletNameForm( - _walletNewVM, - currentTheme.type == ThemeType.dark - ? walletNameImage - : walletNameLightImage); + _walletNewVM, currentTheme.type == ThemeType.dark ? walletNameImage : walletNameLightImage); } class WalletNameForm extends StatefulWidget { @@ -50,9 +47,9 @@ class WalletNameForm extends StatefulWidget { class _WalletNameFormState extends State { _WalletNameFormState(this._walletNewVM) - : _formKey = GlobalKey(), - _languageSelectorKey = GlobalKey(), - _controller = TextEditingController(); + : _formKey = GlobalKey(), + _languageSelectorKey = GlobalKey(), + _controller = TextEditingController(); static const aspectRatioImage = 1.22; @@ -64,10 +61,9 @@ class _WalletNameFormState extends State { @override void initState() { - _stateReaction ??= - reaction((_) => _walletNewVM.state, (ExecutionState state) { + _stateReaction ??= reaction((_) => _walletNewVM.state, (ExecutionState state) async { if (state is ExecutedSuccessfullyState) { - Navigator.of(context) + Navigator.of(navigatorKey.currentContext!) .pushNamed(Routes.preSeed, arguments: _walletNewVM.type); } @@ -90,117 +86,118 @@ class _WalletNameFormState extends State { @override Widget build(BuildContext context) { - return Container( + return Padding( padding: EdgeInsets.only(top: 24), child: ScrollableWithBottomSection( contentPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24), - content: - Column(crossAxisAlignment: CrossAxisAlignment.center, children: [ - Padding( - padding: EdgeInsets.only(left: 12, right: 12), - child: AspectRatio( - aspectRatio: aspectRatioImage, - child: - FittedBox(child: widget.walletImage, fit: BoxFit.fill)), - ), - Padding( - padding: EdgeInsets.only(top: 24), - child: Form( - key: _formKey, - child: Stack( - alignment: Alignment.centerRight, - children: [ - TextFormField( - onChanged: (value) => _walletNewVM.name = value, - controller: _controller, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 20.0, - fontWeight: FontWeight.w600, - color: - Theme.of(context).primaryTextTheme!.headline6!.color!), - decoration: InputDecoration( - hintStyle: TextStyle( - fontSize: 18.0, - fontWeight: FontWeight.w500, - color: Theme.of(context) - .accentTextTheme! - .headline2! - .color!), - hintText: S.of(context).wallet_name, - focusedBorder: UnderlineInputBorder( - borderSide: BorderSide( - color: Theme.of(context) - .accentTextTheme! - .headline2! - .decorationColor!, - width: 1.0)), - enabledBorder: UnderlineInputBorder( - borderSide: BorderSide( - color: Theme.of(context) - .accentTextTheme! - .headline2! - .decorationColor!, - width: 1.0), - ), - suffixIcon: IconButton( - onPressed: () async { - final rName = await generateName(); - FocusManager.instance.primaryFocus?.unfocus(); + content: Center( + child: ConstrainedBox( + constraints: + BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding( + padding: EdgeInsets.only(left: 12, right: 12), + child: AspectRatio( + aspectRatio: aspectRatioImage, + child: FittedBox(child: widget.walletImage, fit: BoxFit.fill)), + ), + Padding( + padding: EdgeInsets.only(top: 24), + child: Form( + key: _formKey, + child: Stack( + alignment: Alignment.centerRight, + children: [ + TextFormField( + onChanged: (value) => _walletNewVM.name = value, + controller: _controller, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 20.0, + fontWeight: FontWeight.w600, + color: Theme.of(context).primaryTextTheme!.headline6!.color!), + decoration: InputDecoration( + hintStyle: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.w500, + color: Theme.of(context).accentTextTheme!.headline2!.color!), + hintText: S.of(context).wallet_name, + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide( + color: Theme.of(context) + .accentTextTheme! + .headline2! + .decorationColor!, + width: 1.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide( + color: Theme.of(context) + .accentTextTheme! + .headline2! + .decorationColor!, + width: 1.0), + ), + suffixIcon: IconButton( + onPressed: () async { + final rName = await generateName(); + FocusManager.instance.primaryFocus?.unfocus(); - setState(() { - _controller.text = rName; - _walletNewVM.name = rName; - _controller.selection = TextSelection.fromPosition( - TextPosition(offset: _controller.text.length)); - }); - }, - icon: Container( - padding: const EdgeInsets.all(8), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(6.0), - color: Theme.of(context).hintColor, - ), - width: 34, - height: 34, - child: Image.asset( - 'assets/images/refresh_icon.png', - color: Theme.of(context) - .primaryTextTheme! - .headline4! - .decorationColor!, + setState(() { + _controller.text = rName; + _walletNewVM.name = rName; + _controller.selection = TextSelection.fromPosition( + TextPosition(offset: _controller.text.length)); + }); + }, + icon: Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(6.0), + color: Theme.of(context).hintColor, + ), + width: 34, + height: 34, + child: Image.asset( + 'assets/images/refresh_icon.png', + color: Theme.of(context) + .primaryTextTheme! + .headline4! + .decorationColor!, + ), + ), + ), ), + validator: WalletNameValidator(), ), - ), + ], ), - validator: WalletNameValidator(), ), - ], - ), + ), + if (_walletNewVM.hasLanguageSelector) ...[ + Padding( + padding: EdgeInsets.only(top: 40), + child: Text( + S.of(context).seed_language_choose, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16.0, + fontWeight: FontWeight.w500, + color: Theme.of(context).primaryTextTheme!.headline6!.color!), + ), + ), + Padding( + padding: EdgeInsets.only(top: 24), + child: SeedLanguageSelector( + key: _languageSelectorKey, initialSelected: defaultSeedLanguage), + ) + ] + ], ), ), - if (_walletNewVM.hasLanguageSelector) ...[ - Padding( - padding: EdgeInsets.only(top: 40), - child: Text( - S.of(context).seed_language_choose, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 16.0, - fontWeight: FontWeight.w500, - color: Theme.of(context).primaryTextTheme!.headline6!.color!), - ), - ), - Padding( - padding: EdgeInsets.only(top: 24), - child: SeedLanguageSelector( - key: _languageSelectorKey, - initialSelected: defaultSeedLanguage), - ) - ] - ]), - bottomSectionPadding: - EdgeInsets.all(24), + ), + bottomSectionPadding: EdgeInsets.all(24), bottomSection: Column( children: [ Observer( diff --git a/lib/src/screens/new_wallet/new_wallet_type_page.dart b/lib/src/screens/new_wallet/new_wallet_type_page.dart index 41179f34c..407582923 100644 --- a/lib/src/screens/new_wallet/new_wallet_type_page.dart +++ b/lib/src/screens/new_wallet/new_wallet_type_page.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:cake_wallet/themes/theme_base.dart'; import 'package:flutter/material.dart'; @@ -13,23 +14,19 @@ class NewWalletTypePage extends BasePage { final void Function(BuildContext, WalletType) onTypeSelected; final walletTypeImage = Image.asset('assets/images/wallet_type.png'); - final walletTypeLightImage = - Image.asset('assets/images/wallet_type_light.png'); + final walletTypeLightImage = Image.asset('assets/images/wallet_type_light.png'); @override String get title => S.current.wallet_list_restore_wallet; @override Widget body(BuildContext context) => WalletTypeForm( - onTypeSelected: onTypeSelected, - walletImage: currentTheme.type == ThemeType.dark - ? walletTypeImage - : walletTypeLightImage); + onTypeSelected: onTypeSelected, + walletImage: currentTheme.type == ThemeType.dark ? walletTypeImage : walletTypeLightImage); } class WalletTypeForm extends StatefulWidget { - WalletTypeForm({required this.onTypeSelected, - required this.walletImage}); + WalletTypeForm({required this.onTypeSelected, required this.walletImage}); final void Function(BuildContext, WalletType) onTypeSelected; final Image walletImage; @@ -39,22 +36,16 @@ class WalletTypeForm extends StatefulWidget { } class WalletTypeFormState extends State { - WalletTypeFormState() - : types = availableWalletTypes; + WalletTypeFormState() : types = availableWalletTypes; static const aspectRatioImage = 1.22; - final moneroIcon = - Image.asset('assets/images/monero_logo.png', height: 24, width: 24); - final bitcoinIcon = - Image.asset('assets/images/bitcoin.png', height: 24, width: 24); - final litecoinIcon = - Image.asset('assets/images/litecoin_icon.png', height: 24, width: 24); + final moneroIcon = Image.asset('assets/images/monero_logo.png', height: 24, width: 24); + final bitcoinIcon = Image.asset('assets/images/bitcoin.png', height: 24, width: 24); + final litecoinIcon = Image.asset('assets/images/litecoin_icon.png', height: 24, width: 24); final walletTypeImage = Image.asset('assets/images/wallet_type.png'); - final walletTypeLightImage = - Image.asset('assets/images/wallet_type_light.png'); - final havenIcon = - Image.asset('assets/images/haven_logo.png', height: 24, width: 24); + final walletTypeLightImage = Image.asset('assets/images/wallet_type_light.png'); + final havenIcon = Image.asset('assets/images/haven_logo.png', height: 24, width: 24); WalletType? selected; List types; @@ -69,35 +60,40 @@ class WalletTypeFormState extends State { Widget build(BuildContext context) { return ScrollableWithBottomSection( contentPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24), - content: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Padding( - padding: EdgeInsets.only(left: 12, right: 12), - child: AspectRatio( - aspectRatio: aspectRatioImage, - child: FittedBox(child: widget.walletImage, fit: BoxFit.fill)), + content: Center( + child: ConstrainedBox( + constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding( + padding: EdgeInsets.only(left: 12, right: 12), + child: AspectRatio( + aspectRatio: aspectRatioImage, + child: FittedBox(child: widget.walletImage, fit: BoxFit.fill)), + ), + Padding( + padding: EdgeInsets.only(top: 48), + child: Text( + S.of(context).choose_wallet_currency, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: Theme.of(context).primaryTextTheme.headline6!.color!), + ), + ), + ...types.map((type) => Padding( + padding: EdgeInsets.only(top: 24), + child: SelectButton( + image: _iconFor(type), + text: walletTypeToDisplayName(type), + isSelected: selected == type, + onTap: () => setState(() => selected = type)), + )) + ], ), - Padding( - padding: EdgeInsets.only(top: 48), - child: Text( - S.of(context).choose_wallet_currency, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: Theme.of(context).primaryTextTheme.headline6!.color!), - ), - ), - ...types.map((type) => Padding( - padding: EdgeInsets.only(top: 24), - child: SelectButton( - image: _iconFor(type), - text: walletTypeToDisplayName(type), - isSelected: selected == type, - onTap: () => setState(() => selected = type)), - )) - ], + ), ), bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24), bottomSection: PrimaryButton( @@ -121,7 +117,8 @@ class WalletTypeFormState extends State { case WalletType.haven: return havenIcon; default: - throw Exception('_iconFor: Incorrect Wallet Type. Cannot find icon for Wallet Type: ${type.toString()}'); + throw Exception( + '_iconFor: Incorrect Wallet Type. Cannot find icon for Wallet Type: ${type.toString()}'); } } diff --git a/lib/src/screens/nodes/node_create_or_edit_page.dart b/lib/src/screens/nodes/node_create_or_edit_page.dart index 723d8b1cc..6ca77d8de 100644 --- a/lib/src/screens/nodes/node_create_or_edit_page.dart +++ b/lib/src/screens/nodes/node_create_or_edit_page.dart @@ -4,7 +4,6 @@ import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cw_core/node.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:mobx/mobx.dart'; import 'package:cake_wallet/generated/i18n.dart'; diff --git a/lib/src/screens/pin_code/pin_code_widget.dart b/lib/src/screens/pin_code/pin_code_widget.dart index a647f3d95..8f30136d0 100644 --- a/lib/src/screens/pin_code/pin_code_widget.dart +++ b/lib/src/screens/pin_code/pin_code_widget.dart @@ -1,17 +1,19 @@ +import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cake_wallet/utils/show_bar.dart'; import 'package:another_flushbar/flushbar.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/generated/i18n.dart'; +import 'package:flutter/services.dart'; class PinCodeWidget extends StatefulWidget { - PinCodeWidget( - {required Key key, - required this.onFullPin, - required this.initialPinLength, - required this.onChangedPin, - required this.hasLengthSwitcher, - this.onChangedPinLength,}) - : super(key: key); + PinCodeWidget({ + required Key key, + required this.onFullPin, + required this.initialPinLength, + required this.onChangedPin, + required this.hasLengthSwitcher, + this.onChangedPinLength, + }) : super(key: key); final void Function(String pin, PinCodeState state) onFullPin; final void Function(String pin) onChangedPin; @@ -25,10 +27,10 @@ class PinCodeWidget extends StatefulWidget { class PinCodeState extends State { PinCodeState() - : _aspectRatio = 0, - pinLength = 0, - pin = '', - title = ''; + : _aspectRatio = 0, + pinLength = 0, + pin = '', + title = ''; static const defaultPinLength = fourPinLength; static const sixPinLength = 6; static const fourPinLength = 4; @@ -75,8 +77,7 @@ class PinCodeState extends State { void setDefaultPinLength() => changePinLength(widget.initialPinLength); void calculateAspectRatio() { - final renderBox = - _gridViewKey.currentContext!.findRenderObject() as RenderBox; + final renderBox = _gridViewKey.currentContext!.findRenderObject() as RenderBox; final cellWidth = renderBox.size.width / 3; final cellHeight = renderBox.size.height / 4; @@ -89,8 +90,7 @@ class PinCodeState extends State { void changeProcessText(String text) { hideProgressText(); - _progressBar = createBar(text, duration: null) - ..show(_key.currentContext!); + _progressBar = createBar(text, duration: null)..show(_key.currentContext!); } void close() { @@ -104,8 +104,8 @@ class PinCodeState extends State { } @override - Widget build(BuildContext context) => Scaffold( - key: _key, body: body(context), resizeToAvoidBottomInset: false); + Widget build(BuildContext context) => + Scaffold(key: _key, body: body(context), resizeToAvoidBottomInset: false); Widget body(BuildContext context) { final deleteIconImage = Image.asset( @@ -117,157 +117,184 @@ class PinCodeState extends State { color: Theme.of(context).primaryTextTheme!.headline6!.color!, ); - return Container( - color: Theme.of(context).backgroundColor, - padding: EdgeInsets.only(left: 40.0, right: 40.0, bottom: 40.0), - child: Column(children: [ - Spacer(flex: 2), - Text(title, - style: TextStyle( - fontSize: 20, - fontWeight: FontWeight.w500, - color: Theme.of(context).primaryTextTheme!.headline6!.color!)), - Spacer(flex: 3), - Container( - width: 180, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: List.generate(pinLength, (index) { - const size = 10.0; - final isFilled = pin.length > index ? pin[index] != null : false; - - return Container( - width: size, - height: size, - decoration: BoxDecoration( - shape: BoxShape.circle, - color: isFilled - ? Theme.of(context).primaryTextTheme!.headline6!.color! - : Theme.of(context) - .accentTextTheme! - .bodyText2! - .color! - .withOpacity(0.25), - )); - }), - ), - ), - Spacer(flex: 2), - if (widget.hasLengthSwitcher) ...[ - TextButton( - onPressed: () { - changePinLength(pinLength == PinCodeState.fourPinLength - ? PinCodeState.sixPinLength - : PinCodeState.fourPinLength); - }, - child: Text( - _changePinLengthText(), + return RawKeyboardListener( + focusNode: FocusNode(), + autofocus: true, + onKey: (keyEvent) { + if (keyEvent is RawKeyDownEvent) { + if (keyEvent.logicalKey.keyLabel == "Backspace") { + _pop(); + return; + } + int? number = int.tryParse(keyEvent.character ?? ''); + if (number != null) { + _push(number); + } + } + }, + child: Container( + color: Theme.of(context).backgroundColor, + padding: EdgeInsets.only(left: 40.0, right: 40.0, bottom: 40.0), + child: Column( + children: [ + Spacer(flex: 2), + Text(title, style: TextStyle( - fontSize: 14.0, - fontWeight: FontWeight.normal, - color: Theme.of(context) - .accentTextTheme! - .bodyText2! - .decorationColor!), - )) - ], - Spacer(flex: 1), - Flexible( - flex: 24, - child: Container( - key: _gridViewKey, - child: _aspectRatio > 0 - ? GridView.count( - shrinkWrap: true, - crossAxisCount: 3, - childAspectRatio: _aspectRatio, - physics: const NeverScrollableScrollPhysics(), - children: List.generate(12, (index) { - const double marginRight = 15; - const double marginLeft = 15; + fontSize: 20, + fontWeight: FontWeight.w500, + color: Theme.of(context).primaryTextTheme!.headline6!.color!)), + Spacer(flex: 3), + Container( + width: 180, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: List.generate(pinLength, (index) { + const size = 10.0; + final isFilled = pin.length > index ? pin[index] != null : false; - if (index == 9) { - return Container( - margin: EdgeInsets.only( - left: marginLeft, right: marginRight), - child: TextButton( - onPressed: () => null, - // (widget.hasLengthSwitcher || - // !settingsStore - // .allowBiometricalAuthentication) - // ? null - // : () { - // FIXME -// if (authStore != null) { -// WidgetsBinding.instance.addPostFrameCallback((_) { -// final biometricAuth = BiometricAuth(); -// biometricAuth.isAuthenticated().then( -// (isAuth) { -// if (isAuth) { -// authStore.biometricAuth(); -// _key.currentState.showSnackBar( -// SnackBar( -// content: Text(S.of(context).authenticated), -// backgroundColor: Colors.green, -// ), -// ); -// } -// } -// ); -// }); -// } -// }, - // FIX-ME: Style - //color: Theme.of(context).backgroundColor, - //shape: CircleBorder(), - child: Container() - // (widget.hasLengthSwitcher || - // !settingsStore - // .allowBiometricalAuthentication) - // ? Offstage() - // : faceImage, + return Container( + width: size, + height: size, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: isFilled + ? Theme.of(context).primaryTextTheme!.headline6!.color! + : Theme.of(context) + .accentTextTheme! + .bodyText2! + .color! + .withOpacity(0.25), + )); + }), + ), + ), + Spacer(flex: 2), + if (widget.hasLengthSwitcher) ...[ + TextButton( + onPressed: () { + changePinLength(pinLength == PinCodeState.fourPinLength + ? PinCodeState.sixPinLength + : PinCodeState.fourPinLength); + }, + child: Text( + _changePinLengthText(), + style: TextStyle( + fontSize: 14.0, + fontWeight: FontWeight.normal, + color: Theme.of(context).accentTextTheme!.bodyText2!.decorationColor!), + ), + ) + ], + Spacer(flex: 1), + Flexible( + flex: 24, + child: Center( + child: ConstrainedBox( + constraints: BoxConstraints( + maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint, + ), + child: Container( + key: _gridViewKey, + child: _aspectRatio > 0 + ? ScrollConfiguration( + behavior: ScrollConfiguration.of(context).copyWith(scrollbars: false), + child: GridView.count( + shrinkWrap: true, + crossAxisCount: 3, + childAspectRatio: _aspectRatio, + physics: const NeverScrollableScrollPhysics(), + children: List.generate(12, (index) { + const double marginRight = 15; + const double marginLeft = 15; + + if (index == 9) { + return Container( + margin: EdgeInsets.only(left: marginLeft, right: marginRight), + child: TextButton( + onPressed: () => null, + // (widget.hasLengthSwitcher || + // !settingsStore + // .allowBiometricalAuthentication) + // ? null + // : () { + // FIXME + // if (authStore != null) { + // WidgetsBinding.instance.addPostFrameCallback((_) { + // final biometricAuth = BiometricAuth(); + // biometricAuth.isAuthenticated().then( + // (isAuth) { + // if (isAuth) { + // authStore.biometricAuth(); + // _key.currentState.showSnackBar( + // SnackBar( + // content: Text(S.of(context).authenticated), + // backgroundColor: Colors.green, + // ), + // ); + // } + // } + // ); + // }); + // } + // }, + // FIX-ME: Style + //color: Theme.of(context).backgroundColor, + //shape: CircleBorder(), + child: Container() + // (widget.hasLengthSwitcher || + // !settingsStore + // .allowBiometricalAuthentication) + // ? Offstage() + // : faceImage, + ), + ); + } else if (index == 10) { + index = 0; + } else if (index == 11) { + return Container( + margin: EdgeInsets.only(left: marginLeft, right: marginRight), + child: TextButton( + onPressed: () => _pop(), + style: TextButton.styleFrom( + backgroundColor: Theme.of(context).backgroundColor, + shape: CircleBorder(), + ), + child: deleteIconImage, + ), + ); + } else { + index++; + } + + return Container( + margin: EdgeInsets.only(left: marginLeft, right: marginRight), + child: TextButton( + onPressed: () => _push(index), + style: TextButton.styleFrom( + backgroundColor: Theme.of(context).backgroundColor, + shape: CircleBorder(), + ), + child: Text('$index', + style: TextStyle( + fontSize: 30.0, + fontWeight: FontWeight.w600, + color: Theme.of(context) + .primaryTextTheme! + .headline6! + .color!)), ), - ); - } else if (index == 10) { - index = 0; - } else if (index == 11) { - return Container( - margin: EdgeInsets.only( - left: marginLeft, right: marginRight), - child: TextButton( - onPressed: () => _pop(), - // FIX-ME: Style - //color: Theme.of(context).backgroundColor, - //shape: CircleBorder(), - child: deleteIconImage, - ), - ); - } else { - index++; - } - - return Container( - margin: EdgeInsets.only( - left: marginLeft, right: marginRight), - child: TextButton( - onPressed: () => _push(index), - // FIX-ME: Style - //color: Theme.of(context).backgroundColor, - //shape: CircleBorder(), - child: Text('$index', - style: TextStyle( - fontSize: 30.0, - fontWeight: FontWeight.w600, - color: Theme.of(context) - .primaryTextTheme! - .headline6! - .color!)), + ); + }), ), - ); - }), - ) - : null)) - ]), + ) + : null, + ), + ), + ), + ) + ], + ), + ), ); } diff --git a/lib/src/screens/receive/anonpay_invoice_page.dart b/lib/src/screens/receive/anonpay_invoice_page.dart index 91c9aaef1..055307d08 100644 --- a/lib/src/screens/receive/anonpay_invoice_page.dart +++ b/lib/src/screens/receive/anonpay_invoice_page.dart @@ -8,6 +8,7 @@ import 'package:cake_wallet/src/screens/dashboard/widgets/present_receive_option import 'package:cake_wallet/src/screens/receive/widgets/anonpay_input_form.dart'; import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; import 'package:cake_wallet/src/widgets/keyboard_done_button.dart'; +import 'package:cake_wallet/utils/device_info.dart'; import 'package:cake_wallet/view_model/anon_invoice_page_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.dart'; import 'package:flutter/material.dart'; @@ -85,7 +86,7 @@ class AnonPayInvoicePage extends BasePage { child: ScrollableWithBottomSection( contentPadding: EdgeInsets.only(bottom: 24), content: Container( - decoration: BoxDecoration( + decoration: DeviceInfo.instance.isMobile ? BoxDecoration( borderRadius: BorderRadius.only( bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24)), gradient: LinearGradient( @@ -96,7 +97,7 @@ class AnonPayInvoicePage extends BasePage { begin: Alignment.topLeft, end: Alignment.bottomRight, ), - ), + ) : null, child: Observer(builder: (_) { return Padding( padding: EdgeInsets.fromLTRB(24, 120, 24, 0), @@ -174,7 +175,6 @@ class AnonPayInvoicePage extends BasePage { } reaction((_) => receiveOptionViewModel.selectedReceiveOption, (ReceivePageOption option) { - Navigator.pop(context); switch (option) { case ReceivePageOption.mainnet: Navigator.popAndPushNamed(context, Routes.addressPage); diff --git a/lib/src/screens/receive/widgets/qr_widget.dart b/lib/src/screens/receive/widgets/qr_widget.dart index 96a19f850..9e68ff0e1 100644 --- a/lib/src/screens/receive/widgets/qr_widget.dart +++ b/lib/src/screens/receive/widgets/qr_widget.dart @@ -1,4 +1,5 @@ import 'package:cake_wallet/routes.dart'; +import 'package:cake_wallet/utils/device_info.dart'; import 'package:cake_wallet/utils/show_bar.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:device_display_brightness/device_display_brightness.dart'; @@ -35,7 +36,7 @@ class QRWidget extends StatelessWidget { @override Widget build(BuildContext context) { final copyImage = Image.asset('assets/images/copy_address.png', - color: Theme.of(context).textTheme!.subtitle1!.decorationColor!); + color: Theme.of(context).textTheme.subtitle1!.decorationColor!); return Column( mainAxisSize: MainAxisSize.min, @@ -51,23 +52,18 @@ class QRWidget extends StatelessWidget { style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, - color: Theme.of(context).accentTextTheme!.headline2!.backgroundColor!), + color: Theme.of(context).accentTextTheme.headline2!.backgroundColor!), ), ), Row( children: [ Spacer(flex: 3), Observer( - builder: (_) { - return Flexible( - flex: 5, - child: GestureDetector( - onTap: () async { - // Get the current brightness: - final double brightness = await DeviceDisplayBrightness.getBrightness(); - - // ignore: unawaited_futures - DeviceDisplayBrightness.setBrightness(1.0); + builder: (_) => Flexible( + flex: 5, + child: GestureDetector( + onTap: () { + changeBrightnessForRoute(() async { await Navigator.pushNamed( context, Routes.fullscreenQR, @@ -75,31 +71,28 @@ class QRWidget extends StatelessWidget { 'qrData': addressListViewModel.uri.toString(), }, ); - // ignore: unawaited_futures - DeviceDisplayBrightness.setBrightness(brightness); - }, - child: Hero( - tag: Key(addressListViewModel.uri.toString()), - child: Center( - child: AspectRatio( - aspectRatio: 1.0, - child: Container( - padding: EdgeInsets.all(5), - decoration: BoxDecoration( - border: Border.all( - width: 3, - color: - Theme.of(context).accentTextTheme!.headline2!.backgroundColor!, - ), + }); + }, + child: Hero( + tag: Key(addressListViewModel.uri.toString()), + child: Center( + child: AspectRatio( + aspectRatio: 1.0, + child: Container( + padding: EdgeInsets.all(5), + decoration: BoxDecoration( + border: Border.all( + width: 3, + color: Theme.of(context).accentTextTheme.headline2!.backgroundColor!, ), - child: QrImage(data: addressListViewModel.uri.toString(), version: qrVersion), ), + child: QrImage(data: addressListViewModel.uri.toString()), ), ), ), ), - ); - } + ), + ), ), Spacer(flex: 3) ], @@ -176,4 +169,23 @@ class QRWidget extends StatelessWidget { ], ); } + + Future changeBrightnessForRoute(Future Function() navigation) async { + // if not mobile, just navigate + if (!DeviceInfo.instance.isMobile) { + navigation(); + return; + } + + // Get the current brightness: + final brightness = await DeviceDisplayBrightness.getBrightness(); + + // ignore: unawaited_futures + DeviceDisplayBrightness.setBrightness(1.0); + + await navigation(); + + // ignore: unawaited_futures + DeviceDisplayBrightness.setBrightness(brightness); + } } diff --git a/lib/src/screens/restore/restore_from_backup_page.dart b/lib/src/screens/restore/restore_from_backup_page.dart index 1ed2aec4b..16aa3dbef 100644 --- a/lib/src/screens/restore/restore_from_backup_page.dart +++ b/lib/src/screens/restore/restore_from_backup_page.dart @@ -1,6 +1,7 @@ import 'package:cake_wallet/core/execution_state.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; +import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:flutter/material.dart'; import 'package:flutter/src/widgets/framework.dart'; @@ -39,43 +40,48 @@ class RestoreFromBackupPage extends BasePage { } }); - return Container( - padding: EdgeInsets.only(bottom: 24, left: 24, right: 24), - child: Column(children: [ - Expanded( - child: Container( - child: Center( - child: TextFormField( - obscureText: true, - enableSuggestions: false, - autocorrect: false, - decoration: InputDecoration( - hintText: S.of(context).enter_backup_password), - keyboardType: TextInputType.visiblePassword, - controller: textEditingController, - style: TextStyle(fontSize: 26, color: Colors.black))), - ), - ), - Container( - child: Row(children: [ - Expanded( - child: PrimaryButton( - onPressed: () => presentFilePicker(), - text: S.of(context).select_backup_file, - color: Colors.grey, - textColor: Colors.white)), - SizedBox(width: 20), - Expanded(child: Observer(builder: (_) { - return LoadingPrimaryButton( - isLoading: - restoreFromBackupViewModel.state is IsExecutingState, - onPressed: () => onImportHandler(context), - text: S.of(context).import, - color: Theme.of(context).accentTextTheme!.bodyText1!.color!, - textColor: Colors.white); - })) - ])), - ])); + return Center( + child: ConstrainedBox( + constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint), + child: Padding( + padding: EdgeInsets.only(bottom: 24, left: 24, right: 24), + child: Column(children: [ + Expanded( + child: Container( + child: Center( + child: TextFormField( + obscureText: true, + enableSuggestions: false, + autocorrect: false, + decoration: InputDecoration( + hintText: S.of(context).enter_backup_password), + keyboardType: TextInputType.visiblePassword, + controller: textEditingController, + style: TextStyle(fontSize: 26, color: Colors.black))), + ), + ), + Container( + child: Row(children: [ + Expanded( + child: PrimaryButton( + onPressed: () => presentFilePicker(), + text: S.of(context).select_backup_file, + color: Colors.grey, + textColor: Colors.white)), + SizedBox(width: 20), + Expanded(child: Observer(builder: (_) { + return LoadingPrimaryButton( + isLoading: + restoreFromBackupViewModel.state is IsExecutingState, + onPressed: () => onImportHandler(context), + text: S.of(context).import, + color: Theme.of(context).accentTextTheme!.bodyText1!.color!, + textColor: Colors.white); + })) + ])), + ])), + ), + ); } Future presentFilePicker() async { diff --git a/lib/src/screens/restore/restore_options_page.dart b/lib/src/screens/restore/restore_options_page.dart index a49fd3cc7..a7eb03778 100644 --- a/lib/src/screens/restore/restore_options_page.dart +++ b/lib/src/screens/restore/restore_options_page.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/routes.dart'; import 'package:flutter/cupertino.dart'; @@ -18,31 +19,33 @@ class RestoreOptionsPage extends BasePage { @override Widget body(BuildContext context) { - return Container( - width: double.infinity, - height: double.infinity, - padding: EdgeInsets.all(24), - child: SingleChildScrollView( - child: Column( - children: [ - RestoreButton( - onPressed: () => - Navigator.pushNamed(context, Routes.restoreWalletOptionsFromWelcome), - image: imageSeedKeys, - title: S.of(context).restore_title_from_seed_keys, - description: - S.of(context).restore_description_from_seed_keys), - Padding( - padding: EdgeInsets.only(top: 24), - child: RestoreButton( + return Center( + child: Container( + width: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint, + height: double.infinity, + padding: EdgeInsets.symmetric(vertical: 24), + child: SingleChildScrollView( + child: Column( + children: [ + RestoreButton( onPressed: () => - Navigator.pushNamed(context, Routes.restoreFromBackup), - image: imageBackup, - title: S.of(context).restore_title_from_backup, - description: S.of(context).restore_description_from_backup), - ) - ], - ), - )); + Navigator.pushNamed(context, Routes.restoreWalletOptionsFromWelcome), + image: imageSeedKeys, + title: S.of(context).restore_title_from_seed_keys, + description: + S.of(context).restore_description_from_seed_keys), + Padding( + padding: EdgeInsets.only(top: 24), + child: RestoreButton( + onPressed: () => + Navigator.pushNamed(context, Routes.restoreFromBackup), + image: imageBackup, + title: S.of(context).restore_title_from_backup, + description: S.of(context).restore_description_from_backup), + ) + ], + ), + )), + ); } } diff --git a/lib/src/screens/restore/wallet_restore_page.dart b/lib/src/screens/restore/wallet_restore_page.dart index ce9af985e..7288d624b 100644 --- a/lib/src/screens/restore/wallet_restore_page.dart +++ b/lib/src/screens/restore/wallet_restore_page.dart @@ -1,5 +1,6 @@ import 'package:cake_wallet/src/widgets/keyboard_done_button.dart'; import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; +import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -23,10 +24,8 @@ import 'package:cake_wallet/core/seed_validator.dart'; class WalletRestorePage extends BasePage { WalletRestorePage(this.walletRestoreViewModel) - : walletRestoreFromSeedFormKey = - GlobalKey(), - walletRestoreFromKeysFormKey = - GlobalKey(), + : walletRestoreFromSeedFormKey = GlobalKey(), + walletRestoreFromKeysFormKey = GlobalKey(), _pages = [], _blockHeightFocusNode = FocusNode(), _controller = PageController(initialPage: 0) { @@ -36,9 +35,8 @@ class WalletRestorePage extends BasePage { _pages.add(WalletRestoreFromSeedForm( displayBlockHeightSelector: walletRestoreViewModel.hasBlockchainHeightLanguageSelector, - displayLanguageSelector: - walletRestoreViewModel.hasSeedLanguageSelector, - type: walletRestoreViewModel.type!, + displayLanguageSelector: walletRestoreViewModel.hasSeedLanguageSelector, + type: walletRestoreViewModel.type, key: walletRestoreFromSeedFormKey, blockHeightFocusNode: _blockHeightFocusNode, onHeightOrDateEntered: (value) { @@ -48,26 +46,22 @@ class WalletRestorePage extends BasePage { }, onSeedChange: (String seed) { if (walletRestoreViewModel.hasBlockchainHeightLanguageSelector) { - final hasHeight = walletRestoreFromSeedFormKey - .currentState!.blockchainHeightKey.currentState!.restoreHeightController - .text - .isNotEmpty; + final hasHeight = walletRestoreFromSeedFormKey.currentState!.blockchainHeightKey + .currentState!.restoreHeightController.text.isNotEmpty; if (hasHeight) { walletRestoreViewModel.isButtonEnabled = _isValidSeed(); } } else { - walletRestoreViewModel.isButtonEnabled = _isValidSeed(); + walletRestoreViewModel.isButtonEnabled = _isValidSeed(); } }, onLanguageChange: (_) { if (walletRestoreViewModel.hasBlockchainHeightLanguageSelector) { - final hasHeight = walletRestoreFromSeedFormKey - .currentState!.blockchainHeightKey.currentState!.restoreHeightController - .text - .isNotEmpty; + final hasHeight = walletRestoreFromSeedFormKey.currentState!.blockchainHeightKey + .currentState!.restoreHeightController.text.isNotEmpty; if (hasHeight) { - walletRestoreViewModel.isButtonEnabled = _isValidSeed(); + walletRestoreViewModel.isButtonEnabled = _isValidSeed(); } } else { walletRestoreViewModel.isButtonEnabled = _isValidSeed(); @@ -78,8 +72,7 @@ class WalletRestorePage extends BasePage { _pages.add(WalletRestoreFromKeysFrom( key: walletRestoreFromKeysFormKey, walletRestoreViewModel: walletRestoreViewModel, - onHeightOrDateEntered: (value) => - walletRestoreViewModel.isButtonEnabled = value)); + onHeightOrDateEntered: (value) => walletRestoreViewModel.isButtonEnabled = value)); break; default: break; @@ -97,8 +90,7 @@ class WalletRestorePage extends BasePage { fontSize: 18.0, fontWeight: FontWeight.bold, fontFamily: 'Lato', - color: titleColor ?? - Theme.of(context).primaryTextTheme!.headline6!.color!), + color: titleColor ?? Theme.of(context).primaryTextTheme.headline6!.color!), )); final WalletRestoreViewModel walletRestoreViewModel; @@ -129,134 +121,134 @@ class WalletRestorePage extends BasePage { reaction((_) => walletRestoreViewModel.mode, (WalletRestoreMode mode) { walletRestoreViewModel.isButtonEnabled = false; - walletRestoreFromSeedFormKey.currentState!.blockchainHeightKey.currentState - !.restoreHeightController.text = ''; - walletRestoreFromSeedFormKey.currentState!.blockchainHeightKey.currentState - !.dateController.text = ''; + walletRestoreFromSeedFormKey + .currentState!.blockchainHeightKey.currentState!.restoreHeightController.text = ''; + walletRestoreFromSeedFormKey + .currentState!.blockchainHeightKey.currentState!.dateController.text = ''; walletRestoreFromSeedFormKey.currentState!.nameTextEditingController.text = ''; - walletRestoreFromKeysFormKey.currentState!.blockchainHeightKey.currentState - !.restoreHeightController.text = ''; - walletRestoreFromKeysFormKey.currentState!.blockchainHeightKey.currentState - !.dateController.text = ''; + walletRestoreFromKeysFormKey + .currentState!.blockchainHeightKey.currentState!.restoreHeightController.text = ''; + walletRestoreFromKeysFormKey + .currentState!.blockchainHeightKey.currentState!.dateController.text = ''; walletRestoreFromKeysFormKey.currentState!.nameTextEditingController.text = ''; }); return KeyboardActions( - config: KeyboardActionsConfig( - keyboardActionsPlatform: KeyboardActionsPlatform.IOS, - keyboardBarColor: Theme.of(context).accentTextTheme!.bodyText1! - .backgroundColor!, - nextFocus: false, - actions: [ - KeyboardActionsItem( - focusNode: _blockHeightFocusNode, - toolbarButtons: [(_) => KeyboardDoneButton()], - ) - ]), - child: Container( - height: 0, - color: Theme.of(context).backgroundColor, - child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ - Expanded( - child: PageView.builder( - onPageChanged: (page) { - walletRestoreViewModel.mode = - page == 0 ? WalletRestoreMode.seed : WalletRestoreMode.keys; - }, - controller: _controller, - itemCount: _pages.length, - itemBuilder: (_, index) => - SingleChildScrollView(child: _pages[index]))), - if (_pages.length > 1) - Padding( - padding: EdgeInsets.only(top: 10), - child: SmoothPageIndicator( - controller: _controller, - count: _pages.length, - effect: ColorTransitionEffect( - spacing: 6.0, - radius: 6.0, - dotWidth: 6.0, - dotHeight: 6.0, - dotColor: Theme.of(context).hintColor.withOpacity(0.5), - activeDotColor: Theme.of(context).hintColor), - )), - Padding( - padding: EdgeInsets.only(top: 20, bottom: 24, left: 24, right: 24), - child: Observer( - builder: (context) { - return LoadingPrimaryButton( - onPressed: _confirmForm, - text: S.of(context).restore_recover, - color: - Theme.of(context).accentTextTheme!.subtitle2!.decorationColor!, - textColor: - Theme.of(context).accentTextTheme!.headline5!.decorationColor!, - isLoading: walletRestoreViewModel.state is IsExecutingState, - isDisabled: !walletRestoreViewModel.isButtonEnabled, - ); - }, - )) - ]))); + config: KeyboardActionsConfig( + keyboardActionsPlatform: KeyboardActionsPlatform.IOS, + keyboardBarColor: Theme.of(context).accentTextTheme.bodyText1!.backgroundColor!, + nextFocus: false, + actions: [ + KeyboardActionsItem( + focusNode: _blockHeightFocusNode, + toolbarButtons: [(_) => KeyboardDoneButton()], + ) + ], + ), + child: Container( + height: 0, + color: Theme.of(context).backgroundColor, + child: Center( + child: ConstrainedBox( + constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Expanded( + child: PageView.builder( + onPageChanged: (page) { + walletRestoreViewModel.mode = + page == 0 ? WalletRestoreMode.seed : WalletRestoreMode.keys; + }, + controller: _controller, + itemCount: _pages.length, + itemBuilder: (_, index) => SingleChildScrollView(child: _pages[index]), + ), + ), + if (_pages.length > 1) + Padding( + padding: EdgeInsets.only(top: 10), + child: SmoothPageIndicator( + controller: _controller, + count: _pages.length, + effect: ColorTransitionEffect( + spacing: 6.0, + radius: 6.0, + dotWidth: 6.0, + dotHeight: 6.0, + dotColor: Theme.of(context).hintColor.withOpacity(0.5), + activeDotColor: Theme.of(context).hintColor, + ), + ), + ), + Padding( + padding: EdgeInsets.only(top: 20, bottom: 24, left: 24, right: 24), + child: Observer( + builder: (context) { + return LoadingPrimaryButton( + onPressed: _confirmForm, + text: S.of(context).restore_recover, + color: Theme.of(context).accentTextTheme.subtitle2!.decorationColor!, + textColor: Theme.of(context).accentTextTheme.headline5!.decorationColor!, + isLoading: walletRestoreViewModel.state is IsExecutingState, + isDisabled: !walletRestoreViewModel.isButtonEnabled, + ); + }, + ), + ) + ], + ), + ), + ), + ), + ); } bool _isValidSeed() { - final seedWords = walletRestoreFromSeedFormKey - .currentState - !.seedWidgetStateKey - .currentState - !.text - .split(' '); + final seedWords = + walletRestoreFromSeedFormKey.currentState!.seedWidgetStateKey.currentState!.text.split(' '); - if ((walletRestoreViewModel.type == WalletType.monero || walletRestoreViewModel.type == WalletType.haven) && + if ((walletRestoreViewModel.type == WalletType.monero || + walletRestoreViewModel.type == WalletType.haven) && seedWords.length != WalletRestoreViewModelBase.moneroSeedMnemonicLength) { return false; } - + if ((walletRestoreViewModel.type == WalletType.bitcoin || - walletRestoreViewModel.type == WalletType.litecoin) && - (seedWords.length != WalletRestoreViewModelBase.electrumSeedMnemonicLength && - seedWords.length != WalletRestoreViewModelBase.electrumShortSeedMnemonicLength)) { + walletRestoreViewModel.type == WalletType.litecoin) && + (seedWords.length != WalletRestoreViewModelBase.electrumSeedMnemonicLength && + seedWords.length != WalletRestoreViewModelBase.electrumShortSeedMnemonicLength)) { return false; } - final words = walletRestoreFromSeedFormKey - .currentState - !.seedWidgetStateKey - .currentState - !.words - .toSet(); - return seedWords - .toSet() - .difference(words) - .toSet() - .isEmpty; + final words = + walletRestoreFromSeedFormKey.currentState!.seedWidgetStateKey.currentState!.words.toSet(); + return seedWords.toSet().difference(words).toSet().isEmpty; } Map _credentials() { final credentials = {}; if (walletRestoreViewModel.mode == WalletRestoreMode.seed) { - credentials['seed'] = walletRestoreFromSeedFormKey - .currentState!.seedWidgetStateKey.currentState!.text; + credentials['seed'] = + walletRestoreFromSeedFormKey.currentState!.seedWidgetStateKey.currentState!.text; if (walletRestoreViewModel.hasBlockchainHeightLanguageSelector) { - credentials['height'] = walletRestoreFromSeedFormKey - .currentState!.blockchainHeightKey.currentState!.height; + credentials['height'] = + walletRestoreFromSeedFormKey.currentState!.blockchainHeightKey.currentState!.height; } - credentials['name'] = walletRestoreFromSeedFormKey.currentState!.nameTextEditingController.text; + credentials['name'] = + walletRestoreFromSeedFormKey.currentState!.nameTextEditingController.text; } else { - credentials['address'] = - walletRestoreFromKeysFormKey.currentState!.addressController.text; - credentials['viewKey'] = - walletRestoreFromKeysFormKey.currentState!.viewKeyController.text; - credentials['spendKey'] = - walletRestoreFromKeysFormKey.currentState!.spendKeyController.text; - credentials['height'] = walletRestoreFromKeysFormKey - .currentState!.blockchainHeightKey.currentState!.height; - credentials['name'] = walletRestoreFromKeysFormKey.currentState!.nameTextEditingController.text; + credentials['address'] = walletRestoreFromKeysFormKey.currentState!.addressController.text; + credentials['viewKey'] = walletRestoreFromKeysFormKey.currentState!.viewKeyController.text; + credentials['spendKey'] = walletRestoreFromKeysFormKey.currentState!.spendKeyController.text; + credentials['height'] = + walletRestoreFromKeysFormKey.currentState!.blockchainHeightKey.currentState!.height; + credentials['name'] = + walletRestoreFromKeysFormKey.currentState!.nameTextEditingController.text; } return credentials; @@ -272,10 +264,8 @@ class WalletRestorePage extends BasePage { : walletRestoreFromKeysFormKey.currentState!.formKey; final name = walletRestoreViewModel.mode == WalletRestoreMode.seed - ? walletRestoreFromSeedFormKey - .currentState!.nameTextEditingController.value.text - : walletRestoreFromKeysFormKey - .currentState!.nameTextEditingController.value.text; + ? walletRestoreFromSeedFormKey.currentState!.nameTextEditingController.value.text + : walletRestoreFromKeysFormKey.currentState!.nameTextEditingController.value.text; if (!formKey.currentState!.validate()) { return; @@ -301,5 +291,3 @@ class WalletRestorePage extends BasePage { }); } } - - diff --git a/lib/src/screens/root/root.dart b/lib/src/screens/root/root.dart index 2526d15f3..fe7ea26a8 100644 --- a/lib/src/screens/root/root.dart +++ b/lib/src/screens/root/root.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'package:cake_wallet/core/auth_service.dart'; +import 'package:cake_wallet/utils/device_info.dart'; import 'package:cake_wallet/utils/payment_request.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/routes.dart'; @@ -54,7 +55,9 @@ class RootState extends State with WidgetsBindingObserver { WidgetsBinding.instance.addObserver(this); super.initState(); - initUniLinks(); + if (DeviceInfo.instance.isMobile) { + initUniLinks(); + } } @override diff --git a/lib/src/screens/seed/pre_seed_page.dart b/lib/src/screens/seed/pre_seed_page.dart index c4ecbbf9a..86d88c96e 100644 --- a/lib/src/screens/seed/pre_seed_page.dart +++ b/lib/src/screens/seed/pre_seed_page.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/themes/theme_base.dart'; @@ -33,44 +34,48 @@ class PreSeedPage extends BasePage { return WillPopScope( onWillPop: () async => false, child: Container( + alignment: Alignment.center, padding: EdgeInsets.all(24), - child: Column( - children: [ - Flexible( - flex: 2, - child: AspectRatio( - aspectRatio: 1, - child: FittedBox(child: image, fit: BoxFit.contain))), - Flexible( - flex: 3, - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Padding( - padding: EdgeInsets.only(top: 70, left: 16, right: 16), - child: Text( - S - .of(context) - .pre_seed_description(wordsCount.toString()), - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.normal, - color: Theme.of(context) - .primaryTextTheme! - .caption! - .color!), + child: ConstrainedBox( + constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint), + child: Column( + children: [ + Flexible( + flex: 2, + child: AspectRatio( + aspectRatio: 1, + child: FittedBox(child: image, fit: BoxFit.contain))), + Flexible( + flex: 3, + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: EdgeInsets.only(top: 70, left: 16, right: 16), + child: Text( + S + .of(context) + .pre_seed_description(wordsCount.toString()), + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.normal, + color: Theme.of(context) + .primaryTextTheme! + .caption! + .color!), + ), ), - ), - PrimaryButton( - onPressed: () => Navigator.of(context) - .popAndPushNamed(Routes.seed, arguments: true), - text: S.of(context).pre_seed_button_text, - color: Theme.of(context).accentTextTheme!.bodyText1!.color!, - textColor: Colors.white) - ], - )) - ], + PrimaryButton( + onPressed: () => Navigator.of(context) + .popAndPushNamed(Routes.seed, arguments: true), + text: S.of(context).pre_seed_button_text, + color: Theme.of(context).accentTextTheme!.bodyText1!.color!, + textColor: Colors.white) + ], + )) + ], + ), ), )); } diff --git a/lib/src/screens/seed/wallet_seed_page.dart b/lib/src/screens/seed/wallet_seed_page.dart index 64895db36..034ab832c 100644 --- a/lib/src/screens/seed/wallet_seed_page.dart +++ b/lib/src/screens/seed/wallet_seed_page.dart @@ -2,6 +2,7 @@ import 'package:cake_wallet/palette.dart'; import 'package:cake_wallet/themes/theme_base.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/utils/share_util.dart'; +import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cake_wallet/utils/show_bar.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:flutter/material.dart'; @@ -52,7 +53,7 @@ class WalletSeedPage extends BasePage { @override Widget? leading(BuildContext context) => - isNewWalletCreated ? Offstage() : super.leading(context); + isNewWalletCreated ? null: super.leading(context); @override Widget trailing(BuildContext context) { @@ -85,114 +86,119 @@ class WalletSeedPage extends BasePage { return WillPopScope(onWillPop: () async => false, child: Container( padding: EdgeInsets.all(24), - child: Column( - children: [ - Flexible( - flex: 2, - child: AspectRatio( - aspectRatio: 1, - child: FittedBox(child: image, fit: BoxFit.fill))), - Flexible( - flex: 3, - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Padding( - padding: EdgeInsets.only(top: 33), - child: Observer(builder: (_) { - return Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Text( - walletSeedViewModel.name, - style: TextStyle( - fontSize: 20, - fontWeight: FontWeight.w600, - color: Theme.of(context) - .primaryTextTheme! - .headline6! - .color!), - ), - Padding( - padding: - EdgeInsets.only(top: 20, left: 16, right: 16), - child: Text( - walletSeedViewModel.seed, - textAlign: TextAlign.center, + alignment: Alignment.center, + child: ConstrainedBox( + constraints: + BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint), + child: Column( + children: [ + Flexible( + flex: 2, + child: AspectRatio( + aspectRatio: 1, + child: FittedBox(child: image, fit: BoxFit.fill))), + Flexible( + flex: 3, + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: EdgeInsets.only(top: 33), + child: Observer(builder: (_) { + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + walletSeedViewModel.name, style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.normal, + fontSize: 20, + fontWeight: FontWeight.w600, color: Theme.of(context) .primaryTextTheme! - .caption! + .headline6! .color!), ), - ) - ], - ); - }), - ), - Column( - children: [ - isNewWalletCreated - ? Padding( - padding: EdgeInsets.only( - bottom: 52, left: 43, right: 43), + Padding( + padding: + EdgeInsets.only(top: 20, left: 16, right: 16), child: Text( - S.of(context).seed_reminder, + walletSeedViewModel.seed, textAlign: TextAlign.center, style: TextStyle( - fontSize: 12, + fontSize: 14, fontWeight: FontWeight.normal, color: Theme.of(context) .primaryTextTheme! - .overline! + .caption! .color!), ), ) - : Offstage(), - Row( - mainAxisSize: MainAxisSize.max, - children: [ - Flexible( - child: Container( - padding: EdgeInsets.only(right: 8.0), - child: PrimaryButton( - onPressed: () { - ShareUtil.share( - text: walletSeedViewModel.seed, - context: context, - ); - }, - text: S.of(context).save, - color: Colors.green, - textColor: Colors.white), - )), - Flexible( - child: Container( - padding: EdgeInsets.only(left: 8.0), - child: Builder( - builder: (context) => PrimaryButton( - onPressed: () { - Clipboard.setData(ClipboardData( - text: walletSeedViewModel.seed)); - showBar(context, - S.of(context).copied_to_clipboard); - }, - text: S.of(context).copy, - color: Theme.of(context) - .accentTextTheme! - .bodyText2! - .color!, - textColor: Colors.white)), - )) - ], - ) - ], - ) - ], - )) - ], + ], + ); + }), + ), + Column( + children: [ + isNewWalletCreated + ? Padding( + padding: EdgeInsets.only( + bottom: 52, left: 43, right: 43), + child: Text( + S.of(context).seed_reminder, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.normal, + color: Theme.of(context) + .primaryTextTheme! + .overline! + .color!), + ), + ) + : Offstage(), + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Flexible( + child: Container( + padding: EdgeInsets.only(right: 8.0), + child: PrimaryButton( + onPressed: () { + ShareUtil.share( + text: walletSeedViewModel.seed, + context: context, + ); + }, + text: S.of(context).save, + color: Colors.green, + textColor: Colors.white), + )), + Flexible( + child: Container( + padding: EdgeInsets.only(left: 8.0), + child: Builder( + builder: (context) => PrimaryButton( + onPressed: () { + Clipboard.setData(ClipboardData( + text: walletSeedViewModel.seed)); + showBar(context, + S.of(context).copied_to_clipboard); + }, + text: S.of(context).copy, + color: Theme.of(context) + .accentTextTheme! + .bodyText2! + .color!, + textColor: Colors.white)), + )) + ], + ) + ], + ) + ], + )) + ], + ), ))); } } diff --git a/lib/src/screens/send/send_page.dart b/lib/src/screens/send/send_page.dart index 881536944..c30c46565 100644 --- a/lib/src/screens/send/send_page.dart +++ b/lib/src/screens/send/send_page.dart @@ -1,10 +1,12 @@ import 'package:cake_wallet/entities/fiat_currency.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator_icon.dart'; import 'package:cake_wallet/src/screens/send/widgets/send_card.dart'; +import 'package:cake_wallet/src/widgets/add_template_button.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/src/widgets/picker.dart'; import 'package:cake_wallet/src/widgets/template_tile.dart'; import 'package:cake_wallet/utils/payment_request.dart'; +import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cake_wallet/view_model/send/output.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -19,7 +21,6 @@ import 'package:cake_wallet/src/widgets/trail_button.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/view_model/send/send_view_model_state.dart'; import 'package:cake_wallet/generated/i18n.dart'; -import 'package:dotted_border/dotted_border.dart'; import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; import 'package:cake_wallet/src/screens/send/widgets/confirm_sending_alert.dart'; import 'package:smooth_page_indicator/smooth_page_indicator.dart'; @@ -50,9 +51,21 @@ class SendPage extends BasePage { @override bool get extendBodyBehindAppBar => true; + @override + bool get canUseCloseIcon => true; + @override AppBarStyle get appBarStyle => AppBarStyle.transparent; + double _sendCardHeight(BuildContext context) { + final double initialHeight = sendViewModel.isElectrumWallet ? 490 : 465; + + if (!ResponsiveLayoutUtil.instance.isMobile(context)) { + return initialHeight - 66; + } + return initialHeight; + } + @override void onClose(BuildContext context) { sendViewModel.onClose(); @@ -104,178 +117,137 @@ class SendPage extends BasePage { key: _formKey, child: ScrollableWithBottomSection( contentPadding: EdgeInsets.only(bottom: 24), - content: Column( - children: [ - Container( - height: sendViewModel.isElectrumWallet ? 490 : 465, - child: Observer( - builder: (_) { - return PageView.builder( - scrollDirection: Axis.horizontal, - controller: controller, - itemCount: sendViewModel.outputs.length, - itemBuilder: (context, index) { - final output = sendViewModel.outputs[index]; - - return SendCard( - key: output.key, - output: output, - sendViewModel: sendViewModel, - initialPaymentRequest: initialPaymentRequest, - ); - }); - }, - )), - Padding( - padding: - EdgeInsets.only(top: 10, left: 24, right: 24, bottom: 10), - child: Container( - height: 10, - child: Observer( - builder: (_) { - final count = sendViewModel.outputs.length; - - return count > 1 - ? SmoothPageIndicator( - controller: controller, - count: count, - effect: ScrollingDotsEffect( - spacing: 6.0, - radius: 6.0, - dotWidth: 6.0, - dotHeight: 6.0, - dotColor: Theme.of(context) - .primaryTextTheme! - .headline3! - .backgroundColor!, - activeDotColor: Theme.of(context) - .primaryTextTheme! - .headline2! - .backgroundColor!), - ) - : Offstage(); - }, - ), - ), - ), - if (sendViewModel.hasMultiRecipient) - Container( - height: 40, - width: double.infinity, - padding: EdgeInsets.only(left: 24), - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Observer( - builder: (_) { - final templates = sendViewModel.templates; - final itemCount = templates.length; - - return Row( - children: [ - GestureDetector( - onTap: () => Navigator.of(context) - .pushNamed(Routes.sendTemplate), - child: Container( - padding: EdgeInsets.only(left: 1, right: 10), - child: DottedBorder( - borderType: BorderType.RRect, - dashPattern: [6, 4], - color: Theme.of(context) - .primaryTextTheme! - .headline2! - .decorationColor!, - strokeWidth: 2, - radius: Radius.circular(20), - child: Container( - height: 34, - padding: EdgeInsets.only(left: 10, right: 10), - alignment: Alignment.center, - decoration: BoxDecoration( - borderRadius: - BorderRadius.all(Radius.circular(20)), - color: Colors.transparent, - ), - child: templates.length >= 1 - ? Icon( - Icons.add, - color: Theme.of(context) - .primaryTextTheme! - .headline2! - .color!, - ) - : Text( - S.of(context).new_template, - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w600, - color: Theme.of(context) - .primaryTextTheme! - .headline2! - .color!, - ), - ), - ), - ), - ), - ), - ListView.builder( + content: FocusTraversalGroup( + policy: OrderedTraversalPolicy(), + child: Column( + children: [ + Container( + height: _sendCardHeight(context), + child: Observer( + builder: (_) { + return PageView.builder( scrollDirection: Axis.horizontal, - shrinkWrap: true, - physics: NeverScrollableScrollPhysics(), - itemCount: itemCount, + controller: controller, + itemCount: sendViewModel.outputs.length, itemBuilder: (context, index) { - final template = templates[index]; - return TemplateTile( - key: UniqueKey(), - to: template.name, - amount: template.isCurrencySelected ? template.amount : template.amountFiat, - from: template.isCurrencySelected ? template.cryptoCurrency : template.fiatCurrency, - onTap: () async { - final fiatFromTemplate = FiatCurrency.all.singleWhere((element) => element.title == template.fiatCurrency); - final output = _defineCurrentOutput(); - output.address = template.address; - if(template.isCurrencySelected){ - output.setCryptoAmount(template.amount); - }else{ - sendViewModel.setFiatCurrency(fiatFromTemplate); - output.setFiatAmount(template.amountFiat); - } - output.resetParsedAddress(); - await output.fetchParsedAddress(context); - }, - onRemove: () { - showPopUp( - context: context, - builder: (dialogContext) { - return AlertWithTwoActions( - alertTitle: S.of(context).template, - alertContent: S - .of(context) - .confirm_delete_template, - rightButtonText: S.of(context).delete, - leftButtonText: S.of(context).cancel, - actionRightButton: () { - Navigator.of(dialogContext).pop(); - sendViewModel.sendTemplateViewModel - .removeTemplate( - template: template); - }, - actionLeftButton: () => - Navigator.of(dialogContext) - .pop()); - }, - ); - }, + final output = sendViewModel.outputs[index]; + + return SendCard( + key: output.key, + output: output, + sendViewModel: sendViewModel, + initialPaymentRequest: initialPaymentRequest, ); - }, - ), - ], - ); - }, + }); + }, + )), + Padding( + padding: + EdgeInsets.only(top: 10, left: 24, right: 24, bottom: 10), + child: Container( + height: 10, + child: Observer( + builder: (_) { + final count = sendViewModel.outputs.length; + + return count > 1 + ? SmoothPageIndicator( + controller: controller, + count: count, + effect: ScrollingDotsEffect( + spacing: 6.0, + radius: 6.0, + dotWidth: 6.0, + dotHeight: 6.0, + dotColor: Theme.of(context) + .primaryTextTheme.headline3! + .backgroundColor!, + activeDotColor: Theme.of(context) + .primaryTextTheme.headline2! + .backgroundColor!), + ) + : Offstage(); + }, + ), ), ), - ) - ], + if (sendViewModel.hasMultiRecipient) + Container( + height: 40, + width: double.infinity, + padding: EdgeInsets.only(left: 24), + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Observer( + builder: (_) { + final templates = sendViewModel.templates; + final itemCount = templates.length; + + return Row( + children: [ + AddTemplateButton( + onTap: () => Navigator.of(context).pushNamed(Routes.sendTemplate), + currentTemplatesLength: templates.length, + ), + ListView.builder( + scrollDirection: Axis.horizontal, + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: itemCount, + itemBuilder: (context, index) { + final template = templates[index]; + return TemplateTile( + key: UniqueKey(), + to: template.name, + amount: template.isCurrencySelected ? template.amount : template.amountFiat, + from: template.isCurrencySelected ? template.cryptoCurrency : template.fiatCurrency, + onTap: () async { + final fiatFromTemplate = FiatCurrency.all.singleWhere((element) => element.title == template.fiatCurrency); + final output = _defineCurrentOutput(); + output.address = template.address; + if(template.isCurrencySelected){ + output.setCryptoAmount(template.amount); + }else{ + sendViewModel.setFiatCurrency(fiatFromTemplate); + output.setFiatAmount(template.amountFiat); + } + output.resetParsedAddress(); + await output.fetchParsedAddress(context); + }, + onRemove: () { + showPopUp( + context: context, + builder: (dialogContext) { + return AlertWithTwoActions( + alertTitle: S.of(context).template, + alertContent: S + .of(context) + .confirm_delete_template, + rightButtonText: S.of(context).delete, + leftButtonText: S.of(context).cancel, + actionRightButton: () { + Navigator.of(dialogContext).pop(); + sendViewModel.sendTemplateViewModel + .removeTemplate( + template: template); + }, + actionLeftButton: () => + Navigator.of(dialogContext) + .pop()); + }, + ); + }, + ); + }, + ), + ], + ); + }, + ), + ), + ) + ], + ), ), bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24), @@ -290,8 +262,7 @@ class SendPage extends BasePage { text: 'Change your asset (${sendViewModel.selectedCryptoCurrency})', color: Colors.transparent, textColor: Theme.of(context) - .accentTextTheme! - .headline3! + .accentTextTheme.headline3! .decorationColor!, ) ) @@ -309,13 +280,11 @@ class SendPage extends BasePage { text: S.of(context).add_receiver, color: Colors.transparent, textColor: Theme.of(context) - .accentTextTheme! - .headline3! + .accentTextTheme.headline3! .decorationColor!, isDottedBorder: true, borderColor: Theme.of(context) - .primaryTextTheme! - .headline3! + .primaryTextTheme.headline3! .decorationColor!, )), Observer( @@ -335,7 +304,7 @@ class SendPage extends BasePage { item.address.isEmpty || item.cryptoAmount.isEmpty) .toList(); - if (notValidItems?.isNotEmpty ?? false) { + if (notValidItems.isNotEmpty ?? false) { showErrorValidationAlert(context); return; } @@ -344,7 +313,7 @@ class SendPage extends BasePage { }, text: S.of(context).send, - color: Theme.of(context).accentTextTheme!.bodyText1!.color!, + color: Theme.of(context).accentTextTheme.bodyText1!.color!, textColor: Colors.white, isLoading: sendViewModel.state is IsExecutingState || sendViewModel.state is TransactionCommitting, diff --git a/lib/src/screens/send/widgets/send_card.dart b/lib/src/screens/send/widgets/send_card.dart index 082067d95..cdae9a8df 100644 --- a/lib/src/screens/send/widgets/send_card.dart +++ b/lib/src/screens/send/widgets/send_card.dart @@ -1,6 +1,7 @@ import 'package:cake_wallet/entities/priority_for_wallet_type.dart'; import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; import 'package:cake_wallet/utils/payment_request.dart'; +import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cw_core/transaction_priority.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/widgets/keyboard_done_button.dart'; @@ -119,7 +120,7 @@ class SendCardState extends State color: Colors.transparent, )), Container( - decoration: BoxDecoration( + decoration: ResponsiveLayoutUtil.instance.isMobile(context) ? BoxDecoration( borderRadius: BorderRadius.only( bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24)), @@ -130,9 +131,14 @@ class SendCardState extends State .subtitle1! .decorationColor!, ], begin: Alignment.topLeft, end: Alignment.bottomRight), - ), + ) : null, child: Padding( - padding: EdgeInsets.fromLTRB(24, 100, 24, 32), + padding: EdgeInsets.fromLTRB( + 24, + ResponsiveLayoutUtil.instance.isMobile(context) ? 100 : 55, + 24, + ResponsiveLayoutUtil.instance.isMobile(context) ? 32 : 0, + ), child: SingleChildScrollView( child: Observer(builder: (_) => Column( mainAxisSize: MainAxisSize.min, diff --git a/lib/src/screens/settings/desktop_settings/desktop_settings_page.dart b/lib/src/screens/settings/desktop_settings/desktop_settings_page.dart new file mode 100644 index 000000000..eee54eb45 --- /dev/null +++ b/lib/src/screens/settings/desktop_settings/desktop_settings_page.dart @@ -0,0 +1,105 @@ +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/routes.dart'; +import 'package:cake_wallet/src/widgets/setting_action_button.dart'; +import 'package:cake_wallet/src/widgets/setting_actions.dart'; +import 'package:cake_wallet/typography.dart'; +import 'package:flutter/material.dart'; +import 'package:cake_wallet/router.dart' as Router; + +final _settingsNavigatorKey = GlobalKey(); + +class DesktopSettingsPage extends StatefulWidget { + const DesktopSettingsPage({super.key}); + + @override + State createState() => _DesktopSettingsPageState(); +} + +class _DesktopSettingsPageState extends State { + final int itemCount = SettingActions.desktopSettings.length; + + int? currentPage; + + void _onItemChange(int index) { + setState(() { + currentPage = index; + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Container( + height: MediaQuery.of(context).size.height, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.all(24), + child: Text( + S.current.settings, + style: textXLarge(), + ), + ), + Expanded( + child: Row( + children: [ + Expanded( + flex: 1, + child: ListView.separated( + padding: EdgeInsets.only(top: 0), + itemBuilder: (_, index) { + final item = SettingActions.desktopSettings[index]; + final isLastTile = index == itemCount - 1; + return SettingActionButton( + isLastTile: isLastTile, + selectionActive: currentPage != null, + isSelected: currentPage == index, + isArrowVisible: true, + onTap: () { + if (currentPage != index) { + final settingContext = + _settingsNavigatorKey.currentState?.context ?? context; + item.onTap.call(settingContext); + _onItemChange(index); + } + }, + image: item.image, + title: item.name, + ); + }, + separatorBuilder: (_, index) => Container( + height: 1, + color: Theme.of(context).primaryTextTheme.caption!.decorationColor!, + ), + itemCount: itemCount, + ), + ), + Flexible( + flex: 2, + child: ConstrainedBox( + constraints: BoxConstraints(maxWidth: 500), + child: Navigator( + key: _settingsNavigatorKey, + initialRoute: Routes.empty_no_route, + onGenerateRoute: (settings) => Router.createRoute(settings), + onGenerateInitialRoutes: + (NavigatorState navigator, String initialRouteName) { + return [ + navigator + .widget.onGenerateRoute!(RouteSettings(name: initialRouteName))! + ]; + }, + ), + ), + ) + ], + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/src/screens/settings/display_settings_page.dart b/lib/src/screens/settings/display_settings_page.dart index 39123a7eb..4f932b189 100644 --- a/lib/src/screens/settings/display_settings_page.dart +++ b/lib/src/screens/settings/display_settings_page.dart @@ -7,9 +7,9 @@ import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.da import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart'; import 'package:cake_wallet/themes/theme_base.dart'; import 'package:cake_wallet/themes/theme_list.dart'; +import 'package:cake_wallet/utils/device_info.dart'; import 'package:cake_wallet/view_model/settings/choices_list_item.dart'; import 'package:cake_wallet/view_model/settings/display_settings_view_model.dart'; -import 'package:cake_wallet/wallet_type_utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -65,14 +65,15 @@ class DisplaySettingsPage extends BasePage { return LanguageService.list[code]?.toLowerCase().contains(searchText) ?? false; }, ), - SettingsChoicesCell( - ChoicesListItem( - title: S.current.color_theme, - items: ThemeList.all, - selectedItem: _displaySettingsViewModel.theme, - onItemSelected: (ThemeBase theme) => _displaySettingsViewModel.setTheme(theme), + if (DeviceInfo.instance.isMobile) + SettingsChoicesCell( + ChoicesListItem( + title: S.current.color_theme, + items: ThemeList.all, + selectedItem: _displaySettingsViewModel.theme, + onItemSelected: (ThemeBase theme) => _displaySettingsViewModel.setTheme(theme), + ), ), - ), ], ), ); diff --git a/lib/src/screens/support/support_page.dart b/lib/src/screens/support/support_page.dart index 5edb723bc..801f81775 100644 --- a/lib/src/screens/support/support_page.dart +++ b/lib/src/screens/support/support_page.dart @@ -1,6 +1,7 @@ import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart'; import 'package:cake_wallet/src/screens/settings/widgets/settings_link_provider_cell.dart'; import 'package:cake_wallet/src/widgets/standard_list.dart'; +import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cake_wallet/view_model/settings/link_list_item.dart'; import 'package:cake_wallet/view_model/settings/regular_list_item.dart'; import 'package:cake_wallet/view_model/support_view_model.dart'; @@ -18,32 +19,34 @@ class SupportPage extends BasePage { @override Widget body(BuildContext context) { - final iconColor = - Theme.of(context).accentTextTheme!.headline1!.backgroundColor!; + final iconColor = Theme.of(context).accentTextTheme!.headline1!.backgroundColor!; // FIX-ME: Added `context` it was not used here before, maby bug ? - return SectionStandardList( - context: context, - sectionCount: 1, - itemCounter: (int _) => supportViewModel.items.length, - itemBuilder: (_, __, index) { - final item = supportViewModel.items[index]; + return Center( + child: ConstrainedBox( + constraints: BoxConstraints(maxWidth: 500), + child: SectionStandardList( + context: context, + sectionCount: 1, + itemCounter: (int _) => supportViewModel.items.length, + itemBuilder: (_, __, index) { + final item = supportViewModel.items[index]; - if (item is RegularListItem) { - return SettingsCellWithArrow( - title: item.title, handler: item.handler); - } + if (item is RegularListItem) { + return SettingsCellWithArrow(title: item.title, handler: item.handler); + } - if (item is LinkListItem) { - return SettingsLinkProviderCell( - title: item.title, - icon: item.icon, - iconColor: item.hasIconColor ? iconColor : null, - link: item.link, - linkTitle: item.linkTitle); - } + if (item is LinkListItem) { + return SettingsLinkProviderCell( + title: item.title, + icon: item.icon, + iconColor: item.hasIconColor ? iconColor : null, + link: item.link, + linkTitle: item.linkTitle); + } - return Container(); - }); + return Container(); + }), + ), + ); } - -} \ No newline at end of file +} diff --git a/lib/src/screens/wallet_list/wallet_list_page.dart b/lib/src/screens/wallet_list/wallet_list_page.dart index d76631b3f..a1e8c51b7 100644 --- a/lib/src/screens/wallet_list/wallet_list_page.dart +++ b/lib/src/screens/wallet_list/wallet_list_page.dart @@ -1,5 +1,6 @@ import 'package:cake_wallet/src/screens/auth/auth_page.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; +import 'package:cake_wallet/utils/device_info.dart'; import 'package:cake_wallet/utils/show_bar.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart'; @@ -23,8 +24,7 @@ class WalletListPage extends BasePage { final WalletListViewModel walletListViewModel; @override - Widget body(BuildContext context) => - WalletListBody(walletListViewModel: walletListViewModel); + Widget body(BuildContext context) => WalletListBody(walletListViewModel: walletListViewModel); } class WalletListBody extends StatefulWidget { @@ -37,28 +37,21 @@ class WalletListBody extends StatefulWidget { } class WalletListBodyState extends State { - final moneroIcon = - Image.asset('assets/images/monero_logo.png', height: 24, width: 24); - final bitcoinIcon = - Image.asset('assets/images/bitcoin.png', height: 24, width: 24); - final litecoinIcon = - Image.asset('assets/images/litecoin_icon.png', height: 24, width: 24); - final nonWalletTypeIcon = - Image.asset('assets/images/close.png', height: 24, width: 24); - final havenIcon = - Image.asset('assets/images/haven_logo.png', height: 24, width: 24); + final moneroIcon = Image.asset('assets/images/monero_logo.png', height: 24, width: 24); + final bitcoinIcon = Image.asset('assets/images/bitcoin.png', height: 24, width: 24); + final litecoinIcon = Image.asset('assets/images/litecoin_icon.png', height: 24, width: 24); + final nonWalletTypeIcon = Image.asset('assets/images/close.png', height: 24, width: 24); + final havenIcon = Image.asset('assets/images/haven_logo.png', height: 24, width: 24); final scrollController = ScrollController(); final double tileHeight = 60; Flushbar? _progressBar; @override Widget build(BuildContext context) { - final newWalletImage = Image.asset('assets/images/new_wallet.png', - height: 12, width: 12, color: Colors.white); + final newWalletImage = + Image.asset('assets/images/new_wallet.png', height: 12, width: 12, color: Colors.white); final restoreWalletImage = Image.asset('assets/images/restore_wallet.png', - height: 12, - width: 12, - color: Theme.of(context).primaryTextTheme!.headline6!.color!); + height: 12, width: 12, color: Theme.of(context).primaryTextTheme.headline6!.color!); return Container( padding: EdgeInsets.only(top: 16), @@ -69,16 +62,13 @@ class WalletListBodyState extends State { builder: (_) => ListView.separated( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), - separatorBuilder: (_, index) => Divider( - color: Theme.of(context).backgroundColor, height: 32), + separatorBuilder: (_, index) => + Divider(color: Theme.of(context).backgroundColor, height: 32), itemCount: widget.walletListViewModel.wallets.length, itemBuilder: (__, index) { final wallet = widget.walletListViewModel.wallets[index]; final currentColor = wallet.isCurrent - ? Theme.of(context) - .accentTextTheme! - .subtitle2! - .decorationColor! + ? Theme.of(context).accentTextTheme.subtitle2!.decorationColor! : Theme.of(context).backgroundColor; final row = GestureDetector( onTap: () async { @@ -90,19 +80,15 @@ class WalletListBodyState extends State { context: context, builder: (dialogContext) { return AlertWithTwoActions( - alertTitle: S - .of(context) - .change_wallet_alert_title, - alertContent: S - .of(context) - .change_wallet_alert_content( - wallet.name), + alertTitle: S.of(context).change_wallet_alert_title, + alertContent: + S.of(context).change_wallet_alert_content(wallet.name), leftButtonText: S.of(context).cancel, rightButtonText: S.of(context).change, actionLeftButton: () => - Navigator.of(context).pop(false), + Navigator.of(dialogContext).pop(false), actionRightButton: () => - Navigator.of(context).pop(true)); + Navigator.of(dialogContext).pop(true)); }) ?? false; @@ -131,12 +117,11 @@ class WalletListBodyState extends State { color: Theme.of(context).backgroundColor, alignment: Alignment.centerLeft, child: Row( - crossAxisAlignment: - CrossAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, children: [ wallet.isEnabled - ? _imageFor(type: wallet.type) - : nonWalletTypeIcon, + ? _imageFor(type: wallet.type) + : nonWalletTypeIcon, SizedBox(width: 10), Text( wallet.name, @@ -144,8 +129,7 @@ class WalletListBodyState extends State { fontSize: 22, fontWeight: FontWeight.w500, color: Theme.of(context) - .primaryTextTheme! - .headline6! + .primaryTextTheme.headline6! .color!), ) ], @@ -163,43 +147,40 @@ class WalletListBodyState extends State { startActionPane: _actionPane(wallet), endActionPane: _actionPane(wallet), child: row, - ); + ); }), ), ), - bottomSectionPadding: - EdgeInsets.only(bottom: 24, right: 24, left: 24), + bottomSectionPadding: EdgeInsets.only(bottom: 24, right: 24, left: 24), bottomSection: Column(children: [ PrimaryImageButton( onPressed: () { - if (isSingleCoin) { - Navigator.of(context).pushNamed(Routes.newWallet, arguments: widget.walletListViewModel.currentWalletType); - } else { - Navigator.of(context).pushNamed(Routes.newWalletType); - } - }, + if (isSingleCoin) { + Navigator.of(context).pushNamed(Routes.newWallet, + arguments: widget.walletListViewModel.currentWalletType); + } else { + Navigator.of(context).pushNamed(Routes.newWalletType); + } + }, image: newWalletImage, text: S.of(context).wallet_list_create_new_wallet, - color: Theme.of(context).accentTextTheme!.bodyText1!.color!, + color: Theme.of(context).accentTextTheme.bodyText1!.color!, textColor: Colors.white, ), SizedBox(height: 10.0), PrimaryImageButton( onPressed: () { - if (isSingleCoin) { - Navigator - .of(context) - .pushNamed( - Routes.restoreWallet, - arguments: widget.walletListViewModel.currentWalletType); - } else { - Navigator.of(context).pushNamed(Routes.restoreWalletType); - } - }, + if (isSingleCoin) { + Navigator.of(context).pushNamed(Routes.restoreWallet, + arguments: widget.walletListViewModel.currentWalletType); + } else { + Navigator.of(context).pushNamed(Routes.restoreWalletType); + } + }, image: restoreWalletImage, text: S.of(context).wallet_list_restore_wallet, - color: Theme.of(context).accentTextTheme!.caption!.color!, - textColor: Theme.of(context).primaryTextTheme!.headline6!.color!) + color: Theme.of(context).accentTextTheme.caption!.color!, + textColor: Theme.of(context).primaryTextTheme.headline6!.color!) ])), ); } @@ -232,9 +213,13 @@ class WalletListBodyState extends State { await widget.walletListViewModel.loadWallet(wallet); auth.hideProgressText(); auth.close(); - WidgetsBinding.instance.addPostFrameCallback((_) { - Navigator.of(context).pop(); - }); + // only pop the wallets route in mobile as it will go back to dashboard page + // in desktop platforms the navigation tree is different + if (DeviceInfo.instance.isMobile) { + WidgetsBinding.instance.addPostFrameCallback((_) { + Navigator.of(context).pop(); + }); + } } catch (e) { auth.changeProcessText( S.of(context).wallet_list_failed_to_load(wallet.name, e.toString())); @@ -245,7 +230,11 @@ class WalletListBodyState extends State { changeProcessText(S.of(context).wallet_list_loading_wallet(wallet.name)); await widget.walletListViewModel.loadWallet(wallet); hideProgressText(); - Navigator.of(context).pop(); + // only pop the wallets route in mobile as it will go back to dashboard page + // in desktop platforms the navigation tree is different + if (DeviceInfo.instance.isMobile) { + Navigator.of(context).pop(); + } } catch (e) { changeProcessText(S.of(context).wallet_list_failed_to_load(wallet.name, e.toString())); } @@ -290,6 +279,7 @@ class WalletListBodyState extends State { ? auth.changeProcessText(S.of(context).wallet_list_removing_wallet(wallet.name)) : changeProcessText(S.of(context).wallet_list_removing_wallet(wallet.name)); await widget.walletListViewModel.remove(wallet); + hideProgressText(); } catch (e) { auth != null ? auth.changeProcessText( @@ -309,8 +299,10 @@ class WalletListBodyState extends State { } void hideProgressText() { - _progressBar?.dismiss(); - _progressBar = null; + Future.delayed(Duration(milliseconds: 50), () { + _progressBar?.dismiss(); + _progressBar = null; + }); } ActionPane _actionPane(WalletListItem wallet) => ActionPane( diff --git a/lib/src/screens/welcome/welcome_page.dart b/lib/src/screens/welcome/welcome_page.dart index a50d8ba8c..86e1cbcf1 100644 --- a/lib/src/screens/welcome/welcome_page.dart +++ b/lib/src/screens/welcome/welcome_page.dart @@ -1,4 +1,5 @@ import 'package:cake_wallet/themes/theme_base.dart'; +import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/widgets/primary_button.dart'; @@ -19,7 +20,7 @@ class WelcomePage extends BasePage { if (isHaven) { return S.of(context).haven_app; } - + return S.of(context).cake_wallet; } @@ -31,172 +32,137 @@ class WelcomePage extends BasePage { if (isHaven) { return S.of(context).haven_app_wallet_text; } - + return S.of(context).first_wallet_text; } @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: Theme - .of(context) - .backgroundColor, + backgroundColor: Theme.of(context).backgroundColor, resizeToAvoidBottomInset: false, body: body(context)); } @override Widget body(BuildContext context) { - final welcomeImage = currentTheme.type == ThemeType.dark - ? welcomeImageDark : welcomeImageLight; + final welcomeImage = currentTheme.type == ThemeType.dark ? welcomeImageDark : welcomeImageLight; final newWalletImage = Image.asset('assets/images/new_wallet.png', height: 12, width: 12, - color: Theme - .of(context) - .accentTextTheme! - .headline5! - .decorationColor!); + color: Theme.of(context).accentTextTheme!.headline5!.decorationColor!); final restoreWalletImage = Image.asset('assets/images/restore_wallet.png', - height: 12, - width: 12, - color: Theme.of(context) - .primaryTextTheme! - .headline6! - .color!); + height: 12, width: 12, color: Theme.of(context).primaryTextTheme!.headline6!.color!); - return WillPopScope(onWillPop: () async => false, child: Container( - padding: EdgeInsets.only(top: 64, bottom: 24, left: 24, right: 24), - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Flexible( - flex: 2, - child: AspectRatio( - aspectRatio: aspectRatioImage, - child: FittedBox(child: welcomeImage, fit: BoxFit.fill) - ) - ), - Flexible( - flex: 3, + return WillPopScope( + onWillPop: () async => false, + child: Container( + padding: EdgeInsets.only(top: 64, bottom: 24, left: 24, right: 24), + child: Center( + child: ConstrainedBox( + constraints: + BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint), child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Column( - children: [ - Padding( - padding: EdgeInsets.only(top: 24), - child: Text( - S - .of(context) - .welcome, - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.w500, - color: Theme - .of(context) - .accentTextTheme! - .headline2! - .color!, + Flexible( + flex: 2, + child: AspectRatio( + aspectRatio: aspectRatioImage, + child: FittedBox(child: welcomeImage, fit: BoxFit.fill))), + Flexible( + flex: 3, + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + children: [ + Padding( + padding: EdgeInsets.only(top: 24), + child: Text( + S.of(context).welcome, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500, + color: Theme.of(context).accentTextTheme!.headline2!.color!, + ), + textAlign: TextAlign.center, + ), + ), + Padding( + padding: EdgeInsets.only(top: 5), + child: Text( + appTitle(context), + style: TextStyle( + fontSize: 36, + fontWeight: FontWeight.bold, + color: Theme.of(context).primaryTextTheme!.headline6!.color!, + ), + textAlign: TextAlign.center, + ), + ), + Padding( + padding: EdgeInsets.only(top: 5), + child: Text( + appDescription(context), + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: Theme.of(context).accentTextTheme!.headline2!.color!, + ), + textAlign: TextAlign.center, + ), + ), + ], ), - textAlign: TextAlign.center, - ), - ), - Padding( - padding: EdgeInsets.only(top: 5), - child: Text( - appTitle(context), - style: TextStyle( - fontSize: 36, - fontWeight: FontWeight.bold, - color: Theme.of(context) - .primaryTextTheme! - .headline6! - .color!, - ), - textAlign: TextAlign.center, - ), - ), - Padding( - padding: EdgeInsets.only(top: 5), - child: Text( - appDescription(context), - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: Theme - .of(context) - .accentTextTheme! - .headline2! - .color!, - ), - textAlign: TextAlign.center, - ), - ), - ], - ), - Column( - children: [ - Text( - S - .of(context) - .please_make_selection, - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.normal, - color: Theme.of(context) - .accentTextTheme! - .headline2! - .color!, - ), - textAlign: TextAlign.center, - ), - Padding( - padding: EdgeInsets.only(top: 24), - child: PrimaryImageButton( - onPressed: () => - Navigator.pushNamed(context, - Routes.newWalletFromWelcome), - image: newWalletImage, - text: S.of(context).create_new, - color: Theme.of(context) - .accentTextTheme! - .subtitle2! - .decorationColor!, - textColor: Theme - .of(context) - .accentTextTheme! - .headline5! - .decorationColor!, - ), - ), - Padding( - padding: EdgeInsets.only(top: 10), - child: PrimaryImageButton( - onPressed: () { - Navigator.pushNamed(context, Routes.restoreOptions); - }, - image: restoreWalletImage, - text: S - .of(context) - .restore_wallet, - color: Theme.of(context) - .accentTextTheme! - .caption! - .color!, - textColor: Theme.of(context) - .primaryTextTheme! - .headline6! - .color!), - ) - ], - ) + Column( + children: [ + Text( + S.of(context).please_make_selection, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.normal, + color: Theme.of(context).accentTextTheme!.headline2!.color!, + ), + textAlign: TextAlign.center, + ), + Padding( + padding: EdgeInsets.only(top: 24), + child: PrimaryImageButton( + onPressed: () => + Navigator.pushNamed(context, Routes.newWalletFromWelcome), + image: newWalletImage, + text: S.of(context).create_new, + color: Theme.of(context) + .accentTextTheme! + .subtitle2! + .decorationColor!, + textColor: Theme.of(context) + .accentTextTheme! + .headline5! + .decorationColor!, + ), + ), + Padding( + padding: EdgeInsets.only(top: 10), + child: PrimaryImageButton( + onPressed: () { + Navigator.pushNamed(context, Routes.restoreOptions); + }, + image: restoreWalletImage, + text: S.of(context).restore_wallet, + color: Theme.of(context).accentTextTheme!.caption!.color!, + textColor: + Theme.of(context).primaryTextTheme!.headline6!.color!), + ) + ], + ) + ], + )) ], - ) - ) - ], - ) - )); + ), + ), + ))); } } diff --git a/lib/src/widgets/add_template_button.dart b/lib/src/widgets/add_template_button.dart new file mode 100644 index 000000000..249c493a6 --- /dev/null +++ b/lib/src/widgets/add_template_button.dart @@ -0,0 +1,52 @@ +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/utils/responsive_layout_util.dart'; +import 'package:dotted_border/dotted_border.dart'; +import 'package:flutter/material.dart'; + +class AddTemplateButton extends StatelessWidget { + final Function() onTap; + final int currentTemplatesLength; + + const AddTemplateButton({Key? key, required this.onTap, required this.currentTemplatesLength}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: onTap, + child: Container( + padding: EdgeInsets.only(left: 1, right: 10), + child: DottedBorder( + borderType: BorderType.RRect, + dashPattern: [6, 4], + color: Theme.of(context).primaryTextTheme.headline3!.decorationColor!, + strokeWidth: 2, + radius: Radius.circular(20), + child: Container( + height: 34, + padding: EdgeInsets.symmetric( + horizontal: ResponsiveLayoutUtil.instance.isMobile(context) ? 10 : 30), + alignment: Alignment.center, + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(20)), + color: Colors.transparent, + ), + child: currentTemplatesLength >= 1 + ? Icon( + Icons.add, + color: Theme.of(context).primaryTextTheme.headline2!.color!, + ) + : Text( + S.of(context).new_template, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: Theme.of(context).primaryTextTheme.headline2!.color!, + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/src/widgets/address_text_field.dart b/lib/src/widgets/address_text_field.dart index 4acc9a145..059dc51aa 100644 --- a/lib/src/widgets/address_text_field.dart +++ b/lib/src/widgets/address_text_field.dart @@ -1,3 +1,6 @@ +import 'dart:io'; + +import 'package:cake_wallet/utils/device_info.dart'; import 'package:flutter/services.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/routes.dart'; @@ -65,7 +68,7 @@ class AddressTextField extends StatelessWidget { style: textStyle ?? TextStyle( fontSize: 16, - color: Theme.of(context).primaryTextTheme!.headline6!.color!), + color: Theme.of(context).primaryTextTheme.headline6!.color!), decoration: InputDecoration( suffixIcon: SizedBox( width: prefixIconWidth * options.length + @@ -102,7 +105,8 @@ class AddressTextField extends StatelessWidget { width: prefixIconWidth * options.length + (spaceBetweenPrefixIcons * options.length), child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisAlignment: DeviceInfo.instance.isMobile + ? MainAxisAlignment.spaceBetween : MainAxisAlignment.end, children: [ SizedBox(width: 5), if (this.options.contains(AddressTextFieldOption.paste)) ...[ @@ -117,8 +121,7 @@ class AddressTextField extends StatelessWidget { decoration: BoxDecoration( color: buttonColor ?? Theme.of(context) - .accentTextTheme! - .headline6! + .accentTextTheme.headline6! .color!, borderRadius: BorderRadius.all(Radius.circular(6))), @@ -126,13 +129,13 @@ class AddressTextField extends StatelessWidget { 'assets/images/paste_ios.png', color: iconColor ?? Theme.of(context) - .primaryTextTheme! - .headline4! + .primaryTextTheme.headline4! .decorationColor!, )), )), ], - if (this.options.contains(AddressTextFieldOption.qrCode)) ...[ + if (this.options.contains(AddressTextFieldOption.qrCode) && DeviceInfo.instance.isMobile) + ...[ Container( width: prefixIconWidth, height: prefixIconHeight, @@ -144,8 +147,7 @@ class AddressTextField extends StatelessWidget { decoration: BoxDecoration( color: buttonColor ?? Theme.of(context) - .accentTextTheme! - .headline6! + .accentTextTheme.headline6! .color!, borderRadius: BorderRadius.all(Radius.circular(6))), @@ -153,12 +155,11 @@ class AddressTextField extends StatelessWidget { 'assets/images/qr_code_icon.png', color: iconColor ?? Theme.of(context) - .primaryTextTheme! - .headline4! + .primaryTextTheme.headline4! .decorationColor!, )), )) - ], + ] else SizedBox(width: 5), if (this .options .contains(AddressTextFieldOption.addressBook)) ...[ @@ -173,8 +174,7 @@ class AddressTextField extends StatelessWidget { decoration: BoxDecoration( color: buttonColor ?? Theme.of(context) - .accentTextTheme! - .headline6! + .accentTextTheme.headline6! .color!, borderRadius: BorderRadius.all(Radius.circular(6))), @@ -182,8 +182,7 @@ class AddressTextField extends StatelessWidget { 'assets/images/open_book.png', color: iconColor ?? Theme.of(context) - .primaryTextTheme! - .headline4! + .primaryTextTheme.headline4! .decorationColor!, )), )) @@ -211,7 +210,7 @@ class AddressTextField extends StatelessWidget { } Future _presetAddressBookPicker(BuildContext context) async { - final contact = await Navigator.of(context, rootNavigator: true) + final contact = await Navigator.of(context) .pushNamed(Routes.pickerAddressBook,arguments: selectedCurrency); if (contact is ContactBase && contact.address != null) { diff --git a/lib/src/widgets/alert_background.dart b/lib/src/widgets/alert_background.dart index 0b4dab470..1b72597af 100644 --- a/lib/src/widgets/alert_background.dart +++ b/lib/src/widgets/alert_background.dart @@ -1,5 +1,5 @@ import 'dart:ui'; -import 'package:flutter/cupertino.dart'; +import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/palette.dart'; @@ -21,10 +21,15 @@ class AlertBackground extends StatelessWidget { filter: ImageFilter.blur(sigmaX: 3.0, sigmaY: 3.0), child: Container( decoration: BoxDecoration(color: PaletteDark.darkNightBlue.withOpacity(0.75)), - child: child, + child: Center( + child: Container( + width: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint, + child: child, + ), + ), ), ), ), ); } -} \ No newline at end of file +} diff --git a/lib/src/widgets/alert_close_button.dart b/lib/src/widgets/alert_close_button.dart index 35a5cd45c..e8e20f125 100644 --- a/lib/src/widgets/alert_close_button.dart +++ b/lib/src/widgets/alert_close_button.dart @@ -13,9 +13,7 @@ class AlertCloseButton extends StatelessWidget { @override Widget build(BuildContext context) { - return Positioned( - bottom: 60, - child: GestureDetector( + return GestureDetector( onTap: () => Navigator.of(context).pop(), child: Container( height: 42, @@ -28,7 +26,6 @@ class AlertCloseButton extends StatelessWidget { child: image ?? closeButton, ), ), - ) ); } } \ No newline at end of file diff --git a/lib/src/widgets/base_text_form_field.dart b/lib/src/widgets/base_text_form_field.dart index 19d43e9c2..ff2102843 100644 --- a/lib/src/widgets/base_text_form_field.dart +++ b/lib/src/widgets/base_text_form_field.dart @@ -27,6 +27,7 @@ class BaseTextFormField extends StatelessWidget { this.maxLength, this.focusNode, this.initialValue, + this.onSubmit, this.borderWidth = 1.0}); final TextEditingController? controller; @@ -54,6 +55,7 @@ class BaseTextFormField extends StatelessWidget { final bool? enableInteractiveSelection; final String? initialValue; final double borderWidth; + final void Function(String)? onSubmit; @override Widget build(BuildContext context) { @@ -71,11 +73,12 @@ class BaseTextFormField extends StatelessWidget { inputFormatters: inputFormatters, enabled: enabled, maxLength: maxLength, + onFieldSubmitted: onSubmit, style: textStyle ?? TextStyle( fontSize: 16.0, color: - textColor ?? Theme.of(context).primaryTextTheme!.headline6!.color!), + textColor ?? Theme.of(context).primaryTextTheme.headline6!.color!), decoration: InputDecoration( prefix: prefix, prefixIcon: prefixIcon, @@ -89,17 +92,17 @@ class BaseTextFormField extends StatelessWidget { focusedBorder: UnderlineInputBorder( borderSide: BorderSide( color: borderColor ?? - Theme.of(context).primaryTextTheme!.headline6!.backgroundColor!, + Theme.of(context).primaryTextTheme.headline6!.backgroundColor!, width: borderWidth)), disabledBorder: UnderlineInputBorder( borderSide: BorderSide( color: borderColor ?? - Theme.of(context).primaryTextTheme!.headline6!.backgroundColor!, + Theme.of(context).primaryTextTheme.headline6!.backgroundColor!, width: borderWidth)), enabledBorder: UnderlineInputBorder( borderSide: BorderSide( color: borderColor ?? - Theme.of(context).primaryTextTheme!.headline6!.backgroundColor!, + Theme.of(context).primaryTextTheme.headline6!.backgroundColor!, width: borderWidth))), validator: validator, ); diff --git a/lib/src/widgets/check_box_picker.dart b/lib/src/widgets/check_box_picker.dart index 80461e26d..a59dda905 100644 --- a/lib/src/widgets/check_box_picker.dart +++ b/lib/src/widgets/check_box_picker.dart @@ -1,4 +1,5 @@ import 'package:cake_wallet/palette.dart'; +import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/src/widgets/alert_background.dart'; import 'package:cake_wallet/src/widgets/alert_close_button.dart'; @@ -32,63 +33,62 @@ class CheckBoxPickerState extends State { @override Widget build(BuildContext context) { return AlertBackground( - child: Stack( - alignment: Alignment.center, - children: [ - Column( - mainAxisSize: MainAxisSize.min, - children: [ - if (widget.title.isNotEmpty) - Container( - padding: EdgeInsets.symmetric(horizontal: 24), - child: Text( - widget.title, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 18, - fontFamily: 'Lato', - fontWeight: FontWeight.bold, - decoration: TextDecoration.none, - color: Colors.white, + child: Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (widget.title.isNotEmpty) + Container( + padding: EdgeInsets.symmetric(horizontal: 24), + child: Text( + widget.title, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 18, + fontFamily: 'Lato', + fontWeight: FontWeight.bold, + decoration: TextDecoration.none, + color: Colors.white, + ), ), ), - ), - Padding( - padding: EdgeInsets.only(left: 24, right: 24, top: 24), - child: ClipRRect( - borderRadius: BorderRadius.all(Radius.circular(30)), - child: Container( - color: Theme.of(context).accentTextTheme.headline6!.color!, - child: ConstrainedBox( - constraints: BoxConstraints( - maxHeight: MediaQuery.of(context).size.height * 0.65, - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Flexible( - child: Stack( - alignment: Alignment.center, - children: [ - (items.length) > 3 - ? Scrollbar( - controller: controller, - child: itemsList(), - ) - : itemsList(), - ], + Padding( + padding: EdgeInsets.only(left: 24, right: 24, top: 24), + child: ClipRRect( + borderRadius: BorderRadius.all(Radius.circular(30)), + child: Container( + color: Theme.of(context).accentTextTheme.headline6!.color!, + child: ConstrainedBox( + constraints: BoxConstraints( + maxHeight: MediaQuery.of(context).size.height * 0.65, + maxWidth: ResponsiveLayoutUtil.kPopupWidth, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Flexible( + child: Stack( + alignment: Alignment.center, + children: [ + items.length > 3 + ? Scrollbar( + controller: controller, + child: itemsList(), + ) + : itemsList(), + ], + ), ), - ), - ], + ], + ), ), ), ), ), - ) - ], - ), - AlertCloseButton(), - ], + SizedBox(height: ResponsiveLayoutUtil.kPopupSpaceHeight), + AlertCloseButton(), + ], + ), ), ); } diff --git a/lib/src/widgets/market_place_item.dart b/lib/src/widgets/market_place_item.dart index 8049a6346..438391c97 100644 --- a/lib/src/widgets/market_place_item.dart +++ b/lib/src/widgets/market_place_item.dart @@ -17,6 +17,9 @@ class MarketPlaceItem extends StatelessWidget { Widget build(BuildContext context) { return InkWell( onTap: onTap, + hoverColor: Colors.transparent, + splashColor: Colors.transparent, + highlightColor: Colors.transparent, child: Stack( children: [ Container( diff --git a/lib/src/widgets/nav_bar.dart b/lib/src/widgets/nav_bar.dart index f6d933c8b..aabe8d9c8 100644 --- a/lib/src/widgets/nav_bar.dart +++ b/lib/src/widgets/nav_bar.dart @@ -1,13 +1,7 @@ import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; class NavBar extends StatelessWidget implements ObstructingPreferredSizeWidget { - factory NavBar( - {Widget? leading, - Widget? middle, - Widget? trailing, - Color? backgroundColor}) { - + factory NavBar({Widget? leading, Widget? middle, Widget? trailing, Color? backgroundColor}) { return NavBar._internal( leading: leading, middle: middle, @@ -17,11 +11,7 @@ class NavBar extends StatelessWidget implements ObstructingPreferredSizeWidget { } factory NavBar.withShadow( - {Widget? leading, - Widget? middle, - Widget? trailing, - Color? backgroundColor}) { - + {Widget? leading, Widget? middle, Widget? trailing, Color? backgroundColor}) { return NavBar._internal( leading: leading, middle: middle, @@ -29,13 +19,15 @@ class NavBar extends StatelessWidget implements ObstructingPreferredSizeWidget { height: 80, backgroundColor: backgroundColor, decoration: BoxDecoration( - color: backgroundColor, - boxShadow: [ - BoxShadow( - color: Color.fromRGBO(132, 141, 198, 0.11), - blurRadius: 8, - offset: Offset(0, 2)) - ]), + color: backgroundColor, + boxShadow: [ + BoxShadow( + color: Color.fromRGBO(132, 141, 198, 0.11), + blurRadius: 8, + offset: Offset(0, 2), + ), + ], + ), ); } @@ -59,14 +51,17 @@ class NavBar extends StatelessWidget implements ObstructingPreferredSizeWidget { @override Widget build(BuildContext context) { + if (leading == null && middle == null && trailing == null) { + return const SizedBox(); + } + final pad = height - _originalHeight; final paddingTop = pad / 2; final _paddingBottom = (pad / 2); return Container( decoration: decoration ?? BoxDecoration(color: backgroundColor), - padding: - EdgeInsetsDirectional.only(bottom: _paddingBottom, top: paddingTop), + padding: EdgeInsetsDirectional.only(bottom: _paddingBottom, top: paddingTop), child: CupertinoNavigationBar( leading: leading, automaticallyImplyLeading: false, diff --git a/lib/src/widgets/picker.dart b/lib/src/widgets/picker.dart index f26ff3ee2..ccf922d41 100644 --- a/lib/src/widgets/picker.dart +++ b/lib/src/widgets/picker.dart @@ -1,5 +1,6 @@ // ignore_for_file: deprecated_member_use +import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/src/widgets/alert_background.dart'; import 'package:cake_wallet/src/widgets/alert_close_button.dart'; @@ -70,108 +71,106 @@ class _PickerState extends State> { @override Widget build(BuildContext context) { return AlertBackground( - child: Stack( - alignment: Alignment.center, + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, children: [ - Column( - mainAxisSize: MainAxisSize.min, - children: [ - if (widget.title?.isNotEmpty ?? false) - Container( - padding: EdgeInsets.symmetric(horizontal: 24), - child: Text( - widget.title!, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 18, - fontFamily: 'Lato', - fontWeight: FontWeight.bold, - decoration: TextDecoration.none, - color: Colors.white, - ), - ), + if (widget.title?.isNotEmpty ?? false) + Container( + padding: EdgeInsets.symmetric(horizontal: 24), + child: Text( + widget.title!, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 18, + fontFamily: 'Lato', + fontWeight: FontWeight.bold, + decoration: TextDecoration.none, + color: Colors.white, ), - Padding( - padding: EdgeInsets.only(left: 24, right: 24, top: 24), - child: ClipRRect( - borderRadius: BorderRadius.all(Radius.circular(30)), - child: Container( - color: Theme.of(context).accentTextTheme.headline6!.color!, - child: ConstrainedBox( - constraints: BoxConstraints( - maxHeight: MediaQuery.of(context).size.height * 0.65, - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - if (widget.hintText != null) - Padding( - padding: const EdgeInsets.all(16), - child: TextFormField( - controller: searchController, - style: TextStyle(color: Theme.of(context).primaryTextTheme.headline6!.color!), - decoration: InputDecoration( - hintText: widget.hintText, - prefixIcon: Image.asset("assets/images/search_icon.png"), - filled: true, - fillColor: Theme.of(context).accentTextTheme.headline3!.color!, - alignLabelWithHint: false, - contentPadding: const EdgeInsets.symmetric(vertical: 4, horizontal: 16), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(14), - borderSide: const BorderSide( - color: Colors.transparent, - )), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(14), - borderSide: const BorderSide( - color: Colors.transparent, - )), - ), + ), + ), + Padding( + padding: EdgeInsets.only(left: 24, right: 24, top: 24), + child: ClipRRect( + borderRadius: BorderRadius.all(Radius.circular(30)), + child: Container( + color: Theme.of(context).accentTextTheme.headline6!.color!, + child: ConstrainedBox( + constraints: BoxConstraints( + maxHeight: MediaQuery.of(context).size.height * 0.65, + maxWidth: ResponsiveLayoutUtil.kPopupWidth, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (widget.hintText != null) + Padding( + padding: const EdgeInsets.all(16), + child: TextFormField( + controller: searchController, + style: TextStyle(color: Theme.of(context).primaryTextTheme.headline6!.color!), + decoration: InputDecoration( + hintText: widget.hintText, + prefixIcon: Image.asset("assets/images/search_icon.png"), + filled: true, + fillColor: Theme.of(context).accentTextTheme.headline3!.color!, + alignLabelWithHint: false, + contentPadding: const EdgeInsets.symmetric(vertical: 4, horizontal: 16), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(14), + borderSide: const BorderSide( + color: Colors.transparent, + )), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(14), + borderSide: const BorderSide( + color: Colors.transparent, + )), ), ), - Divider( - color: Theme.of(context).accentTextTheme.headline6!.backgroundColor!, - height: 1, ), - if (widget.selectedAtIndex != -1) buildSelectedItem(), - Flexible( - child: Stack( - alignment: Alignment.center, - children: [ - items.length > 3 ? Scrollbar( - controller: controller, - child: itemsList(), - ) : itemsList(), - (widget.description?.isNotEmpty ?? false) - ? Positioned( - bottom: 24, - left: 24, - right: 24, - child: Text( - widget.description!, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w500, - fontFamily: 'Lato', - decoration: TextDecoration.none, - color: Theme.of(context).primaryTextTheme.headline6!.color!, - ), + Divider( + color: Theme.of(context).accentTextTheme.headline6!.backgroundColor!, + height: 1, + ), + if (widget.selectedAtIndex != -1) buildSelectedItem(), + Flexible( + child: Stack( + alignment: Alignment.center, + children: [ + items.length > 3 ? Scrollbar( + controller: controller, + child: itemsList(), + ) : itemsList(), + (widget.description?.isNotEmpty ?? false) + ? Positioned( + bottom: 24, + left: 24, + right: 24, + child: Text( + widget.description!, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w500, + fontFamily: 'Lato', + decoration: TextDecoration.none, + color: Theme.of(context).primaryTextTheme.headline6!.color!, ), - ) - : Offstage(), - ], - ), + ), + ) + : Offstage(), + ], ), - ], - ), - ), + ), + ], ), ), - ) - ], + ), + ), ), + SizedBox(height: ResponsiveLayoutUtil.kPopupSpaceHeight), AlertCloseButton(), ], ), diff --git a/lib/src/widgets/primary_button.dart b/lib/src/widgets/primary_button.dart index a563319c5..c27169894 100644 --- a/lib/src/widgets/primary_button.dart +++ b/lib/src/widgets/primary_button.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:dotted_border/dotted_border.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -24,28 +25,31 @@ class PrimaryButton extends StatelessWidget { @override Widget build(BuildContext context) { - final content = SizedBox( - width: double.infinity, - height: 52.0, - child: TextButton( - onPressed: isDisabled - ? (onDisabledPressed != null ? onDisabledPressed : null) : onPressed, - style: ButtonStyle(backgroundColor: MaterialStateProperty.all(isDisabled ? color.withOpacity(0.5) : color), - shape: MaterialStateProperty.all( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular(26.0), + final content = ConstrainedBox( + constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint), + child: SizedBox( + width: double.infinity, + height: 52.0, + child: TextButton( + onPressed: isDisabled + ? (onDisabledPressed != null ? onDisabledPressed : null) : onPressed, + style: ButtonStyle(backgroundColor: MaterialStateProperty.all(isDisabled ? color.withOpacity(0.5) : color), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(26.0), + ), ), - ), - overlayColor: MaterialStateProperty.all(Colors.transparent)), - child: Text(text, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 15.0, - fontWeight: FontWeight.w600, - color: isDisabled - ? textColor.withOpacity(0.5) - : textColor)), - )); + overlayColor: MaterialStateProperty.all(Colors.transparent)), + child: Text(text, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 15.0, + fontWeight: FontWeight.w600, + color: isDisabled + ? textColor.withOpacity(0.5) + : textColor)), + )), + ); return isDottedBorder ? DottedBorder( @@ -77,29 +81,32 @@ class LoadingPrimaryButton extends StatelessWidget { @override Widget build(BuildContext context) { - return SizedBox( - width: double.infinity, - height: 52.0, - child: TextButton( - onPressed: (isLoading || isDisabled) ? null : onPressed, - style: ButtonStyle(backgroundColor: MaterialStateProperty.all(isDisabled ? color.withOpacity(0.5) : color), - shape: MaterialStateProperty.all( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular(26.0), - ), - )), - - child: isLoading - ? CupertinoActivityIndicator(animating: true) - : Text(text, - style: TextStyle( - fontSize: 15.0, - fontWeight: FontWeight.w600, - color: isDisabled - ? textColor.withOpacity(0.5) - : textColor + return ConstrainedBox( + constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint), + child: SizedBox( + width: double.infinity, + height: 52.0, + child: TextButton( + onPressed: (isLoading || isDisabled) ? null : onPressed, + style: ButtonStyle(backgroundColor: MaterialStateProperty.all(isDisabled ? color.withOpacity(0.5) : color), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(26.0), + ), )), - )); + + child: isLoading + ? CupertinoActivityIndicator(animating: true) + : Text(text, + style: TextStyle( + fontSize: 15.0, + fontWeight: FontWeight.w600, + color: isDisabled + ? textColor.withOpacity(0.5) + : textColor + )), + )), + ); } } @@ -130,45 +137,48 @@ class PrimaryIconButton extends StatelessWidget { @override Widget build(BuildContext context) { - return SizedBox( - width: double.infinity, - height: 52.0, - child: TextButton( - onPressed: onPressed, - style: ButtonStyle(backgroundColor: MaterialStateProperty.all(color), - shape: MaterialStateProperty.all( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular(radius), - ), - )), - child: Stack( - children: [ - Row( - mainAxisAlignment: mainAxisAlignment, - children: [ - Container( - width: 26.0, - height: 52.0, - decoration: BoxDecoration( - shape: BoxShape.circle, color: iconBackgroundColor), - child: Center( - child: Icon(iconData, color: iconColor, size: 22.0) - ), + return ConstrainedBox( + constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint), + child: SizedBox( + width: double.infinity, + height: 52.0, + child: TextButton( + onPressed: onPressed, + style: ButtonStyle(backgroundColor: MaterialStateProperty.all(color), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(radius), ), - ], - ), - Container( - height: 52.0, - child: Center( - child: Text(text, - style: TextStyle( - fontSize: 16.0, - color: textColor)), + )), + child: Stack( + children: [ + Row( + mainAxisAlignment: mainAxisAlignment, + children: [ + Container( + width: 26.0, + height: 52.0, + decoration: BoxDecoration( + shape: BoxShape.circle, color: iconBackgroundColor), + child: Center( + child: Icon(iconData, color: iconColor, size: 22.0) + ), + ), + ], ), - ) - ], - ), - )); + Container( + height: 52.0, + child: Center( + child: Text(text, + style: TextStyle( + fontSize: 16.0, + color: textColor)), + ), + ) + ], + ), + )), + ); } } @@ -190,34 +200,37 @@ class PrimaryImageButton extends StatelessWidget { @override Widget build(BuildContext context) { - return SizedBox( - width: double.infinity, - height: 52.0, - child: TextButton( - onPressed: onPressed, - style: ButtonStyle(backgroundColor: MaterialStateProperty.all(color), - shape: MaterialStateProperty.all( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular(26.0), - ), - )), - child:Center( - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - image, - SizedBox(width: 15), - Text( - text, - style: TextStyle( - fontSize: 15, - fontWeight: FontWeight.w600, - color: textColor + return ConstrainedBox( + constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint), + child: SizedBox( + width: double.infinity, + height: 52.0, + child: TextButton( + onPressed: onPressed, + style: ButtonStyle(backgroundColor: MaterialStateProperty.all(color), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(26.0), ), - ) - ], - ), - ) - )); + )), + child:Center( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + image, + SizedBox(width: 15), + Text( + text, + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.w600, + color: textColor + ), + ) + ], + ), + ) + )), + ); } } diff --git a/lib/src/widgets/setting_action_button.dart b/lib/src/widgets/setting_action_button.dart new file mode 100644 index 000000000..ef2d4e1bd --- /dev/null +++ b/lib/src/widgets/setting_action_button.dart @@ -0,0 +1,81 @@ +import 'package:cake_wallet/palette.dart'; +import 'package:flutter/material.dart'; + +class SettingActionButton extends StatelessWidget { + final bool isLastTile; + final bool isSelected; + final bool isArrowVisible; + final bool selectionActive; + final VoidCallback onTap; + final String image; + final String title; + final double fromBottomEdge; + final double fromTopEdge; + final double tileHeight; + const SettingActionButton({ + super.key, + this.isLastTile = false, + this.isSelected = false, + this.selectionActive = true, + this.isArrowVisible = false, + required this.onTap, + required this.image, + required this.title, + this.tileHeight = 60, + this.fromTopEdge = 50, + this.fromBottomEdge = 25, + }); + + @override + Widget build(BuildContext context) { + Color? color = isSelected + ? Theme.of(context).textTheme.headline3!.color + : selectionActive + ? Palette.darkBlue + : Theme.of(context).textTheme.headline3!.color; + return InkWell( + onTap: onTap, + hoverColor: Colors.transparent, + child: Container( + height: tileHeight, + padding: isLastTile + ? EdgeInsets.only( + left: 24, + right: 24, + top: fromBottomEdge, + ) + : EdgeInsets.only(left: 24, right: 24), + alignment: isLastTile ? Alignment.topLeft : null, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Image.asset( + image, + height: 16, + width: 16, + color: Palette.darkBlue, + ), + SizedBox(width: 16), + Expanded( + child: Text( + title, + style: TextStyle( + color: color, + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + ), + if (isArrowVisible) + Icon( + Icons.arrow_forward_ios, + color: color, + size: 16, + ) + ], + ), + ), + ); + } +} diff --git a/lib/src/widgets/setting_actions.dart b/lib/src/widgets/setting_actions.dart new file mode 100644 index 000000000..4dd16670a --- /dev/null +++ b/lib/src/widgets/setting_actions.dart @@ -0,0 +1,109 @@ +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/routes.dart'; +import 'package:flutter/material.dart'; + +class SettingActions { + final String name; + final String image; + final void Function(BuildContext) onTap; + + SettingActions._({ + required this.name, + required this.image, + required this.onTap, + }); + + static List all = [ + connectionSettingAction, + walletSettingAction, + addressBookSettingAction, + securityBackupSettingAction, + privacySettingAction, + displaySettingAction, + otherSettingAction, + supportSettingAction, + ]; + + static List desktopSettings = [ + connectionSettingAction, + walletSettingAction, + addressBookSettingAction, + securityBackupSettingAction, + privacySettingAction, + displaySettingAction, + otherSettingAction, + supportSettingAction, + ]; + + static SettingActions connectionSettingAction = SettingActions._( + name: S.current.connection_sync, + image: 'assets/images/nodes_menu.png', + onTap: (BuildContext context) { + Navigator.pop(context); + Navigator.of(context).pushNamed(Routes.connectionSync); + }, + ); + + static SettingActions walletSettingAction = SettingActions._( + name: S.current.wallets, + image: 'assets/images/wallet_menu.png', + onTap: (BuildContext context) { + Navigator.pop(context); + Navigator.of(context).pushNamed(Routes.walletList); + }, + ); + + static SettingActions addressBookSettingAction = SettingActions._( + name: S.current.address_book_menu, + image: 'assets/images/open_book_menu.png', + onTap: (BuildContext context) { + Navigator.pop(context); + Navigator.of(context).pushNamed(Routes.addressBook); + }, + ); + + static SettingActions securityBackupSettingAction = SettingActions._( + name: S.current.security_and_backup, + image: 'assets/images/key_menu.png', + onTap: (BuildContext context) { + Navigator.pop(context); + Navigator.of(context).pushNamed(Routes.securityBackupPage); + }, + ); + + static SettingActions privacySettingAction = SettingActions._( + name: S.current.privacy, + image: 'assets/images/privacy_menu.png', + onTap: (BuildContext context) { + Navigator.pop(context); + Navigator.of(context).pushNamed(Routes.privacyPage); + }, + ); + + static SettingActions displaySettingAction = SettingActions._( + name: S.current.display_settings, + image: 'assets/images/eye_menu.png', + onTap: (BuildContext context) { + Navigator.pop(context); + Navigator.of(context).pushNamed(Routes.displaySettingsPage); + }, + ); + + static SettingActions otherSettingAction = SettingActions._( + name: S.current.other_settings, + image: 'assets/images/settings_menu.png', + onTap: (BuildContext context) { + Navigator.pop(context); + Navigator.of(context).pushNamed(Routes.otherSettingsPage); + }, + ); + + static SettingActions supportSettingAction = SettingActions._( + name: S.current.settings_support, + image: 'assets/images/question_mark.png', + onTap: (BuildContext context) { + Navigator.pop(context); + Navigator.of(context).pushNamed(Routes.support); + }, + ); +} diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index b6e5a7549..02eb51da7 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -241,8 +241,8 @@ abstract class SettingsStoreBase with Store { {required Box nodeSource, required bool isBitcoinBuyEnabled, FiatCurrency initialFiatCurrency = FiatCurrency.usd, - BalanceDisplayMode initialBalanceDisplayMode = - BalanceDisplayMode.availableBalance}) async { + BalanceDisplayMode initialBalanceDisplayMode = BalanceDisplayMode.availableBalance, + ThemeBase? initialTheme}) async { final sharedPreferences = await getIt.getAsync(); final currentFiatCurrency = FiatCurrency.deserialize(raw: @@ -292,7 +292,7 @@ abstract class SettingsStoreBase with Store { (sharedPreferences.getBool(PreferencesKey.isDarkThemeLegacy) ?? false) ? ThemeType.dark.index : ThemeType.bright.index; - final savedTheme = ThemeList.deserialize( + final savedTheme = initialTheme ?? ThemeList.deserialize( raw: sharedPreferences.getInt(PreferencesKey.currentTheme) ?? legacyTheme); final actionListDisplayMode = ObservableList(); diff --git a/lib/utils/device_info.dart b/lib/utils/device_info.dart new file mode 100644 index 000000000..144ea3fa4 --- /dev/null +++ b/lib/utils/device_info.dart @@ -0,0 +1,11 @@ +import 'dart:io'; + +class DeviceInfo { + DeviceInfo._(); + + static DeviceInfo get instance => DeviceInfo._(); + + bool get isMobile => Platform.isAndroid || Platform.isIOS; + + bool get isDesktop => Platform.isMacOS || Platform.isWindows || Platform.isLinux; +} \ No newline at end of file diff --git a/lib/utils/exception_handler.dart b/lib/utils/exception_handler.dart index 002534ea1..d3689e7e0 100644 --- a/lib/utils/exception_handler.dart +++ b/lib/utils/exception_handler.dart @@ -69,6 +69,7 @@ class ExceptionHandler { static void onError(FlutterErrorDetails errorDetails) async { if (kDebugMode) { FlutterError.presentError(errorDetails); + debugPrint(errorDetails.toString()); return; } diff --git a/lib/utils/responsive_layout_util.dart b/lib/utils/responsive_layout_util.dart new file mode 100644 index 000000000..8ae76ca21 --- /dev/null +++ b/lib/utils/responsive_layout_util.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; + +class ResponsiveLayoutUtil { + static const double _kMobileThreshold = 900; + static const double kDesktopMaxWidthConstraint = 400; + static const double kPopupWidth = 400; + static const double kPopupSpaceHeight = 100; + + + const ResponsiveLayoutUtil._(); + + static final instance = ResponsiveLayoutUtil._(); + + bool isMobile(BuildContext context) { + final MediaQueryData mediaQueryData = MediaQuery.of(context); + return mediaQueryData.size.width < _kMobileThreshold; + } + + /// Returns dynamic size. + /// + /// If screen size is mobile, it returns 66% ([scale]) of the [originalValue]. + double getDynamicSize( + BuildContext context, + double originalValue, { + double? mobileSize, + double? scale, + }) { + scale ??= 2 / 3; + mobileSize ??= originalValue * scale; + final value = isMobile(context) ? mobileSize : originalValue; + + return value.roundToDouble(); + } +} diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index 5384ee743..b23f430f9 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -1,13 +1,9 @@ import 'package:cake_wallet/entities/exchange_api_mode.dart'; -import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/store/anonpay/anonpay_transactions_store.dart'; import 'package:cake_wallet/view_model/dashboard/anonpay_transaction_list_item.dart'; import 'package:cake_wallet/wallet_type_utils.dart'; import 'package:cw_core/transaction_history.dart'; import 'package:cw_core/balance.dart'; -import 'package:cake_wallet/buy/order.dart'; -import 'package:cake_wallet/entities/transaction_history.dart'; -import 'package:cake_wallet/exchange/trade_state.dart'; import 'package:cake_wallet/entities/balance_display_mode.dart'; import 'package:cw_core/transaction_info.dart'; import 'package:cake_wallet/exchange/exchange_provider_description.dart'; @@ -21,10 +17,6 @@ import 'package:cake_wallet/view_model/dashboard/order_list_item.dart'; import 'package:cake_wallet/view_model/dashboard/trade_list_item.dart'; import 'package:cake_wallet/view_model/dashboard/transaction_list_item.dart'; import 'package:cake_wallet/view_model/dashboard/action_list_item.dart'; -import 'package:cake_wallet/view_model/dashboard/action_list_display_mode.dart'; -import 'package:crypto/crypto.dart'; -import 'package:flutter/services.dart'; -import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/sync_status.dart'; diff --git a/lib/view_model/dashboard/desktop_sidebar_view_model.dart b/lib/view_model/dashboard/desktop_sidebar_view_model.dart new file mode 100644 index 000000000..d0320c05f --- /dev/null +++ b/lib/view_model/dashboard/desktop_sidebar_view_model.dart @@ -0,0 +1,34 @@ +import 'package:mobx/mobx.dart'; + +part 'desktop_sidebar_view_model.g.dart'; + +enum SidebarItem { + dashboard, + support, + settings, + transactions; +} + +class DesktopSidebarViewModel = DesktopSidebarViewModelBase with _$DesktopSidebarViewModel; + +abstract class DesktopSidebarViewModelBase with Store { + DesktopSidebarViewModelBase(); + + @observable + SidebarItem currentPage = SidebarItem.dashboard; + + @action + void onPageChange(SidebarItem item) { + if (currentPage == item) { + resetSidebar(); + + return; + } + currentPage = item; + } + + @action + void resetSidebar() { + currentPage = SidebarItem.dashboard; + } +} diff --git a/lib/view_model/node_list/node_list_view_model.dart b/lib/view_model/node_list/node_list_view_model.dart index deb1f29cf..3663d48ac 100644 --- a/lib/view_model/node_list/node_list_view_model.dart +++ b/lib/view_model/node_list/node_list_view_model.dart @@ -1,4 +1,6 @@ import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/store/app_store.dart'; +import 'package:cake_wallet/utils/mobx.dart'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; import 'package:cw_core/wallet_base.dart'; @@ -7,25 +9,28 @@ import 'package:cw_core/node.dart'; import 'package:cake_wallet/entities/node_list.dart'; import 'package:cake_wallet/entities/default_settings_migration.dart'; import 'package:cw_core/wallet_type.dart'; -import 'package:cake_wallet/utils/mobx.dart'; part 'node_list_view_model.g.dart'; class NodeListViewModel = NodeListViewModelBase with _$NodeListViewModel; abstract class NodeListViewModelBase with Store { - NodeListViewModelBase(this._nodeSource, this.wallet, this.settingsStore) - : nodes = ObservableList() { - _nodeSource.bindToList(nodes, - filter: (Node val) => val?.type == wallet.type, initialFire: true); + NodeListViewModelBase(this._nodeSource, this._appStore) + : nodes = ObservableList(), + settingsStore = _appStore.settingsStore { + _bindNodes(); + + reaction((_) => _appStore.wallet, (WalletBase? _wallet) { + _bindNodes(); + }); } @computed Node get currentNode { - final node = settingsStore.nodes[wallet.type]; + final node = settingsStore.nodes[_appStore.wallet!.type]; if (node == null) { - throw Exception('No node for wallet type: ${wallet.type}'); + throw Exception('No node for wallet type: ${_appStore.wallet!.type}'); } return node; @@ -33,19 +38,19 @@ abstract class NodeListViewModelBase with Store { String getAlertContent(String uri) => S.current.change_current_node(uri) + - '${uri.endsWith('.onion') || uri.contains('.onion:') ? '\n' + S.current.orbot_running_alert : ''}'; + '${uri.endsWith('.onion') || uri.contains('.onion:') ? '\n' + S.current.orbot_running_alert : ''}'; final ObservableList nodes; final SettingsStore settingsStore; - final WalletBase wallet; final Box _nodeSource; + final AppStore _appStore; Future reset() async { await resetToDefault(_nodeSource); Node node; - switch (wallet.type) { + switch (_appStore.wallet!.type) { case WalletType.bitcoin: node = getBitcoinDefaultElectrumServer(nodes: _nodeSource)!; break; @@ -59,7 +64,7 @@ abstract class NodeListViewModelBase with Store { node = getHavenDefaultNode(nodes: _nodeSource)!; break; default: - throw Exception('Unexpected wallet type: ${wallet.type}'); + throw Exception('Unexpected wallet type: ${_appStore.wallet!.type}'); } await setAsCurrent(node); @@ -68,6 +73,15 @@ abstract class NodeListViewModelBase with Store { @action Future delete(Node node) async => node.delete(); - Future setAsCurrent(Node node) async => - settingsStore.nodes[wallet.type] = node; + Future setAsCurrent(Node node) async => settingsStore.nodes[_appStore.wallet!.type] = node; + + @action + void _bindNodes() { + nodes.clear(); + _nodeSource.bindToList( + nodes, + filter: (val) => val.type == _appStore.wallet!.type, + initialFire: true, + ); + } } diff --git a/lib/view_model/wallet_keys_view_model.dart b/lib/view_model/wallet_keys_view_model.dart index 023713bd8..e20089915 100644 --- a/lib/view_model/wallet_keys_view_model.dart +++ b/lib/view_model/wallet_keys_view_model.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/store/app_store.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:mobx/mobx.dart'; import 'package:cake_wallet/generated/i18n.dart'; @@ -12,70 +13,83 @@ part 'wallet_keys_view_model.g.dart'; class WalletKeysViewModel = WalletKeysViewModelBase with _$WalletKeysViewModel; abstract class WalletKeysViewModelBase with Store { - WalletKeysViewModelBase(WalletBase wallet) - : title = wallet.type == WalletType.bitcoin || wallet.type == WalletType.litecoin + WalletKeysViewModelBase(this._appStore) + : title = _appStore.wallet!.type == WalletType.bitcoin || + _appStore.wallet!.type == WalletType.litecoin ? S.current.wallet_seed : S.current.wallet_keys, - _wallet = wallet, - _restoreHeight = wallet.walletInfo.restoreHeight, + _restoreHeight = _appStore.wallet!.walletInfo.restoreHeight, items = ObservableList() { - if (wallet.type == WalletType.monero) { - final keys = monero!.getKeys(wallet); - items.addAll([ - if (keys['publicSpendKey'] != null) - StandartListItem(title: S.current.spend_key_public, value: keys['publicSpendKey']!), - if (keys['privateSpendKey'] != null) - StandartListItem(title: S.current.spend_key_private, value: keys['privateSpendKey']!), - if (keys['publicViewKey'] != null) - StandartListItem(title: S.current.view_key_public, value: keys['publicViewKey']!), - if (keys['privateViewKey'] != null) - StandartListItem(title: S.current.view_key_private, value: keys['privateViewKey']!), - StandartListItem(title: S.current.wallet_seed, value: wallet.seed), - ]); - } + _populateItems(); - if (wallet.type == WalletType.haven) { - final keys = haven!.getKeys(wallet); - items.addAll([ - if (keys['publicSpendKey'] != null) - StandartListItem(title: S.current.spend_key_public, value: keys['publicSpendKey']!), - if (keys['privateSpendKey'] != null) - StandartListItem(title: S.current.spend_key_private, value: keys['privateSpendKey']!), - if (keys['publicViewKey'] != null) - StandartListItem(title: S.current.view_key_public, value: keys['publicViewKey']!), - if (keys['privateViewKey'] != null) - StandartListItem(title: S.current.view_key_private, value: keys['privateViewKey']!), - StandartListItem(title: S.current.wallet_seed, value: wallet.seed), - ]); - } - - if (wallet.type == WalletType.bitcoin || wallet.type == WalletType.litecoin) { - items.addAll([ - StandartListItem(title: S.current.wallet_seed, value: wallet.seed), - ]); - } + reaction((_) => _appStore.wallet, (WalletBase? _wallet) { + _populateItems(); + }); } final ObservableList items; final String title; - final WalletBase _wallet; + final AppStore _appStore; final int _restoreHeight; + void _populateItems() { + items.clear(); + + if (_appStore.wallet!.type == WalletType.monero) { + final keys = monero!.getKeys(_appStore.wallet!); + + items.addAll([ + if (keys['publicSpendKey'] != null) + StandartListItem(title: S.current.spend_key_public, value: keys['publicSpendKey']!), + if (keys['privateSpendKey'] != null) + StandartListItem(title: S.current.spend_key_private, value: keys['privateSpendKey']!), + if (keys['publicViewKey'] != null) + StandartListItem(title: S.current.view_key_public, value: keys['publicViewKey']!), + if (keys['privateViewKey'] != null) + StandartListItem(title: S.current.view_key_private, value: keys['privateViewKey']!), + StandartListItem(title: S.current.wallet_seed, value: _appStore.wallet!.seed), + ]); + } + + if (_appStore.wallet!.type == WalletType.haven) { + final keys = haven!.getKeys(_appStore.wallet!); + + items.addAll([ + if (keys['publicSpendKey'] != null) + StandartListItem(title: S.current.spend_key_public, value: keys['publicSpendKey']!), + if (keys['privateSpendKey'] != null) + StandartListItem(title: S.current.spend_key_private, value: keys['privateSpendKey']!), + if (keys['publicViewKey'] != null) + StandartListItem(title: S.current.view_key_public, value: keys['publicViewKey']!), + if (keys['privateViewKey'] != null) + StandartListItem(title: S.current.view_key_private, value: keys['privateViewKey']!), + StandartListItem(title: S.current.wallet_seed, value: _appStore.wallet!.seed), + ]); + } + + if (_appStore.wallet!.type == WalletType.bitcoin || + _appStore.wallet!.type == WalletType.litecoin) { + items.addAll([ + StandartListItem(title: S.current.wallet_seed, value: _appStore.wallet!.seed), + ]); + } + } + Future currentHeight() async { - if (_wallet.type == WalletType.haven) { + if (_appStore.wallet!.type == WalletType.haven) { return await haven!.getCurrentHeight(); } - if (_wallet.type == WalletType.monero) { + if (_appStore.wallet!.type == WalletType.monero) { return monero_wallet.getCurrentHeight(); } return null; } String get _path { - switch (_wallet.type) { + switch (_appStore.wallet!.type) { case WalletType.monero: return 'monero_wallet:'; case WalletType.bitcoin: @@ -85,7 +99,7 @@ abstract class WalletKeysViewModelBase with Store { case WalletType.haven: return 'haven_wallet:'; default: - throw Exception('Unexpected wallet type: ${_wallet.toString()}'); + throw Exception('Unexpected wallet type: ${_appStore.wallet!.toString()}'); } } @@ -103,7 +117,7 @@ abstract class WalletKeysViewModelBase with Store { Future> get _queryParams async { final restoreHeightResult = await restoreHeight; return { - 'seed': _wallet.seed, + 'seed': _appStore.wallet!.seed, if (restoreHeightResult != null) ...{'height': restoreHeightResult} }; } diff --git a/lib/view_model/wallet_list/wallet_list_item.dart b/lib/view_model/wallet_list/wallet_list_item.dart index af30b9bea..a644c07b3 100644 --- a/lib/view_model/wallet_list/wallet_list_item.dart +++ b/lib/view_model/wallet_list/wallet_list_item.dart @@ -1,13 +1,13 @@ -import 'package:flutter/foundation.dart'; import 'package:cw_core/wallet_type.dart'; class WalletListItem { - const WalletListItem( - {required this.name, - required this.type, - required this.key, - this.isCurrent = false, - this.isEnabled = true}); + const WalletListItem({ + required this.name, + required this.type, + required this.key, + this.isCurrent = false, + this.isEnabled = true, + }); final String name; final WalletType type; diff --git a/lib/view_model/wallet_list/wallet_list_view_model.dart b/lib/view_model/wallet_list/wallet_list_view_model.dart index 6d63675ba..be8d928aa 100644 --- a/lib/view_model/wallet_list/wallet_list_view_model.dart +++ b/lib/view_model/wallet_list/wallet_list_view_model.dart @@ -22,6 +22,7 @@ abstract class WalletListViewModelBase with Store { this._authService, ) : wallets = ObservableList() { _updateList(); + reaction((_) => _appStore.wallet, (_) => _updateList()); } @observable diff --git a/macos/.gitignore b/macos/.gitignore new file mode 100644 index 000000000..746adbb6b --- /dev/null +++ b/macos/.gitignore @@ -0,0 +1,7 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ diff --git a/macos/CakeWallet/secRandom.swift b/macos/CakeWallet/secRandom.swift new file mode 100644 index 000000000..c9b2e3593 --- /dev/null +++ b/macos/CakeWallet/secRandom.swift @@ -0,0 +1,12 @@ +import Foundation + +func secRandom(count: Int) -> Data? { + var bytes = [Int8](repeating: 0, count: count) + let status = SecRandomCopyBytes(kSecRandomDefault, bytes.count, &bytes) + + if status == errSecSuccess { + return Data(bytes: bytes, count: bytes.count) + } + + return nil +} diff --git a/macos/Flutter/Flutter-Debug.xcconfig b/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 000000000..4b81f9b2d --- /dev/null +++ b/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/Flutter-Release.xcconfig b/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 000000000..5caa9d157 --- /dev/null +++ b/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 000000000..feebda3f2 --- /dev/null +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,36 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + +import connectivity_macos +import cw_monero +import device_info_plus +import devicelocale +import flutter_secure_storage_macos +import package_info +import path_provider_foundation +import platform_device_id +import platform_device_id_macos +import share_plus_macos +import shared_preferences_foundation +import url_launcher_macos +import wakelock_macos + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin")) + CwMoneroPlugin.register(with: registry.registrar(forPlugin: "CwMoneroPlugin")) + DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) + DevicelocalePlugin.register(with: registry.registrar(forPlugin: "DevicelocalePlugin")) + FlutterSecureStorageMacosPlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStorageMacosPlugin")) + FLTPackageInfoPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlugin")) + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) + PlatformDeviceIdMacosPlugin.register(with: registry.registrar(forPlugin: "PlatformDeviceIdMacosPlugin")) + PlatformDeviceIdMacosPlugin.register(with: registry.registrar(forPlugin: "PlatformDeviceIdMacosPlugin")) + SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) + UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) + WakelockMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockMacosPlugin")) +} diff --git a/macos/Podfile b/macos/Podfile new file mode 100644 index 000000000..0c76ccf54 --- /dev/null +++ b/macos/Podfile @@ -0,0 +1,40 @@ +platform :osx, '12.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end diff --git a/macos/Podfile.lock b/macos/Podfile.lock new file mode 100644 index 000000000..41861493e --- /dev/null +++ b/macos/Podfile.lock @@ -0,0 +1,118 @@ +PODS: + - connectivity_macos (0.0.1): + - FlutterMacOS + - Reachability + - cw_monero (0.0.1): + - cw_monero/Boost (= 0.0.1) + - cw_monero/Monero (= 0.0.1) + - cw_monero/OpenSSL (= 0.0.1) + - cw_monero/Sodium (= 0.0.1) + - cw_monero/Unbound (= 0.0.1) + - FlutterMacOS + - cw_monero/Boost (0.0.1): + - FlutterMacOS + - cw_monero/Monero (0.0.1): + - FlutterMacOS + - cw_monero/OpenSSL (0.0.1): + - FlutterMacOS + - cw_monero/Sodium (0.0.1): + - FlutterMacOS + - cw_monero/Unbound (0.0.1): + - FlutterMacOS + - device_info_plus (0.0.1): + - FlutterMacOS + - devicelocale (0.0.1): + - FlutterMacOS + - flutter_secure_storage_macos (3.3.1): + - FlutterMacOS + - FlutterMacOS (1.0.0) + - package_info (0.0.1): + - FlutterMacOS + - path_provider_foundation (0.0.1): + - Flutter + - FlutterMacOS + - platform_device_id (0.0.1): + - FlutterMacOS + - platform_device_id_macos (0.0.1): + - FlutterMacOS + - Reachability (3.2) + - share_plus_macos (0.0.1): + - FlutterMacOS + - shared_preferences_foundation (0.0.1): + - Flutter + - FlutterMacOS + - url_launcher_macos (0.0.1): + - FlutterMacOS + - wakelock_macos (0.0.1): + - FlutterMacOS + +DEPENDENCIES: + - connectivity_macos (from `Flutter/ephemeral/.symlinks/plugins/connectivity_macos/macos`) + - cw_monero (from `Flutter/ephemeral/.symlinks/plugins/cw_monero/macos`) + - device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`) + - devicelocale (from `Flutter/ephemeral/.symlinks/plugins/devicelocale/macos`) + - flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`) + - FlutterMacOS (from `Flutter/ephemeral`) + - package_info (from `Flutter/ephemeral/.symlinks/plugins/package_info/macos`) + - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/macos`) + - platform_device_id (from `Flutter/ephemeral/.symlinks/plugins/platform_device_id/macos`) + - platform_device_id_macos (from `Flutter/ephemeral/.symlinks/plugins/platform_device_id_macos/macos`) + - share_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos`) + - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/macos`) + - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) + - wakelock_macos (from `Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos`) + +SPEC REPOS: + trunk: + - Reachability + +EXTERNAL SOURCES: + connectivity_macos: + :path: Flutter/ephemeral/.symlinks/plugins/connectivity_macos/macos + cw_monero: + :path: Flutter/ephemeral/.symlinks/plugins/cw_monero/macos + device_info_plus: + :path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos + devicelocale: + :path: Flutter/ephemeral/.symlinks/plugins/devicelocale/macos + flutter_secure_storage_macos: + :path: Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos + FlutterMacOS: + :path: Flutter/ephemeral + package_info: + :path: Flutter/ephemeral/.symlinks/plugins/package_info/macos + path_provider_foundation: + :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/macos + platform_device_id: + :path: Flutter/ephemeral/.symlinks/plugins/platform_device_id/macos + platform_device_id_macos: + :path: Flutter/ephemeral/.symlinks/plugins/platform_device_id_macos/macos + share_plus_macos: + :path: Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos + shared_preferences_foundation: + :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/macos + url_launcher_macos: + :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos + wakelock_macos: + :path: Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos + +SPEC CHECKSUMS: + connectivity_macos: 5dae6ee11d320fac7c05f0d08bd08fc32b5514d9 + cw_monero: f8b7f104508efba2591548e76b5c058d05cba3f0 + device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f + devicelocale: 9f0f36ac651cabae2c33f32dcff4f32b61c38225 + flutter_secure_storage_macos: 6ceee8fbc7f484553ad17f79361b556259df89aa + FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 + package_info: 6eba2fd8d3371dda2d85c8db6fe97488f24b74b2 + path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9 + platform_device_id: 3e414428f45df149bbbfb623e2c0ca27c545b763 + platform_device_id_macos: f763bb55f088be804d61b96eb4710b8ab6598e94 + Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96 + share_plus_macos: 853ee48e7dce06b633998ca0735d482dd671ade4 + shared_preferences_foundation: 986fc17f3d3251412d18b0265f9c64113a8c2472 + url_launcher_macos: 5335912b679c073563f29d89d33d10d459f95451 + wakelock_macos: bc3f2a9bd8d2e6c89fee1e1822e7ddac3bd004a9 + +PODFILE CHECKSUM: 505596d150d38022472859d890f709281982e016 + +COCOAPODS: 1.11.3 diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000..06558bf57 --- /dev/null +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,663 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + 4171CB1F5A4EA2E4DC33F52F /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B38D1DBC56DBD386923BC063 /* Pods_Runner.framework */; }; + 9F565D5929954F53009A75FB /* secRandom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F565D5729954F53009A75FB /* secRandom.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 094BF982245FD1012D60A103 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 0C090639294D3AAC00954DC9 /* libiconv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libiconv.tbd; path = usr/lib/libiconv.tbd; sourceTree = SDKROOT; }; + 2A820A13B0719E9E0CD6686F /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* Cake Wallet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Cake Wallet.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9646C67C7114830A5ACFF5DF /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + 9F565D5729954F53009A75FB /* secRandom.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = secRandom.swift; path = CakeWallet/secRandom.swift; sourceTree = ""; }; + 9F565D5829954F53009A75FB /* decrypt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = decrypt.swift; path = CakeWallet/decrypt.swift; sourceTree = ""; }; + B38D1DBC56DBD386923BC063 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4171CB1F5A4EA2E4DC33F52F /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 9F565D5829954F53009A75FB /* decrypt.swift */, + 9F565D5729954F53009A75FB /* secRandom.swift */, + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + 9B6E7CA3983216A9E173F00F /* Pods */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* Cake Wallet.app */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + 9B6E7CA3983216A9E173F00F /* Pods */ = { + isa = PBXGroup; + children = ( + 9646C67C7114830A5ACFF5DF /* Pods-Runner.debug.xcconfig */, + 2A820A13B0719E9E0CD6686F /* Pods-Runner.release.xcconfig */, + 094BF982245FD1012D60A103 /* Pods-Runner.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 0C090639294D3AAC00954DC9 /* libiconv.tbd */, + B38D1DBC56DBD386923BC063 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 93B711AB4B96E7C8C5C5B844 /* [CP] Check Pods Manifest.lock */, + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + 5592D00118C2EA3C5E0B5FDF /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* Cake Wallet.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; + 5592D00118C2EA3C5E0B5FDF /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 93B711AB4B96E7C8C5C5B844 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9F565D5929954F53009A75FB /* secRandom.swift in Sources */, + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ARCHS = arm64; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 10; + DEVELOPMENT_TEAM = 32J6BB6VUS; + INFOPLIST_FILE = Runner/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = "Cake Wallet"; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.finance"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 12; + MARKETING_VERSION = 1.0.1; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ARCHS = arm64; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 10; + DEVELOPMENT_TEAM = 32J6BB6VUS; + INFOPLIST_FILE = Runner/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = "Cake Wallet"; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.finance"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 12; + MARKETING_VERSION = 1.0.1; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ARCHS = arm64; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 10; + DEVELOPMENT_TEAM = 32J6BB6VUS; + INFOPLIST_FILE = Runner/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = "Cake Wallet"; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.finance"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 12; + MARKETING_VERSION = 1.0.1; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/macos/Runner.xcodeproj/project_base.pbxproj b/macos/Runner.xcodeproj/project_base.pbxproj new file mode 100644 index 000000000..fb3e76252 --- /dev/null +++ b/macos/Runner.xcodeproj/project_base.pbxproj @@ -0,0 +1,636 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 328F945957E1041662291EC5 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C84AA35EA80D710889C68D81 /* Pods_Runner.framework */; }; + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 0C090639294D3AAC00954DC9 /* libiconv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libiconv.tbd; path = usr/lib/libiconv.tbd; sourceTree = SDKROOT; }; + 135D3AD0276D31F62BBEDDBF /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* cake_wallet_new.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = cake_wallet_new.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 359F2F22842E234537DED5E3 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + C84AA35EA80D710889C68D81 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + FF499CFF131B036E3C5638D0 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 328F945957E1041662291EC5 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + 9B6E7CA3983216A9E173F00F /* Pods */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* cake_wallet_new.app */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + 9B6E7CA3983216A9E173F00F /* Pods */ = { + isa = PBXGroup; + children = ( + 359F2F22842E234537DED5E3 /* Pods-Runner.debug.xcconfig */, + 135D3AD0276D31F62BBEDDBF /* Pods-Runner.release.xcconfig */, + FF499CFF131B036E3C5638D0 /* Pods-Runner.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 0C090639294D3AAC00954DC9 /* libiconv.tbd */, + C84AA35EA80D710889C68D81 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 8AB41FC42599228A92F51A44 /* [CP] Check Pods Manifest.lock */, + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + F015812745AAC61FF550BB30 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* cake_wallet_new.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; + 8AB41FC42599228A92F51A44 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + F015812745AAC61FF550BB30 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ARCHS = ; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ARCHS = ; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ARCHS = ; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000..8536e9a81 --- /dev/null +++ b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macos/Runner.xcworkspace/contents.xcworkspacedata b/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..21a3cc14c --- /dev/null +++ b/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/macos/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/macos/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 000000000..f9b0d7c5e --- /dev/null +++ b/macos/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/macos/Runner/AppDelegate.swift b/macos/Runner/AppDelegate.swift new file mode 100644 index 000000000..0c8973175 --- /dev/null +++ b/macos/Runner/AppDelegate.swift @@ -0,0 +1,33 @@ +import Cocoa +import FlutterMacOS +import IOKit.pwr_mgt + +@NSApplicationMain +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } + + override func applicationDidFinishLaunching(_ notification: Notification) { + let controller : FlutterViewController = mainFlutterWindow?.contentViewController as! FlutterViewController + + let utilsChannel = FlutterMethodChannel( + name: "com.cake_wallet/native_utils", + binaryMessenger: controller.engine.binaryMessenger) + utilsChannel.setMethodCallHandler({ [weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in + switch call.method { + case "sec_random": + guard let args = call.arguments as? Dictionary, + let count = args["count"] as? Int else { + result(nil) + return + } + + result(secRandom(count: count)) + + default: + result(FlutterMethodNotImplemented) + } + }) + } +} diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..96d3fee1a --- /dev/null +++ b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "info": { + "version": 1, + "author": "xcode" + }, + "images": [ + { + "size": "16x16", + "idiom": "mac", + "filename": "app_icon_16.png", + "scale": "1x" + }, + { + "size": "16x16", + "idiom": "mac", + "filename": "app_icon_32.png", + "scale": "2x" + }, + { + "size": "32x32", + "idiom": "mac", + "filename": "app_icon_32.png", + "scale": "1x" + }, + { + "size": "32x32", + "idiom": "mac", + "filename": "app_icon_64.png", + "scale": "2x" + }, + { + "size": "128x128", + "idiom": "mac", + "filename": "app_icon_128.png", + "scale": "1x" + }, + { + "size": "128x128", + "idiom": "mac", + "filename": "app_icon_256.png", + "scale": "2x" + }, + { + "size": "256x256", + "idiom": "mac", + "filename": "app_icon_256.png", + "scale": "1x" + }, + { + "size": "256x256", + "idiom": "mac", + "filename": "app_icon_512.png", + "scale": "2x" + }, + { + "size": "512x512", + "idiom": "mac", + "filename": "app_icon_512.png", + "scale": "1x" + }, + { + "size": "512x512", + "idiom": "mac", + "filename": "app_icon_1024.png", + "scale": "2x" + } + ] +} \ No newline at end of file diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 000000000..73101354a Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 000000000..9ceee3c5e Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 000000000..ef46cd805 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png new file mode 100644 index 000000000..6547a1b1b Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png new file mode 100644 index 000000000..e436872e8 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png new file mode 100644 index 000000000..157b00493 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 000000000..a46ed4535 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ diff --git a/macos/Runner/Base.lproj/MainMenu.xib b/macos/Runner/Base.lproj/MainMenu.xib new file mode 100644 index 000000000..a6981c270 --- /dev/null +++ b/macos/Runner/Base.lproj/MainMenu.xib @@ -0,0 +1,344 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macos/Runner/Configs/AppInfo.xcconfig b/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 000000000..84f96bff7 --- /dev/null +++ b/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = Cake Wallet + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2022 com.fotolockr. All rights reserved. diff --git a/macos/Runner/Configs/Debug.xcconfig b/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 000000000..36b0fd946 --- /dev/null +++ b/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/macos/Runner/Configs/Release.xcconfig b/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 000000000..dff4f4956 --- /dev/null +++ b/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/macos/Runner/Configs/Warnings.xcconfig b/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 000000000..42bcbf478 --- /dev/null +++ b/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/macos/Runner/DebugProfileBase.entitlements b/macos/Runner/DebugProfileBase.entitlements new file mode 100644 index 000000000..c1b4345fe --- /dev/null +++ b/macos/Runner/DebugProfileBase.entitlements @@ -0,0 +1,18 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + keychain-access-groups + + $(AppIdentifierPrefix)${BUNDLE_ID} + + com.apple.security.network.client + + + diff --git a/macos/Runner/InfoBase.plist b/macos/Runner/InfoBase.plist new file mode 100644 index 000000000..98d0ea9ee --- /dev/null +++ b/macos/Runner/InfoBase.plist @@ -0,0 +1,34 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + LSApplicationCategoryType + public.app-category.finance + + diff --git a/macos/Runner/MainFlutterWindow.swift b/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 000000000..2722837ec --- /dev/null +++ b/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController.init() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/macos/Runner/ReleaseBase.entitlements b/macos/Runner/ReleaseBase.entitlements new file mode 100644 index 000000000..aef6ac342 --- /dev/null +++ b/macos/Runner/ReleaseBase.entitlements @@ -0,0 +1,14 @@ + + + + + com.apple.security.app-sandbox + + keychain-access-groups + + $(AppIdentifierPrefix)${BUNDLE_ID} + + com.apple.security.network.client + + + diff --git a/pubspec_base.yaml b/pubspec_base.yaml index 7825824a9..b357619bd 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -9,6 +9,7 @@ dependencies: qr_flutter: ^4.0.0 uuid: 3.0.6 shared_preferences: ^2.0.15 + shared_preferences_android: 2.1.0 flutter_secure_storage: git: url: https://github.com/cake-tech/flutter_secure_storage.git @@ -34,7 +35,9 @@ dependencies: local_auth: ^2.1.0 package_info: ^2.0.0 #package_info_plus: ^1.4.2 - devicelocale: ^0.4.3 + devicelocale: + git: + url: https://github.com/OmarHatem28/flutter-devicelocale auto_size_text: ^3.0.0 dotted_border: ^2.0.0+2 smooth_page_indicator: ^1.0.0+2 @@ -61,6 +64,7 @@ dependencies: permission_handler: ^10.0.0 device_display_brightness: ^0.0.6 platform_device_id: ^1.0.1 + wakelock: ^0.6.2 flutter_mailer: ^2.0.2 device_info_plus: 8.1.0 cake_backup: @@ -72,11 +76,11 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - build_runner: ^2.1.11 + build_runner: ^2.3.3 mobx_codegen: ^2.1.1 build_resolvers: ^2.0.9 hive_generator: ^1.1.3 - flutter_launcher_icons: ^0.9.3 + flutter_launcher_icons: ^0.11.0 # check flutter_launcher_icons for usage pedantic: ^1.8.0 # replace https://github.com/dart-lang/lints#migrating-from-packagepedantic @@ -85,6 +89,9 @@ flutter_icons: image_path: "assets/images/app_logo.png" android: true ios: true + macos: + generate: true + image_path: "assets/images/app_logo.png" flutter: uses-material-design: true diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index e06846872..430f2edcf 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -695,5 +695,6 @@ "optional_name": "اسم المستلم الاختياري", "clearnet_link": "رابط Clearnet", "onion_link": "رابط البصل", + "settings": "إعدادات", "sell_monero_com_alert_content": "بيع Monero غير مدعوم حتى الآن" } diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index 6a8f3c89e..3896fb133 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -697,5 +697,6 @@ "optional_name": "Optionaler Empfängername", "clearnet_link": "Clearnet-Link", "onion_link": "Zwiebel-Link", + "settings": "Einstellungen", "sell_monero_com_alert_content": "Der Verkauf von Monero wird noch nicht unterstützt" } diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index b1bf94690..fd3cdbc99 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -697,5 +697,6 @@ "onion_link": "Onion link", "decimal_places_error": "Too many decimal places", "edit_node": "Edit Node", + "settings": "Settings", "sell_monero_com_alert_content": "Selling Monero is not supported yet" } diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 8e9e4992c..2995008ac 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -697,5 +697,6 @@ "optional_name": "Nombre del destinatario opcional", "clearnet_link": "enlace Clearnet", "onion_link": "Enlace de cebolla", + "settings": "Configuraciones", "sell_monero_com_alert_content": "Aún no se admite la venta de Monero" } diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index 9ba3066c1..4dd8e54cb 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -696,6 +696,7 @@ "optional_description": "Descriptif facultatif", "optional_name": "Nom du destinataire facultatif", "clearnet_link": "Lien Clearnet", + "settings": "Paramètres", "onion_link": "Lien .onion", "sell_monero_com_alert_content": "La vente de Monero n'est pas encore prise en charge" } diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index ea9a673a8..e8280bae7 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -697,5 +697,6 @@ "optional_name": "वैकल्पिक प्राप्तकर्ता नाम", "clearnet_link": "क्लियरनेट लिंक", "onion_link": "प्याज का लिंक", + "settings": "समायोजन", "sell_monero_com_alert_content": "मोनेरो बेचना अभी तक समर्थित नहीं है" } diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index 8a0b4b5ed..973aaca59 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -697,5 +697,6 @@ "optional_name": "Izborno ime primatelja", "clearnet_link": "Clearnet veza", "onion_link": "Poveznica luka", + "settings": "Postavke", "sell_monero_com_alert_content": "Prodaja Monera još nije podržana" } diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 968fb991d..4b90f8890 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -697,5 +697,6 @@ "optional_name": "Nome del destinatario facoltativo", "clearnet_link": "Collegamento Clearnet", "onion_link": "Collegamento a cipolla", + "settings": "Impostazioni", "sell_monero_com_alert_content": "La vendita di Monero non è ancora supportata" } diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index 3c45e803d..7caa1b170 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -697,5 +697,6 @@ "optional_name": "オプションの受信者名", "clearnet_link": "クリアネット リンク", "onion_link": "オニオンリンク", + "settings": "設定", "sell_monero_com_alert_content": "モネロの販売はまだサポートされていません" } diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 1535f54b2..327da90db 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -697,5 +697,6 @@ "optional_name": "선택적 수신자 이름", "clearnet_link": "클리어넷 링크", "onion_link": "양파 링크", + "settings": "설정", "sell_monero_com_alert_content": "지원되지 않습니다." } diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index 5020cc966..aaa19d5e7 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -697,5 +697,6 @@ "optional_name": "ရွေးချယ်နိုင်သော လက်ခံသူအမည်", "clearnet_link": "Clearnet လင့်ခ်", "onion_link": "ကြက်သွန်လင့်", + "settings": "ဆက်တင်များ", "sell_monero_com_alert_content": "Monero ရောင်းချခြင်းကို မပံ့ပိုးရသေးပါ။" } diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index 9b19f6e84..875c4e5c0 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -697,5 +697,6 @@ "optional_name": "Optionele naam ontvanger", "clearnet_link": "Clearnet-link", "onion_link": "Ui koppeling", + "settings": "Instellingen", "sell_monero_com_alert_content": "Het verkopen van Monero wordt nog niet ondersteund" } diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index cdc1c916a..b2b1fcfa5 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -697,5 +697,6 @@ "optional_name": "Opcjonalna nazwa odbiorcy", "clearnet_link": "łącze Clearnet", "onion_link": "Łącznik cebulowy", + "settings": "Ustawienia", "sell_monero_com_alert_content": "Sprzedaż Monero nie jest jeszcze obsługiwana" } diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index 86a256dcc..3ac194e7e 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -696,5 +696,6 @@ "optional_name": "Nome do destinatário opcional", "clearnet_link": "link clear net", "onion_link": "ligação de cebola", + "settings": "Configurações", "sell_monero_com_alert_content": "A venda de Monero ainda não é suportada" } diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index 7707e0003..d9d5e1d4f 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -697,5 +697,6 @@ "optional_name": "Необязательное имя получателя", "clearnet_link": "Клирнет ссылка", "onion_link": "Луковая ссылка", + "settings": "Настройки", "sell_monero_com_alert_content": "Продажа Monero пока не поддерживается" } diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index 01a05204e..4b400caea 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -695,5 +695,6 @@ "optional_name": "ชื่อผู้รับเพิ่มเติม", "clearnet_link": "ลิงค์เคลียร์เน็ต", "onion_link": "ลิงค์หัวหอม", + "settings": "การตั้งค่า", "sell_monero_com_alert_content": "ยังไม่รองรับการขาย Monero" } diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index 2df815175..b8fd76499 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -697,5 +697,6 @@ "optional_name": "İsteğe bağlı alıcı adı", "clearnet_link": "Net bağlantı", "onion_link": "soğan bağlantısı", + "settings": "ayarlar", "sell_monero_com_alert_content": "Monero satışı henüz desteklenmiyor" } diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index ee7c4e507..1b9995893 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -696,5 +696,6 @@ "optional_name": "Додаткове ім'я одержувача", "clearnet_link": "Посилання Clearnet", "onion_link": "Посилання на цибулю", + "settings": "Налаштування", "sell_monero_com_alert_content": "Продаж Monero ще не підтримується" } diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index fcaf66b6a..54c3c14a9 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -696,5 +696,6 @@ "optional_name": "可选收件人姓名", "clearnet_link": "明网链接", "onion_link": "洋葱链接", + "settings": "设置", "sell_monero_com_alert_content": "尚不支持出售门罗币" } diff --git a/scripts/macos/app_config.sh b/scripts/macos/app_config.sh new file mode 100755 index 000000000..231945659 --- /dev/null +++ b/scripts/macos/app_config.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +CAKEWALLET="cakewallet" +DIR=`pwd` + +if [ -z "$APP_MACOS_TYPE" ]; then + echo "Please set APP_MACOS_TYPE" + exit 1 +fi + +cd ../.. # go to root +cp -rf ./macos/Runner/InfoBase.plist ./macos/Runner/Info.plist +/usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier ${APP_MACOS_BUNDLE_ID}" ./macos/Runner/Info.plist +/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString ${APP_MACOS_VERSION}" ./macos/Runner/Info.plist +/usr/libexec/PlistBuddy -c "Set :CFBundleVersion ${APP_MACOS_BUILD_NUMBER}" ./macos/Runner/Info.plist + +# Fill entitlements Bundle ID +cp -rf ./macos/Runner/DebugProfileBase.entitlements ./macos/Runner/DebugProfile.entitlements +cp -rf ./macos/Runner/ReleaseBase.entitlements ./macos/Runner/Release.entitlements +sed -i '' "s/\${BUNDLE_ID}/${APP_MACOS_BUNDLE_ID}/g" ./macos/Runner/DebugProfile.entitlements +sed -i '' "s/\${BUNDLE_ID}/${APP_MACOS_BUNDLE_ID}/g" ./macos/Runner/Release.entitlements +CONFIG_ARGS="" + +case $APP_MACOS_TYPE in + $CAKEWALLET) + CONFIG_ARGS="--monero --bitcoin";; #--haven +esac + +cp -rf pubspec_description.yaml pubspec.yaml +flutter pub get +flutter pub run tool/generate_pubspec.dart +flutter pub get +flutter packages pub run tool/configure.dart $CONFIG_ARGS +cd $DIR \ No newline at end of file diff --git a/scripts/macos/app_env.sh b/scripts/macos/app_env.sh new file mode 100755 index 000000000..3fceeb94d --- /dev/null +++ b/scripts/macos/app_env.sh @@ -0,0 +1,39 @@ +#!/bin/sh + +APP_MACOS_NAME="" +APP_MACOS_VERSION="" +APP_MACOS_BUILD_VERSION="" +APP_MACOS_BUNDLE_ID="" + +CAKEWALLET="cakewallet" + +TYPES=($CAKEWALLET) +APP_MACOS_TYPE=$CAKEWALLET + +if [ -n "$1" ]; then + APP_MACOS_TYPE=$1 +fi + +CAKEWALLET_NAME="Cake Wallet" +CAKEWALLET_VERSION="1.0.1" +CAKEWALLET_BUILD_NUMBER=11 +CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" + +if ! [[ " ${TYPES[*]} " =~ " ${APP_MACOS_TYPE} " ]]; then + echo "Wrong app type." + exit 1 +fi + +case $APP_MACOS_TYPE in + $CAKEWALLET) + APP_MACOS_NAME=$CAKEWALLET_NAME + APP_MACOS_VERSION=$CAKEWALLET_VERSION + APP_MACOS_BUILD_NUMBER=$CAKEWALLET_BUILD_NUMBER + APP_MACOS_BUNDLE_ID=$CAKEWALLET_BUNDLE_ID;; +esac + +export APP_MACOS_TYPE +export APP_MACOS_NAME +export APP_MACOS_VERSION +export APP_MACOS_BUILD_NUMBER +export APP_MACOS_BUNDLE_ID diff --git a/scripts/macos/build_all.sh b/scripts/macos/build_all.sh new file mode 100755 index 000000000..4116704bf --- /dev/null +++ b/scripts/macos/build_all.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +./build_monero_all.sh \ No newline at end of file diff --git a/scripts/macos/build_boost_arm64.sh b/scripts/macos/build_boost_arm64.sh new file mode 100755 index 000000000..11f26040f --- /dev/null +++ b/scripts/macos/build_boost_arm64.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +. ./build_boost_common.sh +build_boost_arm64 \ No newline at end of file diff --git a/scripts/macos/build_boost_common.sh b/scripts/macos/build_boost_common.sh new file mode 100755 index 000000000..0c75be2bd --- /dev/null +++ b/scripts/macos/build_boost_common.sh @@ -0,0 +1,210 @@ +#!/bin/sh + +. ./config.sh + +# Boost combined + +BOOST_CXXFLAGS_COMBINED="-arch x86_64 -arch arm64" +BOOST_CFLAGS_COMBINED="-arch x86_64 -arch arm64" +BOOST_LINKFLAGS_COMBINED="-arch x86_64 -arch arm64" + +# Boost arm64 + +BOOST_CXXFLAGS_ARM64="-arch arm64" +BOOST_CFLAGS_ARM64="-arch arm64" +BOOST_LINKFLAGS_ARM64="-arch arm64" + +# Boost x86_64 + +BOOST_CXXFLAGS_X86_64="-arch x86_64" +BOOST_CFLAGS_X86_64="-arch x86_64" +BOOST_LINKFLAGS_X86_64="-arch x86_64" + +# Boost B2 arm64 + +BOOST_B2_CXXFLAGS_ARM_64="-arch arm64" +BOOST_B2_CFLAGS_ARM_64="-arch arm64" +BOOST_B2_LINKFLAGS_ARM_64="-arch arm64" +BOOST_B2_BUILD_DIR_ARM_64=macos-arm64 + +# Boost B2 x86_64 + +BOOST_B2_CXXFLAGS_X86_64="-arch x86_64" +BOOST_B2_CFLAGS_X86_64="-arch x86_64" +BOOST_B2_LINKFLAGS_X86_64="-arch x86_64" +BOOST_B2_BUILD_DIR_X86_64=macos-x86_64 + +build_boost_init_common() { + CXXFLAGS=$1 + CFLAGS=$2 + LINKFLAGS=$3 + BOOST_SRC_DIR=${EXTERNAL_MACOS_SOURCE_DIR}/boost_1_72_0 + BOOST_FILENAME=boost_1_72_0.tar.bz2 + BOOST_VERSION=1.72.0 + BOOST_FILE_PATH=${EXTERNAL_MACOS_SOURCE_DIR}/${BOOST_FILENAME} + BOOST_SHA256="59c9b274bc451cf91a9ba1dd2c7fdcaf5d60b1b3aa83f2c9fa143417cc660722" + + if [ ! -e "$BOOST_FILE_PATH" ]; then + curl -L http://downloads.sourceforge.net/project/boost/boost/${BOOST_VERSION}/${BOOST_FILENAME} > $BOOST_FILE_PATH + fi + + echo $BOOST_SHA256 *$BOOST_FILE_PATH | shasum -a 256 -c - || exit 1 + + cd $EXTERNAL_MACOS_SOURCE_DIR + rm -rf $BOOST_SRC_DIR + tar -xvf $BOOST_FILE_PATH -C $EXTERNAL_MACOS_SOURCE_DIR + cd $BOOST_SRC_DIR + ./bootstrap.sh --with-toolset=clang-darwin cxxflags="${CXXFLAGS}" cflags="${CFLAGS}" linkflags="${LINKFLAGS}" +} + +build_boost_init_arm64() { + CXXFLAGS="-arch arm64" + CFLAGS="-arch arm64" + LINKFLAGS="-arch arm64" + build_boost_init_common "${CXXFLAGS}" "${CFLAGS}" "${LINKFLAGS}" +} + +build_boost_init_x86_64() { + CXXFLAGS="-arch x86_64" + CFLAGS="-arch x86_64" + LINKFLAGS="-arch x86_64" + build_boost_init_common "${CXXFLAGS}" "${CFLAGS}" "${LINKFLAGS}" +} + +build_boost_init_universal() { + CXXFLAGS="-arch x86_64 -arch arm64" + CFLAGS="-arch x86_64 -arch arm64" + LINKFLAGS="-arch x86_64 -arch arm64" + build_boost_init_common "${CXXFLAGS}" "${CFLAGS}" "${LINKFLAGS}" +} + +build_boost_compile_common() { + ARCH=$1 + ABI=$2 + CXXFLAGS=$3 + CFLAGS=$4 + LINKFLAGS=$5 + FLAGS=$6 + BUILD_DIR=$7 + ./b2 toolset=clang-darwin target-os=darwin architecture="${ARCH}" cxxflags="${CXXFLAGS}" cflags="${CFLAGS}" linkflags="${LINKFLAGS}" abi="${ABI}" "${FLAGS}" -a \ + --with-chrono \ + --with-date_time \ + --with-filesystem \ + --with-program_options \ + --with-regex \ + --with-serialization \ + --with-system \ + --with-thread \ + --with-locale \ + --build-dir=$BUILD_DIR \ + --stagedir=${BUILD_DIR}/stage \ + link=static +} + +build_boost_compile_arm64() { + ARCH="arm" + ABI="aapcs" + CXXFLAGS="-arch arm64" + CFLAGS="-arch arm64" + LINKFLAGS="-arch arm64" + FLAGS="" + BUILD_DIR="macos-arm64" + build_boost_compile_common "${ARCH}" "${ABI}" "${CXXFLAGS}" "${CFLAGS}" "${LINKFLAGS}" "${FLAGS}" "${BUILD_DIR}" +} + +build_boost_compile_x86_64() { + ARCH="x86" + ABI="sysv" + CXXFLAGS="-arch x86_64" + CFLAGS="-arch x86_64" + LINKFLAGS="-arch x86_64" + FLAGS="binary-format=mach-o" + BUILD_DIR="macos-x86_64" + build_boost_compile_common "${ARCH}" "${ABI}" "${CXXFLAGS}" "${CFLAGS}" "${LINKFLAGS}" "${FLAGS}" "${BUILD_DIR}" +} + +build_boost_compile_universal() { + ARCHES=(arm x86) + for ARCH in ${ARCHES[@]}; do + ABI="" + CXXFLAGS="" + CFLAGS="" + LINKFLAGS="" + FLAGS="" + BUILD_DIR="" + + case $ARCH in + arm) + ABI="aapcs" + CXXFLAGS="-arch arm64" + CFLAGS="-arch arm64" + LINKFLAGS="-arch arm64" + FLAGS="" + BUILD_DIR="macos-arm64";; + x86) + ABI="sysv" + CXXFLAGS="-arch x86_64" + CFLAGS="-arch x86_64" + LINKFLAGS="-arch x86_64" + FLAGS="binary-format=mach-o" + BUILD_DIR="macos-x86_64" + esac + + build_boost_compile_common "${ARCH}" "${ABI}" "${CXXFLAGS}" "${CFLAGS}" "${LINKFLAGS}" "${FLAGS}" "${BUILD_DIR}" + done +} + +build_boost_install_common() { + ARCH=$1 + LIB_DIR="" + mkdir $EXTERNAL_MACOS_LIB_DIR + mkdir $EXTERNAL_MACOS_INCLUDE_DIR + + case $ARCH in + arm64) LIB_DIR="${BOOST_B2_BUILD_DIR_ARM_64}/stage/lib";; + x86_64) LIB_DIR="${BOOST_B2_BUILD_DIR_X86_64}/stage/lib";; + *) LIB_DIR="lib";; + esac + + cp -r ${LIB_DIR}/*.a ${EXTERNAL_MACOS_LIB_DIR} + cp -r boost ${EXTERNAL_MACOS_INCLUDE_DIR} +} + +build_boost_install_arm64() { + ARCH="arm64" + build_boost_install_common $ARCH +} + +build_boost_install_x86_64() { + ARCH="x86_64" + build_boost_install_common $ARCH +} + +build_boost_install_universal() { + mkdir lib + + for blib in ${BOOST_B2_BUILD_DIR_ARM_64}/stage/lib/*.a; do + lipo -create -arch arm64 $blib -arch x86_64 ${BOOST_B2_BUILD_DIR_X86_64}/stage/lib/$(basename $blib) -output lib/$(basename $blib); + done + + cp -r lib/* ${EXTERNAL_MACOS_LIB_DIR} + cp -r boost ${EXTERNAL_MACOS_INCLUDE_DIR} +} + +build_boost_arm64() { + build_boost_init_arm64 + build_boost_compile_arm64 + build_boost_install_arm64 +} + +build_boost_x86_64() { + build_boost_init_x86_64 + build_boost_compile_x86_64 + build_boost_install_x86_64 +} + +build_boost_universal() { + build_boost_init_universal + build_boost_compile_universal + build_boost_install_universal +} \ No newline at end of file diff --git a/scripts/macos/build_boost_universal.sh b/scripts/macos/build_boost_universal.sh new file mode 100755 index 000000000..0b5945aec --- /dev/null +++ b/scripts/macos/build_boost_universal.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +. ./build_boost_common.sh +build_boost_universal \ No newline at end of file diff --git a/scripts/macos/build_boost_x86_64.sh b/scripts/macos/build_boost_x86_64.sh new file mode 100755 index 000000000..a697b7027 --- /dev/null +++ b/scripts/macos/build_boost_x86_64.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +. ./build_boost_common.sh +build_boost_x86_64 \ No newline at end of file diff --git a/scripts/macos/build_expat.sh b/scripts/macos/build_expat.sh new file mode 100755 index 000000000..0c5857907 --- /dev/null +++ b/scripts/macos/build_expat.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +. ./config.sh + +EXPAT_VERSION=R_2_4_8 +EXPAT_HASH="3bab6c09bbe8bf42d84b81563ddbcf4cca4be838" +EXPAT_SRC_DIR=${EXTERNAL_MACOS_SOURCE_DIR}/libexpat + +git clone https://github.com/libexpat/libexpat.git -b ${EXPAT_VERSION} ${EXPAT_SRC_DIR} +cd $EXPAT_SRC_DIR +test `git rev-parse HEAD` = ${EXPAT_HASH} || exit 1 +cd $EXPAT_SRC_DIR/expat + +./buildconf.sh +./configure --enable-static --disable-shared --prefix=${EXTERNAL_MACOS_DIR} +make +make install \ No newline at end of file diff --git a/scripts/macos/build_haven.sh b/scripts/macos/build_haven.sh new file mode 100755 index 000000000..fb67da442 --- /dev/null +++ b/scripts/macos/build_haven.sh @@ -0,0 +1,50 @@ +#!/bin/sh + +. ./config.sh + +HAVEN_URL="https://github.com/haven-protocol-org/haven-main.git" +HAVEN_DIR_PATH="${EXTERNAL_MACOS_SOURCE_DIR}/haven" +HAVEN_VERSION=tags/v3.0.0 +BUILD_TYPE=release +PREFIX=${EXTERNAL_MACOS_DIR} +DEST_LIB_DIR=${EXTERNAL_MACOS_LIB_DIR}/haven +DEST_INCLUDE_DIR=${EXTERNAL_MACOS_INCLUDE_DIR}/haven +ARCH=`uname -m` + +echo "Cloning haven from - $HAVEN_URL to - $HAVEN_DIR_PATH" +git clone $HAVEN_URL $HAVEN_DIR_PATH +cd $HAVEN_DIR_PATH +git checkout $HAVEN_VERSION +git submodule update --init --force +mkdir -p build +cd .. + +ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +if [ -z $INSTALL_PREFIX ]; then + INSTALL_PREFIX=${ROOT_DIR}/haven +fi + +mkdir -p $DEST_LIB_DIR +mkdir -p $DEST_INCLUDE_DIR + +echo "Building MACOS ${ARCH}" +export CMAKE_INCLUDE_PATH="${PREFIX}/include" +export CMAKE_LIBRARY_PATH="${PREFIX}/lib" +rm -rf haven/build > /dev/null + +mkdir -p haven/build/${BUILD_TYPE} +pushd haven/build/${BUILD_TYPE} +cmake -DARCH=${ARCH} \ + -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \ + -DSTATIC=ON \ + -DBUILD_GUI_DEPS=ON \ + -DINSTALL_VENDORED_LIBUNBOUND=ON \ + -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} \ + -DUSE_DEVICE_TREZOR=OFF \ + ../.. +make -j4 && make install +find . -path ./lib -prune -o -name '*.a' -exec cp '{}' lib \; +cp -r ./lib/* $DEST_LIB_DIR +cp ../../src/wallet/api/wallet2_api.h $DEST_INCLUDE_DIR +popd + diff --git a/scripts/macos/build_monero.sh b/scripts/macos/build_monero.sh new file mode 100755 index 000000000..4dc9a9137 --- /dev/null +++ b/scripts/macos/build_monero.sh @@ -0,0 +1,54 @@ +#!/bin/sh + +. ./config.sh + +MONERO_URL="https://github.com/cake-tech/monero.git" +MONERO_DIR_PATH="${EXTERNAL_MACOS_SOURCE_DIR}/monero" +MONERO_VERSION=release-v0.18.0.0 +BUILD_TYPE=release +PREFIX=${EXTERNAL_MACOS_DIR} +DEST_LIB_DIR=${EXTERNAL_MACOS_LIB_DIR}/monero +DEST_INCLUDE_DIR=${EXTERNAL_MACOS_INCLUDE_DIR}/monero +ARCH=`uname -m` + +echo "Cloning monero from - $MONERO_URL to - $MONERO_DIR_PATH" +git clone $MONERO_URL $MONERO_DIR_PATH +cd $MONERO_DIR_PATH +git checkout $MONERO_VERSION +git submodule update --init --force +mkdir -p build +cd .. + +mkdir -p $DEST_LIB_DIR +mkdir -p $DEST_INCLUDE_DIR + +ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +if [ -z $INSTALL_PREFIX ]; then + INSTALL_PREFIX=${ROOT_DIR}/monero +fi + +echo "Building MACOS ${ARCH}" +export CMAKE_INCLUDE_PATH="${PREFIX}/include" +export CMAKE_LIBRARY_PATH="${PREFIX}/lib" +rm -r monero/build > /dev/null + +if [ "${ARCH}" == "x86_64" ]; then + ARCH="x86-64" +fi + +mkdir -p monero/build/${BUILD_TYPE} +pushd monero/build/${BUILD_TYPE} +cmake -DARCH=${ARCH} \ + -DBUILD_64=ON \ + -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \ + -DSTATIC=ON \ + -DBUILD_GUI_DEPS=ON \ + -DUNBOUND_INCLUDE_DIR=${EXTERNAL_MACOS_INCLUDE_DIR} \ + -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} \ + -DUSE_DEVICE_TREZOR=OFF \ + ../.. +make wallet_api -j4 +find . -path ./lib -prune -o -name '*.a' -exec cp '{}' lib \; +cp -r ./lib/* $DEST_LIB_DIR +cp ../../src/wallet/api/wallet2_api.h $DEST_INCLUDE_DIR +popd diff --git a/scripts/macos/build_monero_all.sh b/scripts/macos/build_monero_all.sh new file mode 100755 index 000000000..f7e55909b --- /dev/null +++ b/scripts/macos/build_monero_all.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +ARCH=`uname -m` + +. ./config.sh + +case $ARCH in + arm64) + ./build_openssl_arm64.sh + ./build_boost_arm64.sh;; + x86_64) + ./build_openssl_x86_64.sh + ./build_boost_x86_64.sh;; +esac + +./build_zmq.sh +./build_expat.sh +./build_unbound.sh +./build_sodium.sh +./build_monero.sh \ No newline at end of file diff --git a/scripts/macos/build_openssl_arm64.sh b/scripts/macos/build_openssl_arm64.sh new file mode 100755 index 000000000..fd8d7b2f5 --- /dev/null +++ b/scripts/macos/build_openssl_arm64.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +. ./build_openssl_common.sh +build_openssl_arm64 \ No newline at end of file diff --git a/scripts/macos/build_openssl_common.sh b/scripts/macos/build_openssl_common.sh new file mode 100755 index 000000000..fd07312fa --- /dev/null +++ b/scripts/macos/build_openssl_common.sh @@ -0,0 +1,116 @@ +#!/bin/sh + +. ./config.sh + +OPEN_SSL_DIR_NAME="OpenSSL" +OPEN_SSL_x86_64_DIR_NAME="${OPEN_SSL_DIR_NAME}-x86_64" +OPEN_SSL_ARM_DIR_NAME="${OPEN_SSL_DIR_NAME}-arm" +OPEN_SSL_X86_64_DIR_PATH="${EXTERNAL_MACOS_SOURCE_DIR}/${OPEN_SSL_x86_64_DIR_NAME}" +OPEN_SSL_ARM_DIR_PATH="${EXTERNAL_MACOS_SOURCE_DIR}/${OPEN_SSL_ARM_DIR_NAME}" + +build_openssl_init_common() { + DIR=$1 + # Use 1.1.1s becasue of https://github.com/openssl/openssl/issues/18720 + OPENSSL_VERSION="1.1.1s" + + echo "============================ OpenSSL ============================" + + cd $EXTERNAL_MACOS_SOURCE_DIR + curl -O https://www.openssl.org/source/openssl-$OPENSSL_VERSION.tar.gz + tar -xvzf openssl-$OPENSSL_VERSION.tar.gz + rm -rf $DIR + rm -rf $OPEN_SSL_DIR_PATH + mv openssl-$OPENSSL_VERSION $DIR + tar -xvzf openssl-$OPENSSL_VERSION.tar.gz + mv openssl-$OPENSSL_VERSION $OPEN_SSL_ARM_DIR_NAME +} + +build_openssl_init_arm64() { + DIR=$OPEN_SSL_ARM_DIR_PATH + build_openssl_init_common ${DIR} +} + +build_openssl_init_x86_64() { + DIR=$OPEN_SSL_X86_64_DIR_PATH + build_openssl_init_common ${DIR} +} + +build_openssl_compile_common() { + ARCH=$1 + DIR="" + XARCH="" + case $ARCH in + arm64) + DIR=$OPEN_SSL_ARM_DIR_PATH + XARCH="darwin64-arm64-cc";; + x86_64) + DIR=$OPEN_SSL_X86_64_DIR_PATH + XARCH="darwin64-x86_64-cc";; + esac + + echo "Build OpenSSL for ${ARCH}" + cd $DIR + ./Configure $XARCH + make + +} + +build_openssl_compile_arm64() { + ARCH=arm64 + build_openssl_compile_common "${ARCH}" +} + +build_openssl_compile_x86_64() { + ARCH=x86_64 + build_openssl_compile_common "${ARCH}" +} + +build_openssl_install_common() { + DIR=$1 + mv ${DIR}/include/* $EXTERNAL_MACOS_INCLUDE_DIR + mv ${DIR}/libcrypto.a ${EXTERNAL_MACOS_LIB_DIR}/libcrypto.a + mv ${DIR}/libssl.a ${EXTERNAL_MACOS_LIB_DIR}/libssl.a +} + +build_openssl_install_arm64() { + build_openssl_install_common "${OPEN_SSL_ARM_DIR_PATH}" +} + +build_openssl_install_x86_64() { + build_openssl_install_common "${OPEN_SSL_X86_64_DIR_PATH}" +} + +build_openssl_install_universal() { + OPEN_SSL_DIR_PATH="${EXTERNAL_MACOS_SOURCE_DIR}/${OPEN_SSL_DIR_NAME}" + mv ${OPEN_SSL_ARM_DIR_PATH}/include/* $OPEN_SSL_DIR_PATH/include + build_openssl_install_common "${OPEN_SSL_DIR_PATH}" +} + +build_openssl_arm64() { + build_openssl_init_arm64 + build_openssl_compile_arm64 + build_openssl_install_arm64 +} + +build_openssl_x86_64() { + build_openssl_init_x86_64 + build_openssl_compile_x86_64 + build_openssl_install_x86_64 +} + +build_openssl_combine() { + OPEN_SSL_DIR_PATH="${EXTERNAL_MACOS_SOURCE_DIR}/${OPEN_SSL_DIR_NAME}" + echo "Create universal bin" + mkdir -p $OPEN_SSL_DIR_PATH/include + lipo -create ${OPEN_SSL_ARM_DIR_PATH}/libcrypto.a ${OPEN_SSL_X86_64_DIR_PATH}/libcrypto.a -output ${OPEN_SSL_DIR_PATH}/libcrypto.a + lipo -create ${OPEN_SSL_ARM_DIR_PATH}/libssl.a ${OPEN_SSL_X86_64_DIR_PATH}/libssl.a -output ${OPEN_SSL_DIR_PATH}/libssl.a +} + +build_openssl_universal() { + build_openssl_init_arm64 + build_openssl_compile_arm64 + build_openssl_init_x86_64 + build_openssl_compile_x86_64 + build_openssl_combine + build_openssl_install_universal +} \ No newline at end of file diff --git a/scripts/macos/build_openssl_universal.sh b/scripts/macos/build_openssl_universal.sh new file mode 100755 index 000000000..ba7fc5e4a --- /dev/null +++ b/scripts/macos/build_openssl_universal.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +. ./build_openssl_common.sh +build_openssl_universal \ No newline at end of file diff --git a/scripts/macos/build_openssl_x86_64.sh b/scripts/macos/build_openssl_x86_64.sh new file mode 100755 index 000000000..6ef326e8a --- /dev/null +++ b/scripts/macos/build_openssl_x86_64.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +. ./build_openssl_common.sh +build_openssl_x86_64 \ No newline at end of file diff --git a/scripts/macos/build_sodium.sh b/scripts/macos/build_sodium.sh new file mode 100755 index 000000000..b50d3c2ee --- /dev/null +++ b/scripts/macos/build_sodium.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +. ./config.sh + +SODIUM_PATH="${EXTERNAL_MACOS_SOURCE_DIR}/libsodium" +SODIUM_URL="https://github.com/jedisct1/libsodium.git" + +echo "============================ SODIUM ============================" + +echo "Cloning SODIUM from - $SODIUM_URL" +git clone $SODIUM_URL $SODIUM_PATH --branch stable +cd $SODIUM_PATH +./dist-build/osx.sh + +mv ${SODIUM_PATH}/libsodium-osx/include/* $EXTERNAL_MACOS_INCLUDE_DIR +mv ${SODIUM_PATH}/libsodium-osx/lib/* $EXTERNAL_MACOS_LIB_DIR \ No newline at end of file diff --git a/scripts/macos/build_unbound.sh b/scripts/macos/build_unbound.sh new file mode 100755 index 000000000..ed115d464 --- /dev/null +++ b/scripts/macos/build_unbound.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +. ./config.sh + +UNBOUND_VERSION=release-1.16.2 +UNBOUND_HASH="cbed768b8ff9bfcf11089a5f1699b7e5707f1ea5" +UNBOUND_URL="https://www.nlnetlabs.nl/downloads/unbound/unbound-${UNBOUND_VERSION}.tar.gz" +UNBOUND_DIR_PATH="${EXTERNAL_MACOS_SOURCE_DIR}/unbound-1.16.2" + +echo "============================ Unbound ============================" +rm -rf ${UNBOUND_DIR_PATH} +git clone https://github.com/NLnetLabs/unbound.git -b ${UNBOUND_VERSION} ${UNBOUND_DIR_PATH} +cd $UNBOUND_DIR_PATH +test `git rev-parse HEAD` = ${UNBOUND_HASH} || exit 1 + +./configure --prefix="${EXTERNAL_MACOS_DIR}" \ + --with-ssl="${EXTERNAL_MACOS_DIR}" \ + --with-libexpat="${EXTERNAL_MACOS_DIR}" \ + --enable-static \ + --disable-shared \ + --disable-flto +make +make install \ No newline at end of file diff --git a/scripts/macos/build_zmq.sh b/scripts/macos/build_zmq.sh new file mode 100755 index 000000000..dd5623f06 --- /dev/null +++ b/scripts/macos/build_zmq.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +. ./config.sh + +ZMQ_PATH="${EXTERNAL_MACOS_SOURCE_DIR}/libzmq" +ZMQ_URL="https://github.com/zeromq/libzmq.git" + +echo "============================ ZMQ ============================" + +echo "Cloning ZMQ from - $ZMQ_URL" +git clone $ZMQ_URL $ZMQ_PATH +cd $ZMQ_PATH +mkdir cmake-build +cd cmake-build +cmake .. -DCMAKE_INSTALL_PREFIX="${EXTERNAL_MACOS_DIR}" +make +make install \ No newline at end of file diff --git a/scripts/macos/cakewallet.sh b/scripts/macos/cakewallet.sh new file mode 100755 index 000000000..5f591327e --- /dev/null +++ b/scripts/macos/cakewallet.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +. ./app_env.sh "cakewallet" +. ./app_config.sh \ No newline at end of file diff --git a/scripts/macos/config.sh b/scripts/macos/config.sh new file mode 100755 index 000000000..493aaa6c3 --- /dev/null +++ b/scripts/macos/config.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +export MACOS_SCRIPTS_DIR=`pwd` +export CW_ROOT=${MACOS_SCRIPTS_DIR}/../.. +export EXTERNAL_DIR=${CW_ROOT}/cw_shared_external/ios/External +export EXTERNAL_MACOS_DIR=${EXTERNAL_DIR}/macos +export EXTERNAL_MACOS_SOURCE_DIR=${EXTERNAL_MACOS_DIR}/sources +export EXTERNAL_MACOS_LIB_DIR=${EXTERNAL_MACOS_DIR}/lib +export EXTERNAL_MACOS_INCLUDE_DIR=${EXTERNAL_MACOS_DIR}/include + +mkdir -p $EXTERNAL_MACOS_LIB_DIR +mkdir -p $EXTERNAL_MACOS_INCLUDE_DIR +mkdir -p $EXTERNAL_MACOS_SOURCE_DIR \ No newline at end of file diff --git a/scripts/macos/gen_arm64.sh b/scripts/macos/gen_arm64.sh new file mode 100755 index 000000000..ca604bb43 --- /dev/null +++ b/scripts/macos/gen_arm64.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +. ./gen_common.sh + +gen "arm64" \ No newline at end of file diff --git a/scripts/macos/gen_common.sh b/scripts/macos/gen_common.sh new file mode 100755 index 000000000..62f4effab --- /dev/null +++ b/scripts/macos/gen_common.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +gen_podspec() { + ARCH=$1 + CW_PLUGIN_DIR="`pwd`/../../cw_monero/macos" + BASE_FILENAME="cw_monero_base.podspec" + BASE_FILE_PATH="${CW_PLUGIN_DIR}/${BASE_FILENAME}" + DEFAULT_FILENAME="cw_monero.podspec" + DEFAULT_FILE_PATH="${CW_PLUGIN_DIR}/${DEFAULT_FILENAME}" + rm -f $DEFAULT_FILE_PATH + cp $BASE_FILE_PATH $DEFAULT_FILE_PATH + sed -i '' "s/#___VALID_ARCHS___#/${ARCH}/g" $DEFAULT_FILE_PATH +} + +gen_project() { + ARCH=$1 + CW_DIR="`pwd`/../../macos/Runner.xcodeproj" + BASE_FILENAME="project_base.pbxproj" + BASE_FILE_PATH="${CW_DIR}/${BASE_FILENAME}" + DEFAULT_FILENAME="project.pbxproj" + DEFAULT_FILE_PATH="${CW_DIR}/${DEFAULT_FILENAME}" + rm -f $DEFAULT_FILE_PATH + cp $BASE_FILE_PATH $DEFAULT_FILE_PATH + sed -i '' "s/ARCHS =.*/ARCHS = ${ARCH};/g" $DEFAULT_FILE_PATH +} + +gen() { + ARCH=$1 + gen_podspec "${ARCH}" + gen_project "${ARCH}" +} \ No newline at end of file diff --git a/scripts/macos/gen_universal.sh b/scripts/macos/gen_universal.sh new file mode 100755 index 000000000..6056053a5 --- /dev/null +++ b/scripts/macos/gen_universal.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +. ./gen_common.sh + +gen "arm64, x86_64" \ No newline at end of file diff --git a/scripts/macos/gen_x86_64.sh b/scripts/macos/gen_x86_64.sh new file mode 100755 index 000000000..c6988d8f0 --- /dev/null +++ b/scripts/macos/gen_x86_64.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +. ./gen_common.sh + +gen "x86_64" \ No newline at end of file diff --git a/scripts/macos/setup.sh b/scripts/macos/setup.sh new file mode 100755 index 000000000..fea2a6c8d --- /dev/null +++ b/scripts/macos/setup.sh @@ -0,0 +1,40 @@ +#!/bin/sh + +. ./config.sh + +cd $EXTERNAL_MACOS_LIB_DIR + + +# LIBRANDOMX_PATH=${EXTERNAL_MACOS_LIB_DIR}/monero/librandomx.a + +# if [ -f "$LIBRANDOMX_PATH" ]; then +# cp $LIBRANDOMX_PATH ./haven +# fi + +libtool -static -o libboost.a ./libboost_*.a +libtool -static -o libmonero.a ./monero/*.a + +# CW_HAVEN_EXTERNAL_LIB=../../../../../cw_haven/macos/External/macos/lib +# CW_HAVEN_EXTERNAL_INCLUDE=../../../../../cw_haven/macos/External/macos/include +CW_MONERO_EXTERNAL_LIB=../../../../../cw_monero/macos/External/macos/lib +CW_MONERO_EXTERNAL_INCLUDE=../../../../../cw_monero/macos/External/macos/include + +# mkdir -p $CW_HAVEN_EXTERNAL_INCLUDE +mkdir -p $CW_MONERO_EXTERNAL_INCLUDE +# mkdir -p $CW_HAVEN_EXTERNAL_LIB +mkdir -p $CW_MONERO_EXTERNAL_LIB + +# ln ./libboost.a ${CW_HAVEN_EXTERNAL_LIB}/libboost.a +# ln ./libcrypto.a ${CW_HAVEN_EXTERNAL_LIB}/libcrypto.a +# ln ./libssl.a ${CW_HAVEN_EXTERNAL_LIB}/libssl.a +# ln ./libsodium.a ${CW_HAVEN_EXTERNAL_LIB}/libsodium.a +# cp ./libhaven.a $CW_HAVEN_EXTERNAL_LIB +# cp ../include/haven/* $CW_HAVEN_EXTERNAL_INCLUDE + +ln ./libboost.a ${CW_MONERO_EXTERNAL_LIB}/libboost.a +ln ./libcrypto.a ${CW_MONERO_EXTERNAL_LIB}/libcrypto.a +ln ./libssl.a ${CW_MONERO_EXTERNAL_LIB}/libssl.a +ln ./libsodium.a ${CW_MONERO_EXTERNAL_LIB}/libsodium.a +ln ./libunbound.a ${CW_MONERO_EXTERNAL_LIB}/libunbound.a +cp ./libmonero.a $CW_MONERO_EXTERNAL_LIB +cp ../include/monero/* $CW_MONERO_EXTERNAL_INCLUDE \ No newline at end of file diff --git a/tool/utils/secret_key.dart b/tool/utils/secret_key.dart index e4e59819f..621ab1cfc 100644 --- a/tool/utils/secret_key.dart +++ b/tool/utils/secret_key.dart @@ -13,6 +13,7 @@ class SecretKey { SecretKey('backupSalt', () => hex.encode(encrypt.Key.fromSecureRandom(8).bytes)), SecretKey('backupKeychainSalt', () => hex.encode(encrypt.Key.fromSecureRandom(12).bytes)), SecretKey('changeNowApiKey', () => ''), + SecretKey('changeNowApiKeyDesktop', () => ''), SecretKey('wyreSecretKey', () => ''), SecretKey('wyreApiKey', () => ''), SecretKey('wyreAccountId', () => ''), @@ -21,6 +22,7 @@ class SecretKey { SecretKey('sideShiftAffiliateId', () => ''), SecretKey('sideShiftApiKey', () => ''), SecretKey('simpleSwapApiKey', () => ''), + SecretKey('simpleSwapApiKeyDesktop', () => ''), SecretKey('anypayToken', () => ''), SecretKey('onramperApiKey', () => ''), SecretKey('ioniaClientId', () => ''),