receive screen redesign and added generate new address button

This commit is contained in:
julian 2022-09-05 19:18:45 -06:00
parent c366e4a9c5
commit d6618518ad
14 changed files with 368 additions and 94 deletions

14
assets/svg/share-2.svg Normal file
View file

@ -0,0 +1,14 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5024_11503)">
<path d="M9 4C9.82843 4 10.5 3.32843 10.5 2.5C10.5 1.67157 9.82843 1 9 1C8.17157 1 7.5 1.67157 7.5 2.5C7.5 3.32843 8.17157 4 9 4Z" fill="white" stroke="#111111" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M3 7.5C3.82843 7.5 4.5 6.82843 4.5 6C4.5 5.17157 3.82843 4.5 3 4.5C2.17157 4.5 1.5 5.17157 1.5 6C1.5 6.82843 2.17157 7.5 3 7.5Z" stroke="#111111" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9 11C9.82843 11 10.5 10.3284 10.5 9.5C10.5 8.67157 9.82843 8 9 8C8.17157 8 7.5 8.67157 7.5 9.5C7.5 10.3284 8.17157 11 9 11Z" stroke="#111111" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M4.29504 6.75488L7.71004 8.74488" stroke="#111111" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7.70504 3.25488L4.29504 5.24488" stroke="#111111" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_5024_11503">
<rect width="12" height="12" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -6,10 +6,12 @@ import 'dart:ui' as ui;
import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_svg/svg.dart';
import 'package:path_provider/path_provider.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:share_plus/share_plus.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/cfcolors.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/constants.dart';
@ -318,8 +320,7 @@ class _GenerateUriQrCodeViewState extends State<GenerateUriQrCodeView> {
child: QrImage(
data: uriString,
size: width,
backgroundColor:
CFColors.almostWhite,
backgroundColor: CFColors.white,
foregroundColor:
CFColors.stackAccent,
),
@ -344,12 +345,40 @@ class _GenerateUriQrCodeViewState extends State<GenerateUriQrCodeView> {
CFColors.buttonGray,
),
),
child: Text(
"Share",
style:
STextStyles.button.copyWith(
color: CFColors.stackAccent,
),
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
.copyWith(
color: CFColors
.stackAccent,
),
),
const SizedBox(
height: 2,
),
],
),
],
),
),
),

View file

