mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-12-23 20:09:23 +00:00
Merge branch 'desktop' of https://github.com/cypherstack/stack_wallet into desktop
This commit is contained in:
commit
7b41da4a4f
51 changed files with 7270 additions and 1970 deletions
BIN
assets/images/litecoin.png
Normal file
BIN
assets/images/litecoin.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 322 KiB |
4
assets/svg/arrow-down.svg
Normal file
4
assets/svg/arrow-down.svg
Normal file
|
@ -0,0 +1,4 @@
|
|||
<svg width="21" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.5 4.16602V15.8327" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M16.3327 10L10.4993 15.8333L4.66602 10" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
After Width: | Height: | Size: 346 B |
11
assets/svg/coin_icons/Litecoin.svg
Normal file
11
assets/svg/coin_icons/Litecoin.svg
Normal file
|
@ -0,0 +1,11 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_6052_99642)">
|
||||
<rect width="24" height="24" rx="12" fill="white"/>
|
||||
<path d="M11.9976 0C9.62389 0 7.30353 0.703873 5.3299 2.02261C3.35627 3.34135 1.81802 5.21572 0.909655 7.4087C0.00129377 9.60167 -0.236375 12.0148 0.226704 14.3428C0.689782 16.6709 1.83281 18.8093 3.51124 20.4878C5.18968 22.1662 7.32813 23.3092 9.65618 23.7723C11.9842 24.2354 14.3973 23.9977 16.5903 23.0893C18.7833 22.181 20.6577 20.6427 21.9764 18.6691C23.2951 16.6955 23.999 14.3751 23.999 12.0015C23.999 10.4254 23.6886 8.86478 23.0854 7.4087C22.4823 5.95261 21.5983 4.62958 20.4839 3.51514C19.3694 2.40071 18.0464 1.51669 16.5903 0.913556C15.1342 0.310427 13.5736 0 11.9976 0V0ZM12.1921 12.3963L10.9437 16.6087H17.6209C17.674 16.6088 17.7263 16.6213 17.7738 16.6451C17.8212 16.669 17.8625 16.7035 17.8943 16.746C17.9261 16.7885 17.9476 16.8378 17.9571 16.8901C17.9666 16.9423 17.9638 16.9961 17.9489 17.0471L17.3683 19.0473C17.3406 19.1429 17.2826 19.2268 17.203 19.2865C17.1234 19.3462 17.0265 19.3784 16.927 19.3783H6.72841L8.45286 13.5546L6.54551 14.1352L6.96646 12.7737L8.87671 12.1931L11.2979 4.0121C11.3245 3.91623 11.3818 3.8317 11.4609 3.77142C11.5401 3.71114 11.6368 3.67842 11.7363 3.67824H14.32C14.3731 3.67805 14.4254 3.69017 14.4729 3.71364C14.5205 3.73712 14.5619 3.77131 14.594 3.81352C14.6261 3.85573 14.6479 3.90482 14.6578 3.95691C14.6677 4.009 14.6654 4.06267 14.651 4.11371L12.6188 11.0318L14.5262 10.4512L14.1168 11.836L12.1921 12.3963Z" fill="#315D9E"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_6052_99642">
|
||||
<rect width="24" height="24" rx="12" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 1.7 KiB |
|
@ -209,6 +209,7 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
|||
bool didLoad = false;
|
||||
|
||||
Future<void> load() async {
|
||||
try {
|
||||
if (didLoad) {
|
||||
return;
|
||||
}
|
||||
|
@ -248,9 +249,8 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
|||
if (_prefs.isAutoBackupEnabled) {
|
||||
switch (_prefs.backupFrequencyType) {
|
||||
case BackupFrequencyType.everyTenMinutes:
|
||||
ref
|
||||
.read(autoSWBServiceProvider)
|
||||
.startPeriodicBackupTimer(duration: const Duration(minutes: 10));
|
||||
ref.read(autoSWBServiceProvider).startPeriodicBackupTimer(
|
||||
duration: const Duration(minutes: 10));
|
||||
break;
|
||||
case BackupFrequencyType.everyAppStart:
|
||||
unawaited(ref.read(autoSWBServiceProvider).doBackup());
|
||||
|
@ -260,6 +260,9 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
|||
break;
|
||||
}
|
||||
}
|
||||
} catch (e, s) {
|
||||
Logger.print("$e $s", normalLength: false);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -206,7 +206,6 @@ class _AddWalletViewState extends State<AddWalletView> {
|
|||
Expanded(
|
||||
child: MobileCoinList(
|
||||
coins: coins,
|
||||
isDesktop: false,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
|
|
|
@ -8,11 +8,9 @@ class MobileCoinList extends StatelessWidget {
|
|||
const MobileCoinList({
|
||||
Key? key,
|
||||
required this.coins,
|
||||
required this.isDesktop,
|
||||
}) : super(key: key);
|
||||
|
||||
final List<Coin> coins;
|
||||
final bool isDesktop;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
|
@ -26,7 +26,8 @@ class SearchableCoinList extends ConsumerWidget {
|
|||
e.name.toLowerCase().contains(lowercaseTerm));
|
||||
}
|
||||
if (!showTestNetCoins) {
|
||||
_coins.removeWhere((e) => e.name.endsWith("TestNet"));
|
||||
_coins.removeWhere(
|
||||
(e) => e.name.endsWith("TestNet") || e == Coin.bitcoincashTestnet);
|
||||
}
|
||||
// remove firo testnet regardless
|
||||
_coins.remove(Coin.firoTestNet);
|
||||
|
|
|
@ -52,11 +52,13 @@ class _SendFromViewState extends ConsumerState<SendFromView> {
|
|||
switch (coin) {
|
||||
case Coin.bitcoin:
|
||||
case Coin.bitcoincash:
|
||||
case Coin.litecoin:
|
||||
case Coin.dogecoin:
|
||||
case Coin.epicCash:
|
||||
case Coin.firo:
|
||||
case Coin.namecoin:
|
||||
case Coin.bitcoinTestNet:
|
||||
case Coin.litecoinTestNet:
|
||||
case Coin.bitcoincashTestnet:
|
||||
case Coin.dogecoinTestNet:
|
||||
case Coin.firoTestNet:
|
||||
|
|
|
@ -20,7 +20,10 @@ import 'package:stackwallet/utilities/logger.dart';
|
|||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
import 'package:stackwallet/widgets/stack_dialog.dart';
|
||||
|
@ -51,6 +54,10 @@ class _GenerateUriQrCodeViewState extends State<GenerateUriQrCodeView> {
|
|||
late TextEditingController amountController;
|
||||
late TextEditingController noteController;
|
||||
|
||||
late final bool isDesktop;
|
||||
late String _uriString;
|
||||
bool didGenerate = false;
|
||||
|
||||
final _amountFocusNode = FocusNode();
|
||||
final _noteFocusNode = FocusNode();
|
||||
|
||||
|
@ -81,8 +88,151 @@ class _GenerateUriQrCodeViewState extends State<GenerateUriQrCodeView> {
|
|||
}
|
||||
}
|
||||
|
||||
String? _generateURI() {
|
||||
final amountString = amountController.text;
|
||||
final noteString = noteController.text;
|
||||
|
||||
if (amountString.isNotEmpty && Decimal.tryParse(amountString) == null) {
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message: "Invalid amount",
|
||||
context: context,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
String query = "";
|
||||
|
||||
if (amountString.isNotEmpty) {
|
||||
query += "amount=$amountString";
|
||||
}
|
||||
if (noteString.isNotEmpty) {
|
||||
if (query.isNotEmpty) {
|
||||
query += "&";
|
||||
}
|
||||
query += "message=$noteString";
|
||||
}
|
||||
|
||||
final uri = Uri(
|
||||
scheme: widget.coin.uriScheme,
|
||||
host: widget.receivingAddress,
|
||||
query: query.isNotEmpty ? query : null,
|
||||
);
|
||||
|
||||
final uriString = uri.toString().replaceFirst("://", ":");
|
||||
|
||||
Logging.instance.log("Generated receiving QR code for: $uriString",
|
||||
level: LogLevel.Info);
|
||||
|
||||
return uriString;
|
||||
}
|
||||
|
||||
void onGeneratePressed() {
|
||||
final uriString = _generateURI();
|
||||
|
||||
if (uriString == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
showDialog<dynamic>(
|
||||
context: context,
|
||||
useSafeArea: false,
|
||||
barrierDismissible: true,
|
||||
builder: (_) {
|
||||
final width = MediaQuery.of(context).size.width / 2;
|
||||
return StackDialogBase(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Center(
|
||||
child: Text(
|
||||
"New QR code",
|
||||
style: STextStyles.pageTitleH2(context),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
Center(
|
||||
child: RepaintBoundary(
|
||||
key: _qrKey,
|
||||
child: SizedBox(
|
||||
width: width + 20,
|
||||
height: width + 20,
|
||||
child: QrImage(
|
||||
data: uriString,
|
||||
size: width,
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.popupBG,
|
||||
foregroundColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
Center(
|
||||
child: SizedBox(
|
||||
width: width,
|
||||
child: TextButton(
|
||||
onPressed: () async {
|
||||
// TODO: add save button as well
|
||||
await _capturePng(true);
|
||||
},
|
||||
style: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.getSecondaryEnabledButtonColor(context),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Center(
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.share,
|
||||
width: 14,
|
||||
height: 14,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 4,
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Text(
|
||||
"Share",
|
||||
textAlign: TextAlign.center,
|
||||
style: STextStyles.button(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.buttonTextSecondary,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
isDesktop = Util.isDesktop;
|
||||
_uriString = Uri(
|
||||
scheme: widget.coin.uriScheme,
|
||||
host: widget.receivingAddress,
|
||||
).toString().replaceFirst("://", ":");
|
||||
amountController = TextEditingController();
|
||||
noteController = TextEditingController();
|
||||
super.initState();
|
||||
|
@ -101,7 +251,10 @@ class _GenerateUriQrCodeViewState extends State<GenerateUriQrCodeView> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
debugPrint("BUILD: $runtimeType");
|
||||
return Scaffold(
|
||||
|
||||
return ConditionalParent(
|
||||
condition: !isDesktop,
|
||||
builder: (child) => Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
|
@ -136,25 +289,52 @@ class _GenerateUriQrCodeViewState extends State<GenerateUriQrCodeView> {
|
|||
child: IntrinsicHeight(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.only(
|
||||
top: 12,
|
||||
left: 32,
|
||||
right: 32,
|
||||
bottom: 32,
|
||||
)
|
||||
: const EdgeInsets.all(0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
mainAxisSize: isDesktop ? MainAxisSize.min : MainAxisSize.max,
|
||||
children: [
|
||||
if (!isDesktop)
|
||||
RoundedWhiteContainer(
|
||||
child: Text(
|
||||
"The new QR code with your address, amount and note will appear in the pop up window.",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
),
|
||||
),
|
||||
if (!isDesktop)
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
Text(
|
||||
"Amount (Optional)",
|
||||
style: STextStyles.smallMed12(context),
|
||||
style: isDesktop
|
||||
? STextStyles.desktopTextExtraExtraSmall(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveSearchIconRight,
|
||||
)
|
||||
: STextStyles.smallMed12(context),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
SizedBox(
|
||||
height: isDesktop ? 10 : 8,
|
||||
),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
|
@ -165,15 +345,30 @@ class _GenerateUriQrCodeViewState extends State<GenerateUriQrCodeView> {
|
|||
enableSuggestions: Util.isDesktop ? false : true,
|
||||
controller: amountController,
|
||||
focusNode: _amountFocusNode,
|
||||
style: STextStyles.field(context),
|
||||
keyboardType: const TextInputType.numberWithOptions(
|
||||
decimal: true),
|
||||
style: isDesktop
|
||||
? STextStyles.desktopTextExtraExtraSmall(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldDefaultText,
|
||||
height: 1.8,
|
||||
)
|
||||
: STextStyles.field(context),
|
||||
keyboardType:
|
||||
const TextInputType.numberWithOptions(decimal: true),
|
||||
onChanged: (_) => setState(() {}),
|
||||
decoration: standardInputDecoration(
|
||||
"Amount",
|
||||
_amountFocusNode,
|
||||
context,
|
||||
).copyWith(
|
||||
contentPadding: isDesktop
|
||||
? const EdgeInsets.only(
|
||||
left: 16,
|
||||
top: 11,
|
||||
bottom: 12,
|
||||
right: 5,
|
||||
)
|
||||
: null,
|
||||
suffixIcon: amountController.text.isNotEmpty
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(right: 0),
|
||||
|
@ -196,16 +391,22 @@ class _GenerateUriQrCodeViewState extends State<GenerateUriQrCodeView> {
|
|||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
SizedBox(
|
||||
height: isDesktop ? 20 : 12,
|
||||
),
|
||||
Text(
|
||||
"Note (Optional)",
|
||||
style: STextStyles.smallMed12(context),
|
||||
style: isDesktop
|
||||
? STextStyles.desktopTextExtraExtraSmall(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveSearchIconRight,
|
||||
)
|
||||
: STextStyles.smallMed12(context),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
SizedBox(
|
||||
height: isDesktop ? 10 : 8,
|
||||
),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
|
@ -216,13 +417,28 @@ class _GenerateUriQrCodeViewState extends State<GenerateUriQrCodeView> {
|
|||
enableSuggestions: Util.isDesktop ? false : true,
|
||||
controller: noteController,
|
||||
focusNode: _noteFocusNode,
|
||||
style: STextStyles.field(context),
|
||||
style: isDesktop
|
||||
? STextStyles.desktopTextExtraExtraSmall(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldDefaultText,
|
||||
height: 1.8,
|
||||
)
|
||||
: STextStyles.field(context),
|
||||
onChanged: (_) => setState(() {}),
|
||||
decoration: standardInputDecoration(
|
||||
"Note",
|
||||
_noteFocusNode,
|
||||
context,
|
||||
).copyWith(
|
||||
contentPadding: isDesktop
|
||||
? const EdgeInsets.only(
|
||||
left: 16,
|
||||
top: 11,
|
||||
bottom: 12,
|
||||
right: 5,
|
||||
)
|
||||
: null,
|
||||
suffixIcon: noteController.text.isNotEmpty
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(right: 0),
|
||||
|
@ -245,91 +461,63 @@ class _GenerateUriQrCodeViewState extends State<GenerateUriQrCodeView> {
|
|||
),
|
||||
),
|
||||
),
|
||||
// SizedBox()
|
||||
// Spacer(),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
SizedBox(
|
||||
height: isDesktop ? 20 : 8,
|
||||
),
|
||||
TextButton(
|
||||
style: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.getPrimaryEnabledButtonColor(context),
|
||||
onPressed: () {
|
||||
final amountString = amountController.text;
|
||||
final noteString = noteController.text;
|
||||
|
||||
if (amountString.isNotEmpty &&
|
||||
Decimal.tryParse(amountString) == null) {
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message: "Invalid amount",
|
||||
context: context,
|
||||
);
|
||||
PrimaryButton(
|
||||
label: "Generate QR code",
|
||||
onPressed: isDesktop
|
||||
? () {
|
||||
final uriString = _generateURI();
|
||||
if (uriString == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String query = "";
|
||||
|
||||
if (amountString.isNotEmpty) {
|
||||
query += "amount=$amountString";
|
||||
setState(() {
|
||||
didGenerate = true;
|
||||
_uriString = uriString;
|
||||
});
|
||||
}
|
||||
if (noteString.isNotEmpty) {
|
||||
if (query.isNotEmpty) {
|
||||
query += "&";
|
||||
}
|
||||
query += "message=$noteString";
|
||||
}
|
||||
|
||||
final uri = Uri(
|
||||
scheme: widget.coin.uriScheme,
|
||||
host: widget.receivingAddress,
|
||||
query: query.isNotEmpty ? query : null,
|
||||
);
|
||||
|
||||
final uriString =
|
||||
uri.toString().replaceFirst("://", ":");
|
||||
|
||||
Logging.instance.log(
|
||||
"Generated receiving QR code for: $uriString",
|
||||
level: LogLevel.Info);
|
||||
|
||||
showDialog<dynamic>(
|
||||
context: context,
|
||||
useSafeArea: false,
|
||||
barrierDismissible: true,
|
||||
builder: (_) {
|
||||
final width =
|
||||
MediaQuery.of(context).size.width / 2;
|
||||
return StackDialogBase(
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Center(
|
||||
child: Text(
|
||||
"New QR code",
|
||||
style:
|
||||
STextStyles.pageTitleH2(context),
|
||||
: onGeneratePressed,
|
||||
desktopMed: true,
|
||||
),
|
||||
if (isDesktop && didGenerate)
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
RoundedWhiteContainer(
|
||||
borderColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.background,
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
"New QR Code",
|
||||
style: STextStyles.desktopTextMedium(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
height: 16,
|
||||
),
|
||||
Center(
|
||||
child: RepaintBoundary(
|
||||
key: _qrKey,
|
||||
child: SizedBox(
|
||||
width: width + 20,
|
||||
height: width + 20,
|
||||
width: 234,
|
||||
height: 234,
|
||||
child: QrImage(
|
||||
data: uriString,
|
||||
size: width,
|
||||
backgroundColor: Theme.of(
|
||||
context)
|
||||
data: _uriString,
|
||||
size: 220,
|
||||
backgroundColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.popupBG,
|
||||
foregroundColor: Theme.of(
|
||||
context)
|
||||
foregroundColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark),
|
||||
),
|
||||
|
@ -338,79 +526,56 @@ class _GenerateUriQrCodeViewState extends State<GenerateUriQrCodeView> {
|
|||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
Center(
|
||||
child: SizedBox(
|
||||
width: width,
|
||||
child: TextButton(
|
||||
Row(
|
||||
children: [
|
||||
SecondaryButton(
|
||||
width: 170,
|
||||
desktopMed: true,
|
||||
onPressed: () async {
|
||||
// TODO: add save button as well
|
||||
await _capturePng(true);
|
||||
await _capturePng(false);
|
||||
},
|
||||
style: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.getSecondaryEnabledButtonColor(
|
||||
context),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.center,
|
||||
children: [
|
||||
Center(
|
||||
child: SvgPicture.asset(
|
||||
label: "Share",
|
||||
icon: SvgPicture.asset(
|
||||
Assets.svg.share,
|
||||
width: 14,
|
||||
height: 14,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 4,
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Text(
|
||||
"Share",
|
||||
textAlign:
|
||||
TextAlign.center,
|
||||
style: STextStyles.button(
|
||||
context)
|
||||
.copyWith(
|
||||
width: 20,
|
||||
height: 20,
|
||||
color: Theme.of(context)
|
||||
.extension<
|
||||
StackColors>()!
|
||||
.extension<StackColors>()!
|
||||
.buttonTextSecondary,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
width: 16,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
PrimaryButton(
|
||||
width: 170,
|
||||
desktopMed: true,
|
||||
onPressed: () async {
|
||||
// TODO: add save functionality instead of share
|
||||
await _capturePng(true);
|
||||
},
|
||||
);
|
||||
},
|
||||
child: Text(
|
||||
"Generate QR Code",
|
||||
style: STextStyles.button(context),
|
||||
label: "Save",
|
||||
icon: SvgPicture.asset(
|
||||
Assets.svg.arrowDown,
|
||||
width: 20,
|
||||
height: 20,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.buttonTextPrimary,
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -117,10 +117,12 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
|
|||
|
||||
case Coin.bitcoin:
|
||||
case Coin.bitcoincash:
|
||||
case Coin.litecoin:
|
||||
case Coin.dogecoin:
|
||||
case Coin.firo:
|
||||
case Coin.namecoin:
|
||||
case Coin.bitcoinTestNet:
|
||||
case Coin.litecoinTestNet:
|
||||
case Coin.bitcoincashTestnet:
|
||||
case Coin.firoTestNet:
|
||||
case Coin.dogecoinTestNet:
|
||||
|
@ -531,11 +533,13 @@ class _NodeFormState extends ConsumerState<NodeForm> {
|
|||
// TODO: which coin servers can have username and password?
|
||||
switch (coin) {
|
||||
case Coin.bitcoin:
|
||||
case Coin.litecoin:
|
||||
case Coin.dogecoin:
|
||||
case Coin.firo:
|
||||
case Coin.namecoin:
|
||||
case Coin.bitcoincash:
|
||||
case Coin.bitcoinTestNet:
|
||||
case Coin.litecoinTestNet:
|
||||
case Coin.bitcoincashTestnet:
|
||||
case Coin.firoTestNet:
|
||||
case Coin.dogecoinTestNet:
|
||||
|
|
|
@ -98,6 +98,7 @@ class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> {
|
|||
break;
|
||||
|
||||
case Coin.bitcoin:
|
||||
case Coin.litecoin:
|
||||
case Coin.dogecoin:
|
||||
case Coin.firo:
|
||||
case Coin.bitcoinTestNet:
|
||||
|
@ -105,6 +106,7 @@ class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> {
|
|||
case Coin.dogecoinTestNet:
|
||||
case Coin.bitcoincash:
|
||||
case Coin.namecoin:
|
||||
case Coin.litecoinTestNet:
|
||||
case Coin.bitcoincashTestnet:
|
||||
final client = ElectrumX(
|
||||
host: node!.host,
|
||||
|
|
|
@ -23,6 +23,7 @@ import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
|||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
|
||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
|
||||
|
@ -865,6 +866,20 @@ class _DesktopTransactionCardRowState
|
|||
);
|
||||
return;
|
||||
}
|
||||
if (Util.isDesktop) {
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
builder: (context) => DesktopDialog(
|
||||
maxHeight: MediaQuery.of(context).size.height - 64,
|
||||
maxWidth: 580,
|
||||
child: TransactionDetailsView(
|
||||
transaction: _transaction,
|
||||
coin: coin,
|
||||
walletId: walletId,
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
unawaited(
|
||||
Navigator.of(context).pushNamed(
|
||||
TransactionDetailsView.routeName,
|
||||
|
@ -875,6 +890,7 @@ class _DesktopTransactionCardRowState
|
|||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
|
|
|
@ -4,13 +4,14 @@ import 'package:stackwallet/providers/providers.dart';
|
|||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
|
||||
import 'package:stackwallet/widgets/stack_text_field.dart';
|
||||
import 'package:stackwallet/widgets/textfield_icon_button.dart';
|
||||
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
|
||||
class EditNoteView extends ConsumerStatefulWidget {
|
||||
const EditNoteView({
|
||||
Key? key,
|
||||
|
@ -33,8 +34,11 @@ class _EditNoteViewState extends ConsumerState<EditNoteView> {
|
|||
late final TextEditingController _noteController;
|
||||
final noteFieldFocusNode = FocusNode();
|
||||
|
||||
late final bool isDesktop;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
isDesktop = Util.isDesktop;
|
||||
_noteController = TextEditingController();
|
||||
_noteController.text = widget.note;
|
||||
super.initState();
|
||||
|
@ -50,15 +54,20 @@ class _EditNoteViewState extends ConsumerState<EditNoteView> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
backgroundColor: isDesktop
|
||||
? Colors.transparent
|
||||
: Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: isDesktop
|
||||
? null
|
||||
: AppBar(
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
leading: AppBarBackButton(
|
||||
onPressed: () async {
|
||||
if (FocusScope.of(context).hasFocus) {
|
||||
FocusScope.of(context).unfocus();
|
||||
await Future<void>.delayed(const Duration(milliseconds: 75));
|
||||
await Future<void>.delayed(
|
||||
const Duration(milliseconds: 75));
|
||||
}
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
|
@ -70,21 +79,34 @@ class _EditNoteViewState extends ConsumerState<EditNoteView> {
|
|||
style: STextStyles.navBarTitle(context),
|
||||
),
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: LayoutBuilder(builder: (context, constraints) {
|
||||
return SingleChildScrollView(
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: constraints.maxHeight,
|
||||
),
|
||||
child: IntrinsicHeight(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
body: MobileEditNoteScaffold(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
ClipRRect(
|
||||
if (isDesktop)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 32,
|
||||
bottom: 12,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Edit note",
|
||||
style: STextStyles.desktopH3(context),
|
||||
),
|
||||
const DesktopDialogCloseButton(),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.symmetric(
|
||||
horizontal: 32,
|
||||
)
|
||||
: const EdgeInsets.all(0),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
|
@ -92,13 +114,29 @@ class _EditNoteViewState extends ConsumerState<EditNoteView> {
|
|||
autocorrect: Util.isDesktop ? false : true,
|
||||
enableSuggestions: Util.isDesktop ? false : true,
|
||||
controller: _noteController,
|
||||
style: STextStyles.field(context),
|
||||
style: isDesktop
|
||||
? STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveText,
|
||||
height: 1.8,
|
||||
)
|
||||
: STextStyles.field(context),
|
||||
focusNode: noteFieldFocusNode,
|
||||
decoration: standardInputDecoration(
|
||||
"Note",
|
||||
noteFieldFocusNode,
|
||||
context,
|
||||
desktopMed: isDesktop,
|
||||
).copyWith(
|
||||
contentPadding: isDesktop
|
||||
? const EdgeInsets.only(
|
||||
left: 16,
|
||||
top: 11,
|
||||
bottom: 12,
|
||||
right: 5,
|
||||
)
|
||||
: null,
|
||||
suffixIcon: _noteController.text.isNotEmpty
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(right: 0),
|
||||
|
@ -121,12 +159,33 @@ class _EditNoteViewState extends ConsumerState<EditNoteView> {
|
|||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// if (!isDesktop)
|
||||
const Spacer(),
|
||||
if (isDesktop)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(32),
|
||||
child: PrimaryButton(
|
||||
label: "Save",
|
||||
onPressed: () async {
|
||||
await ref
|
||||
.read(
|
||||
notesServiceChangeNotifierProvider(widget.walletId))
|
||||
.editOrAddNote(
|
||||
txid: widget.txid,
|
||||
note: _noteController.text,
|
||||
);
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
if (!isDesktop)
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
await ref
|
||||
.read(notesServiceChangeNotifierProvider(
|
||||
widget.walletId))
|
||||
.read(notesServiceChangeNotifierProvider(widget.walletId))
|
||||
.editOrAddNote(
|
||||
txid: widget.txid,
|
||||
note: _noteController.text,
|
||||
|
@ -146,10 +205,43 @@ class _EditNoteViewState extends ConsumerState<EditNoteView> {
|
|||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MobileEditNoteScaffold extends StatelessWidget {
|
||||
const MobileEditNoteScaffold({
|
||||
Key? key,
|
||||
required this.child,
|
||||
}) : super(key: key);
|
||||
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (Util.isDesktop) {
|
||||
return child;
|
||||
} else {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
return SingleChildScrollView(
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: constraints.maxHeight,
|
||||
),
|
||||
child: IntrinsicHeight(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
));
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -3,6 +3,7 @@ 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/wallet_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/desktop_wallet_view.dart';
|
||||
import 'package:stackwallet/providers/providers.dart';
|
||||
import 'package:stackwallet/services/coins/manager.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
|
@ -11,6 +12,7 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
|||
import 'package:stackwallet/utilities/format.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
class FavoriteCard extends ConsumerStatefulWidget {
|
||||
|
@ -54,6 +56,12 @@ class _FavoriteCardState extends ConsumerState<FavoriteCard> {
|
|||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
if (Util.isDesktop) {
|
||||
Navigator.of(context).pushNamed(
|
||||
DesktopWalletView.routeName,
|
||||
arguments: walletId,
|
||||
);
|
||||
} else {
|
||||
Navigator.of(context).pushNamed(
|
||||
WalletView.routeName,
|
||||
arguments: Tuple2(
|
||||
|
@ -61,6 +69,7 @@ class _FavoriteCardState extends ConsumerState<FavoriteCard> {
|
|||
managerProvider,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: SizedBox(
|
||||
width: widget.width,
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -16,9 +16,13 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
|||
import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/custom_loading_overlay.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
class DesktopReceive extends ConsumerStatefulWidget {
|
||||
const DesktopReceive({
|
||||
|
@ -216,6 +220,44 @@ class _DesktopReceiveState extends ConsumerState<DesktopReceive> {
|
|||
// TODO: create transparent button class to account for hover
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
if (Util.isDesktop) {
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
builder: (context) => DesktopDialog(
|
||||
maxHeight: double.infinity,
|
||||
maxWidth: 580,
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
const AppBarBackButton(
|
||||
size: 40,
|
||||
iconSize: 24,
|
||||
),
|
||||
Text(
|
||||
"Generate QR code",
|
||||
style: STextStyles.desktopH3(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
IntrinsicHeight(
|
||||
child: Navigator(
|
||||
onGenerateRoute: RouteGenerator.generateRoute,
|
||||
onGenerateInitialRoutes: (_, __) => [
|
||||
RouteGenerator.generateRoute(
|
||||
RouteSettings(
|
||||
name: GenerateUriQrCodeView.routeName,
|
||||
arguments: Tuple2(coin, receivingAddress),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
unawaited(
|
||||
Navigator.of(context).push(
|
||||
RouteGenerator.getRoute(
|
||||
|
@ -230,6 +272,7 @@ class _DesktopReceiveState extends ConsumerState<DesktopReceive> {
|
|||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
|
@ -32,7 +32,10 @@ import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
|||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:stackwallet/widgets/animated_text.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/addressbook_icon.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/clipboard_icon.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/qrcode_icon.dart';
|
||||
|
@ -107,24 +110,56 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
|||
useSafeArea: false,
|
||||
barrierDismissible: true,
|
||||
builder: (context) {
|
||||
return StackDialog(
|
||||
title: "Transaction failed",
|
||||
message: "Sending to self is currently disabled",
|
||||
rightButton: TextButton(
|
||||
style: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.getSecondaryEnabledButtonColor(context),
|
||||
child: Text(
|
||||
"Ok",
|
||||
style: STextStyles.button(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark),
|
||||
return DesktopDialog(
|
||||
maxWidth: 400,
|
||||
maxHeight: double.infinity,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 32,
|
||||
bottom: 32,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Transaction failed",
|
||||
style: STextStyles.desktopH3(context),
|
||||
),
|
||||
const DesktopDialogCloseButton(),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
Text(
|
||||
"Sending to self is currently disabled",
|
||||
textAlign: TextAlign.left,
|
||||
style: STextStyles.desktopTextExtraExtraSmall(context)
|
||||
.copyWith(
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 40,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
right: 32,
|
||||
),
|
||||
child: SecondaryButton(
|
||||
desktopMed: true,
|
||||
label: "Ok",
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
@ -154,37 +189,79 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
|||
useSafeArea: false,
|
||||
barrierDismissible: true,
|
||||
builder: (context) {
|
||||
return StackDialog(
|
||||
title: "Confirm send all",
|
||||
message:
|
||||
"You are about to send your entire balance. Would you like to continue?",
|
||||
leftButton: TextButton(
|
||||
style: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.getSecondaryEnabledButtonColor(context),
|
||||
child: Text(
|
||||
"Cancel",
|
||||
style: STextStyles.button(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark),
|
||||
return DesktopDialog(
|
||||
maxWidth: 450,
|
||||
maxHeight: double.infinity,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 32,
|
||||
bottom: 32,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Confirm send all",
|
||||
style: STextStyles.desktopH3(context),
|
||||
),
|
||||
const DesktopDialogCloseButton(),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
right: 32,
|
||||
),
|
||||
child: Text(
|
||||
"You are about to send your entire balance. Would you like to continue?",
|
||||
textAlign: TextAlign.left,
|
||||
style: STextStyles.desktopTextExtraExtraSmall(context)
|
||||
.copyWith(
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 40,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
right: 32,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: SecondaryButton(
|
||||
desktopMed: true,
|
||||
label: "Cancel",
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(false);
|
||||
},
|
||||
),
|
||||
rightButton: TextButton(
|
||||
style: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.getPrimaryEnabledButtonColor(context),
|
||||
child: Text(
|
||||
"Yes",
|
||||
style: STextStyles.button(context),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
Expanded(
|
||||
child: PrimaryButton(
|
||||
desktopMed: true,
|
||||
label: "Yes",
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(true);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
|
@ -0,0 +1,89 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/desktop_receive.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/desktop_send.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/send_receive_tab_menu.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
|
||||
class MyWallet extends StatefulWidget {
|
||||
const MyWallet({
|
||||
Key? key,
|
||||
required this.walletId,
|
||||
}) : super(key: key);
|
||||
|
||||
final String walletId;
|
||||
|
||||
@override
|
||||
State<MyWallet> createState() => _MyWalletState();
|
||||
}
|
||||
|
||||
class _MyWalletState extends State<MyWallet> {
|
||||
int _selectedIndex = 0;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListView(
|
||||
primary: false,
|
||||
children: [
|
||||
Text(
|
||||
"My wallet",
|
||||
style: STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveSearchIconLeft,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).extension<StackColors>()!.popupBG,
|
||||
borderRadius: BorderRadius.vertical(
|
||||
top: Radius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: SendReceiveTabMenu(
|
||||
onChanged: (index) {
|
||||
setState(() {
|
||||
_selectedIndex = index;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).extension<StackColors>()!.popupBG,
|
||||
borderRadius: BorderRadius.vertical(
|
||||
bottom: Radius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: IndexedStack(
|
||||
index: _selectedIndex,
|
||||
children: [
|
||||
Padding(
|
||||
key: const Key("desktopSendViewPortKey"),
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: DesktopSend(
|
||||
walletId: widget.walletId,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
key: const Key("desktopReceiveViewPortKey"),
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: DesktopReceive(
|
||||
walletId: widget.walletId,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,176 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:event_bus/event_bus.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_network_settings_view/wallet_network_settings_view.dart';
|
||||
import 'package:stackwallet/providers/providers.dart';
|
||||
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_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/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
class NetworkInfoButton extends ConsumerStatefulWidget {
|
||||
const NetworkInfoButton({
|
||||
Key? key,
|
||||
required this.walletId,
|
||||
this.eventBus,
|
||||
}) : super(key: key);
|
||||
|
||||
final String walletId;
|
||||
final EventBus? eventBus;
|
||||
|
||||
@override
|
||||
ConsumerState<NetworkInfoButton> createState() => _NetworkInfoButtonState();
|
||||
}
|
||||
|
||||
class _NetworkInfoButtonState extends ConsumerState<NetworkInfoButton> {
|
||||
late final String walletId;
|
||||
late final EventBus eventBus;
|
||||
|
||||
late WalletSyncStatus _currentSyncStatus;
|
||||
late NodeConnectionStatus _currentNodeStatus;
|
||||
|
||||
late StreamSubscription<dynamic> _syncStatusSubscription;
|
||||
late StreamSubscription<dynamic> _nodeStatusSubscription;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
walletId = widget.walletId;
|
||||
final managerProvider =
|
||||
ref.read(walletsChangeNotifierProvider).getManagerProvider(walletId);
|
||||
|
||||
eventBus =
|
||||
widget.eventBus != null ? widget.eventBus! : GlobalEventBus.instance;
|
||||
|
||||
if (ref.read(managerProvider).isRefreshing) {
|
||||
_currentSyncStatus = WalletSyncStatus.syncing;
|
||||
_currentNodeStatus = NodeConnectionStatus.connected;
|
||||
} else {
|
||||
_currentSyncStatus = WalletSyncStatus.synced;
|
||||
if (ref.read(managerProvider).isConnected) {
|
||||
_currentNodeStatus = NodeConnectionStatus.connected;
|
||||
} else {
|
||||
_currentNodeStatus = NodeConnectionStatus.disconnected;
|
||||
_currentSyncStatus = WalletSyncStatus.unableToSync;
|
||||
}
|
||||
}
|
||||
|
||||
_syncStatusSubscription =
|
||||
eventBus.on<WalletSyncStatusChangedEvent>().listen(
|
||||
(event) async {
|
||||
if (event.walletId == widget.walletId) {
|
||||
setState(() {
|
||||
_currentSyncStatus = event.newStatus;
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
_nodeStatusSubscription =
|
||||
eventBus.on<NodeConnectionStatusChangedEvent>().listen(
|
||||
(event) async {
|
||||
if (event.walletId == widget.walletId) {
|
||||
setState(() {
|
||||
_currentNodeStatus = event.newStatus;
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_nodeStatusSubscription.cancel();
|
||||
_syncStatusSubscription.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Widget _buildNetworkIcon(WalletSyncStatus status, BuildContext context) {
|
||||
const size = 24.0;
|
||||
switch (status) {
|
||||
case WalletSyncStatus.unableToSync:
|
||||
return SvgPicture.asset(
|
||||
Assets.svg.radioProblem,
|
||||
color: Theme.of(context).extension<StackColors>()!.accentColorRed,
|
||||
width: size,
|
||||
height: size,
|
||||
);
|
||||
case WalletSyncStatus.synced:
|
||||
return SvgPicture.asset(
|
||||
Assets.svg.radio,
|
||||
color: Theme.of(context).extension<StackColors>()!.accentColorGreen,
|
||||
width: size,
|
||||
height: size,
|
||||
);
|
||||
case WalletSyncStatus.syncing:
|
||||
return SvgPicture.asset(
|
||||
Assets.svg.radioSyncing,
|
||||
color: Theme.of(context).extension<StackColors>()!.accentColorYellow,
|
||||
width: size,
|
||||
height: size,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildText(WalletSyncStatus status, BuildContext context) {
|
||||
String label;
|
||||
Color color;
|
||||
|
||||
switch (status) {
|
||||
case WalletSyncStatus.unableToSync:
|
||||
label = "Unable to sync";
|
||||
color = Theme.of(context).extension<StackColors>()!.accentColorRed;
|
||||
break;
|
||||
case WalletSyncStatus.synced:
|
||||
label = "Synchronised";
|
||||
color = Theme.of(context).extension<StackColors>()!.accentColorGreen;
|
||||
break;
|
||||
case WalletSyncStatus.syncing:
|
||||
label = "Synchronising";
|
||||
color = Theme.of(context).extension<StackColors>()!.accentColorYellow;
|
||||
break;
|
||||
}
|
||||
|
||||
return Text(
|
||||
label,
|
||||
style: STextStyles.desktopMenuItemSelected(context).copyWith(
|
||||
color: color,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.of(context).pushNamed(
|
||||
WalletNetworkSettingsView.routeName,
|
||||
arguments: Tuple3(
|
||||
walletId,
|
||||
_currentSyncStatus,
|
||||
_currentNodeStatus,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: Row(
|
||||
children: [
|
||||
_buildNetworkIcon(_currentSyncStatus, context),
|
||||
const SizedBox(
|
||||
width: 6,
|
||||
),
|
||||
_buildText(_currentSyncStatus, context),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
|
||||
|
||||
class QRCodeDesktopPopupContent extends StatelessWidget {
|
||||
const QRCodeDesktopPopupContent({
|
||||
Key? key,
|
||||
required this.value,
|
||||
}) : super(key: key);
|
||||
|
||||
final String value;
|
||||
|
||||
static const String routeName = "qrCodeDesktopPopupContent";
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DesktopDialog(
|
||||
maxWidth: 614,
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: const [
|
||||
DesktopDialogCloseButton(),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 14,
|
||||
),
|
||||
QrImage(
|
||||
data: value,
|
||||
size: 300,
|
||||
foregroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.accentColorDark,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:stackwallet/pages/wallet_view/sub_widgets/transactions_list.dart';
|
||||
import 'package:stackwallet/pages/wallet_view/transaction_views/all_transactions_view.dart';
|
||||
import 'package:stackwallet/providers/providers.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
||||
|
||||
class RecentDesktopTransactions extends ConsumerStatefulWidget {
|
||||
const RecentDesktopTransactions({
|
||||
Key? key,
|
||||
required this.walletId,
|
||||
}) : super(key: key);
|
||||
|
||||
final String walletId;
|
||||
|
||||
@override
|
||||
ConsumerState<RecentDesktopTransactions> createState() =>
|
||||
_RecentDesktopTransactionsState();
|
||||
}
|
||||
|
||||
class _RecentDesktopTransactionsState
|
||||
extends ConsumerState<RecentDesktopTransactions> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Recent transactions",
|
||||
style: STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveSearchIconLeft,
|
||||
),
|
||||
),
|
||||
BlueTextButton(
|
||||
text: "See all",
|
||||
onTap: () {
|
||||
Navigator.of(context).pushNamed(
|
||||
AllTransactionsView.routeName,
|
||||
arguments: widget.walletId,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
Expanded(
|
||||
child: TransactionsList(
|
||||
managerProvider: ref.watch(walletsChangeNotifierProvider
|
||||
.select((value) => value.getManagerProvider(widget.walletId))),
|
||||
walletId: widget.walletId,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
|
||||
class SendReceiveTabMenu extends StatefulWidget {
|
||||
const SendReceiveTabMenu({
|
||||
Key? key,
|
||||
this.initialIndex = 0,
|
||||
this.onChanged,
|
||||
}) : super(key: key);
|
||||
|
||||
final int initialIndex;
|
||||
final void Function(int)? onChanged;
|
||||
|
||||
@override
|
||||
State<SendReceiveTabMenu> createState() => _SendReceiveTabMenuState();
|
||||
}
|
||||
|
||||
class _SendReceiveTabMenuState extends State<SendReceiveTabMenu> {
|
||||
late int _selectedIndex;
|
||||
|
||||
void _onChanged(int newIndex) {
|
||||
if (_selectedIndex != newIndex) {
|
||||
setState(() {
|
||||
_selectedIndex = newIndex;
|
||||
});
|
||||
widget.onChanged?.call(_selectedIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_selectedIndex = widget.initialIndex;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: () => _onChanged(0),
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
Text(
|
||||
"Send",
|
||||
style: STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||
color: _selectedIndex == 0
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorBlue
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 19,
|
||||
),
|
||||
Container(
|
||||
height: 2,
|
||||
decoration: BoxDecoration(
|
||||
color: _selectedIndex == 0
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorBlue
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.background,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: () => _onChanged(1),
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
Text(
|
||||
"Receive",
|
||||
style: STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||
color: _selectedIndex == 1
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorBlue
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 19,
|
||||
),
|
||||
Container(
|
||||
height: 2,
|
||||
decoration: BoxDecoration(
|
||||
color: _selectedIndex == 1
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorBlue
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.background,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,228 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/wallet_keys_desktop_popup.dart';
|
||||
import 'package:stackwallet/providers/providers.dart';
|
||||
import 'package:stackwallet/route_generator.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||
import 'package:stackwallet/widgets/stack_text_field.dart';
|
||||
|
||||
class UnlockWalletKeysDesktop extends ConsumerStatefulWidget {
|
||||
const UnlockWalletKeysDesktop({
|
||||
Key? key,
|
||||
required this.walletId,
|
||||
}) : super(key: key);
|
||||
|
||||
final String walletId;
|
||||
|
||||
@override
|
||||
ConsumerState<UnlockWalletKeysDesktop> createState() =>
|
||||
_UnlockWalletKeysDesktopState();
|
||||
}
|
||||
|
||||
class _UnlockWalletKeysDesktopState
|
||||
extends ConsumerState<UnlockWalletKeysDesktop> {
|
||||
late final TextEditingController passwordController;
|
||||
|
||||
late final FocusNode passwordFocusNode;
|
||||
|
||||
bool continueEnabled = false;
|
||||
bool hidePassword = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
passwordController = TextEditingController();
|
||||
passwordFocusNode = FocusNode();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
passwordController.dispose();
|
||||
passwordFocusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DesktopDialog(
|
||||
maxWidth: 579,
|
||||
maxHeight: double.infinity,
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: const [
|
||||
DesktopDialogCloseButton(),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
SvgPicture.asset(
|
||||
Assets.svg.keys,
|
||||
width: 100,
|
||||
height: 58,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 55,
|
||||
),
|
||||
Text(
|
||||
"Wallet keys",
|
||||
style: STextStyles.desktopH2(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
Text(
|
||||
"Enter your password",
|
||||
style: STextStyles.desktopTextMedium(context).copyWith(
|
||||
color: Theme.of(context).extension<StackColors>()!.textDark3,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 32,
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
child: TextField(
|
||||
key: const Key("enterPasswordUnlockWalletKeysDesktopFieldKey"),
|
||||
focusNode: passwordFocusNode,
|
||||
controller: passwordController,
|
||||
style: STextStyles.desktopTextMedium(context).copyWith(
|
||||
height: 2,
|
||||
),
|
||||
obscureText: hidePassword,
|
||||
enableSuggestions: false,
|
||||
autocorrect: false,
|
||||
decoration: standardInputDecoration(
|
||||
"Enter password",
|
||||
passwordFocusNode,
|
||||
context,
|
||||
).copyWith(
|
||||
suffixIcon: UnconstrainedBox(
|
||||
child: SizedBox(
|
||||
height: 70,
|
||||
child: Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
key: const Key(
|
||||
"enterUnlockWalletKeysDesktopFieldShowPasswordButtonKey"),
|
||||
onTap: () async {
|
||||
setState(() {
|
||||
hidePassword = !hidePassword;
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(1000),
|
||||
),
|
||||
height: 32,
|
||||
width: 32,
|
||||
child: Center(
|
||||
child: SvgPicture.asset(
|
||||
hidePassword
|
||||
? Assets.svg.eye
|
||||
: Assets.svg.eyeSlash,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark3,
|
||||
width: 24,
|
||||
height: 19,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
onChanged: (newValue) {
|
||||
setState(() {
|
||||
continueEnabled = newValue.isNotEmpty;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 55,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 32,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: SecondaryButton(
|
||||
label: "Cancel",
|
||||
onPressed: Navigator.of(context).pop,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
Expanded(
|
||||
child: PrimaryButton(
|
||||
label: "Continue",
|
||||
enabled: continueEnabled,
|
||||
onPressed: continueEnabled
|
||||
? () async {
|
||||
// todo: check password
|
||||
Navigator.of(context).pop();
|
||||
final words = await ref
|
||||
.read(walletsChangeNotifierProvider)
|
||||
.getManager(widget.walletId)
|
||||
.mnemonic;
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (context) => Navigator(
|
||||
initialRoute: WalletKeysDesktopPopup.routeName,
|
||||
onGenerateRoute: RouteGenerator.generateRoute,
|
||||
onGenerateInitialRoutes: (_, __) {
|
||||
return [
|
||||
RouteGenerator.generateRoute(
|
||||
RouteSettings(
|
||||
name: WalletKeysDesktopPopup.routeName,
|
||||
arguments: words,
|
||||
),
|
||||
)
|
||||
];
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
: null,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 32,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/unlock_wallet_keys_desktop.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
|
||||
class WalletKeysButton extends StatelessWidget {
|
||||
const WalletKeysButton({
|
||||
Key? key,
|
||||
required this.walletId,
|
||||
}) : super(key: key);
|
||||
|
||||
final String walletId;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (context) => UnlockWalletKeysDesktop(
|
||||
walletId: walletId,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: Row(
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
Assets.svg.key,
|
||||
width: 20,
|
||||
height: 20,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.buttonTextSecondary,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 6,
|
||||
),
|
||||
Text(
|
||||
"Wallet keys",
|
||||
style: STextStyles.desktopMenuItemSelected(context),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
||||
import 'package:stackwallet/pages/add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/qr_code_desktop_popup_content.dart';
|
||||
import 'package:stackwallet/utilities/address_utils.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/clipboard_interface.dart';
|
||||
import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||
|
||||
class WalletKeysDesktopPopup extends StatelessWidget {
|
||||
const WalletKeysDesktopPopup({
|
||||
Key? key,
|
||||
required this.words,
|
||||
this.clipboardInterface = const ClipboardWrapper(),
|
||||
}) : super(key: key);
|
||||
|
||||
final List<String> words;
|
||||
final ClipboardInterface clipboardInterface;
|
||||
|
||||
static const String routeName = "walletKeysDesktopPopup";
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DesktopDialog(
|
||||
maxWidth: 614,
|
||||
maxHeight: double.infinity,
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 32,
|
||||
),
|
||||
child: Text(
|
||||
"Wallet keys",
|
||||
style: STextStyles.desktopH3(context),
|
||||
),
|
||||
),
|
||||
DesktopDialogCloseButton(
|
||||
onPressedOverride: () {
|
||||
Navigator.of(context, rootNavigator: true).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 28,
|
||||
),
|
||||
Text(
|
||||
"Recovery phrase",
|
||||
style: STextStyles.desktopTextMedium(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 32,
|
||||
),
|
||||
child: Text(
|
||||
"Please write down your recovery phrase in the correct order and save it to keep your funds secure. You will also be asked to verify the words on the next screen.",
|
||||
style: STextStyles.desktopTextExtraExtraSmall(context),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 32,
|
||||
),
|
||||
child: MnemonicTable(
|
||||
words: words,
|
||||
isDesktop: true,
|
||||
itemBorderColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.buttonBackSecondary,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 32,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: SecondaryButton(
|
||||
label: "Show QR code",
|
||||
onPressed: () {
|
||||
final String value = AddressUtils.encodeQRSeedData(words);
|
||||
Navigator.of(context).pushNamed(
|
||||
QRCodeDesktopPopupContent.routeName,
|
||||
arguments: value,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
Expanded(
|
||||
child: PrimaryButton(
|
||||
label: "Copy",
|
||||
onPressed: () async {
|
||||
await clipboardInterface.setData(
|
||||
ClipboardData(text: words.join(" ")),
|
||||
);
|
||||
unawaited(
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.info,
|
||||
message: "Copied to clipboard",
|
||||
iconAsset: Assets.svg.copy,
|
||||
context: context,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 32,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -50,7 +50,7 @@ class _CreateAutoBackup extends State<CreateAutoBackup> {
|
|||
passphraseFocusNode = FocusNode();
|
||||
passphraseRepeatFocusNode = FocusNode();
|
||||
|
||||
super.initState();
|
||||
// super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -63,7 +63,7 @@ class _CreateAutoBackup extends State<CreateAutoBackup> {
|
|||
passphraseFocusNode.dispose();
|
||||
passphraseRepeatFocusNode.dispose();
|
||||
|
||||
super.dispose();
|
||||
// super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -37,6 +37,7 @@ import 'package:stackwallet/pages/intro_view.dart';
|
|||
import 'package:stackwallet/pages/manage_favorites_view/manage_favorites_view.dart';
|
||||
import 'package:stackwallet/pages/notification_views/notifications_view.dart';
|
||||
import 'package:stackwallet/pages/pinpad_views/create_pin_view.dart';
|
||||
import 'package:stackwallet/pages/receive_view/generate_receiving_uri_qr_code_view.dart';
|
||||
import 'package:stackwallet/pages/receive_view/receive_view.dart';
|
||||
import 'package:stackwallet/pages/send_view/send_view.dart';
|
||||
import 'package:stackwallet/pages/settings_views/global_settings_view/about_view.dart';
|
||||
|
@ -87,6 +88,8 @@ import 'package:stackwallet/pages_desktop_specific/home/desktop_home_view.dart';
|
|||
import 'package:stackwallet/pages_desktop_specific/home/desktop_settings_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/my_stack_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/desktop_wallet_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/qr_code_desktop_popup_content.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/wallet_keys_desktop_popup.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/settings_menu/advanced_settings.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/settings_menu/appearance_settings.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/settings_menu/backup_and_restore/backup_and_restore_settings.dart';
|
||||
|
@ -953,6 +956,21 @@ class RouteGenerator {
|
|||
}
|
||||
return _routeError("${settings.name} invalid args: ${args.toString()}");
|
||||
|
||||
case GenerateUriQrCodeView.routeName:
|
||||
if (args is Tuple2<Coin, String>) {
|
||||
return getRoute(
|
||||
shouldUseMaterialRoute: useMaterialPageRoute,
|
||||
builder: (_) => GenerateUriQrCodeView(
|
||||
coin: args.item1,
|
||||
receivingAddress: args.item2,
|
||||
),
|
||||
settings: RouteSettings(
|
||||
name: settings.name,
|
||||
),
|
||||
);
|
||||
}
|
||||
return _routeError("${settings.name} invalid args: ${args.toString()}");
|
||||
|
||||
// == Desktop specific routes ============================================
|
||||
case CreatePasswordView.routeName:
|
||||
return getRoute(
|
||||
|
|
|
@ -15,6 +15,8 @@ import 'package:stackwallet/services/transaction_notification_tracker.dart';
|
|||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/utilities/prefs.dart';
|
||||
|
||||
import 'litecoin/litecoin_wallet.dart';
|
||||
|
||||
abstract class CoinServiceAPI {
|
||||
CoinServiceAPI();
|
||||
|
||||
|
@ -90,6 +92,26 @@ abstract class CoinServiceAPI {
|
|||
tracker: tracker,
|
||||
);
|
||||
|
||||
case Coin.litecoin:
|
||||
return LitecoinWallet(
|
||||
walletId: walletId,
|
||||
walletName: walletName,
|
||||
coin: coin,
|
||||
client: client,
|
||||
cachedClient: cachedClient,
|
||||
tracker: tracker,
|
||||
);
|
||||
|
||||
case Coin.litecoinTestNet:
|
||||
return LitecoinWallet(
|
||||
walletId: walletId,
|
||||
walletName: walletName,
|
||||
coin: coin,
|
||||
client: client,
|
||||
cachedClient: cachedClient,
|
||||
tracker: tracker,
|
||||
);
|
||||
|
||||
case Coin.bitcoinTestNet:
|
||||
return BitcoinWallet(
|
||||
walletId: walletId,
|
||||
|
|
3828
lib/services/coins/litecoin/litecoin_wallet.dart
Normal file
3828
lib/services/coins/litecoin/litecoin_wallet.dart
Normal file
File diff suppressed because it is too large
Load diff
|
@ -87,7 +87,7 @@ class PriceAPI {
|
|||
Map<Coin, Tuple2<Decimal, double>> result = {};
|
||||
try {
|
||||
final uri = Uri.parse(
|
||||
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=${baseCurrency.toLowerCase()}&ids=monero,bitcoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin,wownero&order=market_cap_desc&per_page=10&page=1&sparkline=false");
|
||||
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=${baseCurrency.toLowerCase()}&ids=monero,bitcoin,litecoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin,wownero&order=market_cap_desc&per_page=10&page=1&sparkline=false");
|
||||
// final uri = Uri.parse(
|
||||
// "https://api.coingecko.com/api/v3/coins/markets?vs_currency=${baseCurrency.toLowerCase()}&ids=monero%2Cbitcoin%2Cepic-cash%2Czcoin%2Cdogecoin&order=market_cap_desc&per_page=10&page=1&sparkline=false");
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'package:flutter_libepiccash/epic_cash.dart';
|
|||
import 'package:stackwallet/services/coins/bitcoincash/bitcoincash_wallet.dart';
|
||||
import 'package:stackwallet/services/coins/dogecoin/dogecoin_wallet.dart';
|
||||
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
|
||||
import 'package:stackwallet/services/coins/litecoin/litecoin_wallet.dart';
|
||||
import 'package:stackwallet/services/coins/namecoin/namecoin_wallet.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/utilities/logger.dart';
|
||||
|
@ -42,6 +43,8 @@ class AddressUtils {
|
|||
switch (coin) {
|
||||
case Coin.bitcoin:
|
||||
return Address.validateAddress(address, bitcoin);
|
||||
case Coin.litecoin:
|
||||
return Address.validateAddress(address, litecoin);
|
||||
case Coin.bitcoincash:
|
||||
return Address.validateAddress(address, bitcoincash);
|
||||
case Coin.dogecoin:
|
||||
|
@ -60,6 +63,8 @@ class AddressUtils {
|
|||
return Address.validateAddress(address, namecoin, namecoin.bech32!);
|
||||
case Coin.bitcoinTestNet:
|
||||
return Address.validateAddress(address, testnet);
|
||||
case Coin.litecoinTestNet:
|
||||
return Address.validateAddress(address, litecointestnet);
|
||||
case Coin.bitcoincashTestnet:
|
||||
return Address.validateAddress(address, bitcoincashtestnet);
|
||||
case Coin.firoTestNet:
|
||||
|
|
|
@ -145,11 +145,13 @@ class _SVG {
|
|||
String get walletDesktop => "assets/svg/wallet-desktop.svg";
|
||||
String get exitDesktop => "assets/svg/exit-desktop.svg";
|
||||
String get keys => "assets/svg/keys.svg";
|
||||
String get arrowDown => "assets/svg/arrow-down.svg";
|
||||
|
||||
String get ellipse1 => "assets/svg/Ellipse-43.svg";
|
||||
String get ellipse2 => "assets/svg/Ellipse-42.svg";
|
||||
|
||||
String get bitcoin => "assets/svg/coin_icons/Bitcoin.svg";
|
||||
String get litecoin => "assets/svg/coin_icons/Litecoin.svg";
|
||||
String get bitcoincash => "assets/svg/coin_icons/Bitcoincash.svg";
|
||||
String get dogecoin => "assets/svg/coin_icons/Dogecoin.svg";
|
||||
String get epicCash => "assets/svg/coin_icons/EpicCash.svg";
|
||||
|
@ -174,6 +176,9 @@ class _SVG {
|
|||
switch (coin) {
|
||||
case Coin.bitcoin:
|
||||
return bitcoin;
|
||||
case Coin.litecoin:
|
||||
case Coin.litecoinTestNet:
|
||||
return litecoin;
|
||||
case Coin.bitcoincash:
|
||||
return bitcoincash;
|
||||
case Coin.dogecoin:
|
||||
|
@ -211,6 +216,7 @@ class _PNG {
|
|||
String get firo => "assets/images/firo.png";
|
||||
String get dogecoin => "assets/images/doge.png";
|
||||
String get bitcoin => "assets/images/bitcoin.png";
|
||||
String get litecoin => "assets/images/litecoin.png";
|
||||
String get epicCash => "assets/images/epic-cash.png";
|
||||
String get bitcoincash => "assets/images/bitcoincash.png";
|
||||
String get namecoin => "assets/images/namecoin.png";
|
||||
|
@ -220,6 +226,9 @@ class _PNG {
|
|||
case Coin.bitcoin:
|
||||
case Coin.bitcoinTestNet:
|
||||
return bitcoin;
|
||||
case Coin.litecoin:
|
||||
case Coin.litecoinTestNet:
|
||||
return litecoin;
|
||||
case Coin.bitcoincash:
|
||||
case Coin.bitcoincashTestnet:
|
||||
return bitcoincash;
|
||||
|
|
|
@ -7,6 +7,10 @@ Uri getBlockExplorerTransactionUrlFor({
|
|||
switch (coin) {
|
||||
case Coin.bitcoin:
|
||||
return Uri.parse("https://chain.so/tx/BTC/$txid");
|
||||
case Coin.litecoin:
|
||||
return Uri.parse("https://chain.so/tx/LTC/$txid");
|
||||
case Coin.litecoinTestNet:
|
||||
return Uri.parse("https://chain.so/tx/LTCTEST/$txid");
|
||||
case Coin.bitcoinTestNet:
|
||||
return Uri.parse("https://chain.so/tx/BTCTEST/$txid");
|
||||
case Coin.dogecoin:
|
||||
|
|
|
@ -48,6 +48,8 @@ abstract class Constants {
|
|||
final List<int> values = [];
|
||||
switch (coin) {
|
||||
case Coin.bitcoin:
|
||||
case Coin.litecoin:
|
||||
case Coin.litecoinTestNet:
|
||||
case Coin.bitcoincash:
|
||||
case Coin.bitcoincashTestnet:
|
||||
case Coin.dogecoin:
|
||||
|
@ -85,6 +87,10 @@ abstract class Constants {
|
|||
case Coin.dogecoinTestNet:
|
||||
return 60;
|
||||
|
||||
case Coin.litecoin:
|
||||
case Coin.litecoinTestNet:
|
||||
return 150;
|
||||
|
||||
case Coin.firo:
|
||||
case Coin.firoTestNet:
|
||||
return 150;
|
||||
|
|
|
@ -9,6 +9,7 @@ abstract class DefaultNodes {
|
|||
|
||||
static List<NodeModel> get all => [
|
||||
bitcoin,
|
||||
litecoin,
|
||||
dogecoin,
|
||||
firo,
|
||||
monero,
|
||||
|
@ -17,6 +18,7 @@ abstract class DefaultNodes {
|
|||
namecoin,
|
||||
wownero,
|
||||
bitcoinTestnet,
|
||||
litecoinTestNet,
|
||||
bitcoincashTestnet,
|
||||
dogecoinTestnet,
|
||||
firoTestnet,
|
||||
|
@ -34,6 +36,30 @@ abstract class DefaultNodes {
|
|||
isDown: false,
|
||||
);
|
||||
|
||||
static NodeModel get litecoin => NodeModel(
|
||||
host: "litecoin.stackwallet.com",
|
||||
port: 20063,
|
||||
name: defaultName,
|
||||
id: _nodeId(Coin.litecoin),
|
||||
useSSL: true,
|
||||
enabled: true,
|
||||
coinName: Coin.litecoin.name,
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
);
|
||||
|
||||
static NodeModel get litecoinTestNet => NodeModel(
|
||||
host: "litecoin.stackwallet.com",
|
||||
port: 51002,
|
||||
name: defaultName,
|
||||
id: _nodeId(Coin.litecoinTestNet),
|
||||
useSSL: true,
|
||||
enabled: true,
|
||||
coinName: Coin.litecoinTestNet.name,
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
);
|
||||
|
||||
static NodeModel get bitcoincash => NodeModel(
|
||||
host: "bitcoincash.stackwallet.com",
|
||||
port: 50002,
|
||||
|
@ -171,6 +197,9 @@ abstract class DefaultNodes {
|
|||
case Coin.bitcoin:
|
||||
return bitcoin;
|
||||
|
||||
case Coin.litecoin:
|
||||
return litecoin;
|
||||
|
||||
case Coin.bitcoincash:
|
||||
return bitcoincash;
|
||||
|
||||
|
@ -195,6 +224,9 @@ abstract class DefaultNodes {
|
|||
case Coin.bitcoinTestNet:
|
||||
return bitcoinTestnet;
|
||||
|
||||
case Coin.litecoinTestNet:
|
||||
return litecoinTestNet;
|
||||
|
||||
case Coin.bitcoincashTestnet:
|
||||
return bitcoincashTestnet;
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@ import 'package:stackwallet/services/coins/dogecoin/dogecoin_wallet.dart'
|
|||
import 'package:stackwallet/services/coins/epiccash/epiccash_wallet.dart'
|
||||
as epic;
|
||||
import 'package:stackwallet/services/coins/firo/firo_wallet.dart' as firo;
|
||||
import 'package:stackwallet/services/coins/litecoin/litecoin_wallet.dart'
|
||||
as ltc;
|
||||
import 'package:stackwallet/services/coins/monero/monero_wallet.dart' as xmr;
|
||||
import 'package:stackwallet/services/coins/namecoin/namecoin_wallet.dart'
|
||||
as nmc;
|
||||
|
@ -17,28 +19,33 @@ enum Coin {
|
|||
dogecoin,
|
||||
epicCash,
|
||||
firo,
|
||||
litecoin,
|
||||
monero,
|
||||
wownero,
|
||||
namecoin,
|
||||
wownero,
|
||||
|
||||
///
|
||||
|
||||
///
|
||||
///
|
||||
|
||||
bitcoinTestNet,
|
||||
litecoinTestNet,
|
||||
bitcoincashTestnet,
|
||||
dogecoinTestNet,
|
||||
firoTestNet,
|
||||
}
|
||||
|
||||
// remove firotestnet for now
|
||||
const int kTestNetCoinCount = 3;
|
||||
const int kTestNetCoinCount = 4;
|
||||
|
||||
extension CoinExt on Coin {
|
||||
String get prettyName {
|
||||
switch (this) {
|
||||
case Coin.bitcoin:
|
||||
return "Bitcoin";
|
||||
case Coin.litecoin:
|
||||
return "Litecoin";
|
||||
case Coin.bitcoincash:
|
||||
return "Bitcoin Cash";
|
||||
case Coin.dogecoin:
|
||||
|
@ -55,6 +62,8 @@ extension CoinExt on Coin {
|
|||
return "Namecoin";
|
||||
case Coin.bitcoinTestNet:
|
||||
return "tBitcoin";
|
||||
case Coin.litecoinTestNet:
|
||||
return "tLitecoin";
|
||||
case Coin.bitcoincashTestnet:
|
||||
return "tBitcoin Cash";
|
||||
case Coin.firoTestNet:
|
||||
|
@ -68,6 +77,8 @@ extension CoinExt on Coin {
|
|||
switch (this) {
|
||||
case Coin.bitcoin:
|
||||
return "BTC";
|
||||
case Coin.litecoin:
|
||||
return "LTC";
|
||||
case Coin.bitcoincash:
|
||||
return "BCH";
|
||||
case Coin.dogecoin:
|
||||
|
@ -84,6 +95,8 @@ extension CoinExt on Coin {
|
|||
return "NMC";
|
||||
case Coin.bitcoinTestNet:
|
||||
return "tBTC";
|
||||
case Coin.litecoinTestNet:
|
||||
return "tLTC";
|
||||
case Coin.bitcoincashTestnet:
|
||||
return "tBCH";
|
||||
case Coin.firoTestNet:
|
||||
|
@ -97,6 +110,8 @@ extension CoinExt on Coin {
|
|||
switch (this) {
|
||||
case Coin.bitcoin:
|
||||
return "bitcoin";
|
||||
case Coin.litecoin:
|
||||
return "litecoin";
|
||||
case Coin.bitcoincash:
|
||||
return "bitcoincash";
|
||||
case Coin.dogecoin:
|
||||
|
@ -114,6 +129,8 @@ extension CoinExt on Coin {
|
|||
return "namecoin";
|
||||
case Coin.bitcoinTestNet:
|
||||
return "bitcoin";
|
||||
case Coin.litecoinTestNet:
|
||||
return "litecoin";
|
||||
case Coin.bitcoincashTestnet:
|
||||
return "bitcoincash";
|
||||
case Coin.firoTestNet:
|
||||
|
@ -126,11 +143,13 @@ extension CoinExt on Coin {
|
|||
bool get isElectrumXCoin {
|
||||
switch (this) {
|
||||
case Coin.bitcoin:
|
||||
case Coin.litecoin:
|
||||
case Coin.bitcoincash:
|
||||
case Coin.dogecoin:
|
||||
case Coin.firo:
|
||||
case Coin.namecoin:
|
||||
case Coin.bitcoinTestNet:
|
||||
case Coin.litecoinTestNet:
|
||||
case Coin.bitcoincashTestnet:
|
||||
case Coin.firoTestNet:
|
||||
case Coin.dogecoinTestNet:
|
||||
|
@ -149,6 +168,10 @@ extension CoinExt on Coin {
|
|||
case Coin.bitcoinTestNet:
|
||||
return btc.MINIMUM_CONFIRMATIONS;
|
||||
|
||||
case Coin.litecoin:
|
||||
case Coin.litecoinTestNet:
|
||||
return ltc.MINIMUM_CONFIRMATIONS;
|
||||
|
||||
case Coin.bitcoincash:
|
||||
case Coin.bitcoincashTestnet:
|
||||
return bch.MINIMUM_CONFIRMATIONS;
|
||||
|
@ -182,6 +205,10 @@ Coin coinFromPrettyName(String name) {
|
|||
case "bitcoin":
|
||||
return Coin.bitcoin;
|
||||
|
||||
case "Litecoin":
|
||||
case "litecoin":
|
||||
return Coin.litecoin;
|
||||
|
||||
case "Bitcoincash":
|
||||
case "bitcoincash":
|
||||
case "Bitcoin Cash":
|
||||
|
@ -212,6 +239,12 @@ Coin coinFromPrettyName(String name) {
|
|||
case "bitcoinTestNet":
|
||||
return Coin.bitcoinTestNet;
|
||||
|
||||
case "Litecoin Testnet":
|
||||
case "tlitecoin":
|
||||
case "litecoinTestNet":
|
||||
case "tLitecoin":
|
||||
return Coin.litecoinTestNet;
|
||||
|
||||
case "Bitcoincash Testnet":
|
||||
case "tBitcoin Cash":
|
||||
case "Bitcoin Cash Testnet":
|
||||
|
@ -235,7 +268,10 @@ Coin coinFromPrettyName(String name) {
|
|||
|
||||
default:
|
||||
throw ArgumentError.value(
|
||||
name, "name", "No Coin enum value with that prettyName");
|
||||
name,
|
||||
"name",
|
||||
"No Coin enum value with that prettyName",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -243,6 +279,8 @@ Coin coinFromTickerCaseInsensitive(String ticker) {
|
|||
switch (ticker.toLowerCase()) {
|
||||
case "btc":
|
||||
return Coin.bitcoin;
|
||||
case "ltc":
|
||||
return Coin.litecoin;
|
||||
case "bch":
|
||||
return Coin.bitcoincash;
|
||||
case "doge":
|
||||
|
@ -255,6 +293,8 @@ Coin coinFromTickerCaseInsensitive(String ticker) {
|
|||
return Coin.monero;
|
||||
case "nmc":
|
||||
return Coin.namecoin;
|
||||
case "tltc":
|
||||
return Coin.litecoinTestNet;
|
||||
case "tbtc":
|
||||
return Coin.bitcoinTestNet;
|
||||
case "tbch":
|
||||
|
|
|
@ -181,6 +181,7 @@ class CoinThemeColor {
|
|||
const CoinThemeColor();
|
||||
|
||||
Color get bitcoin => const Color(0xFFFCC17B);
|
||||
Color get litecoin => const Color(0xFF7FA6E1);
|
||||
Color get bitcoincash => const Color(0xFF7BCFB8);
|
||||
Color get firo => const Color(0xFFFF897A);
|
||||
Color get dogecoin => const Color(0xFFFFE079);
|
||||
|
@ -194,6 +195,9 @@ class CoinThemeColor {
|
|||
case Coin.bitcoin:
|
||||
case Coin.bitcoinTestNet:
|
||||
return bitcoin;
|
||||
case Coin.litecoin:
|
||||
case Coin.litecoinTestNet:
|
||||
return litecoin;
|
||||
case Coin.bitcoincash:
|
||||
case Coin.bitcoincashTestnet:
|
||||
return bitcoincash;
|
||||
|
|
|
@ -1406,6 +1406,9 @@ class StackColors extends ThemeExtension<StackColors> {
|
|||
case Coin.bitcoin:
|
||||
case Coin.bitcoinTestNet:
|
||||
return _coin.bitcoin;
|
||||
case Coin.litecoin:
|
||||
case Coin.litecoinTestNet:
|
||||
return _coin.litecoin;
|
||||
case Coin.bitcoincash:
|
||||
case Coin.bitcoincashTestnet:
|
||||
return _coin.bitcoincash;
|
||||
|
|
23
lib/widgets/conditional_parent.dart
Normal file
23
lib/widgets/conditional_parent.dart
Normal file
|
@ -0,0 +1,23 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class ConditionalParent extends StatelessWidget {
|
||||
const ConditionalParent({
|
||||
Key? key,
|
||||
required this.condition,
|
||||
required this.child,
|
||||
required this.builder,
|
||||
}) : super(key: key);
|
||||
|
||||
final bool condition;
|
||||
final Widget child;
|
||||
final Widget Function(Widget) builder;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (condition) {
|
||||
return builder(child);
|
||||
} else {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -51,10 +51,14 @@ class AppBarBackButton extends StatelessWidget {
|
|||
Key? key,
|
||||
this.onPressed,
|
||||
this.isCompact = false,
|
||||
this.size,
|
||||
this.iconSize,
|
||||
}) : super(key: key);
|
||||
|
||||
final VoidCallback? onPressed;
|
||||
final bool isCompact;
|
||||
final double? size;
|
||||
final double? iconSize;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -67,19 +71,20 @@ class AppBarBackButton extends StatelessWidget {
|
|||
)
|
||||
: const EdgeInsets.all(10),
|
||||
child: AppBarIconButton(
|
||||
size: isDesktop
|
||||
size: size ??
|
||||
(isDesktop
|
||||
? isCompact
|
||||
? 42
|
||||
: 56
|
||||
: 32,
|
||||
: 32),
|
||||
color: isDesktop
|
||||
? Theme.of(context).extension<StackColors>()!.textFieldDefaultBG
|
||||
: Theme.of(context).extension<StackColors>()!.background,
|
||||
shadows: const [],
|
||||
icon: SvgPicture.asset(
|
||||
Assets.svg.arrowLeft,
|
||||
width: isCompact ? 18 : 24,
|
||||
height: isCompact ? 18 : 24,
|
||||
width: iconSize ?? (isCompact ? 18 : 24),
|
||||
height: iconSize ?? (isCompact ? 18 : 24),
|
||||
color: Theme.of(context).extension<StackColors>()!.topNavIconPrimary,
|
||||
),
|
||||
onPressed: onPressed ?? Navigator.of(context).pop,
|
||||
|
|
|
@ -11,6 +11,7 @@ class DesktopAppBar extends StatefulWidget {
|
|||
this.trailing,
|
||||
this.background = Colors.transparent,
|
||||
required this.isCompactHeight,
|
||||
this.useSpacers = true,
|
||||
}) : super(key: key);
|
||||
|
||||
final Widget? leading;
|
||||
|
@ -18,6 +19,7 @@ class DesktopAppBar extends StatefulWidget {
|
|||
final Widget? trailing;
|
||||
final Color background;
|
||||
final bool isCompactHeight;
|
||||
final bool useSpacers;
|
||||
|
||||
@override
|
||||
State<DesktopAppBar> createState() => _DesktopAppBarState();
|
||||
|
@ -33,12 +35,16 @@ class _DesktopAppBarState extends State<DesktopAppBar> {
|
|||
items.add(widget.leading!);
|
||||
}
|
||||
|
||||
if (widget.useSpacers) {
|
||||
items.add(const Spacer());
|
||||
}
|
||||
|
||||
if (widget.center != null) {
|
||||
items.add(widget.center!);
|
||||
if (widget.useSpacers) {
|
||||
items.add(const Spacer());
|
||||
}
|
||||
}
|
||||
|
||||
if (widget.trailing != null) {
|
||||
items.add(widget.trailing!);
|
||||
|
|
|
@ -5,6 +5,11 @@ enum ExpandableState {
|
|||
collapsed,
|
||||
}
|
||||
|
||||
class ExpandableController {
|
||||
VoidCallback? toggle;
|
||||
ExpandableState state = ExpandableState.collapsed;
|
||||
}
|
||||
|
||||
class Expandable extends StatefulWidget {
|
||||
const Expandable({
|
||||
Key? key,
|
||||
|
@ -14,6 +19,7 @@ class Expandable extends StatefulWidget {
|
|||
this.animation,
|
||||
this.animationDurationMultiplier = 1.0,
|
||||
this.onExpandChanged,
|
||||
this.controller,
|
||||
}) : super(key: key);
|
||||
|
||||
final Widget header;
|
||||
|
@ -22,6 +28,7 @@ class Expandable extends StatefulWidget {
|
|||
final Animation<double>? animation;
|
||||
final double animationDurationMultiplier;
|
||||
final void Function(ExpandableState)? onExpandChanged;
|
||||
final ExpandableController? controller;
|
||||
|
||||
@override
|
||||
State<Expandable> createState() => _ExpandableState();
|
||||
|
@ -31,19 +38,28 @@ class _ExpandableState extends State<Expandable> with TickerProviderStateMixin {
|
|||
late final AnimationController animationController;
|
||||
late final Animation<double> animation;
|
||||
late final Duration duration;
|
||||
late final ExpandableController? controller;
|
||||
|
||||
ExpandableState _toggleState = ExpandableState.collapsed;
|
||||
|
||||
Future<void> toggle() async {
|
||||
if (animation.isDismissed) {
|
||||
await animationController.forward();
|
||||
widget.onExpandChanged?.call(ExpandableState.collapsed);
|
||||
_toggleState = ExpandableState.collapsed;
|
||||
widget.onExpandChanged?.call(_toggleState);
|
||||
} else if (animation.isCompleted) {
|
||||
await animationController.reverse();
|
||||
widget.onExpandChanged?.call(ExpandableState.expanded);
|
||||
_toggleState = ExpandableState.expanded;
|
||||
widget.onExpandChanged?.call(_toggleState);
|
||||
}
|
||||
controller?.state = _toggleState;
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
controller = widget.controller;
|
||||
controller?.toggle = toggle;
|
||||
|
||||
duration = Duration(
|
||||
milliseconds: (500 * widget.animationDurationMultiplier).toInt(),
|
||||
);
|
||||
|
|
123
lib/widgets/hover_text_field.dart
Normal file
123
lib/widgets/hover_text_field.dart
Normal file
|
@ -0,0 +1,123 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
|
||||
class HoverTextField extends StatefulWidget {
|
||||
const HoverTextField({
|
||||
Key? key,
|
||||
this.controller,
|
||||
this.focusNode,
|
||||
this.readOnly = false,
|
||||
this.enabled,
|
||||
this.onTap,
|
||||
this.onChanged,
|
||||
this.onEditingComplete,
|
||||
this.style,
|
||||
this.onDone,
|
||||
}) : super(key: key);
|
||||
|
||||
final TextEditingController? controller;
|
||||
final FocusNode? focusNode;
|
||||
final bool readOnly;
|
||||
final bool? enabled;
|
||||
final GestureTapCallback? onTap;
|
||||
final ValueChanged<String>? onChanged;
|
||||
final VoidCallback? onEditingComplete;
|
||||
final TextStyle? style;
|
||||
final VoidCallback? onDone;
|
||||
|
||||
@override
|
||||
State<HoverTextField> createState() => _HoverTextFieldState();
|
||||
}
|
||||
|
||||
class _HoverTextFieldState extends State<HoverTextField> {
|
||||
late final TextEditingController? controller;
|
||||
late final FocusNode? focusNode;
|
||||
late bool readOnly;
|
||||
late bool? enabled;
|
||||
late final GestureTapCallback? onTap;
|
||||
late final ValueChanged<String>? onChanged;
|
||||
late final VoidCallback? onEditingComplete;
|
||||
late final TextStyle? style;
|
||||
late final VoidCallback? onDone;
|
||||
|
||||
final InputBorder inputBorder = OutlineInputBorder(
|
||||
borderSide: const BorderSide(
|
||||
width: 0,
|
||||
color: Colors.transparent,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(Constants.size.circularBorderRadius),
|
||||
);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
controller = widget.controller;
|
||||
focusNode = widget.focusNode ?? FocusNode();
|
||||
readOnly = widget.readOnly;
|
||||
enabled = widget.enabled;
|
||||
onChanged = widget.onChanged;
|
||||
style = widget.style;
|
||||
onTap = widget.onTap;
|
||||
onEditingComplete = widget.onEditingComplete;
|
||||
onDone = widget.onDone;
|
||||
|
||||
focusNode!.addListener(() {
|
||||
if (!focusNode!.hasPrimaryFocus && !readOnly) {
|
||||
setState(() {
|
||||
readOnly = true;
|
||||
});
|
||||
onDone?.call();
|
||||
}
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
controller?.dispose();
|
||||
focusNode?.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextField(
|
||||
autocorrect: !Util.isDesktop,
|
||||
enableSuggestions: !Util.isDesktop,
|
||||
controller: controller,
|
||||
focusNode: focusNode,
|
||||
readOnly: readOnly,
|
||||
enabled: enabled,
|
||||
onTap: () {
|
||||
setState(() {
|
||||
readOnly = false;
|
||||
});
|
||||
onTap?.call();
|
||||
},
|
||||
onChanged: onChanged,
|
||||
onEditingComplete: () {
|
||||
setState(() {
|
||||
readOnly = true;
|
||||
});
|
||||
onEditingComplete?.call();
|
||||
onDone?.call();
|
||||
},
|
||||
style: style,
|
||||
decoration: InputDecoration(
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
vertical: 4,
|
||||
horizontal: 12,
|
||||
),
|
||||
border: inputBorder,
|
||||
focusedBorder: inputBorder,
|
||||
disabledBorder: inputBorder,
|
||||
enabledBorder: inputBorder,
|
||||
errorBorder: inputBorder,
|
||||
fillColor: readOnly
|
||||
? Colors.transparent
|
||||
: Theme.of(context).extension<StackColors>()!.textFieldDefaultBG,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
27
lib/widgets/icon_widgets/copy_icon.dart
Normal file
27
lib/widgets/icon_widgets/copy_icon.dart
Normal file
|
@ -0,0 +1,27 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
|
||||
class CopyIcon extends StatelessWidget {
|
||||
const CopyIcon({
|
||||
Key? key,
|
||||
this.width = 18,
|
||||
this.height = 18,
|
||||
this.color,
|
||||
}) : super(key: key);
|
||||
|
||||
final double width;
|
||||
final double height;
|
||||
final Color? color;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SvgPicture.asset(
|
||||
Assets.svg.copy,
|
||||
width: width,
|
||||
height: height,
|
||||
color: color ?? Theme.of(context).extension<StackColors>()!.textDark3,
|
||||
);
|
||||
}
|
||||
}
|
27
lib/widgets/icon_widgets/pencil_icon.dart
Normal file
27
lib/widgets/icon_widgets/pencil_icon.dart
Normal file
|
@ -0,0 +1,27 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
|
||||
class PencilIcon extends StatelessWidget {
|
||||
const PencilIcon({
|
||||
Key? key,
|
||||
this.width = 18,
|
||||
this.height = 18,
|
||||
this.color,
|
||||
}) : super(key: key);
|
||||
|
||||
final double width;
|
||||
final double height;
|
||||
final Color? color;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SvgPicture.asset(
|
||||
Assets.svg.pencil,
|
||||
width: width,
|
||||
height: height,
|
||||
color: color ?? Theme.of(context).extension<StackColors>()!.textDark3,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -102,12 +102,14 @@ class NodeOptionsSheet extends ConsumerWidget {
|
|||
break;
|
||||
|
||||
case Coin.bitcoin:
|
||||
case Coin.litecoin:
|
||||
case Coin.dogecoin:
|
||||
case Coin.firo:
|
||||
case Coin.bitcoinTestNet:
|
||||
case Coin.firoTestNet:
|
||||
case Coin.dogecoinTestNet:
|
||||
case Coin.bitcoincash:
|
||||
case Coin.litecoinTestNet:
|
||||
case Coin.namecoin:
|
||||
case Coin.bitcoincashTestnet:
|
||||
final client = ElectrumX(
|
||||
|
|
|
@ -14,6 +14,7 @@ import 'package:stackwallet/utilities/format.dart';
|
|||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
class TransactionCard extends ConsumerStatefulWidget {
|
||||
|
@ -138,6 +139,20 @@ class _TransactionCardState extends ConsumerState<TransactionCard> {
|
|||
));
|
||||
return;
|
||||
}
|
||||
if (Util.isDesktop) {
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
builder: (context) => DesktopDialog(
|
||||
maxHeight: MediaQuery.of(context).size.height - 64,
|
||||
maxWidth: 580,
|
||||
child: TransactionDetailsView(
|
||||
transaction: _transaction,
|
||||
coin: coin,
|
||||
walletId: walletId,
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
unawaited(
|
||||
Navigator.of(context).pushNamed(
|
||||
TransactionDetailsView.routeName,
|
||||
|
@ -148,6 +163,7 @@ class _TransactionCardState extends ConsumerState<TransactionCard> {
|
|||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
|
|
|
@ -196,6 +196,7 @@ flutter:
|
|||
- assets/images/monero.png
|
||||
- assets/images/wownero.png
|
||||
- assets/images/firo.png
|
||||
- assets/images/litecoin.png
|
||||
- assets/images/doge.png
|
||||
- assets/images/bitcoin.png
|
||||
- assets/images/epic-cash.png
|
||||
|
@ -309,8 +310,10 @@ flutter:
|
|||
- assets/svg/wallet-desktop.svg
|
||||
- assets/svg/exit-desktop.svg
|
||||
- assets/svg/keys.svg
|
||||
- assets/svg/arrow-down.svg
|
||||
# coin icons
|
||||
- assets/svg/coin_icons/Bitcoin.svg
|
||||
- assets/svg/coin_icons/Litecoin.svg
|
||||
- assets/svg/coin_icons/Bitcoincash.svg
|
||||
- assets/svg/coin_icons/Dogecoin.svg
|
||||
- assets/svg/coin_icons/EpicCash.svg
|
||||
|
|
|
@ -26,7 +26,7 @@ void main() {
|
|||
|
||||
when(client.get(
|
||||
Uri.parse(
|
||||
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=monero,bitcoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin,wownero&order=market_cap_desc&per_page=10&page=1&sparkline=false"),
|
||||
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=monero,bitcoin,litecoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin,wownero&order=market_cap_desc&per_page=10&page=1&sparkline=false"),
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
})).thenAnswer((_) async => Response(
|
||||
|
@ -39,10 +39,10 @@ void main() {
|
|||
final price = await priceAPI.getPricesAnd24hChange(baseCurrency: "btc");
|
||||
|
||||
expect(price.toString(),
|
||||
'{Coin.bitcoin: [1, 0.0], Coin.bitcoincash: [0, 0.0], Coin.dogecoin: [0.00000315, -2.68533], Coin.epicCash: [0.00002803, 7.27524], Coin.firo: [0.0001096, -0.89304], Coin.monero: [0.00717236, -0.77656], Coin.wownero: [0, 0.0], Coin.namecoin: [0, 0.0], Coin.bitcoinTestNet: [0, 0.0], Coin.bitcoincashTestnet: [0, 0.0], Coin.dogecoinTestNet: [0, 0.0], Coin.firoTestNet: [0, 0.0]}');
|
||||
'{Coin.bitcoin: [1, 0.0], Coin.bitcoincash: [0, 0.0], Coin.dogecoin: [0.00000315, -2.68533], Coin.epicCash: [0.00002803, 7.27524], Coin.firo: [0.0001096, -0.89304], Coin.monero: [0.00717236, -0.77656], Coin.wownero: [0, 0.0], Coin.namecoin: [0, 0.0], Coin.litecoin: [0, 0.0], Coin.bitcoinTestNet: [0, 0.0], Coin.litecoinTestNet: [0, 0.0], Coin.bitcoincashTestnet: [0, 0.0], Coin.dogecoinTestNet: [0, 0.0], Coin.firoTestNet: [0, 0.0]}');
|
||||
verify(client.get(
|
||||
Uri.parse(
|
||||
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=monero,bitcoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin,wownero&order=market_cap_desc&per_page=10&page=1&sparkline=false"),
|
||||
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=monero,bitcoin,litecoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin,wownero&order=market_cap_desc&per_page=10&page=1&sparkline=false"),
|
||||
headers: {'Content-Type': 'application/json'})).called(1);
|
||||
|
||||
verifyNoMoreInteractions(client);
|
||||
|
@ -53,7 +53,7 @@ void main() {
|
|||
|
||||
when(client.get(
|
||||
Uri.parse(
|
||||
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=monero,bitcoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin,wownero&order=market_cap_desc&per_page=10&page=1&sparkline=false"),
|
||||
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=monero,bitcoin,litecoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin,wownero&order=market_cap_desc&per_page=10&page=1&sparkline=false"),
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
})).thenAnswer((_) async => Response(
|
||||
|
@ -71,12 +71,12 @@ void main() {
|
|||
await priceAPI.getPricesAnd24hChange(baseCurrency: "btc");
|
||||
|
||||
expect(cachedPrice.toString(),
|
||||
'{Coin.bitcoin: [1, 0.0], Coin.bitcoincash: [0, 0.0], Coin.dogecoin: [0.00000315, -2.68533], Coin.epicCash: [0.00002803, 7.27524], Coin.firo: [0.0001096, -0.89304], Coin.monero: [0.00717236, -0.77656], Coin.wownero: [0, 0.0], Coin.namecoin: [0, 0.0], Coin.bitcoinTestNet: [0, 0.0], Coin.bitcoincashTestnet: [0, 0.0], Coin.dogecoinTestNet: [0, 0.0], Coin.firoTestNet: [0, 0.0]}');
|
||||
'{Coin.bitcoin: [1, 0.0], Coin.bitcoincash: [0, 0.0], Coin.dogecoin: [0.00000315, -2.68533], Coin.epicCash: [0.00002803, 7.27524], Coin.firo: [0.0001096, -0.89304], Coin.monero: [0.00717236, -0.77656], Coin.wownero: [0, 0.0], Coin.namecoin: [0, 0.0], Coin.litecoin: [0, 0.0], Coin.bitcoinTestNet: [0, 0.0], Coin.litecoinTestNet: [0, 0.0], Coin.bitcoincashTestnet: [0, 0.0], Coin.dogecoinTestNet: [0, 0.0], Coin.firoTestNet: [0, 0.0]}');
|
||||
|
||||
// verify only called once during filling of cache
|
||||
verify(client.get(
|
||||
Uri.parse(
|
||||
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=monero,bitcoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin,wownero&order=market_cap_desc&per_page=10&page=1&sparkline=false"),
|
||||
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=monero,bitcoin,litecoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin,wownero&order=market_cap_desc&per_page=10&page=1&sparkline=false"),
|
||||
headers: {'Content-Type': 'application/json'})).called(1);
|
||||
|
||||
verifyNoMoreInteractions(client);
|
||||
|
@ -87,7 +87,7 @@ void main() {
|
|||
|
||||
when(client.get(
|
||||
Uri.parse(
|
||||
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=monero,bitcoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin,wownero&order=market_cap_desc&per_page=10&page=1&sparkline=false"),
|
||||
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=monero,bitcoin,litecoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin,wownero&order=market_cap_desc&per_page=10&page=1&sparkline=false"),
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
})).thenAnswer((_) async => Response(
|
||||
|
@ -100,7 +100,7 @@ void main() {
|
|||
final price = await priceAPI.getPricesAnd24hChange(baseCurrency: "btc");
|
||||
|
||||
expect(price.toString(),
|
||||
'{Coin.bitcoin: [0, 0.0], Coin.bitcoincash: [0, 0.0], Coin.dogecoin: [0, 0.0], Coin.epicCash: [0, 0.0], Coin.firo: [0, 0.0], Coin.monero: [0, 0.0], Coin.wownero: [0, 0.0], Coin.namecoin: [0, 0.0], Coin.bitcoinTestNet: [0, 0.0], Coin.bitcoincashTestnet: [0, 0.0], Coin.dogecoinTestNet: [0, 0.0], Coin.firoTestNet: [0, 0.0]}');
|
||||
'{Coin.bitcoin: [0, 0.0], Coin.bitcoincash: [0, 0.0], Coin.dogecoin: [0, 0.0], Coin.epicCash: [0, 0.0], Coin.firo: [0, 0.0], Coin.monero: [0, 0.0], Coin.wownero: [0, 0.0], Coin.namecoin: [0, 0.0], Coin.litecoin: [0, 0.0], Coin.bitcoinTestNet: [0, 0.0], Coin.litecoinTestNet: [0, 0.0], Coin.bitcoincashTestnet: [0, 0.0], Coin.dogecoinTestNet: [0, 0.0], Coin.firoTestNet: [0, 0.0]}');
|
||||
});
|
||||
|
||||
test("no internet available", () async {
|
||||
|
@ -108,7 +108,7 @@ void main() {
|
|||
|
||||
when(client.get(
|
||||
Uri.parse(
|
||||
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=monero,bitcoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin,wownero&order=market_cap_desc&per_page=10&page=1&sparkline=false"),
|
||||
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=monero,bitcoin,litecoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin,wownero&order=market_cap_desc&per_page=10&page=1&sparkline=false"),
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
})).thenThrow(const SocketException(
|
||||
|
@ -120,7 +120,7 @@ void main() {
|
|||
final price = await priceAPI.getPricesAnd24hChange(baseCurrency: "btc");
|
||||
|
||||
expect(price.toString(),
|
||||
'{Coin.bitcoin: [0, 0.0], Coin.bitcoincash: [0, 0.0], Coin.dogecoin: [0, 0.0], Coin.epicCash: [0, 0.0], Coin.firo: [0, 0.0], Coin.monero: [0, 0.0], Coin.wownero: [0, 0.0], Coin.namecoin: [0, 0.0], Coin.bitcoinTestNet: [0, 0.0], Coin.bitcoincashTestnet: [0, 0.0], Coin.dogecoinTestNet: [0, 0.0], Coin.firoTestNet: [0, 0.0]}');
|
||||
'{Coin.bitcoin: [0, 0.0], Coin.bitcoincash: [0, 0.0], Coin.dogecoin: [0, 0.0], Coin.epicCash: [0, 0.0], Coin.firo: [0, 0.0], Coin.monero: [0, 0.0], Coin.wownero: [0, 0.0], Coin.namecoin: [0, 0.0], Coin.litecoin: [0, 0.0], Coin.bitcoinTestNet: [0, 0.0], Coin.litecoinTestNet: [0, 0.0], Coin.bitcoincashTestnet: [0, 0.0], Coin.dogecoinTestNet: [0, 0.0], Coin.firoTestNet: [0, 0.0]}');
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
|
|
Loading…
Reference in a new issue