Merge pull request #583 from cypherstack/ui-fixes

Various fixes
This commit is contained in:
Diego Salazar 2023-06-08 14:55:36 -06:00 committed by GitHub
commit 9528ee4389
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 5055 additions and 471 deletions

View file

@ -147,7 +147,7 @@ Set up Ubuntu 20.04 in WSL2. Follow the entire Linux host section to get set up
<!-- TODO: script the copying or installation of libraries from WSL2 to the parent Windows host -->
### Install Flutter on Windows host
Install Flutter 3.7.12 on your Windows host (not in WSL2) by following these instructions: https://docs.flutter.dev/get-started/install/windows or by running `scripts/windows/deps.ps1`. You may still have to add `C:\development\flutter\bin` to PATH before proceeding, even if you ran `deps.ps1`. Run `flutter doctor` in PowerShell to confirm its installation.
Install Flutter 3.10.3 on your Windows host (not in WSL2) by following these instructions: https://docs.flutter.dev/get-started/install/windows or by running `scripts/windows/deps.ps1`. You may still have to add `C:\development\flutter\bin` to PATH before proceeding, even if you ran `deps.ps1`. Run `flutter doctor` in PowerShell to confirm its installation.
### Dependencies
Install the Windows SDK: https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/ You may need to install the [Windows 10 SDK](https://developer.microsoft.com/en-us/windows/downloads/sdk-archive/), which can be installed [by Visual Studio](https://stackoverflow.com/a/73923899) (`Tools > Get Tools and Features... > Modify > Individual Components > Windows 10 SDK`).

View file

@ -1493,8 +1493,12 @@ class StackTheme {
late final ThemeAssetsV2? assetsV2;
// cheat build runner into adding this at end of property id list in isar
@Name("zAssetsV3")
late final ThemeAssetsV3? assetsV3;
@ignore
IThemeAssets get assets => assetsV2 ?? assetsV1!;
IThemeAssets get assets => assetsV3 ?? assetsV2 ?? assetsV1!;
// ===========================================================================
@ -1517,13 +1521,20 @@ class StackTheme {
themeId: json["id"] as String,
)
: null
..assetsV2 = version >= 2
..assetsV2 = version == 2
? ThemeAssetsV2.fromJson(
json: Map<String, dynamic>.from(json["assets"] as Map),
applicationThemesDirectoryPath: applicationThemesDirectoryPath,
themeId: json["id"] as String,
)
: null
..assetsV3 = version >= 3
? ThemeAssetsV3.fromJson(
json: Map<String, dynamic>.from(json["assets"] as Map),
applicationThemesDirectoryPath: applicationThemesDirectoryPath,
themeId: json["id"] as String,
)
: null
..themeId = json["id"] as String
..name = json["name"] as String
..brightnessString = json["brightness"] as String
@ -1939,10 +1950,6 @@ class ThemeAssets implements IThemeAssets {
@override
late final String? background;
@override
@ignore
String? get walletSummaryCardBackground => null;
ThemeAssets();
factory ThemeAssets.fromJson({
@ -2104,8 +2111,6 @@ class ThemeAssetsV2 implements IThemeAssets {
late final String? loadingGif;
@override
late final String? background;
@override
late final String? walletSummaryCardBackground;
late final String coinPlaceholder;
@ -2200,10 +2205,6 @@ class ThemeAssetsV2 implements IThemeAssets {
: null
..background = json["background"] is String
? "$applicationThemesDirectoryPath/$themeId/assets/${json["background"] as String}"
: null
..walletSummaryCardBackground = json["walletSummaryCardBackground"]
is String
? "$applicationThemesDirectoryPath/$themeId/assets/${json["walletSummaryCardBackground"] as String}"
: null;
}
@ -2254,11 +2255,235 @@ class ThemeAssetsV2 implements IThemeAssets {
'txExchangeFailed: $txExchangeFailed, '
'loadingGif: $loadingGif, '
'background: $background, '
'walletSummaryCardBackground: $walletSummaryCardBackground, '
'coinPlaceholder: $coinPlaceholder, '
'coinIcons: $coinIcons, '
'coinImages: $coinImages, '
'coinSecondaryImages: $coinSecondaryImages'
'coinSecondaryImages: $coinSecondaryImages, '
')';
}
}
@Embedded(inheritance: false)
class ThemeAssetsV3 implements IThemeAssets {
@override
late final String bellNew;
@override
late final String buy;
@override
late final String exchange;
@override
late final String personaIncognito;
@override
late final String personaEasy;
@override
late final String stack;
@override
late final String stackIcon;
@override
late final String receive;
@override
late final String receivePending;
@override
late final String receiveCancelled;
@override
late final String send;
@override
late final String sendPending;
@override
late final String sendCancelled;
@override
late final String themeSelector;
@override
late final String themePreview;
@override
late final String txExchange;
@override
late final String txExchangePending;
@override
late final String txExchangeFailed;
@override
late final String? loadingGif;
@override
late final String? background;
late final String coinPlaceholder;
// Added some future proof params in case we want to add anything else
// This should provide some buffer in stead of creating assetsV4 etc
@Name("otherStringParam1")
late final String? dummy1;
@Name("otherStringParam2")
late final String? dummy2;
@Name("otherStringParam3")
late final String? dummy3;
@ignore
Map<Coin, String> get coinIcons => _coinIcons ??= parseCoinAssetsString(
coinIconsString,
placeHolder: coinPlaceholder,
);
@ignore
Map<Coin, String>? _coinIcons;
late final String coinIconsString;
@ignore
Map<Coin, String> get coinImages => _coinImages ??= parseCoinAssetsString(
coinImagesString,
placeHolder: coinPlaceholder,
);
@ignore
Map<Coin, String>? _coinImages;
late final String coinImagesString;
@ignore
Map<Coin, String> get coinSecondaryImages =>
_coinSecondaryImages ??= parseCoinAssetsString(
coinSecondaryImagesString,
placeHolder: coinPlaceholder,
);
@ignore
Map<Coin, String>? _coinSecondaryImages;
late final String coinSecondaryImagesString;
@ignore
Map<Coin, String>? get coinCardImages =>
_coinCardImages ??= coinCardImagesString == null
? null
: parseCoinAssetsString(
coinCardImagesString!,
placeHolder: coinPlaceholder,
);
@ignore
Map<Coin, String>? _coinCardImages;
late final String? coinCardImagesString;
ThemeAssetsV3();
factory ThemeAssetsV3.fromJson({
required Map<String, dynamic> json,
required String applicationThemesDirectoryPath,
required String themeId,
}) {
return ThemeAssetsV3()
..bellNew =
"$applicationThemesDirectoryPath/$themeId/assets/${json["bell_new"] as String}"
..buy =
"$applicationThemesDirectoryPath/$themeId/assets/${json["buy"] as String}"
..exchange =
"$applicationThemesDirectoryPath/$themeId/assets/${json["exchange"] as String}"
..personaIncognito =
"$applicationThemesDirectoryPath/$themeId/assets/${json["persona_incognito"] as String}"
..personaEasy =
"$applicationThemesDirectoryPath/$themeId/assets/${json["persona_easy"] as String}"
..stack =
"$applicationThemesDirectoryPath/$themeId/assets/${json["stack"] as String}"
..stackIcon =
"$applicationThemesDirectoryPath/$themeId/assets/${json["stack_icon"] as String}"
..receive =
"$applicationThemesDirectoryPath/$themeId/assets/${json["receive"] as String}"
..receivePending =
"$applicationThemesDirectoryPath/$themeId/assets/${json["receive_pending"] as String}"
..receiveCancelled =
"$applicationThemesDirectoryPath/$themeId/assets/${json["receive_cancelled"] as String}"
..send =
"$applicationThemesDirectoryPath/$themeId/assets/${json["send"] as String}"
..sendPending =
"$applicationThemesDirectoryPath/$themeId/assets/${json["send_pending"] as String}"
..sendCancelled =
"$applicationThemesDirectoryPath/$themeId/assets/${json["send_cancelled"] as String}"
..themeSelector =
"$applicationThemesDirectoryPath/$themeId/assets/${json["theme_selector"] as String}"
..themePreview =
"$applicationThemesDirectoryPath/$themeId/assets/${json["theme_preview"] as String}"
..txExchange =
"$applicationThemesDirectoryPath/$themeId/assets/${json["tx_exchange"] as String}"
..txExchangePending =
"$applicationThemesDirectoryPath/$themeId/assets/${json["tx_exchange_pending"] as String}"
..txExchangeFailed =
"$applicationThemesDirectoryPath/$themeId/assets/${json["tx_exchange_failed"] as String}"
..coinPlaceholder =
"$applicationThemesDirectoryPath/$themeId/assets/${json["coin_placeholder"] as String}"
..coinIconsString = createCoinAssetsString(
"$applicationThemesDirectoryPath/$themeId/assets",
Map<String, dynamic>.from(json["coins"]["icons"] as Map),
)
..coinImagesString = createCoinAssetsString(
"$applicationThemesDirectoryPath/$themeId/assets",
Map<String, dynamic>.from(json["coins"]["images"] as Map),
)
..coinSecondaryImagesString = createCoinAssetsString(
"$applicationThemesDirectoryPath/$themeId/assets",
Map<String, dynamic>.from(json["coins"]["secondaries"] as Map),
)
..coinCardImagesString = json["coins"]["cards"] is Map
? createCoinAssetsString(
"$applicationThemesDirectoryPath/$themeId/assets",
Map<String, dynamic>.from(json["coins"]["cards"] as Map),
)
: null
..loadingGif = json["loading_gif"] is String
? "$applicationThemesDirectoryPath/$themeId/assets/${json["loading_gif"] as String}"
: null
..background = json["background"] is String
? "$applicationThemesDirectoryPath/$themeId/assets/${json["background"] as String}"
: null
..dummy1 = null
..dummy2 = null
..dummy3 = null;
}
static String createCoinAssetsString(String path, Map<String, dynamic> json) {
final Map<String, dynamic> map = {};
for (final entry in json.entries) {
map[entry.key] = "$path/${entry.value as String}";
}
return jsonEncode(map);
}
static Map<Coin, String> parseCoinAssetsString(
String jsonString, {
required String placeHolder,
}) {
final json = jsonDecode(jsonString) as Map;
final map = Map<String, dynamic>.from(json);
final Map<Coin, String> result = {};
for (final coin in Coin.values) {
result[coin] = map[coin.name] as String? ?? placeHolder;
}
return result;
}
@override
String toString() {
return 'ThemeAssetsV3('
'bellNew: $bellNew, '
'buy: $buy, '
'exchange: $exchange, '
'personaIncognito: $personaIncognito, '
'personaEasy: $personaEasy, '
'stack: $stack, '
'stackIcon: $stackIcon, '
'receive: $receive, '
'receivePending: $receivePending, '
'receiveCancelled: $receiveCancelled, '
'send: $send, '
'sendPending: $sendPending, '
'sendCancelled: $sendCancelled, '
'themeSelector: $themeSelector, '
'themePreview: $themePreview, '
'txExchange: $txExchange, '
'txExchangePending: $txExchangePending, '
'txExchangeFailed: $txExchangeFailed, '
'loadingGif: $loadingGif, '
'background: $background, '
'coinPlaceholder: $coinPlaceholder, '
'coinIcons: $coinIcons, '
'coinImages: $coinImages, '
'coinSecondaryImages: $coinSecondaryImages, '
'coinCardImages: $coinCardImages'
')';
}
}
@ -2285,5 +2510,4 @@ abstract class IThemeAssets {
String? get loadingGif;
String? get background;
String? get walletSummaryCardBackground;
}

File diff suppressed because it is too large Load diff

View file

@ -73,12 +73,6 @@ class _NewWalletRecoveryPhraseWarningViewState
Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType");
final _numberOfPhraseWords = coin == Coin.monero
? Constants.seedPhraseWordCountMonero
: coin == Coin.wownero
? 14
: Constants.seedPhraseWordCountBip39;
return MasterScaffold(
isDesktop: isDesktop,
appBar: isDesktop
@ -177,7 +171,15 @@ class _NewWalletRecoveryPhraseWarningViewState
width: isDesktop ? 480 : null,
child: isDesktop
? Text(
"On the next screen you will see $_numberOfPhraseWords words that make up your recovery phrase.\n\nPlease write it down. Keep it safe and never share it with anyone. Your recovery phrase is the only way you can access your funds if you forget your PIN, lose your phone, etc.\n\nStack Wallet does not keep nor is able to restore your recover phrase. Only you have access to your wallet.",
"On the next screen you will see "
"${Constants.defaultSeedPhraseLengthFor(coin: coin)} "
"words that make up your recovery phrase.\n\nPlease "
"write it down. Keep it safe and never share it with "
"anyone. Your recovery phrase is the only way you can"
" access your funds if you forget your PIN, lose your"
" phone, etc.\n\nStack Wallet does not keep nor is "
"able to restore your recover phrase. Only you have "
"access to your wallet.",
style: isDesktop
? STextStyles.desktopTextMediumRegular(context)
: STextStyles.subtitle(context).copyWith(
@ -214,7 +216,9 @@ class _NewWalletRecoveryPhraseWarningViewState
),
),
TextSpan(
text: "$_numberOfPhraseWords words",
text:
"${Constants.defaultSeedPhraseLengthFor(coin: coin)}"
" words",
style: STextStyles.desktopH3(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!

View file

@ -26,6 +26,7 @@ import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/amount/amount_formatter.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/background.dart';
@ -156,7 +157,12 @@ class _ConfirmChangeNowSendViewState
Navigator.of(context).popUntil(ModalRoute.withName(routeOnSuccessName));
}
} catch (e) {
} catch (e, s) {
Logging.instance.log(
"Broadcast transaction failed: $e\n$s",
level: LogLevel.Error,
);
// pop sending dialog
Navigator.of(context).pop();
@ -205,9 +211,9 @@ class _ConfirmChangeNowSendViewState
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
const Row(
mainAxisAlignment: MainAxisAlignment.end,
children: const [
children: [
DesktopDialogCloseButton(),
],
),

View file

@ -290,6 +290,7 @@ class _Step4ViewState extends ConsumerState<Step4View> {
walletId: tuple.item1,
routeOnSuccessName: HomeView.routeName,
trade: model.trade!,
shouldSendPublicFiroFunds: firoPublicSend,
),
settings: const RouteSettings(
name: ConfirmChangeNowSendView.routeName,

View file

@ -303,11 +303,17 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
),
),
),
if (coin != Coin.epicCash && coin != Coin.ethereum)
if (coin != Coin.epicCash &&
coin != Coin.ethereum &&
coin != Coin.banano &&
coin != Coin.nano)
const SizedBox(
height: 12,
),
if (coin != Coin.epicCash && coin != Coin.ethereum)
if (coin != Coin.epicCash &&
coin != Coin.ethereum &&
coin != Coin.banano &&
coin != Coin.nano)
TextButton(
onPressed: generateNewAddress,
style: Theme.of(context)

View file

@ -436,36 +436,38 @@ class _ConfirmTransactionViewState
],
),
),
const SizedBox(
height: 12,
),
RoundedWhiteContainer(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Transaction fee",
style: STextStyles.smallMed12(context),
),
Text(
ref.watch(pAmountFormatter(coin)).format(
(transactionInfo["fee"] is Amount
? transactionInfo["fee"] as Amount
: (transactionInfo["fee"] as int)
.toAmountAsRaw(
fractionDigits: ref.watch(
managerProvider.select(
(value) => value.coin.decimals,
),
),
)),
),
style: STextStyles.itemSubtitle12(context),
textAlign: TextAlign.right,
),
],
if (coin != Coin.banano && coin != Coin.nano)
const SizedBox(
height: 12,
),
if (coin != Coin.banano && coin != Coin.nano)
RoundedWhiteContainer(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Transaction fee",
style: STextStyles.smallMed12(context),
),
Text(
ref.watch(pAmountFormatter(coin)).format(
(transactionInfo["fee"] is Amount
? transactionInfo["fee"] as Amount
: (transactionInfo["fee"] as int)
.toAmountAsRaw(
fractionDigits: ref.watch(
managerProvider.select(
(value) => value.coin.decimals,
),
),
)),
),
style: STextStyles.itemSubtitle12(context),
textAlign: TextAlign.right,
),
],
),
),
),
const SizedBox(
height: 12,
),

