Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
dcc4ee2
hardware wallet simulator
n13 Dec 16, 2025
198f970
debug and management tools
n13 Dec 16, 2025
9a985ff
add remove wallets for hw wallet added
n13 Dec 16, 2025
a44a999
remove stray wallet selector added by AI
n13 Dec 16, 2025
07a5e58
debug hw is false by default
n13 Dec 16, 2025
ee4a7c8
formatting
n13 Dec 16, 2025
7a7e503
linter fixes
n13 Dec 16, 2025
d06a784
simplify UnsignedTransactionData
n13 Dec 16, 2025
9379399
mark fields final
n13 Dec 16, 2025
7f6b702
minor bugfix - the entire copy area is clickable
n13 Dec 16, 2025
ca6e811
packing the entire keystone UX into the send overlay!
n13 Dec 16, 2025
3b5e4ac
remove debug code from scanner overlay
n13 Dec 16, 2025
e1be15e
raw signing payload for the HW wallet
n13 Dec 16, 2025
f1dfd22
add comment and remove printout
n13 Dec 16, 2025
09b3a50
Add payload parser, parser tests
n13 Dec 16, 2025
0754122
fix payload parser to correctly return milliseconds for timestamps
n13 Dec 16, 2025
16b7f30
remove print
n13 Dec 16, 2025
cb61a11
test reversible transfer parsing with real world values
n13 Dec 16, 2025
d2ac80c
fix unused import
n13 Dec 16, 2025
97a1a18
linter fixes
n13 Dec 16, 2025
accddc7
adding rust based payload parser
n13 Dec 16, 2025
08d18e5
cast 32 to 64 instead of vice versa
n13 Dec 16, 2025
b50cd31
rust parser throwing errors for anything not allowed
n13 Dec 16, 2025
5d41dd0
recovery screen shows phrases for all non hardware wallets
n13 Dec 16, 2025
a76c039
debug inclusion block in messages
n13 Dec 18, 2025
eac6525
delete unused class
n13 Dec 18, 2025
55e845e
print qr code
n13 Dec 18, 2025
ebbfc12
import ss58 crate
n13 Dec 18, 2025
d1025c3
use ss58 crate for encode decode addresses
n13 Dec 18, 2025
411b83b
adding ur crate for QR Code parsing
n13 Dec 18, 2025
5ca5791
use rust lib for UR encoding
n13 Dec 18, 2025
84b69f0
remove ur_registry - we don't need it
n13 Dec 18, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,4 @@ app.*.symbols
/mobile-app/android/.kotlin
/mobile-app/android/.idea
mobile-app/.env
/rust-transaction-parser/target
19 changes: 18 additions & 1 deletion mobile-app/lib/features/components/button.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
import 'package:resonance_network_wallet/features/styles/app_colors_theme.dart';
import 'package:resonance_network_wallet/features/styles/app_text_theme.dart';

enum ButtonVariant { transparent, neutral, primary, success, danger, glass, glassOutline }
enum ButtonVariant { transparent, neutral, primary, success, danger, glass, glassOutline, dangerOutline }

class Button extends StatelessWidget {
final String label;
Expand Down Expand Up @@ -138,6 +138,23 @@ class Button extends StatelessWidget {
);
break;

case ButtonVariant.dangerOutline:
buttonWidget = Container(
width: width,
padding: padding,
decoration: ShapeDecoration(
color: Colors.transparent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(buttonRadius),
side: BorderSide(color: context.themeColors.buttonDanger, width: 1),
),
),
child: Center(
child: Text(label, style: effectiveTextStyle.copyWith(color: context.themeColors.buttonDanger)),
),
);
break;

