Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
0668eb9
add wallet index
n13 Dec 14, 2025
aa207a8
add account type
n13 Dec 14, 2025
dc021f0
UX for adding hardware
n13 Dec 15, 2025
bd9f22c
melos format
n13 Dec 15, 2025
ec5dd30
scan QR added, debug fill button added
n13 Dec 16, 2025
2a24647
do fee estimate without signing the transaction
n13 Dec 16, 2025
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
bdf5fbf
adding is_complete to our ur library
n13 Dec 19, 2025
737c633
scan for UR QR codes until we found them all
n13 Dec 19, 2025
6b1d4c4
don't parse non-UR codes
n13 Dec 19, 2025
796c9bd
adding random segment test and comment on UR in general
n13 Dec 19, 2025
0024e24
use byte encode / decode
n13 Dec 20, 2025
147b941
parsing signature and pubkey confirmed
n13 Dec 21, 2025
86d0a5a
Merge branch 'main' into n13/4-scan-ur-animated-qr-codes
n13 Jan 7, 2026
aefec85
hardware wallet feature flag
n13 Jan 7, 2026
7aa632f
recover widget tests and mocks
n13 Jan 7, 2026
6c1ff47
melos format
n13 Jan 7, 2026
4ef2721
add cursor to git ignore
n13 Jan 8, 2026
2b7c2ab
remove ai comment
n13 Jan 8, 2026
b8c4fd3
move selected wallet index into its own method
n13 Jan 8, 2026
db71514
delete commented code
n13 Jan 8, 2026
cdd3aa2
delete duplicate import
n13 Jan 8, 2026
d8274ac
move debug hw flag
n13 Jan 8, 2026
76e056c
add disabled to danger outline button, format
n13 Jan 8, 2026
fed6486
Merge branch 'main' into n13/hardware_wallet_support
n13 Jan 8, 2026
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,5 @@ app.*.symbols
/mobile-app/android/.kotlin
/mobile-app/android/.idea
mobile-app/.env
/rust-transaction-parser/target
/.cursor
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 @@ -4,7 +4,7 @@ import 'package:quantus_sdk/quantus_sdk.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 @@ -140,6 +140,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: disabled ? disabledBtnColor : 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
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ class _SelectActionSheetState<T> extends State<SelectActionSheet<T>> {
children: widget.items.map((item) {
return InkWell(
onTap: () {
widget.onSelect(item);
Navigator.pop(context);
Future.microtask(() => widget.onSelect(item));
},
child: Container(
padding: const EdgeInsets.symmetric(vertical: 16),
Expand Down
144 changes: 125 additions & 19 deletions mobile-app/lib/features/main/screens/account_settings_screen.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
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/copy_icon.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 @@ -15,18 +22,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 @@ -39,6 +46,99 @@ 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);
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 @@ -67,6 +167,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 @@ -146,23 +249,26 @@ 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: const CopyIcon(),
onTap: () => ClipboardExtensions.copyTextWithSnackbar(context, widget.account.accountId),
),
],
InkWell(
child: const CopyIcon(),
onTap: () => ClipboardExtensions.copyTextWithSnackbar(context, widget.account.accountId),
),
],
),
),
),
);
Expand Down
Loading