View file

@ -10,6 +10,7 @@
import 'dart:async';
import 'dart:io';
import 'dart:math';
import 'package:bip47/bip47.dart';
import 'package:cw_core/monero_transaction_priority.dart';
@ -26,7 +27,6 @@ import 'package:stackwallet/pages/coin_control/coin_control_view.dart';
import 'package:stackwallet/pages/send_view/confirm_transaction_view.dart';
import 'package:stackwallet/pages/send_view/sub_widgets/building_transaction_dialog.dart';
import 'package:stackwallet/pages/send_view/sub_widgets/firo_balance_selection_sheet.dart';
import 'package:stackwallet/pages/send_view/sub_widgets/openalias_sheet.dart';
import 'package:stackwallet/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/ui/fee_rate_type_state_provider.dart';
@ -138,7 +138,8 @@ class _SendViewState extends ConsumerState<SendView> {
cryptoAmount = cryptoAmount.split(" ").first;
}
final shift = ref.read(pAmountUnit(coin)).shift;
// ensure we don't shift past minimum atomic value
final shift = min(ref.read(pAmountUnit(coin)).shift, coin.decimals);
_amountToSend = cryptoAmount.contains(",")
? Decimal.parse(cryptoAmount.replaceFirst(",", "."))

View file

@ -12,11 +12,11 @@ import 'dart:convert';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
// import 'package:flutter_libepiccash/git_versions.dart' as EPIC_VERSIONS;
// import 'package:flutter_libmonero/git_versions.dart' as MONERO_VERSIONS;
import 'package:flutter_libepiccash/git_versions.dart' as EPIC_VERSIONS;
import 'package:flutter_libmonero/git_versions.dart' as MONERO_VERSIONS;
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:http/http.dart';
// import 'package:lelantus/git_versions.dart' as FIRO_VERSIONS;
import 'package:lelantus/git_versions.dart' as FIRO_VERSIONS;
import 'package:package_info_plus/package_info_plus.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/logger.dart';
@ -115,12 +115,9 @@ class AboutView extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
String firoCommit =
"Unable to fetch version"; //FIRO_VERSIONS.getPluginVersion();
String epicCashCommit =
"Unable to fetch version"; // EPIC_VERSIONS.getPluginVersion();
String moneroCommit =
"Unable to fetch version"; // MONERO_VERSIONS.getPluginVersion();
String firoCommit = FIRO_VERSIONS.getPluginVersion();
String epicCashCommit = EPIC_VERSIONS.getPluginVersion();
String moneroCommit = MONERO_VERSIONS.getPluginVersion();
List<Future> futureFiroList = [
doesCommitExist("cypherstack", "flutter_liblelantus", firoCommit),
isHeadCommit("cypherstack", "flutter_liblelantus", "main", firoCommit),
@ -174,14 +171,14 @@ class AboutView extends ConsumerWidget {
String appName = "";
String build = "";
// if (snapshot.connectionState ==
// ConnectionState.done &&
// snapshot.hasData) {
// version = snapshot.data!.version;
// build = snapshot.data!.buildNumber;
// signature = snapshot.data!.buildSignature;
// appName = snapshot.data!.appName;
// }
if (snapshot.connectionState ==
ConnectionState.done &&
snapshot.hasData) {
version = snapshot.data!.version;
build = snapshot.data!.buildNumber;
signature = snapshot.data!.buildSignature;
appName = snapshot.data!.appName;
}
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,

View file

@ -17,11 +17,11 @@ import 'package:event_bus/event_bus.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
// import 'package:flutter_libepiccash/git_versions.dart' as EPIC_VERSIONS;
// import 'package:flutter_libmonero/git_versions.dart' as MONERO_VERSIONS;
import 'package:flutter_libepiccash/git_versions.dart' as EPIC_VERSIONS;
import 'package:flutter_libmonero/git_versions.dart' as MONERO_VERSIONS;
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
// import 'package:lelantus/git_versions.dart' as FIRO_VERSIONS;
import 'package:lelantus/git_versions.dart' as FIRO_VERSIONS;
import 'package:package_info_plus/package_info_plus.dart';
import 'package:stackwallet/models/isar/models/log.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
@ -302,11 +302,11 @@ class _DebugViewState extends ConsumerState<DebugView> {
packageInfo.buildSignature;
final appName = packageInfo.appName;
String firoCommit =
"Unable to fetch version"; //FIRO_VERSIONS.getPluginVersion();
FIRO_VERSIONS.getPluginVersion();
String epicCashCommit =
"Unable to fetch version"; //EPIC_VERSIONS.getPluginVersion();
EPIC_VERSIONS.getPluginVersion();
String moneroCommit =
"Unable to fetch version"; // MONERO_VERSIONS.getPluginVersion();
MONERO_VERSIONS.getPluginVersion();
DeviceInfoPlugin deviceInfoPlugin =
DeviceInfoPlugin();
final deviceInfo =

View file

@ -9,20 +9,14 @@
*/
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/pages/wallet_view/sub_widgets/wallet_summary_info.dart';
import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/widgets/coin_card.dart';
class WalletSummary extends StatelessWidget {
const WalletSummary({
Key? key,
required this.walletId,
required this.managerProvider,
required this.initialSyncStatus,
this.aspectRatio = 2.0,
this.minHeight = 100.0,
@ -32,7 +26,6 @@ class WalletSummary extends StatelessWidget {
}) : super(key: key);
final String walletId;
final ChangeNotifierProvider<Manager> managerProvider;
final WalletSyncStatus initialSyncStatus;
final double aspectRatio;
@ -52,83 +45,25 @@ class WalletSummary extends StatelessWidget {
maxHeight: maxHeight,
maxWidth: minWidth,
),
child: Stack(
children: [
Consumer(
builder: (_, ref, __) {
return Container(
decoration: BoxDecoration(
color: Theme.of(context)
.extension<StackColors>()!
.colorForCoin(ref.watch(
managerProvider.select((value) => value.coin))),
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
);
},
),
Positioned.fill(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Spacer(
flex: 5,
),
Expanded(
flex: 6,
child: SvgPicture.asset(
Assets.svg.ellipse1,
// fit: BoxFit.fitWidth,
// clipBehavior: Clip.none,
),
),
const SizedBox(
width: 25,
),
],
child: LayoutBuilder(
builder: (_, constraints) => Stack(
children: [
CoinCard(
walletId: walletId,
width: constraints.maxWidth,
height: constraints.maxHeight,
),
),
// Positioned.fill(
// child:
// Column(
// mainAxisAlignment: MainAxisAlignment.end,
// children: [
Align(
alignment: Alignment.bottomCenter,
child: Row(
children: [
const Spacer(
flex: 1,
Positioned.fill(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: WalletSummaryInfo(
walletId: walletId,
initialSyncStatus: initialSyncStatus,
),
Expanded(
flex: 3,
child: SvgPicture.asset(
Assets.svg.ellipse2,
// fit: BoxFit.f,
// clipBehavior: Clip.none,
),
),
const SizedBox(
width: 13,
),
],
),
),
// ],
// ),
// ),
Positioned.fill(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: WalletSummaryInfo(
walletId: walletId,
initialSyncStatus: initialSyncStatus,
),
),
),
],
],
),
),
),
);

View file

@ -148,8 +148,7 @@ class _TransactionDetailsViewState
if (_transaction.numberOfMessages == 1) {
return "Receiving (waiting for sender)";
} else if ((_transaction.numberOfMessages ?? 0) > 1) {
return
"Receiving (waiting for confirmations)"; // TODO test if the sender still has to open again after the receiver has 2 messages present, ie. sender->receiver->sender->node (yes) vs. sender->receiver->node (no)
return "Receiving (waiting for confirmations)"; // TODO test if the sender still has to open again after the receiver has 2 messages present, ie. sender->receiver->sender->node (yes) vs. sender->receiver->node (no)
} else {
return "Receiving";
}
@ -963,94 +962,99 @@ class _TransactionDetailsViewState
],
),
),
isDesktop
? const _Divider()
: const SizedBox(
height: 12,
),
RoundedWhiteContainer(
padding: isDesktop
? const EdgeInsets.all(16)
: const EdgeInsets.all(12),
child: Builder(builder: (context) {
String feeString = showFeePending
? _transaction.isConfirmed(
currentHeight,
coin.requiredConfirmations,
)
? ref
.watch(pAmountFormatter(coin))
.format(
fee,
withUnitName: isTokenTx,
)
: "Pending"
: ref.watch(pAmountFormatter(coin)).format(
fee,
withUnitName: isTokenTx,
);
if (coin != Coin.banano && coin != Coin.nano)
isDesktop
? const _Divider()
: const SizedBox(
height: 12,
),
if (coin != Coin.banano && coin != Coin.nano)
RoundedWhiteContainer(
padding: isDesktop
? const EdgeInsets.all(16)
: const EdgeInsets.all(12),
child: Builder(builder: (context) {
String feeString = showFeePending
? _transaction.isConfirmed(
currentHeight,
coin.requiredConfirmations,
)
? ref
.watch(pAmountFormatter(coin))
.format(
fee,
withUnitName: isTokenTx,
)
: "Pending"
: ref
.watch(pAmountFormatter(coin))
.format(
fee,
withUnitName: isTokenTx,
);
return Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
"Transaction fee",
style: isDesktop
? STextStyles
.desktopTextExtraExtraSmall(
context)
: STextStyles.itemSubtitle(
context),
),
if (isDesktop)
const SizedBox(
height: 2,
),
if (isDesktop)
SelectableText(
feeString,
return Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
"Transaction fee",
style: isDesktop
? STextStyles
.desktopTextExtraExtraSmall(
context)
.copyWith(
color: Theme.of(context)
.extension<
StackColors>()!
.textDark,
)
: STextStyles.itemSubtitle12(
context),
),
],
),
if (!isDesktop)
SelectableText(
feeString,
style: isDesktop
? STextStyles
.desktopTextExtraExtraSmall(
context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
)
: STextStyles.itemSubtitle12(
context),
: STextStyles.itemSubtitle(
context),
),
if (isDesktop)
const SizedBox(
height: 2,
),
if (isDesktop)
SelectableText(
feeString,
style: isDesktop
? STextStyles
.desktopTextExtraExtraSmall(
context)
.copyWith(
color: Theme.of(context)
.extension<
StackColors>()!
.textDark,
)
: STextStyles.itemSubtitle12(
context),
),
],
),
if (isDesktop)
IconCopyButton(data: feeString)
],
);
}),
),
if (!isDesktop)
SelectableText(
feeString,
style: isDesktop
? STextStyles
.desktopTextExtraExtraSmall(
context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
)
: STextStyles.itemSubtitle12(
context),
),
if (isDesktop)
IconCopyButton(data: feeString)
],
);
}),
),
isDesktop
? const _Divider()
: const SizedBox(

View file

@ -680,7 +680,6 @@ class _WalletViewState extends ConsumerState<WalletView> {
padding: const EdgeInsets.symmetric(horizontal: 16),
child: WalletSummary(
walletId: walletId,
managerProvider: managerProvider,
initialSyncStatus: ref.watch(managerProvider
.select((value) => value.isRefreshing))
? WalletSyncStatus.syncing

View file

@ -21,11 +21,11 @@ import 'package:stackwallet/themes/coin_icon_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/amount/amount_formatter.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/coin_card.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:tuple/tuple.dart';
@ -145,66 +145,10 @@ class _FavoriteCardState extends ConsumerState<FavoriteCard> {
width: widget.width,
height: widget.height,
child: CardOverlayStack(
background: Stack(
children: [
Container(
width: widget.width,
height: widget.height,
decoration: BoxDecoration(
color: Theme.of(context)
.extension<StackColors>()!
.colorForCoin(coin),
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
),
Column(
children: [
const Spacer(),
SizedBox(
height: widget.width * 0.3,
child: Row(
children: [
const Spacer(
flex: 9,
),
SvgPicture.asset(
Assets.svg.ellipse2,
height: widget.width * 0.3,
),
// ),
const Spacer(
flex: 2,
),
],
),
),
],
),
Row(
children: [
const Spacer(
flex: 5,
),
SizedBox(
width: widget.width * 0.45,
child: Column(
children: [
SvgPicture.asset(
Assets.svg.ellipse1,
width: widget.width * 0.45,
),
const Spacer(),
],
),
),
const Spacer(
flex: 1,
),
],
),
],
background: CoinCard(
walletId: widget.walletId,
width: widget.width,
height: widget.height,
),
child: Padding(
padding: const EdgeInsets.all(12.0),

View file

@ -215,11 +215,17 @@ class _DesktopReceiveState extends ConsumerState<DesktopReceive> {
),
),
),
if (coin != Coin.epicCash && coin != Coin.ethereum)
if (coin != Coin.epicCash &&
coin != Coin.ethereum &&
coin != Coin.banano &&
coin != Coin.nano)
const SizedBox(
height: 20,
),
if (coin != Coin.epicCash && coin != Coin.ethereum)
if (coin != Coin.epicCash &&
coin != Coin.ethereum &&
coin != Coin.banano &&
coin != Coin.nano)
SecondaryButton(
buttonHeight: ButtonHeight.l,
onPressed: generateNewAddress,

View file

@ -9,6 +9,7 @@
*/
import 'dart:async';
import 'dart:math';
import 'package:bip47/bip47.dart';
import 'package:decimal/decimal.dart';
@ -452,7 +453,8 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
cryptoAmount = cryptoAmount.split(" ").first;
}
final shift = ref.read(pAmountUnit(coin)).shift;
// ensure we don't shift past minimum atomic value
final shift = min(ref.read(pAmountUnit(coin)).shift, coin.decimals);
_amountToSend = cryptoAmount.contains(",")
? Decimal.parse(cryptoAmount.replaceFirst(",", "."))

View file

@ -12,11 +12,11 @@ import 'dart:convert';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
// import 'package:flutter_libepiccash/git_versions.dart' as EPIC_VERSIONS;
// import 'package:flutter_libmonero/git_versions.dart' as MONERO_VERSIONS;
import 'package:flutter_libepiccash/git_versions.dart' as EPIC_VERSIONS;
import 'package:flutter_libmonero/git_versions.dart' as MONERO_VERSIONS;
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:http/http.dart';
// import 'package:lelantus/git_versions.dart' as FIRO_VERSIONS;
import 'package:lelantus/git_versions.dart' as FIRO_VERSIONS;
import 'package:package_info_plus/package_info_plus.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/logger.dart';
@ -108,12 +108,9 @@ class DesktopAboutView extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
String firoCommit =
"Unable to fetch version"; // FIRO_VERSIONS.getPluginVersion();
String epicCashCommit =
"Unable to fetch version"; // EPIC_VERSIONS.getPluginVersion();
String moneroCommit =
"Unable to fetch version"; //MONERO_VERSIONS.getPluginVersion();
String firoCommit = FIRO_VERSIONS.getPluginVersion();
String epicCashCommit = EPIC_VERSIONS.getPluginVersion();
String moneroCommit = MONERO_VERSIONS.getPluginVersion();
List<Future> futureFiroList = [
doesCommitExist("cypherstack", "flutter_liblelantus", firoCommit),
isHeadCommit("cypherstack", "flutter_liblelantus", "main", firoCommit),
@ -250,13 +247,13 @@ class DesktopAboutView extends ConsumerWidget {
String signature = "";
String build = "";
// if (snapshot.connectionState ==
// ConnectionState.done &&
// snapshot.hasData) {
// version = snapshot.data!.version;
// build = snapshot.data!.buildNumber;
// signature = snapshot.data!.buildSignature;
// }
if (snapshot.connectionState ==
ConnectionState.done &&
snapshot.hasData) {
version = snapshot.data!.version;
build = snapshot.data!.buildNumber;
signature = snapshot.data!.buildSignature;
}
return Column(
mainAxisAlignment:
@ -404,7 +401,7 @@ class DesktopAboutView extends ConsumerWidget {
.accentColorGreen);
break;
case CommitStatus
.isOldCommit:
.isOldCommit:
indicationStyle = STextStyles
.itemSubtitle(
context)
@ -416,7 +413,7 @@ class DesktopAboutView extends ConsumerWidget {
.accentColorYellow);
break;
case CommitStatus
.notACommit:
.notACommit:
indicationStyle = STextStyles
.itemSubtitle(
context)
@ -510,7 +507,7 @@ class DesktopAboutView extends ConsumerWidget {
.accentColorGreen);
break;
case CommitStatus
.isOldCommit:
.isOldCommit:
indicationStyle = STextStyles
.itemSubtitle(
context)
@ -522,7 +519,7 @@ class DesktopAboutView extends ConsumerWidget {
.accentColorYellow);
break;
case CommitStatus
.notACommit:
.notACommit:
indicationStyle = STextStyles
.itemSubtitle(
context)
@ -614,7 +611,7 @@ class DesktopAboutView extends ConsumerWidget {
.accentColorGreen);
break;
case CommitStatus
.isOldCommit:
.isOldCommit:
indicationStyle = STextStyles
.itemSubtitle(
context)
@ -626,7 +623,7 @@ class DesktopAboutView extends ConsumerWidget {
.accentColorYellow);
break;
case CommitStatus
.notACommit:
.notACommit:
indicationStyle = STextStyles
.itemSubtitle(
context)

View file

@ -11,6 +11,7 @@ import 'package:stackwallet/models/node_model.dart';
import 'package:stackwallet/models/paymint/fee_object_model.dart';
import 'package:stackwallet/services/coins/coin_service.dart';
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart';
import 'package:stackwallet/services/event_bus/events/global/updated_in_background_event.dart';
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
import 'package:stackwallet/services/event_bus/global_event_bus.dart';
import 'package:stackwallet/services/mixins/coin_control_interface.dart';
@ -293,17 +294,31 @@ class BananoWallet extends CoinServiceAPI
Future<Amount> estimateFeeFor(Amount amount, int feeRate) {
// fees are always 0 :)
return Future.value(
Amount(rawValue: BigInt.from(0), fractionDigits: coin.decimals));
Amount(
rawValue: BigInt.from(0),
fractionDigits: coin.decimals,
),
);
}
@override
Future<void> exit() async {
_hasCalledExit = true;
timer?.cancel();
timer = null;
stopNetworkAlivePinging();
}
@override
// TODO: implement fees
Future<FeeObject> get fees => throw UnimplementedError();
// Banano has no fees
Future<FeeObject> get fees async => FeeObject(
numberOfBlocksFast: 1,
numberOfBlocksAverage: 1,
numberOfBlocksSlow: 1,
fast: 0,
medium: 0,
slow: 0,
);
Future<void> updateBalance() async {
final body = jsonEncode({
@ -550,7 +565,14 @@ class BananoWallet extends CoinServiceAPI
await db.addNewTransactionData(transactionList, walletId);
return;
if (transactionList.isNotEmpty) {
GlobalEventBus.instance.fire(
UpdatedInBackgroundEvent(
"Transactions updated/added for: $walletId $walletName ",
walletId,
),
);
}
}
}
@ -649,20 +671,14 @@ class BananoWallet extends CoinServiceAPI
Map<String, dynamic>? args,
}) async {
try {
int satAmount = amount.raw.toInt();
int realfee = 0;
if (balance.spendable == amount) {
satAmount = balance.spendable.raw.toInt() - realfee;
if (amount.decimals != coin.decimals) {
throw ArgumentError("Banano prepareSend attempted with invalid Amount");
}
Map<String, dynamic> txData = {
"fee": realfee,
"fee": 0,
"addresss": address,
"recipientAmt": Amount(
rawValue: BigInt.from(satAmount),
fractionDigits: coin.decimals,
),
"recipientAmt": amount,
};
Logging.instance.log("prepare send: $txData", level: LogLevel.Info);
@ -720,17 +736,29 @@ class BananoWallet extends CoinServiceAPI
@override
Future<void> refresh() async {
if (refreshMutex) {
Logging.instance.log(
"$walletId $walletName refreshMutex denied",
level: LogLevel.Info,
);
return;
} else {
refreshMutex = true;
}
await _prefs.init();
GlobalEventBus.instance.fire(
WalletSyncStatusChangedEvent(
WalletSyncStatus.syncing,
walletId,
coin,
),
);
try {
GlobalEventBus.instance.fire(
WalletSyncStatusChangedEvent(
WalletSyncStatus.syncing,
walletId,
coin,
),
);
await _prefs.init();
await updateChainHeight();
await updateTransactions();
await updateBalance();
@ -742,9 +770,25 @@ class BananoWallet extends CoinServiceAPI
coin,
),
);
if (shouldAutoSync) {
timer ??= Timer.periodic(const Duration(seconds: 30), (timer) async {
Logging.instance.log(
"Periodic refresh check for $walletId $walletName in object instance: $hashCode",
level: LogLevel.Info);
await refresh();
GlobalEventBus.instance.fire(
UpdatedInBackgroundEvent(
"New data found in $walletId $walletName in background!",
walletId,
),
);
});
}
} catch (e, s) {
Logging.instance.log(
"Failed to refresh banano wallet \'$walletName\': $e\n$s",
"Failed to refresh banano wallet $walletId: '$walletName': $e\n$s",
level: LogLevel.Warning,
);
GlobalEventBus.instance.fire(
@ -755,6 +799,8 @@ class BananoWallet extends CoinServiceAPI
),
);
}
refreshMutex = false;
}
@override
@ -843,9 +889,9 @@ class BananoWallet extends CoinServiceAPI
}
@override
Future<void> updateSentCachedTxData(Map<String, dynamic> txData) {
// TODO: implement updateSentCachedTxData
throw UnimplementedError();
Future<void> updateSentCachedTxData(Map<String, dynamic> txData) async {
// not currently used for nano
return;
}
@override

View file

@ -21,6 +21,7 @@ import 'package:stackwallet/models/node_model.dart';
import 'package:stackwallet/models/paymint/fee_object_model.dart';
import 'package:stackwallet/services/coins/coin_service.dart';
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart';
import 'package:stackwallet/services/event_bus/events/global/updated_in_background_event.dart';
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
import 'package:stackwallet/services/event_bus/global_event_bus.dart';
import 'package:stackwallet/services/mixins/coin_control_interface.dart';
@ -309,11 +310,21 @@ class NanoWallet extends CoinServiceAPI
@override
Future<void> exit() async {
_hasCalledExit = true;
timer?.cancel();
timer = null;
stopNetworkAlivePinging();
}
@override
// Nano has no fees
Future<FeeObject> get fees => throw UnimplementedError();
Future<FeeObject> get fees async => FeeObject(
numberOfBlocksFast: 1,
numberOfBlocksAverage: 1,
numberOfBlocksSlow: 1,
fast: 0,
medium: 0,
slow: 0,
);
Future<void> updateBalance() async {
final body = jsonEncode({
@ -560,7 +571,14 @@ class NanoWallet extends CoinServiceAPI
await db.addNewTransactionData(transactionList, walletId);
return;
if (transactionList.isNotEmpty) {
GlobalEventBus.instance.fire(
UpdatedInBackgroundEvent(
"Transactions updated/added for: $walletId $walletName ",
walletId,
),
);
}
}
}
@ -732,17 +750,27 @@ class NanoWallet extends CoinServiceAPI
@override
Future<void> refresh() async {
await _prefs.init();
GlobalEventBus.instance.fire(
WalletSyncStatusChangedEvent(
WalletSyncStatus.syncing,
walletId,
coin,
),
);
if (refreshMutex) {
Logging.instance.log(
"$walletId $walletName refreshMutex denied",
level: LogLevel.Info,
);
return;
} else {
refreshMutex = true;
}
try {
await _prefs.init();
GlobalEventBus.instance.fire(
WalletSyncStatusChangedEvent(
WalletSyncStatus.syncing,
walletId,
coin,
),
);
await updateChainHeight();
await updateTransactions();
await updateBalance();
@ -754,7 +782,27 @@ class NanoWallet extends CoinServiceAPI
coin,
),
);
} catch (e) {
if (shouldAutoSync) {
timer ??= Timer.periodic(const Duration(seconds: 30), (timer) async {
Logging.instance.log(
"Periodic refresh check for $walletId $walletName in object instance: $hashCode",
level: LogLevel.Info);
await refresh();
GlobalEventBus.instance.fire(
UpdatedInBackgroundEvent(
"New data found in $walletId $walletName in background!",
walletId,
),
);
});
}
} catch (e, s) {
Logging.instance.log(
"Failed to refresh nano wallet $walletId: '$walletName': $e\n$s",
level: LogLevel.Warning,
);
GlobalEventBus.instance.fire(
WalletSyncStatusChangedEvent(
WalletSyncStatus.unableToSync,
@ -763,6 +811,8 @@ class NanoWallet extends CoinServiceAPI
),
);
}
refreshMutex = false;
}
@override

View file

@ -100,7 +100,7 @@ class PriceAPI {
Uri.parse("https://api.coingecko.com/api/v3/coins/markets?vs_currency"
"=${baseCurrency.toLowerCase()}"
"&ids=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin,"
"bitcoin-cash,namecoin,wownero,ethereum,particl,nano,ban"
"bitcoin-cash,namecoin,wownero,ethereum,particl,nano,banano"
"&order=market_cap_desc&per_page=50&page=1&sparkline=false");
final coinGeckoResponse = await client.get(
@ -115,8 +115,9 @@ class PriceAPI {
final coin = coinFromPrettyName(coinName);
final price = Decimal.parse(map["current_price"].toString());
final change24h = map["price_change_percentage_24h"] != null ?
double.parse(map["price_change_percentage_24h"].toString()) : 0.0;
final change24h = map["price_change_percentage_24h"] != null
? double.parse(map["price_change_percentage_24h"].toString())
: 0.0;
result[coin] = Tuple2(price, change24h);
}

View file

@ -0,0 +1,24 @@
/*
* This file is part of Stack Wallet.
*
* Copyright (c) 2023 Cypher Stack
* All Rights Reserved.
* The code is distributed under GPLv3 license, see LICENSE file for details.
* Generated by Cypher Stack on 2023-05-26
*
*/
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/models/isar/stack_theme.dart';
import 'package:stackwallet/themes/theme_providers.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
final coinCardProvider = Provider.family<String?, Coin>((ref, coin) {
final assets = ref.watch(themeAssetsProvider);
if (assets is ThemeAssetsV3) {
return assets.coinCardImages?[coin.mainNetVersion];
} else {
return null;
}
});

View file

@ -50,7 +50,9 @@ final coinIconProvider = Provider.family<String, Coin>((ref, coin) {
default:
return assets.bitcoin;
}
} else if (assets is ThemeAssetsV2) {
return (assets).coinIcons[coin.mainNetVersion]!;
} else {
return (assets as ThemeAssetsV2).coinIcons[coin.mainNetVersion]!;
return (assets as ThemeAssetsV3).coinIcons[coin.mainNetVersion]!;
}
});

View file

@ -52,10 +52,12 @@ final coinImageProvider = Provider.family<String, Coin>((ref, coin) {
case Coin.ethereum:
return assets.ethereumImage;
default:
return assets.bitcoinImage;
return assets.stackIcon;
}
} else if (assets is ThemeAssetsV2) {
return (assets).coinImages[coin.mainNetVersion]!;
} else {
return (assets as ThemeAssetsV2).coinImages[coin.mainNetVersion]!;
return (assets as ThemeAssetsV3).coinImages[coin.mainNetVersion]!;
}
});
@ -99,9 +101,11 @@ final coinImageSecondaryProvider = Provider.family<String, Coin>((ref, coin) {
return assets.ethereumImageSecondary;
default:
return assets.ethereumImageSecondary;
return assets.stackIcon;
}
} else if (assets is ThemeAssetsV2) {
return (assets).coinSecondaryImages[coin.mainNetVersion]!;
} else {
return (assets as ThemeAssetsV2).coinSecondaryImages[coin.mainNetVersion]!;
return (assets as ThemeAssetsV3).coinSecondaryImages[coin.mainNetVersion]!;
}
});

View file

@ -40,9 +40,9 @@ abstract class Constants {
static final BigInt _satsPerCoinMonero = BigInt.from(1000000000000);
static final BigInt _satsPerCoinWownero = BigInt.from(100000000000);
static final BigInt _satsPerCoinNano =
BigInt.parse("1000000000000000000000000000000");// 1*10^30
BigInt.parse("1000000000000000000000000000000"); // 1*10^30
static final BigInt _satsPerCoinBanano =
BigInt.parse("100000000000000000000000000000");// 1*10^29
BigInt.parse("100000000000000000000000000000"); // 1*10^29
static final BigInt _satsPerCoin = BigInt.from(100000000);
static const int _decimalPlaces = 8;
static const int _decimalPlacesNano = 30;
@ -217,8 +217,36 @@ abstract class Constants {
}
}
static const int seedPhraseWordCountBip39 = 12;
static const int seedPhraseWordCountMonero = 25;
static int defaultSeedPhraseLengthFor({required Coin coin}) {
switch (coin) {
case Coin.bitcoin:
case Coin.bitcoinTestNet:
case Coin.bitcoincash:
case Coin.bitcoincashTestnet:
case Coin.eCash:
case Coin.dogecoin:
case Coin.dogecoinTestNet:
case Coin.litecoin:
case Coin.litecoinTestNet:
case Coin.firo:
case Coin.firoTestNet:
case Coin.epicCash:
case Coin.namecoin:
case Coin.particl:
case Coin.ethereum:
return 12;
case Coin.wownero:
return 14;
case Coin.nano:
case Coin.banano:
return 24;
case Coin.monero:
return 25;
}
}
static const Map<int, String> monthMapShort = {
1: 'Jan',

View file

@ -137,29 +137,31 @@ class _AddressBookCardState extends ConsumerState<AddressBookCard> {
const SizedBox(
width: 16,
),
if (isDesktop && !desktopSendFrom) const Spacer(),
if (isDesktop)
Text(
coinsString,
style: STextStyles.label(context),
),
if (!isDesktop)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
Expanded(
child: Column(
crossAxisAlignment: isDesktop && !desktopSendFrom
? CrossAxisAlignment.end
: CrossAxisAlignment.start,
children: [
Text(
contact.name,
style: STextStyles.itemSubtitle12(context),
),
const SizedBox(
height: 4,
),
if (!isDesktop)
Text(
contact.name,
style: STextStyles.itemSubtitle12(context),
),
if (!isDesktop)
const SizedBox(
height: 4,
),
Text(
coinsString,
style: STextStyles.label(context),
textAlign: isDesktop && !desktopSendFrom
? TextAlign.right
: TextAlign.left,
),
],
),
),
if (isDesktop && desktopSendFrom) const Spacer(),
if (isDesktop && desktopSendFrom)
SvgPicture.asset(

123
lib/widgets/coin_card.dart Normal file
View file

@ -0,0 +1,123 @@
/*
* This file is part of Stack Wallet.
*
* Copyright (c) 2023 Cypher Stack
* All Rights Reserved.
* The code is distributed under GPLv3 license, see LICENSE file for details.
* Generated by Cypher Stack on 2023-05-26
*
*/
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/themes/coin_card_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
class CoinCard extends ConsumerWidget {
const CoinCard({
super.key,
required this.walletId,
required this.width,
required this.height,
});
final String walletId;
final double width;
final double height;
@override
Widget build(BuildContext context, WidgetRef ref) {
final coin = ref.watch(
walletsChangeNotifierProvider
.select((value) => value.getManager(walletId).coin),
);
final bool hasCardImageBg = ref.watch(coinCardProvider(coin)) != null;
return Stack(
children: [
if (hasCardImageBg)
Container(
width: width,
height: height,
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.fill,
image: FileImage(
File(
ref.watch(coinCardProvider(coin))!,
),
),
),
),
),
if (!hasCardImageBg)
Container(
width: width,
height: height,
decoration: BoxDecoration(
color: Theme.of(context)
.extension<StackColors>()!
.colorForCoin(coin),
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
),
if (!hasCardImageBg)
Column(
children: [
const Spacer(),
SizedBox(
height: width * 0.3,
child: Row(
children: [
const Spacer(
flex: 9,
),
SvgPicture.asset(
Assets.svg.ellipse2,
height: width * 0.3,
),
// ),
const Spacer(
flex: 2,
),
],
),
),
],
),
if (!hasCardImageBg)
Row(
children: [
const Spacer(
flex: 5,
),
SizedBox(
width: width * 0.45,
child: Column(
children: [
SvgPicture.asset(
Assets.svg.ellipse1,
width: width * 0.45,
),
const Spacer(),
],
),
),
const Spacer(
flex: 1,
),
],
),
],
);
}
}

View file

@ -28,7 +28,7 @@ void main() {
Uri.parse(
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids"
"=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin,bitcoin-cash"
",namecoin,wownero,ethereum,particl,nano,ban&order=market_cap_desc&per_page=50"
",namecoin,wownero,ethereum,particl,nano,banano&order=market_cap_desc&per_page=50"
"&page=1&sparkline=false"),
headers: {
'Content-Type': 'application/json'
@ -100,8 +100,9 @@ void main() {
expect(
price.toString(),
'{'
'Coin.banano: [0, 0.0], '
'Coin.bitcoin: [1, 0.0], '
'Coin.monero: [0.00717236, -0.77656], '
'Coin.banano: [0, 0.0], '
'Coin.bitcoincash: [0, 0.0], '
'Coin.dogecoin: [0.00000315, -2.68533], '
'Coin.eCash: [0, 0.0], '
@ -109,7 +110,6 @@ void main() {
'Coin.ethereum: [0, 0.0], '
'Coin.firo: [0.0001096, -0.89304], '
'Coin.litecoin: [0, 0.0], '
'Coin.monero: [0.00717236, -0.77656], '
'Coin.namecoin: [0, 0.0], '
'Coin.nano: [0, 0.0], '
'Coin.particl: [0, 0.0], '
@ -125,7 +125,7 @@ void main() {
Uri.parse(
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc"
"&ids=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin,"
"bitcoin-cash,namecoin,wownero,ethereum,particl,nano,ban"
"bitcoin-cash,namecoin,wownero,ethereum,particl,nano,banano"
"&order=market_cap_desc&per_page=50&page=1&sparkline=false",
),
headers: {'Content-Type': 'application/json'})).called(1);
@ -140,7 +140,7 @@ void main() {
Uri.parse(
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&"
"ids=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin,"
"bitcoin-cash,namecoin,wownero,ethereum,particl,nano,ban"
"bitcoin-cash,namecoin,wownero,ethereum,particl,nano,banano"
"&order=market_cap_desc&per_page=50&page=1&sparkline=false"),
headers: {
'Content-Type': 'application/json'
@ -217,8 +217,9 @@ void main() {
expect(
cachedPrice.toString(),
'{'
'Coin.banano: [0, 0.0], '
'Coin.bitcoin: [1, 0.0], '
'Coin.monero: [0.00717236, -0.77656], '
'Coin.banano: [0, 0.0], '
'Coin.bitcoincash: [0, 0.0], '
'Coin.dogecoin: [0.00000315, -2.68533], '
'Coin.eCash: [0, 0.0], '
@ -226,7 +227,6 @@ void main() {
'Coin.ethereum: [0, 0.0], '
'Coin.firo: [0.0001096, -0.89304], '
'Coin.litecoin: [0, 0.0], '
'Coin.monero: [0.00717236, -0.77656], '
'Coin.namecoin: [0, 0.0], '
'Coin.nano: [0, 0.0], '
'Coin.particl: [0, 0.0], '
@ -244,7 +244,7 @@ void main() {
Uri.parse(
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids"
"=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin,"
"bitcoin-cash,namecoin,wownero,ethereum,particl,nano,ban"
"bitcoin-cash,namecoin,wownero,ethereum,particl,nano,banano"
"&order=market_cap_desc&per_page=50&page=1&sparkline=false"),
headers: {'Content-Type': 'application/json'})).called(1);
@ -258,7 +258,7 @@ void main() {
Uri.parse(
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc"
"&ids=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin,"
"bitcoin-cash,namecoin,wownero,ethereum,particl,nano,ban"
"bitcoin-cash,namecoin,wownero,ethereum,particl,nano,banano"
"&order=market_cap_desc&per_page=50&page=1&sparkline=false"),
headers: {
'Content-Type': 'application/json'
@ -331,8 +331,9 @@ void main() {
expect(
price.toString(),
'{'
'Coin.banano: [0, 0.0], '
'Coin.bitcoin: [0, 0.0], '
'Coin.monero: [0, 0.0], '
'Coin.banano: [0, 0.0], '
'Coin.bitcoincash: [0, 0.0], '
'Coin.dogecoin: [0, 0.0], '
'Coin.eCash: [0, 0.0], '
@ -340,7 +341,6 @@ void main() {
'Coin.ethereum: [0, 0.0], '
'Coin.firo: [0, 0.0], '
'Coin.litecoin: [0, 0.0], '
'Coin.monero: [0, 0.0], '
'Coin.namecoin: [0, 0.0], '
'Coin.nano: [0, 0.0], '
'Coin.particl: [0, 0.0], '
@ -361,7 +361,7 @@ void main() {
Uri.parse(
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc"
"&ids=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin,"
"bitcoin-cash,namecoin,wownero,ethereum,particl,nano,ban"
"bitcoin-cash,namecoin,wownero,ethereum,particl,nano,banano"
"&order=market_cap_desc&per_page=50&page=1&sparkline=false"),
headers: {
'Content-Type': 'application/json'
@ -376,8 +376,9 @@ void main() {
expect(
price.toString(),
'{'
'Coin.banano: [0, 0.0], '
'Coin.bitcoin: [0, 0.0], '
'Coin.monero: [0, 0.0], '
'Coin.banano: [0, 0.0], '
'Coin.bitcoincash: [0, 0.0], '
'Coin.dogecoin: [0, 0.0], '
'Coin.eCash: [0, 0.0], '
@ -385,7 +386,6 @@ void main() {
'Coin.ethereum: [0, 0.0], '
'Coin.firo: [0, 0.0], '
'Coin.litecoin: [0, 0.0], '
'Coin.monero: [0, 0.0], '
'Coin.namecoin: [0, 0.0], '
'Coin.nano: [0, 0.0], '
'Coin.particl: [0, 0.0], '