@ -1,36 +1,114 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/receive_view/generate_receiving_uri_qr_code_view.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/cfcolors.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/constants.dart';
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/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
import 'package:stackwallet/widgets/custom_loading_overlay.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
class ReceiveView extends StatelessWidget {
class ReceiveView extends ConsumerStatefulWidget {
const ReceiveView({
Key? key,
required this.coin,
required this.receivingAddress,
required this.walletId,
this.clipboard = const ClipboardWrapper(),
}) : super(key: key);
static const String routeName = "/receiveView";
final Coin coin;
final String receivingAddress;
final String walletId;
final ClipboardInterface clipboard;
@override
ConsumerState<ReceiveView> createState() => _ReceiveViewState();
}
class _ReceiveViewState extends ConsumerState<ReceiveView> {
late final Coin coin;
late final String walletId;
late final ClipboardInterface clipboard;
Future<void> generateNewAddress() async {
bool shouldPop = false;
unawaited(
showDialog(
context: context,
builder: (_) {
return WillPopScope(
onWillPop: () async => shouldPop,
child: const CustomLoadingOverlay(
message: "Generating address",
eventBus: null,
),
);
},
),
);
await ref
.read(walletsChangeNotifierProvider)
.getManager(walletId)
.generateNewAddress();
shouldPop = true;
if (mounted) {
Navigator.of(context)
.popUntil(ModalRoute.withName(ReceiveView.routeName));
}
}
String receivingAddress = "";
@override
void initState() {
walletId = widget.walletId;
coin = widget.coin;
clipboard = widget.clipboard;
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
final address = await ref
.read(walletsChangeNotifierProvider)
.getManager(walletId)
.currentReceivingAddress;
setState(() {
receivingAddress = address;
});
});
super.initState();
}
@override
Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType");
ref.listen(
ref
.read(walletsChangeNotifierProvider)
.getManagerProvider(walletId)
.select((value) => value.currentReceivingAddress),
(previous, next) {
if (next is Future<String>) {
next.then((value) => setState(() => receivingAddress = value));
}
});
return Scaffold(
backgroundColor: CFColors.almostWhite,
appBar: AppBar(
@ -52,15 +130,19 @@ class ReceiveView extends StatelessWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
decoration: BoxDecoration(
color: CFColors.white,
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
child: Padding(
padding: const EdgeInsets.all(12.0),
GestureDetector(
onTap: () {
clipboard.setData(
ClipboardData(text: receivingAddress),
);
showFloatingFlushBar(
type: FlushBarType.info,
message: "Copied to clipboard",
iconAsset: Assets.svg.copy,
context: context,
);
},
child: RoundedWhiteContainer(
child: Column(
children: [
Row(
@ -70,35 +152,22 @@ class ReceiveView extends StatelessWidget {
style: STextStyles.itemSubtitle,
),
const Spacer(),
GestureDetector(
onTap: () {
clipboard.setData(
ClipboardData(text: receivingAddress),
);
showFloatingFlushBar(
type: FlushBarType.info,
message: "Copied to clipboard",
iconAsset: Assets.svg.copy,
context: context,
);
},
child: Row(
children: [
SvgPicture.asset(
Assets.svg.copy,
width: 10,
height: 10,
color: CFColors.link2,
),
const SizedBox(
width: 4,
),
Text(
"Copy",
style: STextStyles.link2,
),
],
),
Row(
children: [
SvgPicture.asset(
Assets.svg.copy,
width: 10,
height: 10,
color: CFColors.link2,
),
const SizedBox(
width: 4,
),
Text(
"Copy",
style: STextStyles.link2,
),
],
),
],
),
@ -119,47 +188,62 @@ class ReceiveView extends StatelessWidget {
),
),
),
const SizedBox(
height: 30,
),
Center(
child: QrImage(
data: "${coin.uriScheme}:$receivingAddress",
size: MediaQuery.of(context).size.width / 2,
foregroundColor: CFColors.stackAccent,
if (coin != Coin.epicCash)
const SizedBox(
height: 12,
),
),
const SizedBox(
height: 30,
),
// Spacer(
// flex: 7,
// ),
TextButton(
onPressed: () {
Navigator.of(context).push(
RouteGenerator.getRoute(
shouldUseMaterialRoute:
RouteGenerator.useMaterialPageRoute,
builder: (_) => GenerateUriQrCodeView(
coin: coin,
receivingAddress: receivingAddress,
),
settings: const RouteSettings(
name: GenerateUriQrCodeView.routeName,
),
if (coin != Coin.epicCash)
TextButton(
onPressed: generateNewAddress,
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(
CFColors.buttonGray,
),
),
child: Text(
"Generate new address",
style: STextStyles.button.copyWith(
color: CFColors.stackAccent,
),
);
},
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(
CFColors.buttonGray,
),
),
child: Text(
"Generate QR Code",
style: STextStyles.button.copyWith(
color: CFColors.stackAccent,
const SizedBox(
height: 30,
),
RoundedWhiteContainer(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: Column(
children: [
QrImage(
data: "${coin.uriScheme}:$receivingAddress",
size: MediaQuery.of(context).size.width / 2,
foregroundColor: CFColors.stackAccent,
),
const SizedBox(
height: 20,
),
BlueTextButton(
text: "Create new QR code",
onTap: () async {
unawaited(Navigator.of(context).push(
RouteGenerator.getRoute(
shouldUseMaterialRoute:
RouteGenerator.useMaterialPageRoute,
builder: (_) => GenerateUriQrCodeView(
coin: coin,
receivingAddress: receivingAddress,
),
settings: const RouteSettings(
name: GenerateUriQrCodeView.routeName,
),
),
));
},
),
],
),
),
),
),

View file

@ -532,19 +532,17 @@ class _WalletViewState extends ConsumerState<WalletView> {
onExchangePressed: () =>
_onExchangePressed(context),
onReceivePressed: () async {
final address = await ref
.read(managerProvider)
.currentReceivingAddress;
final coin =
ref.read(managerProvider).coin;
if (mounted) {
Navigator.of(context).pushNamed(
unawaited(
Navigator.of(context).pushNamed(
ReceiveView.routeName,
arguments: Tuple2(
address,
walletId,
coin,
),
);
));
}
},
onSendPressed: () {

View file

@ -681,7 +681,7 @@ class RouteGenerator {
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => ReceiveView(
receivingAddress: args.item1,
walletId: args.item1,
coin: args.item2,
),
settings: RouteSettings(

View file

@ -3825,4 +3825,34 @@ class BitcoinWallet extends CoinServiceAPI {
return available - estimatedFee;
}
@override
Future<bool> generateNewAddress() async {
try {
await _incrementAddressIndexForChain(
0, DerivePathType.bip84); // First increment the receiving index
final newReceivingIndex = DB.instance.get<dynamic>(
boxName: walletId,
key: 'receivingIndexP2WPKH') as int; // Check the new receiving index
final newReceivingAddress = await _generateAddressForChain(
0,
newReceivingIndex,
DerivePathType
.bip84); // Use new index to derive a new receiving address
await _addToAddressesArrayForChain(
newReceivingAddress,
0,
DerivePathType
.bip84); // Add that new receiving address to the array of receiving addresses
_currentReceivingAddress = Future(() =>
newReceivingAddress); // Set the new receiving address that the service
return true;
} catch (e, s) {
Logging.instance.log(
"Exception rethrown from generateNewAddress(): $e\n$s",
level: LogLevel.Error);
return false;
}
}
}

View file

@ -212,4 +212,6 @@ abstract class CoinServiceAPI {
bool get isConnected;
Future<int> estimateFeeFor(int satoshiAmount, int feeRate);
Future<bool> generateNewAddress();
}

View file

@ -3011,6 +3011,35 @@ class DogecoinWallet extends CoinServiceAPI {
return available - estimatedFee;
}
Future<bool> generateNewAddress() async {
try {
await _incrementAddressIndexForChain(
0, DerivePathType.bip44); // First increment the receiving index
final newReceivingIndex = DB.instance.get<dynamic>(
boxName: walletId,
key: 'receivingIndexP2PKH') as int; // Check the new receiving index
final newReceivingAddress = await _generateAddressForChain(
0,
newReceivingIndex,
DerivePathType
.bip44); // Use new index to derive a new receiving address
await _addToAddressesArrayForChain(
newReceivingAddress,
0,
DerivePathType
.bip44); // Add that new receiving address to the array of receiving addresses
_currentReceivingAddressP2PKH = Future(() =>
newReceivingAddress); // Set the new receiving address that the service
return true;
} catch (e, s) {
Logging.instance.log(
"Exception rethrown from generateNewAddress(): $e\n$s",
level: LogLevel.Error);
return false;
}
}
}
// Dogecoin Network

View file

@ -2306,4 +2306,29 @@ class EpicCashWallet extends CoinServiceAPI {
// TODO: implement this
return currentFee;
}
// not used in epic currently
@override
Future<bool> generateNewAddress() async {
try {
// await incrementAddressIndexForChain(
// 0); // First increment the receiving index
// final newReceivingIndex =
// DB.instance.get<dynamic>(boxName: walletId, key: 'receivingIndex')
// as int; // Check the new receiving index
// final newReceivingAddress = await _generateAddressForChain(0,
// newReceivingIndex); // Use new index to derive a new receiving address
// await addToAddressesArrayForChain(newReceivingAddress,
// 0); // Add that new receiving address to the array of receiving addresses
// _currentReceivingAddress = Future(() =>
// newReceivingAddress); // Set the new receiving address that the service
return true;
} catch (e, s) {
Logging.instance.log(
"Exception rethrown from generateNewAddress(): $e\n$s",
level: LogLevel.Error);
return false;
}
}
}

View file

@ -3857,4 +3857,28 @@ class FiroWallet extends CoinServiceAPI {
rethrow;
}
}
@override
Future<bool> generateNewAddress() async {
try {
await incrementAddressIndexForChain(
0); // First increment the receiving index
final newReceivingIndex =
DB.instance.get<dynamic>(boxName: walletId, key: 'receivingIndex')
as int; // Check the new receiving index
final newReceivingAddress = await _generateAddressForChain(0,
newReceivingIndex); // Use new index to derive a new receiving address
await addToAddressesArrayForChain(newReceivingAddress,
0); // Add that new receiving address to the array of receiving addresses
_currentReceivingAddress = Future(() =>
newReceivingAddress); // Set the new receiving address that the service
return true;
} catch (e, s) {
Logging.instance.log(
"Exception rethrown from generateNewAddress(): $e\n$s",
level: LogLevel.Error);
return false;
}
}
}

View file

@ -266,4 +266,12 @@ class Manager with ChangeNotifier {
Future<int> estimateFeeFor(int satoshiAmount, int feeRate) async {
return _currentWallet.estimateFeeFor(satoshiAmount, feeRate);
}
Future<bool> generateNewAddress() async {
final success = await _currentWallet.generateNewAddress();
if (success) {
notifyListeners();
}
return success;
}
}

View file

@ -1521,4 +1521,33 @@ class MoneroWallet extends CoinServiceAPI {
10000;
return fee;
}
@override
Future<bool> generateNewAddress() async {
try {
const String indexKey = "receivingIndex";
// First increment the receiving index
await _incrementAddressIndexForChain(0);
final newReceivingIndex =
DB.instance.get<dynamic>(boxName: walletId, key: indexKey) as int;
// Use new index to derive a new receiving address
final newReceivingAddress =
await _generateAddressForChain(0, newReceivingIndex);
// Add that new receiving address to the array of receiving addresses
await _addToAddressesArrayForChain(newReceivingAddress, 0);
// Set the new receiving address that the service
_currentReceivingAddress = Future(() => newReceivingAddress);
return true;
} catch (e, s) {
Logging.instance.log(
"Exception rethrown from generateNewAddress(): $e\n$s",
level: LogLevel.Error);
return false;
}
}
}

View file

@ -84,6 +84,7 @@ class _SVG {
String get solidSliders => "assets/svg/sliders-solid.svg";
String get questionMessage => "assets/svg/message-question.svg";
String get envelope => "assets/svg/envelope.svg";
String get share => "assets/svg/share-2.svg";
String get receive => "assets/svg/tx-icon-receive.svg";
String get receivePending => "assets/svg/tx-icon-receive-pending.svg";

View file

@ -255,6 +255,7 @@ flutter:
- assets/svg/sliders-solid.svg
- assets/svg/message-question.svg
- assets/svg/envelope.svg
- assets/svg/share-2.svg
# coin icons
- assets/svg/coin_icons/Bitcoin.svg
- assets/svg/coin_icons/Dogecoin.svg