mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-01-08 03:39:23 +00:00
fix: clean up nfc-related code
This commit is contained in:
parent
c7c6d61985
commit
3c871e7d9d
1 changed files with 73 additions and 91 deletions
|
@ -227,7 +227,8 @@ class _MultisigSetupViewState extends ConsumerState<MultisigCoordinatorView> {
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TextField(
|
child: TextField(
|
||||||
controller: TextEditingController(
|
controller: TextEditingController(
|
||||||
text: _myXpub),
|
text: _myXpub,
|
||||||
|
),
|
||||||
enabled: false,
|
enabled: false,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: "Loading xPub...",
|
hintText: "Loading xPub...",
|
||||||
|
@ -240,30 +241,27 @@ class _MultisigSetupViewState extends ConsumerState<MultisigCoordinatorView> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (Platform.isAndroid)
|
if (Platform.isAndroid) ...[
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
if (Platform.isAndroid)
|
|
||||||
SecondaryButton(
|
SecondaryButton(
|
||||||
width: 44,
|
width: 44,
|
||||||
buttonHeight: ButtonHeight.xl,
|
buttonHeight: ButtonHeight.xl,
|
||||||
icon: ShareIcon(
|
icon: ShareIcon(
|
||||||
// TODO: Replace with NFC icon.
|
|
||||||
width: 20,
|
width: 20,
|
||||||
height: 20,
|
height: 20,
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
.buttonTextSecondary,
|
.buttonTextSecondary,
|
||||||
),
|
),
|
||||||
onPressed: () async {
|
onPressed: () => _showNfcDialog(
|
||||||
await _showNfcDialog(
|
title: 'Tap to share xPub',
|
||||||
title: 'Tap to share xPub',
|
onStartNfc: () =>
|
||||||
onStartNfc: () =>
|
_startNfcSessionShareXpub(
|
||||||
_startNfcSessionShareXpub(
|
_myXpub),
|
||||||
_myXpub),
|
isWriting: true,
|
||||||
isWriting: true,
|
),
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
|
],
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
SecondaryButton(
|
SecondaryButton(
|
||||||
width: 44,
|
width: 44,
|
||||||
|
@ -344,18 +342,17 @@ class _MultisigSetupViewState extends ConsumerState<MultisigCoordinatorView> {
|
||||||
if (value.isNotEmpty) {
|
if (value.isNotEmpty) {
|
||||||
ref
|
ref
|
||||||
.read(
|
.read(
|
||||||
multisigCoordinatorStateProvider
|
multisigCoordinatorStateProvider
|
||||||
.notifier)
|
.notifier,
|
||||||
|
)
|
||||||
.addCosignerXpub(value);
|
.addCosignerXpub(value);
|
||||||
}
|
}
|
||||||
setState(
|
setState(() {});
|
||||||
() {}); // Trigger rebuild to update button state.
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (Platform.isAndroid)
|
if (Platform.isAndroid) ...[
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
if (Platform.isAndroid)
|
|
||||||
SecondaryButton(
|
SecondaryButton(
|
||||||
width: 44,
|
width: 44,
|
||||||
buttonHeight: ButtonHeight.xl,
|
buttonHeight: ButtonHeight.xl,
|
||||||
|
@ -367,15 +364,13 @@ class _MultisigSetupViewState extends ConsumerState<MultisigCoordinatorView> {
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
.buttonTextSecondary,
|
.buttonTextSecondary,
|
||||||
),
|
),
|
||||||
onPressed: () async {
|
onPressed: () => _showNfcDialog(
|
||||||
await _showNfcDialog(
|
title: 'Tap to scan xPub',
|
||||||
title: 'Tap to scan xPub',
|
onStartNfc: () =>
|
||||||
onStartNfc: () =>
|
_startNfcSessionScanXpub(i - 1),
|
||||||
_startNfcSessionScanXpub(
|
),
|
||||||
i - 1),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
|
],
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
SecondaryButton(
|
SecondaryButton(
|
||||||
width: 44,
|
width: 44,
|
||||||
|
@ -387,7 +382,7 @@ class _MultisigSetupViewState extends ConsumerState<MultisigCoordinatorView> {
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
.buttonTextSecondary,
|
.buttonTextSecondary,
|
||||||
),
|
),
|
||||||
onPressed: () => {scanQr(i - 1)},
|
onPressed: () => scanQr(i - 1),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
SecondaryButton(
|
SecondaryButton(
|
||||||
|
@ -402,17 +397,18 @@ class _MultisigSetupViewState extends ConsumerState<MultisigCoordinatorView> {
|
||||||
),
|
),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
final data = await Clipboard.getData(
|
final data = await Clipboard.getData(
|
||||||
'text/plain');
|
'text/plain',
|
||||||
|
);
|
||||||
if (data?.text != null) {
|
if (data?.text != null) {
|
||||||
xpubControllers[i - 1].text =
|
xpubControllers[i - 1].text =
|
||||||
data!.text!;
|
data!.text!;
|
||||||
ref
|
ref
|
||||||
.read(
|
.read(
|
||||||
multisigCoordinatorStateProvider
|
multisigCoordinatorStateProvider
|
||||||
.notifier)
|
.notifier,
|
||||||
|
)
|
||||||
.addCosignerXpub(data.text!);
|
.addCosignerXpub(data.text!);
|
||||||
setState(
|
setState(() {});
|
||||||
() {}); // Trigger rebuild to update button state.
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -423,11 +419,11 @@ class _MultisigSetupViewState extends ConsumerState<MultisigCoordinatorView> {
|
||||||
),
|
),
|
||||||
|
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
|
|
||||||
PrimaryButton(
|
PrimaryButton(
|
||||||
label: "Create multisignature account",
|
label: "Create multisignature account",
|
||||||
enabled: xpubControllers.every(
|
enabled: xpubControllers.every(
|
||||||
(controller) => controller.text.isNotEmpty),
|
(controller) => controller.text.isNotEmpty,
|
||||||
|
),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
final _newWallet = _multisigWallet;
|
final _newWallet = _multisigWallet;
|
||||||
|
|
||||||
|
@ -476,8 +472,7 @@ class _MultisigSetupViewState extends ConsumerState<MultisigCoordinatorView> {
|
||||||
final parentWallet =
|
final parentWallet =
|
||||||
await (ref.read(pWallets).getWallet(widget.walletId) as Bip39HDWallet);
|
await (ref.read(pWallets).getWallet(widget.walletId) as Bip39HDWallet);
|
||||||
|
|
||||||
String? otherDataJsonString;
|
final otherDataJsonString = jsonEncode({
|
||||||
otherDataJsonString = jsonEncode({
|
|
||||||
'threshold': widget.threshold,
|
'threshold': widget.threshold,
|
||||||
'participants': widget.participants,
|
'participants': widget.participants,
|
||||||
'coinType':
|
'coinType':
|
||||||
|
@ -526,14 +521,10 @@ class _MultisigSetupViewState extends ConsumerState<MultisigCoordinatorView> {
|
||||||
var node = ref
|
var node = ref
|
||||||
.read(nodeServiceChangeNotifierProvider)
|
.read(nodeServiceChangeNotifierProvider)
|
||||||
.getPrimaryNodeFor(currency: parentWallet.cryptoCurrency);
|
.getPrimaryNodeFor(currency: parentWallet.cryptoCurrency);
|
||||||
|
node ??= parentWallet.cryptoCurrency.defaultNode;
|
||||||
if (node == null) {
|
await ref
|
||||||
node = parentWallet.cryptoCurrency.defaultNode;
|
.read(nodeServiceChangeNotifierProvider)
|
||||||
await ref.read(nodeServiceChangeNotifierProvider).setPrimaryNodeFor(
|
.setPrimaryNodeFor(coin: parentWallet.cryptoCurrency, node: node);
|
||||||
coin: parentWallet.cryptoCurrency,
|
|
||||||
node: node,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final txTracker = TransactionNotificationTracker(walletId: info.walletId);
|
final txTracker = TransactionNotificationTracker(walletId: info.walletId);
|
||||||
|
|
||||||
|
@ -563,9 +554,7 @@ class _MultisigSetupViewState extends ConsumerState<MultisigCoordinatorView> {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
if (isDesktop) {
|
if (isDesktop) {
|
||||||
Navigator.of(context).popUntil(
|
Navigator.of(context).popUntil(
|
||||||
ModalRoute.withName(
|
ModalRoute.withName(DesktopHomeView.routeName),
|
||||||
DesktopHomeView.routeName,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
unawaited(
|
unawaited(
|
||||||
|
@ -640,104 +629,107 @@ class _MultisigSetupViewState extends ConsumerState<MultisigCoordinatorView> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _startNfcSessionShareXpub(String xpub) async {
|
Future<void> _startNfcSessionShareXpub(String xpub) async {
|
||||||
|
setState(() => _nfcStatus = 'Starting NFC sharing...');
|
||||||
|
|
||||||
if (!_isNfcAvailable) {
|
if (!_isNfcAvailable) {
|
||||||
setState(() => _nfcStatus = 'NFC is not available on this device');
|
setState(() => _nfcStatus = 'NFC not available on this device.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final isEnabled = await _flutterNfcHcePlugin.isNfcEnabled();
|
final isEnabled = await _flutterNfcHcePlugin.isNfcEnabled();
|
||||||
if (!isEnabled) {
|
if (!isEnabled) {
|
||||||
setState(() => _nfcStatus = 'Please enable NFC on your device');
|
setState(
|
||||||
|
() => _nfcStatus = 'Please enable NFC in your device settings.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setState(() => _nfcStatus = 'Starting NFC sharing...');
|
|
||||||
|
|
||||||
final result = await _flutterNfcHcePlugin.startNfcHce(
|
final result = await _flutterNfcHcePlugin.startNfcHce(
|
||||||
xpub,
|
xpub,
|
||||||
mimeType: 'text/plain',
|
mimeType: 'text/plain', // We want text record
|
||||||
persistMessage: false,
|
persistMessage: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (result == 'success') {
|
if (result == 'success') {
|
||||||
setState(() => _nfcStatus = 'Sharing xPub: hold devices together');
|
setState(() => _nfcStatus = 'Sharing xPub! Hold devices together.');
|
||||||
} else {
|
} else {
|
||||||
setState(() => _nfcStatus = 'Failed to start NFC sharing');
|
setState(() => _nfcStatus = 'Failed to start NFC sharing.');
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setState(() => _nfcStatus = 'Error: ${e.toString()}');
|
setState(() => _nfcStatus = 'Error: $e');
|
||||||
await _stopNfcSession();
|
await _stopNfcSession();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _startNfcSessionScanXpub(int cosignerIndex) async {
|
Future<void> _startNfcSessionScanXpub(int cosignerIndex) async {
|
||||||
|
setState(() => _nfcStatus = 'Starting NFC reading...');
|
||||||
|
|
||||||
if (!_isNfcAvailable) {
|
if (!_isNfcAvailable) {
|
||||||
setState(() => _nfcStatus = 'NFC is not available');
|
setState(() => _nfcStatus = 'NFC not available.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setState(() => _nfcStatus = 'Ready to scan xPub...');
|
|
||||||
|
|
||||||
await NfcManager.instance.startSession(
|
await NfcManager.instance.startSession(
|
||||||
onDiscovered: (NfcTag tag) async {
|
onDiscovered: (NfcTag tag) async {
|
||||||
try {
|
try {
|
||||||
final ndef = Ndef.from(tag);
|
final ndef = Ndef.from(tag);
|
||||||
if (ndef == null) {
|
if (ndef == null) {
|
||||||
setState(() => _nfcStatus = 'Invalid NFC tag format');
|
setState(() => _nfcStatus = 'Invalid tag format.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final message = await ndef.read();
|
final message = await ndef.read();
|
||||||
if (message.records.isEmpty) {
|
if (message.records.isEmpty) {
|
||||||
setState(() => _nfcStatus = 'No data found on tag');
|
setState(() => _nfcStatus = 'No data on tag.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse xPub from the first record.
|
||||||
final record = message.records.first;
|
final record = message.records.first;
|
||||||
String xpub;
|
String xpub;
|
||||||
|
|
||||||
if (record.typeNameFormat == NdefTypeNameFormat.nfcWellknown &&
|
if (record.typeNameFormat == NdefTypeNameFormat.nfcWellknown &&
|
||||||
record.type.length == 1 &&
|
record.type.length == 1 &&
|
||||||
record.type[0] == 'T'.codeUnitAt(0)) {
|
record.type[0] == 'T'.codeUnitAt(0)) {
|
||||||
final payload = record.payload;
|
final payload = record.payload;
|
||||||
final languageCodeLength = payload[0] & 0x3F;
|
final langLen = payload[0] & 0x3F;
|
||||||
xpub = utf8.decode(payload.sublist(1 + languageCodeLength));
|
xpub = utf8.decode(payload.sublist(1 + langLen));
|
||||||
} else {
|
} else {
|
||||||
xpub = utf8.decode(record.payload);
|
xpub = utf8.decode(record.payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!xpub.startsWith('xpub') && !xpub.startsWith('tpub')) {
|
if (!xpub.startsWith(
|
||||||
setState(() => _nfcStatus = 'Invalid xPub format: $xpub');
|
'xpub') /* && !xpub.startsWith('tpub') and so on. */) {
|
||||||
|
setState(() => _nfcStatus = 'Invalid xPub: $xpub');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Success: fill field, show success text.
|
||||||
setState(() {
|
setState(() {
|
||||||
xpubControllers[cosignerIndex].text = xpub;
|
xpubControllers[cosignerIndex].text = xpub;
|
||||||
_nfcStatus = 'Successfully read xPub';
|
_nfcStatus = 'Successfully read xPub';
|
||||||
});
|
});
|
||||||
|
|
||||||
ref
|
ref.read(multisigCoordinatorStateProvider.notifier).addCosignerXpub(
|
||||||
.read(multisigCoordinatorStateProvider.notifier)
|
xpub,
|
||||||
.addCosignerXpub(xpub);
|
);
|
||||||
|
|
||||||
await NfcManager.instance.stopSession();
|
await NfcManager.instance.stopSession();
|
||||||
|
|
||||||
// Delay to show success message before dismissing.
|
// Delay just to show message.
|
||||||
await Future.delayed(const Duration(milliseconds: 500));
|
await Future.delayed(const Duration(milliseconds: 500));
|
||||||
|
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setState(() => _nfcStatus = 'Error reading tag: ${e.toString()}');
|
setState(() => _nfcStatus = 'Error reading tag: $e');
|
||||||
await NfcManager.instance.stopSession();
|
await NfcManager.instance.stopSession();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setState(() => _nfcStatus = 'Error: ${e.toString()}');
|
setState(() => _nfcStatus = 'Error: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -766,7 +758,8 @@ class _MultisigSetupViewState extends ConsumerState<MultisigCoordinatorView> {
|
||||||
Text(_nfcStatus),
|
Text(_nfcStatus),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
if (!_nfcStatus.contains('Error') &&
|
if (!_nfcStatus.contains('Error') &&
|
||||||
!_nfcStatus.contains('Success'))
|
!_nfcStatus.contains('Success') &&
|
||||||
|
!_nfcStatus.contains('hold devices together'))
|
||||||
const CircularProgressIndicator(),
|
const CircularProgressIndicator(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -795,19 +788,14 @@ class _MultisigSetupViewState extends ConsumerState<MultisigCoordinatorView> {
|
||||||
if (Platform.isAndroid || Platform.isIOS) {
|
if (Platform.isAndroid || Platform.isIOS) {
|
||||||
if (FocusScope.of(context).hasFocus) {
|
if (FocusScope.of(context).hasFocus) {
|
||||||
FocusScope.of(context).unfocus();
|
FocusScope.of(context).unfocus();
|
||||||
await Future<void>.delayed(
|
await Future<void>.delayed(const Duration(milliseconds: 75));
|
||||||
const Duration(milliseconds: 75),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final qrResult = await BarcodeScanner.scan();
|
final qrResult = await BarcodeScanner.scan();
|
||||||
|
|
||||||
xpubControllers[cosignerIndex].text = qrResult.rawContent;
|
xpubControllers[cosignerIndex].text = qrResult.rawContent;
|
||||||
|
ref.read(multisigCoordinatorStateProvider.notifier).addCosignerXpub(
|
||||||
ref
|
qrResult.rawContent,
|
||||||
.read(multisigCoordinatorStateProvider.notifier)
|
);
|
||||||
.addCosignerXpub(qrResult.rawContent);
|
|
||||||
|
|
||||||
setState(() {});
|
setState(() {});
|
||||||
} else {
|
} else {
|
||||||
// Platform.isLinux, Platform.isWindows, or Platform.isMacOS.
|
// Platform.isLinux, Platform.isWindows, or Platform.isMacOS.
|
||||||
|
@ -815,25 +803,19 @@ class _MultisigSetupViewState extends ConsumerState<MultisigCoordinatorView> {
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => const QrCodeScannerDialog(),
|
builder: (context) => const QrCodeScannerDialog(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (qrResult == null) {
|
if (qrResult == null) {
|
||||||
Logging.instance.log(
|
Logging.instance.log("QR scanning cancelled", level: LogLevel.Info);
|
||||||
"QR scanning cancelled",
|
|
||||||
level: LogLevel.Info,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
xpubControllers[cosignerIndex].text = qrResult;
|
xpubControllers[cosignerIndex].text = qrResult;
|
||||||
|
|
||||||
ref
|
ref
|
||||||
.read(multisigCoordinatorStateProvider.notifier)
|
.read(multisigCoordinatorStateProvider.notifier)
|
||||||
.addCosignerXpub(qrResult);
|
.addCosignerXpub(qrResult);
|
||||||
|
setState(() {});
|
||||||
setState(() {}); // Trigger rebuild to update button state.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} on PlatformException catch (e, s) {
|
} on PlatformException catch (e, s) {
|
||||||
Logging.instance.log(
|
Logging.instance.log(
|
||||||
"Failed to get camera permissions while trying to scan qr code: $e\n$s",
|
"Failed to get camera permissions while scanning QR: $e\n$s",
|
||||||
level: LogLevel.Warning,
|
level: LogLevel.Warning,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue