mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-26 06:08:40 +01:00
* refactor: migrate hardware wallet resources to `new-ui`, add Trezor model support, and streamline hardware wallet handling across services * refactor: migrate hardware wallet resources to `new-ui`, add Trezor model support, and streamline hardware wallet handling across services * fix: correctly check for Ledger hardware wallet type in `isHardwareWallet` logic * refactor: unify `isConnected` method for hardware wallets, add Trezor-specific support, and update Monero sync logic * refactor: add Trezor transaction signing support, streamline hardware wallet handling, and update Monero dependency * refactor: improve Trezor transaction handling, adjust hardware wallet state logic, and update Monero dependency references * fix: non-hww tx commit() in xmr fix: prod monero_c fix: keyImage import workaround for Trezor/Cupcake chore: add kotlin's .salive to .gitignore * fix: universal_ble builds on iOS chore: remove prints fix: proper CI prebuilt for linux * new trezor connection ui * fix page title * typo * fix page title anim * refactor: enhance Trezor pairing flow, update state management, and simplify UI logic in hardware wallet integration * fix: correct widget indentation in Trezor pairing failure state UI * fix: extract Trezor pairing failure state UI to `_errorBox` widget and update Trezor dependency reference * refactor: simplify hardware wallet pairing error UI, optimize `_errorBox` layout, and streamline widget logic * Update cw_monero/lib/pending_monero_transaction.dart Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * Update lib/buy/robinhood/robinhood_buy_provider.dart Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * feat: add USB intent filters, device filter for hardware wallets, and update Trezor dependency reference * Update cw_monero/pubspec.yaml [skip ci] * Update cw_wownero/pubspec.yaml [skip ci] * Update cw_zano/pubspec.yaml [skip ci] * fix: use CryptoCurrency.title instead of amount-refactor-only .symbol getter new_wallet_type_page used curr.symbol, which only exists on the amount-refactor branch (String get symbol => title). On dev the equivalent is curr.title. Adapts the Trezor feature to dev's API without pulling in amount-refactor. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Fix wallet list screen not scrollable minor fixes --------- Co-authored-by: Konstantin Ullrich <konstantinullrich12@gmail.com> Co-authored-by: Czarek Nakamoto <cyjan@mrcyjanek.net> Co-authored-by: Robert Malikowski <malikowskirobert@gmail.com> Co-authored-by: Konstantin Ullrich <konstantin@cakewallet.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
183 lines
4.7 KiB
Dart
183 lines
4.7 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
|
|
class DigitInputController {
|
|
String _text = "";
|
|
|
|
String get text => _text;
|
|
|
|
set text(String newText) {
|
|
_text = newText;
|
|
for (final listener in _listeners) {
|
|
listener.call();
|
|
}
|
|
}
|
|
|
|
final List<void Function()> _listeners = [];
|
|
|
|
void addListener(void Function() listener) => _listeners.add(listener);
|
|
}
|
|
|
|
class DigitInputPill extends StatelessWidget {
|
|
const DigitInputPill({super.key, this.digit, required this.highlighted});
|
|
|
|
final String? digit;
|
|
final bool highlighted;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Container(
|
|
width: 51,
|
|
height: 84,
|
|
decoration: BoxDecoration(
|
|
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
|
borderRadius: BorderRadius.circular(16),
|
|
border:
|
|
highlighted ? Border.all(color: Theme.of(context).colorScheme.primary, width: 2) : null,
|
|
),
|
|
child: Center(
|
|
child: Padding(
|
|
padding: EdgeInsets.symmetric(vertical: 16),
|
|
child: Text(digit ?? " ", style: TextStyle(fontSize: 40)),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class DigitInput extends StatefulWidget {
|
|
const DigitInput({
|
|
super.key,
|
|
required this.controller,
|
|
required this.desiredLength,
|
|
this.breakAt = 3,
|
|
});
|
|
|
|
final DigitInputController controller;
|
|
final int desiredLength;
|
|
final int breakAt;
|
|
|
|
@override
|
|
State<DigitInput> createState() => _DigitInputState();
|
|
}
|
|
|
|
class _DigitInputState extends State<DigitInput> implements TextInputClient {
|
|
TextInputConnection? _textInputConnection;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_openVirtualKeyboard();
|
|
}
|
|
|
|
void _openVirtualKeyboard() {
|
|
if (_textInputConnection == null || !_textInputConnection!.attached) {
|
|
_textInputConnection = TextInput.attach(
|
|
this,
|
|
const TextInputConfiguration(
|
|
inputType: TextInputType.numberWithOptions(
|
|
signed: false,
|
|
decimal: false,
|
|
),
|
|
inputAction: TextInputAction.none,
|
|
),
|
|
);
|
|
}
|
|
|
|
_textInputConnection!.setEditingState(
|
|
TextEditingValue(
|
|
text: widget.controller.text,
|
|
selection: TextSelection.collapsed(offset: widget.controller.text.length),
|
|
),
|
|
);
|
|
_textInputConnection!.show();
|
|
}
|
|
|
|
void _closeVirtualKeyboard() => _textInputConnection?.close();
|
|
|
|
@override
|
|
void dispose() {
|
|
_closeVirtualKeyboard();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return GestureDetector(
|
|
onTap: _openVirtualKeyboard,
|
|
child: SizedBox(
|
|
height: 84,
|
|
child: ListView.separated(
|
|
scrollDirection: Axis.horizontal,
|
|
shrinkWrap: true,
|
|
physics: NeverScrollableScrollPhysics(),
|
|
itemBuilder: (context, index) => DigitInputPill(
|
|
digit: widget.controller.text.length > index ? widget.controller.text[index] : null,
|
|
highlighted: widget.controller.text.length == index,
|
|
),
|
|
separatorBuilder: (context, index) =>
|
|
SizedBox(width: (index + 1) % widget.breakAt == 0 ? 16 : 4),
|
|
itemCount: widget.desiredLength,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
void updateEditingValue(TextEditingValue value) {
|
|
final RegExp numberRegExp = RegExp(r'^\d{0,6}$');
|
|
|
|
if (numberRegExp.hasMatch(value.text)) {
|
|
setState(() => widget.controller.text = value.text);
|
|
} else {
|
|
// flutter keeps the text editing state regardless of whether you actually used it or not.
|
|
// so, if validation fails, we gotta set it back to what it was explicitly
|
|
_textInputConnection?.setEditingState(
|
|
TextEditingValue(
|
|
text: widget.controller.text,
|
|
selection: TextSelection.collapsed(offset: widget.controller.text.length),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
@override
|
|
void performAction(TextInputAction action) {}
|
|
|
|
@override
|
|
TextEditingValue? get currentTextEditingValue => TextEditingValue(text: widget.controller.text);
|
|
|
|
@override
|
|
void connectionClosed() => _textInputConnection = null;
|
|
|
|
@override
|
|
AutofillScope? get currentAutofillScope => null;
|
|
|
|
@override
|
|
void performPrivateCommand(String action, Map<String, dynamic> data) {}
|
|
|
|
@override
|
|
void showAutocorrectionPromptRect(int start, int end) {}
|
|
|
|
@override
|
|
void updateFloatingCursor(RawFloatingCursorPoint point) {}
|
|
|
|
@override
|
|
void insertTextPlaceholder(Size size) {}
|
|
|
|
@override
|
|
void removeTextPlaceholder() {}
|
|
|
|
@override
|
|
void showToolbar() {}
|
|
|
|
@override
|
|
void performSelector(String selectorName) {}
|
|
|
|
@override
|
|
void didChangeInputControl(TextInputControl? oldControl, TextInputControl? newControl) {}
|
|
|
|
@override
|
|
void insertContent(KeyboardInsertedContent content) {}
|
|
}
|