case ButtonVariant.success:
buttonWidget = Container(
width: width,
Expand Down
145 changes: 126 additions & 19 deletions mobile-app/lib/features/main/screens/account_settings_screen.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import 'dart:ui';

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:resonance_network_wallet/providers/account_providers.dart';
import 'package:resonance_network_wallet/providers/wallet_providers.dart';
import 'package:resonance_network_wallet/providers/account_associations_providers.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:quantus_sdk/quantus_sdk.dart';
import 'package:resonance_network_wallet/features/components/account_gradient_image.dart';
import 'package:resonance_network_wallet/features/components/app_modal_bottom_sheet.dart';
import 'package:resonance_network_wallet/features/components/button.dart';
import 'package:resonance_network_wallet/features/components/scaffold_base.dart';
import 'package:resonance_network_wallet/features/styles/app_size_theme.dart';
import 'package:resonance_network_wallet/features/components/sphere.dart';
import 'package:resonance_network_wallet/features/components/wallet_app_bar.dart';
import 'package:resonance_network_wallet/features/main/screens/create_account_screen.dart';
Expand All @@ -14,18 +21,18 @@ import 'package:resonance_network_wallet/features/styles/app_text_theme.dart';
import 'package:resonance_network_wallet/shared/extensions/clipboard_extensions.dart';
import 'package:resonance_network_wallet/shared/extensions/media_query_data_extension.dart';

class AccountSettingsScreen extends StatefulWidget {
class AccountSettingsScreen extends ConsumerStatefulWidget {
final Account account;
final String balance;
final String checksumName;

const AccountSettingsScreen({super.key, required this.account, required this.balance, required this.checksumName});

@override
State<AccountSettingsScreen> createState() => _AccountSettingsScreenState();
ConsumerState<AccountSettingsScreen> createState() => _AccountSettingsScreenState();
}

class _AccountSettingsScreenState extends State<AccountSettingsScreen> {
class _AccountSettingsScreenState extends ConsumerState<AccountSettingsScreen> {
void _editAccountName() {
Navigator.push<bool?>(
context,
Expand All @@ -38,6 +45,103 @@ class _AccountSettingsScreenState extends State<AccountSettingsScreen> {
});
}

Widget _buildDisconnectWalletButton() {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: Button(
label: 'Disconnect Wallet',
onPressed: _showDisconnectConfirmation,
variant: ButtonVariant.dangerOutline,
),
);
}

void _showDisconnectConfirmation() {
showAppModalBottomSheet(
context: context,
builder: (context) {
return SafeArea(
bottom: false,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 24),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 35, vertical: 16),
decoration: ShapeDecoration(
color: Colors.black,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)),
),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Align(
alignment: Alignment.topRight,
child: IconButton(
icon: Icon(Icons.close, size: context.themeSize.overlayCloseIconSize),
onPressed: () => Navigator.of(context).pop(),
),
),
const SizedBox(height: 10),
Text('Disconnect Wallet?', style: context.themeText.mediumTitle),
const SizedBox(height: 13),
Text(
'This will remove this account from your wallet. If this is the last account for this hardware wallet, the wallet connection will be removed.',
style: context.themeText.smallParagraph,
),
const SizedBox(height: 28),
Button(
variant: ButtonVariant.danger,
label: 'Disconnect',
onPressed: () async {
Navigator.of(context).pop();
await _disconnectWallet();
},
),
const SizedBox(height: 16),
Center(
child: TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(
'Cancel',
style: context.themeText.smallParagraph?.copyWith(decoration: TextDecoration.underline),
),
),
),
],
),
),
),
),
);
},
);
}

Future<void> _disconnectWallet() async {
try {
final accountsService = AccountsService();
await accountsService.removeAccount(widget.account);

// Invalidate providers to refresh UI
// Use ref.read() for actions, but in this case ref is available on ConsumerState
// We don't need to read the providers, just invalidate them
ref.invalidate(accountsProvider);
ref.invalidate(activeAccountProvider);
ref.invalidate(accountAssociationsProvider);
ref.invalidate(balanceProviderFamily(widget.account.accountId));

if (mounted) {
Navigator.of(context).pop(true); // Return true to indicate change
}
} catch (e) {
print('Failed to disconnect: $e');
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Failed to disconnect: $e')));
}
}
}

