Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
21 changes: 21 additions & 0 deletions mobile-app/lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1105,6 +1105,10 @@
"@accountMenuAddressDetails": {
"description": "Address details menu row label"
},
"accountMenuInnerHash": "Inner Hash",
"@accountMenuInnerHash": {
"description": "Menu row label shown for encrypted accounts instead of address details"
},
"accountMenuShowRecoveryPhrase": "Show Recovery Phrase",
"@accountMenuShowRecoveryPhrase": {
"description": "Show recovery phrase menu row label"
Expand Down Expand Up @@ -1212,6 +1216,23 @@
"description": "App bar title on account details screen"
},

"innerHashTitle": "Inner Hash",
"@innerHashTitle": {
"description": "App bar title on the encrypted account inner hash screen"
},
"innerHashLabel": "INNER HASH",
"@innerHashLabel": {
"description": "Card label above the inner hash value"
},
"innerHashCopied": "Inner hash copied",
"@innerHashCopied": {
"description": "Toaster shown after copying the inner hash"
},
"innerHashLoadError": "Couldn't load inner hash",
"@innerHashLoadError": {
"description": "Toaster shown when deriving the inner hash fails"
},

"addKeystoneAppBarTitle": "Add Keystone Wallet",
"@addKeystoneAppBarTitle": {
"description": "App bar title for the add Keystone wallet screens"
Expand Down
5 changes: 5 additions & 0 deletions mobile-app/lib/l10n/app_id.arb
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@
"accountMenuTitle": "Akun",
"accountMenuAccountName": "Nama Akun",
"accountMenuAddressDetails": "Detail Alamat",
"accountMenuInnerHash": "Inner Hash",
"accountMenuShowRecoveryPhrase": "Tampilkan Recovery Phrase",
"accountMenuNotFound": "Akun tidak ditemukan",
"accountMenuDone": "Selesai",
Expand All @@ -257,6 +258,10 @@
"accountMenuDeleteWalletMessage": "Frasa pemulihan Dompet {number} akan dihapus permanen dari perangkat ini. Pastikan sudah dicadangkan — ini tidak dapat dibatalkan.",
"accountMenuDeleteWalletConfirm": "Hapus Dompet",
"accountDetailsTitle": "Detail Alamat",
"innerHashTitle": "Inner Hash",
"innerHashLabel": "INNER HASH",
"innerHashCopied": "Inner hash disalin",
"innerHashLoadError": "Gagal memuat inner hash",
"invalidAddress": "Alamat tidak valid",
"sendTitle": "Kirim",
"sendPayTitle": "Bayar",
Expand Down
30 changes: 30 additions & 0 deletions mobile-app/lib/l10n/app_localizations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1532,6 +1532,12 @@ abstract class AppLocalizations {
/// **'Address Details'**
String get accountMenuAddressDetails;

/// Menu row label shown for encrypted accounts instead of address details
///
/// In en, this message translates to:
/// **'Inner Hash'**
String get accountMenuInnerHash;

/// Show recovery phrase menu row label
///
/// In en, this message translates to:
Expand Down Expand Up @@ -1640,6 +1646,30 @@ abstract class AppLocalizations {
/// **'Address Details'**
String get accountDetailsTitle;

/// App bar title on the encrypted account inner hash screen
///
/// In en, this message translates to:
/// **'Inner Hash'**
String get innerHashTitle;

/// Card label above the inner hash value
///
/// In en, this message translates to:
/// **'INNER HASH'**
String get innerHashLabel;

/// Toaster shown after copying the inner hash
///
/// In en, this message translates to:
/// **'Inner hash copied'**
String get innerHashCopied;

/// Toaster shown when deriving the inner hash fails
///
/// In en, this message translates to:
/// **'Couldn\'t load inner hash'**
String get innerHashLoadError;

/// App bar title for the add Keystone wallet screens
///
/// In en, this message translates to:
Expand Down
15 changes: 15 additions & 0 deletions mobile-app/lib/l10n/app_localizations_en.dart
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get accountMenuAddressDetails => 'Address Details';

@override
String get accountMenuInnerHash => 'Inner Hash';

@override
String get accountMenuShowRecoveryPhrase => 'Show Recovery Phrase';

Expand Down Expand Up @@ -858,6 +861,18 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get accountDetailsTitle => 'Address Details';

@override
String get innerHashTitle => 'Inner Hash';

@override
String get innerHashLabel => 'INNER HASH';

@override
String get innerHashCopied => 'Inner hash copied';

@override
String get innerHashLoadError => 'Couldn\'t load inner hash';

@override
String get addKeystoneAppBarTitle => 'Add Keystone Wallet';

Expand Down
15 changes: 15 additions & 0 deletions mobile-app/lib/l10n/app_localizations_id.dart
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,9 @@ class AppLocalizationsId extends AppLocalizations {
@override
String get accountMenuAddressDetails => 'Detail Alamat';

@override
String get accountMenuInnerHash => 'Inner Hash';

@override
String get accountMenuShowRecoveryPhrase => 'Tampilkan Recovery Phrase';

Expand Down Expand Up @@ -861,6 +864,18 @@ class AppLocalizationsId extends AppLocalizations {
@override
String get accountDetailsTitle => 'Detail Alamat';

@override
String get innerHashTitle => 'Inner Hash';

@override
String get innerHashLabel => 'INNER HASH';

@override
String get innerHashCopied => 'Inner hash disalin';

@override
String get innerHashLoadError => 'Gagal memuat inner hash';

@override
String get addKeystoneAppBarTitle => 'Add Keystone Wallet';

Expand Down
142 changes: 14 additions & 128 deletions mobile-app/lib/v2/components/address_details_card.dart
Original file line number Diff line number Diff line change
@@ -1,146 +1,32 @@
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:resonance_network_wallet/l10n/app_localizations.dart';
import 'package:quantus_sdk/quantus_sdk.dart';
import 'package:resonance_network_wallet/providers/l10n_provider.dart';
import 'package:resonance_network_wallet/shared/extensions/clipboard_extensions.dart';
import 'package:resonance_network_wallet/v2/components/copyable_data_item.dart';
import 'package:resonance_network_wallet/v2/components/split_card.dart';
import 'package:quantus_sdk/quantus_sdk.dart';

class AddressDetailsCard extends ConsumerStatefulWidget {
class AddressDetailsCard extends ConsumerWidget {
final String accountId;
final String? checksum;

const AddressDetailsCard({super.key, required this.accountId, this.checksum});

@override
ConsumerState<AddressDetailsCard> createState() => _AddressDetailsCardState();
}

class _AddressDetailsCardState extends ConsumerState<AddressDetailsCard> {
bool _addressCopied = false;
bool _checksumCopied = false;
Timer? _resetTimer;

void _copyAddress(BuildContext context) {
context.copyTextWithToaster(widget.accountId);
_triggerCopied(isAddress: true);
}

void _copyChecksum(BuildContext context, AppLocalizations l10n) {
if (widget.checksum == null) return;

context.copyTextWithToaster(widget.checksum!, message: l10n.componentCheckphraseCopied);
_triggerCopied(isAddress: false);
}

void _triggerCopied({required bool isAddress}) {
_resetTimer?.cancel();

setState(() {
if (isAddress) {
_addressCopied = true;
_checksumCopied = false;
} else {
_checksumCopied = true;
_addressCopied = false;
}
});

_resetTimer = Timer(const Duration(seconds: 2), () {
if (mounted) {
setState(() {
if (isAddress) {
_addressCopied = false;
} else {
_checksumCopied = false;
}
});
}
});
}

@override
void dispose() {
_resetTimer?.cancel();
super.dispose();
}

@override
Widget build(BuildContext context) {
Widget build(BuildContext context, WidgetRef ref) {
final l10n = ref.watch(l10nProvider);

return SplitCard(
topChild: InkWell(
onTap: () => _copyAddress(context),
child: _buildItem(context, l10n.componentAddressLabel, widget.accountId, isCopied: _addressCopied),
),
bottomChild: InkWell(
onTap: () => _copyChecksum(context, l10n),
child: _buildItem(
context,
l10n.componentCheckphraseLabel,
widget.checksum ?? l10n.commonLoading,
isCheckphrase: true,
isCopied: _checksumCopied,
),
),
);
}

Widget _buildItem(
BuildContext context,
String label,
String value, {
bool isCheckphrase = false,
required bool isCopied,
}) {
final colors = context.colorsV3;
final text = context.themeTextV3;
final valueTextStyle = isCheckphrase
? text.dataAddressLarge.copyWith(color: colors.semanticLilac)
: text.dataAddressLarge.copyWith(color: colors.textContent);

return Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(label, style: text.labelData.copyWith(color: colors.textMuted)),
const SizedBox(height: 16),
Text(value, style: valueTextStyle),
],
),
),
const SizedBox(width: 32),
_copyButton(isCopied: isCopied),
],
);
}

Widget _copyButton({required bool isCopied}) {
const containerSize = 40.0;
const iconSize = 16.0;
final colors = context.colorsV3;

return AnimatedContainer(
duration: const Duration(milliseconds: 200),
width: containerSize,
height: containerSize,
decoration: BoxDecoration(
color: isCopied ? colors.semanticSage.useOpacity(0.08) : Colors.transparent,
shape: BoxShape.circle,
border: Border.all(color: isCopied ? colors.semanticSage.useOpacity(0.15) : colors.borderHairline, width: 1),
topChild: CopyableDataItem(
label: l10n.componentAddressLabel,
value: accountId,
copiedMessage: l10n.receiveCopiedMessage,
),
child: Center(
child: Icon(
isCopied ? Icons.check : Icons.copy,
size: iconSize,
color: isCopied ? colors.semanticSage : colors.textContent,
),
bottomChild: CopyableDataItem(
label: l10n.componentCheckphraseLabel,
value: checksum ?? l10n.commonLoading,
copiedMessage: l10n.componentCheckphraseCopied,
valueColor: context.colorsV3.semanticLilac,
enabled: checksum != null,
),
);
}
Expand Down
Loading
Loading