Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
2 changes: 2 additions & 0 deletions assets/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@
"walletCreationTitle": "Create wallet",
"walletImportTitle": "Import wallet",
"walletImportByFileTitle": "Importing seed file",
"invalidWalletNameError": "Invalid wallet name, please remove special chars",
"invalidWalletFileNameError": "Invalid filename, please rename it to remove special chars",
"walletImportCreatePasswordTitle": "Create a password for \"{}\" wallet",
"walletImportByFileDescription": "Create a password of your seed file to decrypt it. This password will be used to log in to your wallet",
"walletLogInTitle": "Log in",
Expand Down
20 changes: 18 additions & 2 deletions lib/blocs/wallets_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -90,16 +90,32 @@ class WalletsRepository {
}

String? validateWalletName(String name) {
// Disallow special characters except letters, digits, space, underscore and hyphen
if (RegExp(r'[^\w\- ]').hasMatch(name)) {
return LocaleKeys.invalidWalletNameError.tr();
}
// This shouldn't happen, but just in case.
if (_cachedWallets == null) {
getWallets().ignore();
return null;
}

final trimmedName = name.trim();

// Check if the trimmed name is empty (prevents space-only names)
if (trimmedName.isEmpty) {
return LocaleKeys.walletCreationNameLengthError.tr();
}

// Check if trimmed name exceeds length limit
if (trimmedName.length > 40) {
return LocaleKeys.walletCreationNameLengthError.tr();
}

// Check for duplicates using the exact input name (not trimmed)
// This preserves backward compatibility with existing wallets that might have spaces
if (_cachedWallets!.firstWhereOrNull((w) => w.name == name) != null) {
return LocaleKeys.walletCreationExistNameError.tr();
} else if (name.isEmpty || name.length > 40) {
return LocaleKeys.walletCreationNameLengthError.tr();
}

return null;
Expand Down
5 changes: 3 additions & 2 deletions lib/generated/codegen_loader.g.dart
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,9 @@ abstract class LocaleKeys {
static const walletCreationTitle = 'walletCreationTitle';
static const walletImportTitle = 'walletImportTitle';
static const walletImportByFileTitle = 'walletImportByFileTitle';
static const walletImportCreatePasswordTitle =
'walletImportCreatePasswordTitle';
static const invalidWalletNameError = 'invalidWalletNameError';
static const invalidWalletFileNameError = 'invalidWalletFileNameError';
static const walletImportCreatePasswordTitle = 'walletImportCreatePasswordTitle';
static const walletImportByFileDescription = 'walletImportByFileDescription';
static const walletLogInTitle = 'walletLogInTitle';
static const walletCreationNameHint = 'walletCreationNameHint';
Expand Down
23 changes: 20 additions & 3 deletions lib/views/wallets_manager/widgets/wallet_creation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,19 @@ class _WalletCreationState extends State<WalletCreation> {
bool _inProgress = false;
bool _isHdMode = true;

late final WalletsRepository _walletsRepository;

@override
void initState() {
super.initState();

_walletsRepository = context.read<WalletsRepository>();

_nameController.addListener(() {
if (mounted) setState(() {});
});
}

@override
Widget build(BuildContext context) {
return BlocListener<AuthBloc, AuthBlocState>(
Expand Down Expand Up @@ -151,7 +164,7 @@ class _WalletCreationState extends State<WalletCreation> {
}

Widget _buildNameField() {
final walletsRepository = RepositoryProvider.of<WalletsRepository>(context);
final walletsRepository = _walletsRepository;
return UiTextFormField(
key: const Key('name-wallet-field'),
controller: _nameController,
Expand All @@ -173,12 +186,16 @@ class _WalletCreationState extends State<WalletCreation> {

WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
widget.onCreate(
name: _nameController.text,
name: _nameController.text.trim(),
password: _passwordController.text,
walletType: _isHdMode ? WalletType.hdwallet : WalletType.iguana,
);
});
}

bool get _isCreateButtonEnabled => _eulaAndTosChecked && !_inProgress;
bool get _isCreateButtonEnabled {
final nameError = _walletsRepository.validateWalletName(_nameController.text);
final isNameValid = nameError == null;
return _eulaAndTosChecked && !_inProgress && isNameValid;
}
}
32 changes: 31 additions & 1 deletion lib/views/wallets_manager/widgets/wallet_import_by_file.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,40 @@ class _WalletImportByFileState extends State<WalletImportByFile> {
bool _eulaAndTosChecked = false;
bool _allowCustomSeed = false;

// Whether the selected file name contains characters that are not allowed
late final bool _hasInvalidFileName;

String? _filePasswordError;
String? _commonError;

bool get _isValidData {
return _filePasswordError == null;
}

bool get _isButtonEnabled => _eulaAndTosChecked;
bool get _isButtonEnabled => _eulaAndTosChecked && !_hasInvalidFileName;

@override
void initState() {
super.initState();

// Detect illegal characters in the filename (anything other than letters, numbers, underscore, hyphen, dot and space)
_hasInvalidFileName = _containsIllegalChars(widget.fileData.name);

if (_hasInvalidFileName) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (!mounted) return;
setState(() {
_commonError = LocaleKeys.invalidWalletFileNameError.tr();
});
_formKey.currentState?.validate();
});
}
}

bool _containsIllegalChars(String fileName) {
// Allow alphanumerics, underscore, hyphen, dot and space in the filename
return RegExp(r'[^\w.\- ]').hasMatch(fileName);
}

@override
Widget build(BuildContext context) {
Expand Down Expand Up @@ -183,6 +209,10 @@ class _WalletImportByFileState extends State<WalletImportByFile> {
late final KomodoDefiSdk _sdk = context.read<KomodoDefiSdk>();

Future<void> _onImport() async {
if (_hasInvalidFileName) {
// Early return if filename is invalid; button should already be disabled
return;
}
final EncryptionTool encryptionTool = EncryptionTool();
final String? fileData = await encryptionTool.decryptData(
_filePasswordController.text,
Expand Down
4 changes: 2 additions & 2 deletions lib/views/wallets_manager/widgets/wallet_simple_import.dart
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ class _WalletImportWrapperState extends State<WalletSimpleImport> {
_step == WalletSimpleImportSteps.nameAndSeed
? LocaleKeys.walletImportTitle.tr()
: LocaleKeys.walletImportCreatePasswordTitle.tr(
args: [_nameController.text],
args: [_nameController.text.trim()],
),
style: Theme.of(
context,
Expand Down Expand Up @@ -325,7 +325,7 @@ class _WalletImportWrapperState extends State<WalletSimpleImport> {

WidgetsBinding.instance.addPostFrameCallback((_) {
widget.onImport(
name: _nameController.text,
name: _nameController.text.trim(),
password: _passwordController.text,
walletConfig: config,
);
Expand Down
Loading