@override
Widget build(BuildContext context) {
return ScaffoldBase(
Expand Down Expand Up @@ -66,6 +170,9 @@ class _AccountSettingsScreenState extends State<AccountSettingsScreen> {
_buildAddressSection(),
const SizedBox(height: 20),
_buildSecuritySection(),
const SizedBox(height: 20),
if (widget.account.accountType == AccountType.keystone) _buildDisconnectWalletButton(),
const SizedBox(height: 30),
],
),
],
Expand Down Expand Up @@ -145,23 +252,23 @@ class _AccountSettingsScreenState extends State<AccountSettingsScreen> {
return _buildSettingCard(
child: Padding(
padding: const EdgeInsets.only(top: 10.0, left: 10.0, bottom: 10.0, right: 18.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: context.isTablet ? 550 : 251,
child: Text(
context.isTablet
? widget.account.accountId
: AddressFormattingService.splitIntoChunks(widget.account.accountId).join(' '),
style: context.themeText.smallParagraph,
child: InkWell(
onTap: () => ClipboardExtensions.copyTextWithSnackbar(context, widget.account.accountId),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: context.isTablet ? 550 : 251,
child: Text(
context.isTablet
? widget.account.accountId
: AddressFormattingService.splitIntoChunks(widget.account.accountId).join(' '),
style: context.themeText.smallParagraph,
),
),
),
InkWell(
child: Icon(Icons.copy, color: Colors.white, size: context.isTablet ? 26 : 22),
onTap: () => ClipboardExtensions.copyTextWithSnackbar(context, widget.account.accountId),
),
],
Icon(Icons.copy, color: Colors.white, size: context.isTablet ? 26 : 22),
],
),
),
),
);
Expand Down
56 changes: 28 additions & 28 deletions mobile-app/lib/features/main/screens/accounts_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ class _AccountsScreenState extends ConsumerState<AccountsScreen> {
),
child: Column(
children: [
_buildWalletSelector(),
// _buildWalletSelector(),
Expanded(child: _buildAccountsList()),

Button(
Expand All @@ -172,33 +172,33 @@ class _AccountsScreenState extends ConsumerState<AccountsScreen> {
return _isHardwareWallet(selectedAccounts) ? 'Add Hardware Account' : 'Add Account';
}

Widget _buildWalletSelector() {
final accounts = ref.watch(accountsProvider).value ?? <Account>[];
final grouped = _groupByWallet(accounts);
if (grouped.length <= 1) return const SizedBox(height: 0);

final walletIndexes = grouped.keys.toList()..sort();
final initialWallet = _selectedWalletIndex ?? walletIndexes.first;

final items = walletIndexes
.map((ix) => Item<int>(value: ix, label: _walletLabel(ix, grouped[ix] ?? const <Account>[])))
.toList();

return Padding(
padding: const EdgeInsets.only(top: 8, bottom: 10),
child: Align(
alignment: Alignment.centerLeft,
child: Select<int>(
width: 220,
items: items,
initialValue: initialWallet,
onSelect: (item) {
setState(() => _selectedWalletIndex = item.value);
},
),
),
);
}
// Widget _buildWalletSelector() {
// final accounts = ref.watch(accountsProvider).value ?? <Account>[];
// final grouped = _groupByWallet(accounts);
// if (grouped.length <= 1) return const SizedBox(height: 0);

// final walletIndexes = grouped.keys.toList()..sort();
// final initialWallet = _selectedWalletIndex ?? walletIndexes.first;

// final items = walletIndexes
// .map((ix) => Item<int>(value: ix, label: _walletLabel(ix, grouped[ix] ?? const <Account>[])))
// .toList();

// return Padding(
// padding: const EdgeInsets.only(top: 8, bottom: 10),
// child: Align(
// alignment: Alignment.centerLeft,
// child: Select<int>(
// width: 220,
// items: items,
// initialValue: initialWallet,
// onSelect: (item) {
// setState(() => _selectedWalletIndex = item.value);
// },
// ),
// ),
// );
// }

Widget _buildAccountsList() {
final accountsAsync = ref.watch(accountsProvider);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,20 +125,12 @@ class _AddHardwareAccountScreenState extends ConsumerState<AddHardwareAccountScr
Row(
children: [
Expanded(
child: Button(
variant: ButtonVariant.neutral,
label: 'Scan QR Code',
onPressed: _scanQRCode,
),
child: Button(variant: ButtonVariant.neutral, label: 'Scan QR Code', onPressed: _scanQRCode),
),
if (kDebugMode) ...[
const SizedBox(width: 12),
Expanded(
child: Button(
variant: ButtonVariant.neutral,
label: 'Debug Fill',
onPressed: _fillDebugAddress,
),
child: Button(variant: ButtonVariant.neutral, label: 'Debug Fill', onPressed: _fillDebugAddress),
),
],
],
Expand Down
Loading