diff --git a/assets/svg/circle-plus-filled.svg b/assets/svg/circle-plus-filled.svg
new file mode 100644
index 000000000..3e3244adb
--- /dev/null
+++ b/assets/svg/circle-plus-filled.svg
@@ -0,0 +1,3 @@
+
diff --git a/lib/models/paynym/paynym_account.dart b/lib/models/paynym/paynym_account.dart
new file mode 100644
index 000000000..08438a6b7
--- /dev/null
+++ b/lib/models/paynym/paynym_account.dart
@@ -0,0 +1,48 @@
+import 'package:stackwallet/models/paynym/paynym_code.dart';
+
+class PaynymAccount {
+ final String nymID;
+ final String nymName;
+
+ final List codes;
+
+ /// list of nymId
+ final List followers;
+
+ /// list of nymId
+ final List following;
+
+ PaynymAccount(
+ this.nymID,
+ this.nymName,
+ this.codes,
+ this.followers,
+ this.following,
+ );
+
+ PaynymAccount.fromMap(Map map)
+ : nymID = map["nymID"] as String,
+ nymName = map["nymName"] as String,
+ codes = (map["codes"] as List)
+ .map((e) => PaynymCode.fromMap(Map.from(e as Map)))
+ .toList(),
+ followers = (map["followers"] as List)
+ .map((e) => e["nymId"] as String)
+ .toList(),
+ following = (map["following"] as List)
+ .map((e) => e["nymId"] as String)
+ .toList();
+
+ Map toMap() => {
+ "nymID": nymID,
+ "nymName": nymName,
+ "codes": codes.map((e) => e.toMap()),
+ "followers": followers.map((e) => {"nymId": e}),
+ "following": followers.map((e) => {"nymId": e}),
+ };
+
+ @override
+ String toString() {
+ return toMap().toString();
+ }
+}
diff --git a/lib/models/paynym/paynym_code.dart b/lib/models/paynym/paynym_code.dart
new file mode 100644
index 000000000..d01366d20
--- /dev/null
+++ b/lib/models/paynym/paynym_code.dart
@@ -0,0 +1,27 @@
+class PaynymCode {
+ final bool claimed;
+ final bool segwit;
+ final String code;
+
+ PaynymCode(
+ this.claimed,
+ this.segwit,
+ this.code,
+ );
+
+ PaynymCode.fromMap(Map map)
+ : claimed = map["claimed"] as bool,
+ segwit = map["segwit"] as bool,
+ code = map["code"] as String;
+
+ Map toMap() => {
+ "claimed": claimed,
+ "segwit": segwit,
+ "code": code,
+ };
+
+ @override
+ String toString() {
+ return toMap().toString();
+ }
+}
diff --git a/lib/pages/paynym/paynym_claim_view.dart b/lib/pages/paynym/paynym_claim_view.dart
index adb70f1d8..b9b5ebd95 100644
--- a/lib/pages/paynym/paynym_claim_view.dart
+++ b/lib/pages/paynym/paynym_claim_view.dart
@@ -1,4 +1,5 @@
import 'dart:async';
+import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@@ -8,6 +9,7 @@ import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/services/coins/coin_paynym_extension.dart';
import 'package:stackwallet/services/coins/dogecoin/dogecoin_wallet.dart';
import 'package:stackwallet/utilities/assets.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';
@@ -88,53 +90,52 @@ class _PaynymClaimViewState extends ConsumerState {
builder: (context) => const ClaimingPaynymDialog(),
).then((value) => shouldCancel = value == true),
);
- // generate and submit paynym to api
+ // ghet wallet to access paynym calls
final wallet = ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.wallet as DogecoinWallet;
+
+ // get payment code
final pCode = await wallet.getPaymentCode();
- final result = await ref
+ // attempt to create new entry in paynym.is db
+ final created = await ref
.read(paynymAPIProvider)
.create(pCode.toString());
- // final result =
- // await ref.read(paynymAPIProvider).token(pCode.toString());
+ if (created.claimed) {
+ // payment code already claimed
+ debugPrint("pcode already claimed!!");
+ return;
+ }
- // final token =
- // "IlBNOFRKWWt1U2RZWEpud0RCcThDaGZpbmZYdjNzcnhoUXJ4M2VvRXdiU3c1MXdNamRvOUpKMkRzeWN3VDNndDN6SFE3Y1YxZ3J2YWJNbW1mMUJ0ajZmWTd0Z2tnU3o5QjhNWnVSM2tqWWZnTUxNVVJKQ1hOIg.FoPF3g.KUMZDC4U_ek-B6cqPLYilXniQv8";
- //
- // print("======================");
- // print(token);
- // print(token.codeUnits);
- // print(utf8.encode(token));
- // print(utf8.decode(token.codeUnits));
- //
- // print("======================");
- //
- // final signed = await wallet.signWithNotificationKey(
- // Uint8List.fromList(token.codeUnits));
- //
- // final signedString = Format.uint8listToString(signed);
- //
- // print("======================");
- // print(signed);
- // print(signedString);
- //
- // print("======================");
+ String token;
- // final result2 = await ref
- // .read(paynymAPIProvider)
- // .claim(token, signedString);
+ if (created.token == null) {
+ // payment code already in db
+ // so we need to fetch a token
- // print("======================");
- // print(
- // result2); // {claimed: PM8TJYkuSdYXJnwDBq8ChfinfXv3srxhQrx3eoEwbSw51wMjdo9JJ2DsycwT3gt3zHQ7cV1grvabMmmf1Btj6fY7tgkgSz9B8MZuR3kjYfgMLMURJCXN, token: IlBNOFRKWWt1U2RZWEpud0RCcThDaGZpbmZYdjNzcnhoUXJ4M2VvRXdiU3c1MXdNamRvOUpKMkRzeWN3VDNndDN6SFE3Y1YxZ3J2YWJNbW1mMUJ0ajZmWTd0Z2tnU3o5QjhNWnVSM2tqWWZnTUxNVVJKQ1hOIg.FoPF3g.KUMZDC4U_ek-B6cqPLYilXniQv8}
- // print("======================");
+ token = await ref
+ .read(paynymAPIProvider)
+ .token(pCode.toString());
+ } else {
+ token = created.token!;
+ }
- await Future.delayed(const Duration(seconds: 3));
+ // sign token with notification private key
+ final signatureBytes = await wallet.signWithNotificationKey(
+ Uint8List.fromList(token.codeUnits));
+ final signature = Format.uint8listToString(signatureBytes);
+
+ // claim paynym account
+ final claim =
+ await ref.read(paynymAPIProvider).claim(token, signature);
+
+ if (claim["claimed"] == pCode.toString()) {
+ // mark claim successful
+ }
if (mounted && !shouldCancel) {
Navigator.of(context).pop();
diff --git a/lib/pages/paynym/paynym_home_view.dart b/lib/pages/paynym/paynym_home_view.dart
index 93873b003..4782e2491 100644
--- a/lib/pages/paynym/paynym_home_view.dart
+++ b/lib/pages/paynym/paynym_home_view.dart
@@ -1,18 +1,31 @@
import 'package:flutter/material.dart';
+import 'package:flutter_svg/svg.dart';
+import 'package:stackwallet/models/paynym/paynym_account.dart';
+import 'package:stackwallet/utilities/assets.dart';
+import 'package:stackwallet/utilities/constants.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:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
+import 'package:stackwallet/widgets/desktop/secondary_button.dart';
+import 'package:stackwallet/widgets/icon_widgets/copy_icon.dart';
+import 'package:stackwallet/widgets/icon_widgets/qrcode_icon.dart';
+import 'package:stackwallet/widgets/icon_widgets/share_icon.dart';
+import 'package:stackwallet/widgets/loading_indicator.dart';
+import 'package:stackwallet/widgets/rounded_white_container.dart';
+import 'package:stackwallet/widgets/toggle.dart';
class PaynymHomeView extends StatefulWidget {
const PaynymHomeView({
Key? key,
required this.walletId,
- required this.paymentCodeString,
+ required this.nymAccount,
}) : super(key: key);
final String walletId;
- final String paymentCodeString;
+ final PaynymAccount nymAccount;
static const String routeName = "/paynymHome";
@@ -21,6 +34,8 @@ class PaynymHomeView extends StatefulWidget {
}
class _PaynymHomeViewState extends State {
+ bool showFollowing = false;
+
@override
Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType");
@@ -40,14 +55,172 @@ class _PaynymHomeViewState extends State {
style: STextStyles.navBarTitle(context),
overflow: TextOverflow.ellipsis,
),
+ actions: [
+ Padding(
+ padding: const EdgeInsets.symmetric(vertical: 6),
+ child: AspectRatio(
+ aspectRatio: 1,
+ child: AppBarIconButton(
+ icon: SvgPicture.asset(
+ Assets.svg.circlePlusFilled,
+ width: 20,
+ height: 20,
+ color: Theme.of(context).extension()!.textDark,
+ ),
+ onPressed: () {
+ // todo add ?
+ },
+ ),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.symmetric(vertical: 6),
+ child: AspectRatio(
+ aspectRatio: 1,
+ child: AppBarIconButton(
+ icon: SvgPicture.asset(
+ Assets.svg.circleQuestion,
+ width: 20,
+ height: 20,
+ color: Theme.of(context).extension()!.textDark,
+ ),
+ onPressed: () {
+ // todo add ?
+ },
+ ),
+ ),
+ ),
+ const SizedBox(
+ width: 4,
+ ),
+ ],
),
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
+ crossAxisAlignment: CrossAxisAlignment.center,
children: [
PayNymBot(
- paymentCodeString: widget.paymentCodeString,
+ paymentCodeString: widget.nymAccount.codes.first.code,
+ ),
+ const SizedBox(
+ height: 10,
+ ),
+ Text(
+ widget.nymAccount.nymName,
+ style: STextStyles.desktopMenuItemSelected(context),
+ ),
+ const SizedBox(
+ height: 4,
+ ),
+ Text(
+ Format.shorten(widget.nymAccount.codes.first.code, 12, 5),
+ style: STextStyles.label(context),
+ ),
+ const SizedBox(
+ height: 11,
+ ),
+ Row(
+ children: [
+ Expanded(
+ child: SecondaryButton(
+ label: "Copy",
+ buttonHeight: ButtonHeight.l,
+ iconSpacing: 4,
+ icon: CopyIcon(
+ width: 10,
+ height: 10,
+ color: Theme.of(context)
+ .extension()!
+ .textDark,
+ ),
+ onPressed: () {
+ // copy to clipboard
+ },
+ ),
+ ),
+ const SizedBox(
+ width: 13,
+ ),
+ Expanded(
+ child: SecondaryButton(
+ label: "Share",
+ buttonHeight: ButtonHeight.l,
+ iconSpacing: 4,
+ icon: ShareIcon(
+ width: 10,
+ height: 10,
+ color: Theme.of(context)
+ .extension()!
+ .textDark,
+ ),
+ onPressed: () {
+ // copy to clipboard
+ },
+ ),
+ ),
+ const SizedBox(
+ width: 13,
+ ),
+ Expanded(
+ child: SecondaryButton(
+ label: "Address",
+ buttonHeight: ButtonHeight.l,
+ iconSpacing: 4,
+ icon: QrCodeIcon(
+ width: 10,
+ height: 10,
+ color: Theme.of(context)
+ .extension()!
+ .textDark,
+ ),
+ onPressed: () {
+ // copy to clipboard
+ },
+ ),
+ ),
+ ],
+ ),
+ const SizedBox(
+ height: 24,
+ ),
+ SizedBox(
+ height: 40,
+ child: Toggle(
+ onColor: Theme.of(context).extension()!.popupBG,
+ onText: "Following",
+ offColor: Theme.of(context)
+ .extension()!
+ .textFieldDefaultBG,
+ offText: "Followers",
+ isOn: showFollowing,
+ onValueChanged: (value) {
+ setState(() {
+ showFollowing = value;
+ });
+ },
+ decoration: BoxDecoration(
+ color: Colors.blue,
+ borderRadius: BorderRadius.circular(
+ Constants.size.circularBorderRadius,
+ ),
+ ),
+ ),
+ ),
+ const SizedBox(
+ height: 16,
+ ),
+ RoundedWhiteContainer(
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Text(
+ "Your PayNym contacts will appear here",
+ style: STextStyles.label(context),
+ ),
+ ],
+ ),
),
],
),
@@ -61,12 +234,30 @@ class PayNymBot extends StatelessWidget {
const PayNymBot({
Key? key,
required this.paymentCodeString,
+ this.size = 60.0,
}) : super(key: key);
final String paymentCodeString;
+ final double size;
@override
Widget build(BuildContext context) {
- return Image.network("https://paynym.is/$paymentCodeString/avatar");
+ return ClipRRect(
+ borderRadius: BorderRadius.circular(size / 2),
+ child: SizedBox(
+ width: size,
+ height: size,
+ child: Image.network(
+ "https://paynym.is/$paymentCodeString/avatar",
+ loadingBuilder: (context, child, event) {
+ if (event == null) {
+ return child;
+ } else {
+ return const LoadingIndicator();
+ }
+ },
+ ),
+ ),
+ );
}
}
diff --git a/lib/pages/wallet_view/sub_widgets/wallet_navigation_bar.dart b/lib/pages/wallet_view/sub_widgets/wallet_navigation_bar.dart
index 41b429311..af5b19f14 100644
--- a/lib/pages/wallet_view/sub_widgets/wallet_navigation_bar.dart
+++ b/lib/pages/wallet_view/sub_widgets/wallet_navigation_bar.dart
@@ -1,10 +1,21 @@
+import 'dart:async';
+
import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/pages/paynym/paynym_claim_view.dart';
+import 'package:stackwallet/pages/paynym/paynym_home_view.dart';
+import 'package:stackwallet/providers/global/paynym_api_provider.dart';
+import 'package:stackwallet/providers/global/wallets_provider.dart';
+import 'package:stackwallet/services/coins/coin_paynym_extension.dart';
+import 'package:stackwallet/services/coins/dogecoin/dogecoin_wallet.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
+import 'package:tuple/tuple.dart';
+
+import '../../../widgets/loading_indicator.dart';
class WalletNavigationBar extends StatefulWidget {
const WalletNavigationBar({
@@ -88,42 +99,84 @@ class _WalletNavigationBarState extends State {
AnimatedOpacity(
opacity: scale,
duration: duration,
- child: GestureDetector(
- onTap: () {
- setState(() {
- scale = 0;
- });
- Navigator.of(context).pushNamed(
- PaynymClaimView.routeName,
- arguments: widget.walletId,
- );
- },
- child: Container(
- padding: const EdgeInsets.all(16),
- width: 146,
- decoration: BoxDecoration(
- color:
- Theme.of(context).extension()!.popupBG,
- boxShadow: [
- Theme.of(context)
- .extension()!
- .standardBoxShadow
- ],
- borderRadius: BorderRadius.circular(
- widget.height / 2.0,
+ child: Consumer(builder: (context, ref, __) {
+ return GestureDetector(
+ onTap: () async {
+ setState(() {
+ scale = 0;
+ });
+ unawaited(
+ showDialog(
+ context: context,
+ builder: (context) => const LoadingIndicator(
+ width: 100,
+ ),
+ ),
+ );
+
+ // todo make generic and not doge specific
+ final wallet = (ref
+ .read(walletsChangeNotifierProvider)
+ .getManager(widget.walletId)
+ .wallet as DogecoinWallet);
+
+ final code = await wallet.getPaymentCode();
+
+ final account = await ref
+ .read(paynymAPIProvider)
+ .nym(code.toString());
+
+ if (mounted) {
+ Navigator.of(context).pop();
+
+ // check if account exists and for matching code to see if claimed
+ if (account != null &&
+ account.codes
+ .where((e) =>
+ e.code == code.toString() && e.claimed)
+ .isNotEmpty) {
+ await Navigator.of(context).pushNamed(
+ PaynymHomeView.routeName,
+ arguments: Tuple2(
+ widget.walletId,
+ account,
+ ),
+ );
+ } else {
+ await Navigator.of(context).pushNamed(
+ PaynymClaimView.routeName,
+ arguments: widget.walletId,
+ );
+ }
+ }
+ },
+ child: Container(
+ padding: const EdgeInsets.all(16),
+ width: 146,
+ decoration: BoxDecoration(
+ color:
+ Theme.of(context).extension()!.popupBG,
+ boxShadow: [
+ Theme.of(context)
+ .extension()!
+ .standardBoxShadow
+ ],
+ borderRadius: BorderRadius.circular(
+ widget.height / 2.0,
+ ),
+ ),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Text(
+ "Paynym",
+ style: STextStyles.w600_12(context),
+ ),
+ ],
),
),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- Text(
- "Paynym",
- style: STextStyles.w600_12(context),
- ),
- ],
- ),
- ),
- ),
+ );
+ }),
),
const SizedBox(
height: 8,
diff --git a/lib/route_generator.dart b/lib/route_generator.dart
index 3da3a29d7..3b4c4e103 100644
--- a/lib/route_generator.dart
+++ b/lib/route_generator.dart
@@ -6,6 +6,7 @@ import 'package:stackwallet/models/contact_address_entry.dart';
import 'package:stackwallet/models/exchange/incomplete_exchange.dart';
import 'package:stackwallet/models/exchange/response_objects/trade.dart';
import 'package:stackwallet/models/paymint/transactions_model.dart';
+import 'package:stackwallet/models/paynym/paynym_account.dart';
import 'package:stackwallet/models/send_view_auto_fill_data.dart';
import 'package:stackwallet/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart';
import 'package:stackwallet/pages/add_wallet_views/create_or_restore_wallet_view/create_or_restore_wallet_view.dart';
@@ -204,12 +205,12 @@ class RouteGenerator {
return _routeError("${settings.name} invalid args: ${args.toString()}");
case PaynymHomeView.routeName:
- if (args is Tuple2) {
+ if (args is Tuple2) {
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => PaynymHomeView(
walletId: args.item1,
- paymentCodeString: args.item2,
+ nymAccount: args.item2,
),
settings: RouteSettings(
name: settings.name,
diff --git a/lib/services/coins/coin_paynym_extension.dart b/lib/services/coins/coin_paynym_extension.dart
index 99ee6ec32..366b2cf78 100644
--- a/lib/services/coins/coin_paynym_extension.dart
+++ b/lib/services/coins/coin_paynym_extension.dart
@@ -34,4 +34,7 @@ extension PayNym on DogecoinWallet {
final signed = pair.sign(SHA256Digest().process(data));
return signed;
}
+
+ // Future