From 3b5994bed9e426039d1421899b08f0192b24fe83 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Thu, 30 Nov 2023 18:11:20 +0000 Subject: [PATCH 001/131] Update edit_custom_field_screen.dart --- lib/screens/edit_custom_field_screen.dart | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/screens/edit_custom_field_screen.dart b/lib/screens/edit_custom_field_screen.dart index b6d2adeb..16d0178f 100644 --- a/lib/screens/edit_custom_field_screen.dart +++ b/lib/screens/edit_custom_field_screen.dart @@ -55,7 +55,20 @@ class _EditCustomFieldScreen extends State { ], value: _customField.fieldType, decoration: InputDecoration(labelText: localizations.type), - onChanged: (value) => _customField.fieldType = value as FieldType, + onChanged: (value) { + if (value == null) return; + dynamic type = value as FieldType; + bool obscured; + if (type == FieldType.password) { + obscured = true; + } else { + obscured = false; + } + setState(() { + _customField.fieldType = type; + _customField.obscured = obscured; + }); + }, )), PassyPadding(ThreeWidgetButton( center: Text(localizations.obscured), From 288bae6ea90ff199730f915f4bea9d356d8d1987 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Mon, 4 Dec 2023 17:04:04 +0000 Subject: [PATCH 002/131] CLI: Redundant type checks removed --- lib/passy_cli/bin/passy_cli.dart | 86 ++++++-------------------------- 1 file changed, 15 insertions(+), 71 deletions(-) diff --git a/lib/passy_cli/bin/passy_cli.dart b/lib/passy_cli/bin/passy_cli.dart index 5efc0df8..8087c992 100644 --- a/lib/passy_cli/bin/passy_cli.dart +++ b/lib/passy_cli/bin/passy_cli.dart @@ -1562,18 +1562,8 @@ Future executeCommand(List command, 'error': {'type': 'Missing arguments'}, }; } - dynamic username = args[3]; - dynamic auth = args[4]; - if (username is! String) { - throw { - 'error': {'type': 'Username is not of type String'}, - }; - } - if (auth is! String) { - throw { - 'error': {'type': 'Auth is not of type String'}, - }; - } + String username = args[3]; + String auth = args[4]; Encrypter? encrypter = _encrypters[username]; if (encrypter == null) { return { @@ -1610,12 +1600,7 @@ Future executeCommand(List command, 'error': {'type': 'Missing arguments'}, }; } - dynamic username = args[3]; - if (username is! String) { - throw { - 'error': {'type': 'Username is not of type String'}, - }; - } + String username = args[3]; refreshAccounts(); AccountCredentialsFile? creds = _accounts[username]; if (creds == null) { @@ -1642,18 +1627,8 @@ Future executeCommand(List command, 'error': {'type': 'Missing arguments'}, }; } - dynamic username = args[3]; - dynamic password = args[4]; - if (username is! String) { - throw { - 'error': {'type': 'Username is not of type String'}, - }; - } - if (password is! String) { - throw { - 'error': {'type': 'Password is not of type String'}, - }; - } + String username = args[3]; + String password = args[4]; String accPath = _accountsPath + Platform.pathSeparator + username + @@ -1694,8 +1669,9 @@ Future executeCommand(List command, } } loadedModules.remove('2d0d1_$username'); + Uint8List passwordBytes; try { - password = base64Decode(password); + passwordBytes = base64Decode(password); } catch (e) { return { 'error': { @@ -1703,11 +1679,11 @@ Future executeCommand(List command, }, }; } - String result = await _login(username, password); + String result = await _login(username, passwordBytes); if (result != 'true') { return { 'error': {'type': 'Failedd to login'}, - 'hash': sha512.convert(password).toString(), + 'hash': sha512.convert(passwordBytes).toString(), }; } Encrypter encrypter = _encrypters[username]!; @@ -1783,18 +1759,8 @@ Future executeCommand(List command, 'error': {'type': 'Missing arguments'}, }; } - dynamic username = args[3]; - dynamic deviceId = args[4]; - if (username is! String) { - throw { - 'error': {'type': 'Username is not of type String'}, - }; - } - if (deviceId is! String) { - throw { - 'error': {'type': 'Device id is not of type String'}, - }; - } + String username = args[3]; + String deviceId = args[4]; if (deviceId.length < 16) { throw { 'error': {'type': 'Invalid device id'}, @@ -1836,32 +1802,10 @@ Future executeCommand(List command, 'error': {'type': 'Missing arguments'}, }; } - dynamic username = args[3]; - dynamic deviceId = args[4]; - dynamic auth = args[5]; - dynamic connectionData = args[6]; - if (username is! String) { - throw { - 'error': {'type': 'Username is not of type String'}, - }; - } - if (deviceId is! String) { - throw { - 'error': {'type': 'Device id is not of type String'}, - }; - } - if (auth is! String) { - throw { - 'error': {'type': 'Auth is not of type String'}, - }; - } - if (connectionData is! String) { - throw { - 'error': { - 'type': 'Connection data is not of type String' - }, - }; - } + String username = args[3]; + String deviceId = args[4]; + String auth = args[5]; + String connectionData = args[6]; if (deviceId.length < 16) { throw { 'error': {'type': 'Invalid device id'}, From 0721fdb8c8e9d949e40da82faffe7db5c8a50fd1 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Tue, 5 Dec 2023 23:52:22 +0000 Subject: [PATCH 003/131] 'isGoogle' TFA property now synchronized --- lib/passy_data/tfa.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/passy_data/tfa.dart b/lib/passy_data/tfa.dart index cc9d0867..01d57ba2 100644 --- a/lib/passy_data/tfa.dart +++ b/lib/passy_data/tfa.dart @@ -35,7 +35,7 @@ class TFA with JsonConvertable, CSVConvertable { length = json['length'], interval = json['interval'], algorithm = algorithmFromName(json['algorithm']) ?? Algorithm.SHA1, - isGoogle = true; + isGoogle = json['isGoogle'] ?? true; TFA.fromCSV(List csv) : secret = csv[0], From 0146c5f49f73c6a68ac924df85417849a1cd463f Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Thu, 7 Dec 2023 01:11:58 +0000 Subject: [PATCH 004/131] TFA.type implemented --- lib/passy_data/tfa.dart | 44 ++++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/lib/passy_data/tfa.dart b/lib/passy_data/tfa.dart index 01d57ba2..ef6ac29a 100644 --- a/lib/passy_data/tfa.dart +++ b/lib/passy_data/tfa.dart @@ -3,6 +3,27 @@ import 'package:passy/passy_data/common.dart'; import 'package:passy/passy_data/csv_convertable.dart'; import 'package:passy/passy_data/json_convertable.dart'; +enum TFAType { +// ignore: constant_identifier_names + TOTP, +// ignore: constant_identifier_names + HOTP, +// ignore: constant_identifier_names + Steam, +} + +TFAType? tfaTypeFromName(String name) { + switch (name) { + case 'TOTP': + return TFAType.TOTP; + case 'HOTP': + return TFAType.HOTP; + case 'Steam': + return TFAType.Steam; + } + return null; +} + Algorithm? algorithmFromName(String name) { switch (name) { case 'SHA1': @@ -21,6 +42,7 @@ class TFA with JsonConvertable, CSVConvertable { int interval; Algorithm algorithm; bool isGoogle; + TFAType type; TFA({ this.secret = '', @@ -28,6 +50,7 @@ class TFA with JsonConvertable, CSVConvertable { this.interval = 30, this.algorithm = Algorithm.SHA1, this.isGoogle = true, + this.type = TFAType.TOTP, }); TFA.fromJson(Map json) @@ -35,14 +58,19 @@ class TFA with JsonConvertable, CSVConvertable { length = json['length'], interval = json['interval'], algorithm = algorithmFromName(json['algorithm']) ?? Algorithm.SHA1, - isGoogle = json['isGoogle'] ?? true; + isGoogle = json['isGoogle'] ?? true, + type = tfaTypeFromName(json['type']) ?? TFAType.TOTP; - TFA.fromCSV(List csv) - : secret = csv[0], - length = int.tryParse(csv[1]) ?? 6, - interval = int.tryParse(csv[2]) ?? 30, - algorithm = algorithmFromName(csv[3]) ?? Algorithm.SHA1, - isGoogle = boolFromString(csv[4]) ?? true; + factory TFA.fromCSV(List csv) { + if (csv.length == 5) csv.add(TFAType.TOTP.name); + return TFA( + secret: csv[0], + length: int.tryParse(csv[1]) ?? 6, + interval: int.tryParse(csv[2]) ?? 30, + algorithm: algorithmFromName(csv[3]) ?? Algorithm.SHA1, + isGoogle: boolFromString(csv[4]) ?? true, + type: tfaTypeFromName(csv[5]) ?? TFAType.TOTP); + } @override Map toJson() => { @@ -51,6 +79,7 @@ class TFA with JsonConvertable, CSVConvertable { 'interval': interval, 'algorithm': algorithm.name, 'isGoogle': isGoogle, + 'type': type.name, }; @override @@ -60,5 +89,6 @@ class TFA with JsonConvertable, CSVConvertable { interval.toString(), algorithm.name, isGoogle.toString(), + type.name, ]; } From 2c7a3c477124b2c227db2fbb57679994c3dd1dc4 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Thu, 7 Dec 2023 01:35:33 +0000 Subject: [PATCH 005/131] TFA.generate() implemented --- lib/passy_data/tfa.dart | 60 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/lib/passy_data/tfa.dart b/lib/passy_data/tfa.dart index ef6ac29a..d00e3fb8 100644 --- a/lib/passy_data/tfa.dart +++ b/lib/passy_data/tfa.dart @@ -1,7 +1,11 @@ +import 'dart:typed_data'; + +import 'package:crypto/crypto.dart'; import 'package:otp/otp.dart'; import 'package:passy/passy_data/common.dart'; import 'package:passy/passy_data/csv_convertable.dart'; import 'package:passy/passy_data/json_convertable.dart'; +import 'package:base32/base32.dart'; enum TFAType { // ignore: constant_identifier_names @@ -91,4 +95,60 @@ class TFA with JsonConvertable, CSVConvertable { isGoogle.toString(), type.name, ]; + + String _generateTOTP() { + return OTP.generateTOTPCodeString( + secret, + DateTime.now().millisecondsSinceEpoch, + length: length, + interval: interval, + algorithm: algorithm, + isGoogle: isGoogle, + ); + } + + String _generateHOTP() { + return OTP.generateHOTPCodeString( + secret, + interval, + length: length, + algorithm: algorithm, + isGoogle: isGoogle, + ); + } + + Future _generateSteam() async { + var time = ByteData(8) + ..setUint32(0, 0, Endian.big) + ..setUint32( + 4, + ((DateTime.now().millisecondsSinceEpoch / 1000).floor() / 30).floor(), + Endian.big); + Hmac hmac = Hmac(sha1, base32.decode(secret)); + Digest digest = hmac.convert(time.buffer.asUint8List()); + var bytes = digest.bytes; + var start = bytes[19] & 0x0F; + var code = bytes.sublist(start, start + 4); + var totp = + Uint8List.fromList(code).buffer.asByteData().getUint32(0, Endian.big) & + 0x7FFFFFFF; + const chars = '23456789BCDFGHJKMNPQRTVWXY'; + String result = ''; + for (int i = 0; i != 5; i++) { + result += chars[(totp % chars.length).toInt()]; + totp ~/= chars.length; + } + return result; + } + + Future generate() async { + switch (type) { + case TFAType.TOTP: + return _generateTOTP(); + case TFAType.HOTP: + return _generateHOTP(); + case TFAType.Steam: + return _generateSteam(); + } + } } From f4811a6e467821870a0bc4863b98b83a76fef6e4 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Thu, 7 Dec 2023 01:38:56 +0000 Subject: [PATCH 006/131] TFA.generate() is now synchronous --- lib/passy_data/tfa.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/passy_data/tfa.dart b/lib/passy_data/tfa.dart index d00e3fb8..1b6b7cf8 100644 --- a/lib/passy_data/tfa.dart +++ b/lib/passy_data/tfa.dart @@ -117,7 +117,7 @@ class TFA with JsonConvertable, CSVConvertable { ); } - Future _generateSteam() async { + String _generateSteam() { var time = ByteData(8) ..setUint32(0, 0, Endian.big) ..setUint32( @@ -141,7 +141,7 @@ class TFA with JsonConvertable, CSVConvertable { return result; } - Future generate() async { + String generate() { switch (type) { case TFAType.TOTP: return _generateTOTP(); From 9024e87ad27c27366f233aaf64b819c2b4ddd85e Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Thu, 7 Dec 2023 18:57:15 +0000 Subject: [PATCH 007/131] New TFA types UI implemented --- lib/l10n/app_en.arb | 5 +- lib/screens/edit_password_screen.dart | 93 +++++++++++++++++------- lib/screens/password_screen.dart | 100 +++++++++++++++++--------- 3 files changed, 140 insertions(+), 58 deletions(-) diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 100a3968..a36356f8 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -337,5 +337,8 @@ "scrollOrPinchToZoom": "Scroll or pinch to zoom", "missingFile": "Missing file", "rename": "Rename", - "fileName": "File name" + "fileName": "File name", + "refresh": "Refresh", + "counter": "Counter", + "@counter": { "description": "HOTP 2FA counter stores a number that is incremented on every logon" } } diff --git a/lib/screens/edit_password_screen.dart b/lib/screens/edit_password_screen.dart index c5ed5b22..73eed109 100644 --- a/lib/screens/edit_password_screen.dart +++ b/lib/screens/edit_password_screen.dart @@ -45,6 +45,8 @@ class _EditPasswordScreen extends State { Algorithm _tfaAlgorithm = Algorithm.SHA1; bool _tfaIsGoogle = true; bool _tfaIsExpanded = false; + TFAType _tfaType = TFAType.TOTP; + UniqueKey _tfaKey = UniqueKey(); String _website = ''; List _attachments = []; @@ -78,6 +80,7 @@ class _EditPasswordScreen extends State { _tfaInterval = _tfa.interval; _tfaAlgorithm = _tfa.algorithm; _tfaIsGoogle = _tfa.isGoogle; + _tfaType = _tfa.type; } _website = _passwordArgs.website; _attachments = List.from(_passwordArgs.attachments); @@ -109,6 +112,7 @@ class _EditPasswordScreen extends State { interval: _tfaInterval, algorithm: _tfaAlgorithm, isGoogle: _tfaIsGoogle, + type: _tfaType, ), website: _website, attachments: _attachments, @@ -211,6 +215,37 @@ class _EditPasswordScreen extends State { }, body: Column( children: [ + PassyPadding(EnumDropDownButtonFormField( + value: _tfaType, + values: TFAType.values, + decoration: InputDecoration( + labelText: + '2FA ${localizations.type.toLowerCase()}'), + onChanged: (value) { + if (value == null) return; + if (value == _tfaType) return; + setState(() { + _tfaType = value; + switch (value) { + case TFAType.TOTP: + _tfaLength = 6; + _tfaInterval = 30; + break; + case TFAType.HOTP: + _tfaLength = 6; + _tfaInterval = 0; + break; + case TFAType.Steam: + _tfaLength = 5; + _tfaInterval = 30; + break; + } + _tfaAlgorithm = Algorithm.SHA1; + _tfaIsGoogle = true; + _tfaKey = UniqueKey(); + }); + }, + )), PassyPadding(TextFormField( initialValue: _tfaSecret.replaceFirst('=', ''), decoration: InputDecoration( @@ -230,6 +265,8 @@ class _EditPasswordScreen extends State { }, )), PassyPadding(TextFormField( + key: _tfaKey, + enabled: _tfaType != TFAType.Steam, initialValue: _tfaLength.toString(), decoration: InputDecoration( labelText: @@ -241,10 +278,12 @@ class _EditPasswordScreen extends State { setState(() => _tfaLength = int.parse(value)), )), PassyPadding(TextFormField( + key: _tfaKey, + enabled: _tfaType != TFAType.Steam, initialValue: _tfaInterval.toString(), decoration: InputDecoration( labelText: - '2FA ${localizations.interval.toLowerCase()}'), + '2FA ${_tfaType == TFAType.HOTP ? localizations.counter .toLowerCase(): localizations.interval.toLowerCase()}'), inputFormatters: [ FilteringTextInputFormatter.digitsOnly ], @@ -252,36 +291,40 @@ class _EditPasswordScreen extends State { setState(() => _tfaInterval = int.parse(value)), )), PassyPadding(EnumDropDownButtonFormField( + key: _tfaKey, value: _tfaAlgorithm, values: Algorithm.values, decoration: InputDecoration( labelText: '2FA ${localizations.algorithm.toLowerCase()}'), - onChanged: (value) { - if (value != null) { - setState(() => _tfaAlgorithm = value); - } - }, - )), - PassyPadding(DropdownButtonFormField( - items: [ - DropdownMenuItem( - child: Text( - '${localizations.true_} (${localizations.recommended.toLowerCase()})'), - value: true, - ), - DropdownMenuItem( - child: Text(localizations.false_), - value: false, - ), - ], - value: _tfaIsGoogle, - decoration: InputDecoration( - labelText: - '2FA ${localizations.isGoogle.replaceRange(0, 1, localizations.isGoogle[0].toLowerCase())}'), - onChanged: (value) => - setState(() => _tfaIsGoogle = value as bool), + onChanged: _tfaType == TFAType.Steam + ? null + : (value) { + if (value != null) { + setState(() => _tfaAlgorithm = value); + } + }, )), + if (_tfaType != TFAType.Steam) + PassyPadding(DropdownButtonFormField( + items: [ + DropdownMenuItem( + child: Text( + '${localizations.true_} (${localizations.recommended.toLowerCase()})'), + value: true, + ), + DropdownMenuItem( + child: Text(localizations.false_), + value: false, + ), + ], + value: _tfaIsGoogle, + decoration: InputDecoration( + labelText: + '2FA ${localizations.isGoogle.replaceRange(0, 1, localizations.isGoogle[0].toLowerCase())}'), + onChanged: (value) => + setState(() => _tfaIsGoogle = value as bool), + )), ], )) ]), diff --git a/lib/screens/password_screen.dart b/lib/screens/password_screen.dart index 436ca88c..bef2b266 100644 --- a/lib/screens/password_screen.dart +++ b/lib/screens/password_screen.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'package:flutter/material.dart'; -import 'package:otp/otp.dart'; import 'package:passy/common/common.dart'; import 'package:passy/passy_data/custom_field.dart'; @@ -72,16 +71,8 @@ class _PasswordScreen extends State { break; } if (_tfaProgress < _tfaProgressLast) { - setState(() { - _tfaCode = OTP.generateTOTPCodeString( - tfa.secret, - DateTime.now().millisecondsSinceEpoch, - length: tfa.length, - interval: tfa.interval, - algorithm: tfa.algorithm, - isGoogle: tfa.isGoogle, - ); - }); + if (!mounted) return; + setState(() => _tfaCode = tfa.generate()); } _tfaProgressLast = _tfaProgress; await Future.delayed(const Duration(milliseconds: 50)); @@ -150,8 +141,72 @@ class _PasswordScreen extends State { Widget build(BuildContext context) { if (password == null) { password = ModalRoute.of(context)!.settings.arguments as Password; - if (password!.tfa != null) generateTFA = _generateTFA(password!.tfa!); + if (password!.tfa != null) { + if (password!.tfa!.type == TFAType.HOTP) { + setState(() { + _tfaCode = password!.tfa!.generate(); + }); + } else { + generateTFA = _generateTFA(password!.tfa!); + } + } + } + Widget? tfaWidget; + if (password!.tfa != null) { + if (password!.tfa!.type == TFAType.HOTP) { + tfaWidget = Container( + padding: EdgeInsets.only(right: PassyTheme.passyPadding.right), + child: Row( + children: [ + Flexible( + child: PassyPadding(RecordButton( + title: localizations.tfaCode, + value: _tfaCode, + )), + ), + FloatingActionButton( + heroTag: null, + child: const Icon(Icons.refresh_rounded), + tooltip: localizations.refresh, + onPressed: () async { + Navigator.pushNamed(context, SplashScreen.routeName); + password!.tfa!.interval++; + await _account.setPassword(password!); + Navigator.popUntil( + context, + (route) => + route.settings.name == PasswordScreen.routeName); + if (!mounted) return; + setState(() { + _tfaCode = password!.tfa!.generate(); + }); + }), + ], + ), + ); + } else { + tfaWidget = Row( + children: [ + SizedBox( + width: PassyTheme.passyPadding.left * 2, + ), + SizedBox( + child: CircularProgressIndicator( + value: _tfaProgress, + color: _tfaColor, + ), + ), + Flexible( + child: PassyPadding(RecordButton( + title: localizations.tfaCode, + value: _tfaCode, + )), + ), + ], + ); + } } + _account.reloadFavoritesSync(); isFavorite = _account.favoritePasswords[password!.key]?.status == EntryStatus.alive; @@ -209,26 +264,7 @@ class _PasswordScreen extends State { obscureValue: true, isPassword: true, )), - if (password!.tfa != null) - Row( - children: [ - SizedBox( - width: PassyTheme.passyPadding.left * 2, - ), - SizedBox( - child: CircularProgressIndicator( - value: _tfaProgress, - color: _tfaColor, - ), - ), - Flexible( - child: PassyPadding(RecordButton( - title: localizations.tfaCode, - value: _tfaCode, - )), - ), - ], - ), + if (tfaWidget != null) tfaWidget, if (password!.website != '') Row( children: [ From 55d957295526835eedd61d1a3f50fc9b10da6a0d Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 9 Dec 2023 17:07:27 +0000 Subject: [PATCH 008/131] Create build_cli.sh --- build_cli.sh | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100755 build_cli.sh diff --git a/build_cli.sh b/build_cli.sh new file mode 100755 index 00000000..6cefb3d9 --- /dev/null +++ b/build_cli.sh @@ -0,0 +1,31 @@ +#! /bin/bash +cd $(dirname $0) + +user_interrupt() { + exit +} + +trap user_interrupt SIGINT +trap user_interrupt SIGTSTP + +if [ ! -d ./build ]; then mkdir ./build; fi +if [ ! -d ./build/cli ]; then mkdir ./build/cli; fi +if [ -d ./build/cli/latest ]; then rm -rf ./build/cli/latest; fi +mkdir ./build/cli/latest +mkdir ./build/cli/latest/lib +flutter pub get +echo 'Building Passy CLI...' +dart compile exe ./lib/passy_cli/bin/passy_cli.dart -o ./build/cli/latest/passy_cli +cp ./lib/passy_cli/bin/passy_cli_native_messaging.sh ./build/cli/latest +cp ./lib/passy_cli/passy_cli_native_messaging.json ./build/cli/latest +cd ./build/cli +echo 'Cloning Argon2...' +if [ -d ./phc-winner-argon2 ]; then rm -rf ./phc-winner-argon2; fi +git clone https://github.com/P-H-C/phc-winner-argon2 +cd phc-winner-argon2 +make +cp libargon2.so.1 ../latest/lib/libargon2.so +cd .. +echo 'All done.' +echo "Passy CLI: $PWD/latest" + From 4a813c8b87960ff5bf6f7fe9a5fbebab7c5ad353 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 9 Dec 2023 18:03:34 +0000 Subject: [PATCH 009/131] Create armv7.sh --- .github/workflows/armv7.sh | 61 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100755 .github/workflows/armv7.sh diff --git a/.github/workflows/armv7.sh b/.github/workflows/armv7.sh new file mode 100755 index 00000000..e030207c --- /dev/null +++ b/.github/workflows/armv7.sh @@ -0,0 +1,61 @@ +#! /bin/bash +cd /Passy + +echo "====================================================" +echo "Install dependencies" +echo "====================================================" + +apt-get update +apt-get -y install clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev binutils coreutils desktop-file-utils fakeroot fuse libgdk-pixbuf2.0-dev patchelf python3-pip python3-setuptools squashfs-tools strace util-linux zsync git file unzip zip wget curl libc6 + +echo "====================================================" +echo "Configure" +echo "====================================================" + +export PATH="$PATH:$PWD/submodules/flutter/bin" +git config --global --add safe.directory /Passy +git config --global --add safe.directory /Passy/submodules/flutter +git submodule init +git submodule update + +echo "====================================================" +echo "Install dart" +echo "====================================================" + +mkdir submodules/flutter/bin/cache +flutter doctor +export LAST_PWD=$PWD +cd submodules/flutter/bin/cache +rm -rf dart-sdk +wget https://storage.googleapis.com/dart-archive/channels/stable/release/3.1.0/sdk/dartsdk-linux-arm-release.zip +unzip dartsdk-linux-arm-release.zip +rm dartsdk-linux-arm-release.zip +cd $LAST_PWD + +echo "====================================================" +echo "Install flutter" +echo "====================================================" + +flutter clean +flutter doctor + +echo "====================================================" +echo "Configure flutter" +echo "====================================================" + +flutter config --no-analytics + +echo "====================================================" +echo "Build Passy CLI" +echo "====================================================" + +bash build_cli.sh + +echo "====================================================" +echo "Prepare releases" +echo "====================================================" + +cd /passy-build +mkdir cli +cp -r /Passy/build/cli/latest/. cli + From 5671f45cd6dab2bcf283059b513660d2b42cebd3 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 9 Dec 2023 18:06:12 +0000 Subject: [PATCH 010/131] Create build-linux-armv7-ci.yml --- .github/workflows/build-linux-armv7-ci.yml | 48 ++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 .github/workflows/build-linux-armv7-ci.yml diff --git a/.github/workflows/build-linux-armv7-ci.yml b/.github/workflows/build-linux-armv7-ci.yml new file mode 100644 index 00000000..916805cc --- /dev/null +++ b/.github/workflows/build-linux-armv7-ci.yml @@ -0,0 +1,48 @@ +name: Build Passy for all platforms +on: + workflow_dispatch: + pull_request: + types: [ opened, synchronize, reopened, review_requested ] + branches: + - main + - dev +jobs: + build_linux_armv7: + name: Build Passy CLI for Linux ARMv7 + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Set up QEMU + uses: docker/setup-qemu-action@v2.1.0 + - name: Create build directory + shell: bash + run: | + cd .. + mkdir build + mkdir bin + cd bin + wget https://raw.githubusercontent.com/GlitterWare/Passy/4a813c8b87960ff5bf6f7fe9a5fbebab7c5ad353/.github/workflows/armv7.sh + chmod +x armv7.sh + - name: Install dependencies + shell: bash + run: sudo apt install -y qemu binfmt-support qemu-user-static qemu-system-arm + - name: Build for ARMv7 + uses: tj-actions/docker-run@v2 + with: + image: ubuntu:latest + name: ubuntu + options: --rm --platform linux/arm/v7 -v $PWD:/Passy -v /home/runner/work/Passy/build:/passy-build -v /home/runner/work/Passy/bin:/passy-bin + args: /passy-bin/armv7.sh + - name: Archive Passy CLI build + uses: thedoctor0/zip-release@0.7.1 + with: + type: zip + directory: ../build + filename: Passy-CLI-Linux-ARMv7.zip + path: cli + - name: Upload Passy CLI build + uses: actions/upload-artifact@v3 + with: + name: linux-bundle + path: /home/runner/work/Passy/build/cli/Passy-CLI-Linux-ARMv7.zip From ff1666e7961c9d7e1eb33a1e8e93e8351bf7bffc Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 9 Dec 2023 18:12:27 +0000 Subject: [PATCH 011/131] Update build-linux-armv7-ci.yml --- .github/workflows/build-linux-armv7-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-linux-armv7-ci.yml b/.github/workflows/build-linux-armv7-ci.yml index 916805cc..ba94c41b 100644 --- a/.github/workflows/build-linux-armv7-ci.yml +++ b/.github/workflows/build-linux-armv7-ci.yml @@ -1,4 +1,4 @@ -name: Build Passy for all platforms +name: Build Passy for Linux ARMv7 on: workflow_dispatch: pull_request: From d6df5d194696b8e69eb9c134301a46ca2a600b89 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 9 Dec 2023 18:30:26 +0000 Subject: [PATCH 012/131] Update build-linux-armv7-ci.yml --- .github/workflows/build-linux-armv7-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-linux-armv7-ci.yml b/.github/workflows/build-linux-armv7-ci.yml index ba94c41b..60ebfce3 100644 --- a/.github/workflows/build-linux-armv7-ci.yml +++ b/.github/workflows/build-linux-armv7-ci.yml @@ -45,4 +45,4 @@ jobs: uses: actions/upload-artifact@v3 with: name: linux-bundle - path: /home/runner/work/Passy/build/cli/Passy-CLI-Linux-ARMv7.zip + path: /home/runner/work/Passy/build/Passy-CLI-Linux-ARMv7.zip From d3fb60f2cea2f66d1bd788dec023706fc1e675c2 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 9 Dec 2023 20:53:41 +0000 Subject: [PATCH 013/131] GitHub workflows updated --- .github/workflows/build-all-ci.yml | 73 +++++++++++++++------- .github/workflows/build-linux-armv7-ci.yml | 3 +- .github/workflows/build-windows-ci.yml | 12 ++-- 3 files changed, 58 insertions(+), 30 deletions(-) diff --git a/.github/workflows/build-all-ci.yml b/.github/workflows/build-all-ci.yml index 4d3a2897..f5be0afa 100644 --- a/.github/workflows/build-all-ci.yml +++ b/.github/workflows/build-all-ci.yml @@ -7,8 +7,8 @@ on: - main - dev jobs: - build_linux: - name: Build Passy for Android and Linux + build_android: + name: Build Passy for Android runs-on: ubuntu-latest steps: - name: Checkout repository @@ -19,22 +19,56 @@ jobs: sudo apt-get update sudo apt-get -y install clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev binutils coreutils desktop-file-utils fakeroot fuse libgdk-pixbuf2.0-dev patchelf python3-pip python3-setuptools squashfs-tools strace util-linux zsync - name: Install flutter - uses: subosito/flutter-action@v2 + shell: bash + run: | + git submodule init + git submodule update + echo "$PWD/submodules/flutter/bin" >> $GITHUB_PATH + - name: Configure flutter + shell: bash + run: flutter config --no-analytics + - name: Build Android + shell: bash + run: bash build_android.sh + - name: Prepare releases + shell: bash + run: | + cd .. + mkdir build + cp Passy/build/app/outputs/flutter-apk/app-release.apk build/Passy-Android.apk + - name: Upload Android build + uses: actions/upload-artifact@v3 with: - flutter-version: '3.10.1' - channel: 'stable' + name: android + path: /home/runner/work/Passy/build/Passy-Android.apk + build_linux: + name: Build Passy for Linux + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Install dependencies + shell: bash + run: | + sudo apt-get update + sudo apt-get -y install clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev binutils coreutils desktop-file-utils fakeroot fuse libgdk-pixbuf2.0-dev patchelf python3-pip python3-setuptools squashfs-tools strace util-linux zsync + - name: Install flutter + shell: bash + run: | + git submodule init + git submodule update + echo "$PWD/submodules/flutter/bin" >> $GITHUB_PATH - name: Configure flutter shell: bash run: flutter config --no-analytics - name: Build with updates popup shell: bash - run: bash build_all_with_updates_popup.sh + run: echo "echo 3;echo y;echo;" | bash | bash build_all.sh - name: Prepare releases shell: bash run: | cd .. mkdir -p build/linux-bundle/Passy - cp Passy/build/app/outputs/flutter-apk/app-release.apk build/Passy-Android.apk cp -r Passy/build/linux/x64/release/bundle/. build/linux-bundle/Passy cp Passy/build/appimage/Passy-Latest-x86_64.AppImage build chmod +x build/linux-bundle/Passy/passy @@ -53,11 +87,6 @@ jobs: directory: ../build filename: Passy-Linux-AppImage.zip path: Passy-Latest-x86_64.AppImage - - name: Upload Android build - uses: actions/upload-artifact@v3 - with: - name: android - path: /home/runner/work/Passy/build/Passy-Android.apk - name: Upload Linux bundle build uses: actions/upload-artifact@v3 with: @@ -70,14 +99,13 @@ jobs: path: /home/runner/work/Passy/build/Passy-Linux-AppImage.zip - name: Build without updates popup shell: bash - run: bash build_all_without_updates_popup.sh + run: echo "echo 3;echo n;echo;" | bash | bash build_all.sh - name: Prepare releases shell: bash run: | cd .. rm -rf build/* mkdir -p build/linux-bundle/Passy - cp Passy/build/app/outputs/flutter-apk/app-release.apk build/Passy-Android.apk cp -r Passy/build/linux/x64/release/bundle/. build/linux-bundle/Passy cp Passy/build/appimage/Passy-Latest-x86_64.AppImage build chmod +x build/linux-bundle/Passy/passy @@ -96,11 +124,6 @@ jobs: directory: ../build filename: Passy-Linux-AppImage.zip path: Passy-Latest-x86_64.AppImage - - name: Upload Android no updates popup build - uses: actions/upload-artifact@v3 - with: - name: android-no-updates-popup - path: /home/runner/work/Passy/build/Passy-Android.apk - name: Upload Linux bundle no updates popup build uses: actions/upload-artifact@v3 with: @@ -118,16 +141,18 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 - name: Install flutter - uses: subosito/flutter-action@v2 - with: - flutter-version: '3.10.1' - channel: 'stable' + shell: bash + run: | + git submodule init + git submodule update + echo "$PWD/submodules/flutter/bin" >> $GITHUB_PATH + echo "$PWD/submodules/flutter/bin/cache/dart-sdk/bin" >> $GITHUB_PATH - name: Configure flutter shell: bash run: flutter config --no-analytics - name: Build Passy for Windows shell: bash - run: ./build_windows_with_updates_popup.bat + run: flutter --no-version-check --suppress-analytics build windows - name: Prepare releases shell: bash run: | diff --git a/.github/workflows/build-linux-armv7-ci.yml b/.github/workflows/build-linux-armv7-ci.yml index 60ebfce3..301548c9 100644 --- a/.github/workflows/build-linux-armv7-ci.yml +++ b/.github/workflows/build-linux-armv7-ci.yml @@ -6,6 +6,7 @@ on: branches: - main - dev + jobs: build_linux_armv7: name: Build Passy CLI for Linux ARMv7 @@ -44,5 +45,5 @@ jobs: - name: Upload Passy CLI build uses: actions/upload-artifact@v3 with: - name: linux-bundle + name: passy-cli-linux-armv7 path: /home/runner/work/Passy/build/Passy-CLI-Linux-ARMv7.zip diff --git a/.github/workflows/build-windows-ci.yml b/.github/workflows/build-windows-ci.yml index 6bae8b50..7cc95a3b 100644 --- a/.github/workflows/build-windows-ci.yml +++ b/.github/workflows/build-windows-ci.yml @@ -9,16 +9,18 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 - name: Install flutter - uses: subosito/flutter-action@v2 - with: - flutter-version: '3.10.1' - channel: 'stable' + shell: bash + run: | + git submodule init + git submodule update + echo "$PWD/submodules/flutter/bin" >> $GITHUB_PATH + echo "$PWD/submodules/flutter/bin/cache/dart-sdk/bin" >> $GITHUB_PATH - name: Configure flutter shell: bash run: flutter config --no-analytics - name: Build Passy for Windows shell: bash - run: ./build_windows_with_updates_popup.bat + run: flutter --no-version-check --suppress-analytics build windows - name: Prepare releases shell: bash run: | From ddb2743ec846adbd91be77fe386adc33026daa7f Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 9 Dec 2023 20:54:16 +0000 Subject: [PATCH 014/131] Update build-linux-armv7-ci.yml --- .github/workflows/build-linux-armv7-ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build-linux-armv7-ci.yml b/.github/workflows/build-linux-armv7-ci.yml index 301548c9..6b93df46 100644 --- a/.github/workflows/build-linux-armv7-ci.yml +++ b/.github/workflows/build-linux-armv7-ci.yml @@ -6,7 +6,6 @@ on: branches: - main - dev - jobs: build_linux_armv7: name: Build Passy CLI for Linux ARMv7 From 8fbb562c7ed05db8cf34d719fb7b007af5660d77 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 9 Dec 2023 21:00:11 +0000 Subject: [PATCH 015/131] Update passy_cli.dart --- lib/passy_cli/bin/passy_cli.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/passy_cli/bin/passy_cli.dart b/lib/passy_cli/bin/passy_cli.dart index 8087c992..aa38ca98 100644 --- a/lib/passy_cli/bin/passy_cli.dart +++ b/lib/passy_cli/bin/passy_cli.dart @@ -1641,7 +1641,7 @@ Future executeCommand(List command, loadedModules.remove('2d0d1_$username'); return { 'error': { - 'type': 'Faileda to login', + 'type': 'Failed to login', }, }; } @@ -1659,7 +1659,7 @@ Future executeCommand(List command, } return { 'error': { - 'type': 'Failedb to login', + 'type': 'Failed to login', 'local': creds.value.passwordHash, 'remote': sha512 .convert(base64Decode(password)) @@ -1675,14 +1675,14 @@ Future executeCommand(List command, } catch (e) { return { 'error': { - 'type': 'Failedc to login', + 'type': 'Failed to login', }, }; } String result = await _login(username, passwordBytes); if (result != 'true') { return { - 'error': {'type': 'Failedd to login'}, + 'error': {'type': 'Failed to login'}, 'hash': sha512.convert(passwordBytes).toString(), }; } From 8e676d08ea1ee8d3aeb858b8f48f80cfe17a4ecc Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 9 Dec 2023 21:01:51 +0000 Subject: [PATCH 016/131] Update tfa.dart --- lib/passy_data/tfa.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/passy_data/tfa.dart b/lib/passy_data/tfa.dart index 1b6b7cf8..aa741c8b 100644 --- a/lib/passy_data/tfa.dart +++ b/lib/passy_data/tfa.dart @@ -63,7 +63,7 @@ class TFA with JsonConvertable, CSVConvertable { interval = json['interval'], algorithm = algorithmFromName(json['algorithm']) ?? Algorithm.SHA1, isGoogle = json['isGoogle'] ?? true, - type = tfaTypeFromName(json['type']) ?? TFAType.TOTP; + type = tfaTypeFromName(json['type'] ?? TFAType.TOTP.name) ?? TFAType.TOTP; factory TFA.fromCSV(List csv) { if (csv.length == 5) csv.add(TFAType.TOTP.name); From 682d8069d5113900c1f2c5556edf36abcac14e5d Mon Sep 17 00:00:00 2001 From: sls1005 <90055573+sls1005@users.noreply.github.com> Date: Thu, 28 Dec 2023 16:31:02 +0800 Subject: [PATCH 017/131] Translate to Traditional Chinese --- lib/l10n/app_zh_Hant.arb | 341 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 341 insertions(+) create mode 100644 lib/l10n/app_zh_Hant.arb diff --git a/lib/l10n/app_zh_Hant.arb b/lib/l10n/app_zh_Hant.arb new file mode 100644 index 00000000..ad4d5a1f --- /dev/null +++ b/lib/l10n/app_zh_Hant.arb @@ -0,0 +1,341 @@ +{ + "translatorsReadme": "READ THIS, DO NOT TRANSLATE THIS LINE: For those who want to help localize Passy, please carefully read https://github.com/GlitterWare/Passy/blob/dev/LOCALIZATION.md before translating this file", + "usernameIsEmpty": "用戶名爲空", + "usernameShorterThan2Letters": "用戶名稱短於兩個字", + "usernameAlreadyInUse": "用戶名已被使用", + "passwordIsEmpty": "密碼爲空", + "passwordsDoNotMatch": "密碼不符", + "couldNotAddAccount": "無法添加帳號", + "details": "詳請", + "addAccount": "新增帳號", + "createLocalAccount": "建立本地帳號", + "username": "用戶名稱", + "password": "密碼", + "confirmPassword": "確認密碼", + "automaticBackup": "自動備份", + "interval": "間隔", + "changeBackupPath": "變更備份路徑", + "backupPathColon": "備份路徑:", + "restorePassyBackup": "復原 Passy 備份", + "backupAndRestore": "備份與還原", + "backup": "備份", + "restore": "還原", + "incorrectPassword": "密碼錯誤", + "couldNotAuthenticate": "無法驗證", + "biometricAuthentication": "生物辨識登入", + "accountPassword": "帳號密碼", + "changePassword": "變更密碼", + "youAreChangingPasswordFor": "正在替以下用戶變更密碼:", + "@youAreChangingPasswordFor": { "description": "This message is followed by a username in the Change password screen" }, + "currentPassword": "當前密碼", + "newPassword": "新密碼", + "changeUsername": "變更用戶名稱", + "newUsername": "新名稱", + "currentUsernameIs": "當前用戶名稱爲", + "@currentUsernameIs": { "description": "This message is followed by a username in the Change username screen" }, + "typeInTheNewUsername": "輸入新名稱", + "couldNotImportAccount": "無法匯入帳號", + "passyImport": "Passy 匯入", + "confirmImport1": "欲匯入的帳號若已存在,則", + "confirmImport2Highlighted": "其當前資料將遺失", + "confirmImport3": "而被匯入的資料所取代", + "@confirmImport1": { "description": "confirmImport1, confirmImport2Highlighted and confirmImport3 make up a single message used in the Confirm import screen. confirmImport2Highlighted is shown in a more noticeable color to increase user attention." }, + "@confirmImport2Highlighted": { "description": "confirmImport1, confirmImport2Highlighted and confirmImport3 make up a single message used in the Confirm import screen. confirmImport2Highlighted is shown in a more noticeable color to increase user attention." }, + "@confirmImport3": { "description": "confirmImport1, confirmImport2Highlighted and confirmImport3 make up a single message used in the Confirm import screen. confirmImport2Highlighted is shown in a more noticeable color to increase user attention." }, + "enterAccountPasswordToImport": "輸入帳號密碼以匯入", + "enterPassword": "輸入密碼", + "passyRestore": "Passy 還原", + "confirmRestore1": "欲還原的帳號若已存在,則", + "confirmRestore2Highlighted": "其當前資料將遺失", + "confirmRestore3": "而被先前備份的資料所取代", + "@confirmRestore1": { "description": "confirmRestore1, confirmRestore2Highlighted and confirmRestore3 make up a single message used in the Confirm restore screen. confirmImport2Highlighted is shown in a more noticeable color to increase user attention." }, + "@confirmRestore2Highlighted": { "description": "confirmRestore1, confirmRestore2Highlighted and confirmRestore3 make up a single message used in the Confirm restore screen. confirmImport2Highlighted is shown in a more noticeable color to increase user attention." }, + "@confirmRestore3": { "description": "confirmRestore1, confirmRestore2Highlighted and confirmRestore3 make up a single message used in the Confirm restore screen. confirmImport2Highlighted is shown in a more noticeable color to increase user attention." }, + "enterAccountPasswordToRestore": "輸入帳號密碼以還原", + "couldNotRestoreAccount": "無法還原帳號", + "connect": "連線", + "connect1": "必須和主機處於", + "connect2Highlighted": "相同網路", + "connect3": "以連接", + "connect4": "輸入", + "connect5Highlighted": "QR 碼下方顯示的主機位址", + "@connect1": { "description": "connect1, connect2Highlighted, connect3, connect4 and connect5Highlighted make up a single message used in the Connect screen. The keys ending with 'Highlighted' are shown in a more noticeable color to increase user attention" }, + "@connect2Highlighted": { "description": "connect1, connect2Highlighted, connect3, connect4 and connect5Highlighted make up a single message used in the Connect screen. The keys ending with 'Highlighted' are shown in a more noticeable color to increase user attention" }, + "@connect3": { "description": "connect1, connect2Highlighted, connect3, connect4 and connect5Highlighted make up a single message used in the Connect screen. The keys ending with 'Highlighted' are shown in a more noticeable color to increase user attention" }, + "@connect4": { "description": "connect1, connect2Highlighted, connect3, connect4 and connect5Highlighted make up a single message used in the Connect screen. The keys ending with 'Highlighted' are shown in a more noticeable color to increase user attention" }, + "@connect5Highlighted": { "description": "connect1, connect2Highlighted, connect3, connect4 and connect5Highlighted make up a single message used in the Connect screen. The keys ending with 'Highlighted' are shown in a more noticeable color to increase user attention" }, + "hostAddress": "主機位址", + "credentials": "驗證", + "customField": "自訂欄", + "title": "標題", + "type": "類型", + "false_": "僞", + "true_": "眞", + "obscured": "遮蓋", + "multiline": "多行", + "idCard": "ID 卡", + "nickname": "暱稱", + "idNumber": "識別碼", + "name": "名稱", + "country": "國家", + "additionalInfo": "額外資訊", + "identity": "身份", + "firstName": "名", + "middleName": "中間名", + "lastName": "姓", + "gender": "性別", + "email": "電子郵件", + "phoneNumber": "電話號碼", + "firstAddresssLine": "第一行地址", + "secondAddressLine": "第二行地址", + "zipCode": "郵遞區號", + "city": "城市", + "note": "筆記", + "generate": "生成", + "secret": "祕密", + "length": "長度", + "algorithm": "演算法", + "isGoogle": "是 Google", + "website": "網站", + "paymentCard": "支付卡", + "cardNumber": "卡號", + "cardHolderName": "持卡者名稱", + "expirationDate": "到期日", + "recommended": "推薦", + "exportAndImport": "匯入與匯出", + "export": "匯出", + "import": "匯入", + "confirmExport": "確認匯出", + "confirmExport1": "若匯出,帳號資料將爲", + "confirmExport2Highlighted": "未加密的狀態", + "confirmExport3": "倘若匯出的資料落入壞人之手,您儲存於 Passy 的資料恐會全部洩露", + "@confirmExport1": { "description": "confirmExport1, confirmExport2Highlighted and confirmExport3 make up a single message used in the Confirm export screen. confirmExport2Highlighted is shown in a more noticeable color to increase user attention." }, + "@confirmExport2Highlighted": { "description": "confirmExport1, confirmExport2Highlighted and confirmExport3 make up a single message used in the Confirm export screen. confirmExport2Highlighted is shown in a more noticeable color to increase user attention." }, + "@confirmExport3": { "description": "confirmExport1, confirmExport2Highlighted and confirmExport3 make up a single message used in the Confirm export screen. confirmExport2Highlighted is shown in a more noticeable color to increase user attention." }, + "cancel": "取消", + "confirm": "確認", + "exportPassy": "匯出 Passy", + "exportSaved": "匯出的資料已儲存", + "accessDeniedTryAnotherFolder": "存取遭拒。請嘗試其他資料夾", + "couldNotExport": "無法匯出", + "passyExport": "Passy 匯出", + "settings": "設定", + "enableAutofill": "啓用自動填入", + "about": "關於", + "updatesPopupEnabled": "彈出更新已啓用", + "removeIDCard": "移除 ID 卡", + "idCardsCanOnlyBeRestoredFromABackup": "ID 卡僅能從備份中還原", + "remove": "移除", + "idCards": "ID 卡", + "noIDCards": "無 ID 卡", + "identities": "身份", + "noIdentities": "無身份", + "removeIdentity": "移除身份", + "identitiesCanOnlyBeRestoredFromABackup": "身份僅能從備份中還原", + "importFromPassy": "從 Passy 匯入", + "log": "紀錄", + "copy": "複製", + "submitAnIssue": "回報問題", + "couldNotLogin": "無法登入", + "authenticate": "驗證", + "logIn": "登入", + "logOut": "登出", + "stay": "停留", + "areYouSureYouWantToLogOutQuestion": "您確定要登出嗎?", + "scanQRCode": "掃描 QR 碼", + "canNotScanQuestion": "無法掃描?", + "search": "搜尋", + "synchronize": "同步", + "host": "主機", + "passwords": "密碼", + "paymentCards": "支付卡", + "notes": "筆記", + "noAccounts": "無帳號", + "removeNote": "移除筆記", + "notesCanOnlyBeRestoredFromABackup": "筆記僅能從備份中還原", + "noNotes": "無筆記", + "removePassword": "移除密碼", + "passwordsCanOnlyBeRestoredFromABackup": "密碼僅能從備份中還原", + "noPasswords": "無密碼", + "removePaymentCard": "移除支付卡", + "paymentCardsCanOnlyBeRestoredFromABackup": "支付卡僅能從備份中還原", + "noPaymentCards": "無支付卡", + "tfaCode": "2FA 碼", + "visit": "造訪", + "removeAccount": "移除帳號", + "confirmRemoveAccount1": "輸入該帳號的用戶名", + "confirmRemoveAccount2": "以移除之", + "confirmRemoveAccount3": "此行動", + "confirmRemoveAccount4Highlighted": "不可還原", + "@confirmRemoveAccount1": { "description": "confirmRemoveAccount1, confirmRemoveAccount2, confirmRemoveAccount3 and confirmRemoveAccount4Highlighted make up a single message used in the Remove account screen. confirmRemoveAccount1 is followed by the account username. The keys ending with 'Highlighted' are shown in a more noticeable color to increase user attention" }, + "@confirmRemoveAccount2": { "description": "confirmRemoveAccount1, confirmRemoveAccount2, confirmRemoveAccount3 and confirmRemoveAccount4Highlighted make up a single message used in the Remove account screen. confirmRemoveAccount1 is followed by the account username. The keys ending with 'Highlighted' are shown in a more noticeable color to increase user attention" }, + "@confirmRemoveAccount3": { "description": "confirmRemoveAccount1, confirmRemoveAccount2, confirmRemoveAccount3 and confirmRemoveAccount4Highlighted make up a single message used in the Remove account screen. confirmRemoveAccount1 is followed by the account username. The keys ending with 'Highlighted' are shown in a more noticeable color to increase user attention" }, + "@confirmRemoveAccount4Highlighted": { "description": "confirmRemoveAccount1, confirmRemoveAccount2, confirmRemoveAccount3 and confirmRemoveAccount4Highlighted make up a single message used in the Remove account screen. confirmRemoveAccount1 is followed by the account username. The keys ending with 'Highlighted' are shown in a more noticeable color to increase user attention" }, + "confirmUsername": "確認用戶名", + "usernamesDoNotMatch": "用戶名不符", + "security": "安全性", + "protectScreen": "保護熒幕", + "automaticScreenLock": "自動熒幕鎖", + "donate": "捐款", + "accountSetup": "帳號設定", + "unlock": "解鎖", + "addCustomField": "添加自訂欄", + "selectDate": "選擇日期", + "add": "新增", + "edit": "編輯", + "save": "儲存", + "addEntry": "新增項目", + "copied": "已複製", + "numbers": "數字", + "symbols": "符號", + "done": "完成", + "allEntries": "所有項目", + "searchAllEntries": "搜尋全部項目", + "favorites": "最愛", + "noFavorites": "沒有最愛", + "noFavorites1": "您可透過按壓", + "noFavorites2": "任一項目頂端列的按鈕來新增最愛", + "@noFavorites1": { "description": "noFavorites, noFavorites1 and noFavorites2 make up a single message used in the Favorites search screen. noFavorites1 is followed by a star icon." }, + "@noFavorites2": { "description": "noFavorites, noFavorites1 and noFavorites2 make up a single message used in the Favorites search screen. noFavorites1 is followed by a star icon." }, + "addToFavorites": "添加至最愛", + "removeFromFavorites": "從最愛移除", + "addedToFavorites": "已添加至最愛", + "removedFromFavorites": "已從最愛移除", + "noEntries": "無項目", + "noSearchResults": "無搜尋結果", + "settingUpSynchronization": "設定同步", + "shareEntry": "分享項目", + "dateOfIssue": "發行日", + "notSpecified": "未指定", + "male": "男性", + "female": "女性", + "other": "其他", + "csvImport": "CSV 匯入", + "importPasswords": "匯入密碼", + "importPaymentCards": "匯入支付卡", + "importNotes": "匯入筆記", + "importIDCards": "匯入 ID 卡", + "importIdentities": "匯入身份", + "noCSVDataFound": "未找到 CSV 資料", + "csvImportMessage1": "選擇 CSV 欄位數值以對應項目變數", + "csvImportMessage2": "此模板將被用於所有 CSV 項目", + "@csvImportMessage1": { "description": "csvImportMessage1 and csvImportMessage2 make up a single message used in the CSV import entries screen" }, + "@csvImportMessage2": { "description": "csvImportMessage1 and csvImportMessage2 make up a single message used in the CSV import entries screen" }, + "noConnectorFound": "沒有連接者", + "pleaseDownloadAndInstallTheMainPassyApplication": "請下載並安裝 Passy 主應用程式", + "download": "下載", + "report": "回報問題", + "failedToLoad": "未能成功載入", + "autofill": "自動填入", + "pleaseOpenTheDesktopApplicationAndAddAnAccount": "請開啓桌面版應用程式以新增帳號", + "passyBrowserExtension": "Passy 瀏覽器外掛程式", + "extensionSettingsNote": "欲使用完整功能,包括同步、自動備份和項目分享,請至 Passy 桌面版應用程式", + "requestAFeature": "功能請求", + "privacyPolicy": "隱私權政策", + "addPassword": "新增密碼", + "addPaymentCard": "新增支付卡", + "addNote": "添加筆記", + "addIDCard": "新增 ID 卡", + "addIdentity": "添加身份", + "newVersionAvailable": "有新版可用", + "unableToConnectBrowserExtension": "無法連結至瀏覽器外掛程式", + "somethingWentWrong": "出了些差錯", + "twoFactorAuthentication": "兩步驟驗證", + "advancedSettings": "進階設定", + "enableMarkdown": "啓用 Markdown", + "markdownPreview": "Markdown 預覽", + "keyDerivation": "密鑰派生", + "keyDerivationType": "密鑰派生類型", + "keyDerivationDescription": "密鑰派生能讓您的密碼變得難以猜測,從而顯著地增強您帳號的安全性。", + "keyDerivationWarning1": "非常建議使用密鑰派生。", + "keyDerivationWarning2": "您的所有裝置必須具有相同的密鑰派生類型以使其能正常同步。", + "keyDerivationWarning3": "倘若您未在其他裝置上見到此選單,請確保您所有的裝置皆更新至 Passy 最新版。", + "@keyDerivationWarning1": { "description": "keyDerivationWarning1, keyDerivationWarning2 and keyDerivationWarning3 make up a single message used in the key derivation screen" }, + "@keyDerivationWarning2": { "description": "keyDerivationWarning1, keyDerivationWarning2 and keyDerivationWarning3 make up a single message used in the key derivation screen" }, + "@keyDerivationWarning3": { "description": "keyDerivationWarning1, keyDerivationWarning2 and keyDerivationWarning3 make up a single message used in the key derivation screen" }, + "youAreChangingKeyDerivationFor": "正在替以下用戶變更密鑰派生:", + "@youAreChangingKeyDerivationFor": { "description": "This message is followed by a username in the key derivation screen" }, + "synchronizationServers": "同步伺服器", + "serverSetup": "伺服器設定", + "removeServers": "移除伺服器", + "synchronizationInterval": "同步間隔", + "reset": "重設", + "synchronizationServerSetup": "同步伺服器設定", + "connectToServer": "連線至伺服器", + "none": "無", + "syncServerSetupInfo": "您僅需一臺伺服器即可存取您所有帳號。", + "chooseHostAddressAndPort": "選擇主機位址與連接埠", + "port": "連接埠", + "installServer": "安裝伺服器", + "doubleClickMessage": "使用您的檔案管理器,導引至選定的安裝目錄並雙擊按鍵", + "doubleClickMessage1": "倘若雙擊使其開啓於文字編輯器中,您可能必須右鍵點擊該檔案並選擇「當作程式執行(Run as a Program)」。", + "@installMessage": { "description": "This message is followed by an executable file name that the user has to double-click" }, + "startServer": "啓動伺服器", + "testConnection": "測試連接", + "optional": "可選", + "addServerToAutostart": "將伺服器添加至 autostart", + "onClientDevices": "於用戶端裝置", + "@onClientDevices": { "description": "This message is followed by a list of actions that have to be performed on client device(s)" }, + "serverInstalled": "伺服器已安裝", + "couldNotInstallServer": "無法安裝伺服器", + "hostAddressIsEmpty": "主機位址爲空", + "invalidPortSpecified": "所指定的埠無效", + "couldNotConnectToServer": "無法連線至伺服器", + "intervalIsLessThan": "間隔少於 ", + "@intervalIsLessThan": { "description": "This message is followed by a time interval and units for that interval" }, + "seconds": "秒", + "minutes": "分", + "hours": "小時", + "days": "日", + "weeks": "週", + "months": "月", + "years": "年", + "connectToSynchronizationServer": "連線至同步伺服器", + "nicknameCanNotBeEmpty": "暱稱不可爲空", + "nicknameAlreadyInUse": "暱稱已被使用", + "lastSynchronization": "最後同步", + "connecting": "連線中", + "connectionEstablished": "連線已建立", + "syncError": "同步錯誤", + "invalidAddressFormat": "位址形式無效", + "connectionFailed": "連線失敗", + "synchronizationComplete": "已完成同步", + "entriesAdded": "項目已添加", + "entriesRemoved": "項目已移除", + "csvExport": "CSV 匯出", + "kdbxImport": "KDBX 匯入", + "kdbxExport": "KDBX 匯出", + "imported": "已匯入", + "backupYourAccountBeforeMakingChangesToTheseSettings": "請於變更這些設定前先備份您的帳號。", + "backupYourAccountBeforeProceeding": "在執行下一步之前備份您的帳號", + "synchronizationLogs": "同步紀錄", + "noRecentActivity": "無近期活動。", + "yourAccountWillBeStoredLocally1": "您的帳號將本地保存於", + "yourAccountWillBeStoredLocally2Highlighted": "此裝置。", + "@yourAccountWillBeStoredLocally1": { "description": "yourAccountWillBeStoredLocally1 and yourAccountWillBeStoredLocally2Highlighted make up a single message used in the add account screen. yourAccountWillBeStoredLocally2Highlighted is shown in a more noticeable color to increase user attention." }, + "@yourAccountWillBeStoredLocally2Highlighted": { "description": "yourAccountWillBeStoredLocally1 and yourAccountWillBeStoredLocally2Highlighted make up a single message used in the add account screen. yourAccountWillBeStoredLocally2Highlighted is shown in a more noticeable color to increase user attention." }, + "files": "檔案", + "noFiles": "無檔案", + "fileType": "檔案類型", + "photo": "相片", + "file": "檔案", + "folder": "資料夾", + "unknown": "未知", + "plainText": "純文字", + "markdown": "Markdown", + "folderName": "資料夾名稱", + "fileSize": "檔案大小", + "filePreview": "檔案預覽", + "failedToDisplayFile": "未能成功顯示檔案", + "addFile": "新增檔案", + "failedToAddFile": "未能成功添加檔案", + "removeFile": "移除檔案", + "filesCanOnlyBeRestoredFromABackup": "檔案僅能從備份中還原", + "attachFile": "附加檔案", + "attachments": "附件", + "synchronizedFiles": "已同步的檔案", + "scrollOrPinchToZoom": "捲動或捏合以縮放", + "missingFile": "遺漏檔案", + "rename": "重新命名", + "fileName": "檔案名稱" +} From bae6be58df2b59bd18e563240b97dbc8d9a9594a Mon Sep 17 00:00:00 2001 From: sls1005 <90055573+sls1005@users.noreply.github.com> Date: Thu, 28 Dec 2023 16:34:20 +0800 Subject: [PATCH 018/131] Add support for Traditional Chinese --- lib/main.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/main.dart b/lib/main.dart index 88a0fd43..0e021fc3 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -216,4 +216,5 @@ const List supportedLocales = [ Locale('it'), Locale('ru'), Locale('zh'), + Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant'), ]; From 0a54c83eb271b24da5f3f39267c4926affe2a6b8 Mon Sep 17 00:00:00 2001 From: t1011 <31567272+t1011@users.noreply.github.com> Date: Sun, 11 Feb 2024 18:03:17 +0300 Subject: [PATCH 019/131] Updated russian translate --- lib/l10n/app_ru.arb | 106 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 105 insertions(+), 1 deletion(-) diff --git a/lib/l10n/app_ru.arb b/lib/l10n/app_ru.arb index eab176f4..b82494c3 100644 --- a/lib/l10n/app_ru.arb +++ b/lib/l10n/app_ru.arb @@ -236,5 +236,109 @@ "addPaymentCard": "Добавить платежную карту", "addNote": "Добавить заметку", "addIDCard": "Добавить удостоверение личности", - "addIdentity": "Добавить личность" + "addIdentity": "Добавить личность", + "newVersionAvailable": "Доступна новая версия", + "unableToConnectBrowserExtension": "Не удается подключить расширение браузера", + "somethingWentWrong": "Что-то пошло не так", + "twoFactorAuthentication": "Двухфакторная аутентификация", + "advancedSettings": "Дополнительные настройки", + "enableMarkdown": "Включить Markdown", + "markdownPreview": "Предпросмотр Markdown", + "keyDerivation": "Деривация ключа", + "keyDerivationType": "Тип деривации ключа", + "keyDerivationDescription": "Деривация ключа может значительно повысить безопасность вашей учетной записи, сделав пароль более трудноугадываемым.", + "keyDerivationWarning1": "Настоятельно рекомендуется использовать деривацию ключа.", + "keyDerivationWarning2": "Для правильной работы синхронизации все ваши устройства должны иметь одинаковый тип деривации ключа.", + "keyDerivationWarning3": "Если вы не видите это меню на других устройствах, убедитесь, что все ваши устройства обновлены до последней версии Passy.", + "@keyDerivationWarning1": { "description": "keyDerivationWarning1, keyDerivationWarning2 and keyDerivationWarning3 make up a single message used in the key derivation screen" }, + "@keyDerivationWarning2": { "description": "keyDerivationWarning1, keyDerivationWarning2 and keyDerivationWarning3 make up a single message used in the key derivation screen" }, + "@keyDerivationWarning3": { "description": "keyDerivationWarning1, keyDerivationWarning2 and keyDerivationWarning3 make up a single message used in the key derivation screen" }, + "youAreChangingKeyDerivationFor": "Вы меняете деривацию ключа для ", + "@youAreChangingKeyDerivationFor": { "description": "This message is followed by a username in the key derivation screen" }, + "synchronizationServers": "Серверы синхронизации", + "serverSetup": "Настройка сервера", + "removeServers": "Удалить серверы", + "synchronizationInterval": "Интервал синхронизации", + "reset": "Сброс", + "synchronizationServerSetup": "Настройка сервера синхронизации", + "connectToServer": "Подключение к серверу", + "none": "Нет", + "syncServerSetupInfo": "Вам нужен только один сервер для доступа ко всем вашим аккаунтам.", + "chooseHostAddressAndPort": "Выберите адрес и порт хоста", + "port": "Порт", + "installServer": "Установить сервер", + "doubleClickMessage": "Используя файловый менеджер, перейдите в указанную директорию установки и сделайте двойной щелчок ", + "doubleClickMessage1": "Если при двойном щелчке файл открывается в текстовом редакторе, необходимо щелкнуть его правой кнопкой мыши и выбрать `Запустить как программу`.", + "@installMessage": { "description": "This message is followed by an executable file name that the user has to double-click" }, + "startServer": "Запустить сервер", + "testConnection": "Тестовое подключение", + "optional": "Опционально", + "addServerToAutostart": "Добавить сервер в автозапуск", + "onClientDevices": "На клиентском устройстве(ах)", + "@onClientDevices": { "description": "This message is followed by a list of actions that have to be performed on client device(s)" }, + "serverInstalled": "Сервер установлен", + "couldNotInstallServer": "Не удалось установить сервер", + "hostAddressIsEmpty": "Адрес хоста не указан", + "invalidPortSpecified": "Указан недопустимый порт", + "couldNotConnectToServer": "Не удается подключиться к серверу", + "intervalIsLessThan": "Интервал меньше, чем ", + "@intervalIsLessThan": { "description": "This message is followed by a time interval and units for that interval" }, + "seconds": "сек.", + "minutes": "мин.", + "hours": "час.", + "days": "дн.", + "weeks": "нед.", + "months": "мес.", + "years": "г.", + "connectToSynchronizationServer": "Подключение к серверу синхронизации", + "nicknameCanNotBeEmpty": "Название не может быть пустым", + "nicknameAlreadyInUse": "Название уже используется", + "lastSynchronization": "Последняя синхронизация", + "connecting": "Подключение", + "connectionEstablished": "Соединение установлено", + "syncError": "Ошибка синхронизации", + "invalidAddressFormat": "Недопустимый формат адреса", + "connectionFailed": "Не удалось подключиться", + "synchronizationComplete": "Синхронизация выполнена", + "entriesAdded": "Добавлены записи", + "entriesRemoved": "Удалены записи", + "csvExport": "Экспорт CSV", + "kdbxImport": "Импорт KDBX", + "kdbxExport": "Экспорт KDBX", + "imported": "Импортировано", + "backupYourAccountBeforeMakingChangesToTheseSettings": "Прежде чем вносить изменения в эти настройки, создайте резервную копию вашего аккаунта.", + "backupYourAccountBeforeProceeding": "Создайте резервную копию аккаунта, прежде чем продолжить", + "synchronizationLogs": "Журналы синхронизации", + "noRecentActivity": "Нет недавней активности.", + "yourAccountWillBeStoredLocally1": "Ваш аккаунт будет храниться локально ", + "yourAccountWillBeStoredLocally2Highlighted": "на этом устройстве.", + "@yourAccountWillBeStoredLocally1": { "description": "yourAccountWillBeStoredLocally1 and yourAccountWillBeStoredLocally2Highlighted make up a single message used in the add account screen. yourAccountWillBeStoredLocally2Highlighted is shown in a more noticeable color to increase user attention." }, + "@yourAccountWillBeStoredLocally2Highlighted": { "description": "yourAccountWillBeStoredLocally1 and yourAccountWillBeStoredLocally2Highlighted make up a single message used in the add account screen. yourAccountWillBeStoredLocally2Highlighted is shown in a more noticeable color to increase user attention." }, + "files": "Файлы", + "noFiles": "Нет файлов", + "fileType": "Тип файла", + "photo": "Фото", + "file": "Файл", + "folder": "Папка", + "unknown": "Неизвестно", + "plainText": "Простой текст", + "markdown": "Markdown", + "folderName": "Название папки", + "fileSize": "Размер файла", + "filePreview": "Предпросмотр файла", + "failedToDisplayFile": "Не удалось отобразить файл", + "addFile": "Добавить файл", + "failedToAddFile": "Не удалось добавить файл", + "removeFile": "Удалить файл", + "filesCanOnlyBeRestoredFromABackup": "Файлы можно восстановить только из резервной копии", + "attachFile": "Прикрепить файл", + "attachments": "Вложения", + "synchronizedFiles": "Синхронизированные файлы", + "scrollOrPinchToZoom": "Прокрутите или раздвиньте для увеличения", + "missingFile": "Отсутствующий файл", + "rename": "Переименовать", + "fileName": "Название файла", + "refresh": "Обновить", + "counter": "Счетчик", + "@counter": { "description": "HOTP 2FA counter stores a number that is incremented on every logon" } } From 7089f05388c92c0a3bd035dc749f4a9a9af62c08 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Wed, 21 Feb 2024 16:25:32 +0000 Subject: [PATCH 020/131] Update payment_card_button.dart --- lib/passy_flutter/widgets/payment_card_button.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/passy_flutter/widgets/payment_card_button.dart b/lib/passy_flutter/widgets/payment_card_button.dart index d3db6c31..2a59e7eb 100644 --- a/lib/passy_flutter/widgets/payment_card_button.dart +++ b/lib/passy_flutter/widgets/payment_card_button.dart @@ -58,6 +58,7 @@ class PaymentCardButton extends StatelessWidget { paymentCard.cardNumber.replaceAll('*', '0')), isSwipeGestureEnabled: isSwipeGestureEnabled, onCreditCardWidgetChange: (brand) {}, + bankName: ' ', ), ), Padding( From e899d41d8f0df5f9c5dfa9ea04bfb8225b838b0a Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Wed, 21 Feb 2024 16:29:55 +0000 Subject: [PATCH 021/131] Update pubspec.yaml --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 54104e26..c2c581f4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -45,7 +45,7 @@ dependencies: flutter_web_browser: ^0.17.1 flutter_locker: ^2.1.2 cached_network_image: ^3.2.1 - flutter_credit_card: ^3.0.1 + flutter_credit_card: ^3.0.7 flutter_date_pickers: ^0.4.0 credit_card_type_detector: ^2.0.0 flutter_secure_screen: ^0.0.1 From fd01046134f5b545643d1fef92b13f2dad1699a6 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Thu, 7 Mar 2024 23:43:33 +0000 Subject: [PATCH 022/131] EntryTagList implemented --- lib/l10n/app_en.arb | 5 +- .../widgets/entry_tag_button.dart | 41 ++++ lib/passy_flutter/widgets/entry_tag_list.dart | 185 ++++++++++++++++++ 3 files changed, 230 insertions(+), 1 deletion(-) create mode 100644 lib/passy_flutter/widgets/entry_tag_button.dart create mode 100644 lib/passy_flutter/widgets/entry_tag_list.dart diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index a36356f8..74ad8b53 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -340,5 +340,8 @@ "fileName": "File name", "refresh": "Refresh", "counter": "Counter", - "@counter": { "description": "HOTP 2FA counter stores a number that is incremented on every logon" } + "@counter": { "description": "HOTP 2FA counter stores a number that is incremented on every logon" }, + "createTag": "Create tag", + "create": "Create", + "tag": "Tag" } diff --git a/lib/passy_flutter/widgets/entry_tag_button.dart b/lib/passy_flutter/widgets/entry_tag_button.dart new file mode 100644 index 00000000..39a78e10 --- /dev/null +++ b/lib/passy_flutter/widgets/entry_tag_button.dart @@ -0,0 +1,41 @@ +import 'package:flutter/material.dart'; +import 'package:passy/passy_flutter/passy_flutter.dart'; + +class EntryTagButton extends StatelessWidget { + final String tag; + final Color color; + final bool isSelected; + final void Function()? onPressed; + + const EntryTagButton( + this.tag, { + super.key, + this.color = PassyTheme.lightContentColor, + this.isSelected = false, + this.onPressed, + }); + + @override + Widget build(BuildContext context) { + return TextButton.icon( + onPressed: onPressed ?? () {}, + style: ElevatedButton.styleFrom( + backgroundColor: color, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(35.0), + ), + ), + icon: Icon(isSelected ? Icons.close_rounded: Icons.add_rounded), + label: Padding( + padding: EdgeInsets.only( + top: PassyTheme.passyPadding.top, + bottom: PassyTheme.passyPadding.bottom, + right: PassyTheme.passyPadding.right), + child: Text( + tag, + style: const TextStyle(color: PassyTheme.darkContentColor), + ), + ), + ); + } +} diff --git a/lib/passy_flutter/widgets/entry_tag_list.dart b/lib/passy_flutter/widgets/entry_tag_list.dart new file mode 100644 index 00000000..239e14d9 --- /dev/null +++ b/lib/passy_flutter/widgets/entry_tag_list.dart @@ -0,0 +1,185 @@ +import 'package:flutter/material.dart'; +import 'package:passy/common/common.dart'; +import 'package:passy/passy_flutter/passy_flutter.dart'; +import 'package:passy/passy_flutter/widgets/entry_tag_button.dart'; + +class EntryTagList extends StatefulWidget { + final List selected; + final List notSelected; + final void Function(String tag) onAdded; + final void Function(String tag) onRemoved; + final void Function() onAddPressed; + final bool showAddButton; + + const EntryTagList({ + super.key, + this.selected = const [], + this.notSelected = const [], + void Function(String tag)? onAdded, + void Function(String tag)? onRemoved, + void Function()? onAddPressed, + this.showAddButton = false, + }) : onAdded = onAdded ?? _onChanged, + onRemoved = onRemoved ?? _onChanged, + onAddPressed = onAddPressed ?? _void; + + static void _onChanged(tag) {} + static void _void() {} + + @override + State createState() => _EntryTagList(); +} + +class _EntryTagList extends State { + final ScrollController _scrollController = ScrollController(); + final GlobalKey _key = GlobalKey(); + + bool showScrollbar = true; + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + WidgetsBinding.instance.addPostFrameCallback((_) { + _key.currentContext?.size?.width; + setState(() { + showScrollbar = (_key.currentContext?.size?.width ?? 0) == + MediaQuery.of(context).size.width; + }); + }); + + List notSelectedButtons = []; + List selectedButtons = []; + + for (String tag in widget.selected) { + selectedButtons.add( + Padding( + padding: EdgeInsets.only( + left: PassyTheme.passyPadding.left / 2, + right: PassyTheme.passyPadding.right / 2, + bottom: showScrollbar ? 14 : 0), + child: EntryTagButton( + tag, + isSelected: true, + onPressed: () { + widget.onRemoved(tag); + }, + ), + ), + ); + } + + for (String tag in widget.notSelected) { + notSelectedButtons.add( + Padding( + padding: EdgeInsets.only( + left: PassyTheme.passyPadding.left / 2, + right: PassyTheme.passyPadding.right / 2, + bottom: showScrollbar ? 14 : 0), + child: EntryTagButton( + tag, + onPressed: () { + widget.onAdded(tag); + }, + ), + ), + ); + } + + Widget scrollView = CustomScrollView( + controller: _scrollController, + shrinkWrap: true, + scrollDirection: Axis.horizontal, + slivers: [ + SliverList.list( + children: [ + Row( + children: [ + if (widget.showAddButton) + Padding( + padding: EdgeInsets.only(bottom: showScrollbar ? 14 : 0), + child: FloatingActionButton( + heroTag: null, + child: const Icon(Icons.add), + onPressed: () { + widget.onAddPressed(); + showDialog( + context: context, + builder: (context) { + String tag = ''; + return AlertDialog( + shape: PassyTheme.dialogShape, + title: Text(localizations.createTag), + content: TextFormField( + autofocus: true, + decoration: InputDecoration( + labelText: localizations.tag), + onChanged: (value) => {tag = value}, + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: Text(localizations.cancel, + style: const TextStyle( + color: PassyTheme + .lightContentSecondaryColor)), + ), + TextButton( + onPressed: () { + widget.onAdded(tag); + Navigator.pop(context); + }, + child: Text(localizations.create, + style: const TextStyle( + color: PassyTheme + .lightContentSecondaryColor)), + ), + ], + ); + }, + ); + }, + ), + ), + ...selectedButtons, + if (selectedButtons.isNotEmpty && notSelectedButtons.isNotEmpty) + Padding( + padding: EdgeInsets.only( + left: PassyTheme.passyPadding.left / 2, + right: PassyTheme.passyPadding.right / 2, + bottom: showScrollbar ? 14 : 0), + child: const SizedBox( + height: 36, + width: 2, + child: DecoratedBox( + decoration: + BoxDecoration(color: PassyTheme.lightContentColor), + ), + ), + ), + ...notSelectedButtons, + ], + ), + ], + ), + ], + ); + + return SizedBox( + key: _key, + height: showScrollbar ? 50 : 36, + child: PrimaryScrollController( + controller: _scrollController, + child: showScrollbar + ? Scrollbar( + thumbVisibility: true, + child: scrollView, + ) + : scrollView, + ), + ); + } +} From 3182129b1e8125e0f8b80d986440498699243cd0 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Thu, 7 Mar 2024 23:43:57 +0000 Subject: [PATCH 023/131] Update widgets.dart --- lib/passy_flutter/widgets/widgets.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/passy_flutter/widgets/widgets.dart b/lib/passy_flutter/widgets/widgets.dart index b05fbd0b..63cecaf3 100644 --- a/lib/passy_flutter/widgets/widgets.dart +++ b/lib/passy_flutter/widgets/widgets.dart @@ -9,6 +9,7 @@ export 'double_action_button.dart'; export 'edit_screen_appbar.dart'; export 'entries_screen_appbar.dart'; export 'entry_screen_appbar.dart'; +export 'entry_tag_list.dart'; export 'enum_dropdown_button_2.dart'; export 'enum_dropdown_button_form_field.dart'; export 'favicon_image.dart'; From bb011e1cc4c477184f99739d1dc8702a769a2e75 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Thu, 7 Mar 2024 23:45:11 +0000 Subject: [PATCH 024/131] Update passy_search.dart --- lib/passy_data/passy_search.dart | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/passy_data/passy_search.dart b/lib/passy_data/passy_search.dart index 41035201..2ebfa287 100644 --- a/lib/passy_data/passy_search.dart +++ b/lib/passy_data/passy_search.dart @@ -1,11 +1,15 @@ import 'package:passy/passy_data/password.dart'; class PassySearch { - static List searchPasswords( - {required Iterable passwords, required String terms}) { + static List searchPasswords({ + required Iterable passwords, + required String terms, + List tags = const [], + }) { final List _found = []; final List _terms = terms.trim().toLowerCase().split(' '); for (PasswordMeta _password in passwords) { + if (_password.tags.length < tags.length) continue; { bool testPassword(PasswordMeta value) => _password.key == value.key; @@ -13,6 +17,12 @@ class PassySearch { } { int _positiveCount = 0; + bool _tagMismatch = false; + for (String tag in tags) { + if (_password.tags.contains(tag)) continue; + _tagMismatch = true; + } + if (_tagMismatch) continue; for (String _term in _terms) { if (_password.username.toLowerCase().contains(_term)) { _positiveCount++; From 9e0c17fee3273fd6f5fff84ea7618fc79c49cfc1 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Thu, 7 Mar 2024 23:46:00 +0000 Subject: [PATCH 025/131] Update payment_card_button_mini.dart --- lib/passy_flutter/widgets/payment_card_button_mini.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/passy_flutter/widgets/payment_card_button_mini.dart b/lib/passy_flutter/widgets/payment_card_button_mini.dart index 8245652f..234d4ea0 100644 --- a/lib/passy_flutter/widgets/payment_card_button_mini.dart +++ b/lib/passy_flutter/widgets/payment_card_button_mini.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:flutter_credit_card/credit_card_widget.dart'; +import 'package:flutter_credit_card/flutter_credit_card.dart'; import 'package:passy/passy_data/payment_card.dart'; import 'package:passy/passy_flutter/common/common.dart'; import 'package:passy/passy_flutter/passy_flutter.dart'; From 558c6c5b28c914cd8d614ec2dd818e9e307d9aa5 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Thu, 7 Mar 2024 23:46:42 +0000 Subject: [PATCH 026/131] PassyEntriesEncryptedCSVFile.tags implemented --- lib/passy_data/note.dart | 9 ++++++ .../passy_entries_encrypted_csv_file.dart | 29 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/lib/passy_data/note.dart b/lib/passy_data/note.dart index bc5bc7fb..62195f6a 100644 --- a/lib/passy_data/note.dart +++ b/lib/passy_data/note.dart @@ -28,6 +28,7 @@ class Note extends PassyEntry { String note; bool isMarkdown; List attachments; + List tags; Note({ String? key, @@ -35,7 +36,9 @@ class Note extends PassyEntry { this.note = '', this.isMarkdown = false, List? attachments, + List? tags, }) : attachments = attachments ?? [], + tags = tags ?? [], super(key ?? DateTime.now().toUtc().toIso8601String()); @override @@ -50,6 +53,9 @@ class Note extends PassyEntry { : (json['attachments'] as List) .map((e) => e.toString()) .toList(), + tags = json['tags'] == null + ? [] + : (json['tags'] as List).map((e) => e.toString()).toList(), super(json['key'] ?? DateTime.now().toUtc().toIso8601String()); Note._fromCSV(List csv) @@ -58,11 +64,13 @@ class Note extends PassyEntry { isMarkdown = boolFromString(csv[3] ?? 'false') ?? false, attachments = (csv[4] as List).map((e) => e.toString()).toList(), + tags = (csv[5] as List).map((e) => e.toString()).toList(), super(csv[0] ?? DateTime.now().toUtc().toIso8601String()); factory Note.fromCSV(List csv) { if (csv.length == 3) csv.add('false'); if (csv.length == 4) csv.add([]); + if (csv.length == 5) csv.add([]); return Note._fromCSV(csv); } @@ -75,6 +83,7 @@ class Note extends PassyEntry { 'title': title, 'note': note, 'isMarkdown': isMarkdown, + 'tags': tags, 'attachments': attachments, }; diff --git a/lib/passy_data/passy_entries_encrypted_csv_file.dart b/lib/passy_data/passy_entries_encrypted_csv_file.dart index 2234f4b8..ad1af53a 100644 --- a/lib/passy_data/passy_entries_encrypted_csv_file.dart +++ b/lib/passy_data/passy_entries_encrypted_csv_file.dart @@ -11,6 +11,7 @@ import 'passy_kdbx_value.dart'; class PassyEntriesEncryptedCSVFile> { final File _file; Encrypter _encrypter; + EntryType? _entryType; set encrypter(Encrypter encrypter) => _encrypter = encrypter; List get keys { @@ -25,6 +26,34 @@ class PassyEntriesEncryptedCSVFile> { return _keys; } + List get tags { + List _tags = []; + RandomAccessFile _raf = _file.openSync(); + if (skipLine(_raf, lineDelimiter: ',') == -1) { + _raf.closeSync(); + return _tags; + } + int _tagIndex = T.toString() == 'Note' ? 5 : 3; + processLines(_raf, onLine: (entry, eofReached) { + List _decoded = entry.split(','); + String _decrypted = decrypt(_decoded[1], + encrypter: _encrypter, iv: IV.fromBase64(_decoded[0])); + List _csv = csvDecode(_decrypted, recursive: true); + if (_csv.length < _tagIndex + 1) { + return true; + } + for (dynamic tag in (_csv[_tagIndex] as List)) { + tag = tag.toString(); + if (_tags.contains(tag)) continue; + _tags.add(tag); + } + if (skipLine(_raf, lineDelimiter: ',') == -1) return true; + return null; + }); + _raf.closeSync(); + return _tags; + } + Map get metadata { Map _meta = {}; RandomAccessFile _raf = _file.openSync(); From 4d104ab3d688f8b4e83eabcb2f536ef82aee9db3 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Thu, 7 Mar 2024 23:47:19 +0000 Subject: [PATCH 027/131] LoadedAccount tags wrappers implemented --- lib/passy_data/loaded_account.dart | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/passy_data/loaded_account.dart b/lib/passy_data/loaded_account.dart index 030e5f6d..ee739b05 100644 --- a/lib/passy_data/loaded_account.dart +++ b/lib/passy_data/loaded_account.dart @@ -1079,8 +1079,17 @@ class LoadedAccount { } } + List get tags => [ + ..._passwords.tags, + ..._notes.tags, + ..._paymentCards.tags, + ..._idCards.tags, + ..._identities.tags, + ]; + // Passwords wrappers List get passwordKeys => _passwords.keys; + List get passwordTags => _passwords.tags; Map get passwordsMetadata { bool _isHistoryChanged = false; _history.reloadSync(); @@ -1138,6 +1147,7 @@ class LoadedAccount { // Notes wrappers List get notesKeys => _notes.keys; + List get notesTags => _notes.tags; Map get notesMetadata { bool _isHistoryChanged = false; _history.reloadSync(); @@ -1197,6 +1207,7 @@ class LoadedAccount { // Payment Cards wrappers List get paymentCardKeys => _paymentCards.keys; + List get paymentCardTags => _paymentCards.tags; Map get paymentCardsMetadata { bool _isHistoryChanged = false; _history.reloadSync(); @@ -1257,6 +1268,7 @@ class LoadedAccount { // ID Cards wrappers List get idCardsKeys => _idCards.keys; + List get idCardsTags => _idCards.tags; Map get idCardsMetadata { bool _isHistoryChanged = false; _history.reloadSync(); @@ -1315,6 +1327,7 @@ class LoadedAccount { // Identities wrappers List get identitiesKeys => _identities.keys; + List get identitiesTags => _identities.tags; Map get identitiesMetadata { bool _isHistoryChanged = false; _history.reloadSync(); From 4ae64dc8756502ad7f47c39f19a0ae8adff0da07 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Thu, 7 Mar 2024 23:48:54 +0000 Subject: [PATCH 028/131] SearchScreen tags + Password tags UI implemented --- lib/screens/id_cards_screen.dart | 2 +- lib/screens/identities_screen.dart | 2 +- lib/screens/login_screen.dart | 2 +- lib/screens/main_screen.dart | 5 ++- lib/screens/notes_screen.dart | 2 +- lib/screens/password_screen.dart | 55 +++++++++++++++++++++++++++ lib/screens/passwords_screen.dart | 29 ++++++++++++-- lib/screens/payment_cards_screen.dart | 2 +- lib/screens/search_screen.dart | 45 +++++++++++++++++++--- 9 files changed, 128 insertions(+), 16 deletions(-) diff --git a/lib/screens/id_cards_screen.dart b/lib/screens/id_cards_screen.dart index b2ac5e7e..feeb5cf0 100644 --- a/lib/screens/id_cards_screen.dart +++ b/lib/screens/id_cards_screen.dart @@ -29,7 +29,7 @@ class _IDCardsScreen extends State { void _onSearchPressed() { Navigator.pushNamed(context, SearchScreen.routeName, arguments: - SearchScreenArgs(builder: (String terms, void Function() rebuild) { + SearchScreenArgs(builder: (String terms, List tags, void Function() rebuild) { final List _found = []; final List _terms = terms.trim().toLowerCase().split(' '); for (IDCardMeta _idCard in _account.idCardsMetadata.values) { diff --git a/lib/screens/identities_screen.dart b/lib/screens/identities_screen.dart index 100ba8e7..de9271b7 100644 --- a/lib/screens/identities_screen.dart +++ b/lib/screens/identities_screen.dart @@ -28,7 +28,7 @@ class _IdentitiesScreen extends State { void _onSearchPressed() { Navigator.pushNamed(context, SearchScreen.routeName, arguments: - SearchScreenArgs(builder: (String terms, void Function() rebuild) { + SearchScreenArgs(builder: (String terms, List tags, void Function() rebuild) { final List _found = []; final List _terms = terms.trim().toLowerCase().split(' '); for (IdentityMeta _identity in _account.identitiesMetadata.values) { diff --git a/lib/screens/login_screen.dart b/lib/screens/login_screen.dart index 3fb098c7..85933c79 100644 --- a/lib/screens/login_screen.dart +++ b/lib/screens/login_screen.dart @@ -91,7 +91,7 @@ class _LoginScreen extends State { } } - Widget _buildPasswords(String terms, void Function() rebuild) { + Widget _buildPasswords(String terms, List tags, void Function() rebuild) { List _found = PassySearch.searchPasswords( passwords: data.loadedAccount!.passwordsMetadata.values, terms: terms); List _dataSets = []; diff --git a/lib/screens/main_screen.dart b/lib/screens/main_screen.dart index dabc7453..918b216f 100644 --- a/lib/screens/main_screen.dart +++ b/lib/screens/main_screen.dart @@ -54,7 +54,8 @@ class _MainScreen extends State bool _unlockScreenOn = false; String _lastSyncDate = 'NaN'; - Widget _searchBuilder(String terms, void Function() rebuild) { + Widget _searchBuilder( + String terms, List tags, void Function() rebuild) { final List _found = []; final List _terms = terms.trim().toLowerCase().split(' '); final List _searchEntries = []; @@ -196,7 +197,7 @@ class _MainScreen extends State ); } - Widget _favoritesSearchBuilder(String terms, void Function() setState) { + Widget _favoritesSearchBuilder(String terms, List tags, void Function() setState) { if (!_account.hasFavorites) { return CustomScrollView( slivers: [ diff --git a/lib/screens/notes_screen.dart b/lib/screens/notes_screen.dart index 8c867798..618b184d 100644 --- a/lib/screens/notes_screen.dart +++ b/lib/screens/notes_screen.dart @@ -29,7 +29,7 @@ class _NotesScreen extends State { void _onSearchPressed() { Navigator.pushNamed(context, SearchScreen.routeName, arguments: - SearchScreenArgs(builder: (String terms, void Function() rebuild) { + SearchScreenArgs(builder: (String terms, List tags, void Function() rebuild) { final List _found = []; final List _terms = terms.trim().toLowerCase().split(' '); for (NoteMeta _note in _account.notesMetadata.values) { diff --git a/lib/screens/password_screen.dart b/lib/screens/password_screen.dart index bef2b266..fce7fede 100644 --- a/lib/screens/password_screen.dart +++ b/lib/screens/password_screen.dart @@ -30,6 +30,8 @@ class PasswordScreen extends StatefulWidget { class _PasswordScreen extends State { final Completer _onClosed = Completer(); final LoadedAccount _account = data.loadedAccount!; + List _tags = []; + List _selected = []; Password? password; Future? generateTFA; String _tfaCode = ''; @@ -84,6 +86,7 @@ class _PasswordScreen extends State { @override void initState() { super.initState(); + _tags = _account.passwordTags; } @override @@ -150,6 +153,12 @@ class _PasswordScreen extends State { generateTFA = _generateTFA(password!.tfa!); } } + _selected = password!.tags.toList(); + for (String tag in _selected) { + if (_tags.contains(tag)) { + _tags.remove(tag); + } + } } Widget? tfaWidget; if (password!.tfa != null) { @@ -242,6 +251,52 @@ class _PasswordScreen extends State { ), body: ListView( children: [ + Center( + child: Padding( + padding: EdgeInsets.only( + top: PassyTheme.passyPadding.top / 2, + bottom: PassyTheme.passyPadding.bottom / 2), + child: EntryTagList( + showAddButton: true, + selected: _selected, + notSelected: _tags, + onAdded: (tag) async { + Navigator.pushNamed(context, SplashScreen.routeName); + password!.tags = _selected.toList(); + password!.tags.add(tag); + await _account.setPassword(password!); + Navigator.popUntil( + context, + (route) => + route.settings.name == PasswordScreen.routeName); + if (!mounted) return; + setState(() { + _tags.remove(tag); + _selected.add(tag); + _selected.sort(); + password!.tags = _selected; + }); + }, + onRemoved: (tag) async { + Navigator.pushNamed(context, SplashScreen.routeName); + password!.tags = _selected.toList(); + password!.tags.remove(tag); + await _account.setPassword(password!); + Navigator.popUntil( + context, + (route) => + route.settings.name == PasswordScreen.routeName); + if (!mounted) return; + setState(() { + _tags.add(tag); + _tags.sort(); + _selected.remove(tag); + password!.tags = _selected; + }); + }, + ), + ), + ), if (password!.attachments.isNotEmpty) AttachmentsListView(files: password!.attachments), if (password!.nickname != '') diff --git a/lib/screens/passwords_screen.dart b/lib/screens/passwords_screen.dart index 7eed34e3..7db9b7da 100644 --- a/lib/screens/passwords_screen.dart +++ b/lib/screens/passwords_screen.dart @@ -24,13 +24,18 @@ class PasswordsScreen extends StatefulWidget { class _PasswordsScreen extends State { final LoadedAccount _account = data.loadedAccount!; + final List _tags = data.loadedAccount!.passwordTags; void _onAddPressed() => Navigator.pushNamed(context, EditPasswordScreen.routeName); - Widget _buildPasswords(String terms, void Function() rebuild) { + Widget _buildPasswords( + String terms, + List tags, + void Function() rebuild, + ) { List _found = PassySearch.searchPasswords( - passwords: _account.passwordsMetadata.values, terms: terms); + passwords: _account.passwordsMetadata.values, terms: terms, tags: tags); if (_found.isEmpty) { return CustomScrollView( slivers: [ @@ -60,9 +65,12 @@ class _PasswordsScreen extends State { ); } - void _onSearchPressed() { + void _onSearchPressed({String? tag}) { Navigator.pushNamed(context, SearchScreen.routeName, - arguments: SearchScreenArgs(builder: _buildPasswords)); + arguments: SearchScreenArgs( + builder: _buildPasswords, + notSelectedTags: _account.passwordTags..remove(tag), + selectedTags: tag == null ? [] : [tag])); } @override @@ -111,6 +119,19 @@ class _PasswordsScreen extends State { context, EditPasswordScreen.routeName), ), ), + Center( + child: Padding( + padding: EdgeInsets.only( + top: PassyTheme.passyPadding.top / 2, + bottom: PassyTheme.passyPadding.bottom / 2), + child: EntryTagList( + notSelected: _tags, + onAdded: (tag) => setState(() { + _onSearchPressed(tag: tag); + }), + ), + ), + ), ], passwords: _passwords.toList(), onPressed: (password) => Navigator.pushNamed( diff --git a/lib/screens/payment_cards_screen.dart b/lib/screens/payment_cards_screen.dart index 3dd7e229..fe6df048 100644 --- a/lib/screens/payment_cards_screen.dart +++ b/lib/screens/payment_cards_screen.dart @@ -25,7 +25,7 @@ class _PaymentCardsScreen extends State { void _onSearchPressed() { Navigator.pushNamed(context, SearchScreen.routeName, arguments: SearchScreenArgs( - builder: (String terms, void Function() rebuild) { + builder: (String terms, List tags, void Function() rebuild) { final List _found = []; final List _terms = terms.trim().toLowerCase().split(' '); for (PaymentCardMeta _paymentCard diff --git a/lib/screens/search_screen.dart b/lib/screens/search_screen.dart index 1bd324ea..1c1a7439 100644 --- a/lib/screens/search_screen.dart +++ b/lib/screens/search_screen.dart @@ -6,13 +6,21 @@ import 'package:passy/passy_flutter/widgets/widgets.dart'; class SearchScreenArgs { String? title; - Widget Function(String terms, void Function() rebuild) builder; + Widget Function( + String terms, + List tags, + void Function() rebuild, + ) builder; bool isAutofill; + List notSelectedTags; + List selectedTags; SearchScreenArgs({ this.title, required this.builder, this.isAutofill = false, + this.notSelectedTags = const [], + this.selectedTags = const [], }); } @@ -30,8 +38,11 @@ class _SearchScreen extends State { Widget _widget = const Text(''); TextEditingController queryController = TextEditingController(); FocusNode queryFocus = FocusNode()..requestFocus(); + List selected = []; + List notSelected = []; Future? entryBuilder; - late Widget Function(String terms, void Function() rebuild) _builder; + late Widget Function(String terms, List tags, void Function() rebuild) + _builder; @override void initState() { @@ -47,7 +58,7 @@ class _SearchScreen extends State { Future.delayed(const Duration(milliseconds: 100), () { entryBuilder = null; setState(() { - _widget = _builder(queryController.text, rebuild); + _widget = _builder(queryController.text, selected, rebuild); }); }); }); @@ -59,7 +70,9 @@ class _SearchScreen extends State { ModalRoute.of(context)!.settings.arguments as SearchScreenArgs; _builder = args.builder; if (!_initialized) { - _widget = _builder(queryController.text, rebuild); + selected = args.selectedTags; + notSelected = args.notSelectedTags; + _widget = _builder(queryController.text, selected, rebuild); _initialized = true; } return Scaffold( @@ -102,11 +115,33 @@ class _SearchScreen extends State { const Duration(milliseconds: 100), () { entryBuilder = null; setState(() { - _widget = _builder(queryController.text, rebuild); + _widget = + _builder(queryController.text, selected, rebuild); }); }); }); })), + if (selected.isNotEmpty || notSelected.isNotEmpty) + Padding( + padding: EdgeInsets.only( + top: PassyTheme.passyPadding.top / 2, + bottom: PassyTheme.passyPadding.bottom / 2), + child: EntryTagList( + notSelected: notSelected, + selected: selected, + onAdded: (tag) => setState(() { + selected.add(tag); + selected.sort(); + notSelected.remove(tag); + _widget = _builder(queryController.text, selected, rebuild); + }), + onRemoved: (tag) => setState(() { + selected.remove(tag); + notSelected.add(tag); + notSelected.sort(); + _widget = _builder(queryController.text, selected, rebuild); + }), + )), Expanded( child: _widget, ), From e386226f302942f41ba6210b70476b1c13eac1f1 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 9 Mar 2024 08:06:03 +0000 Subject: [PATCH 029/131] Auth verification improved --- lib/passy_data/synchronization.dart | 13 +++++++++---- lib/passy_data/synchronization_2d0d0_utils.dart | 9 +++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/lib/passy_data/synchronization.dart b/lib/passy_data/synchronization.dart index 168e7bc4..8b412b4d 100644 --- a/lib/passy_data/synchronization.dart +++ b/lib/passy_data/synchronization.dart @@ -948,12 +948,15 @@ class Synchronization { } } + String lastAuth = ''; + Map auth() { + lastAuth = util.generateAuth( + encrypter: remoteEncrypter, + usernameEncrypter: usernameEncrypter, + withIV: _authWithIV); return { - 'auth': util.generateAuth( - encrypter: remoteEncrypter, - usernameEncrypter: usernameEncrypter, - withIV: _authWithIV), + 'auth': lastAuth, }; } @@ -1011,6 +1014,7 @@ class Synchronization { if (!response.containsKey('error')) { try { util.verifyAuth(response['auth'], + lastAuth: lastAuth, encrypter: remoteEncrypter, usernameEncrypter: usernameEncrypter, withIV: true); @@ -1145,6 +1149,7 @@ class Synchronization { } try { util.verifyAuth(authResponse['auth'], + lastAuth: lastAuth, encrypter: remoteEncrypter, usernameEncrypter: usernameEncrypter, withIV: _authWithIV); diff --git a/lib/passy_data/synchronization_2d0d0_utils.dart b/lib/passy_data/synchronization_2d0d0_utils.dart index d7c68fd8..e6785886 100644 --- a/lib/passy_data/synchronization_2d0d0_utils.dart +++ b/lib/passy_data/synchronization_2d0d0_utils.dart @@ -552,6 +552,7 @@ String generateAuth({ void verifyAuth( dynamic auth, { + String lastAuth = '', required Encrypter encrypter, required Encrypter usernameEncrypter, bool withIV = false, @@ -565,6 +566,14 @@ void verifyAuth( }, }; } + if (auth == lastAuth) { + throw { + 'error': { + 'type': 'Illegal auth', + 'description': 'Received auth is identical to sent auth', + }, + }; + } IV? iv; if (withIV) { try { From c1ed586260627e4bf001a77a7b0fdd58a6de7459 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 9 Mar 2024 11:50:48 +0000 Subject: [PATCH 030/131] Update passwords_screen.dart --- lib/screens/passwords_screen.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/screens/passwords_screen.dart b/lib/screens/passwords_screen.dart index 7db9b7da..131166ae 100644 --- a/lib/screens/passwords_screen.dart +++ b/lib/screens/passwords_screen.dart @@ -69,7 +69,7 @@ class _PasswordsScreen extends State { Navigator.pushNamed(context, SearchScreen.routeName, arguments: SearchScreenArgs( builder: _buildPasswords, - notSelectedTags: _account.passwordTags..remove(tag), + notSelectedTags: _tags.toList()..remove(tag), selectedTags: tag == null ? [] : [tag])); } From 67908c70aab672bcd353f86ca31926a5ecd7098a Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 9 Mar 2024 12:09:02 +0000 Subject: [PATCH 031/131] CSV export annotation updated --- lib/passy_data/loaded_account.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/passy_data/loaded_account.dart b/lib/passy_data/loaded_account.dart index ee739b05..93b5e2c4 100644 --- a/lib/passy_data/loaded_account.dart +++ b/lib/passy_data/loaded_account.dart @@ -553,30 +553,30 @@ class LoadedAccount { await _passwords.export( File('${tempPath}passwords.csv'), annotation: - '"customFields","additionalInfo","tags","nickname","iconName","username","email","password","tfa","website"', + '"customFields","additionalInfo","tags","nickname","iconName","username","email","password","tfa","website","attachments"', skipKey: true, ); await _paymentCards.export( File('${tempPath}payment_cards.csv'), annotation: - '"customFields","additionalInfo","tags","nickname","cardNumber","cardholderName","cvv","exp"', + '"customFields","additionalInfo","tags","nickname","cardNumber","cardholderName","cvv","exp","attachments"', skipKey: true, ); await _notes.export( File('${tempPath}notes.csv'), - annotation: '"title","note","isMarkdown"', + annotation: '"title","note","isMarkdown","tags","attachments"', skipKey: true, ); await _idCards.export( File('${tempPath}id_cards.csv'), annotation: - '"customFields","additionalInfo","tags","nickname","pictures","type","idNumber","name","issDate","expDate","country"', + '"customFields","additionalInfo","tags","nickname","pictures","type","idNumber","name","issDate","expDate","country","attachments"', skipKey: true, ); await _identities.export( File('${tempPath}identities.csv'), annotation: - '"customFields","additionalInfo","tags","nickname","title","firstName","middleName","lastName","gender","email","number","firstAddressLine","secondAddressLine","zipCode","city","country"', + '"customFields","additionalInfo","tags","nickname","title","firstName","middleName","lastName","gender","email","number","firstAddressLine","secondAddressLine","zipCode","city","country","attachments', skipKey: true, ); await _versionFile.copy('${tempPath}version.txt'); From bba142e27b25f801050bb36d66fcb93f24211a2f Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 9 Mar 2024 12:09:14 +0000 Subject: [PATCH 032/131] Update note.dart --- lib/passy_data/note.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/passy_data/note.dart b/lib/passy_data/note.dart index 62195f6a..8759e985 100644 --- a/lib/passy_data/note.dart +++ b/lib/passy_data/note.dart @@ -62,9 +62,9 @@ class Note extends PassyEntry { : title = csv[1] ?? '', note = csv[2] ?? '', isMarkdown = boolFromString(csv[3] ?? 'false') ?? false, + tags = (csv[4] as List).map((e) => e.toString()).toList(), attachments = - (csv[4] as List).map((e) => e.toString()).toList(), - tags = (csv[5] as List).map((e) => e.toString()).toList(), + (csv[5] as List).map((e) => e.toString()).toList(), super(csv[0] ?? DateTime.now().toUtc().toIso8601String()); factory Note.fromCSV(List csv) { @@ -93,6 +93,7 @@ class Note extends PassyEntry { title, note, isMarkdown.toString(), + tags, attachments, ]; From 96d4bb646dd138bb6dd0261e1c84e769226ac084 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 9 Mar 2024 12:34:43 +0000 Subject: [PATCH 033/131] Futurized tag getters --- lib/passy_data/loaded_account.dart | 20 +++++++--- .../passy_entries_encrypted_csv_file.dart | 1 - lib/screens/password_screen.dart | 30 +++++++------- lib/screens/passwords_screen.dart | 39 +++++++++++++------ 4 files changed, 58 insertions(+), 32 deletions(-) diff --git a/lib/passy_data/loaded_account.dart b/lib/passy_data/loaded_account.dart index 93b5e2c4..69ce3461 100644 --- a/lib/passy_data/loaded_account.dart +++ b/lib/passy_data/loaded_account.dart @@ -14,6 +14,7 @@ import 'package:passy/passy_data/file_meta.dart'; import 'package:passy/passy_data/json_file.dart'; import 'package:passy/passy_data/local_settings.dart'; import 'package:passy/passy_data/passy_entires_json_file.dart'; +import 'package:passy/passy_data/passy_entries_encrypted_csv_file.dart'; import 'package:passy/passy_data/passy_entries_file_collection.dart'; import 'package:archive/archive_io.dart'; import 'package:passy/passy_data/tfa.dart'; @@ -1089,7 +1090,9 @@ class LoadedAccount { // Passwords wrappers List get passwordKeys => _passwords.keys; - List get passwordTags => _passwords.tags; + Future> get passwordTags => compute( + (PassyEntriesEncryptedCSVFile _passwords) => _passwords.tags, + _passwords); Map get passwordsMetadata { bool _isHistoryChanged = false; _history.reloadSync(); @@ -1147,7 +1150,8 @@ class LoadedAccount { // Notes wrappers List get notesKeys => _notes.keys; - List get notesTags => _notes.tags; + Future> get notesTags => compute( + (PassyEntriesEncryptedCSVFile _notes) => _notes.tags, _notes); Map get notesMetadata { bool _isHistoryChanged = false; _history.reloadSync(); @@ -1207,7 +1211,10 @@ class LoadedAccount { // Payment Cards wrappers List get paymentCardKeys => _paymentCards.keys; - List get paymentCardTags => _paymentCards.tags; + Future> get paymentCardTags => compute( + (PassyEntriesEncryptedCSVFile _paymentCards) => + _paymentCards.tags, + _paymentCards); Map get paymentCardsMetadata { bool _isHistoryChanged = false; _history.reloadSync(); @@ -1268,7 +1275,8 @@ class LoadedAccount { // ID Cards wrappers List get idCardsKeys => _idCards.keys; - List get idCardsTags => _idCards.tags; + Future> get idCardsTags => compute( + (PassyEntriesEncryptedCSVFile _idCards) => _idCards.tags, _idCards); Map get idCardsMetadata { bool _isHistoryChanged = false; _history.reloadSync(); @@ -1327,7 +1335,9 @@ class LoadedAccount { // Identities wrappers List get identitiesKeys => _identities.keys; - List get identitiesTags => _identities.tags; + Future> get identitiesTags => compute( + (PassyEntriesEncryptedCSVFile _identities) => _identities.tags, + _identities); Map get identitiesMetadata { bool _isHistoryChanged = false; _history.reloadSync(); diff --git a/lib/passy_data/passy_entries_encrypted_csv_file.dart b/lib/passy_data/passy_entries_encrypted_csv_file.dart index ad1af53a..223c8cbc 100644 --- a/lib/passy_data/passy_entries_encrypted_csv_file.dart +++ b/lib/passy_data/passy_entries_encrypted_csv_file.dart @@ -11,7 +11,6 @@ import 'passy_kdbx_value.dart'; class PassyEntriesEncryptedCSVFile> { final File _file; Encrypter _encrypter; - EntryType? _entryType; set encrypter(Encrypter encrypter) => _encrypter = encrypter; List get keys { diff --git a/lib/screens/password_screen.dart b/lib/screens/password_screen.dart index fce7fede..a9b65d53 100644 --- a/lib/screens/password_screen.dart +++ b/lib/screens/password_screen.dart @@ -81,14 +81,6 @@ class _PasswordScreen extends State { } } - //TODO: implement tags - - @override - void initState() { - super.initState(); - _tags = _account.passwordTags; - } - @override void deactivate() { super.deactivate(); @@ -140,6 +132,21 @@ class _PasswordScreen extends State { ); } + Future _load() async { + List newTags = await _account.passwordTags; + if (mounted) { + setState(() { + _tags = newTags; + _selected = password!.tags.toList(); + for (String tag in _selected) { + if (_tags.contains(tag)) { + _tags.remove(tag); + } + } + }); + } + } + @override Widget build(BuildContext context) { if (password == null) { @@ -153,12 +160,7 @@ class _PasswordScreen extends State { generateTFA = _generateTFA(password!.tfa!); } } - _selected = password!.tags.toList(); - for (String tag in _selected) { - if (_tags.contains(tag)) { - _tags.remove(tag); - } - } + _load(); } Widget? tfaWidget; if (password!.tfa != null) { diff --git a/lib/screens/passwords_screen.dart b/lib/screens/passwords_screen.dart index 131166ae..be1819b0 100644 --- a/lib/screens/passwords_screen.dart +++ b/lib/screens/passwords_screen.dart @@ -24,7 +24,8 @@ class PasswordsScreen extends StatefulWidget { class _PasswordsScreen extends State { final LoadedAccount _account = data.loadedAccount!; - final List _tags = data.loadedAccount!.passwordTags; + List _tags = []; + bool _isLoading = false; void _onAddPressed() => Navigator.pushNamed(context, EditPasswordScreen.routeName); @@ -73,8 +74,21 @@ class _PasswordsScreen extends State { selectedTags: tag == null ? [] : [tag])); } + Future _load() async { + List newTags = await _account.passwordTags; + if (mounted) { + setState(() { + _tags = newTags; + }); + } + } + @override Widget build(BuildContext context) { + if (!_isLoading) { + _isLoading = true; + _load(); + } List _passwords = _account.passwordsMetadata.values.toList(); return Scaffold( appBar: EntriesScreenAppBar( @@ -119,19 +133,20 @@ class _PasswordsScreen extends State { context, EditPasswordScreen.routeName), ), ), - Center( - child: Padding( - padding: EdgeInsets.only( - top: PassyTheme.passyPadding.top / 2, - bottom: PassyTheme.passyPadding.bottom / 2), - child: EntryTagList( - notSelected: _tags, - onAdded: (tag) => setState(() { - _onSearchPressed(tag: tag); - }), + if (_tags.isNotEmpty) + Center( + child: Padding( + padding: EdgeInsets.only( + top: PassyTheme.passyPadding.top / 2, + bottom: PassyTheme.passyPadding.bottom / 2), + child: EntryTagList( + notSelected: _tags, + onAdded: (tag) => setState(() { + _onSearchPressed(tag: tag); + }), + ), ), ), - ), ], passwords: _passwords.toList(), onPressed: (password) => Navigator.pushNamed( From a9f855483ee25bbb8c242f25044e6130682ca926 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 9 Mar 2024 12:34:57 +0000 Subject: [PATCH 034/131] EntryTagList scrollbar fixed --- lib/passy_flutter/widgets/entry_tag_list.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/passy_flutter/widgets/entry_tag_list.dart b/lib/passy_flutter/widgets/entry_tag_list.dart index 239e14d9..97ee4c06 100644 --- a/lib/passy_flutter/widgets/entry_tag_list.dart +++ b/lib/passy_flutter/widgets/entry_tag_list.dart @@ -34,7 +34,7 @@ class _EntryTagList extends State { final ScrollController _scrollController = ScrollController(); final GlobalKey _key = GlobalKey(); - bool showScrollbar = true; + bool showScrollbar = false; @override void initState() { From bbd62df001b84f4141109cc89ab9696aa2be469e Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sun, 10 Mar 2024 20:40:17 +0000 Subject: [PATCH 035/131] Update passwords_screen.dart --- lib/screens/passwords_screen.dart | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/screens/passwords_screen.dart b/lib/screens/passwords_screen.dart index be1819b0..f83bdd91 100644 --- a/lib/screens/passwords_screen.dart +++ b/lib/screens/passwords_screen.dart @@ -25,7 +25,6 @@ class PasswordsScreen extends StatefulWidget { class _PasswordsScreen extends State { final LoadedAccount _account = data.loadedAccount!; List _tags = []; - bool _isLoading = false; void _onAddPressed() => Navigator.pushNamed(context, EditPasswordScreen.routeName); @@ -85,10 +84,7 @@ class _PasswordsScreen extends State { @override Widget build(BuildContext context) { - if (!_isLoading) { - _isLoading = true; _load(); - } List _passwords = _account.passwordsMetadata.values.toList(); return Scaffold( appBar: EntriesScreenAppBar( From ebd5edcd30b22965c06ff4e51040b2ec068f2674 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sun, 10 Mar 2024 20:44:26 +0000 Subject: [PATCH 036/131] Update passy_entries_encrypted_csv_file.dart --- lib/passy_data/passy_entries_encrypted_csv_file.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/passy_data/passy_entries_encrypted_csv_file.dart b/lib/passy_data/passy_entries_encrypted_csv_file.dart index 223c8cbc..11135434 100644 --- a/lib/passy_data/passy_entries_encrypted_csv_file.dart +++ b/lib/passy_data/passy_entries_encrypted_csv_file.dart @@ -32,7 +32,7 @@ class PassyEntriesEncryptedCSVFile> { _raf.closeSync(); return _tags; } - int _tagIndex = T.toString() == 'Note' ? 5 : 3; + int _tagIndex = T.toString() == 'Note' ? 4 : 3; processLines(_raf, onLine: (entry, eofReached) { List _decoded = entry.split(','); String _decrypted = decrypt(_decoded[1], From 88059d85539f6b11e8b0be07727936ecc8ca49b5 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sun, 10 Mar 2024 20:44:37 +0000 Subject: [PATCH 037/131] NoteMeta.tags implemented --- lib/passy_data/note.dart | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/passy_data/note.dart b/lib/passy_data/note.dart index 8759e985..10f707d5 100644 --- a/lib/passy_data/note.dart +++ b/lib/passy_data/note.dart @@ -13,13 +13,19 @@ typedef NotesFile = PassyEntriesEncryptedCSVFile; class NoteMeta extends EntryMeta { final String title; + final List tags; - NoteMeta({required String key, required this.title}) : super(key); + NoteMeta({ + required String key, + required this.title, + required this.tags, + }) : super(key); @override toJson() => { 'key': key, 'title': title, + 'tags': tags, }; } @@ -42,7 +48,7 @@ class Note extends PassyEntry { super(key ?? DateTime.now().toUtc().toIso8601String()); @override - EntryMeta get metadata => NoteMeta(key: key, title: title); + EntryMeta get metadata => NoteMeta(key: key, title: title, tags: tags,); Note.fromJson(Map json) : title = json['title'] ?? '', From 908eacc6d0ea467a27aeba2064b065eed2129efd Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sun, 10 Mar 2024 20:45:02 +0000 Subject: [PATCH 038/131] Notes tags UI implemented --- lib/screens/note_screen.dart | 98 ++++++++++++++++++++----- lib/screens/notes_screen.dart | 133 ++++++++++++++++++++++------------ 2 files changed, 167 insertions(+), 64 deletions(-) diff --git a/lib/screens/note_screen.dart b/lib/screens/note_screen.dart index ac279bd6..198a45f4 100644 --- a/lib/screens/note_screen.dart +++ b/lib/screens/note_screen.dart @@ -25,6 +25,9 @@ class NoteScreen extends StatefulWidget { class _NoteScreen extends State { final LoadedAccount _account = data.loadedAccount!; bool isFavorite = false; + Note? _note; + List _tags = []; + List _selected = []; void _onRemovePressed(Note note) { showDialog( @@ -71,23 +74,42 @@ class _NoteScreen extends State { ); } + Future _load() async { + List newTags = await _account.notesTags; + if (mounted) { + setState(() { + _tags = newTags; + _selected = _note!.tags.toList(); + for (String tag in _selected) { + if (_tags.contains(tag)) { + _tags.remove(tag); + } + } + }); + } + } + @override Widget build(BuildContext context) { - final Note _note = ModalRoute.of(context)!.settings.arguments as Note; + if (_note == null) { + _note = ModalRoute.of(context)!.settings.arguments as Note; + _load(); + } _account.reloadFavoritesSync(); - isFavorite = _account.favoriteNotes[_note.key]?.status == EntryStatus.alive; + isFavorite = + _account.favoriteNotes[_note!.key]?.status == EntryStatus.alive; return Scaffold( appBar: EntryScreenAppBar( entryType: EntryType.note, - entryKey: _note.key, + entryKey: _note!.key, title: Center(child: Text(localizations.note)), - onRemovePressed: () => _onRemovePressed(_note), - onEditPressed: () => _onEditPressed(_note), + onRemovePressed: () => _onRemovePressed(_note!), + onEditPressed: () => _onEditPressed(_note!), isFavorite: isFavorite, onFavoritePressed: () async { if (isFavorite) { - await _account.removeFavoriteNote(_note.key); + await _account.removeFavoriteNote(_note!.key); showSnackBar(context, message: localizations.removedFromFavorites, icon: const Icon( @@ -95,7 +117,7 @@ class _NoteScreen extends State { color: PassyTheme.darkContentColor, )); } else { - await _account.addFavoriteNote(_note.key); + await _account.addFavoriteNote(_note!.key); showSnackBar(context, message: localizations.addedToFavorites, icon: const Icon( @@ -107,29 +129,71 @@ class _NoteScreen extends State { }, ), body: ListView(children: [ - if (_note.title != '') + Center( + child: Padding( + padding: EdgeInsets.only( + top: PassyTheme.passyPadding.top / 2, + bottom: PassyTheme.passyPadding.bottom / 2), + child: EntryTagList( + showAddButton: true, + selected: _selected, + notSelected: _tags, + onAdded: (tag) async { + Navigator.pushNamed(context, SplashScreen.routeName); + _note!.tags = _selected.toList(); + _note!.tags.add(tag); + await _account.setNote(_note!); + Navigator.popUntil(context, + (route) => route.settings.name == NoteScreen.routeName); + if (!mounted) return; + setState(() { + _tags.remove(tag); + _selected.add(tag); + _selected.sort(); + _note!.tags = _selected; + }); + }, + onRemoved: (tag) async { + Navigator.pushNamed(context, SplashScreen.routeName); + _note!.tags = _selected.toList(); + _note!.tags.remove(tag); + await _account.setNote(_note!); + Navigator.popUntil(context, + (route) => route.settings.name == NoteScreen.routeName); + if (!mounted) return; + setState(() { + _tags.add(tag); + _tags.sort(); + _selected.remove(tag); + _note!.tags = _selected; + }); + }, + ), + ), + ), + if (_note!.title != '') PassyPadding( - RecordButton(title: localizations.title, value: _note.title)), - if (_note.attachments.isNotEmpty) - AttachmentsListView(files: _note.attachments), - if (_note.note != '') - if (!_note.isMarkdown) + RecordButton(title: localizations.title, value: _note!.title)), + if (_note!.attachments.isNotEmpty) + AttachmentsListView(files: _note!.attachments), + if (_note!.note != '') + if (!_note!.isMarkdown) PassyPadding(RecordButton( title: localizations.note, - value: _note.note, + value: _note!.note, valueAlign: TextAlign.left, )), - if (_note.isMarkdown) + if (_note!.isMarkdown) PassyPadding(Text( localizations.note, style: const TextStyle(color: PassyTheme.lightContentSecondaryColor), )), - if (_note.isMarkdown) + if (_note!.isMarkdown) Padding( padding: EdgeInsets.fromLTRB(20, PassyTheme.passyPadding.top, PassyTheme.passyPadding.right, PassyTheme.passyPadding.bottom), - child: PassyMarkdownBody(data: _note.note), + child: PassyMarkdownBody(data: _note!.note), ), ]), ); diff --git a/lib/screens/notes_screen.dart b/lib/screens/notes_screen.dart index 618b184d..8f6f42aa 100644 --- a/lib/screens/notes_screen.dart +++ b/lib/screens/notes_screen.dart @@ -23,65 +23,90 @@ class NotesScreen extends StatefulWidget { class _NotesScreen extends State { final LoadedAccount _account = data.loadedAccount!; + List _tags = []; void _onAddPressed() => Navigator.pushNamed(context, EditNoteScreen.routeName); - void _onSearchPressed() { - Navigator.pushNamed(context, SearchScreen.routeName, arguments: - SearchScreenArgs(builder: (String terms, List tags, void Function() rebuild) { - final List _found = []; - final List _terms = terms.trim().toLowerCase().split(' '); - for (NoteMeta _note in _account.notesMetadata.values) { - { - bool testNote(NoteMeta value) => _note.key == value.key; + void _onSearchPressed({String? tag}) { + Navigator.pushNamed( + context, + SearchScreen.routeName, + arguments: SearchScreenArgs( + notSelectedTags: _tags.toList()..remove(tag), + selectedTags: tag == null ? [] : [tag], + builder: (String terms, List tags, void Function() rebuild) { + final List _found = []; + final List _terms = terms.trim().toLowerCase().split(' '); + for (NoteMeta _note in _account.notesMetadata.values) { + { + bool testNote(NoteMeta value) => _note.key == value.key; - if (_found.any(testNote)) continue; - } - { - int _positiveCount = 0; - for (String _term in _terms) { - if (_note.title.toLowerCase().contains(_term)) { - _positiveCount++; - continue; + if (_found.any(testNote)) continue; + } + { + bool _tagMismatch = false; + for (String tag in tags) { + if (_note.tags.contains(tag)) continue; + _tagMismatch = true; + } + if (_tagMismatch) continue; + int _positiveCount = 0; + for (String _term in _terms) { + if (_note.title.toLowerCase().contains(_term)) { + _positiveCount++; + continue; + } + } + if (_positiveCount == _terms.length) { + _found.add(_note); + } } } - if (_positiveCount == _terms.length) { - _found.add(_note); - } - } - } - if (_found.isEmpty) { - return CustomScrollView( - slivers: [ - SliverFillRemaining( - hasScrollBody: false, - child: Column( - children: [ - const Spacer(flex: 7), - Text( - localizations.noSearchResults, - textAlign: TextAlign.center, + if (_found.isEmpty) { + return CustomScrollView( + slivers: [ + SliverFillRemaining( + hasScrollBody: false, + child: Column( + children: [ + const Spacer(flex: 7), + Text( + localizations.noSearchResults, + textAlign: TextAlign.center, + ), + const Spacer(flex: 7), + ], ), - const Spacer(flex: 7), - ], - ), - ), - ], - ); - } - return NoteButtonListView( - notes: _found, - shouldSort: true, - onPressed: (note) => Navigator.pushNamed(context, NoteScreen.routeName, - arguments: _account.getNote(note.key)), - popupMenuItemBuilder: notePopupMenuBuilder, - ); - })); + ), + ], + ); + } + return NoteButtonListView( + notes: _found, + shouldSort: true, + onPressed: (note) => Navigator.pushNamed( + context, NoteScreen.routeName, + arguments: _account.getNote(note.key)), + popupMenuItemBuilder: notePopupMenuBuilder, + ); + }, + ), + ); + } + + Future _load() async { + List newTags = await _account.notesTags; + if (mounted) { + setState(() { + _tags = newTags; + }); + } } @override Widget build(BuildContext context) { + _load(); List _notes = _account.notesMetadata.values.toList(); return Scaffold( appBar: EntriesScreenAppBar( @@ -126,6 +151,20 @@ class _NotesScreen extends State { Navigator.pushNamed(context, EditNoteScreen.routeName), ), ), + if (_tags.isNotEmpty) + Center( + child: Padding( + padding: EdgeInsets.only( + top: PassyTheme.passyPadding.top / 2, + bottom: PassyTheme.passyPadding.bottom / 2), + child: EntryTagList( + notSelected: _tags, + onAdded: (tag) => setState(() { + _onSearchPressed(tag: tag); + }), + ), + ), + ), ], notes: _notes, shouldSort: true, From 29f8330f61817aa333821eec98ffd33c2595f3fb Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sun, 10 Mar 2024 21:09:28 +0000 Subject: [PATCH 039/131] ID Cards tags UI implemented --- lib/screens/id_card_screen.dart | 115 ++++++++++++++++++------ lib/screens/id_cards_screen.dart | 149 +++++++++++++++++++------------ 2 files changed, 181 insertions(+), 83 deletions(-) diff --git a/lib/screens/id_card_screen.dart b/lib/screens/id_card_screen.dart index 326405cb..f9f37090 100644 --- a/lib/screens/id_card_screen.dart +++ b/lib/screens/id_card_screen.dart @@ -27,12 +27,33 @@ class IDCardScreen extends StatefulWidget { class _IDCardScreen extends State { final LoadedAccount _account = data.loadedAccount!; bool isFavorite = false; + IDCard? _idCard; + List _tags = []; + List _selected = []; + + Future _load() async { + List newTags = await _account.idCardsTags; + if (mounted) { + setState(() { + _tags = newTags; + _selected = _idCard!.tags.toList(); + for (String tag in _selected) { + if (_tags.contains(tag)) { + _tags.remove(tag); + } + } + }); + } + } @override Widget build(BuildContext context) { - final IDCard _idCard = ModalRoute.of(context)!.settings.arguments as IDCard; + if (_idCard == null) { + _idCard = ModalRoute.of(context)!.settings.arguments as IDCard; + _load(); + } isFavorite = - _account.favoriteIDCards[_idCard.key]?.status == EntryStatus.alive; + _account.favoriteIDCards[_idCard!.key]?.status == EntryStatus.alive; void _onRemovePressed() { showDialog( @@ -60,7 +81,7 @@ class _IDCardScreen extends State { ), onPressed: () { Navigator.pushNamed(context, SplashScreen.routeName); - _account.removeIDCard(_idCard.key).whenComplete(() { + _account.removeIDCard(_idCard!.key).whenComplete(() { Navigator.popUntil(context, (r) => r.settings.name == MainScreen.routeName); Navigator.pushNamed(context, IDCardsScreen.routeName); @@ -76,21 +97,21 @@ class _IDCardScreen extends State { Navigator.pushNamed( context, EditIDCardScreen.routeName, - arguments: _idCard, + arguments: _idCard!, ); } return Scaffold( appBar: EntryScreenAppBar( entryType: EntryType.idCard, - entryKey: _idCard.key, + entryKey: _idCard!.key, title: Center(child: Text(localizations.idCard)), onRemovePressed: () => _onRemovePressed(), onEditPressed: () => _onEditPressed(), isFavorite: isFavorite, onFavoritePressed: () async { if (isFavorite) { - await _account.removeFavoriteIDCard(_idCard.key); + await _account.removeFavoriteIDCard(_idCard!.key); showSnackBar(context, message: localizations.removedFromFavorites, icon: const Icon( @@ -98,7 +119,7 @@ class _IDCardScreen extends State { color: PassyTheme.darkContentColor, )); } else { - await _account.addFavoriteIDCard(_idCard.key); + await _account.addFavoriteIDCard(_idCard!.key); showSnackBar(context, message: localizations.addedToFavorites, icon: const Icon( @@ -111,43 +132,85 @@ class _IDCardScreen extends State { ), body: ListView( children: [ - if (_idCard.attachments.isNotEmpty) - AttachmentsListView(files: _idCard.attachments), - if (_idCard.nickname != '') + Center( + child: Padding( + padding: EdgeInsets.only( + top: PassyTheme.passyPadding.top / 2, + bottom: PassyTheme.passyPadding.bottom / 2), + child: EntryTagList( + showAddButton: true, + selected: _selected, + notSelected: _tags, + onAdded: (tag) async { + Navigator.pushNamed(context, SplashScreen.routeName); + _idCard!.tags = _selected.toList(); + _idCard!.tags.add(tag); + await _account.setIDCard(_idCard!); + Navigator.popUntil(context, + (route) => route.settings.name == IDCardScreen.routeName); + if (!mounted) return; + setState(() { + _tags.remove(tag); + _selected.add(tag); + _selected.sort(); + _idCard!.tags = _selected; + }); + }, + onRemoved: (tag) async { + Navigator.pushNamed(context, SplashScreen.routeName); + _idCard!.tags = _selected.toList(); + _idCard!.tags.remove(tag); + await _account.setIDCard(_idCard!); + Navigator.popUntil(context, + (route) => route.settings.name == IDCardScreen.routeName); + if (!mounted) return; + setState(() { + _tags.add(tag); + _tags.sort(); + _selected.remove(tag); + _idCard!.tags = _selected; + }); + }, + ), + ), + ), + if (_idCard!.attachments.isNotEmpty) + AttachmentsListView(files: _idCard!.attachments), + if (_idCard!.nickname != '') PassyPadding(RecordButton( title: localizations.nickname, - value: _idCard.nickname, + value: _idCard!.nickname, )), - if (_idCard.type != '') + if (_idCard!.type != '') PassyPadding(RecordButton( title: localizations.type, - value: _idCard.type, + value: _idCard!.type, )), - if (_idCard.idNumber != '') + if (_idCard!.idNumber != '') PassyPadding(RecordButton( title: localizations.idNumber, - value: _idCard.idNumber, + value: _idCard!.idNumber, )), - if (_idCard.name != '') + if (_idCard!.name != '') PassyPadding(RecordButton( title: localizations.name, - value: _idCard.name, + value: _idCard!.name, )), - if (_idCard.country != '') + if (_idCard!.country != '') PassyPadding(RecordButton( - title: localizations.country, value: _idCard.country)), - if (_idCard.issDate != '') + title: localizations.country, value: _idCard!.country)), + if (_idCard!.issDate != '') PassyPadding(RecordButton( - title: localizations.dateOfIssue, value: _idCard.issDate)), - if (_idCard.expDate != '') + title: localizations.dateOfIssue, value: _idCard!.issDate)), + if (_idCard!.expDate != '') PassyPadding(RecordButton( - title: localizations.expirationDate, value: _idCard.expDate)), - for (CustomField _customField in _idCard.customFields) + title: localizations.expirationDate, value: _idCard!.expDate)), + for (CustomField _customField in _idCard!.customFields) PassyPadding(CustomFieldButton(customField: _customField)), - if (_idCard.additionalInfo != '') + if (_idCard!.additionalInfo != '') PassyPadding(RecordButton( title: localizations.additionalInfo, - value: _idCard.additionalInfo)), + value: _idCard!.additionalInfo)), ], ), ); diff --git a/lib/screens/id_cards_screen.dart b/lib/screens/id_cards_screen.dart index feeb5cf0..64223f90 100644 --- a/lib/screens/id_cards_screen.dart +++ b/lib/screens/id_cards_screen.dart @@ -23,72 +23,93 @@ class IDCardsScreen extends StatefulWidget { class _IDCardsScreen extends State { final LoadedAccount _account = data.loadedAccount!; + List _tags = []; void _onAddPressed() => Navigator.pushNamed(context, EditIDCardScreen.routeName); - void _onSearchPressed() { - Navigator.pushNamed(context, SearchScreen.routeName, arguments: - SearchScreenArgs(builder: (String terms, List tags, void Function() rebuild) { - final List _found = []; - final List _terms = terms.trim().toLowerCase().split(' '); - for (IDCardMeta _idCard in _account.idCardsMetadata.values) { - { - bool testIDCard(IDCardMeta value) => _idCard.key == value.key; + void _onSearchPressed({String? tag}) { + Navigator.pushNamed(context, SearchScreen.routeName, + arguments: SearchScreenArgs( + notSelectedTags: _tags.toList()..remove(tag), + selectedTags: tag == null ? [] : [tag], + builder: + (String terms, List tags, void Function() rebuild) { + final List _found = []; + final List _terms = terms.trim().toLowerCase().split(' '); + for (IDCardMeta _idCard in _account.idCardsMetadata.values) { + { + bool testIDCard(IDCardMeta value) => _idCard.key == value.key; - if (_found.any(testIDCard)) continue; - } - { - int _positiveCount = 0; - for (String _term in _terms) { - if (_idCard.nickname.toLowerCase().contains(_term)) { - _positiveCount++; - continue; - } - if (_idCard.name.toLowerCase().contains(_term)) { - _positiveCount++; - continue; - } - } - if (_positiveCount == _terms.length) { - _found.add(_idCard); - } - } - } - if (_found.isEmpty) { - return CustomScrollView( - slivers: [ - SliverFillRemaining( - hasScrollBody: false, - child: Column( - children: [ - const Spacer(flex: 7), - Text( - localizations.noSearchResults, - textAlign: TextAlign.center, - ), - const Spacer(flex: 7), - ], - ), - ), - ], - ); - } - return IDCardButtonListView( - idCards: _found, - shouldSort: true, - onPressed: (idCard) => Navigator.pushNamed( - context, - IDCardScreen.routeName, - arguments: _account.getIDCard(idCard.key), - ), - popupMenuItemBuilder: idCardPopupMenuBuilder, - ); - })); + if (_found.any(testIDCard)) continue; + } + { + bool _tagMismatch = false; + for (String tag in tags) { + if (_idCard.tags.contains(tag)) continue; + _tagMismatch = true; + } + if (_tagMismatch) continue; + int _positiveCount = 0; + for (String _term in _terms) { + if (_idCard.nickname.toLowerCase().contains(_term)) { + _positiveCount++; + continue; + } + if (_idCard.name.toLowerCase().contains(_term)) { + _positiveCount++; + continue; + } + } + if (_positiveCount == _terms.length) { + _found.add(_idCard); + } + } + } + if (_found.isEmpty) { + return CustomScrollView( + slivers: [ + SliverFillRemaining( + hasScrollBody: false, + child: Column( + children: [ + const Spacer(flex: 7), + Text( + localizations.noSearchResults, + textAlign: TextAlign.center, + ), + const Spacer(flex: 7), + ], + ), + ), + ], + ); + } + return IDCardButtonListView( + idCards: _found, + shouldSort: true, + onPressed: (idCard) => Navigator.pushNamed( + context, + IDCardScreen.routeName, + arguments: _account.getIDCard(idCard.key), + ), + popupMenuItemBuilder: idCardPopupMenuBuilder, + ); + })); + } + + Future _load() async { + List newTags = await _account.idCardsTags; + if (mounted) { + setState(() { + _tags = newTags; + }); + } } @override Widget build(BuildContext context) { + _load(); List _idCards = _account.idCardsMetadata.values.toList(); return Scaffold( appBar: EntriesScreenAppBar( @@ -133,6 +154,20 @@ class _IDCardsScreen extends State { context, EditIDCardScreen.routeName), ), ), + if (_tags.isNotEmpty) + Center( + child: Padding( + padding: EdgeInsets.only( + top: PassyTheme.passyPadding.top / 2, + bottom: PassyTheme.passyPadding.bottom / 2), + child: EntryTagList( + notSelected: _tags, + onAdded: (tag) => setState(() { + _onSearchPressed(tag: tag); + }), + ), + ), + ), ], idCards: _idCards, shouldSort: true, From 25a46314efdd1c1753cabced7e9e61fe719fb42c Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sun, 10 Mar 2024 21:27:27 +0000 Subject: [PATCH 040/131] Identities tags UI implemented --- lib/screens/identities_screen.dart | 35 ++++++- lib/screens/identity_screen.dart | 142 +++++++++++++++++++++-------- 2 files changed, 137 insertions(+), 40 deletions(-) diff --git a/lib/screens/identities_screen.dart b/lib/screens/identities_screen.dart index de9271b7..256a4664 100644 --- a/lib/screens/identities_screen.dart +++ b/lib/screens/identities_screen.dart @@ -22,13 +22,20 @@ class IdentitiesScreen extends StatefulWidget { class _IdentitiesScreen extends State { final _account = data.loadedAccount!; + List _tags = []; void _onAddPressed() => Navigator.pushNamed(context, EditIdentityScreen.routeName); - void _onSearchPressed() { + void _onSearchPressed({String? tag}) { Navigator.pushNamed(context, SearchScreen.routeName, arguments: - SearchScreenArgs(builder: (String terms, List tags, void Function() rebuild) { + SearchScreenArgs( + + notSelectedTags: _tags.toList()..remove(tag), + selectedTags: tag == null ? [] : [tag], + + builder: + (String terms, List tags, void Function() rebuild) { final List _found = []; final List _terms = terms.trim().toLowerCase().split(' '); for (IdentityMeta _identity in _account.identitiesMetadata.values) { @@ -86,8 +93,18 @@ class _IdentitiesScreen extends State { })); } + Future _load() async { + List newTags = await _account.identitiesTags; + if (mounted) { + setState(() { + _tags = newTags; + }); + } + } + @override Widget build(BuildContext context) { + _load(); List _identities = _account.identitiesMetadata.values.toList(); return Scaffold( @@ -133,6 +150,20 @@ class _IdentitiesScreen extends State { context, EditIdentityScreen.routeName), ), ), + if (_tags.isNotEmpty) + Center( + child: Padding( + padding: EdgeInsets.only( + top: PassyTheme.passyPadding.top / 2, + bottom: PassyTheme.passyPadding.bottom / 2), + child: EntryTagList( + notSelected: _tags, + onAdded: (tag) => setState(() { + _onSearchPressed(tag: tag); + }), + ), + ), + ), ], identities: _identities, shouldSort: true, diff --git a/lib/screens/identity_screen.dart b/lib/screens/identity_screen.dart index e5bbc157..073acbec 100644 --- a/lib/screens/identity_screen.dart +++ b/lib/screens/identity_screen.dart @@ -27,14 +27,34 @@ class IdentityScreen extends StatefulWidget { class _IdentityScreen extends State { final LoadedAccount _account = data.loadedAccount!; bool isFavorite = false; + Identity? _identity; + List _tags = []; + List _selected = []; + + Future _load() async { + List newTags = await _account.identitiesTags; + if (mounted) { + setState(() { + _tags = newTags; + _selected = _identity!.tags.toList(); + for (String tag in _selected) { + if (_tags.contains(tag)) { + _tags.remove(tag); + } + } + }); + } + } @override Widget build(BuildContext context) { - final Identity _identity = - ModalRoute.of(context)!.settings.arguments as Identity; + if (_identity == null) { + _identity = ModalRoute.of(context)!.settings.arguments as Identity; + _load(); + } _account.reloadFavoritesSync(); - isFavorite = - _account.favoriteIdentities[_identity.key]?.status == EntryStatus.alive; + isFavorite = _account.favoriteIdentities[_identity!.key]?.status == + EntryStatus.alive; void _onRemovePressed() { showDialog( @@ -61,7 +81,7 @@ class _IdentityScreen extends State { ), onPressed: () { Navigator.pushNamed(context, SplashScreen.routeName); - _account.removeIdentity(_identity.key).whenComplete(() { + _account.removeIdentity(_identity!.key).whenComplete(() { Navigator.popUntil(context, (r) => r.settings.name == MainScreen.routeName); Navigator.pushNamed(context, IdentitiesScreen.routeName); @@ -78,21 +98,21 @@ class _IdentityScreen extends State { Navigator.pushNamed( context, EditIdentityScreen.routeName, - arguments: _identity, + arguments: _identity!, ); } return Scaffold( appBar: EntryScreenAppBar( entryType: EntryType.identity, - entryKey: _identity.key, + entryKey: _identity!.key, title: Center(child: Text(localizations.identity)), onRemovePressed: _onRemovePressed, onEditPressed: _onEditPressed, isFavorite: isFavorite, onFavoritePressed: () async { if (isFavorite) { - await _account.removeFavoriteIdentity(_identity.key); + await _account.removeFavoriteIdentity(_identity!.key); showSnackBar(context, message: localizations.removedFromFavorites, icon: const Icon( @@ -100,7 +120,7 @@ class _IdentityScreen extends State { color: PassyTheme.darkContentColor, )); } else { - await _account.addFavoriteIdentity(_identity.key); + await _account.addFavoriteIdentity(_identity!.key); showSnackBar(context, message: localizations.addedToFavorites, icon: const Icon( @@ -113,74 +133,120 @@ class _IdentityScreen extends State { ), body: ListView( children: [ - if (_identity.attachments.isNotEmpty) - AttachmentsListView(files: _identity.attachments), - if (_identity.nickname != '') + Center( + child: Padding( + padding: EdgeInsets.only( + top: PassyTheme.passyPadding.top / 2, + bottom: PassyTheme.passyPadding.bottom / 2), + child: EntryTagList( + showAddButton: true, + selected: _selected, + notSelected: _tags, + onAdded: (tag) async { + Navigator.pushNamed(context, SplashScreen.routeName); + _identity!.tags = _selected.toList(); + _identity!.tags.add(tag); + await _account.setIdentity(_identity!); + Navigator.popUntil( + context, + (route) => + route.settings.name == IdentityScreen.routeName); + if (!mounted) return; + setState(() { + _tags.remove(tag); + _selected.add(tag); + _selected.sort(); + _identity!.tags = _selected; + }); + }, + onRemoved: (tag) async { + Navigator.pushNamed(context, SplashScreen.routeName); + _identity!.tags = _selected.toList(); + _identity!.tags.remove(tag); + await _account.setIdentity(_identity!); + Navigator.popUntil( + context, + (route) => + route.settings.name == IdentityScreen.routeName); + if (!mounted) return; + setState(() { + _tags.add(tag); + _tags.sort(); + _selected.remove(tag); + _identity!.tags = _selected; + }); + }, + ), + ), + ), + if (_identity!.attachments.isNotEmpty) + AttachmentsListView(files: _identity!.attachments), + if (_identity!.nickname != '') PassyPadding(RecordButton( title: localizations.nickname, - value: _identity.nickname, + value: _identity!.nickname, )), PassyPadding(RecordButton( title: localizations.title, - value: capitalize(_identity.title.name))), - if (_identity.firstName != '') + value: capitalize(_identity!.title.name))), + if (_identity!.firstName != '') PassyPadding(RecordButton( title: localizations.firstName, - value: _identity.firstName, + value: _identity!.firstName, )), - if (_identity.middleName != '') + if (_identity!.middleName != '') PassyPadding(RecordButton( title: localizations.middleName, - value: _identity.middleName, + value: _identity!.middleName, )), - if (_identity.lastName != '') + if (_identity!.lastName != '') PassyPadding(RecordButton( title: localizations.lastName, - value: _identity.lastName, + value: _identity!.lastName, )), PassyPadding(RecordButton( title: localizations.gender, - value: genderToReadableName(_identity.gender), + value: genderToReadableName(_identity!.gender), )), - if (_identity.email != '') + if (_identity!.email != '') PassyPadding(RecordButton( title: localizations.email, - value: _identity.email, + value: _identity!.email, )), - if (_identity.number != '') + if (_identity!.number != '') PassyPadding(RecordButton( title: localizations.phoneNumber, - value: _identity.number, + value: _identity!.number, )), - if (_identity.firstAddressLine != '') + if (_identity!.firstAddressLine != '') PassyPadding(RecordButton( title: localizations.firstAddresssLine, - value: _identity.firstAddressLine)), - if (_identity.secondAddressLine != '') + value: _identity!.firstAddressLine)), + if (_identity!.secondAddressLine != '') PassyPadding(RecordButton( title: localizations.secondAddressLine, - value: _identity.secondAddressLine)), - if (_identity.zipCode != '') + value: _identity!.secondAddressLine)), + if (_identity!.zipCode != '') PassyPadding(RecordButton( title: localizations.zipCode, - value: _identity.zipCode, + value: _identity!.zipCode, )), - if (_identity.city != '') + if (_identity!.city != '') PassyPadding(RecordButton( title: localizations.city, - value: _identity.city, + value: _identity!.city, )), - if (_identity.country != '') + if (_identity!.country != '') PassyPadding(RecordButton( title: localizations.country, - value: _identity.country, + value: _identity!.country, )), - for (CustomField _customField in _identity.customFields) + for (CustomField _customField in _identity!.customFields) PassyPadding(CustomFieldButton(customField: _customField)), - if (_identity.additionalInfo != '') + if (_identity!.additionalInfo != '') PassyPadding(RecordButton( title: localizations.additionalInfo, - value: _identity.additionalInfo)), + value: _identity!.additionalInfo)), ], ), ); From eadf55d663a91422202082325af24566c0342287 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sun, 10 Mar 2024 21:47:39 +0000 Subject: [PATCH 041/131] Payment Cards tags UI implemented --- lib/screens/payment_card_screen.dart | 115 +++++++++++++++----- lib/screens/payment_cards_screen.dart | 149 ++++++++++++++++---------- 2 files changed, 183 insertions(+), 81 deletions(-) diff --git a/lib/screens/payment_card_screen.dart b/lib/screens/payment_card_screen.dart index 1338652c..9adb9233 100644 --- a/lib/screens/payment_card_screen.dart +++ b/lib/screens/payment_card_screen.dart @@ -26,6 +26,9 @@ class PaymentCardScreen extends StatefulWidget { class _PaymentCardScreen extends State { final _account = data.loadedAccount!; bool isFavorite = false; + PaymentCard? _paymentCard; + List _tags = []; + List _selected = []; void _onRemovePressed(PaymentCard paymentCard) { showDialog( @@ -73,25 +76,42 @@ class _PaymentCardScreen extends State { ); } + Future _load() async { + List newTags = await _account.paymentCardTags; + if (mounted) { + setState(() { + _tags = newTags; + _selected = _paymentCard!.tags.toList(); + for (String tag in _selected) { + if (_tags.contains(tag)) { + _tags.remove(tag); + } + } + }); + } + } + @override Widget build(BuildContext context) { - final PaymentCard _paymentCard = - ModalRoute.of(context)!.settings.arguments as PaymentCard; + if (_paymentCard == null) { + _paymentCard = ModalRoute.of(context)!.settings.arguments as PaymentCard; + _load(); + } _account.reloadFavoritesSync(); - isFavorite = _account.favoritePaymentCards[_paymentCard.key]?.status == + isFavorite = _account.favoritePaymentCards[_paymentCard!.key]?.status == EntryStatus.alive; return Scaffold( appBar: EntryScreenAppBar( entryType: EntryType.paymentCard, - entryKey: _paymentCard.key, + entryKey: _paymentCard!.key, title: Center(child: Text(localizations.paymentCard)), - onRemovePressed: () => _onRemovePressed(_paymentCard), - onEditPressed: () => _onEditPressed(_paymentCard), + onRemovePressed: () => _onRemovePressed(_paymentCard!), + onEditPressed: () => _onEditPressed(_paymentCard!), isFavorite: isFavorite, onFavoritePressed: () async { if (isFavorite) { - await _account.removeFavoritePaymentCard(_paymentCard.key); + await _account.removeFavoritePaymentCard(_paymentCard!.key); showSnackBar(context, message: localizations.removedFromFavorites, icon: const Icon( @@ -99,7 +119,7 @@ class _PaymentCardScreen extends State { color: PassyTheme.darkContentColor, )); } else { - await _account.addFavoritePaymentCard(_paymentCard.key); + await _account.addFavoritePaymentCard(_paymentCard!.key); showSnackBar(context, message: localizations.addedToFavorites, icon: const Icon( @@ -112,45 +132,92 @@ class _PaymentCardScreen extends State { ), body: ListView(children: [ PaymentCardButton( - paymentCard: _paymentCard.uncensoredMetadata, + paymentCard: _paymentCard!.uncensoredMetadata, obscureCardNumber: false, obscureCardCvv: false, isSwipeGestureEnabled: false, ), - if (_paymentCard.attachments.isNotEmpty) - AttachmentsListView(files: _paymentCard.attachments), - if (_paymentCard.nickname != '') + + Center( + child: Padding( + padding: EdgeInsets.only( + top: PassyTheme.passyPadding.top / 2, + bottom: PassyTheme.passyPadding.bottom / 2), + child: EntryTagList( + showAddButton: true, + selected: _selected, + notSelected: _tags, + onAdded: (tag) async { + Navigator.pushNamed(context, SplashScreen.routeName); + _paymentCard!.tags = _selected.toList(); + _paymentCard!.tags.add(tag); + await _account.setPaymentCard(_paymentCard!); + Navigator.popUntil( + context, + (route) => + route.settings.name == PaymentCardScreen.routeName); + if (!mounted) return; + setState(() { + _tags.remove(tag); + _selected.add(tag); + _selected.sort(); + _paymentCard!.tags = _selected; + }); + }, + onRemoved: (tag) async { + Navigator.pushNamed(context, SplashScreen.routeName); + _paymentCard!.tags = _selected.toList(); + _paymentCard!.tags.remove(tag); + await _account.setPaymentCard(_paymentCard!); + Navigator.popUntil( + context, + (route) => + route.settings.name == PaymentCardScreen.routeName); + if (!mounted) return; + setState(() { + _tags.add(tag); + _tags.sort(); + _selected.remove(tag); + _paymentCard!.tags = _selected; + }); + }, + ), + ), + ), + if (_paymentCard!.attachments.isNotEmpty) + AttachmentsListView(files: _paymentCard!.attachments), + if (_paymentCard!.nickname != '') PassyPadding(RecordButton( title: localizations.nickname, - value: _paymentCard.nickname, + value: _paymentCard!.nickname, )), - if (_paymentCard.cardNumber != '') + if (_paymentCard!.cardNumber != '') PassyPadding(RecordButton( title: localizations.cardNumber, - value: _paymentCard.cardNumber, + value: _paymentCard!.cardNumber, )), - if (_paymentCard.cardholderName != '') + if (_paymentCard!.cardholderName != '') PassyPadding(RecordButton( title: localizations.cardHolderName, - value: _paymentCard.cardholderName, + value: _paymentCard!.cardholderName, )), - if (_paymentCard.exp != '') + if (_paymentCard!.exp != '') PassyPadding(RecordButton( title: localizations.expirationDate, - value: _paymentCard.exp, + value: _paymentCard!.exp, )), - if (_paymentCard.cvv != '') + if (_paymentCard!.cvv != '') PassyPadding(RecordButton( title: 'CVV', - value: _paymentCard.cvv, + value: _paymentCard!.cvv, obscureValue: true, )), - for (CustomField _customField in _paymentCard.customFields) + for (CustomField _customField in _paymentCard!.customFields) PassyPadding(CustomFieldButton(customField: _customField)), - if (_paymentCard.additionalInfo != '') + if (_paymentCard!.additionalInfo != '') PassyPadding(RecordButton( title: localizations.additionalInfo, - value: _paymentCard.additionalInfo, + value: _paymentCard!.additionalInfo, )), ]), ); diff --git a/lib/screens/payment_cards_screen.dart b/lib/screens/payment_cards_screen.dart index fe6df048..9b5f1b4a 100644 --- a/lib/screens/payment_cards_screen.dart +++ b/lib/screens/payment_cards_screen.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:passy/common/common.dart'; import 'package:passy/passy_data/loaded_account.dart'; import 'package:passy/passy_data/payment_card.dart'; -import 'package:passy/passy_flutter/widgets/widgets.dart'; +import 'package:passy/passy_flutter/passy_flutter.dart'; import '../passy_data/entry_type.dart'; import 'edit_payment_card_screen.dart'; @@ -21,78 +21,99 @@ class PaymentCardsScreen extends StatefulWidget { class _PaymentCardsScreen extends State { final LoadedAccount _account = data.loadedAccount!; + List _tags = []; - void _onSearchPressed() { + void _onSearchPressed({String? tag}) { Navigator.pushNamed(context, SearchScreen.routeName, arguments: SearchScreenArgs( - builder: (String terms, List tags, void Function() rebuild) { - final List _found = []; - final List _terms = terms.trim().toLowerCase().split(' '); - for (PaymentCardMeta _paymentCard - in data.loadedAccount!.paymentCardsMetadata.values) { - { - bool testPaymentCard(PaymentCardMeta value) => - _paymentCard.key == value.key; + notSelectedTags: _tags.toList()..remove(tag), + selectedTags: tag == null ? [] : [tag], + builder: (String terms, List tags, void Function() rebuild) { + final List _found = []; + final List _terms = terms.trim().toLowerCase().split(' '); + for (PaymentCardMeta _paymentCard + in data.loadedAccount!.paymentCardsMetadata.values) { + { + bool testPaymentCard(PaymentCardMeta value) => + _paymentCard.key == value.key; - if (_found.any(testPaymentCard)) continue; - } - { - int _positiveCount = 0; - for (String _term in _terms) { - if (_paymentCard.cardholderName.toLowerCase().contains(_term)) { - _positiveCount++; - continue; + if (_found.any(testPaymentCard)) continue; } - if (_paymentCard.nickname.toLowerCase().contains(_term)) { - _positiveCount++; - continue; + { + int _positiveCount = 0; + bool _tagMismatch = false; + for (String tag in tags) { + if (_paymentCard.tags.contains(tag)) continue; + _tagMismatch = true; + } + if (_tagMismatch) continue; + for (String _term in _terms) { + if (_paymentCard.cardholderName + .toLowerCase() + .contains(_term)) { + _positiveCount++; + continue; + } + if (_paymentCard.nickname.toLowerCase().contains(_term)) { + _positiveCount++; + continue; + } + if (_paymentCard.exp.toLowerCase().contains(_term)) { + _positiveCount++; + continue; + } + } + if (_positiveCount == _terms.length) { + _found.add(_paymentCard); + } } - if (_paymentCard.exp.toLowerCase().contains(_term)) { - _positiveCount++; - continue; - } - } - if (_positiveCount == _terms.length) { - _found.add(_paymentCard); } - } - } - if (_found.isEmpty) { - return CustomScrollView( - slivers: [ - SliverFillRemaining( - hasScrollBody: false, - child: Column( - children: [ - const Spacer(flex: 7), - Text( - localizations.noSearchResults, - textAlign: TextAlign.center, + if (_found.isEmpty) { + return CustomScrollView( + slivers: [ + SliverFillRemaining( + hasScrollBody: false, + child: Column( + children: [ + const Spacer(flex: 7), + Text( + localizations.noSearchResults, + textAlign: TextAlign.center, + ), + const Spacer(flex: 7), + ], ), - const Spacer(flex: 7), - ], - ), - ), - ], - ); - } - return PaymentCardButtonListView( - paymentCards: _found, - shouldSort: true, - onPressed: (paymentCard) => { - Navigator.pushNamed(context, PaymentCardScreen.routeName, - arguments: _account.getPaymentCard(paymentCard.key)), + ), + ], + ); + } + return PaymentCardButtonListView( + paymentCards: _found, + shouldSort: true, + onPressed: (paymentCard) => { + Navigator.pushNamed(context, PaymentCardScreen.routeName, + arguments: _account.getPaymentCard(paymentCard.key)), + }, + ); }, - ); - }, - )); + )); } void _onAddPressed() => Navigator.pushNamed(context, EditPaymentCardScreen.routeName); + Future _load() async { + List newTags = await _account.paymentCardTags; + if (mounted) { + setState(() { + _tags = newTags; + }); + } + } + @override Widget build(BuildContext context) { + _load(); List _paymentCards = _account.paymentCardsMetadata.values.toList(); return Scaffold( @@ -139,6 +160,20 @@ class _PaymentCardsScreen extends State { context, EditPaymentCardScreen.routeName), ), ), + if (_tags.isNotEmpty) + Center( + child: Padding( + padding: EdgeInsets.only( + top: PassyTheme.passyPadding.top / 2, + bottom: PassyTheme.passyPadding.bottom / 2), + child: EntryTagList( + notSelected: _tags, + onAdded: (tag) => setState(() { + _onSearchPressed(tag: tag); + }), + ), + ), + ), ], paymentCards: _paymentCards, shouldSort: true, From a8b3a8d04d24d8bae4562f1af54c59b549079794 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sun, 10 Mar 2024 21:57:07 +0000 Subject: [PATCH 042/131] Update app_en.arb --- lib/l10n/app_en.arb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 74ad8b53..8bab339c 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -343,5 +343,6 @@ "@counter": { "description": "HOTP 2FA counter stores a number that is incremented on every logon" }, "createTag": "Create tag", "create": "Create", - "tag": "Tag" + "tag": "Tag", + "noTags": "No tags" } From 393df61a4a6555415f9ef86516ff9889274977b6 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sun, 10 Mar 2024 21:57:09 +0000 Subject: [PATCH 043/131] Update entry_tag_list.dart --- lib/passy_flutter/widgets/entry_tag_list.dart | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/passy_flutter/widgets/entry_tag_list.dart b/lib/passy_flutter/widgets/entry_tag_list.dart index 97ee4c06..e970cbc4 100644 --- a/lib/passy_flutter/widgets/entry_tag_list.dart +++ b/lib/passy_flutter/widgets/entry_tag_list.dart @@ -13,8 +13,8 @@ class EntryTagList extends StatefulWidget { const EntryTagList({ super.key, - this.selected = const [], - this.notSelected = const [], + this.selected = const [], + this.notSelected = const [], void Function(String tag)? onAdded, void Function(String tag)? onRemoved, void Function()? onAddPressed, @@ -98,6 +98,14 @@ class _EntryTagList extends State { children: [ Row( children: [ + if (selectedButtons.isEmpty && notSelectedButtons.isEmpty) + Padding( + padding: EdgeInsets.only( + left: PassyTheme.passyPadding.left / 2, + right: PassyTheme.passyPadding.right / 2, + bottom: showScrollbar ? 14 : 0), + child: Text(localizations.noTags), + ), if (widget.showAddButton) Padding( padding: EdgeInsets.only(bottom: showScrollbar ? 14 : 0), From 4a1c390d1ead905caa1b2366d1abbacc35486cba Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sun, 10 Mar 2024 22:03:44 +0000 Subject: [PATCH 044/131] Update encrypted_json_file.dart --- lib/passy_data/encrypted_json_file.dart | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/passy_data/encrypted_json_file.dart b/lib/passy_data/encrypted_json_file.dart index c75c25ba..5c336649 100644 --- a/lib/passy_data/encrypted_json_file.dart +++ b/lib/passy_data/encrypted_json_file.dart @@ -50,8 +50,11 @@ class EncryptedJsonFile with SaveableFileBase { Future reload() async => value = _fromJson( jsonDecode(decrypt(await _file.readAsString(), encrypter: _encrypter))); - void reloadSync() => value = _fromJson( - jsonDecode(decrypt(_file.readAsStringSync(), encrypter: _encrypter))); + void reloadSync() { + String read = decrypt(_file.readAsStringSync(), encrypter: _encrypter); + if (read.isEmpty) return; + value = _fromJson(jsonDecode(read)); + } @override Future save() => From bbeed5eb8703b4fbe0fdc5a6a20b3ce5179b60ed Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sun, 10 Mar 2024 22:05:45 +0000 Subject: [PATCH 045/131] Update entry_tag_list.dart --- lib/passy_flutter/widgets/entry_tag_list.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/passy_flutter/widgets/entry_tag_list.dart b/lib/passy_flutter/widgets/entry_tag_list.dart index e970cbc4..4bd11abf 100644 --- a/lib/passy_flutter/widgets/entry_tag_list.dart +++ b/lib/passy_flutter/widgets/entry_tag_list.dart @@ -126,6 +126,10 @@ class _EntryTagList extends State { decoration: InputDecoration( labelText: localizations.tag), onChanged: (value) => {tag = value}, + onFieldSubmitted: (tag) { + widget.onAdded(tag); + Navigator.pop(context); + }, ), actions: [ TextButton( From 502db8584b4b15f05cdd991dc9f0544cce959e58 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sun, 10 Mar 2024 23:30:41 +0000 Subject: [PATCH 046/131] Tags search repaired --- lib/screens/id_cards_screen.dart | 10 +- lib/screens/identities_screen.dart | 141 ++++++++++++++------------ lib/screens/notes_screen.dart | 12 ++- lib/screens/passwords_screen.dart | 12 ++- lib/screens/payment_cards_screen.dart | 10 +- 5 files changed, 112 insertions(+), 73 deletions(-) diff --git a/lib/screens/id_cards_screen.dart b/lib/screens/id_cards_screen.dart index 64223f90..50ade077 100644 --- a/lib/screens/id_cards_screen.dart +++ b/lib/screens/id_cards_screen.dart @@ -99,7 +99,12 @@ class _IDCardsScreen extends State { } Future _load() async { - List newTags = await _account.idCardsTags; + List newTags; + try { + newTags = await _account.idCardsTags; + } catch (_) { + return; + } if (mounted) { setState(() { _tags = newTags; @@ -110,7 +115,8 @@ class _IDCardsScreen extends State { @override Widget build(BuildContext context) { _load(); - List _idCards = _account.idCardsMetadata.values.toList(); + List _idCards = []; + try{_idCards = _account.idCardsMetadata.values.toList();}catch(_){} return Scaffold( appBar: EntriesScreenAppBar( entryType: EntryType.idCard, diff --git a/lib/screens/identities_screen.dart b/lib/screens/identities_screen.dart index 256a4664..a3c9cdfe 100644 --- a/lib/screens/identities_screen.dart +++ b/lib/screens/identities_screen.dart @@ -28,73 +28,86 @@ class _IdentitiesScreen extends State { Navigator.pushNamed(context, EditIdentityScreen.routeName); void _onSearchPressed({String? tag}) { - Navigator.pushNamed(context, SearchScreen.routeName, arguments: - SearchScreenArgs( - + Navigator.pushNamed(context, SearchScreen.routeName, + arguments: SearchScreenArgs( notSelectedTags: _tags.toList()..remove(tag), selectedTags: tag == null ? [] : [tag], + builder: + (String terms, List tags, void Function() rebuild) { + final List _found = []; + final List _terms = terms.trim().toLowerCase().split(' '); + for (IdentityMeta _identity + in _account.identitiesMetadata.values) { + { + bool testIdentity(IdentityMeta value) => + _identity.key == value.key; - builder: - (String terms, List tags, void Function() rebuild) { - final List _found = []; - final List _terms = terms.trim().toLowerCase().split(' '); - for (IdentityMeta _identity in _account.identitiesMetadata.values) { - { - bool testIdentity(IdentityMeta value) => _identity.key == value.key; - - if (_found.any(testIdentity)) continue; - } - { - int _positiveCount = 0; - for (String _term in _terms) { - if (_identity.firstAddressLine.toLowerCase().contains(_term)) { - _positiveCount++; - continue; - } - if (_identity.nickname.toLowerCase().contains(_term)) { - _positiveCount++; - continue; - } - } - if (_positiveCount == _terms.length) { - _found.add(_identity); - } - } - } - if (_found.isEmpty) { - return CustomScrollView( - slivers: [ - SliverFillRemaining( - hasScrollBody: false, - child: Column( - children: [ - const Spacer(flex: 7), - Text( - localizations.noSearchResults, - textAlign: TextAlign.center, - ), - const Spacer(flex: 7), - ], - ), - ), - ], - ); - } - return IdentityButtonListView( - identities: _found, - shouldSort: true, - onPressed: (identity) => Navigator.pushNamed( - context, - IdentityScreen.routeName, - arguments: _account.getIdentity(identity.key), - ), - popupMenuItemBuilder: identityPopupMenuBuilder, - ); - })); + if (_found.any(testIdentity)) continue; + } + { + bool _tagMismatch = false; + for (String tag in tags) { + if (_identity.tags.contains(tag)) continue; + _tagMismatch = true; + } + if (_tagMismatch) continue; + int _positiveCount = 0; + for (String _term in _terms) { + if (_identity.firstAddressLine + .toLowerCase() + .contains(_term)) { + _positiveCount++; + continue; + } + if (_identity.nickname.toLowerCase().contains(_term)) { + _positiveCount++; + continue; + } + } + if (_positiveCount == _terms.length) { + _found.add(_identity); + } + } + } + if (_found.isEmpty) { + return CustomScrollView( + slivers: [ + SliverFillRemaining( + hasScrollBody: false, + child: Column( + children: [ + const Spacer(flex: 7), + Text( + localizations.noSearchResults, + textAlign: TextAlign.center, + ), + const Spacer(flex: 7), + ], + ), + ), + ], + ); + } + return IdentityButtonListView( + identities: _found, + shouldSort: true, + onPressed: (identity) => Navigator.pushNamed( + context, + IdentityScreen.routeName, + arguments: _account.getIdentity(identity.key), + ), + popupMenuItemBuilder: identityPopupMenuBuilder, + ); + })); } Future _load() async { - List newTags = await _account.identitiesTags; + List newTags; + try { + newTags = await _account.identitiesTags; + } catch (_) { + return; + } if (mounted) { setState(() { _tags = newTags; @@ -105,8 +118,10 @@ class _IdentitiesScreen extends State { @override Widget build(BuildContext context) { _load(); - List _identities = - _account.identitiesMetadata.values.toList(); + List _identities = []; + try { + _identities = _account.identitiesMetadata.values.toList(); + } catch (_) {} return Scaffold( appBar: EntriesScreenAppBar( entryType: EntryType.identity, diff --git a/lib/screens/notes_screen.dart b/lib/screens/notes_screen.dart index 8f6f42aa..8355205d 100644 --- a/lib/screens/notes_screen.dart +++ b/lib/screens/notes_screen.dart @@ -96,7 +96,12 @@ class _NotesScreen extends State { } Future _load() async { - List newTags = await _account.notesTags; + List newTags ; + try { + newTags = await _account.notesTags; + } catch (_) { + return; + } if (mounted) { setState(() { _tags = newTags; @@ -107,7 +112,10 @@ class _NotesScreen extends State { @override Widget build(BuildContext context) { _load(); - List _notes = _account.notesMetadata.values.toList(); + List _notes = []; + try { + _notes = _account.notesMetadata.values.toList(); + } catch (_) {} return Scaffold( appBar: EntriesScreenAppBar( entryType: EntryType.note, diff --git a/lib/screens/passwords_screen.dart b/lib/screens/passwords_screen.dart index f83bdd91..3dd5fb7e 100644 --- a/lib/screens/passwords_screen.dart +++ b/lib/screens/passwords_screen.dart @@ -74,7 +74,10 @@ class _PasswordsScreen extends State { } Future _load() async { - List newTags = await _account.passwordTags; + List newTags; + try { + newTags = await _account.passwordTags; + } catch (_) {return;} if (mounted) { setState(() { _tags = newTags; @@ -84,8 +87,11 @@ class _PasswordsScreen extends State { @override Widget build(BuildContext context) { - _load(); - List _passwords = _account.passwordsMetadata.values.toList(); + _load(); + List _passwords = []; + try { + _passwords = _account.passwordsMetadata.values.toList(); + } catch (_) {} return Scaffold( appBar: EntriesScreenAppBar( entryType: EntryType.password, diff --git a/lib/screens/payment_cards_screen.dart b/lib/screens/payment_cards_screen.dart index 9b5f1b4a..05deb09a 100644 --- a/lib/screens/payment_cards_screen.dart +++ b/lib/screens/payment_cards_screen.dart @@ -103,7 +103,9 @@ class _PaymentCardsScreen extends State { Navigator.pushNamed(context, EditPaymentCardScreen.routeName); Future _load() async { - List newTags = await _account.paymentCardTags; + List newTags; + try{ + newTags = await _account.paymentCardTags;}catch(_){return;} if (mounted) { setState(() { _tags = newTags; @@ -114,8 +116,10 @@ class _PaymentCardsScreen extends State { @override Widget build(BuildContext context) { _load(); - List _paymentCards = - _account.paymentCardsMetadata.values.toList(); + List _paymentCards = []; + try { + _paymentCards = _account.paymentCardsMetadata.values.toList(); + } catch (_) {} return Scaffold( appBar: EntriesScreenAppBar( entryType: EntryType.paymentCard, From dc74196f7a47a2d5b0aa9ea07cecf0254c732151 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sun, 10 Mar 2024 23:30:58 +0000 Subject: [PATCH 047/131] Tags IO repaired --- lib/screens/id_card_screen.dart | 76 +++++++++++++------------- lib/screens/identity_screen.dart | 80 +++++++++++++--------------- lib/screens/note_screen.dart | 76 +++++++++++++------------- lib/screens/password_screen.dart | 31 ++++------- lib/screens/payment_card_screen.dart | 32 ++++------- 5 files changed, 130 insertions(+), 165 deletions(-) diff --git a/lib/screens/id_card_screen.dart b/lib/screens/id_card_screen.dart index f9f37090..2f570a29 100644 --- a/lib/screens/id_card_screen.dart +++ b/lib/screens/id_card_screen.dart @@ -30,6 +30,7 @@ class _IDCardScreen extends State { IDCard? _idCard; List _tags = []; List _selected = []; + bool _tagsLoaded = false; Future _load() async { List newTags = await _account.idCardsTags; @@ -42,6 +43,7 @@ class _IDCardScreen extends State { _tags.remove(tag); } } + _tagsLoaded = true; }); } } @@ -132,48 +134,42 @@ class _IDCardScreen extends State { ), body: ListView( children: [ - Center( - child: Padding( - padding: EdgeInsets.only( - top: PassyTheme.passyPadding.top / 2, - bottom: PassyTheme.passyPadding.bottom / 2), - child: EntryTagList( - showAddButton: true, - selected: _selected, - notSelected: _tags, - onAdded: (tag) async { - Navigator.pushNamed(context, SplashScreen.routeName); - _idCard!.tags = _selected.toList(); - _idCard!.tags.add(tag); - await _account.setIDCard(_idCard!); - Navigator.popUntil(context, - (route) => route.settings.name == IDCardScreen.routeName); - if (!mounted) return; - setState(() { - _tags.remove(tag); - _selected.add(tag); - _selected.sort(); - _idCard!.tags = _selected; - }); - }, - onRemoved: (tag) async { - Navigator.pushNamed(context, SplashScreen.routeName); - _idCard!.tags = _selected.toList(); - _idCard!.tags.remove(tag); - await _account.setIDCard(_idCard!); - Navigator.popUntil(context, - (route) => route.settings.name == IDCardScreen.routeName); - if (!mounted) return; - setState(() { - _tags.add(tag); - _tags.sort(); - _selected.remove(tag); - _idCard!.tags = _selected; - }); - }, + if (_tagsLoaded) + Center( + child: Padding( + padding: EdgeInsets.only( + top: PassyTheme.passyPadding.top / 2, + bottom: PassyTheme.passyPadding.bottom / 2), + child: EntryTagList( + showAddButton: true, + selected: _selected, + notSelected: _tags, + onAdded: (tag) async { + if (_idCard!.tags.contains(tag)) return; + Navigator.pushNamed(context, SplashScreen.routeName); + _idCard!.tags = _selected.toList(); + _idCard!.tags.add(tag); + await _account.setIDCard(_idCard!); + Navigator.popUntil(context, + (r) => r.settings.name == MainScreen.routeName); + Navigator.pushNamed(context, IDCardsScreen.routeName); + Navigator.pushNamed(context, IDCardScreen.routeName, + arguments: _idCard!); + }, + onRemoved: (tag) async { + Navigator.pushNamed(context, SplashScreen.routeName); + _idCard!.tags = _selected.toList(); + _idCard!.tags.remove(tag); + await _account.setIDCard(_idCard!); + Navigator.popUntil(context, + (r) => r.settings.name == MainScreen.routeName); + Navigator.pushNamed(context, IDCardsScreen.routeName); + Navigator.pushNamed(context, IDCardScreen.routeName, + arguments: _idCard!); + }, + ), ), ), - ), if (_idCard!.attachments.isNotEmpty) AttachmentsListView(files: _idCard!.attachments), if (_idCard!.nickname != '') diff --git a/lib/screens/identity_screen.dart b/lib/screens/identity_screen.dart index 073acbec..3fccec4e 100644 --- a/lib/screens/identity_screen.dart +++ b/lib/screens/identity_screen.dart @@ -30,6 +30,7 @@ class _IdentityScreen extends State { Identity? _identity; List _tags = []; List _selected = []; + bool _tagsLoaded = false; Future _load() async { List newTags = await _account.identitiesTags; @@ -42,6 +43,7 @@ class _IdentityScreen extends State { _tags.remove(tag); } } + _tagsLoaded = true; }); } } @@ -133,52 +135,42 @@ class _IdentityScreen extends State { ), body: ListView( children: [ - Center( - child: Padding( - padding: EdgeInsets.only( - top: PassyTheme.passyPadding.top / 2, - bottom: PassyTheme.passyPadding.bottom / 2), - child: EntryTagList( - showAddButton: true, - selected: _selected, - notSelected: _tags, - onAdded: (tag) async { - Navigator.pushNamed(context, SplashScreen.routeName); - _identity!.tags = _selected.toList(); - _identity!.tags.add(tag); - await _account.setIdentity(_identity!); - Navigator.popUntil( - context, - (route) => - route.settings.name == IdentityScreen.routeName); - if (!mounted) return; - setState(() { - _tags.remove(tag); - _selected.add(tag); - _selected.sort(); - _identity!.tags = _selected; - }); - }, - onRemoved: (tag) async { - Navigator.pushNamed(context, SplashScreen.routeName); - _identity!.tags = _selected.toList(); - _identity!.tags.remove(tag); - await _account.setIdentity(_identity!); - Navigator.popUntil( - context, - (route) => - route.settings.name == IdentityScreen.routeName); - if (!mounted) return; - setState(() { - _tags.add(tag); - _tags.sort(); - _selected.remove(tag); - _identity!.tags = _selected; - }); - }, + if (_tagsLoaded) + Center( + child: Padding( + padding: EdgeInsets.only( + top: PassyTheme.passyPadding.top / 2, + bottom: PassyTheme.passyPadding.bottom / 2), + child: EntryTagList( + showAddButton: true, + selected: _selected, + notSelected: _tags, + onAdded: (tag) async { + if (_identity!.tags.contains(tag)) return; + Navigator.pushNamed(context, SplashScreen.routeName); + _identity!.tags = _selected.toList(); + _identity!.tags.add(tag); + await _account.setIdentity(_identity!); + Navigator.popUntil(context, + (r) => r.settings.name == MainScreen.routeName); + Navigator.pushNamed(context, IdentitiesScreen.routeName); + Navigator.pushNamed(context, IdentityScreen.routeName, + arguments: _identity!); + }, + onRemoved: (tag) async { + Navigator.pushNamed(context, SplashScreen.routeName); + _identity!.tags = _selected.toList(); + _identity!.tags.remove(tag); + await _account.setIdentity(_identity!); + Navigator.popUntil(context, + (r) => r.settings.name == MainScreen.routeName); + Navigator.pushNamed(context, IdentitiesScreen.routeName); + Navigator.pushNamed(context, IdentityScreen.routeName, + arguments: _identity!); + }, + ), ), ), - ), if (_identity!.attachments.isNotEmpty) AttachmentsListView(files: _identity!.attachments), if (_identity!.nickname != '') diff --git a/lib/screens/note_screen.dart b/lib/screens/note_screen.dart index 198a45f4..b9432fdf 100644 --- a/lib/screens/note_screen.dart +++ b/lib/screens/note_screen.dart @@ -28,6 +28,7 @@ class _NoteScreen extends State { Note? _note; List _tags = []; List _selected = []; + bool _tagsLoaded = false; void _onRemovePressed(Note note) { showDialog( @@ -85,6 +86,7 @@ class _NoteScreen extends State { _tags.remove(tag); } } + _tagsLoaded = true; }); } } @@ -129,48 +131,42 @@ class _NoteScreen extends State { }, ), body: ListView(children: [ - Center( - child: Padding( - padding: EdgeInsets.only( - top: PassyTheme.passyPadding.top / 2, - bottom: PassyTheme.passyPadding.bottom / 2), - child: EntryTagList( - showAddButton: true, - selected: _selected, - notSelected: _tags, - onAdded: (tag) async { - Navigator.pushNamed(context, SplashScreen.routeName); - _note!.tags = _selected.toList(); - _note!.tags.add(tag); - await _account.setNote(_note!); - Navigator.popUntil(context, - (route) => route.settings.name == NoteScreen.routeName); - if (!mounted) return; - setState(() { - _tags.remove(tag); - _selected.add(tag); - _selected.sort(); - _note!.tags = _selected; - }); - }, - onRemoved: (tag) async { - Navigator.pushNamed(context, SplashScreen.routeName); - _note!.tags = _selected.toList(); - _note!.tags.remove(tag); - await _account.setNote(_note!); - Navigator.popUntil(context, - (route) => route.settings.name == NoteScreen.routeName); - if (!mounted) return; - setState(() { - _tags.add(tag); - _tags.sort(); - _selected.remove(tag); - _note!.tags = _selected; - }); - }, + if (_tagsLoaded) + Center( + child: Padding( + padding: EdgeInsets.only( + top: PassyTheme.passyPadding.top / 2, + bottom: PassyTheme.passyPadding.bottom / 2), + child: EntryTagList( + showAddButton: true, + selected: _selected, + notSelected: _tags, + onAdded: (tag) async { + if (_note!.tags.contains(tag)) return; + Navigator.pushNamed(context, SplashScreen.routeName); + _note!.tags = _selected.toList(); + _note!.tags.add(tag); + await _account.setNote(_note!); + Navigator.popUntil( + context, (r) => r.settings.name == MainScreen.routeName); + Navigator.pushNamed(context, NotesScreen.routeName); + Navigator.pushNamed(context, NoteScreen.routeName, + arguments: _note!); + }, + onRemoved: (tag) async { + Navigator.pushNamed(context, SplashScreen.routeName); + _note!.tags = _selected.toList(); + _note!.tags.remove(tag); + await _account.setNote(_note!); + Navigator.popUntil( + context, (r) => r.settings.name == MainScreen.routeName); + Navigator.pushNamed(context, NotesScreen.routeName); + Navigator.pushNamed(context, NoteScreen.routeName, + arguments: _note!); + }, + ), ), ), - ), if (_note!.title != '') PassyPadding( RecordButton(title: localizations.title, value: _note!.title)), diff --git a/lib/screens/password_screen.dart b/lib/screens/password_screen.dart index a9b65d53..53b19097 100644 --- a/lib/screens/password_screen.dart +++ b/lib/screens/password_screen.dart @@ -32,6 +32,7 @@ class _PasswordScreen extends State { final LoadedAccount _account = data.loadedAccount!; List _tags = []; List _selected = []; + bool _tagsLoaded = false; Password? password; Future? generateTFA; String _tfaCode = ''; @@ -143,6 +144,7 @@ class _PasswordScreen extends State { _tags.remove(tag); } } + _tagsLoaded = true; }); } } @@ -263,21 +265,16 @@ class _PasswordScreen extends State { selected: _selected, notSelected: _tags, onAdded: (tag) async { + if (password!.tags.contains(tag)) return; Navigator.pushNamed(context, SplashScreen.routeName); password!.tags = _selected.toList(); password!.tags.add(tag); await _account.setPassword(password!); Navigator.popUntil( - context, - (route) => - route.settings.name == PasswordScreen.routeName); - if (!mounted) return; - setState(() { - _tags.remove(tag); - _selected.add(tag); - _selected.sort(); - password!.tags = _selected; - }); + context, (r) => r.settings.name == MainScreen.routeName); + Navigator.pushNamed(context, PasswordsScreen.routeName); + Navigator.pushNamed(context, PasswordScreen.routeName, + arguments: password!); }, onRemoved: (tag) async { Navigator.pushNamed(context, SplashScreen.routeName); @@ -285,16 +282,10 @@ class _PasswordScreen extends State { password!.tags.remove(tag); await _account.setPassword(password!); Navigator.popUntil( - context, - (route) => - route.settings.name == PasswordScreen.routeName); - if (!mounted) return; - setState(() { - _tags.add(tag); - _tags.sort(); - _selected.remove(tag); - password!.tags = _selected; - }); + context, (r) => r.settings.name == MainScreen.routeName); + Navigator.pushNamed(context, PasswordsScreen.routeName); + Navigator.pushNamed(context, PasswordScreen.routeName, + arguments: password!); }, ), ), diff --git a/lib/screens/payment_card_screen.dart b/lib/screens/payment_card_screen.dart index 9adb9233..f76526c6 100644 --- a/lib/screens/payment_card_screen.dart +++ b/lib/screens/payment_card_screen.dart @@ -29,6 +29,7 @@ class _PaymentCardScreen extends State { PaymentCard? _paymentCard; List _tags = []; List _selected = []; + bool _tagsLoaded = false; void _onRemovePressed(PaymentCard paymentCard) { showDialog( @@ -87,6 +88,7 @@ class _PaymentCardScreen extends State { _tags.remove(tag); } } + _tagsLoaded = true; }); } } @@ -137,7 +139,6 @@ class _PaymentCardScreen extends State { obscureCardCvv: false, isSwipeGestureEnabled: false, ), - Center( child: Padding( padding: EdgeInsets.only( @@ -148,21 +149,16 @@ class _PaymentCardScreen extends State { selected: _selected, notSelected: _tags, onAdded: (tag) async { + if (_paymentCard!.tags.contains(tag)) return; Navigator.pushNamed(context, SplashScreen.routeName); _paymentCard!.tags = _selected.toList(); _paymentCard!.tags.add(tag); await _account.setPaymentCard(_paymentCard!); Navigator.popUntil( - context, - (route) => - route.settings.name == PaymentCardScreen.routeName); - if (!mounted) return; - setState(() { - _tags.remove(tag); - _selected.add(tag); - _selected.sort(); - _paymentCard!.tags = _selected; - }); + context, (r) => r.settings.name == MainScreen.routeName); + Navigator.pushNamed(context, PaymentCardsScreen.routeName); + Navigator.pushNamed(context, PaymentCardScreen.routeName, + arguments: _paymentCard!); }, onRemoved: (tag) async { Navigator.pushNamed(context, SplashScreen.routeName); @@ -170,16 +166,10 @@ class _PaymentCardScreen extends State { _paymentCard!.tags.remove(tag); await _account.setPaymentCard(_paymentCard!); Navigator.popUntil( - context, - (route) => - route.settings.name == PaymentCardScreen.routeName); - if (!mounted) return; - setState(() { - _tags.add(tag); - _tags.sort(); - _selected.remove(tag); - _paymentCard!.tags = _selected; - }); + context, (r) => r.settings.name == MainScreen.routeName); + Navigator.pushNamed(context, PaymentCardsScreen.routeName); + Navigator.pushNamed(context, PaymentCardScreen.routeName, + arguments: _paymentCard!); }, ), ), From 59140012541973d985ae33032aa31b9c3e06390f Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Mon, 11 Mar 2024 01:12:13 +0000 Subject: [PATCH 048/131] build() looping repaired for entries screens --- lib/screens/id_cards_screen.dart | 8 +++++++- lib/screens/identities_screen.dart | 4 ++++ lib/screens/notes_screen.dart | 6 +++++- lib/screens/passwords_screen.dart | 8 +++++++- lib/screens/payment_cards_screen.dart | 11 +++++++++-- 5 files changed, 32 insertions(+), 5 deletions(-) diff --git a/lib/screens/id_cards_screen.dart b/lib/screens/id_cards_screen.dart index 50ade077..ae8bc417 100644 --- a/lib/screens/id_cards_screen.dart +++ b/lib/screens/id_cards_screen.dart @@ -1,3 +1,4 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:passy/common/common.dart'; @@ -105,6 +106,9 @@ class _IDCardsScreen extends State { } catch (_) { return; } + if (listEquals(newTags, _tags)) { + return; + } if (mounted) { setState(() { _tags = newTags; @@ -116,7 +120,9 @@ class _IDCardsScreen extends State { Widget build(BuildContext context) { _load(); List _idCards = []; - try{_idCards = _account.idCardsMetadata.values.toList();}catch(_){} + try { + _idCards = _account.idCardsMetadata.values.toList(); + } catch (_) {} return Scaffold( appBar: EntriesScreenAppBar( entryType: EntryType.idCard, diff --git a/lib/screens/identities_screen.dart b/lib/screens/identities_screen.dart index a3c9cdfe..9e2923b4 100644 --- a/lib/screens/identities_screen.dart +++ b/lib/screens/identities_screen.dart @@ -1,3 +1,4 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:passy/common/common.dart'; @@ -108,6 +109,9 @@ class _IdentitiesScreen extends State { } catch (_) { return; } + if (listEquals(newTags, _tags)) { + return; + } if (mounted) { setState(() { _tags = newTags; diff --git a/lib/screens/notes_screen.dart b/lib/screens/notes_screen.dart index 8355205d..e4660924 100644 --- a/lib/screens/notes_screen.dart +++ b/lib/screens/notes_screen.dart @@ -1,3 +1,4 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:passy/common/common.dart'; @@ -96,12 +97,15 @@ class _NotesScreen extends State { } Future _load() async { - List newTags ; + List newTags; try { newTags = await _account.notesTags; } catch (_) { return; } + if (listEquals(newTags, _tags)) { + return; + } if (mounted) { setState(() { _tags = newTags; diff --git a/lib/screens/passwords_screen.dart b/lib/screens/passwords_screen.dart index 3dd5fb7e..4c31b855 100644 --- a/lib/screens/passwords_screen.dart +++ b/lib/screens/passwords_screen.dart @@ -1,3 +1,4 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:passy/common/common.dart'; @@ -77,7 +78,12 @@ class _PasswordsScreen extends State { List newTags; try { newTags = await _account.passwordTags; - } catch (_) {return;} + } catch (_) { + return; + } + if (listEquals(newTags, _tags)) { + return; + } if (mounted) { setState(() { _tags = newTags; diff --git a/lib/screens/payment_cards_screen.dart b/lib/screens/payment_cards_screen.dart index 05deb09a..8de69d86 100644 --- a/lib/screens/payment_cards_screen.dart +++ b/lib/screens/payment_cards_screen.dart @@ -1,3 +1,4 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:passy/common/common.dart'; import 'package:passy/passy_data/loaded_account.dart'; @@ -104,8 +105,14 @@ class _PaymentCardsScreen extends State { Future _load() async { List newTags; - try{ - newTags = await _account.paymentCardTags;}catch(_){return;} + try { + newTags = await _account.paymentCardTags; + } catch (_) { + return; + } + if (listEquals(newTags, _tags)) { + return; + } if (mounted) { setState(() { _tags = newTags; From ab8e7fd0464079f6c5ae1abf56c90a821ef077a7 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Mon, 11 Mar 2024 01:20:25 +0000 Subject: [PATCH 049/131] Reduced tags performance overhead --- lib/screens/id_cards_screen.dart | 4 +++- lib/screens/identities_screen.dart | 4 +++- lib/screens/notes_screen.dart | 4 +++- lib/screens/passwords_screen.dart | 4 +++- lib/screens/payment_cards_screen.dart | 4 +++- 5 files changed, 15 insertions(+), 5 deletions(-) diff --git a/lib/screens/id_cards_screen.dart b/lib/screens/id_cards_screen.dart index ae8bc417..7feb81bd 100644 --- a/lib/screens/id_cards_screen.dart +++ b/lib/screens/id_cards_screen.dart @@ -25,6 +25,7 @@ class IDCardsScreen extends StatefulWidget { class _IDCardsScreen extends State { final LoadedAccount _account = data.loadedAccount!; List _tags = []; + bool _isLoading = false; void _onAddPressed() => Navigator.pushNamed(context, EditIDCardScreen.routeName); @@ -100,6 +101,7 @@ class _IDCardsScreen extends State { } Future _load() async { + _isLoading = true; List newTags; try { newTags = await _account.idCardsTags; @@ -118,7 +120,7 @@ class _IDCardsScreen extends State { @override Widget build(BuildContext context) { - _load(); + if (!_isLoading) _load().whenComplete(() => _isLoading = false); List _idCards = []; try { _idCards = _account.idCardsMetadata.values.toList(); diff --git a/lib/screens/identities_screen.dart b/lib/screens/identities_screen.dart index 9e2923b4..30a5a91c 100644 --- a/lib/screens/identities_screen.dart +++ b/lib/screens/identities_screen.dart @@ -24,6 +24,7 @@ class IdentitiesScreen extends StatefulWidget { class _IdentitiesScreen extends State { final _account = data.loadedAccount!; List _tags = []; + bool _isLoading = false; void _onAddPressed() => Navigator.pushNamed(context, EditIdentityScreen.routeName); @@ -103,6 +104,7 @@ class _IdentitiesScreen extends State { } Future _load() async { + _isLoading = true; List newTags; try { newTags = await _account.identitiesTags; @@ -121,7 +123,7 @@ class _IdentitiesScreen extends State { @override Widget build(BuildContext context) { - _load(); + if (!_isLoading) _load().whenComplete(() => _isLoading = false); List _identities = []; try { _identities = _account.identitiesMetadata.values.toList(); diff --git a/lib/screens/notes_screen.dart b/lib/screens/notes_screen.dart index e4660924..d1669ea4 100644 --- a/lib/screens/notes_screen.dart +++ b/lib/screens/notes_screen.dart @@ -25,6 +25,7 @@ class NotesScreen extends StatefulWidget { class _NotesScreen extends State { final LoadedAccount _account = data.loadedAccount!; List _tags = []; + bool _isLoading = false; void _onAddPressed() => Navigator.pushNamed(context, EditNoteScreen.routeName); @@ -97,6 +98,7 @@ class _NotesScreen extends State { } Future _load() async { + _isLoading = true; List newTags; try { newTags = await _account.notesTags; @@ -115,7 +117,7 @@ class _NotesScreen extends State { @override Widget build(BuildContext context) { - _load(); + if (!_isLoading) _load().whenComplete(() => _isLoading = false); List _notes = []; try { _notes = _account.notesMetadata.values.toList(); diff --git a/lib/screens/passwords_screen.dart b/lib/screens/passwords_screen.dart index 4c31b855..99bfcac4 100644 --- a/lib/screens/passwords_screen.dart +++ b/lib/screens/passwords_screen.dart @@ -26,6 +26,7 @@ class PasswordsScreen extends StatefulWidget { class _PasswordsScreen extends State { final LoadedAccount _account = data.loadedAccount!; List _tags = []; + bool _isLoading = false; void _onAddPressed() => Navigator.pushNamed(context, EditPasswordScreen.routeName); @@ -75,6 +76,7 @@ class _PasswordsScreen extends State { } Future _load() async { + _isLoading = true; List newTags; try { newTags = await _account.passwordTags; @@ -93,7 +95,7 @@ class _PasswordsScreen extends State { @override Widget build(BuildContext context) { - _load(); + if (!_isLoading) _load().whenComplete(() => _isLoading = false); List _passwords = []; try { _passwords = _account.passwordsMetadata.values.toList(); diff --git a/lib/screens/payment_cards_screen.dart b/lib/screens/payment_cards_screen.dart index 8de69d86..58e5b272 100644 --- a/lib/screens/payment_cards_screen.dart +++ b/lib/screens/payment_cards_screen.dart @@ -23,6 +23,7 @@ class PaymentCardsScreen extends StatefulWidget { class _PaymentCardsScreen extends State { final LoadedAccount _account = data.loadedAccount!; List _tags = []; + bool _isLoading = false; void _onSearchPressed({String? tag}) { Navigator.pushNamed(context, SearchScreen.routeName, @@ -104,6 +105,7 @@ class _PaymentCardsScreen extends State { Navigator.pushNamed(context, EditPaymentCardScreen.routeName); Future _load() async { + _isLoading = true; List newTags; try { newTags = await _account.paymentCardTags; @@ -122,7 +124,7 @@ class _PaymentCardsScreen extends State { @override Widget build(BuildContext context) { - _load(); + if (!_isLoading) _load().whenComplete(() => _isLoading = false); List _paymentCards = []; try { _paymentCards = _account.paymentCardsMetadata.values.toList(); From b1e3744a7168c6cbdadb770f75acac42c6dd510e Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Mon, 11 Mar 2024 15:22:40 +0000 Subject: [PATCH 050/131] Update flutter to 3.19.3 --- submodules/flutter | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/flutter b/submodules/flutter index ff5b5b5f..ba393198 160000 --- a/submodules/flutter +++ b/submodules/flutter @@ -1 +1 @@ -Subproject commit ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a +Subproject commit ba393198430278b6595976de84fe170f553cc728 From ae0221faeee84365a06a3004b76536f2d7b8b866 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Mon, 11 Mar 2024 22:16:33 +0000 Subject: [PATCH 051/131] UI Style repaired + tag emojis implemented --- lib/passy_flutter/common/common.dart | 27 ++- lib/passy_flutter/passy_theme.dart | 19 +- .../widgets/entry_tag_button.dart | 14 +- .../widgets/entry_tag_creation_dialog.dart | 148 ++++++++++++++ lib/passy_flutter/widgets/entry_tag_list.dart | 43 +--- .../enum_dropdown_button_form_field.dart | 1 + .../widgets/payment_card_button.dart | 73 +++---- lib/passy_flutter/widgets/record_button.dart | 3 +- lib/passy_flutter/widgets/widgets.dart | 2 + lib/screens/key_derivation_screen.dart | 1 + lib/screens/log_screen.dart | 1 + lib/screens/login_screen.dart | 15 +- lib/screens/servers_screen.dart | 11 +- linux/flutter/generated_plugin_registrant.cc | 4 + linux/flutter/generated_plugins.cmake | 1 + pubspec.lock | 184 +++++++++++++----- pubspec.yaml | 10 +- .../flutter/generated_plugin_registrant.cc | 3 + windows/flutter/generated_plugins.cmake | 1 + 19 files changed, 416 insertions(+), 145 deletions(-) create mode 100644 lib/passy_flutter/widgets/entry_tag_creation_dialog.dart diff --git a/lib/passy_flutter/common/common.dart b/lib/passy_flutter/common/common.dart index 98eb89b9..7492c1a4 100644 --- a/lib/passy_flutter/common/common.dart +++ b/lib/passy_flutter/common/common.dart @@ -7,6 +7,31 @@ import 'package:passy/common/assets.dart'; import 'package:passy/passy_flutter/passy_flutter.dart'; import 'package:http/http.dart' as http; +class CardAssetPaths { + const CardAssetPaths._(); + + static const String visa = 'icons/visa.png'; + static const String rupay = 'icons/rupay.png'; + static const String mastercard = 'icons/mastercard.png'; + static const String americanExpress = 'icons/amex.png'; + static const String unionpay = 'icons/unionpay.png'; + static const String discover = 'icons/discover.png'; + static const String elo = 'icons/elo.png'; + static const String hipercard = 'icons/hipercard.png'; + static const String chip = 'icons/chip.png'; +} + +const Map cardTypeIconAsset = { + CardType.visa: CardAssetPaths.visa, + CardType.rupay: CardAssetPaths.rupay, + CardType.americanExpress: CardAssetPaths.americanExpress, + CardType.mastercard: CardAssetPaths.mastercard, + CardType.unionpay: CardAssetPaths.unionpay, + CardType.discover: CardAssetPaths.discover, + CardType.elo: CardAssetPaths.elo, + CardType.hipercard: CardAssetPaths.hipercard, +}; + String capitalize(String string) { if (string.isEmpty) return ''; String _firstLetter = string[0].toUpperCase(); @@ -91,7 +116,7 @@ Widget getCardTypeImage(CardType? cardType) { } return Image.asset( - CardTypeIconAsset[cardType]!, + cardTypeIconAsset[cardType]!, height: 48, width: 48, package: 'flutter_credit_card', diff --git a/lib/passy_flutter/passy_theme.dart b/lib/passy_flutter/passy_theme.dart index 7db94d7d..f054b678 100644 --- a/lib/passy_flutter/passy_theme.dart +++ b/lib/passy_flutter/passy_theme.dart @@ -16,9 +16,10 @@ class PassyTheme { static final ThemeData theme = ThemeData( colorScheme: const ColorScheme.dark( - primary: Color.fromRGBO(74, 20, 140, 1), - onPrimary: lightContentColor, - secondary: Color.fromRGBO(123, 31, 162, 1), + //primary: Color.fromRGBO(74, 20, 140, 1), + primary: lightContentColor, + onPrimary: darkContentColor, + secondary: lightContentColor, onSecondary: lightContentColor, onSurface: lightContentColor, ), @@ -39,11 +40,17 @@ class PassyTheme { focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(100), borderSide: const BorderSide(color: lightContentColor)), + isDense: true, + contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), ), - elevatedButtonTheme: ElevatedButtonThemeData( + elevatedButtonTheme: const ElevatedButtonThemeData( style: ButtonStyle( - shadowColor: - MaterialStateProperty.resolveWith((states) => Colors.transparent), + backgroundColor: MaterialStatePropertyAll(darkPassyPurple), + ), + ), + textButtonTheme: const TextButtonThemeData( + style: ButtonStyle( + backgroundColor: MaterialStatePropertyAll(darkPassyPurple), ), ), textSelectionTheme: const TextSelectionThemeData( diff --git a/lib/passy_flutter/widgets/entry_tag_button.dart b/lib/passy_flutter/widgets/entry_tag_button.dart index 39a78e10..f6fda3d8 100644 --- a/lib/passy_flutter/widgets/entry_tag_button.dart +++ b/lib/passy_flutter/widgets/entry_tag_button.dart @@ -19,17 +19,15 @@ class EntryTagButton extends StatelessWidget { Widget build(BuildContext context) { return TextButton.icon( onPressed: onPressed ?? () {}, - style: ElevatedButton.styleFrom( - backgroundColor: color, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(35.0), - ), + style: ButtonStyle( + backgroundColor: MaterialStatePropertyAll(color), ), - icon: Icon(isSelected ? Icons.close_rounded: Icons.add_rounded), + icon: Icon(isSelected ? Icons.close_rounded : Icons.add_rounded, + color: PassyTheme.theme.colorScheme.onPrimary), label: Padding( padding: EdgeInsets.only( - top: PassyTheme.passyPadding.top, - bottom: PassyTheme.passyPadding.bottom, + top: PassyTheme.passyPadding.top / 1.5, + bottom: PassyTheme.passyPadding.bottom / 1.5, right: PassyTheme.passyPadding.right), child: Text( tag, diff --git a/lib/passy_flutter/widgets/entry_tag_creation_dialog.dart b/lib/passy_flutter/widgets/entry_tag_creation_dialog.dart new file mode 100644 index 00000000..39ae07f5 --- /dev/null +++ b/lib/passy_flutter/widgets/entry_tag_creation_dialog.dart @@ -0,0 +1,148 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:passy/common/common.dart'; +import 'package:passy/passy_flutter/passy_flutter.dart'; +import 'package:emoji_picker_flutter/emoji_picker_flutter.dart' as ep; + +class EntryTagCreationDialog extends StatefulWidget { + const EntryTagCreationDialog({Key? key}) : super(key: key); + + @override + State createState() => _EntryTagCreationDialog(); +} + +class _EntryTagCreationDialog extends State { + String _tag = ''; + final TextEditingController _controller = TextEditingController(); + + @override + void initState() { + super.initState(); + _controller.addListener( + () => setState(() => _tag = _controller.text), + ); + } + + @override + void dispose() { + // Clean up the controller when the widget is removed from the + // widget tree. + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Dialog( + shape: PassyTheme.dialogShape, + child: Container( + constraints: const BoxConstraints(maxWidth: 450, maxHeight: 450), + child: ListView( + shrinkWrap: true, + children: [ + Padding( + padding: const EdgeInsets.all(20), + child: Text( + localizations.createTag, + style: const TextStyle( + fontSize: 20, fontWeight: FontWeight.bold), + )), + PassyPadding( + TextFormField( + controller: _controller, + autofocus: true, + decoration: InputDecoration(labelText: localizations.tag), + onFieldSubmitted: (tag) { + Navigator.pop(context, tag); + }, + ), + ), + SizedBox( + height: 200, + child: Theme( + data: ThemeData( + colorScheme: const ColorScheme.dark( + //primary: Color.fromRGBO(74, 20, 140, 1), + primary: PassyTheme.lightContentColor, + onPrimary: PassyTheme.darkContentColor, + secondary: PassyTheme.lightContentColor, + onSecondary: PassyTheme.lightContentColor, + onSurface: PassyTheme.lightContentColor, + ), + inputDecorationTheme: InputDecorationTheme( + floatingLabelStyle: const TextStyle( + color: PassyTheme.lightContentSecondaryColor), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(100), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(100), + borderSide: const BorderSide( + color: PassyTheme.darkContentSecondaryColor, + ), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(100), + borderSide: const BorderSide( + color: PassyTheme.lightContentColor)), + contentPadding: const EdgeInsets.symmetric( + horizontal: 16, vertical: 16), + ), + ), + child: ep.EmojiPicker( + textEditingController: _controller, + onEmojiSelected: (ep.Category? category, ep.Emoji emoji) {}, + config: ep.Config( + height: 256, + checkPlatformCompatibility: true, + emojiViewConfig: ep.EmojiViewConfig( + backgroundColor: PassyTheme.darkContentColor, + // Issue: https://github.com/flutter/flutter/issues/28894 + emojiSizeMax: 28 * + (defaultTargetPlatform == TargetPlatform.iOS + ? 1.20 + : 1.0), + ), + swapCategoryAndBottomBar: false, + skinToneConfig: const ep.SkinToneConfig(), + categoryViewConfig: const ep.CategoryViewConfig( + backgroundColor: PassyTheme.darkContentColor), + bottomActionBarConfig: const ep.BottomActionBarConfig( + backgroundColor: PassyTheme.darkContentColor), + searchViewConfig: const ep.SearchViewConfig( + buttonIconColor: PassyTheme.lightContentColor, + backgroundColor: PassyTheme.darkContentColor), + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.all(20), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Spacer(), + TextButton( + onPressed: () => Navigator.pop(context), + child: Text(localizations.cancel, + style: const TextStyle( + color: PassyTheme.lightContentSecondaryColor)), + ), + const SizedBox(width: 8), + TextButton( + onPressed: () { + Navigator.pop(context, _tag); + }, + child: Text(localizations.create, + style: const TextStyle( + color: PassyTheme.lightContentSecondaryColor)), + ), + ], + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/passy_flutter/widgets/entry_tag_list.dart b/lib/passy_flutter/widgets/entry_tag_list.dart index 4bd11abf..fe15dddb 100644 --- a/lib/passy_flutter/widgets/entry_tag_list.dart +++ b/lib/passy_flutter/widgets/entry_tag_list.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:passy/common/common.dart'; import 'package:passy/passy_flutter/passy_flutter.dart'; -import 'package:passy/passy_flutter/widgets/entry_tag_button.dart'; class EntryTagList extends StatefulWidget { final List selected; @@ -116,43 +115,11 @@ class _EntryTagList extends State { widget.onAddPressed(); showDialog( context: context, - builder: (context) { - String tag = ''; - return AlertDialog( - shape: PassyTheme.dialogShape, - title: Text(localizations.createTag), - content: TextFormField( - autofocus: true, - decoration: InputDecoration( - labelText: localizations.tag), - onChanged: (value) => {tag = value}, - onFieldSubmitted: (tag) { - widget.onAdded(tag); - Navigator.pop(context); - }, - ), - actions: [ - TextButton( - onPressed: () => Navigator.pop(context), - child: Text(localizations.cancel, - style: const TextStyle( - color: PassyTheme - .lightContentSecondaryColor)), - ), - TextButton( - onPressed: () { - widget.onAdded(tag); - Navigator.pop(context); - }, - child: Text(localizations.create, - style: const TextStyle( - color: PassyTheme - .lightContentSecondaryColor)), - ), - ], - ); - }, - ); + builder: (context) => const EntryTagCreationDialog(), + ).then((value) { + if (value is! String) return; + widget.onAdded(value); + }); }, ), ), diff --git a/lib/passy_flutter/widgets/enum_dropdown_button_form_field.dart b/lib/passy_flutter/widgets/enum_dropdown_button_form_field.dart index 533d2a61..d6f22e57 100644 --- a/lib/passy_flutter/widgets/enum_dropdown_button_form_field.dart +++ b/lib/passy_flutter/widgets/enum_dropdown_button_form_field.dart @@ -57,6 +57,7 @@ class EnumDropDownButtonFormField extends StatelessWidget { )); } return DropdownButtonFormField( + borderRadius: const BorderRadius.all(Radius.circular(30)), items: _menuItems, value: value, decoration: decoration, diff --git a/lib/passy_flutter/widgets/payment_card_button.dart b/lib/passy_flutter/widgets/payment_card_button.dart index 2a59e7eb..8adabe06 100644 --- a/lib/passy_flutter/widgets/payment_card_button.dart +++ b/lib/passy_flutter/widgets/payment_card_button.dart @@ -27,42 +27,49 @@ class PaymentCardButton extends StatelessWidget { return Center( child: Stack( children: [ - TextButton( - onPressed: onPressed, - child: CreditCardWidget( - glassmorphismConfig: Glassmorphism.defaultConfig(), - width: 350, - height: 200, - cardNumber: beautifyCardNumber(paymentCard.cardNumber), - expiryDate: paymentCard.exp, - cardHolderName: paymentCard.cardholderName, - customCardTypeIcons: customCardTypeIcons ?? - [ - CustomCardTypeIcon( - cardType: CardType.otherBrand, - cardImage: SvgPicture.asset( - 'assets/images/logo_circle.svg', - colorFilter: const ColorFilter.mode( - Colors.purple, BlendMode.srcIn), - width: 50, - )) - ], - cvvCode: '', - showBackView: false, - obscureCardNumber: obscureCardNumber, - obscureCardCvv: obscureCardCvv, - isHolderNameVisible: true, - isChipVisible: false, - backgroundImage: 'assets/images/payment_card_bg.png', - cardType: cardTypeFromNumber( - paymentCard.cardNumber.replaceAll('*', '0')), - isSwipeGestureEnabled: isSwipeGestureEnabled, - onCreditCardWidgetChange: (brand) {}, - bankName: ' ', + InkWell( + borderRadius: BorderRadius.all(Radius.circular(12)), + onTap: onPressed, + child: ClipRect( + child: Align( + heightFactor: 0.857, + widthFactor: 0.917, + child: CreditCardWidget( + glassmorphismConfig: Glassmorphism.defaultConfig(), + width: 350, + height: 200, + cardNumber: beautifyCardNumber(paymentCard.cardNumber), + expiryDate: paymentCard.exp, + cardHolderName: paymentCard.cardholderName, + customCardTypeIcons: customCardTypeIcons ?? + [ + CustomCardTypeIcon( + cardType: CardType.otherBrand, + cardImage: SvgPicture.asset( + 'assets/images/logo_circle.svg', + colorFilter: const ColorFilter.mode( + Colors.purple, BlendMode.srcIn), + width: 50, + )) + ], + cvvCode: '', + showBackView: false, + obscureCardNumber: obscureCardNumber, + obscureCardCvv: obscureCardCvv, + isHolderNameVisible: true, + isChipVisible: false, + backgroundImage: 'assets/images/payment_card_bg.png', + cardType: cardTypeFromNumber( + paymentCard.cardNumber.replaceAll('*', '0')), + isSwipeGestureEnabled: isSwipeGestureEnabled, + onCreditCardWidgetChange: (brand) {}, + bankName: ' ', + ), + ), ), ), Padding( - padding: const EdgeInsets.fromLTRB(35, 32, 0, 0), + padding: const EdgeInsets.fromLTRB(18, 12, 0, 0), child: Text( paymentCard.nickname, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), diff --git a/lib/passy_flutter/widgets/record_button.dart b/lib/passy_flutter/widgets/record_button.dart index d30aae65..45f503f5 100644 --- a/lib/passy_flutter/widgets/record_button.dart +++ b/lib/passy_flutter/widgets/record_button.dart @@ -25,7 +25,8 @@ class RecordButton extends StatelessWidget { this.valueAlign = TextAlign.center, this.left, }) : titleStyle = titleStyle ?? - const TextStyle(color: PassyTheme.lightContentSecondaryColor), + const TextStyle( + color: PassyTheme.lightContentSecondaryColor, height: 0.94), super(key: key); @override diff --git a/lib/passy_flutter/widgets/widgets.dart b/lib/passy_flutter/widgets/widgets.dart index 63cecaf3..e497dfd4 100644 --- a/lib/passy_flutter/widgets/widgets.dart +++ b/lib/passy_flutter/widgets/widgets.dart @@ -9,6 +9,8 @@ export 'double_action_button.dart'; export 'edit_screen_appbar.dart'; export 'entries_screen_appbar.dart'; export 'entry_screen_appbar.dart'; +export 'entry_tag_button.dart'; +export 'entry_tag_creation_dialog.dart'; export 'entry_tag_list.dart'; export 'enum_dropdown_button_2.dart'; export 'enum_dropdown_button_form_field.dart'; diff --git a/lib/screens/key_derivation_screen.dart b/lib/screens/key_derivation_screen.dart index 038c6ce7..ef21e231 100644 --- a/lib/screens/key_derivation_screen.dart +++ b/lib/screens/key_derivation_screen.dart @@ -139,6 +139,7 @@ class _KeyDerivationScreen extends State { const SizedBox(height: 24), Container( child: PassyPadding(DropdownButtonFormField( + borderRadius: const BorderRadius.all(Radius.circular(30)), items: [ DropdownMenuItem( child: Text( diff --git a/lib/screens/log_screen.dart b/lib/screens/log_screen.dart index e97446fc..0b1d7903 100644 --- a/lib/screens/log_screen.dart +++ b/lib/screens/log_screen.dart @@ -44,6 +44,7 @@ class LogScreen extends StatelessWidget { padding: PassyTheme.appBarButtonPadding, splashRadius: PassyTheme.appBarButtonSplashRadius, icon: SvgPicture.asset( + width: PassyTheme.appBarButtonSplashRadius , 'assets/images/github_icon.svg', colorFilter: const ColorFilter.mode( PassyTheme.lightContentColor, BlendMode.srcIn), diff --git a/lib/screens/login_screen.dart b/lib/screens/login_screen.dart index 85933c79..79d999b5 100644 --- a/lib/screens/login_screen.dart +++ b/lib/screens/login_screen.dart @@ -91,7 +91,8 @@ class _LoginScreen extends State { } } - Widget _buildPasswords(String terms, List tags, void Function() rebuild) { + Widget _buildPasswords( + String terms, List tags, void Function() rebuild) { List _found = PassySearch.searchPasswords( passwords: data.loadedAccount!.passwordsMetadata.values, terms: terms); List _dataSets = []; @@ -234,9 +235,7 @@ class _LoginScreen extends State { if (data.getBioAuthEnabled(_username) == true) { _bioAuthButton = FloatingActionButton( onPressed: () => _bioAuth(), - child: const Icon( - Icons.fingerprint_rounded, - ), + child: const Icon(Icons.fingerprint_rounded), tooltip: localizations.authenticate, heroTag: null, ); @@ -256,6 +255,8 @@ class _LoginScreen extends State { _floatingActionButton = Row(mainAxisAlignment: MainAxisAlignment.center, children: [ FloatingActionButton( + foregroundColor: PassyTheme.lightContentColor, + backgroundColor: Colors.purple, child: const Icon(Icons.settings_rounded), tooltip: localizations.settings, heroTag: null, @@ -266,6 +267,8 @@ class _LoginScreen extends State { Padding( padding: EdgeInsets.only(left: PassyTheme.passyPadding.left), child: FloatingActionButton( + foregroundColor: PassyTheme.lightContentColor, + backgroundColor: Colors.purple, child: const Icon(Icons.extension_rounded), tooltip: localizations.passyBrowserExtension, heroTag: null, @@ -331,6 +334,9 @@ class _LoginScreen extends State { children: [ if (!isAutofill) FloatingActionButton( + foregroundColor: + PassyTheme.lightContentColor, + backgroundColor: Colors.purple, onPressed: () => Navigator.pushReplacementNamed(context, AddAccountScreen.routeName), @@ -340,6 +346,7 @@ class _LoginScreen extends State { ), Expanded( child: DropdownButtonFormField( + isDense: false, borderRadius: const BorderRadius.all( Radius.circular(30)), value: _username, diff --git a/lib/screens/servers_screen.dart b/lib/screens/servers_screen.dart index cf6e131b..f54df5a6 100644 --- a/lib/screens/servers_screen.dart +++ b/lib/screens/servers_screen.dart @@ -9,6 +9,7 @@ import 'package:passy/passy_flutter/passy_flutter.dart'; import 'package:passy/screens/common.dart'; import 'package:passy/screens/manage_servers_screen.dart'; import 'package:passy/screens/settings_screen.dart'; +import 'package:text_divider/text_divider.dart'; import 'server_connect_screen.dart'; import 'synchronization_logs_screen.dart'; @@ -157,6 +158,14 @@ class _ServersScreen extends State { onPressed: () => Navigator.pushNamed( context, ManageServersScreen.routeName) .then((value) => setState(() {})))), + TextDivider.horizontal( + color: PassyTheme.lightContentSecondaryColor, + text: Text( + localizations.synchronizationInterval, + style: const TextStyle( + color: PassyTheme.lightContentSecondaryColor), + ), + ), PassyPadding(Row( mainAxisSize: MainAxisSize.max, children: [ @@ -164,8 +173,6 @@ class _ServersScreen extends State { child: TextFormField( key: _syncIntervalKey, initialValue: _syncIntervalString, - decoration: InputDecoration( - labelText: localizations.synchronizationInterval), onChanged: (value) => setState(() => _syncIntervalString = value), inputFormatters: [ diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 839b912c..fdd196b1 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -7,12 +7,16 @@ #include "generated_plugin_registrant.h" #include +#include #include void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) dargon2_flutter_desktop_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "Dargon2FlutterDesktopPlugin"); dargon2_flutter_desktop_plugin_register_with_registrar(dargon2_flutter_desktop_registrar); + g_autoptr(FlPluginRegistrar) emoji_picker_flutter_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "EmojiPickerFlutterPlugin"); + emoji_picker_flutter_plugin_register_with_registrar(emoji_picker_flutter_registrar); g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 38ae80cf..f091b123 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST dargon2_flutter_desktop + emoji_picker_flutter url_launcher_linux ) diff --git a/pubspec.lock b/pubspec.lock index 6e880fd6..26a2e38b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -133,26 +133,26 @@ packages: dependency: "direct main" description: name: cached_network_image - sha256: fd3d0dc1d451f9a252b32d95d3f0c3c487bc41a75eba2e6097cb0b9c71491b15 + sha256: "28ea9690a8207179c319965c13cd8df184d5ee721ae2ce60f398ced1219cea1f" url: "https://pub.dev" source: hosted - version: "3.2.3" + version: "3.3.1" cached_network_image_platform_interface: dependency: transitive description: name: cached_network_image_platform_interface - sha256: bb2b8403b4ccdc60ef5f25c70dead1f3d32d24b9d6117cfc087f496b178594a7 + sha256: "9e90e78ae72caa874a323d78fa6301b3fb8fa7ea76a8f96dc5b5bf79f283bf2f" url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "4.0.0" cached_network_image_web: dependency: transitive description: name: cached_network_image_web - sha256: b8eb814ebfcb4dea049680f8c1ffb2df399e4d03bf7a352c775e26fa06e02fa0 + sha256: "42a835caa27c220d1294311ac409a43361088625a4f23c820b006dd9bffb3316" url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.1.1" camera: dependency: "direct main" description: @@ -197,10 +197,10 @@ packages: dependency: "direct dev" description: name: change_app_package_name - sha256: f9ebaf68a4b5a68c581492579bb68273c523ef325fbf9ce2f1b57fb136ad023b + sha256: bfa8a781b1c8bbd3a69c91bb4c29869c8b02431f8d2615230045c83007d29009 url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.0.0" characters: dependency: transitive description: @@ -245,10 +245,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" compute: dependency: "direct main" description: @@ -410,6 +410,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.9" + emoji_picker_flutter: + dependency: "direct main" + description: + name: emoji_picker_flutter + sha256: "871339250c00dc469b7fdaaec84f4e10ffa435e730a4f3f3fd06ebd5289ea5ad" + url: "https://pub.dev" + source: hosted + version: "2.1.0" encrypt: dependency: "direct main" description: @@ -438,10 +446,10 @@ packages: dependency: "direct main" description: name: favicon - sha256: "7bd54f7d36d84b91c746277ae2570643f9ba0f67510ef92476d6d30539124cfb" + sha256: ebb7423ba7ccc87d3cce9b60de1b5f72004de3cddeedd88d98463fc7f66faf0c url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.2" ffi: dependency: transitive description: @@ -487,14 +495,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.14.0" - flutter_blurhash: - dependency: transitive - description: - name: flutter_blurhash - sha256: "05001537bd3fac7644fa6558b09ec8c0a3f2eba78c0765f88912882b1331a5c6" - url: "https://pub.dev" - source: hosted - version: "0.7.0" flutter_cache_manager: dependency: transitive description: @@ -507,10 +507,10 @@ packages: dependency: "direct main" description: name: flutter_credit_card - sha256: "95522e85b7bccaf4829218890549dd70e2d82e78c7eb8ab6b03e2e7ce733934c" + sha256: dffaee369b0e64fb81e63e78d6410762f73b67346692bd2ae7bdcc6d911452e3 url: "https://pub.dev" source: hosted - version: "3.0.7" + version: "4.0.1" flutter_date_pickers: dependency: "direct main" description: @@ -743,6 +743,30 @@ packages: url: "https://pub.dev" source: hosted version: "0.2.2" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + url: "https://pub.dev" + source: hosted + version: "10.0.0" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + url: "https://pub.dev" + source: hosted + version: "2.0.1" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + url: "https://pub.dev" + source: hosted + version: "2.0.1" lints: dependency: transitive description: @@ -787,26 +811,26 @@ packages: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.8.0" meta: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.11.0" mime: dependency: transitive description: @@ -819,10 +843,10 @@ packages: dependency: transitive description: name: octo_image - sha256: "107f3ed1330006a3bea63615e81cf637433f5135a52466c7caa0e7152bca9143" + sha256: "45b40f99622f11901238e18d48f5f12ea36426d8eced9f4cbf58479c7aa2430d" url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "2.0.0" otp: dependency: "direct main" description: @@ -843,10 +867,10 @@ packages: dependency: transitive description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.0" path_parsing: dependency: transitive description: @@ -1007,6 +1031,62 @@ packages: url: "https://pub.dev" source: hosted version: "0.27.7" + shared_preferences: + dependency: transitive + description: + name: shared_preferences + sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02" + url: "https://pub.dev" + source: hosted + version: "2.2.2" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06" + url: "https://pub.dev" + source: hosted + version: "2.2.1" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c" + url: "https://pub.dev" + source: hosted + version: "2.3.5" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: d4ec5fc9ebb2f2e056c617112aa75dcf92fc2e4faaf2ae999caa297473f75d8a + url: "https://pub.dev" + source: hosted + version: "2.3.1" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf + url: "https://pub.dev" + source: hosted + version: "2.2.1" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59" + url: "https://pub.dev" + source: hosted + version: "2.3.2" shelf: dependency: transitive description: @@ -1080,18 +1160,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" stream_transform: dependency: transitive description: @@ -1144,10 +1224,18 @@ packages: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + url: "https://pub.dev" + source: hosted + version: "0.6.1" + text_divider: + dependency: "direct main" + description: + name: text_divider + sha256: b3e812231d7b38d99310cec63d71363efd6299950c59bfc3c1b0fedb7717264d url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "1.0.0" timezone: dependency: transitive description: @@ -1284,22 +1372,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - watcher: + vm_service: dependency: transitive description: - name: watcher - sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + name: vm_service + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 url: "https://pub.dev" source: hosted - version: "1.1.0" - web: + version: "13.0.0" + watcher: dependency: transitive description: - name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + name: watcher + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "1.1.0" web_socket_channel: dependency: transitive description: @@ -1365,5 +1453,5 @@ packages: source: hosted version: "0.1.1" sdks: - dart: ">=3.1.0 <4.0.0" - flutter: ">=3.13.0" + dart: ">=3.2.0-0 <4.0.0" + flutter: ">=3.16.0" diff --git a/pubspec.yaml b/pubspec.yaml index c2c581f4..9867ca6f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -44,8 +44,8 @@ dependencies: file_picker: ^5.1.0 flutter_web_browser: ^0.17.1 flutter_locker: ^2.1.2 - cached_network_image: ^3.2.1 - flutter_credit_card: ^3.0.7 + cached_network_image: ^3.3.1 + flutter_credit_card: ^4.0.1 flutter_date_pickers: ^0.4.0 credit_card_type_detector: ^2.0.0 flutter_secure_screen: ^0.0.1 @@ -59,7 +59,7 @@ dependencies: sdk: flutter crypton: ^2.1.0 event: ^2.1.2 - favicon: ^1.1.0 + favicon: ^1.1.1 csv: ^5.0.2 dargon2_flutter: ^3.3.0 dargon2_flutter_mobile: ^3.3.0 @@ -78,6 +78,8 @@ dependencies: url: https://github.com/GlitterWare/kdbx.dart.git ref: 7e24cacc9e469b2391cefb6e2619e5ffa9db84f8 dropdown_button2: ^2.3.9 + emoji_picker_flutter: ^2.1.0 + text_divider: ^1.0.0 dev_dependencies: flutter_test: @@ -86,7 +88,7 @@ dev_dependencies: json_serializable: rename: flutter_launcher_icons: ^0.9.2 - change_app_package_name: ^1.1.0 + change_app_package_name: 1.0.0 # The "flutter_lints" package below contains a set of recommended lints to # encourage good coding practices. The lint set provided by the package is diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 7a61f07c..31f7004a 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -7,11 +7,14 @@ #include "generated_plugin_registrant.h" #include +#include #include void RegisterPlugins(flutter::PluginRegistry* registry) { Dargon2FlutterDesktopPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("Dargon2FlutterDesktopPlugin")); + EmojiPickerFlutterPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("EmojiPickerFlutterPluginCApi")); UrlLauncherWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 1a10ec9a..b8cda7ad 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST dargon2_flutter_desktop + emoji_picker_flutter url_launcher_windows ) From 3729740b9a9862fb6a5a31781fcb9435206c0c6b Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Mon, 11 Mar 2024 22:16:54 +0000 Subject: [PATCH 052/131] Tag search implemented --- lib/screens/search_screen.dart | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/screens/search_screen.dart b/lib/screens/search_screen.dart index 1c1a7439..0d35c9f5 100644 --- a/lib/screens/search_screen.dart +++ b/lib/screens/search_screen.dart @@ -107,6 +107,17 @@ class _SearchScreen extends State { }, onChanged: (s) { setState(() { + String lowerS = s.toLowerCase(); + notSelected.sort( + (a, b) { + a = a.toLowerCase(); + b = b.toLowerCase(); + int aMatches = lowerS.allMatches(a).length; + int bMatches = lowerS.allMatches(b).length; + if (aMatches == bMatches) return a.compareTo(b); + return bMatches - aMatches; + }, + ); int baseOffset = queryController.selection.baseOffset; queryController.text = s; queryController.selection = TextSelection( @@ -130,12 +141,17 @@ class _SearchScreen extends State { notSelected: notSelected, selected: selected, onAdded: (tag) => setState(() { + queryFocus.requestFocus(); + if (queryController.text.isNotEmpty) { + queryController.text = ''; + } selected.add(tag); selected.sort(); notSelected.remove(tag); _widget = _builder(queryController.text, selected, rebuild); }), onRemoved: (tag) => setState(() { + queryFocus.requestFocus(); selected.remove(tag); notSelected.add(tag); notSelected.sort(); From c7bda0b6fefd24069c65cd6ebaa8663a85eb9643 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Mon, 11 Mar 2024 23:38:50 +0000 Subject: [PATCH 053/131] Caps lock warning implemented --- lib/screens/add_account_screen.dart | 39 +++++++++++++++++++++++++---- lib/screens/login_screen.dart | 19 ++++++++++++-- 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/lib/screens/add_account_screen.dart b/lib/screens/add_account_screen.dart index 1b276acd..a8d29d0c 100644 --- a/lib/screens/add_account_screen.dart +++ b/lib/screens/add_account_screen.dart @@ -5,7 +5,7 @@ import 'package:flutter/services.dart'; import 'package:passy/common/common.dart'; import 'package:passy/passy_data/common.dart'; import 'package:passy/passy_data/loaded_account.dart'; -import 'package:passy/passy_flutter/passy_theme.dart'; +import 'package:passy/passy_flutter/passy_flutter.dart'; import 'package:passy/screens/common.dart'; import 'package:passy/screens/setup_screen.dart'; import 'package:encrypt/encrypt.dart' as crypt; @@ -27,6 +27,7 @@ class _AddAccountScreen extends State { String _username = ''; String _password = ''; String _confirmPassword = ''; + bool isCapsLockEnabled = false; void _addAccount() async { if (_username.isEmpty) { @@ -184,11 +185,25 @@ class _AddAccountScreen extends State { ), Row( children: [ + if (isCapsLockEnabled) + const PassyPadding(Icon( + Icons.arrow_upward_rounded, + color: Color.fromRGBO(255, 82, 82, 1), + )), Expanded( child: TextField( obscureText: true, - onChanged: (a) => - setState(() => _password = a), + onChanged: (a) => setState(() { + if (HardwareKeyboard + .instance.lockModesEnabled + .contains( + KeyboardLockMode.capsLock)) { + isCapsLockEnabled = true; + } else { + isCapsLockEnabled = false; + } + _password = a; + }), decoration: InputDecoration( hintText: localizations.password, ), @@ -201,14 +216,28 @@ class _AddAccountScreen extends State { ), Row( children: [ + if (isCapsLockEnabled) + const PassyPadding(Icon( + Icons.arrow_upward_rounded, + color: Color.fromRGBO(255, 82, 82, 1), + )), Expanded( child: TextField( obscureText: true, decoration: InputDecoration( hintText: localizations.confirmPassword, ), - onChanged: (a) => - setState(() => _confirmPassword = a), + onChanged: (a) => setState(() { + if (HardwareKeyboard + .instance.lockModesEnabled + .contains( + KeyboardLockMode.capsLock)) { + isCapsLockEnabled = true; + } else { + isCapsLockEnabled = false; + } + _confirmPassword = a; + }), onSubmitted: (value) => _addAccount(), inputFormatters: [ LengthLimitingTextInputFormatter(32), diff --git a/lib/screens/login_screen.dart b/lib/screens/login_screen.dart index 79d999b5..491bde23 100644 --- a/lib/screens/login_screen.dart +++ b/lib/screens/login_screen.dart @@ -46,6 +46,7 @@ class _LoginScreen extends State { String _username = data.info.value.lastUsername; FloatingActionButton? _bioAuthButton; final TextEditingController _passwordController = TextEditingController(); + bool isCapsLockEnabled = false; Future _bioAuth() async { if (Platform.isAndroid || Platform.isIOS) { @@ -365,12 +366,26 @@ class _LoginScreen extends State { ), Row( children: [ + if (isCapsLockEnabled) + const PassyPadding(Icon( + Icons.arrow_upward_rounded, + color: Color.fromRGBO(255, 82, 82, 1), + )), Expanded( child: TextField( controller: _passwordController, obscureText: true, - onChanged: (a) => - setState(() => _password = a), + onChanged: (a) => setState(() { + if (HardwareKeyboard + .instance.lockModesEnabled + .contains( + KeyboardLockMode.capsLock)) { + isCapsLockEnabled = true; + } else { + isCapsLockEnabled = false; + } + _password = a; + }), onSubmitted: (s) => login(), decoration: InputDecoration( hintText: localizations.password, From 024c6df87492d1488d2e925646834681a6a46dc0 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Tue, 12 Mar 2024 00:05:10 +0000 Subject: [PATCH 054/131] Update record_button.dart --- lib/passy_flutter/widgets/record_button.dart | 23 +++++++++++--------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/lib/passy_flutter/widgets/record_button.dart b/lib/passy_flutter/widgets/record_button.dart index 45f503f5..63a75e8e 100644 --- a/lib/passy_flutter/widgets/record_button.dart +++ b/lib/passy_flutter/widgets/record_button.dart @@ -50,16 +50,19 @@ class RecordButton extends StatelessWidget { builder: (_) => RecordDialog( value: value, highlightSpecial: isPassword, textAlign: valueAlign), ), - right: IconButton( - icon: const Icon(Icons.copy_rounded), - tooltip: localizations.copy, - onPressed: () { - Clipboard.setData(ClipboardData(text: value)); - showSnackBar(context, - message: '$title ${localizations.copied.toLowerCase()}', - icon: const Icon(Icons.copy_rounded, - color: PassyTheme.darkContentColor)); - }, + right: CircleAvatar(child: + IconButton( + padding: EdgeInsets.zero, + icon: const Icon(Icons.copy_rounded), + tooltip: localizations.copy, + onPressed: () { + Clipboard.setData(ClipboardData(text: value)); + showSnackBar(context, + message: '$title ${localizations.copied.toLowerCase()}', + icon: const Icon(Icons.copy_rounded, + color: PassyTheme.darkContentColor)); + }, + ), ), ); } From a352e22df961c05fe5d1a95280afe82ecefe6ef7 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Wed, 13 Mar 2024 02:23:14 +0000 Subject: [PATCH 055/131] Update change_password_screen.dart --- lib/screens/change_password_screen.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/screens/change_password_screen.dart b/lib/screens/change_password_screen.dart index 5449f11b..2b78e1d3 100644 --- a/lib/screens/change_password_screen.dart +++ b/lib/screens/change_password_screen.dart @@ -38,7 +38,7 @@ class _ChangePasswordScreen extends State { } if ((await data.createPasswordHash(_account.username, password: _password)) != - _account.passwordHash) { + data.getPasswordHash(_account.username)) { showSnackBar(context, message: localizations.incorrectPassword, icon: const Icon( From c7d78cc8adfc9610811705aef9384883a2c097aa Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Wed, 13 Mar 2024 02:48:53 +0000 Subject: [PATCH 056/131] Master key character limiting removed --- lib/screens/add_account_screen.dart | 6 ------ lib/screens/login_screen.dart | 3 --- 2 files changed, 9 deletions(-) diff --git a/lib/screens/add_account_screen.dart b/lib/screens/add_account_screen.dart index a8d29d0c..f50d8e57 100644 --- a/lib/screens/add_account_screen.dart +++ b/lib/screens/add_account_screen.dart @@ -207,9 +207,6 @@ class _AddAccountScreen extends State { decoration: InputDecoration( hintText: localizations.password, ), - inputFormatters: [ - LengthLimitingTextInputFormatter(32), - ], ), ) ], @@ -239,9 +236,6 @@ class _AddAccountScreen extends State { _confirmPassword = a; }), onSubmitted: (value) => _addAccount(), - inputFormatters: [ - LengthLimitingTextInputFormatter(32), - ], ), ), FloatingActionButton( diff --git a/lib/screens/login_screen.dart b/lib/screens/login_screen.dart index 491bde23..a5339b28 100644 --- a/lib/screens/login_screen.dart +++ b/lib/screens/login_screen.dart @@ -390,9 +390,6 @@ class _LoginScreen extends State { decoration: InputDecoration( hintText: localizations.password, ), - inputFormatters: [ - LengthLimitingTextInputFormatter(32), - ], autofocus: true, ), ), From 0baada40bf8c4e9f8f996dd8341aa9bd2160b08b Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Wed, 13 Mar 2024 04:03:57 +0000 Subject: [PATCH 057/131] LoadedAccount.importAegis() implemented --- lib/passy_data/convert_aegis.dart | 141 +++++++++++++++++++++++++++++ lib/passy_data/loaded_account.dart | 7 ++ 2 files changed, 148 insertions(+) create mode 100644 lib/passy_data/convert_aegis.dart diff --git a/lib/passy_data/convert_aegis.dart b/lib/passy_data/convert_aegis.dart new file mode 100644 index 00000000..71e39396 --- /dev/null +++ b/lib/passy_data/convert_aegis.dart @@ -0,0 +1,141 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:convert/convert.dart'; +import 'package:otp/otp.dart' as otp; +import 'package:passy/passy_data/password.dart'; +import 'package:passy/passy_data/tfa.dart'; +import 'package:pointycastle/key_derivators/scrypt.dart'; +import 'package:pointycastle/key_derivators/api.dart'; +import 'package:pointycastle/api.dart'; +import 'package:pointycastle/block/modes/gcm.dart'; +import 'package:pointycastle/block/aes.dart'; + +TFAType? _aegisTypeToTFAType(String type) { + switch (type) { + case 'totp': + return TFAType.TOTP; + case 'hotp': + return TFAType.HOTP; + case 'steam': + return TFAType.Steam; + } + return null; +} + +otp.Algorithm? _aegisAlgoToTFAType(String algo) { + switch (algo) { + case 'SHA1': + return otp.Algorithm.SHA1; + case 'SHA256': + return otp.Algorithm.SHA256; + case 'SHA512': + return otp.Algorithm.SHA512; + } + return null; +} + +List _convertAegis(Map db) { + String keyPrefix = '${DateTime.now().toUtc().toIso8601String()}-import'; + List result = []; + var entries = db['entries']; + int i = 0; + for (var entry in entries) { + var name = entry['name']; + var type = entry['type']; + TFAType? tfaType = _aegisTypeToTFAType(type); + if (tfaType == null) continue; + var info = entry['info']; + var algo = info['algo']; + otp.Algorithm? tfaAlgo = _aegisAlgoToTFAType(algo); + if (tfaAlgo == null) continue; + var secret = info['secret']; + var digits = info['digits']; + var period = info['period']; + TFA tfa = TFA( + type: tfaType, + algorithm: tfaAlgo, + secret: secret, + length: digits, + interval: period, + isGoogle: tfaType != TFAType.Steam, + ); + var issuer = info['issuer']; + var note = info['note']; + String additionalInfo = ''; + if (issuer != null) additionalInfo = issuer; + if (note != null) { + if (additionalInfo.isEmpty) { + additionalInfo = note; + } else { + additionalInfo += '\n\n$note'; + } + } + String key = '$keyPrefix-$i'; + result.add(Password( + key: key, + nickname: name, + tfa: tfa, + additionalInfo: additionalInfo, + )); + i++; + } + return result; +} + +List convertAegis({required File aegisFile, String? password}) { + String contents = aegisFile.readAsStringSync(); + var json = jsonDecode(contents); + var db = json['db']; + if (db is Map) { + return _convertAegis(db); + } + if (password == null) throw 'No password provided for encrypted Aegis export'; + var header = json['header']; + var slots = header['slots']; + Uint8List? decryptedKey; + for (var slot in slots) { + if (slot['type'] != 1) continue; + var key = slot['key']; + var keyParams = slot['key_params']; + var nonce = keyParams['nonce']; + var tag = keyParams['tag']; + var n = slot['n']; + var r = slot['r']; + var p = slot['p']; + var salt = slot['salt']; + var scrypt = Scrypt(); + scrypt.init( + ScryptParameters(n, r, p, 32, Uint8List.fromList(hex.decode(salt)))); + var derivedKey = scrypt.process(Uint8List.fromList(utf8.encode(password))); + var cipher = GCMBlockCipher(AESEngine()); + cipher.init( + false, + AEADParameters( + KeyParameter(derivedKey), + 16 * 8, + Uint8List.fromList(hex.decode(nonce)), + Uint8List(0), + )); + decryptedKey = cipher + .process(Uint8List.fromList([...hex.decode(key), ...hex.decode(tag)])); + } + var params = header['params']; + var nonce = params['nonce']; + var tag = params['tag']; + var cipher = GCMBlockCipher(AESEngine()); + cipher.init( + false, + AEADParameters( + KeyParameter(decryptedKey!), + 16 * 8, + Uint8List.fromList(hex.decode(nonce)), + Uint8List(0), + )); + var dbDecrypted = cipher + .process(Uint8List.fromList([...base64Decode(db), ...hex.decode(tag)])); + var dbDecoded = utf8.decode(dbDecrypted); + var dbJson = jsonDecode(dbDecoded); + return _convertAegis(dbJson); +} diff --git a/lib/passy_data/loaded_account.dart b/lib/passy_data/loaded_account.dart index 69ce3461..c0672c42 100644 --- a/lib/passy_data/loaded_account.dart +++ b/lib/passy_data/loaded_account.dart @@ -8,6 +8,7 @@ import 'package:crypton/crypton.dart'; import 'package:encrypt/encrypt.dart'; import 'package:kdbx/kdbx.dart'; import 'package:passy/passy_data/argon2_info.dart'; +import 'package:passy/passy_data/convert_aegis.dart'; import 'package:passy/passy_data/custom_field.dart'; import 'package:passy/passy_data/file_index.dart'; import 'package:passy/passy_data/file_meta.dart'; @@ -738,6 +739,12 @@ class LoadedAccount { await _history.save(); } + Future importAegis({required File aegisFile, String? password}) async { + List newPasswords = + convertAegis(aegisFile: aegisFile, password: password); + await setPasswords(newPasswords); + } + Future Function(PassyEntry value) setEntry(EntryType type) { switch (type) { case EntryType.password: From 304a3f1d0a35dd4ca99c538a1eda49f7ac839ab7 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Wed, 13 Mar 2024 11:35:04 +0000 Subject: [PATCH 058/131] Update import_screen.dart --- lib/screens/import_screen.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/screens/import_screen.dart b/lib/screens/import_screen.dart index 642accff..c7d680bb 100644 --- a/lib/screens/import_screen.dart +++ b/lib/screens/import_screen.dart @@ -114,7 +114,7 @@ class _ImportScreen extends State { padding: const EdgeInsets.only(right: 30), child: SvgPicture.asset( logoCircleSvg, - width: 30, + width: 24, colorFilter: const ColorFilter.mode( PassyTheme.lightContentColor, BlendMode.srcIn), ), From 3a01e697e86a69b53aa0ae82f1588857d563aaeb Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Wed, 13 Mar 2024 12:39:42 +0000 Subject: [PATCH 059/131] `context` argument removed from showSnackBar() --- lib/common/synchronization_wrapper.dart | 4 +- .../widgets/entries_screen_appbar.dart | 1 - .../widgets/entry_screen_appbar.dart | 1 - .../widgets/passy_file_widget.dart | 4 +- lib/passy_flutter/widgets/record_button.dart | 2 +- lib/screens/add_account_screen.dart | 6 --- lib/screens/add_file_screen.dart | 1 - lib/screens/biometric_auth_screen.dart | 4 +- lib/screens/change_password_screen.dart | 8 ++-- lib/screens/change_username_screen.dart | 4 +- lib/screens/common.dart | 46 +++++++++---------- lib/screens/confirm_import_screen.dart | 5 +- lib/screens/confirm_kdbx_export_screen.dart | 9 ++-- lib/screens/confirm_restore_screen.dart | 1 - lib/screens/csv_import_entries_screen.dart | 1 - lib/screens/csv_import_screen.dart | 4 -- lib/screens/export_screen.dart | 5 +- lib/screens/id_card_screen.dart | 4 +- lib/screens/identity_screen.dart | 4 +- lib/screens/key_derivation_screen.dart | 4 +- lib/screens/log_screen.dart | 2 +- lib/screens/login_screen.dart | 2 - lib/screens/main_screen.dart | 6 +-- lib/screens/note_screen.dart | 4 +- lib/screens/password_screen.dart | 4 +- lib/screens/passy_file_screen.dart | 2 +- lib/screens/payment_card_screen.dart | 4 +- lib/screens/remove_account_screen.dart | 2 +- lib/screens/server_connect_screen.dart | 11 +---- lib/screens/server_setup_screen.dart | 9 ---- lib/screens/servers_screen.dart | 4 +- lib/screens/settings_screen.dart | 1 - lib/screens/splash_screen.dart | 1 - lib/screens/unlock_screen.dart | 2 +- 34 files changed, 64 insertions(+), 108 deletions(-) diff --git a/lib/common/synchronization_wrapper.dart b/lib/common/synchronization_wrapper.dart index 2b0ea885..77340788 100644 --- a/lib/common/synchronization_wrapper.dart +++ b/lib/common/synchronization_wrapper.dart @@ -76,7 +76,7 @@ class SynchronizationWrapper { void _onSyncError(String log) { void _showLog() => navigatorKey.currentState! .pushNamed(LogScreen.routeName, arguments: log); - showSnackBar(_context, + showSnackBar( message: localizations.syncError, icon: const Icon(Icons.sync_problem_rounded, color: PassyTheme.darkContentColor), @@ -94,7 +94,6 @@ class SynchronizationWrapper { _hostAddress = HostAddress.parse(address); } catch (e) { showSnackBar( - _context, message: localizations.invalidAddressFormat, icon: const Icon(Icons.sync_problem_rounded, color: PassyTheme.darkContentColor), @@ -114,7 +113,6 @@ class SynchronizationWrapper { ); _sync!.connect(_hostAddress).onError((error, stackTrace) { showSnackBar( - _context, message: localizations.connectionFailed, icon: const Icon(Icons.sync_problem_rounded, color: PassyTheme.darkContentColor), diff --git a/lib/passy_flutter/widgets/entries_screen_appbar.dart b/lib/passy_flutter/widgets/entries_screen_appbar.dart index 6ad81842..ea5b3c84 100644 --- a/lib/passy_flutter/widgets/entries_screen_appbar.dart +++ b/lib/passy_flutter/widgets/entries_screen_appbar.dart @@ -44,7 +44,6 @@ class EntriesScreenAppBar extends StatelessWidget onPressed: () { if (!data.loadedAccount!.isRSAKeypairLoaded) { showSnackBar( - context, message: localizations.settingUpSynchronization, icon: const Icon(CupertinoIcons.clock_solid, color: PassyTheme.darkContentColor), diff --git a/lib/passy_flutter/widgets/entry_screen_appbar.dart b/lib/passy_flutter/widgets/entry_screen_appbar.dart index 7b5d6ce0..518a883f 100644 --- a/lib/passy_flutter/widgets/entry_screen_appbar.dart +++ b/lib/passy_flutter/widgets/entry_screen_appbar.dart @@ -52,7 +52,6 @@ class EntryScreenAppBar extends StatelessWidget implements PreferredSizeWidget { onPressed: () { if (!data.loadedAccount!.isRSAKeypairLoaded) { showSnackBar( - context, message: localizations.settingUpSynchronization, icon: const Icon(CupertinoIcons.clock_solid, color: PassyTheme.darkContentColor), diff --git a/lib/passy_flutter/widgets/passy_file_widget.dart b/lib/passy_flutter/widgets/passy_file_widget.dart index 3059af70..2178e486 100644 --- a/lib/passy_flutter/widgets/passy_file_widget.dart +++ b/lib/passy_flutter/widgets/passy_file_widget.dart @@ -80,7 +80,7 @@ class _PassyFileWidget extends State { case FileEntryType.photo: if (widget.name.endsWith('.svg')) { return InkWell( - onTap: () => showSnackBar(context, + onTap: () => showSnackBar( message: localizations.scrollOrPinchToZoom, icon: const Icon( Icons.zoom_in, @@ -102,7 +102,7 @@ class _PassyFileWidget extends State { ); } return InkWell( - onTap: () => showSnackBar(context, + onTap: () => showSnackBar( message: localizations.scrollOrPinchToZoom, icon: const Icon( Icons.zoom_in, diff --git a/lib/passy_flutter/widgets/record_button.dart b/lib/passy_flutter/widgets/record_button.dart index 63a75e8e..a512bda3 100644 --- a/lib/passy_flutter/widgets/record_button.dart +++ b/lib/passy_flutter/widgets/record_button.dart @@ -57,7 +57,7 @@ class RecordButton extends StatelessWidget { tooltip: localizations.copy, onPressed: () { Clipboard.setData(ClipboardData(text: value)); - showSnackBar(context, + showSnackBar( message: '$title ${localizations.copied.toLowerCase()}', icon: const Icon(Icons.copy_rounded, color: PassyTheme.darkContentColor)); diff --git a/lib/screens/add_account_screen.dart b/lib/screens/add_account_screen.dart index f50d8e57..50c34667 100644 --- a/lib/screens/add_account_screen.dart +++ b/lib/screens/add_account_screen.dart @@ -32,7 +32,6 @@ class _AddAccountScreen extends State { void _addAccount() async { if (_username.isEmpty) { showSnackBar( - context, message: localizations.usernameIsEmpty, icon: const Icon(Icons.person_rounded, color: PassyTheme.darkContentColor), @@ -41,7 +40,6 @@ class _AddAccountScreen extends State { } if (_username.length < 2) { showSnackBar( - context, message: localizations.usernameShorterThan2Letters, icon: const Icon(Icons.person_rounded, color: PassyTheme.darkContentColor), @@ -50,7 +48,6 @@ class _AddAccountScreen extends State { } if (data.hasAccount(_username)) { showSnackBar( - context, message: localizations.usernameAlreadyInUse, icon: const Icon(Icons.person_rounded, color: PassyTheme.darkContentColor), @@ -59,7 +56,6 @@ class _AddAccountScreen extends State { } if (_password.isEmpty) { showSnackBar( - context, message: localizations.passwordIsEmpty, icon: const Icon(Icons.lock_rounded, color: PassyTheme.darkContentColor), @@ -68,7 +64,6 @@ class _AddAccountScreen extends State { } if (_password != _confirmPassword) { showSnackBar( - context, message: localizations.passwordsDoNotMatch, icon: const Icon(Icons.lock_rounded, color: PassyTheme.darkContentColor), @@ -83,7 +78,6 @@ class _AddAccountScreen extends State { } catch (e, s) { if (!mounted) return; showSnackBar( - context, message: localizations.couldNotAddAccount, icon: const Icon(Icons.error_outline_rounded, color: PassyTheme.darkContentColor), diff --git a/lib/screens/add_file_screen.dart b/lib/screens/add_file_screen.dart index c3dca38e..d43035ec 100644 --- a/lib/screens/add_file_screen.dart +++ b/lib/screens/add_file_screen.dart @@ -96,7 +96,6 @@ class _AddFileScreen extends State { await _account.addFile(args.file, useIsolate: true, meta: _fileMeta); } catch (e, s) { showSnackBar( - context, message: localizations.failedToAddFile, icon: const Icon(Icons.save_rounded, color: PassyTheme.darkContentColor), diff --git a/lib/screens/biometric_auth_screen.dart b/lib/screens/biometric_auth_screen.dart index 03635d9f..22606c37 100644 --- a/lib/screens/biometric_auth_screen.dart +++ b/lib/screens/biometric_auth_screen.dart @@ -32,7 +32,7 @@ class _BiometricAuthScreen extends State { if ((await data.createPasswordHash(_username, password: _password)) .toString() != data.loadedAccount!.passwordHash) { - showSnackBar(context, + showSnackBar( message: localizations.incorrectPassword, icon: const Icon(Icons.lock_rounded, color: PassyTheme.darkContentColor)); @@ -47,7 +47,7 @@ class _BiometricAuthScreen extends State { } catch (e) { Future.delayed(const Duration(seconds: 2)) .then((value) => MainScreen.shouldLockScreen = true); - showSnackBar(context, + showSnackBar( message: localizations.couldNotAuthenticate, icon: const Icon(Icons.fingerprint_rounded, color: PassyTheme.darkContentColor)); diff --git a/lib/screens/change_password_screen.dart b/lib/screens/change_password_screen.dart index 2b78e1d3..9e0467b5 100644 --- a/lib/screens/change_password_screen.dart +++ b/lib/screens/change_password_screen.dart @@ -28,7 +28,7 @@ class _ChangePasswordScreen extends State { void _onConfirmPressed() async { if (!_isBackupComplete) { - showSnackBar(context, + showSnackBar( message: localizations.backupYourAccountBeforeProceeding, icon: const Icon( Icons.save_rounded, @@ -39,7 +39,7 @@ class _ChangePasswordScreen extends State { if ((await data.createPasswordHash(_account.username, password: _password)) != data.getPasswordHash(_account.username)) { - showSnackBar(context, + showSnackBar( message: localizations.incorrectPassword, icon: const Icon( Icons.lock_rounded, @@ -48,7 +48,7 @@ class _ChangePasswordScreen extends State { return; } if (_newPassword.isEmpty) { - showSnackBar(context, + showSnackBar( message: localizations.passwordIsEmpty, icon: const Icon( Icons.lock_rounded, @@ -57,7 +57,7 @@ class _ChangePasswordScreen extends State { return; } if (_newPassword != _newPasswordConfirm) { - showSnackBar(context, + showSnackBar( message: localizations.passwordsDoNotMatch, icon: const Icon( Icons.lock_rounded, diff --git a/lib/screens/change_username_screen.dart b/lib/screens/change_username_screen.dart index a5f75d67..1cabc509 100644 --- a/lib/screens/change_username_screen.dart +++ b/lib/screens/change_username_screen.dart @@ -30,14 +30,14 @@ class _ChangeUsernameScreen extends State { labelText: localizations.newUsername, onConfirmPressed: (context, value) { if (value.length < 2) { - showSnackBar(context, + showSnackBar( message: localizations.usernameShorterThan2Letters, icon: const Icon(Icons.person_rounded, color: PassyTheme.darkContentColor)); return; } if (data.hasAccount(value)) { - showSnackBar(context, + showSnackBar( message: localizations.usernameAlreadyInUse, icon: const Icon(Icons.person_rounded, color: PassyTheme.darkContentColor)); diff --git a/lib/screens/common.dart b/lib/screens/common.dart index 5bad5284..db0415b8 100644 --- a/lib/screens/common.dart +++ b/lib/screens/common.dart @@ -126,20 +126,19 @@ Future backupAccount( outputDirectoryPath: _buDir, fileName: _fileName, ); - showSnackBar(context, + showSnackBar( message: 'Backup saved', icon: const Icon(Icons.save_rounded, color: PassyTheme.darkContentColor)); return _buDir; } catch (e, s) { if (e is FileSystemException) { - showSnackBar(context, + showSnackBar( message: 'Access denied, try another folder', icon: const Icon(Icons.save_rounded, color: PassyTheme.darkContentColor)); } else { showSnackBar( - context, message: 'Could not backup', icon: const Icon(Icons.save_rounded, color: PassyTheme.darkContentColor), @@ -224,8 +223,7 @@ Result? qrResultFromImage(imglib.Image image) { } } -ScaffoldFeatureController showSnackBar( - BuildContext context, { +ScaffoldFeatureController? showSnackBar({ required String message, TextStyle? textStyle, required Widget icon, @@ -233,6 +231,8 @@ ScaffoldFeatureController showSnackBar( Duration duration = const Duration(milliseconds: 4000), Color? backgroundColor, }) { + BuildContext? context = navigatorKey.currentContext; + if (context == null) return null; ScaffoldMessenger.of(context).clearSnackBars(); return ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Row(children: [ @@ -273,7 +273,7 @@ List idCardPopupMenuBuilder( onTap: () { Clipboard.setData(ClipboardData( text: data.loadedAccount!.getIDCard(idCardMeta.key)!.idNumber)); - showSnackBar(context, + showSnackBar( message: 'ID number copied', icon: const Icon(Icons.copy_rounded, color: PassyTheme.darkContentColor)); @@ -285,7 +285,7 @@ List idCardPopupMenuBuilder( icon: const Icon(Icons.person_outline_rounded), onTap: () { Clipboard.setData(ClipboardData(text: idCardMeta.name)); - showSnackBar(context, + showSnackBar( message: 'Name copied', icon: const Icon(Icons.copy_rounded, color: PassyTheme.darkContentColor)); @@ -315,7 +315,7 @@ List identityPopupMenuBuilder( _name += ' ${_identity.lastName}'; } Clipboard.setData(ClipboardData(text: _name)); - showSnackBar(context, + showSnackBar( message: 'Name copied', icon: const Icon(Icons.copy_rounded, color: PassyTheme.darkContentColor)); @@ -327,7 +327,7 @@ List identityPopupMenuBuilder( onTap: () { Clipboard.setData(ClipboardData( text: data.loadedAccount!.getIdentity(identityMeta.key)!.email)); - showSnackBar(context, + showSnackBar( message: 'Email copied', icon: const Icon(Icons.copy_rounded, color: PassyTheme.darkContentColor)); @@ -339,7 +339,7 @@ List identityPopupMenuBuilder( icon: const Icon(Icons.house_outlined), onTap: () { Clipboard.setData(ClipboardData(text: identityMeta.firstAddressLine)); - showSnackBar(context, + showSnackBar( message: 'Address line copied', icon: const Icon(Icons.copy_rounded, color: PassyTheme.darkContentColor)); @@ -357,7 +357,7 @@ List notePopupMenuBuilder( onTap: () { Clipboard.setData(ClipboardData( text: data.loadedAccount!.getNote(identityMeta.key)!.note)); - showSnackBar(context, + showSnackBar( message: 'Note copied', icon: const Icon(Icons.copy_rounded, color: PassyTheme.darkContentColor)); @@ -377,7 +377,7 @@ List passwordPopupMenuBuilder( Clipboard.setData(ClipboardData( text: data.loadedAccount!.getPassword(passwordMeta.key)!.username)); - showSnackBar(context, + showSnackBar( message: 'Username copied', icon: const Icon(Icons.copy_rounded, color: PassyTheme.darkContentColor)); @@ -389,7 +389,7 @@ List passwordPopupMenuBuilder( onTap: () { Clipboard.setData(ClipboardData( text: data.loadedAccount!.getPassword(passwordMeta.key)!.email)); - showSnackBar(context, + showSnackBar( message: 'Email copied', icon: const Icon(Icons.copy_rounded, color: PassyTheme.darkContentColor)); @@ -401,7 +401,7 @@ List passwordPopupMenuBuilder( onTap: () { Clipboard.setData(ClipboardData( text: data.loadedAccount!.getPassword(passwordMeta.key)!.password)); - showSnackBar(context, + showSnackBar( message: 'Password copied', icon: const Icon(Icons.copy_rounded, color: PassyTheme.darkContentColor)); @@ -437,7 +437,7 @@ List paymentCardPopupMenuBuilder( text: data.loadedAccount! .getPaymentCard(paymentCardMeta.key)! .cardNumber)); - showSnackBar(context, + showSnackBar( message: 'Card number copied', icon: const Icon(Icons.copy_rounded, color: PassyTheme.darkContentColor)); @@ -450,7 +450,7 @@ List paymentCardPopupMenuBuilder( onTap: () { Clipboard.setData( ClipboardData(text: paymentCardMeta.cardholderName)); - showSnackBar(context, + showSnackBar( message: 'Card holder name copied', icon: const Icon(Icons.copy_rounded, color: PassyTheme.darkContentColor)); @@ -462,7 +462,7 @@ List paymentCardPopupMenuBuilder( icon: const Icon(Icons.date_range_outlined), onTap: () { Clipboard.setData(ClipboardData(text: paymentCardMeta.exp)); - showSnackBar(context, + showSnackBar( message: 'Expiration date copied', icon: const Icon(Icons.copy_rounded, color: PassyTheme.darkContentColor)); @@ -475,7 +475,7 @@ List paymentCardPopupMenuBuilder( Clipboard.setData(ClipboardData( text: data.loadedAccount!.getPaymentCard(paymentCardMeta.key)!.cvv)); - showSnackBar(context, + showSnackBar( message: 'CVV copied', icon: const Icon(Icons.copy_rounded, color: PassyTheme.darkContentColor)); @@ -503,7 +503,7 @@ List filePopupMenuBuilder( await (onChanged?.call()); if (!context.mounted) return; Navigator.pop(context); - showSnackBar(context, + showSnackBar( message: 'File renamed', icon: const Icon(Icons.edit_outlined, color: PassyTheme.darkContentColor)); @@ -540,7 +540,7 @@ List filePopupMenuBuilder( await (onChanged?.call()); if (!context.mounted) return; Navigator.pop(context); - showSnackBar(context, + showSnackBar( message: localizations.exportSaved, icon: const Icon(Icons.ios_share_rounded, color: PassyTheme.darkContentColor)); @@ -585,7 +585,7 @@ List filePopupMenuBuilder( await (onChanged?.call()); if (!context.mounted) return; Navigator.pop(context); - showSnackBar(context, + showSnackBar( message: 'Folder removed', icon: const Icon(Icons.delete_outline_rounded, color: PassyTheme.darkContentColor)); @@ -595,7 +595,7 @@ List filePopupMenuBuilder( await (onChanged?.call()); if (!context.mounted) return; Navigator.pop(context); - showSnackBar(context, + showSnackBar( message: 'File removed', icon: const Icon(Icons.delete_outline_rounded, color: PassyTheme.darkContentColor)); @@ -765,7 +765,6 @@ setOnError(BuildContext context) { FlutterError.presentError(e); try { showSnackBar( - navigatorKey.currentContext!, message: localizations.somethingWentWrong, icon: const Icon(Icons.error_outline_rounded, color: PassyTheme.darkContentColor), @@ -781,7 +780,6 @@ setOnError(BuildContext context) { PlatformDispatcher.instance.onError = (error, stack) { try { showSnackBar( - navigatorKey.currentContext!, message: localizations.somethingWentWrong, icon: const Icon(Icons.error_outline_rounded, color: PassyTheme.darkContentColor), diff --git a/lib/screens/confirm_import_screen.dart b/lib/screens/confirm_import_screen.dart index 9e67c0e8..8c04e18f 100644 --- a/lib/screens/confirm_import_screen.dart +++ b/lib/screens/confirm_import_screen.dart @@ -60,7 +60,6 @@ class _ConfirmImportScreen extends State { context, (route) => route.settings.name == MainScreen.routeName); Navigator.pushReplacementNamed(context, LoginScreen.routeName); showSnackBar( - context, message: localizations.imported, icon: const Icon(Icons.check, color: PassyTheme.darkContentColor), ); @@ -74,16 +73,16 @@ class _ConfirmImportScreen extends State { Navigator.popUntil(context, (route) => route.settings.name == ImportScreen.routeName); showSnackBar( - context, message: localizations.imported, icon: const Icon(Icons.check, color: PassyTheme.darkContentColor), ); break; } } catch (e, s) { + await Future.delayed(const Duration(milliseconds: 200)); + if (!mounted) return; Navigator.pop(context); showSnackBar( - context, message: localizations.couldNotImportAccount, icon: const Icon(Icons.download_for_offline_outlined, color: PassyTheme.darkContentColor), diff --git a/lib/screens/confirm_kdbx_export_screen.dart b/lib/screens/confirm_kdbx_export_screen.dart index ee105534..77fee1f9 100644 --- a/lib/screens/confirm_kdbx_export_screen.dart +++ b/lib/screens/confirm_kdbx_export_screen.dart @@ -30,7 +30,7 @@ class _ConfirmKdbxExportScreen extends State { void _onConfirmPressed() async { if (_newPassword.isEmpty) { - showSnackBar(context, + showSnackBar( message: localizations.passwordIsEmpty, icon: const Icon( Icons.lock_rounded, @@ -39,7 +39,7 @@ class _ConfirmKdbxExportScreen extends State { return; } if (_newPassword != _newPasswordConfirm) { - showSnackBar(context, + showSnackBar( message: localizations.passwordsDoNotMatch, icon: const Icon( Icons.lock_rounded, @@ -76,19 +76,18 @@ class _ConfirmKdbxExportScreen extends State { fileName: path.basename(_file.path)); Navigator.pop(context); Navigator.pop(context); - showSnackBar(context, + showSnackBar( message: localizations.exportSaved, icon: const Icon(Icons.ios_share_rounded, color: PassyTheme.darkContentColor)); } catch (e, s) { if (e is FileSystemException) { - showSnackBar(context, + showSnackBar( message: localizations.accessDeniedTryAnotherFolder, icon: const Icon(Icons.ios_share_rounded, color: PassyTheme.darkContentColor)); } else { showSnackBar( - context, message: localizations.couldNotExport, icon: const Icon(Icons.ios_share_rounded, color: PassyTheme.darkContentColor), diff --git a/lib/screens/confirm_restore_screen.dart b/lib/screens/confirm_restore_screen.dart index 79672ce5..e90f7437 100644 --- a/lib/screens/confirm_restore_screen.dart +++ b/lib/screens/confirm_restore_screen.dart @@ -53,7 +53,6 @@ class _ConfirmRestoreScreen extends State { }, onError: (e, s) { showSnackBar( - context, message: localizations.couldNotRestoreAccount, icon: const Icon(Icons.settings_backup_restore_rounded, color: PassyTheme.darkContentColor), diff --git a/lib/screens/csv_import_entries_screen.dart b/lib/screens/csv_import_entries_screen.dart index 4bb74c8d..7f604f48 100644 --- a/lib/screens/csv_import_entries_screen.dart +++ b/lib/screens/csv_import_entries_screen.dart @@ -240,7 +240,6 @@ class _CSVImportEntriesScreen extends State { } catch (e, s) { Navigator.pop(context); showSnackBar( - context, message: localizations.couldNotImportAccount, icon: const Icon(Icons.download_for_offline_outlined, color: PassyTheme.darkContentColor), diff --git a/lib/screens/csv_import_screen.dart b/lib/screens/csv_import_screen.dart index 01b0d1be..abbab935 100644 --- a/lib/screens/csv_import_screen.dart +++ b/lib/screens/csv_import_screen.dart @@ -39,7 +39,6 @@ class _CSVImportScreen extends State { ); } catch (e, s) { showSnackBar( - context, message: localizations.couldNotImportAccount, icon: const Icon(Icons.download_for_offline_outlined, color: PassyTheme.darkContentColor), @@ -62,7 +61,6 @@ class _CSVImportScreen extends State { fileData = (await file.readAsString()).replaceAll('\r', ''); } catch (e, s) { showSnackBar( - context, message: localizations.couldNotImportAccount, icon: const Icon(Icons.download_for_offline_outlined, color: PassyTheme.darkContentColor), @@ -80,7 +78,6 @@ class _CSVImportScreen extends State { .convert(fileData, shouldParseNumbers: false, eol: '\n'); } catch (e, s) { showSnackBar( - context, message: localizations.couldNotImportAccount, icon: const Icon(Icons.download_for_offline_outlined, color: PassyTheme.darkContentColor), @@ -94,7 +91,6 @@ class _CSVImportScreen extends State { } if (fileDataDecoded.isEmpty) { showSnackBar( - context, message: localizations.noCSVDataFound, icon: const Icon(Icons.download_for_offline_outlined, color: PassyTheme.darkContentColor), diff --git a/lib/screens/export_screen.dart b/lib/screens/export_screen.dart index 0205621a..81905f39 100644 --- a/lib/screens/export_screen.dart +++ b/lib/screens/export_screen.dart @@ -114,19 +114,18 @@ class _ExportScreen extends State { fileName: path.basename(_expFile)); break; } - showSnackBar(context, + showSnackBar( message: localizations.exportSaved, icon: const Icon(Icons.ios_share_rounded, color: PassyTheme.darkContentColor)); } catch (e, s) { if (e is FileSystemException) { - showSnackBar(context, + showSnackBar( message: localizations.accessDeniedTryAnotherFolder, icon: const Icon(Icons.ios_share_rounded, color: PassyTheme.darkContentColor)); } else { showSnackBar( - context, message: localizations.couldNotExport, icon: const Icon(Icons.ios_share_rounded, color: PassyTheme.darkContentColor), diff --git a/lib/screens/id_card_screen.dart b/lib/screens/id_card_screen.dart index 2f570a29..ac60d661 100644 --- a/lib/screens/id_card_screen.dart +++ b/lib/screens/id_card_screen.dart @@ -114,7 +114,7 @@ class _IDCardScreen extends State { onFavoritePressed: () async { if (isFavorite) { await _account.removeFavoriteIDCard(_idCard!.key); - showSnackBar(context, + showSnackBar( message: localizations.removedFromFavorites, icon: const Icon( Icons.star_outline_rounded, @@ -122,7 +122,7 @@ class _IDCardScreen extends State { )); } else { await _account.addFavoriteIDCard(_idCard!.key); - showSnackBar(context, + showSnackBar( message: localizations.addedToFavorites, icon: const Icon( Icons.star_rounded, diff --git a/lib/screens/identity_screen.dart b/lib/screens/identity_screen.dart index 3fccec4e..cc292e5d 100644 --- a/lib/screens/identity_screen.dart +++ b/lib/screens/identity_screen.dart @@ -115,7 +115,7 @@ class _IdentityScreen extends State { onFavoritePressed: () async { if (isFavorite) { await _account.removeFavoriteIdentity(_identity!.key); - showSnackBar(context, + showSnackBar( message: localizations.removedFromFavorites, icon: const Icon( Icons.star_outline_rounded, @@ -123,7 +123,7 @@ class _IdentityScreen extends State { )); } else { await _account.addFavoriteIdentity(_identity!.key); - showSnackBar(context, + showSnackBar( message: localizations.addedToFavorites, icon: const Icon( Icons.star_rounded, diff --git a/lib/screens/key_derivation_screen.dart b/lib/screens/key_derivation_screen.dart index ef21e231..87f48b6e 100644 --- a/lib/screens/key_derivation_screen.dart +++ b/lib/screens/key_derivation_screen.dart @@ -34,7 +34,7 @@ class _KeyDerivationScreen extends State { Future _onConfirmPressed() async { if (!_isBackupComplete) { - showSnackBar(context, + showSnackBar( message: localizations.backupYourAccountBeforeProceeding, icon: const Icon( Icons.save_rounded, @@ -45,7 +45,7 @@ class _KeyDerivationScreen extends State { if ((await data.createPasswordHash(_account.username, password: _password)) != _account.passwordHash) { - showSnackBar(context, + showSnackBar( message: localizations.incorrectPassword, icon: const Icon( Icons.lock_rounded, diff --git a/lib/screens/log_screen.dart b/lib/screens/log_screen.dart index 0b1d7903..6307fc5b 100644 --- a/lib/screens/log_screen.dart +++ b/lib/screens/log_screen.dart @@ -33,7 +33,7 @@ class LogScreen extends StatelessWidget { tooltip: localizations.copy, onPressed: () { Clipboard.setData(ClipboardData(text: '```\n$_log\n```')); - showSnackBar(context, + showSnackBar( message: '${localizations.log} ${localizations.copied.toLowerCase()}', icon: const Icon(Icons.copy_rounded, diff --git a/lib/screens/login_screen.dart b/lib/screens/login_screen.dart index a5339b28..8ddfd536 100644 --- a/lib/screens/login_screen.dart +++ b/lib/screens/login_screen.dart @@ -169,7 +169,6 @@ class _LoginScreen extends State { } if (_isPasswordWrong) { showSnackBar( - context, message: localizations.incorrectPassword, icon: const Icon(Icons.lock_rounded, color: PassyTheme.darkContentColor), @@ -217,7 +216,6 @@ class _LoginScreen extends State { Navigator.pushReplacementNamed(context, MainScreen.routeName); } catch (e, s) { showSnackBar( - context, message: localizations.couldNotLogin, icon: const Icon(Icons.lock_rounded, color: PassyTheme.darkContentColor), diff --git a/lib/screens/main_screen.dart b/lib/screens/main_screen.dart index 918b216f..742000cb 100644 --- a/lib/screens/main_screen.dart +++ b/lib/screens/main_screen.dart @@ -15,7 +15,6 @@ import 'package:passy/passy_data/payment_card.dart'; import 'package:passy/passy_flutter/search_entry_data.dart'; import 'package:passy/screens/common.dart'; import 'package:passy/passy_flutter/widgets/widgets.dart'; -import 'package:passy/screens/files_screen.dart'; import 'package:passy/screens/id_card_screen.dart'; import 'package:passy/screens/identity_screen.dart'; import 'package:passy/screens/login_screen.dart'; @@ -197,7 +196,8 @@ class _MainScreen extends State ); } - Widget _favoritesSearchBuilder(String terms, List tags, void Function() setState) { + Widget _favoritesSearchBuilder( + String terms, List tags, void Function() setState) { if (!_account.hasFavorites) { return CustomScrollView( slivers: [ @@ -505,7 +505,6 @@ class _MainScreen extends State onPressed: () { if (!_account.isRSAKeypairLoaded) { showSnackBar( - context, message: localizations.settingUpSynchronization, icon: const Icon(CupertinoIcons.clock_solid, color: PassyTheme.darkContentColor), @@ -628,7 +627,6 @@ class _MainScreen extends State onPressed: () { if (!_account.isRSAKeypairLoaded) { showSnackBar( - context, message: localizations.settingUpSynchronization, icon: const Icon(CupertinoIcons.clock_solid, color: PassyTheme.darkContentColor), diff --git a/lib/screens/note_screen.dart b/lib/screens/note_screen.dart index b9432fdf..2e2a2828 100644 --- a/lib/screens/note_screen.dart +++ b/lib/screens/note_screen.dart @@ -112,7 +112,7 @@ class _NoteScreen extends State { onFavoritePressed: () async { if (isFavorite) { await _account.removeFavoriteNote(_note!.key); - showSnackBar(context, + showSnackBar( message: localizations.removedFromFavorites, icon: const Icon( Icons.star_outline_rounded, @@ -120,7 +120,7 @@ class _NoteScreen extends State { )); } else { await _account.addFavoriteNote(_note!.key); - showSnackBar(context, + showSnackBar( message: localizations.addedToFavorites, icon: const Icon( Icons.star_rounded, diff --git a/lib/screens/password_screen.dart b/lib/screens/password_screen.dart index 53b19097..6b224c20 100644 --- a/lib/screens/password_screen.dart +++ b/lib/screens/password_screen.dart @@ -235,7 +235,7 @@ class _PasswordScreen extends State { onFavoritePressed: () async { if (isFavorite) { await _account.removeFavoritePassword(password!.key); - showSnackBar(context, + showSnackBar( message: localizations.removedFromFavorites, icon: const Icon( Icons.star_outline_rounded, @@ -243,7 +243,7 @@ class _PasswordScreen extends State { )); } else { await _account.addFavoritePassword(password!.key); - showSnackBar(context, + showSnackBar( message: localizations.addedToFavorites, icon: const Icon( Icons.star_rounded, diff --git a/lib/screens/passy_file_screen.dart b/lib/screens/passy_file_screen.dart index 6c20fe62..b3619e2f 100644 --- a/lib/screens/passy_file_screen.dart +++ b/lib/screens/passy_file_screen.dart @@ -60,7 +60,7 @@ class _PassyFileScreen extends State { await Future.delayed(const Duration(milliseconds: 200)); await _account.exportFile(args.key, file: File(expFile)); Navigator.pop(context); - showSnackBar(context, + showSnackBar( message: localizations.exportSaved, icon: const Icon(Icons.ios_share_rounded, color: PassyTheme.darkContentColor)); diff --git a/lib/screens/payment_card_screen.dart b/lib/screens/payment_card_screen.dart index f76526c6..22f5c619 100644 --- a/lib/screens/payment_card_screen.dart +++ b/lib/screens/payment_card_screen.dart @@ -114,7 +114,7 @@ class _PaymentCardScreen extends State { onFavoritePressed: () async { if (isFavorite) { await _account.removeFavoritePaymentCard(_paymentCard!.key); - showSnackBar(context, + showSnackBar( message: localizations.removedFromFavorites, icon: const Icon( Icons.star_outline_rounded, @@ -122,7 +122,7 @@ class _PaymentCardScreen extends State { )); } else { await _account.addFavoritePaymentCard(_paymentCard!.key); - showSnackBar(context, + showSnackBar( message: localizations.addedToFavorites, icon: const Icon( Icons.star_rounded, diff --git a/lib/screens/remove_account_screen.dart b/lib/screens/remove_account_screen.dart index f6458964..d696bbd3 100644 --- a/lib/screens/remove_account_screen.dart +++ b/lib/screens/remove_account_screen.dart @@ -46,7 +46,7 @@ class _RemoveAccountScreen extends State { onBackPressed: (context) => Navigator.pop(context), onConfirmPressed: (context, value) { if (value != _username) { - showSnackBar(context, + showSnackBar( message: localizations.usernamesDoNotMatch, icon: const Icon(Icons.error_outline_rounded, color: PassyTheme.darkContentColor)); diff --git a/lib/screens/server_connect_screen.dart b/lib/screens/server_connect_screen.dart index 6806fddc..bd8e9526 100644 --- a/lib/screens/server_connect_screen.dart +++ b/lib/screens/server_connect_screen.dart @@ -29,14 +29,14 @@ class _ServerConnectScreen extends State { Future _onConnectPressed({bool testRun = false}) async { if (!testRun) { if (_nickname.isEmpty) { - showSnackBar(context, + showSnackBar( message: localizations.nicknameCanNotBeEmpty, icon: const Icon(Icons.desktop_windows_rounded, color: PassyTheme.darkContentColor)); return; } if (_account.sync2d0d0ServerInfo.keys.contains(_nickname)) { - showSnackBar(context, + showSnackBar( message: localizations.nicknameAlreadyInUse, icon: const Icon(Icons.desktop_windows_rounded, color: PassyTheme.darkContentColor)); @@ -46,7 +46,6 @@ class _ServerConnectScreen extends State { String? address = _address; if (address == null) { showSnackBar( - context, message: localizations.hostAddressIsEmpty, icon: const Icon(Icons.desktop_windows_rounded, color: PassyTheme.darkContentColor), @@ -55,7 +54,6 @@ class _ServerConnectScreen extends State { } if (address.isEmpty) { showSnackBar( - context, message: localizations.hostAddressIsEmpty, icon: const Icon(Icons.desktop_windows_rounded, color: PassyTheme.darkContentColor), @@ -64,7 +62,6 @@ class _ServerConnectScreen extends State { } if (_port == 0) { showSnackBar( - context, message: localizations.invalidPortSpecified, icon: const Icon(Icons.numbers_rounded, color: PassyTheme.darkContentColor), @@ -75,7 +72,6 @@ class _ServerConnectScreen extends State { await _account.testSynchronizationConnection2d0d0(address, _port); } catch (e, s) { showSnackBar( - context, message: localizations.couldNotConnectToServer, icon: const Icon( Icons.cast_rounded, @@ -93,7 +89,6 @@ class _ServerConnectScreen extends State { setState(() => _connectionChecked = true); if (testRun) return; showSnackBar( - context, message: localizations.connecting, icon: const Icon(Icons.cast_rounded, color: PassyTheme.darkContentColor), ); @@ -103,7 +98,6 @@ class _ServerConnectScreen extends State { } catch (e, s) { if (navigatorKey.currentContext == null) return; showSnackBar( - navigatorKey.currentContext!, message: localizations.couldNotConnectToServer, icon: const Icon(Icons.cast_rounded, color: PassyTheme.darkContentColor), @@ -121,7 +115,6 @@ class _ServerConnectScreen extends State { await _account.saveSettings(); if (navigatorKey.currentContext == null) return; showSnackBar( - navigatorKey.currentContext!, message: localizations.connectionEstablished, icon: const Icon(Icons.cast_rounded, color: PassyTheme.darkContentColor), ); diff --git a/lib/screens/server_setup_screen.dart b/lib/screens/server_setup_screen.dart index 3819743f..44f25236 100644 --- a/lib/screens/server_setup_screen.dart +++ b/lib/screens/server_setup_screen.dart @@ -30,7 +30,6 @@ class _ServerSetupScreen extends State { String? address = _address; if (address == null) { showSnackBar( - context, message: localizations.hostAddressIsEmpty, icon: const Icon(Icons.desktop_windows_rounded, color: PassyTheme.darkContentColor), @@ -39,7 +38,6 @@ class _ServerSetupScreen extends State { } if (address.isEmpty) { showSnackBar( - context, message: localizations.hostAddressIsEmpty, icon: const Icon(Icons.desktop_windows_rounded, color: PassyTheme.darkContentColor), @@ -48,7 +46,6 @@ class _ServerSetupScreen extends State { } if (_port == 0) { showSnackBar( - context, message: localizations.invalidPortSpecified, icon: const Icon(Icons.numbers_rounded, color: PassyTheme.darkContentColor), @@ -69,14 +66,12 @@ class _ServerSetupScreen extends State { port: _port, ); showSnackBar( - context, message: localizations.serverInstalled, icon: const Icon(Icons.install_desktop_rounded, color: PassyTheme.darkContentColor), ); } catch (e, s) { showSnackBar( - context, message: localizations.couldNotInstallServer, icon: const Icon(Icons.install_desktop_rounded, color: PassyTheme.darkContentColor), @@ -94,7 +89,6 @@ class _ServerSetupScreen extends State { String? address = _address; if (address == null) { showSnackBar( - context, message: localizations.hostAddressIsEmpty, icon: const Icon(Icons.desktop_windows_rounded, color: PassyTheme.darkContentColor), @@ -103,7 +97,6 @@ class _ServerSetupScreen extends State { } if (address.isEmpty) { showSnackBar( - context, message: localizations.hostAddressIsEmpty, icon: const Icon(Icons.desktop_windows_rounded, color: PassyTheme.darkContentColor), @@ -112,7 +105,6 @@ class _ServerSetupScreen extends State { } if (_port == 0) { showSnackBar( - context, message: localizations.invalidPortSpecified, icon: const Icon(Icons.numbers_rounded, color: PassyTheme.darkContentColor), @@ -123,7 +115,6 @@ class _ServerSetupScreen extends State { await _account.testSynchronizationConnection2d0d0(address, _port); } catch (e, s) { showSnackBar( - context, message: localizations.couldNotConnectToServer, icon: const Icon( Icons.cast_rounded, diff --git a/lib/screens/servers_screen.dart b/lib/screens/servers_screen.dart index f54df5a6..30d09d0d 100644 --- a/lib/screens/servers_screen.dart +++ b/lib/screens/servers_screen.dart @@ -67,7 +67,7 @@ class _ServersScreen extends State { _syncInterval = int.parse(_syncIntervalString); } if (_syncInterval < 1) { - showSnackBar(context, + showSnackBar( message: '${localizations.intervalIsLessThan}5 ${localizations.seconds.toLowerCase()}', icon: @@ -76,7 +76,7 @@ class _ServersScreen extends State { } if (_syncIntervalUnits == IntervalUnit.seconds) { if (_syncInterval < 5) { - showSnackBar(context, + showSnackBar( message: '${localizations.intervalIsLessThan}5 ${localizations.seconds.toLowerCase()}', icon: const Icon(Icons.timelapse, diff --git a/lib/screens/settings_screen.dart b/lib/screens/settings_screen.dart index 5a50c1e2..6225a0cb 100644 --- a/lib/screens/settings_screen.dart +++ b/lib/screens/settings_screen.dart @@ -147,7 +147,6 @@ class _SettingsScreen extends State { onPressed: () { if (!data.loadedAccount!.isRSAKeypairLoaded) { showSnackBar( - context, message: localizations.settingUpSynchronization, icon: const Icon(CupertinoIcons.clock_solid, color: PassyTheme.darkContentColor), diff --git a/lib/screens/splash_screen.dart b/lib/screens/splash_screen.dart index c956a2a3..025bc74c 100644 --- a/lib/screens/splash_screen.dart +++ b/lib/screens/splash_screen.dart @@ -269,7 +269,6 @@ class SplashScreen extends StatelessWidget { if (hasAccess != true) { if (context.mounted) { showSnackBar( - context, message: localizations.unableToConnectBrowserExtension, icon: const Icon(Icons.extension_rounded, color: PassyTheme.lightContentColor), diff --git a/lib/screens/unlock_screen.dart b/lib/screens/unlock_screen.dart index d4039006..d320ddf2 100644 --- a/lib/screens/unlock_screen.dart +++ b/lib/screens/unlock_screen.dart @@ -78,7 +78,7 @@ class _UnlockScreen extends State with WidgetsBindingObserver { Navigator.pop(context); return; } - showSnackBar(context, + showSnackBar( message: localizations.incorrectPassword, icon: const Icon( Icons.lock_rounded, From 85711434b2be216e72e096bc5c55a222166d6c7d Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Wed, 13 Mar 2024 12:41:51 +0000 Subject: [PATCH 060/131] Aegis import UI implemented --- lib/l10n/app_en.arb | 3 +- lib/screens/confirm_import_screen.dart | 41 ++++++++++++++++++++++++-- lib/screens/import_screen.dart | 35 ++++++++++++++++++++++ 3 files changed, 75 insertions(+), 4 deletions(-) diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 8bab339c..009af58c 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -344,5 +344,6 @@ "createTag": "Create tag", "create": "Create", "tag": "Tag", - "noTags": "No tags" + "noTags": "No tags", + "aegisImport": "Aegis import" } diff --git a/lib/screens/confirm_import_screen.dart b/lib/screens/confirm_import_screen.dart index 8c04e18f..020cba47 100644 --- a/lib/screens/confirm_import_screen.dart +++ b/lib/screens/confirm_import_screen.dart @@ -18,6 +18,7 @@ import 'log_screen.dart'; enum ImportType { passy, kdbx, + aegis, } class ConfirmImportScreenArgs { @@ -77,11 +78,35 @@ class _ConfirmImportScreen extends State { icon: const Icon(Icons.check, color: PassyTheme.darkContentColor), ); break; + case ImportType.aegis: + File file = File(args.path); + await _account.importAegis( + aegisFile: file, password: value.isEmpty ? null : value); + Navigator.popUntil(context, + (route) => route.settings.name == ImportScreen.routeName); + showSnackBar( + message: localizations.imported, + icon: const Icon(Icons.check, color: PassyTheme.darkContentColor), + ); + break; } } catch (e, s) { await Future.delayed(const Duration(milliseconds: 200)); if (!mounted) return; Navigator.pop(context); + if (e.runtimeType.toString() == 'InvalidCipherTextException') { + showSnackBar( + message: localizations.incorrectPassword, + icon: const Icon(Icons.download_for_offline_outlined, + color: PassyTheme.darkContentColor), + action: SnackBarAction( + label: localizations.details, + onPressed: () => Navigator.pushNamed(context, LogScreen.routeName, + arguments: e.toString() + '\n' + s.toString()), + ), + ); + return; + } showSnackBar( message: localizations.couldNotImportAccount, icon: const Icon(Icons.download_for_offline_outlined, @@ -99,10 +124,20 @@ class _ConfirmImportScreen extends State { Widget build(BuildContext context) { ConfirmImportScreenArgs args = ModalRoute.of(context)!.settings.arguments as ConfirmImportScreenArgs; + String title; + switch (args.importType) { + case ImportType.passy: + title = localizations.passyImport; + break; + case ImportType.kdbx: + title = localizations.kdbxImport; + break; + case ImportType.aegis: + title = localizations.aegisImport; + break; + } return ConfirmStringScaffold( - title: Text(args.importType == ImportType.passy - ? localizations.passyImport - : localizations.kdbxImport), + title: Text(title), message: PassyPadding(Text.rich( TextSpan( text: localizations.confirmImport1, diff --git a/lib/screens/import_screen.dart b/lib/screens/import_screen.dart index c7d680bb..aab4fe7a 100644 --- a/lib/screens/import_screen.dart +++ b/lib/screens/import_screen.dart @@ -74,6 +74,32 @@ class _ImportScreen extends State { ); } + void _onAegisImportPressed() { + MainScreen.shouldLockScreen = false; + FilePicker.platform + .pickFiles( + dialogTitle: localizations.aegisImport, + type: Platform.isAndroid ? FileType.any : FileType.custom, + allowedExtensions: Platform.isAndroid ? null : ['json'], + lockParentWindow: true, + ) + .then( + (_pick) { + Future.delayed(const Duration(seconds: 2)) + .then((value) => MainScreen.shouldLockScreen = true); + if (_pick == null) return; + Navigator.pushNamed( + context, + ConfirmImportScreen.routeName, + arguments: ConfirmImportScreenArgs( + path: _pick.files[0].path!, + importType: ImportType.aegis, + ), + ); + }, + ); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -108,6 +134,15 @@ class _ImportScreen extends State { right: const Icon(Icons.arrow_forward_ios_rounded), onPressed: _onKdbxImportPressed, )), + PassyPadding(ThreeWidgetButton( + center: Text(localizations.aegisImport), + left: const Padding( + padding: EdgeInsets.only(right: 30), + child: Icon(Icons.security), + ), + right: const Icon(Icons.arrow_forward_ios_rounded), + onPressed: _onAegisImportPressed, + )), PassyPadding(ThreeWidgetButton( center: Text(localizations.passyImport), left: Padding( From 03fd87688a6b16e7f79f8cb32c6789574a9a9069 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Wed, 13 Mar 2024 13:05:22 +0000 Subject: [PATCH 061/131] SearchEntryData.tags implemented --- lib/passy_flutter/search_entry_data.dart | 40 ++++++++++++++++++++---- lib/screens/main_screen.dart | 28 +++++++++++------ 2 files changed, 52 insertions(+), 16 deletions(-) diff --git a/lib/passy_flutter/search_entry_data.dart b/lib/passy_flutter/search_entry_data.dart index 8b3a943a..cb27bd58 100644 --- a/lib/passy_flutter/search_entry_data.dart +++ b/lib/passy_flutter/search_entry_data.dart @@ -42,24 +42,44 @@ String _descriptionFromEntry(EntryType type, EntryMeta entry) { } } +List _tagsFromEntry(EntryType type, EntryMeta entry) { + switch (type) { + case EntryType.idCard: + return (entry as IDCardMeta).tags; + case EntryType.identity: + return (entry as IdentityMeta).tags; + case EntryType.note: + return (entry as NoteMeta).tags; + case EntryType.password: + return (entry as PasswordMeta).tags; + case EntryType.paymentCard: + return (entry as PaymentCardMeta).tags; + default: + return []; + } +} + class SearchEntryData { final String name; final String description; final EntryType type; final EntryMeta meta; + final List tags; SearchEntryData({ required this.name, required this.description, required this.type, required this.meta, + required this.tags, }); SearchEntryData.fromEntry({ required this.type, required this.meta, }) : name = _nameFromEntry(type, meta), - description = _descriptionFromEntry(type, meta); + description = _descriptionFromEntry(type, meta), + tags = _tagsFromEntry(type, meta); static List fromEntries({ List? idCards, @@ -73,24 +93,32 @@ class SearchEntryData { name: idCard.nickname, description: idCard.name, type: EntryType.idCard, - meta: idCard))); + meta: idCard, + tags: idCard.tags))); identities?.forEach((identity) => _result.add(SearchEntryData( name: identity.nickname, description: identity.firstAddressLine, type: EntryType.identity, - meta: identity))); + meta: identity, + tags: identity.tags))); notes?.forEach((note) => _result.add(SearchEntryData( - name: note.title, description: '', type: EntryType.note, meta: note))); + name: note.title, + description: '', + type: EntryType.note, + meta: note, + tags: note.tags))); passwords?.forEach((password) => _result.add(SearchEntryData( name: password.nickname, description: password.username, type: EntryType.password, - meta: password))); + meta: password, + tags: password.tags))); paymentCards?.forEach((paymentCard) => _result.add(SearchEntryData( name: paymentCard.nickname, description: paymentCard.cardholderName, type: EntryType.paymentCard, - meta: paymentCard))); + meta: paymentCard, + tags: paymentCard.tags))); return _result; } diff --git a/lib/screens/main_screen.dart b/lib/screens/main_screen.dart index 742000cb..dba18b7f 100644 --- a/lib/screens/main_screen.dart +++ b/lib/screens/main_screen.dart @@ -92,35 +92,40 @@ class _MainScreen extends State name: idCard.nickname, description: idCard.name, type: EntryType.idCard, - meta: idCard)); + meta: idCard, + tags: idCard.tags)); } for (IdentityMeta _identity in identitiesMetadata.values) { _searchEntries.add(SearchEntryData( name: _identity.nickname, description: _identity.firstAddressLine, type: EntryType.identity, - meta: _identity)); + meta: _identity, + tags: _identity.tags)); } for (NoteMeta _note in notesMetadata.values) { _searchEntries.add(SearchEntryData( name: _note.title, description: '', type: EntryType.note, - meta: _note)); + meta: _note, + tags: _note.tags)); } for (PasswordMeta _password in passwordsMetadata.values) { _searchEntries.add(SearchEntryData( name: _password.nickname, description: _password.username, type: EntryType.password, - meta: _password)); + meta: _password, + tags: _password.tags)); } for (PaymentCardMeta _paymentCard in paymentCardsMetadata.values) { _searchEntries.add(SearchEntryData( name: _paymentCard.nickname, description: _paymentCard.cardholderName, type: EntryType.paymentCard, - meta: _paymentCard)); + meta: _paymentCard, + tags: _paymentCard.tags)); } for (SearchEntryData _searchEntry in _searchEntries) { { @@ -239,7 +244,8 @@ class _MainScreen extends State name: idCard.nickname, description: idCard.name, type: EntryType.idCard, - meta: idCard)); + meta: idCard, + tags: idCard.tags)); } for (EntryEvent event in _account.favoriteIdentities.values) { if (event.status == EntryStatus.removed) continue; @@ -249,7 +255,8 @@ class _MainScreen extends State name: _identity.nickname, description: _identity.firstAddressLine, type: EntryType.identity, - meta: _identity)); + meta: _identity, + tags: _identity.tags)); } for (EntryEvent event in _account.favoriteNotes.values) { if (event.status == EntryStatus.removed) continue; @@ -259,7 +266,8 @@ class _MainScreen extends State name: _note.title, description: '', type: EntryType.note, - meta: _note)); + meta: _note, + tags: _note.tags)); } for (EntryEvent event in _account.favoritePasswords.values) { if (event.status == EntryStatus.removed) continue; @@ -269,7 +277,7 @@ class _MainScreen extends State name: _password.nickname, description: _password.username, type: EntryType.password, - meta: _password)); + meta: _password, tags: _password.tags)); } for (EntryEvent event in _account.favoritePaymentCards.values) { if (event.status == EntryStatus.removed) continue; @@ -279,7 +287,7 @@ class _MainScreen extends State name: _paymentCard.nickname, description: _paymentCard.cardholderName, type: EntryType.paymentCard, - meta: _paymentCard)); + meta: _paymentCard, tags: _paymentCard.tags)); } for (SearchEntryData _searchEntry in _searchEntries) { { From 60dfb69d258316c57109a11d6ea539cf2e9209db Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Wed, 13 Mar 2024 13:20:40 +0000 Subject: [PATCH 062/131] LoadedAccount.tags futurized --- lib/passy_data/loaded_account.dart | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/lib/passy_data/loaded_account.dart b/lib/passy_data/loaded_account.dart index c0672c42..69f2a973 100644 --- a/lib/passy_data/loaded_account.dart +++ b/lib/passy_data/loaded_account.dart @@ -1087,13 +1087,24 @@ class LoadedAccount { } } - List get tags => [ - ..._passwords.tags, - ..._notes.tags, - ..._paymentCards.tags, - ..._idCards.tags, - ..._identities.tags, - ]; + Future> get tags async { + List> tags = await Future.wait([ + passwordTags, + notesTags, + paymentCardTags, + idCardsTags, + identitiesTags, + ]); + List result = [...tags.removeLast()] + ; + for (List list in tags) { + for (String tag in list) { + if (result.contains(tag)) continue; + result.add(tag); + } + } + return result; + } // Passwords wrappers List get passwordKeys => _passwords.keys; From b8b4e883206f3355d67ca79ffd816e6411b8eda6 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Wed, 13 Mar 2024 13:20:56 +0000 Subject: [PATCH 063/131] Update loaded_account.dart --- lib/passy_data/loaded_account.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/passy_data/loaded_account.dart b/lib/passy_data/loaded_account.dart index 69f2a973..18579db6 100644 --- a/lib/passy_data/loaded_account.dart +++ b/lib/passy_data/loaded_account.dart @@ -1095,8 +1095,7 @@ class LoadedAccount { idCardsTags, identitiesTags, ]); - List result = [...tags.removeLast()] - ; + List result = [...tags.removeLast()]; for (List list in tags) { for (String tag in list) { if (result.contains(tag)) continue; From d85b5030401546b0e3e05980674b3643b8ca4692 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Wed, 13 Mar 2024 13:35:14 +0000 Subject: [PATCH 064/131] Update entry_tag_list.dart --- lib/passy_flutter/widgets/entry_tag_list.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/passy_flutter/widgets/entry_tag_list.dart b/lib/passy_flutter/widgets/entry_tag_list.dart index fe15dddb..400e8ae7 100644 --- a/lib/passy_flutter/widgets/entry_tag_list.dart +++ b/lib/passy_flutter/widgets/entry_tag_list.dart @@ -105,6 +105,10 @@ class _EntryTagList extends State { bottom: showScrollbar ? 14 : 0), child: Text(localizations.noTags), ), + if (selectedButtons.isEmpty && notSelectedButtons.isEmpty) + const SizedBox( + width: 10 + ), if (widget.showAddButton) Padding( padding: EdgeInsets.only(bottom: showScrollbar ? 14 : 0), From 73bccc7c1e959a0ca404c7087192a5c2cf7a014d Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Wed, 13 Mar 2024 13:38:52 +0000 Subject: [PATCH 065/131] SearchScreenArgs.entryType implemented + SearchScreenArgs.notSelectedTags unimplemented --- lib/screens/id_cards_screen.dart | 2 +- lib/screens/identities_screen.dart | 2 +- lib/screens/login_screen.dart | 2 + lib/screens/main_screen.dart | 9 ++++- lib/screens/notes_screen.dart | 2 +- lib/screens/passwords_screen.dart | 2 +- lib/screens/payment_cards_screen.dart | 2 +- lib/screens/search_screen.dart | 55 ++++++++++++++++++++++++--- 8 files changed, 64 insertions(+), 12 deletions(-) diff --git a/lib/screens/id_cards_screen.dart b/lib/screens/id_cards_screen.dart index 7feb81bd..89b2a53d 100644 --- a/lib/screens/id_cards_screen.dart +++ b/lib/screens/id_cards_screen.dart @@ -33,7 +33,7 @@ class _IDCardsScreen extends State { void _onSearchPressed({String? tag}) { Navigator.pushNamed(context, SearchScreen.routeName, arguments: SearchScreenArgs( - notSelectedTags: _tags.toList()..remove(tag), + entryType: EntryType.idCard, selectedTags: tag == null ? [] : [tag], builder: (String terms, List tags, void Function() rebuild) { diff --git a/lib/screens/identities_screen.dart b/lib/screens/identities_screen.dart index 30a5a91c..70b73ef4 100644 --- a/lib/screens/identities_screen.dart +++ b/lib/screens/identities_screen.dart @@ -32,7 +32,7 @@ class _IdentitiesScreen extends State { void _onSearchPressed({String? tag}) { Navigator.pushNamed(context, SearchScreen.routeName, arguments: SearchScreenArgs( - notSelectedTags: _tags.toList()..remove(tag), + entryType: EntryType.identity, selectedTags: tag == null ? [] : [tag], builder: (String terms, List tags, void Function() rebuild) { diff --git a/lib/screens/login_screen.dart b/lib/screens/login_screen.dart index 8ddfd536..02634ca7 100644 --- a/lib/screens/login_screen.dart +++ b/lib/screens/login_screen.dart @@ -77,6 +77,7 @@ class _LoginScreen extends State { context, SearchScreen.routeName, arguments: SearchScreenArgs( + entryType: null, builder: _buildPasswords, isAutofill: true, ), @@ -202,6 +203,7 @@ class _LoginScreen extends State { context, SearchScreen.routeName, arguments: SearchScreenArgs( + entryType: null, builder: _buildPasswords, isAutofill: true, ), diff --git a/lib/screens/main_screen.dart b/lib/screens/main_screen.dart index dba18b7f..f3261baa 100644 --- a/lib/screens/main_screen.dart +++ b/lib/screens/main_screen.dart @@ -277,7 +277,8 @@ class _MainScreen extends State name: _password.nickname, description: _password.username, type: EntryType.password, - meta: _password, tags: _password.tags)); + meta: _password, + tags: _password.tags)); } for (EntryEvent event in _account.favoritePaymentCards.values) { if (event.status == EntryStatus.removed) continue; @@ -287,7 +288,8 @@ class _MainScreen extends State name: _paymentCard.nickname, description: _paymentCard.cardholderName, type: EntryType.paymentCard, - meta: _paymentCard, tags: _paymentCard.tags)); + meta: _paymentCard, + tags: _paymentCard.tags)); } for (SearchEntryData _searchEntry in _searchEntries) { { @@ -484,6 +486,7 @@ class _MainScreen extends State if (mounted) { Navigator.pushNamed(context, SearchScreen.routeName, arguments: SearchScreenArgs( + entryType: null, title: localizations.favorites, builder: _favoritesSearchBuilder, )); @@ -499,6 +502,7 @@ class _MainScreen extends State center: Text(localizations.searchAllEntries), onPressed: () => Navigator.pushNamed(context, SearchScreen.routeName, arguments: SearchScreenArgs( + entryType: null, title: localizations.allEntries, builder: _searchBuilder, )), @@ -622,6 +626,7 @@ class _MainScreen extends State onPressed: () => Navigator.pushNamed(context, SearchScreen.routeName, arguments: SearchScreenArgs( + entryType: null, title: localizations.allEntries, builder: _searchBuilder, )), diff --git a/lib/screens/notes_screen.dart b/lib/screens/notes_screen.dart index d1669ea4..616a2014 100644 --- a/lib/screens/notes_screen.dart +++ b/lib/screens/notes_screen.dart @@ -35,7 +35,7 @@ class _NotesScreen extends State { context, SearchScreen.routeName, arguments: SearchScreenArgs( - notSelectedTags: _tags.toList()..remove(tag), + entryType: EntryType.note, selectedTags: tag == null ? [] : [tag], builder: (String terms, List tags, void Function() rebuild) { final List _found = []; diff --git a/lib/screens/passwords_screen.dart b/lib/screens/passwords_screen.dart index 99bfcac4..d2c630f9 100644 --- a/lib/screens/passwords_screen.dart +++ b/lib/screens/passwords_screen.dart @@ -71,7 +71,7 @@ class _PasswordsScreen extends State { Navigator.pushNamed(context, SearchScreen.routeName, arguments: SearchScreenArgs( builder: _buildPasswords, - notSelectedTags: _tags.toList()..remove(tag), + entryType: EntryType.password, selectedTags: tag == null ? [] : [tag])); } diff --git a/lib/screens/payment_cards_screen.dart b/lib/screens/payment_cards_screen.dart index 58e5b272..7f7d4977 100644 --- a/lib/screens/payment_cards_screen.dart +++ b/lib/screens/payment_cards_screen.dart @@ -28,7 +28,7 @@ class _PaymentCardsScreen extends State { void _onSearchPressed({String? tag}) { Navigator.pushNamed(context, SearchScreen.routeName, arguments: SearchScreenArgs( - notSelectedTags: _tags.toList()..remove(tag), + entryType: EntryType.paymentCard, selectedTags: tag == null ? [] : [tag], builder: (String terms, List tags, void Function() rebuild) { final List _found = []; diff --git a/lib/screens/search_screen.dart b/lib/screens/search_screen.dart index 0d35c9f5..dc79114e 100644 --- a/lib/screens/search_screen.dart +++ b/lib/screens/search_screen.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:passy/common/common.dart'; +import 'package:passy/passy_data/entry_type.dart'; +import 'package:passy/passy_data/loaded_account.dart'; import 'package:passy/passy_flutter/passy_theme.dart'; import 'package:passy/passy_flutter/widgets/widgets.dart'; @@ -12,14 +14,14 @@ class SearchScreenArgs { void Function() rebuild, ) builder; bool isAutofill; - List notSelectedTags; + EntryType? entryType; List selectedTags; SearchScreenArgs({ this.title, required this.builder, this.isAutofill = false, - this.notSelectedTags = const [], + required this.entryType, this.selectedTags = const [], }); } @@ -34,7 +36,9 @@ class SearchScreen extends StatefulWidget { } class _SearchScreen extends State { + final LoadedAccount _account = data.loadedAccount!; bool _initialized = false; + bool _loaded = false; Widget _widget = const Text(''); TextEditingController queryController = TextEditingController(); FocusNode queryFocus = FocusNode()..requestFocus(); @@ -64,15 +68,56 @@ class _SearchScreen extends State { }); } + Future _load(SearchScreenArgs args) async { + List newTags; + try { + switch (args.entryType) { + case EntryType.password: + newTags = await _account.passwordTags; + break; + case EntryType.paymentCard: + newTags = await _account.paymentCardTags; + break; + case EntryType.note: + newTags = await _account.notesTags; + break; + case EntryType.idCard: + newTags = await _account.idCardsTags; + break; + case EntryType.identity: + newTags = await _account.identitiesTags; + break; + case null: + newTags = await _account.tags; + break; + } + } catch (_) { + return; + } + if (mounted) { + setState(() { + _loaded = true; + if (newTags.isEmpty) { + selected = []; + return; + } + for (String tag in newTags) { + if (selected.contains(tag)) continue; + notSelected.add(tag); + } + }); + } + } + @override Widget build(BuildContext context) { SearchScreenArgs args = ModalRoute.of(context)!.settings.arguments as SearchScreenArgs; _builder = args.builder; if (!_initialized) { - selected = args.selectedTags; - notSelected = args.notSelectedTags; + selected = args.selectedTags.toList(); _widget = _builder(queryController.text, selected, rebuild); + _load(args); _initialized = true; } return Scaffold( @@ -132,7 +177,7 @@ class _SearchScreen extends State { }); }); })), - if (selected.isNotEmpty || notSelected.isNotEmpty) + if (_loaded && (selected.isNotEmpty || notSelected.isNotEmpty)) Padding( padding: EdgeInsets.only( top: PassyTheme.passyPadding.top / 2, From 2f98b28b8ff937fccab6567e8c1d331e8945df6b Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Wed, 13 Mar 2024 13:39:55 +0000 Subject: [PATCH 066/131] Main Screen tag search implemented --- lib/screens/main_screen.dart | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/screens/main_screen.dart b/lib/screens/main_screen.dart index f3261baa..21ce6e4d 100644 --- a/lib/screens/main_screen.dart +++ b/lib/screens/main_screen.dart @@ -135,6 +135,12 @@ class _MainScreen extends State if (_found.any(testSearchEntry)) continue; } { + bool _tagMismatch = false; + for (String tag in tags) { + if (_searchEntry.tags.contains(tag)) continue; + _tagMismatch = true; + } + if (_tagMismatch) continue; int _positiveCount = 0; for (String _term in _terms) { if (_searchEntry.name.toLowerCase().contains(_term)) { @@ -299,6 +305,12 @@ class _MainScreen extends State if (_found.any(testSearchEntry)) continue; } { + bool _tagMismatch = false; + for (String tag in tags) { + if (_searchEntry.tags.contains(tag)) continue; + _tagMismatch = true; + } + if (_tagMismatch) continue; int _positiveCount = 0; for (String _term in _terms) { if (_searchEntry.name.toLowerCase().contains(_term)) { From 452c36df0b43b210fba93bd0a98f17341bf33252 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Wed, 13 Mar 2024 14:00:49 +0000 Subject: [PATCH 067/131] Cleanup --- lib/passy_flutter/widgets/entry_tag_list.dart | 4 +--- lib/passy_flutter/widgets/payment_card_button.dart | 2 +- lib/passy_flutter/widgets/record_button.dart | 4 ++-- lib/screens/add_account_screen.dart | 13 ++++++++----- lib/screens/id_cards_screen.dart | 2 +- lib/screens/identities_screen.dart | 2 +- lib/screens/log_screen.dart | 2 +- lib/screens/main_screen.dart | 8 ++++---- lib/screens/password_screen.dart | 2 +- lib/screens/unlock_screen.dart | 13 ++++++++----- 10 files changed, 28 insertions(+), 24 deletions(-) diff --git a/lib/passy_flutter/widgets/entry_tag_list.dart b/lib/passy_flutter/widgets/entry_tag_list.dart index 400e8ae7..afec7808 100644 --- a/lib/passy_flutter/widgets/entry_tag_list.dart +++ b/lib/passy_flutter/widgets/entry_tag_list.dart @@ -106,9 +106,7 @@ class _EntryTagList extends State { child: Text(localizations.noTags), ), if (selectedButtons.isEmpty && notSelectedButtons.isEmpty) - const SizedBox( - width: 10 - ), + const SizedBox(width: 10), if (widget.showAddButton) Padding( padding: EdgeInsets.only(bottom: showScrollbar ? 14 : 0), diff --git a/lib/passy_flutter/widgets/payment_card_button.dart b/lib/passy_flutter/widgets/payment_card_button.dart index 8adabe06..70b2c4f7 100644 --- a/lib/passy_flutter/widgets/payment_card_button.dart +++ b/lib/passy_flutter/widgets/payment_card_button.dart @@ -28,7 +28,7 @@ class PaymentCardButton extends StatelessWidget { child: Stack( children: [ InkWell( - borderRadius: BorderRadius.all(Radius.circular(12)), + borderRadius: const BorderRadius.all(Radius.circular(12)), onTap: onPressed, child: ClipRect( child: Align( diff --git a/lib/passy_flutter/widgets/record_button.dart b/lib/passy_flutter/widgets/record_button.dart index a512bda3..a31d7f88 100644 --- a/lib/passy_flutter/widgets/record_button.dart +++ b/lib/passy_flutter/widgets/record_button.dart @@ -50,8 +50,8 @@ class RecordButton extends StatelessWidget { builder: (_) => RecordDialog( value: value, highlightSpecial: isPassword, textAlign: valueAlign), ), - right: CircleAvatar(child: - IconButton( + right: CircleAvatar( + child: IconButton( padding: EdgeInsets.zero, icon: const Icon(Icons.copy_rounded), tooltip: localizations.copy, diff --git a/lib/screens/add_account_screen.dart b/lib/screens/add_account_screen.dart index 50c34667..0471a1e1 100644 --- a/lib/screens/add_account_screen.dart +++ b/lib/screens/add_account_screen.dart @@ -102,16 +102,19 @@ class _AddAccountScreen extends State { }); } - Future _onWillPop() { - if (data.noAccounts) return Future.value(true); + void _onWillPop(bool isPopped) { + if (data.noAccounts) { + Navigator.pop(context); + return; + } Navigator.pushReplacementNamed(context, LoginScreen.routeName); - return Future.value(false); } @override Widget build(BuildContext context) { - return WillPopScope( - onWillPop: _onWillPop, + return PopScope( + canPop: false, + onPopInvoked: _onWillPop, child: Scaffold( appBar: AppBar( automaticallyImplyLeading: false, diff --git a/lib/screens/id_cards_screen.dart b/lib/screens/id_cards_screen.dart index 89b2a53d..5efe70a6 100644 --- a/lib/screens/id_cards_screen.dart +++ b/lib/screens/id_cards_screen.dart @@ -33,7 +33,7 @@ class _IDCardsScreen extends State { void _onSearchPressed({String? tag}) { Navigator.pushNamed(context, SearchScreen.routeName, arguments: SearchScreenArgs( - entryType: EntryType.idCard, + entryType: EntryType.idCard, selectedTags: tag == null ? [] : [tag], builder: (String terms, List tags, void Function() rebuild) { diff --git a/lib/screens/identities_screen.dart b/lib/screens/identities_screen.dart index 70b73ef4..14c9a4de 100644 --- a/lib/screens/identities_screen.dart +++ b/lib/screens/identities_screen.dart @@ -32,7 +32,7 @@ class _IdentitiesScreen extends State { void _onSearchPressed({String? tag}) { Navigator.pushNamed(context, SearchScreen.routeName, arguments: SearchScreenArgs( - entryType: EntryType.identity, + entryType: EntryType.identity, selectedTags: tag == null ? [] : [tag], builder: (String terms, List tags, void Function() rebuild) { diff --git a/lib/screens/log_screen.dart b/lib/screens/log_screen.dart index 6307fc5b..ee438672 100644 --- a/lib/screens/log_screen.dart +++ b/lib/screens/log_screen.dart @@ -44,7 +44,7 @@ class LogScreen extends StatelessWidget { padding: PassyTheme.appBarButtonPadding, splashRadius: PassyTheme.appBarButtonSplashRadius, icon: SvgPicture.asset( - width: PassyTheme.appBarButtonSplashRadius , + width: PassyTheme.appBarButtonSplashRadius, 'assets/images/github_icon.svg', colorFilter: const ColorFilter.mode( PassyTheme.lightContentColor, BlendMode.srcIn), diff --git a/lib/screens/main_screen.dart b/lib/screens/main_screen.dart index 21ce6e4d..59371806 100644 --- a/lib/screens/main_screen.dart +++ b/lib/screens/main_screen.dart @@ -416,9 +416,8 @@ class _MainScreen extends State ); } - Future _onWillPop() { + void _onWillPop(bool isPopped) { _logOut(); - return Future.value(false); } @override @@ -614,8 +613,9 @@ class _MainScreen extends State )), ]; - return WillPopScope( - onWillPop: _onWillPop, + return PopScope( + canPop: false, + onPopInvoked: _onWillPop, child: Scaffold( appBar: AppBar( automaticallyImplyLeading: false, diff --git a/lib/screens/password_screen.dart b/lib/screens/password_screen.dart index 6b224c20..bab01ed7 100644 --- a/lib/screens/password_screen.dart +++ b/lib/screens/password_screen.dart @@ -265,7 +265,7 @@ class _PasswordScreen extends State { selected: _selected, notSelected: _tags, onAdded: (tag) async { - if (password!.tags.contains(tag)) return; + if (password!.tags.contains(tag)) return; Navigator.pushNamed(context, SplashScreen.routeName); password!.tags = _selected.toList(); password!.tags.add(tag); diff --git a/lib/screens/unlock_screen.dart b/lib/screens/unlock_screen.dart index d320ddf2..29d729e0 100644 --- a/lib/screens/unlock_screen.dart +++ b/lib/screens/unlock_screen.dart @@ -37,14 +37,16 @@ class _UnlockScreen extends State with WidgetsBindingObserver { data.unloadAccount(); } - Future _onWillPop() { - if (_shouldPop) return Future.value(true); + void _onWillPop(bool isPopped) { + if (_shouldPop) { + Navigator.pop(context); + return; + } Navigator.popUntil(context, (route) { if (route.settings.name != MainScreen.routeName) return false; _logOut(); return true; }); - return Future.value(false); } Future _bioAuth() async { @@ -127,8 +129,9 @@ class _UnlockScreen extends State with WidgetsBindingObserver { @override Widget build(BuildContext context) { - return WillPopScope( - onWillPop: _onWillPop, + return PopScope( + canPop: false, + onPopInvoked: _onWillPop, child: Scaffold( appBar: AppBar( title: Text(localizations.unlock), From 03e83e6a37caee4617abe2c80ab42e6f4b477e62 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Wed, 13 Mar 2024 14:06:19 +0000 Subject: [PATCH 068/131] Backup localized --- lib/l10n/app_en.arb | 5 ++++- lib/screens/common.dart | 11 +++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 009af58c..8f7e436a 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -345,5 +345,8 @@ "create": "Create", "tag": "Tag", "noTags": "No tags", - "aegisImport": "Aegis import" + "aegisImport": "Aegis import", + "backupPassy": "Backup Passy", + "backupSaved": "Backup saved", + "couldNotBackup": "Could not backup" } diff --git a/lib/screens/common.dart b/lib/screens/common.dart index db0415b8..88d47967 100644 --- a/lib/screens/common.dart +++ b/lib/screens/common.dart @@ -92,7 +92,6 @@ void openUrl(String url) { launchUrlString(url); } -//TODO: localize backup Future backupAccount( BuildContext context, { required String username, @@ -105,14 +104,14 @@ Future backupAccount( String? _buDir; if (autoFilename) { _buDir = await FilePicker.platform.getDirectoryPath( - dialogTitle: 'Backup Passy', + dialogTitle: localizations.backupPassy, lockParentWindow: true, ); } else { _fileName = 'passy-backup-$username-${DateTime.now().toUtc().toIso8601String().replaceAll(':', ';')}.zip'; _buDir = await FilePicker.platform.saveFile( - dialogTitle: 'Backup Passy', + dialogTitle: localizations.backupPassy, lockParentWindow: true, fileName: _fileName, ); @@ -127,19 +126,19 @@ Future backupAccount( fileName: _fileName, ); showSnackBar( - message: 'Backup saved', + message: localizations.backupSaved, icon: const Icon(Icons.save_rounded, color: PassyTheme.darkContentColor)); return _buDir; } catch (e, s) { if (e is FileSystemException) { showSnackBar( - message: 'Access denied, try another folder', + message: localizations.accessDeniedTryAnotherFolder, icon: const Icon(Icons.save_rounded, color: PassyTheme.darkContentColor)); } else { showSnackBar( - message: 'Could not backup', + message: localizations.couldNotBackup, icon: const Icon(Icons.save_rounded, color: PassyTheme.darkContentColor), action: SnackBarAction( From acee98098f7adba260c584f4d58d03ce83bdc256 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Wed, 13 Mar 2024 14:15:29 +0000 Subject: [PATCH 069/131] Menu builders localized --- lib/screens/common.dart | 53 ++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/lib/screens/common.dart b/lib/screens/common.dart index 88d47967..e899bb78 100644 --- a/lib/screens/common.dart +++ b/lib/screens/common.dart @@ -262,30 +262,29 @@ PopupMenuItem getIconedPopupMenuItem({ ); } -//TODO: localize menu builders List idCardPopupMenuBuilder( BuildContext context, IDCardMeta idCardMeta) { return [ getIconedPopupMenuItem( - content: const Text('ID number'), + content: Text(localizations.idNumber), icon: const Icon(Icons.numbers_outlined), onTap: () { Clipboard.setData(ClipboardData( text: data.loadedAccount!.getIDCard(idCardMeta.key)!.idNumber)); showSnackBar( - message: 'ID number copied', + message: localizations.idNumber, icon: const Icon(Icons.copy_rounded, color: PassyTheme.darkContentColor)); }, ), if (idCardMeta.name != '') getIconedPopupMenuItem( - content: const Text('Name'), + content: Text(localizations.name), icon: const Icon(Icons.person_outline_rounded), onTap: () { Clipboard.setData(ClipboardData(text: idCardMeta.name)); showSnackBar( - message: 'Name copied', + message: localizations.name, icon: const Icon(Icons.copy_rounded, color: PassyTheme.darkContentColor)); }, @@ -297,7 +296,7 @@ List identityPopupMenuBuilder( BuildContext context, IdentityMeta identityMeta) { return [ getIconedPopupMenuItem( - content: const Text('Name'), + content: Text(localizations.name), icon: const Icon(Icons.person_outline_rounded), onTap: () { Identity? _identity = data.loadedAccount!.getIdentity(identityMeta.key); @@ -315,31 +314,31 @@ List identityPopupMenuBuilder( } Clipboard.setData(ClipboardData(text: _name)); showSnackBar( - message: 'Name copied', + message: localizations.name, icon: const Icon(Icons.copy_rounded, color: PassyTheme.darkContentColor)); }, ), getIconedPopupMenuItem( - content: const Text('Email'), + content: Text(localizations.email), icon: const Icon(Icons.mail_outline_rounded), onTap: () { Clipboard.setData(ClipboardData( text: data.loadedAccount!.getIdentity(identityMeta.key)!.email)); showSnackBar( - message: 'Email copied', + message: localizations.email, icon: const Icon(Icons.copy_rounded, color: PassyTheme.darkContentColor)); }, ), if (identityMeta.firstAddressLine != '') getIconedPopupMenuItem( - content: const Text('Address line'), + content: Text(localizations.firstAddresssLine), icon: const Icon(Icons.house_outlined), onTap: () { Clipboard.setData(ClipboardData(text: identityMeta.firstAddressLine)); showSnackBar( - message: 'Address line copied', + message: localizations.firstAddresssLine, icon: const Icon(Icons.copy_rounded, color: PassyTheme.darkContentColor)); }, @@ -351,13 +350,13 @@ List notePopupMenuBuilder( BuildContext context, NoteMeta identityMeta) { return [ getIconedPopupMenuItem( - content: const Text('Copy'), + content: Text(localizations.copy), icon: const Icon(Icons.copy_rounded), onTap: () { Clipboard.setData(ClipboardData( text: data.loadedAccount!.getNote(identityMeta.key)!.note)); showSnackBar( - message: 'Note copied', + message: localizations.copied, icon: const Icon(Icons.copy_rounded, color: PassyTheme.darkContentColor)); }, @@ -370,45 +369,45 @@ List passwordPopupMenuBuilder( return [ if (passwordMeta.username != '') getIconedPopupMenuItem( - content: const Text('Username'), + content: Text(localizations.username), icon: const Icon(Icons.person_outline_rounded), onTap: () { Clipboard.setData(ClipboardData( text: data.loadedAccount!.getPassword(passwordMeta.key)!.username)); showSnackBar( - message: 'Username copied', + message: localizations.username, icon: const Icon(Icons.copy_rounded, color: PassyTheme.darkContentColor)); }, ), getIconedPopupMenuItem( - content: const Text('Email'), + content: Text(localizations.email), icon: const Icon(Icons.mail_outline_rounded), onTap: () { Clipboard.setData(ClipboardData( text: data.loadedAccount!.getPassword(passwordMeta.key)!.email)); showSnackBar( - message: 'Email copied', + message: localizations.email, icon: const Icon(Icons.copy_rounded, color: PassyTheme.darkContentColor)); }, ), getIconedPopupMenuItem( - content: const Text('Password'), + content: Text(localizations.password), icon: const Icon(Icons.lock_outline_rounded), onTap: () { Clipboard.setData(ClipboardData( text: data.loadedAccount!.getPassword(passwordMeta.key)!.password)); showSnackBar( - message: 'Password copied', + message: localizations.password, icon: const Icon(Icons.copy_rounded, color: PassyTheme.darkContentColor)); }, ), if (passwordMeta.website != '') getIconedPopupMenuItem( - content: const Text('Visit'), + content: Text(localizations.visit), icon: const Icon(Icons.open_in_browser_outlined), onTap: () { String _url = @@ -429,7 +428,7 @@ List paymentCardPopupMenuBuilder( return [ if (paymentCardMeta.cardNumber != '') getIconedPopupMenuItem( - content: const Text('Card number'), + content: Text(localizations.cardNumber), icon: const Icon(Icons.numbers_outlined), onTap: () { Clipboard.setData(ClipboardData( @@ -437,32 +436,32 @@ List paymentCardPopupMenuBuilder( .getPaymentCard(paymentCardMeta.key)! .cardNumber)); showSnackBar( - message: 'Card number copied', + message: localizations.cardNumber, icon: const Icon(Icons.copy_rounded, color: PassyTheme.darkContentColor)); }, ), if (paymentCardMeta.cardholderName != '') getIconedPopupMenuItem( - content: const Text('Card holder name'), + content: Text(localizations.cardHolderName), icon: const Icon(Icons.person_outline_rounded), onTap: () { Clipboard.setData( ClipboardData(text: paymentCardMeta.cardholderName)); showSnackBar( - message: 'Card holder name copied', + message: localizations.cardHolderName, icon: const Icon(Icons.copy_rounded, color: PassyTheme.darkContentColor)); }, ), if (paymentCardMeta.exp != '') getIconedPopupMenuItem( - content: const Text('Expiration date'), + content: Text(localizations.expirationDate), icon: const Icon(Icons.date_range_outlined), onTap: () { Clipboard.setData(ClipboardData(text: paymentCardMeta.exp)); showSnackBar( - message: 'Expiration date copied', + message: localizations.expirationDate, icon: const Icon(Icons.copy_rounded, color: PassyTheme.darkContentColor)); }, @@ -475,7 +474,7 @@ List paymentCardPopupMenuBuilder( text: data.loadedAccount!.getPaymentCard(paymentCardMeta.key)!.cvv)); showSnackBar( - message: 'CVV copied', + message: 'CVV', icon: const Icon(Icons.copy_rounded, color: PassyTheme.darkContentColor)); }, From 0a04be76703298eb7d79e0820394ad6f5cf676bc Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Wed, 13 Mar 2024 14:25:58 +0000 Subject: [PATCH 070/131] Tags loading indicators implemented --- lib/screens/id_card_screen.dart | 71 ++++++++++++++------------- lib/screens/identity_screen.dart | 73 +++++++++++++++------------- lib/screens/note_screen.dart | 71 ++++++++++++++------------- lib/screens/password_screen.dart | 58 +++++++++++----------- lib/screens/payment_card_screen.dart | 60 ++++++++++++----------- 5 files changed, 172 insertions(+), 161 deletions(-) diff --git a/lib/screens/id_card_screen.dart b/lib/screens/id_card_screen.dart index ac60d661..1eea5bb1 100644 --- a/lib/screens/id_card_screen.dart +++ b/lib/screens/id_card_screen.dart @@ -134,42 +134,43 @@ class _IDCardScreen extends State { ), body: ListView( children: [ - if (_tagsLoaded) - Center( - child: Padding( - padding: EdgeInsets.only( - top: PassyTheme.passyPadding.top / 2, - bottom: PassyTheme.passyPadding.bottom / 2), - child: EntryTagList( - showAddButton: true, - selected: _selected, - notSelected: _tags, - onAdded: (tag) async { - if (_idCard!.tags.contains(tag)) return; - Navigator.pushNamed(context, SplashScreen.routeName); - _idCard!.tags = _selected.toList(); - _idCard!.tags.add(tag); - await _account.setIDCard(_idCard!); - Navigator.popUntil(context, - (r) => r.settings.name == MainScreen.routeName); - Navigator.pushNamed(context, IDCardsScreen.routeName); - Navigator.pushNamed(context, IDCardScreen.routeName, - arguments: _idCard!); - }, - onRemoved: (tag) async { - Navigator.pushNamed(context, SplashScreen.routeName); - _idCard!.tags = _selected.toList(); - _idCard!.tags.remove(tag); - await _account.setIDCard(_idCard!); - Navigator.popUntil(context, - (r) => r.settings.name == MainScreen.routeName); - Navigator.pushNamed(context, IDCardsScreen.routeName); - Navigator.pushNamed(context, IDCardScreen.routeName, - arguments: _idCard!); - }, - ), - ), + Center( + child: Padding( + padding: EdgeInsets.only( + top: PassyTheme.passyPadding.top / 2, + bottom: PassyTheme.passyPadding.bottom / 2), + child: !_tagsLoaded + ? const CircularProgressIndicator() + : EntryTagList( + showAddButton: true, + selected: _selected, + notSelected: _tags, + onAdded: (tag) async { + if (_idCard!.tags.contains(tag)) return; + Navigator.pushNamed(context, SplashScreen.routeName); + _idCard!.tags = _selected.toList(); + _idCard!.tags.add(tag); + await _account.setIDCard(_idCard!); + Navigator.popUntil(context, + (r) => r.settings.name == MainScreen.routeName); + Navigator.pushNamed(context, IDCardsScreen.routeName); + Navigator.pushNamed(context, IDCardScreen.routeName, + arguments: _idCard!); + }, + onRemoved: (tag) async { + Navigator.pushNamed(context, SplashScreen.routeName); + _idCard!.tags = _selected.toList(); + _idCard!.tags.remove(tag); + await _account.setIDCard(_idCard!); + Navigator.popUntil(context, + (r) => r.settings.name == MainScreen.routeName); + Navigator.pushNamed(context, IDCardsScreen.routeName); + Navigator.pushNamed(context, IDCardScreen.routeName, + arguments: _idCard!); + }, + ), ), + ), if (_idCard!.attachments.isNotEmpty) AttachmentsListView(files: _idCard!.attachments), if (_idCard!.nickname != '') diff --git a/lib/screens/identity_screen.dart b/lib/screens/identity_screen.dart index cc292e5d..6ed772f5 100644 --- a/lib/screens/identity_screen.dart +++ b/lib/screens/identity_screen.dart @@ -135,42 +135,45 @@ class _IdentityScreen extends State { ), body: ListView( children: [ - if (_tagsLoaded) - Center( - child: Padding( - padding: EdgeInsets.only( - top: PassyTheme.passyPadding.top / 2, - bottom: PassyTheme.passyPadding.bottom / 2), - child: EntryTagList( - showAddButton: true, - selected: _selected, - notSelected: _tags, - onAdded: (tag) async { - if (_identity!.tags.contains(tag)) return; - Navigator.pushNamed(context, SplashScreen.routeName); - _identity!.tags = _selected.toList(); - _identity!.tags.add(tag); - await _account.setIdentity(_identity!); - Navigator.popUntil(context, - (r) => r.settings.name == MainScreen.routeName); - Navigator.pushNamed(context, IdentitiesScreen.routeName); - Navigator.pushNamed(context, IdentityScreen.routeName, - arguments: _identity!); - }, - onRemoved: (tag) async { - Navigator.pushNamed(context, SplashScreen.routeName); - _identity!.tags = _selected.toList(); - _identity!.tags.remove(tag); - await _account.setIdentity(_identity!); - Navigator.popUntil(context, - (r) => r.settings.name == MainScreen.routeName); - Navigator.pushNamed(context, IdentitiesScreen.routeName); - Navigator.pushNamed(context, IdentityScreen.routeName, - arguments: _identity!); - }, - ), - ), + Center( + child: Padding( + padding: EdgeInsets.only( + top: PassyTheme.passyPadding.top / 2, + bottom: PassyTheme.passyPadding.bottom / 2), + child: !_tagsLoaded + ? const CircularProgressIndicator() + : EntryTagList( + showAddButton: true, + selected: _selected, + notSelected: _tags, + onAdded: (tag) async { + if (_identity!.tags.contains(tag)) return; + Navigator.pushNamed(context, SplashScreen.routeName); + _identity!.tags = _selected.toList(); + _identity!.tags.add(tag); + await _account.setIdentity(_identity!); + Navigator.popUntil(context, + (r) => r.settings.name == MainScreen.routeName); + Navigator.pushNamed( + context, IdentitiesScreen.routeName); + Navigator.pushNamed(context, IdentityScreen.routeName, + arguments: _identity!); + }, + onRemoved: (tag) async { + Navigator.pushNamed(context, SplashScreen.routeName); + _identity!.tags = _selected.toList(); + _identity!.tags.remove(tag); + await _account.setIdentity(_identity!); + Navigator.popUntil(context, + (r) => r.settings.name == MainScreen.routeName); + Navigator.pushNamed( + context, IdentitiesScreen.routeName); + Navigator.pushNamed(context, IdentityScreen.routeName, + arguments: _identity!); + }, + ), ), + ), if (_identity!.attachments.isNotEmpty) AttachmentsListView(files: _identity!.attachments), if (_identity!.nickname != '') diff --git a/lib/screens/note_screen.dart b/lib/screens/note_screen.dart index 2e2a2828..e6b941c0 100644 --- a/lib/screens/note_screen.dart +++ b/lib/screens/note_screen.dart @@ -131,42 +131,43 @@ class _NoteScreen extends State { }, ), body: ListView(children: [ - if (_tagsLoaded) - Center( - child: Padding( - padding: EdgeInsets.only( - top: PassyTheme.passyPadding.top / 2, - bottom: PassyTheme.passyPadding.bottom / 2), - child: EntryTagList( - showAddButton: true, - selected: _selected, - notSelected: _tags, - onAdded: (tag) async { - if (_note!.tags.contains(tag)) return; - Navigator.pushNamed(context, SplashScreen.routeName); - _note!.tags = _selected.toList(); - _note!.tags.add(tag); - await _account.setNote(_note!); - Navigator.popUntil( - context, (r) => r.settings.name == MainScreen.routeName); - Navigator.pushNamed(context, NotesScreen.routeName); - Navigator.pushNamed(context, NoteScreen.routeName, - arguments: _note!); - }, - onRemoved: (tag) async { - Navigator.pushNamed(context, SplashScreen.routeName); - _note!.tags = _selected.toList(); - _note!.tags.remove(tag); - await _account.setNote(_note!); - Navigator.popUntil( - context, (r) => r.settings.name == MainScreen.routeName); - Navigator.pushNamed(context, NotesScreen.routeName); - Navigator.pushNamed(context, NoteScreen.routeName, - arguments: _note!); - }, - ), - ), + Center( + child: Padding( + padding: EdgeInsets.only( + top: PassyTheme.passyPadding.top / 2, + bottom: PassyTheme.passyPadding.bottom / 2), + child: !_tagsLoaded + ? const CircularProgressIndicator() + : EntryTagList( + showAddButton: true, + selected: _selected, + notSelected: _tags, + onAdded: (tag) async { + if (_note!.tags.contains(tag)) return; + Navigator.pushNamed(context, SplashScreen.routeName); + _note!.tags = _selected.toList(); + _note!.tags.add(tag); + await _account.setNote(_note!); + Navigator.popUntil(context, + (r) => r.settings.name == MainScreen.routeName); + Navigator.pushNamed(context, NotesScreen.routeName); + Navigator.pushNamed(context, NoteScreen.routeName, + arguments: _note!); + }, + onRemoved: (tag) async { + Navigator.pushNamed(context, SplashScreen.routeName); + _note!.tags = _selected.toList(); + _note!.tags.remove(tag); + await _account.setNote(_note!); + Navigator.popUntil(context, + (r) => r.settings.name == MainScreen.routeName); + Navigator.pushNamed(context, NotesScreen.routeName); + Navigator.pushNamed(context, NoteScreen.routeName, + arguments: _note!); + }, + ), ), + ), if (_note!.title != '') PassyPadding( RecordButton(title: localizations.title, value: _note!.title)), diff --git a/lib/screens/password_screen.dart b/lib/screens/password_screen.dart index bab01ed7..cc9b1634 100644 --- a/lib/screens/password_screen.dart +++ b/lib/screens/password_screen.dart @@ -260,34 +260,36 @@ class _PasswordScreen extends State { padding: EdgeInsets.only( top: PassyTheme.passyPadding.top / 2, bottom: PassyTheme.passyPadding.bottom / 2), - child: EntryTagList( - showAddButton: true, - selected: _selected, - notSelected: _tags, - onAdded: (tag) async { - if (password!.tags.contains(tag)) return; - Navigator.pushNamed(context, SplashScreen.routeName); - password!.tags = _selected.toList(); - password!.tags.add(tag); - await _account.setPassword(password!); - Navigator.popUntil( - context, (r) => r.settings.name == MainScreen.routeName); - Navigator.pushNamed(context, PasswordsScreen.routeName); - Navigator.pushNamed(context, PasswordScreen.routeName, - arguments: password!); - }, - onRemoved: (tag) async { - Navigator.pushNamed(context, SplashScreen.routeName); - password!.tags = _selected.toList(); - password!.tags.remove(tag); - await _account.setPassword(password!); - Navigator.popUntil( - context, (r) => r.settings.name == MainScreen.routeName); - Navigator.pushNamed(context, PasswordsScreen.routeName); - Navigator.pushNamed(context, PasswordScreen.routeName, - arguments: password!); - }, - ), + child: !_tagsLoaded + ? const CircularProgressIndicator() + : EntryTagList( + showAddButton: true, + selected: _selected, + notSelected: _tags, + onAdded: (tag) async { + if (password!.tags.contains(tag)) return; + Navigator.pushNamed(context, SplashScreen.routeName); + password!.tags = _selected.toList(); + password!.tags.add(tag); + await _account.setPassword(password!); + Navigator.popUntil(context, + (r) => r.settings.name == MainScreen.routeName); + Navigator.pushNamed(context, PasswordsScreen.routeName); + Navigator.pushNamed(context, PasswordScreen.routeName, + arguments: password!); + }, + onRemoved: (tag) async { + Navigator.pushNamed(context, SplashScreen.routeName); + password!.tags = _selected.toList(); + password!.tags.remove(tag); + await _account.setPassword(password!); + Navigator.popUntil(context, + (r) => r.settings.name == MainScreen.routeName); + Navigator.pushNamed(context, PasswordsScreen.routeName); + Navigator.pushNamed(context, PasswordScreen.routeName, + arguments: password!); + }, + ), ), ), if (password!.attachments.isNotEmpty) diff --git a/lib/screens/payment_card_screen.dart b/lib/screens/payment_card_screen.dart index 22f5c619..ae4a5d60 100644 --- a/lib/screens/payment_card_screen.dart +++ b/lib/screens/payment_card_screen.dart @@ -144,34 +144,38 @@ class _PaymentCardScreen extends State { padding: EdgeInsets.only( top: PassyTheme.passyPadding.top / 2, bottom: PassyTheme.passyPadding.bottom / 2), - child: EntryTagList( - showAddButton: true, - selected: _selected, - notSelected: _tags, - onAdded: (tag) async { - if (_paymentCard!.tags.contains(tag)) return; - Navigator.pushNamed(context, SplashScreen.routeName); - _paymentCard!.tags = _selected.toList(); - _paymentCard!.tags.add(tag); - await _account.setPaymentCard(_paymentCard!); - Navigator.popUntil( - context, (r) => r.settings.name == MainScreen.routeName); - Navigator.pushNamed(context, PaymentCardsScreen.routeName); - Navigator.pushNamed(context, PaymentCardScreen.routeName, - arguments: _paymentCard!); - }, - onRemoved: (tag) async { - Navigator.pushNamed(context, SplashScreen.routeName); - _paymentCard!.tags = _selected.toList(); - _paymentCard!.tags.remove(tag); - await _account.setPaymentCard(_paymentCard!); - Navigator.popUntil( - context, (r) => r.settings.name == MainScreen.routeName); - Navigator.pushNamed(context, PaymentCardsScreen.routeName); - Navigator.pushNamed(context, PaymentCardScreen.routeName, - arguments: _paymentCard!); - }, - ), + child: !_tagsLoaded + ? const CircularProgressIndicator() + : EntryTagList( + showAddButton: true, + selected: _selected, + notSelected: _tags, + onAdded: (tag) async { + if (_paymentCard!.tags.contains(tag)) return; + Navigator.pushNamed(context, SplashScreen.routeName); + _paymentCard!.tags = _selected.toList(); + _paymentCard!.tags.add(tag); + await _account.setPaymentCard(_paymentCard!); + Navigator.popUntil(context, + (r) => r.settings.name == MainScreen.routeName); + Navigator.pushNamed( + context, PaymentCardsScreen.routeName); + Navigator.pushNamed(context, PaymentCardScreen.routeName, + arguments: _paymentCard!); + }, + onRemoved: (tag) async { + Navigator.pushNamed(context, SplashScreen.routeName); + _paymentCard!.tags = _selected.toList(); + _paymentCard!.tags.remove(tag); + await _account.setPaymentCard(_paymentCard!); + Navigator.popUntil(context, + (r) => r.settings.name == MainScreen.routeName); + Navigator.pushNamed( + context, PaymentCardsScreen.routeName); + Navigator.pushNamed(context, PaymentCardScreen.routeName, + arguments: _paymentCard!); + }, + ), ), ), if (_paymentCard!.attachments.isNotEmpty) From 1a576dfaa0416e69e2e05fc150e65b308c6ebd7c Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Wed, 13 Mar 2024 20:39:00 +0000 Subject: [PATCH 071/131] Update main_screen.dart --- lib/screens/main_screen.dart | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/screens/main_screen.dart b/lib/screens/main_screen.dart index 59371806..56b71866 100644 --- a/lib/screens/main_screen.dart +++ b/lib/screens/main_screen.dart @@ -585,18 +585,16 @@ class _MainScreen extends State onPressed: () => Navigator.pushNamed(context, IdentitiesScreen.routeName), )), - /* PassyPadding(ThreeWidgetButton( - center: Text(localizations.files), + center: Text(localizations.files + ' (Coming soon)'), left: const Padding( padding: EdgeInsets.only(right: 30), child: Icon(Icons.description_outlined), ), right: const Icon(Icons.arrow_forward_ios_rounded), - onPressed: () => Navigator.pushNamed(context, FilesScreen.routeName) - .then((value) => setState(() {})), + //onPressed: () => Navigator.pushNamed(context, FilesScreen.routeName) + // .then((value) => setState(() {})), )), - */ if ((_account.keyDerivationType == KeyDerivationType.none) && recommendKeyDerivation) PassyPadding(ThreeWidgetButton( From 801adb174badea5303d54ef6c5bd366f5af0c206 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Wed, 13 Mar 2024 20:57:58 +0000 Subject: [PATCH 072/131] PopScope methods repaired --- lib/screens/add_account_screen.dart | 1 + lib/screens/main_screen.dart | 1 + lib/screens/unlock_screen.dart | 7 ++----- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/screens/add_account_screen.dart b/lib/screens/add_account_screen.dart index 0471a1e1..9118c433 100644 --- a/lib/screens/add_account_screen.dart +++ b/lib/screens/add_account_screen.dart @@ -103,6 +103,7 @@ class _AddAccountScreen extends State { } void _onWillPop(bool isPopped) { + if (isPopped) return; if (data.noAccounts) { Navigator.pop(context); return; diff --git a/lib/screens/main_screen.dart b/lib/screens/main_screen.dart index 56b71866..1086123a 100644 --- a/lib/screens/main_screen.dart +++ b/lib/screens/main_screen.dart @@ -417,6 +417,7 @@ class _MainScreen extends State } void _onWillPop(bool isPopped) { + if (isPopped) return; _logOut(); } diff --git a/lib/screens/unlock_screen.dart b/lib/screens/unlock_screen.dart index 29d729e0..96a5d4e3 100644 --- a/lib/screens/unlock_screen.dart +++ b/lib/screens/unlock_screen.dart @@ -38,15 +38,12 @@ class _UnlockScreen extends State with WidgetsBindingObserver { } void _onWillPop(bool isPopped) { + if (isPopped) return; if (_shouldPop) { Navigator.pop(context); return; } - Navigator.popUntil(context, (route) { - if (route.settings.name != MainScreen.routeName) return false; - _logOut(); - return true; - }); + _logOut(); } Future _bioAuth() async { From 7e1a66ccf979d0b5f3ef463bb89eed5ab5bcd9b8 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Thu, 14 Mar 2024 00:36:04 +0000 Subject: [PATCH 073/131] Main Screen button arrangement improved --- lib/screens/main_screen.dart | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/lib/screens/main_screen.dart b/lib/screens/main_screen.dart index 1086123a..d541cb38 100644 --- a/lib/screens/main_screen.dart +++ b/lib/screens/main_screen.dart @@ -684,24 +684,25 @@ class _MainScreen extends State child: ListView( children: [ _screenButtons[0], - _screenButtons[1], - _screenButtons[2], + _screenButtons[3], + _screenButtons[6], + if (_screenButtons.length == 10) _screenButtons[9], ], )), Expanded( child: ListView( children: [ - _screenButtons[3], + _screenButtons[1], _screenButtons[4], - _screenButtons[5], + _screenButtons[7], ], )), Expanded( child: ListView( children: [ - _screenButtons[6], - _screenButtons[7], - if (_screenButtons.length == 9) _screenButtons[8], + _screenButtons[2], + _screenButtons[5], + _screenButtons[8], ], )) ]); @@ -711,19 +712,20 @@ class _MainScreen extends State Expanded( child: ListView(children: [ _screenButtons[0], - _screenButtons[1], _screenButtons[2], - _screenButtons[3], - if (_screenButtons.length == 9) _screenButtons[8], + _screenButtons[4], + _screenButtons[6], + _screenButtons[8], ]), ), Expanded( child: ListView( children: [ - _screenButtons[4], + _screenButtons[1], + _screenButtons[3], _screenButtons[5], - _screenButtons[6], _screenButtons[7], + if (_screenButtons.length == 10) _screenButtons[9], ], )), ]); From ec23d7f19549e23722eed80f9fb71f43212b1b26 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Thu, 14 Mar 2024 00:41:50 +0000 Subject: [PATCH 074/131] Tag search optimized --- lib/passy_data/passy_search.dart | 6 ++++-- lib/screens/id_cards_screen.dart | 6 ++++-- lib/screens/identities_screen.dart | 6 ++++-- lib/screens/main_screen.dart | 6 ++++-- lib/screens/notes_screen.dart | 6 ++++-- lib/screens/payment_cards_screen.dart | 6 ++++-- 6 files changed, 24 insertions(+), 12 deletions(-) diff --git a/lib/passy_data/passy_search.dart b/lib/passy_data/passy_search.dart index 2ebfa287..e9bc629e 100644 --- a/lib/passy_data/passy_search.dart +++ b/lib/passy_data/passy_search.dart @@ -19,8 +19,10 @@ class PassySearch { int _positiveCount = 0; bool _tagMismatch = false; for (String tag in tags) { - if (_password.tags.contains(tag)) continue; - _tagMismatch = true; + if (!_password.tags.contains(tag)) { + _tagMismatch = true; + break; + } } if (_tagMismatch) continue; for (String _term in _terms) { diff --git a/lib/screens/id_cards_screen.dart b/lib/screens/id_cards_screen.dart index 5efe70a6..15b7edd5 100644 --- a/lib/screens/id_cards_screen.dart +++ b/lib/screens/id_cards_screen.dart @@ -48,8 +48,10 @@ class _IDCardsScreen extends State { { bool _tagMismatch = false; for (String tag in tags) { - if (_idCard.tags.contains(tag)) continue; - _tagMismatch = true; + if (!_idCard.tags.contains(tag)) { + _tagMismatch = true; + break; + } } if (_tagMismatch) continue; int _positiveCount = 0; diff --git a/lib/screens/identities_screen.dart b/lib/screens/identities_screen.dart index 14c9a4de..75b4405c 100644 --- a/lib/screens/identities_screen.dart +++ b/lib/screens/identities_screen.dart @@ -49,8 +49,10 @@ class _IdentitiesScreen extends State { { bool _tagMismatch = false; for (String tag in tags) { - if (_identity.tags.contains(tag)) continue; - _tagMismatch = true; + if (!_identity.tags.contains(tag)) { + _tagMismatch = true; + break; + } } if (_tagMismatch) continue; int _positiveCount = 0; diff --git a/lib/screens/main_screen.dart b/lib/screens/main_screen.dart index d541cb38..deba27aa 100644 --- a/lib/screens/main_screen.dart +++ b/lib/screens/main_screen.dart @@ -137,8 +137,10 @@ class _MainScreen extends State { bool _tagMismatch = false; for (String tag in tags) { - if (_searchEntry.tags.contains(tag)) continue; - _tagMismatch = true; + if (!_searchEntry.tags.contains(tag)) { + _tagMismatch = true; + break; + } } if (_tagMismatch) continue; int _positiveCount = 0; diff --git a/lib/screens/notes_screen.dart b/lib/screens/notes_screen.dart index 616a2014..cf3ae035 100644 --- a/lib/screens/notes_screen.dart +++ b/lib/screens/notes_screen.dart @@ -49,8 +49,10 @@ class _NotesScreen extends State { { bool _tagMismatch = false; for (String tag in tags) { - if (_note.tags.contains(tag)) continue; - _tagMismatch = true; + if (!_note.tags.contains(tag)) { + _tagMismatch = true; + break; + } } if (_tagMismatch) continue; int _positiveCount = 0; diff --git a/lib/screens/payment_cards_screen.dart b/lib/screens/payment_cards_screen.dart index 7f7d4977..3ee52e1c 100644 --- a/lib/screens/payment_cards_screen.dart +++ b/lib/screens/payment_cards_screen.dart @@ -45,8 +45,10 @@ class _PaymentCardsScreen extends State { int _positiveCount = 0; bool _tagMismatch = false; for (String tag in tags) { - if (_paymentCard.tags.contains(tag)) continue; - _tagMismatch = true; + if (!_paymentCard.tags.contains(tag)) { + _tagMismatch = true; + break; + } } if (_tagMismatch) continue; for (String _term in _terms) { From 58be78546314e48e1e1faeff47570a4a2b6d2097 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 16 Mar 2024 22:00:28 +0000 Subject: [PATCH 075/131] Update bug_report.yml --- .github/ISSUE_TEMPLATE/bug_report.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 428eeb61..96e4449d 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -37,6 +37,7 @@ body: label: App version description: Select the app version. You can find it in the app settings under the `About` section. options: + - v1.8.0 - Entry Tags - v1.7.0 - Better Synchronization - v1.6.0 - Markdown Notes - v1.5.2 - Windows Hotfix From 24592181c7cdd0685941c000ea1f6008aded2e29 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 16 Mar 2024 22:03:13 +0000 Subject: [PATCH 076/131] Version changed --- lib/passy_data/common.dart | 2 +- linux_assets/com.glitterware.passy.appdata.xml | 2 +- pubspec.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/passy_data/common.dart b/lib/passy_data/common.dart index 75ddeebc..47d602fe 100644 --- a/lib/passy_data/common.dart +++ b/lib/passy_data/common.dart @@ -17,7 +17,7 @@ import 'dart:io'; import 'glare/glare_client.dart'; import 'key_derivation_type.dart'; -const String passyVersion = '1.7.0'; +const String passyVersion = '1.8.0'; const String syncVersion = '2.1.0'; const String accountVersion = '2.4.0'; diff --git a/linux_assets/com.glitterware.passy.appdata.xml b/linux_assets/com.glitterware.passy.appdata.xml index 79d4aebc..445eab31 100644 --- a/linux_assets/com.glitterware.passy.appdata.xml +++ b/linux_assets/com.glitterware.passy.appdata.xml @@ -41,7 +41,7 @@ - + diff --git a/pubspec.yaml b/pubspec.yaml index 9867ca6f..41b11af5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.7.0+18 +version: 1.8.0+19 environment: sdk: ">=2.17.0 <3.0.0" From f245e46f7ae14fdf280ac46a989946978d495d26 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 16 Mar 2024 22:53:30 +0000 Subject: [PATCH 077/131] Update setup_screen.dart --- lib/screens/setup_screen.dart | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/lib/screens/setup_screen.dart b/lib/screens/setup_screen.dart index 7d081858..82648963 100644 --- a/lib/screens/setup_screen.dart +++ b/lib/screens/setup_screen.dart @@ -30,6 +30,7 @@ class _SetupScreen extends State { centerTitle: true, ), floatingActionButton: FloatingActionButton( + tooltip: localizations.done, child: const Icon(Icons.check_rounded), onPressed: () => Navigator.pushReplacementNamed(context, MainScreen.routeName), @@ -71,6 +72,26 @@ class _SetupScreen extends State { context, AutomaticBackupScreen.routeName, arguments: data.loadedAccount!.username), )), + PassyPadding(ThreeWidgetButton( + color: PassyTheme.lightContentColor, + center: Text( + localizations.done, + style: const TextStyle(color: PassyTheme.darkContentColor), + ), + left: const Padding( + padding: EdgeInsets.only(right: 30), + child: Icon( + Icons.check_rounded, + color: PassyTheme.darkContentColor, + ), + ), + right: const Icon( + Icons.arrow_forward_ios_rounded, + color: PassyTheme.darkContentColor, + ), + onPressed: () => + Navigator.pushReplacementNamed(context, MainScreen.routeName), + )), ], ), ); From e3f8fdfbffb2383e5589ccd26ee7f82f6e73b21a Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 16 Mar 2024 23:08:52 +0000 Subject: [PATCH 078/131] Update confirm_import_screen.dart --- lib/screens/confirm_import_screen.dart | 34 ++++++++++++++------------ 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/lib/screens/confirm_import_screen.dart b/lib/screens/confirm_import_screen.dart index 020cba47..023d4784 100644 --- a/lib/screens/confirm_import_screen.dart +++ b/lib/screens/confirm_import_screen.dart @@ -138,22 +138,24 @@ class _ConfirmImportScreen extends State { } return ConfirmStringScaffold( title: Text(title), - message: PassyPadding(Text.rich( - TextSpan( - text: localizations.confirmImport1, - children: [ - TextSpan( - text: localizations.confirmImport2Highlighted, - style: const TextStyle( - color: PassyTheme.lightContentSecondaryColor), - ), - TextSpan( - text: - '${localizations.confirmImport3}.\n\n${localizations.enterAccountPasswordToImport}.'), - ], - ), - textAlign: TextAlign.center, - )), + message: args.importType == ImportType.passy + ? PassyPadding(Text.rich( + TextSpan( + text: localizations.confirmImport1, + children: [ + TextSpan( + text: localizations.confirmImport2Highlighted, + style: const TextStyle( + color: PassyTheme.lightContentSecondaryColor), + ), + TextSpan( + text: + '${localizations.confirmImport3}.\n\n${localizations.enterAccountPasswordToImport}.'), + ], + ), + textAlign: TextAlign.center, + )) + : PassyPadding(Text(localizations.enterAccountPasswordToImport)), labelText: localizations.enterPassword, obscureText: true, confirmIcon: const Icon(Icons.download_for_offline_outlined), From d9fcea6e64eaf655c7da091637b84ea07b103131 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 16 Mar 2024 23:49:38 +0000 Subject: [PATCH 079/131] Popup menu button padding adjusted --- lib/passy_flutter/widgets/id_card_button.dart | 2 +- lib/passy_flutter/widgets/identity_button.dart | 2 +- lib/passy_flutter/widgets/password_button.dart | 2 +- lib/passy_flutter/widgets/payment_card_button_mini.dart | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/passy_flutter/widgets/id_card_button.dart b/lib/passy_flutter/widgets/id_card_button.dart index 34ac4aa3..5b7abab1 100644 --- a/lib/passy_flutter/widgets/id_card_button.dart +++ b/lib/passy_flutter/widgets/id_card_button.dart @@ -53,7 +53,7 @@ class IDCardButton extends StatelessWidget { child: PopupMenuButton( shape: PassyTheme.dialogShape, icon: const Icon(Icons.more_vert_rounded), - padding: const EdgeInsets.fromLTRB(12, 14, 12, 14), + padding: const EdgeInsets.fromLTRB(12, 22, 12, 22), splashRadius: 24, itemBuilder: popupMenuItemBuilder!, ), diff --git a/lib/passy_flutter/widgets/identity_button.dart b/lib/passy_flutter/widgets/identity_button.dart index 25ee5cb8..0a700d32 100644 --- a/lib/passy_flutter/widgets/identity_button.dart +++ b/lib/passy_flutter/widgets/identity_button.dart @@ -52,7 +52,7 @@ class IdentityButton extends StatelessWidget { child: PopupMenuButton( shape: PassyTheme.dialogShape, icon: const Icon(Icons.more_vert_rounded), - padding: const EdgeInsets.fromLTRB(12, 14, 12, 14), + padding: const EdgeInsets.fromLTRB(12, 22, 12, 22), splashRadius: 24, itemBuilder: popupMenuItemBuilder!, ), diff --git a/lib/passy_flutter/widgets/password_button.dart b/lib/passy_flutter/widgets/password_button.dart index 9225c341..33d35967 100644 --- a/lib/passy_flutter/widgets/password_button.dart +++ b/lib/passy_flutter/widgets/password_button.dart @@ -53,7 +53,7 @@ class PasswordButton extends StatelessWidget { child: PopupMenuButton( shape: PassyTheme.dialogShape, icon: const Icon(Icons.more_vert_rounded), - padding: const EdgeInsets.fromLTRB(12, 23, 12, 23), + padding: const EdgeInsets.fromLTRB(12, 22, 12, 22), splashRadius: 24, itemBuilder: popupMenuItemBuilder!, ), diff --git a/lib/passy_flutter/widgets/payment_card_button_mini.dart b/lib/passy_flutter/widgets/payment_card_button_mini.dart index 234d4ea0..1a8bfd5f 100644 --- a/lib/passy_flutter/widgets/payment_card_button_mini.dart +++ b/lib/passy_flutter/widgets/payment_card_button_mini.dart @@ -53,7 +53,7 @@ class PaymentCardButtonMini extends StatelessWidget { child: PopupMenuButton( shape: PassyTheme.dialogShape, icon: const Icon(Icons.more_vert_rounded), - padding: const EdgeInsets.fromLTRB(12, 23, 12, 23), + padding: const EdgeInsets.fromLTRB(12, 22, 12, 22), splashRadius: 24, itemBuilder: popupMenuItemBuilder!, ), From 1af2a4704c5c3d3c283044a695c65ce3eabd44dc Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 16 Mar 2024 23:51:51 +0000 Subject: [PATCH 080/131] Update password_screen.dart --- lib/screens/password_screen.dart | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/lib/screens/password_screen.dart b/lib/screens/password_screen.dart index cc9b1634..6b5930cb 100644 --- a/lib/screens/password_screen.dart +++ b/lib/screens/password_screen.dart @@ -185,14 +185,11 @@ class _PasswordScreen extends State { Navigator.pushNamed(context, SplashScreen.routeName); password!.tfa!.interval++; await _account.setPassword(password!); - Navigator.popUntil( - context, - (route) => - route.settings.name == PasswordScreen.routeName); - if (!mounted) return; - setState(() { - _tfaCode = password!.tfa!.generate(); - }); + Navigator.popUntil(context, + (r) => r.settings.name == MainScreen.routeName); + Navigator.pushNamed(context, PasswordsScreen.routeName); + Navigator.pushNamed(context, PasswordScreen.routeName, + arguments: password!); }), ], ), From 23b96530063d43c08434c35085a535b4e2b7c2ad Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 16 Mar 2024 23:53:08 +0000 Subject: [PATCH 081/131] Autofocus enabled for ConfirmStringScaffold --- lib/passy_flutter/widgets/confirm_string_scaffold.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/passy_flutter/widgets/confirm_string_scaffold.dart b/lib/passy_flutter/widgets/confirm_string_scaffold.dart index 56282ffc..39171119 100644 --- a/lib/passy_flutter/widgets/confirm_string_scaffold.dart +++ b/lib/passy_flutter/widgets/confirm_string_scaffold.dart @@ -64,6 +64,7 @@ class _ConfirmStringScaffold extends State { widget.message, PassyPadding( ButtonedTextFormField( + autofocus: true, labelText: widget.labelText, obscureText: widget.obscureText, onChanged: (s) => setState(() => _input = s), From 3eaaccdf13dcaa81298e0aa82062f129a98de3b6 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 16 Mar 2024 23:53:15 +0000 Subject: [PATCH 082/131] Update convert_aegis.dart --- lib/passy_data/convert_aegis.dart | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/lib/passy_data/convert_aegis.dart b/lib/passy_data/convert_aegis.dart index 71e39396..7af9bc79 100644 --- a/lib/passy_data/convert_aegis.dart +++ b/lib/passy_data/convert_aegis.dart @@ -4,6 +4,7 @@ import 'dart:typed_data'; import 'package:convert/convert.dart'; import 'package:otp/otp.dart' as otp; +import 'package:passy/passy_data/custom_field.dart'; import 'package:passy/passy_data/password.dart'; import 'package:passy/passy_data/tfa.dart'; import 'package:pointycastle/key_derivators/scrypt.dart'; @@ -47,12 +48,19 @@ List _convertAegis(Map db) { TFAType? tfaType = _aegisTypeToTFAType(type); if (tfaType == null) continue; var info = entry['info']; + if (info is! Map) continue; var algo = info['algo']; otp.Algorithm? tfaAlgo = _aegisAlgoToTFAType(algo); if (tfaAlgo == null) continue; - var secret = info['secret']; - var digits = info['digits']; - var period = info['period']; + var secret = info.containsKey('secret') ? info['secret'] : ''; + var digits = info.containsKey('digits') ? info['digits'] : 6; + var period = info.containsKey('period') + ? info['period'] + : (info.containsKey('counter') + ? info['counter'] + : (tfaType == TFAType.HOTP) + ? 0 + : 30); TFA tfa = TFA( type: tfaType, algorithm: tfaAlgo, @@ -61,23 +69,22 @@ List _convertAegis(Map db) { interval: period, isGoogle: tfaType != TFAType.Steam, ); - var issuer = info['issuer']; - var note = info['note']; + var issuer = entry['issuer']; + var note = entry['note']; String additionalInfo = ''; - if (issuer != null) additionalInfo = issuer; - if (note != null) { - if (additionalInfo.isEmpty) { - additionalInfo = note; - } else { - additionalInfo += '\n\n$note'; - } + List customFields = []; + if (issuer != null) { + customFields.add(CustomField(title: 'Issuer', value: issuer)); } + if (note != null) additionalInfo = note; + String key = '$keyPrefix-$i'; result.add(Password( key: key, nickname: name, tfa: tfa, additionalInfo: additionalInfo, + customFields: customFields, )); i++; } From ad0d41035b641b022531e54da86411659a1870fc Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 16 Mar 2024 23:54:18 +0000 Subject: [PATCH 083/131] Padding used for Payment Card buttons --- lib/screens/edit_payment_card_screen.dart | 4 ++-- lib/screens/payment_card_screen.dart | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/screens/edit_payment_card_screen.dart b/lib/screens/edit_payment_card_screen.dart index 23259295..7f219c76 100644 --- a/lib/screens/edit_payment_card_screen.dart +++ b/lib/screens/edit_payment_card_screen.dart @@ -108,7 +108,7 @@ class _EditPaymentCardScreen extends State { ), body: ListView( children: [ - PaymentCardButton( + PassyPadding(PaymentCardButton( paymentCard: PaymentCardMeta( key: '', tags: [], @@ -120,7 +120,7 @@ class _EditPaymentCardScreen extends State { obscureCardNumber: false, obscureCardCvv: false, isSwipeGestureEnabled: false, - ), + )), /* AttachmentsEditor( files: _attachments, diff --git a/lib/screens/payment_card_screen.dart b/lib/screens/payment_card_screen.dart index ae4a5d60..892d5c2e 100644 --- a/lib/screens/payment_card_screen.dart +++ b/lib/screens/payment_card_screen.dart @@ -133,12 +133,12 @@ class _PaymentCardScreen extends State { }, ), body: ListView(children: [ - PaymentCardButton( + PassyPadding(PaymentCardButton( paymentCard: _paymentCard!.uncensoredMetadata, obscureCardNumber: false, obscureCardCvv: false, isSwipeGestureEnabled: false, - ), + )), Center( child: Padding( padding: EdgeInsets.only( From 43aada3deba726bf747cae99c3f2532b79c036cf Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 16 Mar 2024 23:54:36 +0000 Subject: [PATCH 084/131] Payment Cards screen title centered --- lib/screens/payment_cards_screen.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/screens/payment_cards_screen.dart b/lib/screens/payment_cards_screen.dart index 3ee52e1c..588cb0eb 100644 --- a/lib/screens/payment_cards_screen.dart +++ b/lib/screens/payment_cards_screen.dart @@ -134,7 +134,7 @@ class _PaymentCardsScreen extends State { return Scaffold( appBar: EntriesScreenAppBar( entryType: EntryType.paymentCard, - title: Text(localizations.paymentCards), + title: Center(child: Text(localizations.paymentCards)), onAddPressed: _onAddPressed, onSearchPressed: _onSearchPressed, ), From 9dc72b3bc8548c9d585c2e2feb352a9bb1e140d9 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sun, 17 Mar 2024 00:29:04 +0000 Subject: [PATCH 085/131] Update edit_password_screen.dart --- lib/screens/edit_password_screen.dart | 30 ++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/lib/screens/edit_password_screen.dart b/lib/screens/edit_password_screen.dart index 73eed109..edc84574 100644 --- a/lib/screens/edit_password_screen.dart +++ b/lib/screens/edit_password_screen.dart @@ -1,7 +1,10 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_autofill_service/flutter_autofill_service.dart'; import 'package:otp/otp.dart'; +import 'package:base32/base32.dart'; import 'package:passy/common/common.dart'; import 'package:passy/passy_data/custom_field.dart'; import 'package:passy/passy_data/loaded_account.dart'; @@ -49,6 +52,8 @@ class _EditPasswordScreen extends State { UniqueKey _tfaKey = UniqueKey(); String _website = ''; List _attachments = []; + String _steamSharedSecret = ''; + UniqueKey _tfaSecretFieldKey = UniqueKey(); @override Widget build(BuildContext context) { @@ -246,7 +251,30 @@ class _EditPasswordScreen extends State { }); }, )), + if (_tfaType == TFAType.Steam) + PassyPadding(TextFormField( + initialValue: _steamSharedSecret, + decoration: const InputDecoration( + labelText: 'Steam shared_secret'), + onChanged: (value) { + String? newTfaSecret; + try { + newTfaSecret = base32.encode(base64Decode(value)); + if (newTfaSecret.length.isOdd) { + newTfaSecret += '='; + } + } catch (_) {} + setState(() { + _steamSharedSecret = value; + if (newTfaSecret != null) { + _tfaSecret = newTfaSecret; + _tfaSecretFieldKey = UniqueKey(); + } + }); + }, + )), PassyPadding(TextFormField( + key: _tfaSecretFieldKey, initialValue: _tfaSecret.replaceFirst('=', ''), decoration: InputDecoration( labelText: @@ -283,7 +311,7 @@ class _EditPasswordScreen extends State { initialValue: _tfaInterval.toString(), decoration: InputDecoration( labelText: - '2FA ${_tfaType == TFAType.HOTP ? localizations.counter .toLowerCase(): localizations.interval.toLowerCase()}'), + '2FA ${_tfaType == TFAType.HOTP ? localizations.counter.toLowerCase() : localizations.interval.toLowerCase()}'), inputFormatters: [ FilteringTextInputFormatter.digitsOnly ], From 64382a2788f1f88b0c7bf09d8980290b1fe529c2 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sun, 17 Mar 2024 02:50:34 +0000 Subject: [PATCH 086/131] Unlock Screen improved --- lib/main.dart | 25 +++++++++ lib/screens/main_screen.dart | 26 +--------- lib/screens/unlock_screen.dart | 94 +++++++++++++++++++++++----------- 3 files changed, 89 insertions(+), 56 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 0e021fc3..291eddf2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -198,6 +198,31 @@ class Passy extends StatelessWidget { const SynchronizationLogsScreen(), UnlockScreen.routeName: (context) => const UnlockScreen(), }, + builder: (context, child) { + return Stack( + children: [ + child!, + Overlay( + initialEntries: [ + OverlayEntry( + builder: (context) => FutureBuilder( + future: Future(() async { + while (SplashScreen.loaded != true) { + await Future.delayed(const Duration(seconds: 1)); + } + return ''; + }), + builder: (ctx, snapshot) { + if (!snapshot.hasData) return const SizedBox.shrink(); + return const UnlockScreen(); + }, + ), + ), + ], + ), + ], + ); + }, localizationsDelegates: const [ AppLocalizations.delegate, // Add this line GlobalMaterialLocalizations.delegate, diff --git a/lib/screens/main_screen.dart b/lib/screens/main_screen.dart index deba27aa..362b8b33 100644 --- a/lib/screens/main_screen.dart +++ b/lib/screens/main_screen.dart @@ -22,7 +22,6 @@ import 'package:passy/screens/note_screen.dart'; import 'package:passy/screens/password_screen.dart'; import 'package:passy/screens/payment_card_screen.dart'; import 'package:passy/screens/search_screen.dart'; -import 'package:passy/screens/unlock_screen.dart'; import 'package:passy/common/common.dart'; import 'package:passy/passy_flutter/passy_theme.dart'; @@ -47,10 +46,9 @@ class MainScreen extends StatefulWidget { } class _MainScreen extends State - with SingleTickerProviderStateMixin, WidgetsBindingObserver, RouteAware { + with SingleTickerProviderStateMixin, RouteAware { final GlobalKey qrKey = GlobalKey(debugLabel: 'QR'); final LoadedAccount _account = data.loadedAccount!; - bool _unlockScreenOn = false; String _lastSyncDate = 'NaN'; Widget _searchBuilder( @@ -429,30 +427,9 @@ class _MainScreen extends State routeObserver.subscribe(this, ModalRoute.of(context)!); } - @override - void didChangeAppLifecycleState(AppLifecycleState state) { - super.didChangeAppLifecycleState(state); - if (!MainScreen.shouldLockScreen) return; - if (UnlockScreen.isAuthenticating) return; - if (data.loadedAccount == null) return; - if (_unlockScreenOn) return; - if (data.loadedAccount?.autoScreenLock == false) return; - _unlockScreenOn = true; - Navigator.pushNamed(context, UnlockScreen.routeName).then((value) => - Future.delayed(const Duration(seconds: 2)) - .then((value) => _unlockScreenOn = false)); - } - - @override - Future didPushRoute(String route) { - if (route == UnlockScreen.routeName) _unlockScreenOn = true; - return Future.value(true); - } - @override void dispose() { super.dispose(); - WidgetsBinding.instance.removeObserver(this); routeObserver.unsubscribe(this); } @@ -476,7 +453,6 @@ class _MainScreen extends State FlutterSecureScreen.singleton .setAndroidScreenSecure(_account.protectScreen); } - WidgetsBinding.instance.addObserver(this); DateTime? lastSyncDate = _account.lastSyncDate?.toLocal(); if (lastSyncDate != null) { _lastSyncDate = diff --git a/lib/screens/unlock_screen.dart b/lib/screens/unlock_screen.dart index 96a5d4e3..0e6921a0 100644 --- a/lib/screens/unlock_screen.dart +++ b/lib/screens/unlock_screen.dart @@ -3,6 +3,7 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:passy/common/common.dart'; +import 'package:passy/main.dart'; import 'package:passy/passy_data/bio_starge.dart'; import 'package:passy/passy_data/biometric_storage_data.dart'; import 'package:passy/passy_data/loaded_account.dart'; @@ -24,37 +25,41 @@ class UnlockScreen extends StatefulWidget { } class _UnlockScreen extends State with WidgetsBindingObserver { - final LoadedAccount _account = data.loadedAccount!; bool _shouldPop = false; String _password = ''; FloatingActionButton? _bioAuthButton; final TextEditingController _passwordController = TextEditingController(); + bool _unlockScreenOn = false; + final FocusNode _passwordFocus = FocusNode(); void _logOut() { - Navigator.popUntil( - context, (route) => route.settings.name == MainScreen.routeName); - Navigator.pushReplacementNamed(context, LoginScreen.routeName); + Navigator.popUntil(navigatorKey.currentContext!, + (route) => route.settings.name == MainScreen.routeName); + Navigator.pushReplacementNamed( + navigatorKey.currentContext!, LoginScreen.routeName); data.unloadAccount(); + setState(() {}); } void _onWillPop(bool isPopped) { if (isPopped) return; if (_shouldPop) { - Navigator.pop(context); + setState(() => _unlockScreenOn = false); return; } _logOut(); } Future _bioAuth() async { + LoadedAccount? account = data.loadedAccount; + if (account == null) return; if (UnlockScreen.isAuthenticating) return; if (!mounted) return; if (!Platform.isAndroid && !Platform.isIOS) return; - if (!_account.bioAuthEnabled) return; + if (!account.bioAuthEnabled) return; UnlockScreen.isAuthenticating = true; try { - BiometricStorageData data = - await BioStorage.fromLocker(_account.username); + BiometricStorageData data = await BioStorage.fromLocker(account.username); Future.delayed(const Duration(seconds: 2)) .then((value) => UnlockScreen.isAuthenticating = false); if (data.password.isEmpty) return; @@ -64,17 +69,22 @@ class _UnlockScreen extends State with WidgetsBindingObserver { return; } _shouldPop = true; - Navigator.pop(context); + setState(() => _unlockScreenOn = false); } void _unlock() async { + LoadedAccount? account = data.loadedAccount; + if (account == null) return; String _passwordHash = - (await data.createPasswordHash(_account.username, password: _password)) + (await data.createPasswordHash(account.username, password: _password)) .toString(); - _password = ''; - if (_passwordHash == data.getPasswordHash(_account.username)) { + if (_passwordHash == data.getPasswordHash(account.username)) { _shouldPop = true; - Navigator.pop(context); + setState(() { + _password = ''; + _passwordController.text = ''; + _unlockScreenOn = false; + }); return; } showSnackBar( @@ -93,39 +103,60 @@ class _UnlockScreen extends State with WidgetsBindingObserver { @override void initState() { super.initState(); + _passwordFocus.addListener(() { + if (!_unlockScreenOn) return; + if (!_passwordFocus.hasFocus) _passwordFocus.requestFocus(); + }); WidgetsBinding.instance.addObserver(this); - if (!Platform.isAndroid && !Platform.isIOS) return; - if (data.getBioAuthEnabled(_account.username) == true) { - _bioAuthButton = FloatingActionButton( - onPressed: () { - UnlockScreen.isAuthenticating = false; - _bioAuth(); - }, - child: const Icon( - Icons.fingerprint_rounded, - ), - tooltip: localizations.authenticate, - heroTag: null, - ); - return; - } - _bioAuthButton = null; } @override void dispose() { super.dispose(); + _passwordFocus.dispose(); WidgetsBinding.instance.removeObserver(this); } @override void didChangeAppLifecycleState(AppLifecycleState state) async { - if (state != AppLifecycleState.resumed) return; + super.didChangeAppLifecycleState(state); + if (_unlockScreenOn) return; + LoadedAccount? account = data.loadedAccount; + if (account == null) return; + if (!account.autoScreenLock) return; + if (UnlockScreen.isAuthenticating) return; + if ((state != AppLifecycleState.resumed) && + (state != AppLifecycleState.inactive)) return; + setState(() => _unlockScreenOn = true); + _passwordFocus.requestFocus(); await _bioAuth(); } @override Widget build(BuildContext context) { + LoadedAccount? account = data.loadedAccount; + if (account == null) { + _unlockScreenOn = false; + return const SizedBox.shrink(); + } + if (!_unlockScreenOn) return const SizedBox.shrink(); + if (Platform.isAndroid && !Platform.isIOS) { + if (data.getBioAuthEnabled(account.username) == true) { + _bioAuthButton = FloatingActionButton( + onPressed: () { + UnlockScreen.isAuthenticating = false; + _bioAuth(); + }, + child: const Icon( + Icons.fingerprint_rounded, + ), + tooltip: localizations.authenticate, + heroTag: null, + ); + } else { + _bioAuthButton = null; + } + } return PopScope( canPop: false, onPopInvoked: _onWillPop, @@ -154,7 +185,7 @@ class _UnlockScreen extends State with WidgetsBindingObserver { flex: 5, ), Text( - _account.username, + account.username, style: const TextStyle( fontSize: 20, fontWeight: FontWeight.bold, @@ -165,6 +196,7 @@ class _UnlockScreen extends State with WidgetsBindingObserver { ), PassyPadding( ButtonedTextFormField( + focusNode: _passwordFocus, controller: _passwordController, labelText: localizations.password, obscureText: true, From 9f5c0941f6a58819016bc19d95281d871de9c272 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sun, 17 Mar 2024 02:52:49 +0000 Subject: [PATCH 087/131] Update unlock_screen.dart --- lib/screens/unlock_screen.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/screens/unlock_screen.dart b/lib/screens/unlock_screen.dart index 0e6921a0..252af93c 100644 --- a/lib/screens/unlock_screen.dart +++ b/lib/screens/unlock_screen.dart @@ -121,6 +121,7 @@ class _UnlockScreen extends State with WidgetsBindingObserver { void didChangeAppLifecycleState(AppLifecycleState state) async { super.didChangeAppLifecycleState(state); if (_unlockScreenOn) return; + if (!MainScreen.shouldLockScreen) return; LoadedAccount? account = data.loadedAccount; if (account == null) return; if (!account.autoScreenLock) return; From 06788d8cec34529f3301fc8a6a531102deb765d5 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sun, 17 Mar 2024 11:43:23 +0000 Subject: [PATCH 088/131] Update unlock_screen.dart --- lib/screens/unlock_screen.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/screens/unlock_screen.dart b/lib/screens/unlock_screen.dart index 252af93c..725a0217 100644 --- a/lib/screens/unlock_screen.dart +++ b/lib/screens/unlock_screen.dart @@ -44,6 +44,7 @@ class _UnlockScreen extends State with WidgetsBindingObserver { void _onWillPop(bool isPopped) { if (isPopped) return; if (_shouldPop) { + _shouldPop = false; setState(() => _unlockScreenOn = false); return; } From 325c2d9aed734b004eef17e88e0698f6e08b58f8 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sun, 17 Mar 2024 11:47:21 +0000 Subject: [PATCH 089/131] Update unlock_screen.dart --- lib/screens/unlock_screen.dart | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/screens/unlock_screen.dart b/lib/screens/unlock_screen.dart index 725a0217..1cc6b35b 100644 --- a/lib/screens/unlock_screen.dart +++ b/lib/screens/unlock_screen.dart @@ -38,14 +38,18 @@ class _UnlockScreen extends State with WidgetsBindingObserver { Navigator.pushReplacementNamed( navigatorKey.currentContext!, LoginScreen.routeName); data.unloadAccount(); - setState(() {}); + UnlockScreen.shouldLockScreen = true; + setState(() { + _unlockScreenOn = false; + }); } void _onWillPop(bool isPopped) { if (isPopped) return; if (_shouldPop) { - _shouldPop = false; - setState(() => _unlockScreenOn = false); + setState(() { + _shouldPop = false; + _unlockScreenOn = false; return; } _logOut(); From bcf21a0887d2edd741142f01a52aa77fcce848be Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sun, 17 Mar 2024 11:47:30 +0000 Subject: [PATCH 090/131] Update unlock_screen.dart --- lib/screens/unlock_screen.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/screens/unlock_screen.dart b/lib/screens/unlock_screen.dart index 1cc6b35b..4df24ff9 100644 --- a/lib/screens/unlock_screen.dart +++ b/lib/screens/unlock_screen.dart @@ -50,6 +50,7 @@ class _UnlockScreen extends State with WidgetsBindingObserver { setState(() { _shouldPop = false; _unlockScreenOn = false; + }); return; } _logOut(); From 956e69b31f4a61c62d782635fd450730fd34fdde Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sun, 17 Mar 2024 11:55:28 +0000 Subject: [PATCH 091/131] MainScreen.shouldLockScreen -> UnlockScreen --- lib/screens/backup_and_restore_screen.dart | 6 +++--- lib/screens/biometric_auth_screen.dart | 8 ++++---- lib/screens/common.dart | 5 +++-- lib/screens/confirm_kdbx_export_screen.dart | 6 +++--- lib/screens/csv_import_screen.dart | 6 +++--- lib/screens/export_screen.dart | 6 +++--- lib/screens/files_screen.dart | 5 +++-- lib/screens/import_screen.dart | 14 +++++++------- lib/screens/main_screen.dart | 1 - lib/screens/passy_file_screen.dart | 6 +++--- lib/screens/unlock_screen.dart | 3 ++- 11 files changed, 34 insertions(+), 32 deletions(-) diff --git a/lib/screens/backup_and_restore_screen.dart b/lib/screens/backup_and_restore_screen.dart index e836d9c3..b7b3764f 100644 --- a/lib/screens/backup_and_restore_screen.dart +++ b/lib/screens/backup_and_restore_screen.dart @@ -3,10 +3,10 @@ import 'package:flutter/material.dart'; import 'package:passy/common/common.dart'; import 'package:passy/passy_flutter/passy_theme.dart'; import 'package:passy/passy_flutter/widgets/widgets.dart'; +import 'package:passy/screens/unlock_screen.dart'; import 'common.dart'; import 'confirm_restore_screen.dart'; -import 'main_screen.dart'; import 'settings_screen.dart'; class BackupAndRestoreScreen extends StatefulWidget { @@ -28,7 +28,7 @@ class _BackupAndRestoreScreen extends State { } void _onRestorePressed() { - MainScreen.shouldLockScreen = false; + UnlockScreen.shouldLockScreen = false; FilePicker.platform .pickFiles( dialogTitle: localizations.restorePassyBackup, @@ -39,7 +39,7 @@ class _BackupAndRestoreScreen extends State { .then( (_pick) { Future.delayed(const Duration(seconds: 2)) - .then((value) => MainScreen.shouldLockScreen = true); + .then((value) => UnlockScreen.shouldLockScreen = true); if (_pick == null) return; Navigator.pushNamed( context, diff --git a/lib/screens/biometric_auth_screen.dart b/lib/screens/biometric_auth_screen.dart index 22606c37..b71fa566 100644 --- a/lib/screens/biometric_auth_screen.dart +++ b/lib/screens/biometric_auth_screen.dart @@ -7,7 +7,7 @@ import 'package:passy/passy_data/biometric_storage_data.dart'; import 'package:passy/passy_flutter/widgets/widgets.dart'; import 'package:passy/passy_flutter/passy_theme.dart'; import 'package:passy/screens/common.dart'; -import 'package:passy/screens/main_screen.dart'; +import 'package:passy/screens/unlock_screen.dart'; import 'settings_screen.dart'; @@ -39,14 +39,14 @@ class _BiometricAuthScreen extends State { return; } _bioData = BiometricStorageData(key: _username, password: _password); - MainScreen.shouldLockScreen = false; + UnlockScreen.shouldLockScreen = false; try { await _bioData.save(); Future.delayed(const Duration(seconds: 2)) - .then((value) => MainScreen.shouldLockScreen = true); + .then((value) => UnlockScreen.shouldLockScreen = true); } catch (e) { Future.delayed(const Duration(seconds: 2)) - .then((value) => MainScreen.shouldLockScreen = true); + .then((value) => UnlockScreen.shouldLockScreen = true); showSnackBar( message: localizations.couldNotAuthenticate, icon: const Icon(Icons.fingerprint_rounded, diff --git a/lib/screens/common.dart b/lib/screens/common.dart index e899bb78..22c9bcf5 100644 --- a/lib/screens/common.dart +++ b/lib/screens/common.dart @@ -23,6 +23,7 @@ import 'package:passy/screens/id_cards_screen.dart'; import 'package:passy/screens/identities_screen.dart'; import 'package:passy/screens/notes_screen.dart'; import 'package:passy/screens/payment_cards_screen.dart'; +import 'package:passy/screens/unlock_screen.dart'; import 'package:url_launcher/url_launcher_string.dart'; import 'package:zxing2/qrcode.dart'; @@ -99,7 +100,7 @@ Future backupAccount( }) async { if (Platform.isAndroid) autoFilename = true; try { - MainScreen.shouldLockScreen = false; + UnlockScreen.shouldLockScreen = false; String? _fileName; String? _buDir; if (autoFilename) { @@ -117,7 +118,7 @@ Future backupAccount( ); } Future.delayed(const Duration(seconds: 2)) - .then((value) => MainScreen.shouldLockScreen = true); + .then((value) => UnlockScreen.shouldLockScreen = true); if (_buDir == null) return null; if (!autoFilename) _buDir = File(_buDir).parent.path; await data.backupAccount( diff --git a/lib/screens/confirm_kdbx_export_screen.dart b/lib/screens/confirm_kdbx_export_screen.dart index 77fee1f9..62484eb7 100644 --- a/lib/screens/confirm_kdbx_export_screen.dart +++ b/lib/screens/confirm_kdbx_export_screen.dart @@ -7,11 +7,11 @@ import 'package:passy/passy_data/loaded_account.dart'; import 'package:passy/passy_flutter/passy_flutter.dart'; import 'package:passy/screens/common.dart'; import 'package:passy/screens/splash_screen.dart'; +import 'package:passy/screens/unlock_screen.dart'; import 'package:path/path.dart' as path; import 'export_screen.dart'; import 'log_screen.dart'; -import 'main_screen.dart'; class ConfirmKdbxExportScreen extends StatefulWidget { static const String routeName = '${ExportScreen.routeName}/kdbx'; @@ -48,7 +48,7 @@ class _ConfirmKdbxExportScreen extends State { return; } try { - MainScreen.shouldLockScreen = false; + UnlockScreen.shouldLockScreen = false; String fileName = 'passy-kdbx-export-${_account.username}-${DateTime.now().toUtc().toIso8601String().replaceAll(':', ';')}.zip'; String? _expFile; @@ -100,7 +100,7 @@ class _ConfirmKdbxExportScreen extends State { } } Future.delayed(const Duration(seconds: 2)) - .then((value) => MainScreen.shouldLockScreen = true); + .then((value) => UnlockScreen.shouldLockScreen = true); } @override diff --git a/lib/screens/csv_import_screen.dart b/lib/screens/csv_import_screen.dart index abbab935..25cf3dd3 100644 --- a/lib/screens/csv_import_screen.dart +++ b/lib/screens/csv_import_screen.dart @@ -12,10 +12,10 @@ import 'package:passy/passy_data/payment_card.dart'; import 'package:passy/passy_flutter/passy_flutter.dart'; import 'package:passy/screens/csv_import_entries_screen.dart'; import 'package:csv/csv.dart'; +import 'package:passy/screens/unlock_screen.dart'; import 'common.dart'; import 'import_screen.dart'; import 'log_screen.dart'; -import 'main_screen.dart'; class CSVImportScreen extends StatefulWidget { const CSVImportScreen({Key? key}) : super(key: key); @@ -29,7 +29,7 @@ class CSVImportScreen extends StatefulWidget { class _CSVImportScreen extends State { void _onImportPressed( String title, EntryType entryType, Map entryJson) async { - MainScreen.shouldLockScreen = false; + UnlockScreen.shouldLockScreen = false; FilePickerResult? fileResult; try { fileResult = await FilePicker.platform.pickFiles( @@ -50,7 +50,7 @@ class _CSVImportScreen extends State { ); return; } - MainScreen.shouldLockScreen = false; + UnlockScreen.shouldLockScreen = false; if (fileResult == null) return; if (fileResult.files.isEmpty) return; String? filePath = fileResult.files[0].path; diff --git a/lib/screens/export_screen.dart b/lib/screens/export_screen.dart index 81905f39..44497c67 100644 --- a/lib/screens/export_screen.dart +++ b/lib/screens/export_screen.dart @@ -7,11 +7,11 @@ import 'package:passy/common/common.dart'; import 'package:passy/passy_data/loaded_account.dart'; import 'package:passy/passy_flutter/passy_theme.dart'; import 'package:passy/passy_flutter/widgets/widgets.dart'; +import 'package:passy/screens/unlock_screen.dart'; import 'package:path/path.dart' as path; import 'package:passy/screens/common.dart'; import 'package:passy/screens/confirm_kdbx_export_screen.dart'; -import 'package:passy/screens/main_screen.dart'; import 'export_and_import_screen.dart'; import 'log_screen.dart'; @@ -70,7 +70,7 @@ class _ExportScreen extends State { } Future _onPassyExport(String username, _ExportType type) async { - MainScreen.shouldLockScreen = false; + UnlockScreen.shouldLockScreen = false; try { bool? _isConfirmed = await _showExportWarningDialog(); if (_isConfirmed != true) return; @@ -138,7 +138,7 @@ class _ExportScreen extends State { } } Future.delayed(const Duration(seconds: 2)) - .then((value) => MainScreen.shouldLockScreen = true); + .then((value) => UnlockScreen.shouldLockScreen = true); } @override diff --git a/lib/screens/files_screen.dart b/lib/screens/files_screen.dart index 0385ede7..72c84348 100644 --- a/lib/screens/files_screen.dart +++ b/lib/screens/files_screen.dart @@ -11,6 +11,7 @@ import 'package:passy/passy_flutter/passy_flutter.dart'; import 'package:passy/screens/add_file_screen.dart'; import 'package:passy/screens/common.dart'; import 'package:passy/screens/passy_file_screen.dart'; +import 'package:passy/screens/unlock_screen.dart'; import 'main_screen.dart'; @@ -176,13 +177,13 @@ class _FilesScreen extends State { Future _onAddFilePressed(FilesScreenArgs args, {FileEntryType type = FileEntryType.file}) async { - MainScreen.shouldLockScreen = false; + UnlockScreen.shouldLockScreen = false; FilePickerResult? result = await FilePicker.platform.pickFiles( dialogTitle: localizations.addFile, lockParentWindow: true, ); Future.delayed(const Duration(seconds: 2)) - .then((value) => MainScreen.shouldLockScreen = true); + .then((value) => UnlockScreen.shouldLockScreen = true); if (result == null) return; if (result.files.isEmpty) return; File file = File(result.files[0].path!); diff --git a/lib/screens/import_screen.dart b/lib/screens/import_screen.dart index aab4fe7a..755436e6 100644 --- a/lib/screens/import_screen.dart +++ b/lib/screens/import_screen.dart @@ -8,7 +8,7 @@ import 'package:passy/common/common.dart'; import 'package:passy/passy_flutter/passy_flutter.dart'; import 'package:passy/screens/confirm_import_screen.dart'; import 'package:passy/screens/csv_import_screen.dart'; -import 'package:passy/screens/main_screen.dart'; +import 'package:passy/screens/unlock_screen.dart'; import 'export_and_import_screen.dart'; @@ -23,7 +23,7 @@ class ImportScreen extends StatefulWidget { class _ImportScreen extends State { void _onPassyImportPressed() { - MainScreen.shouldLockScreen = false; + UnlockScreen.shouldLockScreen = false; FilePicker.platform .pickFiles( dialogTitle: localizations.importFromPassy, @@ -34,7 +34,7 @@ class _ImportScreen extends State { .then( (_pick) { Future.delayed(const Duration(seconds: 2)) - .then((value) => MainScreen.shouldLockScreen = true); + .then((value) => UnlockScreen.shouldLockScreen = true); if (_pick == null) return; Navigator.pushNamed( context, @@ -49,7 +49,7 @@ class _ImportScreen extends State { } void _onKdbxImportPressed() { - MainScreen.shouldLockScreen = false; + UnlockScreen.shouldLockScreen = false; FilePicker.platform .pickFiles( dialogTitle: localizations.kdbxImport, @@ -60,7 +60,7 @@ class _ImportScreen extends State { .then( (_pick) { Future.delayed(const Duration(seconds: 2)) - .then((value) => MainScreen.shouldLockScreen = true); + .then((value) => UnlockScreen.shouldLockScreen = true); if (_pick == null) return; Navigator.pushNamed( context, @@ -75,7 +75,7 @@ class _ImportScreen extends State { } void _onAegisImportPressed() { - MainScreen.shouldLockScreen = false; + UnlockScreen.shouldLockScreen = false; FilePicker.platform .pickFiles( dialogTitle: localizations.aegisImport, @@ -86,7 +86,7 @@ class _ImportScreen extends State { .then( (_pick) { Future.delayed(const Duration(seconds: 2)) - .then((value) => MainScreen.shouldLockScreen = true); + .then((value) => UnlockScreen.shouldLockScreen = true); if (_pick == null) return; Navigator.pushNamed( context, diff --git a/lib/screens/main_screen.dart b/lib/screens/main_screen.dart index 362b8b33..5579a013 100644 --- a/lib/screens/main_screen.dart +++ b/lib/screens/main_screen.dart @@ -37,7 +37,6 @@ import 'notes_screen.dart'; class MainScreen extends StatefulWidget { static const routeName = '/main'; - static bool shouldLockScreen = true; const MainScreen({Key? key}) : super(key: key); diff --git a/lib/screens/passy_file_screen.dart b/lib/screens/passy_file_screen.dart index b3619e2f..a223b5d3 100644 --- a/lib/screens/passy_file_screen.dart +++ b/lib/screens/passy_file_screen.dart @@ -6,8 +6,8 @@ import 'package:passy/common/common.dart'; import 'package:passy/passy_data/loaded_account.dart'; import 'package:passy/passy_flutter/passy_flutter.dart'; import 'package:passy/screens/files_screen.dart'; -import 'package:passy/screens/main_screen.dart'; import 'package:passy/screens/splash_screen.dart'; +import 'package:passy/screens/unlock_screen.dart'; import 'common.dart'; @@ -36,7 +36,7 @@ class _PassyFileScreen extends State { final LoadedAccount _account = data.loadedAccount!; Future _onExportPressed(PassyFileScreenArgs args) async { - MainScreen.shouldLockScreen = false; + UnlockScreen.shouldLockScreen = false; String? expFile; if (Platform.isAndroid) { String? expDir = await FilePicker.platform.getDirectoryPath( @@ -54,7 +54,7 @@ class _PassyFileScreen extends State { ); } Future.delayed(const Duration(seconds: 2)) - .then((value) => MainScreen.shouldLockScreen = true); + .then((value) => UnlockScreen.shouldLockScreen = true); if (expFile == null) return; Navigator.pushNamed(context, SplashScreen.routeName); await Future.delayed(const Duration(milliseconds: 200)); diff --git a/lib/screens/unlock_screen.dart b/lib/screens/unlock_screen.dart index 4df24ff9..aebd3ae1 100644 --- a/lib/screens/unlock_screen.dart +++ b/lib/screens/unlock_screen.dart @@ -15,6 +15,7 @@ import 'common.dart'; class UnlockScreen extends StatefulWidget { static const String routeName = '/unlock'; + static bool shouldLockScreen = true; static bool isAuthenticating = false; @@ -127,7 +128,7 @@ class _UnlockScreen extends State with WidgetsBindingObserver { void didChangeAppLifecycleState(AppLifecycleState state) async { super.didChangeAppLifecycleState(state); if (_unlockScreenOn) return; - if (!MainScreen.shouldLockScreen) return; + if (!UnlockScreen.shouldLockScreen) return; LoadedAccount? account = data.loadedAccount; if (account == null) return; if (!account.autoScreenLock) return; From 1cb52b6c6144855a2b71803a5546ab47d2b6e57a Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sun, 17 Mar 2024 13:24:08 +0000 Subject: [PATCH 092/131] Update entry_tag_list.dart --- lib/passy_flutter/widgets/entry_tag_list.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/passy_flutter/widgets/entry_tag_list.dart b/lib/passy_flutter/widgets/entry_tag_list.dart index afec7808..1d4266fa 100644 --- a/lib/passy_flutter/widgets/entry_tag_list.dart +++ b/lib/passy_flutter/widgets/entry_tag_list.dart @@ -43,7 +43,7 @@ class _EntryTagList extends State { @override Widget build(BuildContext context) { WidgetsBinding.instance.addPostFrameCallback((_) { - _key.currentContext?.size?.width; + if (!mounted) return; setState(() { showScrollbar = (_key.currentContext?.size?.width ?? 0) == MediaQuery.of(context).size.width; From 3707008d95841b0f7b8be7d9118270e99cdc7f35 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sun, 17 Mar 2024 14:10:44 +0000 Subject: [PATCH 093/131] Update login_screen.dart --- lib/screens/login_screen.dart | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/screens/login_screen.dart b/lib/screens/login_screen.dart index 02634ca7..2141352d 100644 --- a/lib/screens/login_screen.dart +++ b/lib/screens/login_screen.dart @@ -154,8 +154,17 @@ class _LoginScreen extends State { _derivedPassword = (await data.getArgon2Key(_username, password: _password))! .rawBytes; - } catch (_) { - // TODO: show an error log + } catch (e, s) { + showSnackBar( + message: localizations.couldNotLogin, + icon: const Icon(Icons.lock_rounded, + color: PassyTheme.darkContentColor), + action: SnackBarAction( + label: localizations.details, + onPressed: () => Navigator.pushNamed(context, LogScreen.routeName, + arguments: e.toString() + '\n' + s.toString()), + ), + ); _isPasswordWrong = true; break; } From d1c955b1257402e82615bae8499f064419ee6786 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sun, 17 Mar 2024 14:29:29 +0000 Subject: [PATCH 094/131] PassyEntriesEncryptedCSVFile.renameTag() --- .../passy_entries_encrypted_csv_file.dart | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/lib/passy_data/passy_entries_encrypted_csv_file.dart b/lib/passy_data/passy_entries_encrypted_csv_file.dart index 11135434..e5e03967 100644 --- a/lib/passy_data/passy_entries_encrypted_csv_file.dart +++ b/lib/passy_data/passy_entries_encrypted_csv_file.dart @@ -378,4 +378,51 @@ class PassyEntriesEncryptedCSVFile> { }); await _raf.close(); } + + Future renameTag({ + required String tag, + required String newTag, + }) async { + RandomAccessFile _raf = await _file.open(); + if (skipLine(_raf, lineDelimiter: ',') == -1) { + await _raf.close(); + return; + } + File _tempFile; + { + String _tempPath = (Directory.systemTemp).path + + Platform.pathSeparator + + 'passy-set-entries-${entryTypeFromType(T)}-' + + DateTime.now().toUtc().toIso8601String().replaceAll(':', ';'); + _tempFile = await File(_tempPath).create(); + } + int _tagIndex = T.toString() == 'Note' ? 4 : 3; + RandomAccessFile _tempRaf = await _tempFile.open(mode: FileMode.append); + await processLinesAsync(_raf, onLine: (entry, eofReached) async { + if (eofReached) return true; + List _decoded = entry.split(','); + entry = decrypt(_decoded[1], + encrypter: _encrypter, iv: IV.fromBase64(_decoded[0])); + List _csv = csvDecode(entry, recursive: true); + if (_csv.length < _tagIndex + 1) { + return true; + } + var tagList = _csv[_tagIndex]; + for (dynamic oldTag in (tagList as List).toList()) { + oldTag = oldTag.toString(); + if (oldTag != tag) continue; + tagList.remove(tag); + tagList.add(newTag); + } + entry = _encodeEntryForSaving(_csv); + await _tempRaf.writeString(entry); + if (skipLine(_raf, lineDelimiter: ',') == -1) return true; + return null; + }); + await _raf.close(); + await _tempRaf.close(); + await _file.delete(); + await _tempFile.copy(_file.absolute.path); + await _tempFile.delete(); + } } From 7d5df783e93f7c6fc83fda610b9ca28235bc5f8d Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sun, 17 Mar 2024 14:29:49 +0000 Subject: [PATCH 095/131] LoadedAccount.renameTag() implemented --- lib/passy_data/loaded_account.dart | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/passy_data/loaded_account.dart b/lib/passy_data/loaded_account.dart index 18579db6..f201d237 100644 --- a/lib/passy_data/loaded_account.dart +++ b/lib/passy_data/loaded_account.dart @@ -1105,6 +1105,19 @@ class LoadedAccount { return result; } + Future renameTag({ + required String tag, + required String newTag, + }) async { + await Future.wait([ + _passwords.renameTag(tag: tag, newTag: newTag), + _notes.renameTag(tag: tag, newTag: newTag), + _paymentCards.renameTag(tag: tag, newTag: newTag), + _identities.renameTag(tag: tag, newTag: newTag), + _idCards.renameTag(tag: tag, newTag: newTag), + ]); + } + // Passwords wrappers List get passwordKeys => _passwords.keys; Future> get passwordTags => compute( From 328f8311bc2bce946e1ee3d507116abc867ec217 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sun, 17 Mar 2024 14:30:08 +0000 Subject: [PATCH 096/131] Update passy_entries_encrypted_csv_file.dart --- lib/passy_data/passy_entries_encrypted_csv_file.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/passy_data/passy_entries_encrypted_csv_file.dart b/lib/passy_data/passy_entries_encrypted_csv_file.dart index e5e03967..2421ab10 100644 --- a/lib/passy_data/passy_entries_encrypted_csv_file.dart +++ b/lib/passy_data/passy_entries_encrypted_csv_file.dart @@ -334,7 +334,7 @@ class PassyEntriesEncryptedCSVFile> { RandomAccessFile _raf = await _file.open(); RandomAccessFile rafExport = await file.open(mode: FileMode.write); if (skipLine(_raf, lineDelimiter: ',') == -1) { - _raf.closeSync(); + await _raf.close(); return; } if (annotation != null) { @@ -359,7 +359,7 @@ class PassyEntriesEncryptedCSVFile> { final KdbxGroup groupFinal = group ?? file.body.rootGroup; RandomAccessFile _raf = await _file.open(); if (skipLine(_raf, lineDelimiter: ',') == -1) { - _raf.closeSync(); + await _raf.close(); return; } processLines(_raf, onLine: (entry, eofReached) { From 21a61baf936e96620075a4e7db7c1f4349e6242b Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sun, 17 Mar 2024 14:30:29 +0000 Subject: [PATCH 097/131] EntryTagButton.onSecondary implemented --- .../widgets/entry_tag_button.dart | 41 +++++++++++-------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/lib/passy_flutter/widgets/entry_tag_button.dart b/lib/passy_flutter/widgets/entry_tag_button.dart index f6fda3d8..1acc7156 100644 --- a/lib/passy_flutter/widgets/entry_tag_button.dart +++ b/lib/passy_flutter/widgets/entry_tag_button.dart @@ -6,6 +6,7 @@ class EntryTagButton extends StatelessWidget { final Color color; final bool isSelected; final void Function()? onPressed; + final void Function()? onSecondary; const EntryTagButton( this.tag, { @@ -13,27 +14,31 @@ class EntryTagButton extends StatelessWidget { this.color = PassyTheme.lightContentColor, this.isSelected = false, this.onPressed, + this.onSecondary, }); @override Widget build(BuildContext context) { - return TextButton.icon( - onPressed: onPressed ?? () {}, - style: ButtonStyle( - backgroundColor: MaterialStatePropertyAll(color), - ), - icon: Icon(isSelected ? Icons.close_rounded : Icons.add_rounded, - color: PassyTheme.theme.colorScheme.onPrimary), - label: Padding( - padding: EdgeInsets.only( - top: PassyTheme.passyPadding.top / 1.5, - bottom: PassyTheme.passyPadding.bottom / 1.5, - right: PassyTheme.passyPadding.right), - child: Text( - tag, - style: const TextStyle(color: PassyTheme.darkContentColor), - ), - ), - ); + return GestureDetector( + onSecondaryTap: onSecondary, + child: TextButton.icon( + onLongPress: onSecondary, + onPressed: onPressed ?? () {}, + style: ButtonStyle( + backgroundColor: MaterialStatePropertyAll(color), + ), + icon: Icon(isSelected ? Icons.close_rounded : Icons.add_rounded, + color: PassyTheme.theme.colorScheme.onPrimary), + label: Padding( + padding: EdgeInsets.only( + top: PassyTheme.passyPadding.top / 1.5, + bottom: PassyTheme.passyPadding.bottom / 1.5, + right: PassyTheme.passyPadding.right), + child: Text( + tag, + style: const TextStyle(color: PassyTheme.darkContentColor), + ), + ), + )); } } From b031a6aac359853e4c68d3d169fde05c56a8482b Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sun, 17 Mar 2024 14:30:42 +0000 Subject: [PATCH 098/131] EntryTagList.onSecondary implemented --- lib/passy_flutter/widgets/entry_tag_list.dart | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/passy_flutter/widgets/entry_tag_list.dart b/lib/passy_flutter/widgets/entry_tag_list.dart index 1d4266fa..93900617 100644 --- a/lib/passy_flutter/widgets/entry_tag_list.dart +++ b/lib/passy_flutter/widgets/entry_tag_list.dart @@ -7,6 +7,7 @@ class EntryTagList extends StatefulWidget { final List notSelected; final void Function(String tag) onAdded; final void Function(String tag) onRemoved; + final void Function(String tag) onSecondary; final void Function() onAddPressed; final bool showAddButton; @@ -16,14 +17,17 @@ class EntryTagList extends StatefulWidget { this.notSelected = const [], void Function(String tag)? onAdded, void Function(String tag)? onRemoved, + void Function(String tag)? onSecondary, void Function()? onAddPressed, this.showAddButton = false, }) : onAdded = onAdded ?? _onChanged, onRemoved = onRemoved ?? _onChanged, + onSecondary = onSecondary ?? _voidString, onAddPressed = onAddPressed ?? _void; static void _onChanged(tag) {} static void _void() {} + static void _voidString(String foobar) {} @override State createState() => _EntryTagList(); @@ -66,6 +70,9 @@ class _EntryTagList extends State { onPressed: () { widget.onRemoved(tag); }, + onSecondary: () { + widget.onSecondary(tag); + }, ), ), ); @@ -83,6 +90,9 @@ class _EntryTagList extends State { onPressed: () { widget.onAdded(tag); }, + onSecondary: () { + widget.onSecondary(tag); + }, ), ), ); From e36886b7bb3d1bb81b3e584d7bec398206ffc2e0 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sun, 17 Mar 2024 14:38:13 +0000 Subject: [PATCH 099/131] RenameTagDialog implemented --- lib/l10n/app_en.arb | 3 +- .../widgets/rename_tag_dialog.dart | 155 ++++++++++++++++++ 2 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 lib/passy_flutter/widgets/rename_tag_dialog.dart diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 8f7e436a..8bad12c8 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -348,5 +348,6 @@ "aegisImport": "Aegis import", "backupPassy": "Backup Passy", "backupSaved": "Backup saved", - "couldNotBackup": "Could not backup" + "couldNotBackup": "Could not backup", + "renameTag": "Rename tag" } diff --git a/lib/passy_flutter/widgets/rename_tag_dialog.dart b/lib/passy_flutter/widgets/rename_tag_dialog.dart new file mode 100644 index 00000000..5ea958c8 --- /dev/null +++ b/lib/passy_flutter/widgets/rename_tag_dialog.dart @@ -0,0 +1,155 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:passy/common/common.dart'; +import 'package:passy/passy_flutter/passy_flutter.dart'; +import 'package:emoji_picker_flutter/emoji_picker_flutter.dart' as ep; + +class RenameTagDialog extends StatefulWidget { + final String tag; + + const RenameTagDialog({ + Key? key, + required this.tag, + }) : super(key: key); + + @override + State createState() => _RenameTagDialog(); +} + +class _RenameTagDialog extends State { + String _tag = ''; + final TextEditingController _controller = TextEditingController(); + + @override + void initState() { + super.initState(); + _tag = widget.tag; + _controller.text = widget.tag; + _controller.addListener( + () => setState(() => _tag = _controller.text), + ); + } + + @override + void dispose() { + // Clean up the controller when the widget is removed from the + // widget tree. + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Dialog( + shape: PassyTheme.dialogShape, + child: Container( + constraints: const BoxConstraints(maxWidth: 450, maxHeight: 450), + child: ListView( + shrinkWrap: true, + children: [ + Padding( + padding: const EdgeInsets.all(20), + child: Text( + localizations.renameTag, + style: const TextStyle( + fontSize: 20, fontWeight: FontWeight.bold), + )), + PassyPadding( + TextFormField( + controller: _controller, + autofocus: true, + decoration: InputDecoration(labelText: localizations.tag), + onFieldSubmitted: (tag) { + Navigator.pop(context, tag); + }, + ), + ), + SizedBox( + height: 200, + child: Theme( + data: ThemeData( + colorScheme: const ColorScheme.dark( + //primary: Color.fromRGBO(74, 20, 140, 1), + primary: PassyTheme.lightContentColor, + onPrimary: PassyTheme.darkContentColor, + secondary: PassyTheme.lightContentColor, + onSecondary: PassyTheme.lightContentColor, + onSurface: PassyTheme.lightContentColor, + ), + inputDecorationTheme: InputDecorationTheme( + floatingLabelStyle: const TextStyle( + color: PassyTheme.lightContentSecondaryColor), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(100), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(100), + borderSide: const BorderSide( + color: PassyTheme.darkContentSecondaryColor, + ), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(100), + borderSide: const BorderSide( + color: PassyTheme.lightContentColor)), + contentPadding: const EdgeInsets.symmetric( + horizontal: 16, vertical: 16), + ), + ), + child: ep.EmojiPicker( + textEditingController: _controller, + onEmojiSelected: (ep.Category? category, ep.Emoji emoji) {}, + config: ep.Config( + height: 256, + checkPlatformCompatibility: true, + emojiViewConfig: ep.EmojiViewConfig( + backgroundColor: PassyTheme.darkContentColor, + // Issue: https://github.com/flutter/flutter/issues/28894 + emojiSizeMax: 28 * + (defaultTargetPlatform == TargetPlatform.iOS + ? 1.20 + : 1.0), + ), + swapCategoryAndBottomBar: false, + skinToneConfig: const ep.SkinToneConfig(), + categoryViewConfig: const ep.CategoryViewConfig( + backgroundColor: PassyTheme.darkContentColor), + bottomActionBarConfig: const ep.BottomActionBarConfig( + backgroundColor: PassyTheme.darkContentColor), + searchViewConfig: const ep.SearchViewConfig( + buttonIconColor: PassyTheme.lightContentColor, + backgroundColor: PassyTheme.darkContentColor), + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.all(20), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Spacer(), + TextButton( + onPressed: () => Navigator.pop(context), + child: Text(localizations.cancel, + style: const TextStyle( + color: PassyTheme.lightContentSecondaryColor)), + ), + const SizedBox(width: 8), + TextButton( + onPressed: () { + Navigator.pop(context, _tag); + }, + child: Text(localizations.rename, + style: const TextStyle( + color: PassyTheme.lightContentSecondaryColor)), + ), + ], + ), + ), + ], + ), + ), + ); + } +} From 752edb06275affe583a402778a55a01bdadcdaaa Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sun, 17 Mar 2024 14:38:17 +0000 Subject: [PATCH 100/131] Update widgets.dart --- lib/passy_flutter/widgets/widgets.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/passy_flutter/widgets/widgets.dart b/lib/passy_flutter/widgets/widgets.dart index e497dfd4..86be10d3 100644 --- a/lib/passy_flutter/widgets/widgets.dart +++ b/lib/passy_flutter/widgets/widgets.dart @@ -38,5 +38,6 @@ export 'payment_card_button.dart'; export 'record_button.dart'; export 'record_dialog.dart'; export 'rename_file_dialog.dart'; +export 'rename_tag_dialog.dart'; export 'string_generator_dialog.dart'; export 'three_widget_button.dart'; From 05a39c59fa45643229b5ccb3bf65ad17590e2872 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sun, 17 Mar 2024 14:50:09 +0000 Subject: [PATCH 101/131] Rename tag UI implemented --- lib/screens/id_card_screen.dart | 38 +++++++++++++++++++++++++++ lib/screens/identity_screen.dart | 39 ++++++++++++++++++++++++++++ lib/screens/note_screen.dart | 37 ++++++++++++++++++++++++++ lib/screens/password_screen.dart | 38 +++++++++++++++++++++++++++ lib/screens/payment_card_screen.dart | 38 +++++++++++++++++++++++++++ 5 files changed, 190 insertions(+) diff --git a/lib/screens/id_card_screen.dart b/lib/screens/id_card_screen.dart index 1eea5bb1..c1a0dbb6 100644 --- a/lib/screens/id_card_screen.dart +++ b/lib/screens/id_card_screen.dart @@ -13,6 +13,7 @@ import 'package:passy/screens/splash_screen.dart'; import 'common.dart'; import 'edit_id_card_screen.dart'; import 'id_cards_screen.dart'; +import 'log_screen.dart'; import 'main_screen.dart'; class IDCardScreen extends StatefulWidget { @@ -145,6 +146,43 @@ class _IDCardScreen extends State { showAddButton: true, selected: _selected, notSelected: _tags, + onSecondary: (tag) async { + String? newTag = await showDialog( + context: context, + builder: (ctx) => RenameTagDialog(tag: tag), + ); + if (newTag == null) return; + if (newTag == tag) return; + Navigator.pushNamed(context, SplashScreen.routeName); + try { + await _account.renameTag(tag: tag, newTag: newTag); + } catch (e, s) { + Navigator.pop(context); + showSnackBar( + message: localizations.somethingWentWrong, + icon: const Icon(Icons.error_outline_rounded, + color: PassyTheme.darkContentColor), + action: SnackBarAction( + label: localizations.details, + onPressed: () => Navigator.pushNamed( + context, LogScreen.routeName, + arguments: + e.toString() + '\n' + s.toString()), + ), + ); + return; + } + _idCard!.tags = _selected.toList(); + if (_idCard!.tags.contains(tag)) { + _idCard!.tags.remove(tag); + _idCard!.tags.add(newTag); + } + Navigator.popUntil(context, + (r) => r.settings.name == MainScreen.routeName); + Navigator.pushNamed(context, IDCardsScreen.routeName); + Navigator.pushNamed(context, IDCardScreen.routeName, + arguments: _idCard!); + }, onAdded: (tag) async { if (_idCard!.tags.contains(tag)) return; Navigator.pushNamed(context, SplashScreen.routeName); diff --git a/lib/screens/identity_screen.dart b/lib/screens/identity_screen.dart index 6ed772f5..c216f5fe 100644 --- a/lib/screens/identity_screen.dart +++ b/lib/screens/identity_screen.dart @@ -11,6 +11,7 @@ import 'package:passy/passy_flutter/widgets/widgets.dart'; import 'package:passy/screens/splash_screen.dart'; import 'common.dart'; +import 'log_screen.dart'; import 'main_screen.dart'; import 'edit_identity_screen.dart'; import 'identities_screen.dart'; @@ -146,6 +147,44 @@ class _IdentityScreen extends State { showAddButton: true, selected: _selected, notSelected: _tags, + onSecondary: (tag) async { + String? newTag = await showDialog( + context: context, + builder: (ctx) => RenameTagDialog(tag: tag), + ); + if (newTag == null) return; + if (newTag == tag) return; + Navigator.pushNamed(context, SplashScreen.routeName); + try { + await _account.renameTag(tag: tag, newTag: newTag); + } catch (e, s) { + Navigator.pop(context); + showSnackBar( + message: localizations.somethingWentWrong, + icon: const Icon(Icons.error_outline_rounded, + color: PassyTheme.darkContentColor), + action: SnackBarAction( + label: localizations.details, + onPressed: () => Navigator.pushNamed( + context, LogScreen.routeName, + arguments: + e.toString() + '\n' + s.toString()), + ), + ); + return; + } + _identity!.tags = _selected.toList(); + if (_identity!.tags.contains(tag)) { + _identity!.tags.remove(tag); + _identity!.tags.add(newTag); + } + Navigator.popUntil(context, + (r) => r.settings.name == MainScreen.routeName); + Navigator.pushNamed( + context, IdentitiesScreen.routeName); + Navigator.pushNamed(context, IdentityScreen.routeName, + arguments: _identity!); + }, onAdded: (tag) async { if (_identity!.tags.contains(tag)) return; Navigator.pushNamed(context, SplashScreen.routeName); diff --git a/lib/screens/note_screen.dart b/lib/screens/note_screen.dart index e6b941c0..2b7e5d2b 100644 --- a/lib/screens/note_screen.dart +++ b/lib/screens/note_screen.dart @@ -8,6 +8,7 @@ import 'package:passy/passy_flutter/passy_theme.dart'; import 'package:passy/passy_flutter/widgets/widgets.dart'; import 'common.dart'; +import 'log_screen.dart'; import 'main_screen.dart'; import 'edit_note_screen.dart'; import 'notes_screen.dart'; @@ -142,6 +143,42 @@ class _NoteScreen extends State { showAddButton: true, selected: _selected, notSelected: _tags, + onSecondary: (tag) async { + String? newTag = await showDialog( + context: context, + builder: (ctx) => RenameTagDialog(tag: tag), + ); + if (newTag == null) return; + if (newTag == tag) return; + Navigator.pushNamed(context, SplashScreen.routeName); + try { + await _account.renameTag(tag: tag, newTag: newTag); + } catch (e, s) { + Navigator.pop(context); + showSnackBar( + message: localizations.somethingWentWrong, + icon: const Icon(Icons.error_outline_rounded, + color: PassyTheme.darkContentColor), + action: SnackBarAction( + label: localizations.details, + onPressed: () => Navigator.pushNamed( + context, LogScreen.routeName, + arguments: e.toString() + '\n' + s.toString()), + ), + ); + return; + } + _note!.tags = _selected.toList(); + if (_note!.tags.contains(tag)) { + _note!.tags.remove(tag); + _note!.tags.add(newTag); + } + Navigator.popUntil(context, + (r) => r.settings.name == MainScreen.routeName); + Navigator.pushNamed(context, NotesScreen.routeName); + Navigator.pushNamed(context, NoteScreen.routeName, + arguments: _note!); + }, onAdded: (tag) async { if (_note!.tags.contains(tag)) return; Navigator.pushNamed(context, SplashScreen.routeName); diff --git a/lib/screens/password_screen.dart b/lib/screens/password_screen.dart index 6b5930cb..719c03bd 100644 --- a/lib/screens/password_screen.dart +++ b/lib/screens/password_screen.dart @@ -14,6 +14,7 @@ import 'package:passy/passy_flutter/passy_theme.dart'; import 'package:passy/screens/common.dart'; import 'edit_password_screen.dart'; +import 'log_screen.dart'; import 'main_screen.dart'; import 'passwords_screen.dart'; import 'splash_screen.dart'; @@ -263,6 +264,43 @@ class _PasswordScreen extends State { showAddButton: true, selected: _selected, notSelected: _tags, + onSecondary: (tag) async { + String? newTag = await showDialog( + context: context, + builder: (ctx) => RenameTagDialog(tag: tag), + ); + if (newTag == null) return; + if (newTag == tag) return; + Navigator.pushNamed(context, SplashScreen.routeName); + try { + await _account.renameTag(tag: tag, newTag: newTag); + } catch (e, s) { + Navigator.pop(context); + showSnackBar( + message: localizations.somethingWentWrong, + icon: const Icon(Icons.error_outline_rounded, + color: PassyTheme.darkContentColor), + action: SnackBarAction( + label: localizations.details, + onPressed: () => Navigator.pushNamed( + context, LogScreen.routeName, + arguments: + e.toString() + '\n' + s.toString()), + ), + ); + return; + } + password!.tags = _selected.toList(); + if (password!.tags.contains(tag)) { + password!.tags.remove(tag); + password!.tags.add(newTag); + } + Navigator.popUntil(context, + (r) => r.settings.name == MainScreen.routeName); + Navigator.pushNamed(context, PasswordsScreen.routeName); + Navigator.pushNamed(context, PasswordScreen.routeName, + arguments: password!); + }, onAdded: (tag) async { if (password!.tags.contains(tag)) return; Navigator.pushNamed(context, SplashScreen.routeName); diff --git a/lib/screens/payment_card_screen.dart b/lib/screens/payment_card_screen.dart index 892d5c2e..b62feb69 100644 --- a/lib/screens/payment_card_screen.dart +++ b/lib/screens/payment_card_screen.dart @@ -9,6 +9,7 @@ import 'package:passy/passy_flutter/widgets/widgets.dart'; import 'package:passy/passy_flutter/passy_theme.dart'; import 'package:passy/screens/common.dart'; +import 'log_screen.dart'; import 'main_screen.dart'; import 'edit_payment_card_screen.dart'; import 'payment_cards_screen.dart'; @@ -150,6 +151,43 @@ class _PaymentCardScreen extends State { showAddButton: true, selected: _selected, notSelected: _tags, + onSecondary: (tag) async { + String? newTag = await showDialog( + context: context, + builder: (ctx) => RenameTagDialog(tag: tag), + ); + if (newTag == null) return; + if (newTag == tag) return; + Navigator.pushNamed(context, SplashScreen.routeName); + try { + await _account.renameTag(tag: tag, newTag: newTag); + } catch (e, s) { + Navigator.pop(context); + showSnackBar( + message: localizations.somethingWentWrong, + icon: const Icon(Icons.error_outline_rounded, + color: PassyTheme.darkContentColor), + action: SnackBarAction( + label: localizations.details, + onPressed: () => Navigator.pushNamed( + context, LogScreen.routeName, + arguments: e.toString() + '\n' + s.toString()), + ), + ); + return; + } + _paymentCard!.tags = _selected.toList(); + if (_paymentCard!.tags.contains(tag)) { + _paymentCard!.tags.remove(tag); + _paymentCard!.tags.add(newTag); + } + Navigator.popUntil(context, + (r) => r.settings.name == MainScreen.routeName); + Navigator.pushNamed( + context, PaymentCardsScreen.routeName); + Navigator.pushNamed(context, PaymentCardScreen.routeName, + arguments: _paymentCard!); + }, onAdded: (tag) async { if (_paymentCard!.tags.contains(tag)) return; Navigator.pushNamed(context, SplashScreen.routeName); From 1b7cc1f82bc1da438cdebd5fb1b5564fb4caf0d1 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Mon, 18 Mar 2024 01:36:18 +0000 Subject: [PATCH 102/131] Dependencies upgraded --- pubspec.lock | 275 ++++++++++++++++++++++++++------------------------- pubspec.yaml | 5 +- 2 files changed, 146 insertions(+), 134 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 26a2e38b..a7125858 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,26 +5,26 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051 + sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" url: "https://pub.dev" source: hosted - version: "64.0.0" + version: "67.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893" + sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" url: "https://pub.dev" source: hosted - version: "6.2.0" + version: "6.4.1" archive: dependency: transitive description: name: archive - sha256: e0902a06f0e00414e4e3438a084580161279f137aeb862274710f29ec10cf01e + sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d" url: "https://pub.dev" source: hosted - version: "3.3.9" + version: "3.4.10" args: dependency: transitive description: @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: asn1lib - sha256: "21afe4333076c02877d14f4a89df111e658a6d466cbfc802eb705eb91bd5adfd" + sha256: c9c85fedbe2188b95133cbe960e16f5f448860f7133330e272edbbca5893ddc6 url: "https://pub.dev" source: hosted - version: "1.5.0" + version: "1.5.2" async: dependency: transitive description: @@ -85,34 +85,34 @@ packages: dependency: transitive description: name: build_daemon - sha256: "5f02d73eb2ba16483e693f80bee4f088563a820e47d1027d4cdfe62b5bb43e65" + sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "4.0.1" build_resolvers: dependency: transitive description: name: build_resolvers - sha256: d912852cce27c9e80a93603db721c267716894462e7033165178b91138587972 + sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.4.2" build_runner: dependency: "direct dev" description: name: build_runner - sha256: "10c6bcdbf9d049a0b666702cf1cee4ddfdc38f02a19d35ae392863b47519848b" + sha256: "581bacf68f89ec8792f5e5a0b2c4decd1c948e97ce659dc783688c8a88fbec21" url: "https://pub.dev" source: hosted - version: "2.4.6" + version: "2.4.8" build_runner_core: dependency: transitive description: name: build_runner_core - sha256: "6d6ee4276b1c5f34f21fdf39425202712d2be82019983d52f351c94aafbc2c41" + sha256: "4ae8ffe5ac758da294ecf1802f2aff01558d8b1b00616aa7538ea9a8a5d50799" url: "https://pub.dev" source: hosted - version: "7.2.10" + version: "7.3.0" built_collection: dependency: transitive description: @@ -125,10 +125,10 @@ packages: dependency: transitive description: name: built_value - sha256: ff627b645b28fb8bdb69e645f910c2458fd6b65f6585c3a53e0626024897dedf + sha256: fedde275e0a6b798c3296963c5cd224e3e1b55d0e478d5b7e65e6b540f363a0e url: "https://pub.dev" source: hosted - version: "8.6.2" + version: "8.9.1" cached_network_image: dependency: "direct main" description: @@ -157,42 +157,42 @@ packages: dependency: "direct main" description: name: camera - sha256: f63f2687fb1795c36f7c57b18a03071880eabb0fd8b5291b0fcd3fb979cb0fb1 + sha256: "9499cbc2e51d8eb0beadc158b288380037618ce4e30c9acbc4fae1ac3ecb5797" url: "https://pub.dev" source: hosted - version: "0.10.5+4" + version: "0.10.5+9" camera_android: dependency: transitive description: name: camera_android - sha256: ed4f645848074166fc3b8e20350f83ca07e09a2becc1e185040ee561f955d4df + sha256: "351429510121d179b9aac5a2e8cb525c3cd6c39f4d709c5f72dfb21726e52371" url: "https://pub.dev" source: hosted - version: "0.10.8+8" + version: "0.10.8+16" camera_avfoundation: dependency: transitive description: name: camera_avfoundation - sha256: "718b60ed2e22b4067fe6e2c0e9ebe2856c2de5c8b1289ba95d10db85b0b00bc2" + sha256: "8b113e43ee4434c9244c03c905432a0d5956cedaded3cd7381abaab89ce50297" url: "https://pub.dev" source: hosted - version: "0.9.13+4" + version: "0.9.14+1" camera_platform_interface: dependency: transitive description: name: camera_platform_interface - sha256: "8734d1c682f034bdb12d0d6ff379b0535a9b8e44266b530025bf8266d6a62f28" + sha256: a250314a48ea337b35909a4c9d5416a208d736dcb01d0b02c6af122be66660b0 url: "https://pub.dev" source: hosted - version: "2.5.2" + version: "2.7.4" camera_web: dependency: transitive description: name: camera_web - sha256: d4c2c571c7af04f8b10702ca16bb9ed2a26e64534171e8f75c9349b2c004d8f1 + sha256: f18ccfb33b2a7c49a52ad5aa3f07330b7422faaecbdfd9b9fe8e51182f6ad67d url: "https://pub.dev" source: hosted - version: "0.3.2+3" + version: "0.3.2+4" change_app_package_name: dependency: "direct dev" description: @@ -237,10 +237,10 @@ packages: dependency: transitive description: name: code_builder - sha256: "315a598c7fbe77f22de1c9da7cfd6fd21816312f16ffa124453b4fc679e540f1" + sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 url: "https://pub.dev" source: hosted - version: "4.6.0" + version: "4.10.0" collection: dependency: transitive description: @@ -277,10 +277,10 @@ packages: dependency: transitive description: name: cross_file - sha256: fd832b5384d0d6da4f6df60b854d33accaaeb63aa9e10e736a87381f08dee2cb + sha256: "55d7b444feb71301ef6b8838dbc1ae02e63dd48c8773f3810ff53bb1e2945b32" url: "https://pub.dev" source: hosted - version: "0.3.3+5" + version: "0.3.4+1" crypto: dependency: "direct main" description: @@ -293,10 +293,10 @@ packages: dependency: "direct main" description: name: crypton - sha256: dc939afa51968664f92d09ac13039884875f38ca31285861a0b3aadc96e0aaa8 + sha256: "17b6631fbf89e389d421b46629132287ed37d601b2ad1357445826ab85022271" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.2.1" csslib: dependency: transitive description: @@ -309,10 +309,10 @@ packages: dependency: "direct main" description: name: csv - sha256: "016b31a51a913744a0a1655c74ff13c9379e1200e246a03d96c81c5d9ed297b5" + sha256: "63ed2871dd6471193dffc52c0e6c76fb86269c00244d244297abbb355c84a86e" url: "https://pub.dev" source: hosted - version: "5.0.2" + version: "5.1.1" cupertino_icons: dependency: "direct main" description: @@ -390,18 +390,18 @@ packages: dependency: transitive description: name: dart_style - sha256: "1efa911ca7086affd35f463ca2fc1799584fb6aa89883cf0af8e3664d6a02d55" + sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.3.6" dio: dependency: transitive description: name: dio - sha256: ce75a1b40947fea0a0e16ce73337122a86762e38b982e1ccb909daa3b9bc4197 + sha256: "49af28382aefc53562459104f64d16b9dfd1e8ef68c862d5af436cc8356ce5a8" url: "https://pub.dev" source: hosted - version: "5.3.2" + version: "5.4.1" dropdown_button2: dependency: "direct main" description: @@ -422,10 +422,10 @@ packages: dependency: "direct main" description: name: encrypt - sha256: "4fd4e4fdc21b9d7d4141823e1e6515cd94e7b8d84749504c232999fba25d9bbb" + sha256: "62d9aa4670cc2a8798bab89b39fc71b6dfbacf615de6cf5001fb39f7e4a996a2" url: "https://pub.dev" source: hosted - version: "5.0.1" + version: "5.0.3" event: dependency: "direct main" description: @@ -454,10 +454,10 @@ packages: dependency: transitive description: name: ffi - sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" + sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.2" file: dependency: transitive description: @@ -490,11 +490,12 @@ packages: flutter_autofill_service: dependency: "direct main" description: - name: flutter_autofill_service - sha256: d4385563c46310a0a47191ab5c4b7414a48517a40ee2c74512c3ab119f28466b - url: "https://pub.dev" - source: hosted - version: "0.14.0" + path: "." + ref: "8d742765ddda32c113d630c35577c02feb3df102" + resolved-ref: "8d742765ddda32c113d630c35577c02feb3df102" + url: "https://github.com/kee-org/flutter_autofill_service.git" + source: git + version: "0.18.1" flutter_cache_manager: dependency: transitive description: @@ -544,26 +545,26 @@ packages: dependency: "direct main" description: name: flutter_locker - sha256: "4d7d209952a150f199b6c6b37cb6e9807df98a850c81e02b9c2a888aaa0a9ee1" + sha256: faadef863de4b3b8a48970b7f625dba6f9e223f45d9ddfb63b1d3494cad71f32 url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" flutter_markdown: dependency: "direct main" description: name: flutter_markdown - sha256: a10979814c5f4ddbe2b6143fba25d927599e21e3ba65b3862995960606fae78f + sha256: cb44f7831b23a6bdd0f501718b0d2e8045cbc625a15f668af37ddb80314821db url: "https://pub.dev" source: hosted - version: "0.6.17+3" + version: "0.6.21" flutter_plugin_android_lifecycle: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: f185ac890306b5779ecbd611f52502d8d4d63d27703ef73161ca0407e815f02c + sha256: b068ffc46f82a55844acfa4fdbb61fad72fa2aef0905548419d97f0f95c456da url: "https://pub.dev" source: hosted - version: "2.0.16" + version: "2.0.17" flutter_secure_screen: dependency: "direct main" description: @@ -576,10 +577,10 @@ packages: dependency: transitive description: name: flutter_svg - sha256: "8c5d68a82add3ca76d792f058b186a0599414f279f00ece4830b9b231b570338" + sha256: "7b4ca6cf3304575fe9c8ec64813c8d02ee41d2afe60bcfe0678bcb5375d596a2" url: "https://pub.dev" source: hosted - version: "2.0.7" + version: "2.0.10+1" flutter_test: dependency: "direct dev" description: flutter @@ -642,10 +643,10 @@ packages: dependency: transitive description: name: http - sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2" + sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" url: "https://pub.dev" source: hosted - version: "0.13.6" + version: "1.2.1" http_multi_server: dependency: transitive description: @@ -779,10 +780,10 @@ packages: dependency: transitive description: name: logger - sha256: "7ad7215c15420a102ec687bb320a7312afd449bac63bfb1c60d9787c27b9767f" + sha256: b3ff55aeb08d9d8901b767650285872cb1bb8f508373b3e348d60268b0c7f770 url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "2.1.0" logging: dependency: transitive description: @@ -803,10 +804,10 @@ packages: dependency: transitive description: name: markdown - sha256: acf35edccc0463a9d7384e437c015a3535772e09714cf60e07eeef3a15870dcd + sha256: ef2a1298144e3f985cc736b22e0ccdaf188b5b3970648f2d9dc13efd1d9df051 url: "https://pub.dev" source: hosted - version: "7.1.1" + version: "7.2.2" matcher: dependency: transitive description: @@ -835,10 +836,10 @@ packages: dependency: transitive description: name: mime - sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.0.5" octo_image: dependency: transitive description: @@ -883,26 +884,26 @@ packages: dependency: "direct main" description: name: path_provider - sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa + sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: "6b8b19bd80da4f11ce91b2d1fb931f3006911477cec227cce23d3253d80df3f1" + sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.2.2" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" + sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.2" path_provider_linux: dependency: transitive description: @@ -915,10 +916,10 @@ packages: dependency: transitive description: name: path_provider_platform_interface - sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c" + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" path_provider_windows: dependency: transitive description: @@ -931,34 +932,34 @@ packages: dependency: transitive description: name: petitparser - sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 + sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 url: "https://pub.dev" source: hosted - version: "5.4.0" + version: "6.0.2" platform: dependency: transitive description: name: platform - sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102 + sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" url: "https://pub.dev" source: hosted - version: "3.1.2" + version: "3.1.4" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" url: "https://pub.dev" source: hosted - version: "2.1.6" + version: "2.1.8" pointycastle: dependency: transitive description: name: pointycastle - sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c" + sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29" url: "https://pub.dev" source: hosted - version: "3.7.3" + version: "3.7.4" pool: dependency: transitive description: @@ -1019,10 +1020,10 @@ packages: dependency: "direct dev" description: name: rename - sha256: b0d9407186d834ad73aba9938da95a20208c4be99f151d9f376ac62a08d08bad + sha256: "6ef5daf4b11130e71d93630cfb70725e5a35b19039739cfcd2b272c834ba25fe" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "3.0.2" rxdart: dependency: transitive description: @@ -1067,18 +1068,18 @@ packages: dependency: transitive description: name: shared_preferences_platform_interface - sha256: d4ec5fc9ebb2f2e056c617112aa75dcf92fc2e4faaf2ae999caa297473f75d8a + sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.2" shared_preferences_web: dependency: transitive description: name: shared_preferences_web - sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf + sha256: "9aee1089b36bd2aafe06582b7d7817fd317ef05fc30e6ba14bff247d0933042a" url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.3.0" shared_preferences_windows: dependency: transitive description: @@ -1112,10 +1113,10 @@ packages: dependency: transitive description: name: source_gen - sha256: fc0da689e5302edb6177fdd964efcb7f58912f43c28c2047a808f5bfff643d16 + sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.5.0" source_helper: dependency: transitive description: @@ -1144,18 +1145,18 @@ packages: dependency: transitive description: name: sqflite - sha256: "591f1602816e9c31377d5f008c2d9ef7b8aca8941c3f89cc5fd9d84da0c38a9a" + sha256: a9016f495c927cb90557c909ff26a6d92d9bd54fc42ba92e19d4e79d61e798c6 url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.3.2" sqflite_common: dependency: transitive description: name: sqflite_common - sha256: "1b92f368f44b0dee2425bb861cfa17b6f6cf3961f762ff6f941d20b33355660a" + sha256: "3da423ce7baf868be70e2c0976c28a1bb2f73644268b7ffa7d2e08eab71f16a4" url: "https://pub.dev" source: hosted - version: "2.5.0" + version: "2.5.4" stack_trace: dependency: transitive description: @@ -1200,10 +1201,10 @@ packages: dependency: transitive description: name: synchronized - sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60" + sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.0+1" system_info2: dependency: "direct main" description: @@ -1272,98 +1273,98 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: "47e208a6711459d813ba18af120d9663c20bdf6985d6ad39fe165d2538378d27" + sha256: "0ecc004c62fd3ed36a2ffcbe0dd9700aee63bd7532d0b642a488b1ec310f492e" url: "https://pub.dev" source: hosted - version: "6.1.14" + version: "6.2.5" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: b04af59516ab45762b2ca6da40fa830d72d0f6045cd97744450b73493fa76330 + sha256: d4ed0711849dd8e33eb2dd69c25db0d0d3fdc37e0a62e629fe32f57a22db2745 url: "https://pub.dev" source: hosted - version: "6.1.0" + version: "6.3.0" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: "7c65021d5dee51813d652357bc65b8dd4a6177082a9966bc8ba6ee477baa795f" + sha256: "9149d493b075ed740901f3ee844a38a00b33116c7c5c10d7fb27df8987fb51d5" url: "https://pub.dev" source: hosted - version: "6.1.5" + version: "6.2.5" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - sha256: b651aad005e0cb06a01dbd84b428a301916dc75f0e7ea6165f80057fee2d8e8e + sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811 url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "3.1.1" url_launcher_macos: dependency: transitive description: name: url_launcher_macos - sha256: b55486791f666e62e0e8ff825e58a023fd6b1f71c49926483f1128d3bbd8fe88 + sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234 url: "https://pub.dev" source: hosted - version: "3.0.7" + version: "3.1.0" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface - sha256: "95465b39f83bfe95fcb9d174829d6476216f2d548b79c38ab2506e0458787618" + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" url: "https://pub.dev" source: hosted - version: "2.1.5" + version: "2.3.2" url_launcher_web: dependency: transitive description: name: url_launcher_web - sha256: "2942294a500b4fa0b918685aff406773ba0a4cd34b7f42198742a94083020ce5" + sha256: "3692a459204a33e04bc94f5fb91158faf4f2c8903281ddd82915adecdb1a901d" url: "https://pub.dev" source: hosted - version: "2.0.20" + version: "2.3.0" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: "95fef3129dc7cfaba2bc3d5ba2e16063bb561fc6d78e63eee16162bc70029069" + sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7 url: "https://pub.dev" source: hosted - version: "3.0.8" + version: "3.1.1" uuid: dependency: transitive description: name: uuid - sha256: e03928880bdbcbf496fb415573f5ab7b1ea99b9b04f669c01104d085893c3134 + sha256: cd210a09f7c18cbe5a02511718e0334de6559871052c90a90c0cca46a4aa81c8 url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "4.3.3" vector_graphics: dependency: transitive description: name: vector_graphics - sha256: "670f6e07aca990b4a2bcdc08a784193c4ccdd1932620244c3a86bb72a0eac67f" + sha256: "32c3c684e02f9bc0afb0ae0aa653337a2fe022e8ab064bcd7ffda27a74e288e3" url: "https://pub.dev" source: hosted - version: "1.1.7" + version: "1.1.11+1" vector_graphics_codec: dependency: transitive description: name: vector_graphics_codec - sha256: "7451721781d967db9933b63f5733b1c4533022c0ba373a01bdd79d1a5457f69f" + sha256: c86987475f162fadff579e7320c7ddda04cd2fdeffbe1129227a85d9ac9e03da url: "https://pub.dev" source: hosted - version: "1.1.7" + version: "1.1.11+1" vector_graphics_compiler: dependency: transitive description: name: vector_graphics_compiler - sha256: "80a13c613c8bde758b1464a1755a7b3a8f2b6cec61fbf0f5a53c94c30f03ba2e" + sha256: "12faff3f73b1741a36ca7e31b292ddeb629af819ca9efe9953b70bd63fc8cd81" url: "https://pub.dev" source: hosted - version: "1.1.7" + version: "1.1.11+1" vector_math: dependency: transitive description: @@ -1388,54 +1389,62 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + web: + dependency: transitive + description: + name: web + sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + url: "https://pub.dev" + source: hosted + version: "0.5.1" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b + sha256: "1d8e795e2a8b3730c41b8a98a2dff2e0fb57ae6f0764a1c46ec5915387d257b2" url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.4.4" websafe_svg: dependency: "direct main" description: name: websafe_svg - sha256: "306549379ad9c6252c19441070466f5ce813feb267f24c6c1ee4a2918c305faa" + sha256: e8918c90845b48415aca59cd02b3b5540ad0766fa2957d4362210aba5580737a url: "https://pub.dev" source: hosted - version: "3.0.0+5" + version: "3.0.1+1" win32: dependency: transitive description: name: win32 - sha256: "9e82a402b7f3d518fb9c02d0e9ae45952df31b9bf34d77baf19da2de03fc2aaa" + sha256: "8cb58b45c47dcb42ab3651533626161d6b67a2921917d8d429791f76972b3480" url: "https://pub.dev" source: hosted - version: "5.0.7" + version: "5.3.0" win32_registry: dependency: transitive description: name: win32_registry - sha256: e4506d60b7244251bc59df15656a3093501c37fb5af02105a944d73eb95be4c9 + sha256: "41fd8a189940d8696b1b810efb9abcf60827b6cbfab90b0c43e8439e3a39d85a" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" xdg_directories: dependency: transitive description: name: xdg_directories - sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2" + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d url: "https://pub.dev" source: hosted - version: "1.0.3" + version: "1.0.4" xml: dependency: transitive description: name: xml - sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" + sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 url: "https://pub.dev" source: hosted - version: "6.3.0" + version: "6.5.0" yaml: dependency: transitive description: @@ -1453,5 +1462,5 @@ packages: source: hosted version: "0.1.1" sdks: - dart: ">=3.2.0-0 <4.0.0" - flutter: ">=3.16.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.19.0" diff --git a/pubspec.yaml b/pubspec.yaml index 41b11af5..0b73dcb9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -50,7 +50,10 @@ dependencies: credit_card_type_detector: ^2.0.0 flutter_secure_screen: ^0.0.1 camera: ^0.10.5+4 - flutter_autofill_service: ^0.14.0 + flutter_autofill_service: + git: + url: https://github.com/kee-org/flutter_autofill_service.git + ref: 8d742765ddda32c113d630c35577c02feb3df102 url_launcher: ^6.0.20 intranet_ip: ^1.0.2 zxing2: ^0.1.1 From 6bd7707b1faba45b98eeef7ef541f5b230a4b3b8 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Mon, 18 Mar 2024 01:36:51 +0000 Subject: [PATCH 103/131] Android build pipeline upgraded --- android/app/build.gradle | 35 ++++++++++++++++++----------------- android/build.gradle | 14 +------------- android/settings.gradle | 30 ++++++++++++++++++++++-------- 3 files changed, 41 insertions(+), 38 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 6724dc38..04335d63 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -1,3 +1,17 @@ +buildscript { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id "com.android.application" + id "kotlin-android" + id "dev.flutter.flutter-gradle-plugin" +} + def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { @@ -6,11 +20,6 @@ if (localPropertiesFile.exists()) { } } -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) { flutterVersionCode = '1' @@ -21,12 +30,10 @@ if (flutterVersionName == null) { flutterVersionName = '1.0' } -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - android { - compileSdkVersion 33 + namespace "com.glitterware.passy" + + compileSdkVersion 34 compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 @@ -46,7 +53,7 @@ android { applicationId "com.glitterware.passy" // mobile_scanner requires minSdkVersion 21 // flutter_autofill_service requires minSdkVersion 26 - minSdkVersion 26 + minSdkVersion 29 targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName @@ -72,9 +79,3 @@ dependencies { implementation 'co.infinum:goldfinger:2.0.1' implementation "androidx.fragment:fragment:1.3.4" } - -buildscript { - repositories { - mavenCentral() - } -} \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle index 5c8d9b8f..0f49d7d1 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,20 +1,8 @@ -buildscript { - ext.kotlin_version = '1.7.10' - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:7.2.1' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - allprojects { repositories { google() mavenCentral() + gradlePluginPortal() } } diff --git a/android/settings.gradle b/android/settings.gradle index c893196c..6542ff7b 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1,14 +1,26 @@ -include ':app' +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} -def flutterSdkPath = properties.getProperty("flutter.sdk") -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "7.2.1" apply false + id "org.jetbrains.kotlin.android" version "1.8.22" apply false +} def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() @@ -23,3 +35,5 @@ plugins.each { name, path -> include ":$name" project(":$name").projectDir = pluginDirectory } + +include ":app" From 5cea2050cab30e48e6fefed840e28732b0fb6021 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Mon, 18 Mar 2024 01:42:40 +0000 Subject: [PATCH 104/131] Revert "Dependencies upgraded" This reverts commit 1b7cc1f82bc1da438cdebd5fb1b5564fb4caf0d1. --- pubspec.lock | 275 +++++++++++++++++++++++++-------------------------- pubspec.yaml | 5 +- 2 files changed, 134 insertions(+), 146 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index a7125858..26a2e38b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,26 +5,26 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" + sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051 url: "https://pub.dev" source: hosted - version: "67.0.0" + version: "64.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" + sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893" url: "https://pub.dev" source: hosted - version: "6.4.1" + version: "6.2.0" archive: dependency: transitive description: name: archive - sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d" + sha256: e0902a06f0e00414e4e3438a084580161279f137aeb862274710f29ec10cf01e url: "https://pub.dev" source: hosted - version: "3.4.10" + version: "3.3.9" args: dependency: transitive description: @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: asn1lib - sha256: c9c85fedbe2188b95133cbe960e16f5f448860f7133330e272edbbca5893ddc6 + sha256: "21afe4333076c02877d14f4a89df111e658a6d466cbfc802eb705eb91bd5adfd" url: "https://pub.dev" source: hosted - version: "1.5.2" + version: "1.5.0" async: dependency: transitive description: @@ -85,34 +85,34 @@ packages: dependency: transitive description: name: build_daemon - sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" + sha256: "5f02d73eb2ba16483e693f80bee4f088563a820e47d1027d4cdfe62b5bb43e65" url: "https://pub.dev" source: hosted - version: "4.0.1" + version: "4.0.0" build_resolvers: dependency: transitive description: name: build_resolvers - sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" + sha256: d912852cce27c9e80a93603db721c267716894462e7033165178b91138587972 url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.3.2" build_runner: dependency: "direct dev" description: name: build_runner - sha256: "581bacf68f89ec8792f5e5a0b2c4decd1c948e97ce659dc783688c8a88fbec21" + sha256: "10c6bcdbf9d049a0b666702cf1cee4ddfdc38f02a19d35ae392863b47519848b" url: "https://pub.dev" source: hosted - version: "2.4.8" + version: "2.4.6" build_runner_core: dependency: transitive description: name: build_runner_core - sha256: "4ae8ffe5ac758da294ecf1802f2aff01558d8b1b00616aa7538ea9a8a5d50799" + sha256: "6d6ee4276b1c5f34f21fdf39425202712d2be82019983d52f351c94aafbc2c41" url: "https://pub.dev" source: hosted - version: "7.3.0" + version: "7.2.10" built_collection: dependency: transitive description: @@ -125,10 +125,10 @@ packages: dependency: transitive description: name: built_value - sha256: fedde275e0a6b798c3296963c5cd224e3e1b55d0e478d5b7e65e6b540f363a0e + sha256: ff627b645b28fb8bdb69e645f910c2458fd6b65f6585c3a53e0626024897dedf url: "https://pub.dev" source: hosted - version: "8.9.1" + version: "8.6.2" cached_network_image: dependency: "direct main" description: @@ -157,42 +157,42 @@ packages: dependency: "direct main" description: name: camera - sha256: "9499cbc2e51d8eb0beadc158b288380037618ce4e30c9acbc4fae1ac3ecb5797" + sha256: f63f2687fb1795c36f7c57b18a03071880eabb0fd8b5291b0fcd3fb979cb0fb1 url: "https://pub.dev" source: hosted - version: "0.10.5+9" + version: "0.10.5+4" camera_android: dependency: transitive description: name: camera_android - sha256: "351429510121d179b9aac5a2e8cb525c3cd6c39f4d709c5f72dfb21726e52371" + sha256: ed4f645848074166fc3b8e20350f83ca07e09a2becc1e185040ee561f955d4df url: "https://pub.dev" source: hosted - version: "0.10.8+16" + version: "0.10.8+8" camera_avfoundation: dependency: transitive description: name: camera_avfoundation - sha256: "8b113e43ee4434c9244c03c905432a0d5956cedaded3cd7381abaab89ce50297" + sha256: "718b60ed2e22b4067fe6e2c0e9ebe2856c2de5c8b1289ba95d10db85b0b00bc2" url: "https://pub.dev" source: hosted - version: "0.9.14+1" + version: "0.9.13+4" camera_platform_interface: dependency: transitive description: name: camera_platform_interface - sha256: a250314a48ea337b35909a4c9d5416a208d736dcb01d0b02c6af122be66660b0 + sha256: "8734d1c682f034bdb12d0d6ff379b0535a9b8e44266b530025bf8266d6a62f28" url: "https://pub.dev" source: hosted - version: "2.7.4" + version: "2.5.2" camera_web: dependency: transitive description: name: camera_web - sha256: f18ccfb33b2a7c49a52ad5aa3f07330b7422faaecbdfd9b9fe8e51182f6ad67d + sha256: d4c2c571c7af04f8b10702ca16bb9ed2a26e64534171e8f75c9349b2c004d8f1 url: "https://pub.dev" source: hosted - version: "0.3.2+4" + version: "0.3.2+3" change_app_package_name: dependency: "direct dev" description: @@ -237,10 +237,10 @@ packages: dependency: transitive description: name: code_builder - sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 + sha256: "315a598c7fbe77f22de1c9da7cfd6fd21816312f16ffa124453b4fc679e540f1" url: "https://pub.dev" source: hosted - version: "4.10.0" + version: "4.6.0" collection: dependency: transitive description: @@ -277,10 +277,10 @@ packages: dependency: transitive description: name: cross_file - sha256: "55d7b444feb71301ef6b8838dbc1ae02e63dd48c8773f3810ff53bb1e2945b32" + sha256: fd832b5384d0d6da4f6df60b854d33accaaeb63aa9e10e736a87381f08dee2cb url: "https://pub.dev" source: hosted - version: "0.3.4+1" + version: "0.3.3+5" crypto: dependency: "direct main" description: @@ -293,10 +293,10 @@ packages: dependency: "direct main" description: name: crypton - sha256: "17b6631fbf89e389d421b46629132287ed37d601b2ad1357445826ab85022271" + sha256: dc939afa51968664f92d09ac13039884875f38ca31285861a0b3aadc96e0aaa8 url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.2.0" csslib: dependency: transitive description: @@ -309,10 +309,10 @@ packages: dependency: "direct main" description: name: csv - sha256: "63ed2871dd6471193dffc52c0e6c76fb86269c00244d244297abbb355c84a86e" + sha256: "016b31a51a913744a0a1655c74ff13c9379e1200e246a03d96c81c5d9ed297b5" url: "https://pub.dev" source: hosted - version: "5.1.1" + version: "5.0.2" cupertino_icons: dependency: "direct main" description: @@ -390,18 +390,18 @@ packages: dependency: transitive description: name: dart_style - sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9" + sha256: "1efa911ca7086affd35f463ca2fc1799584fb6aa89883cf0af8e3664d6a02d55" url: "https://pub.dev" source: hosted - version: "2.3.6" + version: "2.3.2" dio: dependency: transitive description: name: dio - sha256: "49af28382aefc53562459104f64d16b9dfd1e8ef68c862d5af436cc8356ce5a8" + sha256: ce75a1b40947fea0a0e16ce73337122a86762e38b982e1ccb909daa3b9bc4197 url: "https://pub.dev" source: hosted - version: "5.4.1" + version: "5.3.2" dropdown_button2: dependency: "direct main" description: @@ -422,10 +422,10 @@ packages: dependency: "direct main" description: name: encrypt - sha256: "62d9aa4670cc2a8798bab89b39fc71b6dfbacf615de6cf5001fb39f7e4a996a2" + sha256: "4fd4e4fdc21b9d7d4141823e1e6515cd94e7b8d84749504c232999fba25d9bbb" url: "https://pub.dev" source: hosted - version: "5.0.3" + version: "5.0.1" event: dependency: "direct main" description: @@ -454,10 +454,10 @@ packages: dependency: transitive description: name: ffi - sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" + sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.0" file: dependency: transitive description: @@ -490,12 +490,11 @@ packages: flutter_autofill_service: dependency: "direct main" description: - path: "." - ref: "8d742765ddda32c113d630c35577c02feb3df102" - resolved-ref: "8d742765ddda32c113d630c35577c02feb3df102" - url: "https://github.com/kee-org/flutter_autofill_service.git" - source: git - version: "0.18.1" + name: flutter_autofill_service + sha256: d4385563c46310a0a47191ab5c4b7414a48517a40ee2c74512c3ab119f28466b + url: "https://pub.dev" + source: hosted + version: "0.14.0" flutter_cache_manager: dependency: transitive description: @@ -545,26 +544,26 @@ packages: dependency: "direct main" description: name: flutter_locker - sha256: faadef863de4b3b8a48970b7f625dba6f9e223f45d9ddfb63b1d3494cad71f32 + sha256: "4d7d209952a150f199b6c6b37cb6e9807df98a850c81e02b9c2a888aaa0a9ee1" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.2" flutter_markdown: dependency: "direct main" description: name: flutter_markdown - sha256: cb44f7831b23a6bdd0f501718b0d2e8045cbc625a15f668af37ddb80314821db + sha256: a10979814c5f4ddbe2b6143fba25d927599e21e3ba65b3862995960606fae78f url: "https://pub.dev" source: hosted - version: "0.6.21" + version: "0.6.17+3" flutter_plugin_android_lifecycle: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: b068ffc46f82a55844acfa4fdbb61fad72fa2aef0905548419d97f0f95c456da + sha256: f185ac890306b5779ecbd611f52502d8d4d63d27703ef73161ca0407e815f02c url: "https://pub.dev" source: hosted - version: "2.0.17" + version: "2.0.16" flutter_secure_screen: dependency: "direct main" description: @@ -577,10 +576,10 @@ packages: dependency: transitive description: name: flutter_svg - sha256: "7b4ca6cf3304575fe9c8ec64813c8d02ee41d2afe60bcfe0678bcb5375d596a2" + sha256: "8c5d68a82add3ca76d792f058b186a0599414f279f00ece4830b9b231b570338" url: "https://pub.dev" source: hosted - version: "2.0.10+1" + version: "2.0.7" flutter_test: dependency: "direct dev" description: flutter @@ -643,10 +642,10 @@ packages: dependency: transitive description: name: http - sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" + sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "0.13.6" http_multi_server: dependency: transitive description: @@ -780,10 +779,10 @@ packages: dependency: transitive description: name: logger - sha256: b3ff55aeb08d9d8901b767650285872cb1bb8f508373b3e348d60268b0c7f770 + sha256: "7ad7215c15420a102ec687bb320a7312afd449bac63bfb1c60d9787c27b9767f" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "1.4.0" logging: dependency: transitive description: @@ -804,10 +803,10 @@ packages: dependency: transitive description: name: markdown - sha256: ef2a1298144e3f985cc736b22e0ccdaf188b5b3970648f2d9dc13efd1d9df051 + sha256: acf35edccc0463a9d7384e437c015a3535772e09714cf60e07eeef3a15870dcd url: "https://pub.dev" source: hosted - version: "7.2.2" + version: "7.1.1" matcher: dependency: transitive description: @@ -836,10 +835,10 @@ packages: dependency: transitive description: name: mime - sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" + sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e url: "https://pub.dev" source: hosted - version: "1.0.5" + version: "1.0.4" octo_image: dependency: transitive description: @@ -884,26 +883,26 @@ packages: dependency: "direct main" description: name: path_provider - sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b + sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668" + sha256: "6b8b19bd80da4f11ce91b2d1fb931f3006911477cec227cce23d3253d80df3f1" url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.2.0" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f" + sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.3.1" path_provider_linux: dependency: transitive description: @@ -916,10 +915,10 @@ packages: dependency: transitive description: name: path_provider_platform_interface - sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" path_provider_windows: dependency: transitive description: @@ -932,34 +931,34 @@ packages: dependency: transitive description: name: petitparser - sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 + sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 url: "https://pub.dev" source: hosted - version: "6.0.2" + version: "5.4.0" platform: dependency: transitive description: name: platform - sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" + sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102 url: "https://pub.dev" source: hosted - version: "3.1.4" + version: "3.1.2" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d url: "https://pub.dev" source: hosted - version: "2.1.8" + version: "2.1.6" pointycastle: dependency: transitive description: name: pointycastle - sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29" + sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c" url: "https://pub.dev" source: hosted - version: "3.7.4" + version: "3.7.3" pool: dependency: transitive description: @@ -1020,10 +1019,10 @@ packages: dependency: "direct dev" description: name: rename - sha256: "6ef5daf4b11130e71d93630cfb70725e5a35b19039739cfcd2b272c834ba25fe" + sha256: b0d9407186d834ad73aba9938da95a20208c4be99f151d9f376ac62a08d08bad url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "2.1.1" rxdart: dependency: transitive description: @@ -1068,18 +1067,18 @@ packages: dependency: transitive description: name: shared_preferences_platform_interface - sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b" + sha256: d4ec5fc9ebb2f2e056c617112aa75dcf92fc2e4faaf2ae999caa297473f75d8a url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.3.1" shared_preferences_web: dependency: transitive description: name: shared_preferences_web - sha256: "9aee1089b36bd2aafe06582b7d7817fd317ef05fc30e6ba14bff247d0933042a" + sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.2.1" shared_preferences_windows: dependency: transitive description: @@ -1113,10 +1112,10 @@ packages: dependency: transitive description: name: source_gen - sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" + sha256: fc0da689e5302edb6177fdd964efcb7f58912f43c28c2047a808f5bfff643d16 url: "https://pub.dev" source: hosted - version: "1.5.0" + version: "1.4.0" source_helper: dependency: transitive description: @@ -1145,18 +1144,18 @@ packages: dependency: transitive description: name: sqflite - sha256: a9016f495c927cb90557c909ff26a6d92d9bd54fc42ba92e19d4e79d61e798c6 + sha256: "591f1602816e9c31377d5f008c2d9ef7b8aca8941c3f89cc5fd9d84da0c38a9a" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.3.0" sqflite_common: dependency: transitive description: name: sqflite_common - sha256: "3da423ce7baf868be70e2c0976c28a1bb2f73644268b7ffa7d2e08eab71f16a4" + sha256: "1b92f368f44b0dee2425bb861cfa17b6f6cf3961f762ff6f941d20b33355660a" url: "https://pub.dev" source: hosted - version: "2.5.4" + version: "2.5.0" stack_trace: dependency: transitive description: @@ -1201,10 +1200,10 @@ packages: dependency: transitive description: name: synchronized - sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558" + sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60" url: "https://pub.dev" source: hosted - version: "3.1.0+1" + version: "3.1.0" system_info2: dependency: "direct main" description: @@ -1273,98 +1272,98 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: "0ecc004c62fd3ed36a2ffcbe0dd9700aee63bd7532d0b642a488b1ec310f492e" + sha256: "47e208a6711459d813ba18af120d9663c20bdf6985d6ad39fe165d2538378d27" url: "https://pub.dev" source: hosted - version: "6.2.5" + version: "6.1.14" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: d4ed0711849dd8e33eb2dd69c25db0d0d3fdc37e0a62e629fe32f57a22db2745 + sha256: b04af59516ab45762b2ca6da40fa830d72d0f6045cd97744450b73493fa76330 url: "https://pub.dev" source: hosted - version: "6.3.0" + version: "6.1.0" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: "9149d493b075ed740901f3ee844a38a00b33116c7c5c10d7fb27df8987fb51d5" + sha256: "7c65021d5dee51813d652357bc65b8dd4a6177082a9966bc8ba6ee477baa795f" url: "https://pub.dev" source: hosted - version: "6.2.5" + version: "6.1.5" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811 + sha256: b651aad005e0cb06a01dbd84b428a301916dc75f0e7ea6165f80057fee2d8e8e url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "3.0.6" url_launcher_macos: dependency: transitive description: name: url_launcher_macos - sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234 + sha256: b55486791f666e62e0e8ff825e58a023fd6b1f71c49926483f1128d3bbd8fe88 url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.0.7" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface - sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" + sha256: "95465b39f83bfe95fcb9d174829d6476216f2d548b79c38ab2506e0458787618" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.1.5" url_launcher_web: dependency: transitive description: name: url_launcher_web - sha256: "3692a459204a33e04bc94f5fb91158faf4f2c8903281ddd82915adecdb1a901d" + sha256: "2942294a500b4fa0b918685aff406773ba0a4cd34b7f42198742a94083020ce5" url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.0.20" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7 + sha256: "95fef3129dc7cfaba2bc3d5ba2e16063bb561fc6d78e63eee16162bc70029069" url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "3.0.8" uuid: dependency: transitive description: name: uuid - sha256: cd210a09f7c18cbe5a02511718e0334de6559871052c90a90c0cca46a4aa81c8 + sha256: e03928880bdbcbf496fb415573f5ab7b1ea99b9b04f669c01104d085893c3134 url: "https://pub.dev" source: hosted - version: "4.3.3" + version: "4.0.0" vector_graphics: dependency: transitive description: name: vector_graphics - sha256: "32c3c684e02f9bc0afb0ae0aa653337a2fe022e8ab064bcd7ffda27a74e288e3" + sha256: "670f6e07aca990b4a2bcdc08a784193c4ccdd1932620244c3a86bb72a0eac67f" url: "https://pub.dev" source: hosted - version: "1.1.11+1" + version: "1.1.7" vector_graphics_codec: dependency: transitive description: name: vector_graphics_codec - sha256: c86987475f162fadff579e7320c7ddda04cd2fdeffbe1129227a85d9ac9e03da + sha256: "7451721781d967db9933b63f5733b1c4533022c0ba373a01bdd79d1a5457f69f" url: "https://pub.dev" source: hosted - version: "1.1.11+1" + version: "1.1.7" vector_graphics_compiler: dependency: transitive description: name: vector_graphics_compiler - sha256: "12faff3f73b1741a36ca7e31b292ddeb629af819ca9efe9953b70bd63fc8cd81" + sha256: "80a13c613c8bde758b1464a1755a7b3a8f2b6cec61fbf0f5a53c94c30f03ba2e" url: "https://pub.dev" source: hosted - version: "1.1.11+1" + version: "1.1.7" vector_math: dependency: transitive description: @@ -1389,62 +1388,54 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" - web: - dependency: transitive - description: - name: web - sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" - url: "https://pub.dev" - source: hosted - version: "0.5.1" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: "1d8e795e2a8b3730c41b8a98a2dff2e0fb57ae6f0764a1c46ec5915387d257b2" + sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b url: "https://pub.dev" source: hosted - version: "2.4.4" + version: "2.4.0" websafe_svg: dependency: "direct main" description: name: websafe_svg - sha256: e8918c90845b48415aca59cd02b3b5540ad0766fa2957d4362210aba5580737a + sha256: "306549379ad9c6252c19441070466f5ce813feb267f24c6c1ee4a2918c305faa" url: "https://pub.dev" source: hosted - version: "3.0.1+1" + version: "3.0.0+5" win32: dependency: transitive description: name: win32 - sha256: "8cb58b45c47dcb42ab3651533626161d6b67a2921917d8d429791f76972b3480" + sha256: "9e82a402b7f3d518fb9c02d0e9ae45952df31b9bf34d77baf19da2de03fc2aaa" url: "https://pub.dev" source: hosted - version: "5.3.0" + version: "5.0.7" win32_registry: dependency: transitive description: name: win32_registry - sha256: "41fd8a189940d8696b1b810efb9abcf60827b6cbfab90b0c43e8439e3a39d85a" + sha256: e4506d60b7244251bc59df15656a3093501c37fb5af02105a944d73eb95be4c9 url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.1" xdg_directories: dependency: transitive description: name: xdg_directories - sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d + sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.0.3" xml: dependency: transitive description: name: xml - sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 + sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" url: "https://pub.dev" source: hosted - version: "6.5.0" + version: "6.3.0" yaml: dependency: transitive description: @@ -1462,5 +1453,5 @@ packages: source: hosted version: "0.1.1" sdks: - dart: ">=3.3.0 <4.0.0" - flutter: ">=3.19.0" + dart: ">=3.2.0-0 <4.0.0" + flutter: ">=3.16.0" diff --git a/pubspec.yaml b/pubspec.yaml index 0b73dcb9..41b11af5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -50,10 +50,7 @@ dependencies: credit_card_type_detector: ^2.0.0 flutter_secure_screen: ^0.0.1 camera: ^0.10.5+4 - flutter_autofill_service: - git: - url: https://github.com/kee-org/flutter_autofill_service.git - ref: 8d742765ddda32c113d630c35577c02feb3df102 + flutter_autofill_service: ^0.14.0 url_launcher: ^6.0.20 intranet_ip: ^1.0.2 zxing2: ^0.1.1 From d24595cf0df8c629bf47ecb3f6c0460ef3096d51 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Mon, 18 Mar 2024 02:38:52 +0000 Subject: [PATCH 105/131] `camera` and `flutter_autofill_service` upgraded --- pubspec.lock | 19 ++++++++++--------- pubspec.yaml | 7 +++++-- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 26a2e38b..fe98dfe3 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -157,18 +157,18 @@ packages: dependency: "direct main" description: name: camera - sha256: f63f2687fb1795c36f7c57b18a03071880eabb0fd8b5291b0fcd3fb979cb0fb1 + sha256: "9499cbc2e51d8eb0beadc158b288380037618ce4e30c9acbc4fae1ac3ecb5797" url: "https://pub.dev" source: hosted - version: "0.10.5+4" + version: "0.10.5+9" camera_android: dependency: transitive description: name: camera_android - sha256: ed4f645848074166fc3b8e20350f83ca07e09a2becc1e185040ee561f955d4df + sha256: "351429510121d179b9aac5a2e8cb525c3cd6c39f4d709c5f72dfb21726e52371" url: "https://pub.dev" source: hosted - version: "0.10.8+8" + version: "0.10.8+16" camera_avfoundation: dependency: transitive description: @@ -490,11 +490,12 @@ packages: flutter_autofill_service: dependency: "direct main" description: - name: flutter_autofill_service - sha256: d4385563c46310a0a47191ab5c4b7414a48517a40ee2c74512c3ab119f28466b - url: "https://pub.dev" - source: hosted - version: "0.14.0" + path: "." + ref: "8d742765ddda32c113d630c35577c02feb3df102" + resolved-ref: "8d742765ddda32c113d630c35577c02feb3df102" + url: "https://github.com/kee-org/flutter_autofill_service.git" + source: git + version: "0.18.1" flutter_cache_manager: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 41b11af5..429ab648 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -49,8 +49,11 @@ dependencies: flutter_date_pickers: ^0.4.0 credit_card_type_detector: ^2.0.0 flutter_secure_screen: ^0.0.1 - camera: ^0.10.5+4 - flutter_autofill_service: ^0.14.0 + camera: ^0.10.5+9 + flutter_autofill_service: + git: + url: https://github.com/kee-org/flutter_autofill_service.git + ref: 8d742765ddda32c113d630c35577c02feb3df102 url_launcher: ^6.0.20 intranet_ip: ^1.0.2 zxing2: ^0.1.1 From addf7a076ce85e3559a353da9efa45083c8b9420 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Mon, 18 Mar 2024 12:53:36 +0000 Subject: [PATCH 106/131] Android build pipeline fixed --- android/app/build.gradle | 7 ++++++- android/app/proguard-rules.pro | 8 ++++++++ android/build.gradle | 9 +++++++++ android/gradle.properties | 2 +- android/gradle/wrapper/gradle-wrapper.properties | 2 +- android/settings.gradle | 2 +- pubspec.lock | 9 +++++---- pubspec.yaml | 5 ++++- 8 files changed, 35 insertions(+), 9 deletions(-) create mode 100644 android/app/proguard-rules.pro diff --git a/android/app/build.gradle b/android/app/build.gradle index 04335d63..68209290 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -65,8 +65,14 @@ android { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. signingConfig signingConfigs.debug + proguardFiles 'proguard-rules.pro' } } + + lintOptions { + checkReleaseBuilds false + abortOnError false + } } flutter { @@ -74,7 +80,6 @@ flutter { } dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.0" implementation 'com.android.support:multidex:1.0.3' implementation 'co.infinum:goldfinger:2.0.1' implementation "androidx.fragment:fragment:1.3.4" diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro new file mode 100644 index 00000000..6f01dfa5 --- /dev/null +++ b/android/app/proguard-rules.pro @@ -0,0 +1,8 @@ +-dontwarn dalvik.system.VMStack +-dontwarn java.lang.ProcessHandle +-dontwarn java.lang.management.ManagementFactory +-dontwarn java.lang.management.RuntimeMXBean +-dontwarn javax.naming.InitialContext +-dontwarn javax.naming.NameNotFoundException +-dontwarn javax.naming.NamingException +-dontwarn sun.reflect.Reflection diff --git a/android/build.gradle b/android/build.gradle index 0f49d7d1..ce8cb39b 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -9,6 +9,15 @@ allprojects { rootProject.buildDir = '../build' subprojects { project.buildDir = "${rootProject.buildDir}/${project.name}" + afterEvaluate { project -> + if (project.hasProperty('android')) { + project.android { + if (namespace == null) { + namespace project.group + } + } + } + } } subprojects { project.evaluationDependsOn(':app') diff --git a/android/gradle.properties b/android/gradle.properties index 94adc3a3..f622fd8f 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,3 +1,3 @@ -org.gradle.jvmargs=-Xmx1536M +org.gradle.jvmargs=-Xmx2048M android.useAndroidX=true android.enableJetifier=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 562c5e44..89e56bdb 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.zip diff --git a/android/settings.gradle b/android/settings.gradle index 6542ff7b..f6499b1a 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -18,7 +18,7 @@ pluginManagement { plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version "7.2.1" apply false + id "com.android.application" version "8.1.3" apply false id "org.jetbrains.kotlin.android" version "1.8.22" apply false } diff --git a/pubspec.lock b/pubspec.lock index fe98dfe3..22f15409 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -413,10 +413,11 @@ packages: emoji_picker_flutter: dependency: "direct main" description: - name: emoji_picker_flutter - sha256: "871339250c00dc469b7fdaaec84f4e10ffa435e730a4f3f3fd06ebd5289ea5ad" - url: "https://pub.dev" - source: hosted + path: "." + ref: ba7e1116380e37bb3fac0d53277dd6937d8849d5 + resolved-ref: ba7e1116380e37bb3fac0d53277dd6937d8849d5 + url: "https://github.com/GlitterWare/emoji_picker_flutter" + source: git version: "2.1.0" encrypt: dependency: "direct main" diff --git a/pubspec.yaml b/pubspec.yaml index 429ab648..10d2f11e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -81,7 +81,10 @@ dependencies: url: https://github.com/GlitterWare/kdbx.dart.git ref: 7e24cacc9e469b2391cefb6e2619e5ffa9db84f8 dropdown_button2: ^2.3.9 - emoji_picker_flutter: ^2.1.0 + emoji_picker_flutter: + git: + url: https://github.com/GlitterWare/emoji_picker_flutter + ref: ba7e1116380e37bb3fac0d53277dd6937d8849d5 text_divider: ^1.0.0 dev_dependencies: From 0bd62f51ef30c6dbba626b9f865efeb08e542170 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Mon, 18 Mar 2024 14:18:47 +0000 Subject: [PATCH 107/131] `flutter_autofill_service` upgraded --- pubspec.lock | 6 +++--- pubspec.yaml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 22f15409..443d2554 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -492,9 +492,9 @@ packages: dependency: "direct main" description: path: "." - ref: "8d742765ddda32c113d630c35577c02feb3df102" - resolved-ref: "8d742765ddda32c113d630c35577c02feb3df102" - url: "https://github.com/kee-org/flutter_autofill_service.git" + ref: f6a3df54dd5b9547bedf6c46645f5e9f94014ce2 + resolved-ref: f6a3df54dd5b9547bedf6c46645f5e9f94014ce2 + url: "https://github.com/GlitterWare/flutter_autofill_service.git" source: git version: "0.18.1" flutter_cache_manager: diff --git a/pubspec.yaml b/pubspec.yaml index 10d2f11e..ad0a96d0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -52,8 +52,8 @@ dependencies: camera: ^0.10.5+9 flutter_autofill_service: git: - url: https://github.com/kee-org/flutter_autofill_service.git - ref: 8d742765ddda32c113d630c35577c02feb3df102 + url: https://github.com/GlitterWare/flutter_autofill_service.git + ref: f6a3df54dd5b9547bedf6c46645f5e9f94014ce2 url_launcher: ^6.0.20 intranet_ip: ^1.0.2 zxing2: ^0.1.1 From 98acabd3db353b05e93e4da52d99169b31dec520 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Mon, 18 Mar 2024 14:51:52 +0000 Subject: [PATCH 108/131] Revert "`flutter_autofill_service` upgraded" This reverts commit 0bd62f51ef30c6dbba626b9f865efeb08e542170. --- pubspec.lock | 6 +++--- pubspec.yaml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 443d2554..22f15409 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -492,9 +492,9 @@ packages: dependency: "direct main" description: path: "." - ref: f6a3df54dd5b9547bedf6c46645f5e9f94014ce2 - resolved-ref: f6a3df54dd5b9547bedf6c46645f5e9f94014ce2 - url: "https://github.com/GlitterWare/flutter_autofill_service.git" + ref: "8d742765ddda32c113d630c35577c02feb3df102" + resolved-ref: "8d742765ddda32c113d630c35577c02feb3df102" + url: "https://github.com/kee-org/flutter_autofill_service.git" source: git version: "0.18.1" flutter_cache_manager: diff --git a/pubspec.yaml b/pubspec.yaml index ad0a96d0..10d2f11e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -52,8 +52,8 @@ dependencies: camera: ^0.10.5+9 flutter_autofill_service: git: - url: https://github.com/GlitterWare/flutter_autofill_service.git - ref: f6a3df54dd5b9547bedf6c46645f5e9f94014ce2 + url: https://github.com/kee-org/flutter_autofill_service.git + ref: 8d742765ddda32c113d630c35577c02feb3df102 url_launcher: ^6.0.20 intranet_ip: ^1.0.2 zxing2: ^0.1.1 From b87c203e886bd35a47e81987e721c3edd35d128e Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Mon, 18 Mar 2024 15:03:41 +0000 Subject: [PATCH 109/131] Unlock Screen only authenticates on resume --- lib/screens/unlock_screen.dart | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/screens/unlock_screen.dart b/lib/screens/unlock_screen.dart index aebd3ae1..230c67e5 100644 --- a/lib/screens/unlock_screen.dart +++ b/lib/screens/unlock_screen.dart @@ -58,11 +58,11 @@ class _UnlockScreen extends State with WidgetsBindingObserver { } Future _bioAuth() async { + if (!Platform.isAndroid && !Platform.isIOS) return; LoadedAccount? account = data.loadedAccount; if (account == null) return; if (UnlockScreen.isAuthenticating) return; if (!mounted) return; - if (!Platform.isAndroid && !Platform.isIOS) return; if (!account.bioAuthEnabled) return; UnlockScreen.isAuthenticating = true; try { @@ -136,8 +136,10 @@ class _UnlockScreen extends State with WidgetsBindingObserver { if ((state != AppLifecycleState.resumed) && (state != AppLifecycleState.inactive)) return; setState(() => _unlockScreenOn = true); - _passwordFocus.requestFocus(); - await _bioAuth(); + if (state == AppLifecycleState.resumed) { + _passwordFocus.requestFocus(); + await _bioAuth(); + } } @override From 413961c9a7b155facb921094f65ed9a8a82fe593 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Mon, 18 Mar 2024 15:18:57 +0000 Subject: [PATCH 110/131] `flutter_autofill_service` upgraded --- pubspec.lock | 6 +++--- pubspec.yaml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 22f15409..d38441a4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -492,9 +492,9 @@ packages: dependency: "direct main" description: path: "." - ref: "8d742765ddda32c113d630c35577c02feb3df102" - resolved-ref: "8d742765ddda32c113d630c35577c02feb3df102" - url: "https://github.com/kee-org/flutter_autofill_service.git" + ref: "23db7e69367606f91244884ea5c0abe3efee922e" + resolved-ref: "23db7e69367606f91244884ea5c0abe3efee922e" + url: "https://github.com/GlitterWare/flutter_autofill_service.git" source: git version: "0.18.1" flutter_cache_manager: diff --git a/pubspec.yaml b/pubspec.yaml index 10d2f11e..369f3edd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -52,8 +52,8 @@ dependencies: camera: ^0.10.5+9 flutter_autofill_service: git: - url: https://github.com/kee-org/flutter_autofill_service.git - ref: 8d742765ddda32c113d630c35577c02feb3df102 + url: https://github.com/GlitterWare/flutter_autofill_service.git + ref: 23db7e69367606f91244884ea5c0abe3efee922e url_launcher: ^6.0.20 intranet_ip: ^1.0.2 zxing2: ^0.1.1 From bf38e961c245adf0ff967181bb7f9a1d8d2f64a9 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Wed, 20 Mar 2024 21:31:27 +0000 Subject: [PATCH 111/131] Entry button overflow set to ellipsis --- lib/passy_flutter/widgets/id_card_button.dart | 1 + lib/passy_flutter/widgets/identity_button.dart | 1 + lib/passy_flutter/widgets/password_button.dart | 1 + lib/passy_flutter/widgets/payment_card_button_mini.dart | 1 + 4 files changed, 4 insertions(+) diff --git a/lib/passy_flutter/widgets/id_card_button.dart b/lib/passy_flutter/widgets/id_card_button.dart index 5b7abab1..ed2dd6dc 100644 --- a/lib/passy_flutter/widgets/id_card_button.dart +++ b/lib/passy_flutter/widgets/id_card_button.dart @@ -41,6 +41,7 @@ class IDCardButton extends StatelessWidget { child: Text( idCard.name, style: const TextStyle(color: Colors.grey), + overflow: TextOverflow.ellipsis, ), alignment: Alignment.centerLeft, ), diff --git a/lib/passy_flutter/widgets/identity_button.dart b/lib/passy_flutter/widgets/identity_button.dart index 0a700d32..61ee1aeb 100644 --- a/lib/passy_flutter/widgets/identity_button.dart +++ b/lib/passy_flutter/widgets/identity_button.dart @@ -40,6 +40,7 @@ class IdentityButton extends StatelessWidget { child: Text( identity.firstAddressLine, style: const TextStyle(color: Colors.grey), + overflow: TextOverflow.ellipsis, ), alignment: Alignment.centerLeft, ), diff --git a/lib/passy_flutter/widgets/password_button.dart b/lib/passy_flutter/widgets/password_button.dart index 33d35967..7b14b773 100644 --- a/lib/passy_flutter/widgets/password_button.dart +++ b/lib/passy_flutter/widgets/password_button.dart @@ -41,6 +41,7 @@ class PasswordButton extends StatelessWidget { child: Text( password.username, style: const TextStyle(color: Colors.grey), + overflow: TextOverflow.ellipsis, ), alignment: Alignment.centerLeft, ), diff --git a/lib/passy_flutter/widgets/payment_card_button_mini.dart b/lib/passy_flutter/widgets/payment_card_button_mini.dart index 1a8bfd5f..4943a98c 100644 --- a/lib/passy_flutter/widgets/payment_card_button_mini.dart +++ b/lib/passy_flutter/widgets/payment_card_button_mini.dart @@ -41,6 +41,7 @@ class PaymentCardButtonMini extends StatelessWidget { child: Text( paymentCard.cardholderName, style: const TextStyle(color: Colors.grey), + overflow: TextOverflow.ellipsis, ), alignment: Alignment.centerLeft, ), From a55cc7d5819a092326130a2221b1bb2d32f0a468 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Wed, 20 Mar 2024 21:43:46 +0000 Subject: [PATCH 112/131] Update entry_tag_list.dart --- lib/passy_flutter/widgets/entry_tag_list.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/passy_flutter/widgets/entry_tag_list.dart b/lib/passy_flutter/widgets/entry_tag_list.dart index 93900617..576f7246 100644 --- a/lib/passy_flutter/widgets/entry_tag_list.dart +++ b/lib/passy_flutter/widgets/entry_tag_list.dart @@ -20,12 +20,11 @@ class EntryTagList extends StatefulWidget { void Function(String tag)? onSecondary, void Function()? onAddPressed, this.showAddButton = false, - }) : onAdded = onAdded ?? _onChanged, - onRemoved = onRemoved ?? _onChanged, + }) : onAdded = onAdded ?? _voidString, + onRemoved = onRemoved ?? _voidString, onSecondary = onSecondary ?? _voidString, onAddPressed = onAddPressed ?? _void; - static void _onChanged(tag) {} static void _void() {} static void _voidString(String foobar) {} From cdefa9bc57df2af33694e7108d2d946f82abf511 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Wed, 20 Mar 2024 22:11:57 +0000 Subject: [PATCH 113/131] Update entry_tag_button.dart --- lib/passy_flutter/widgets/entry_tag_button.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/passy_flutter/widgets/entry_tag_button.dart b/lib/passy_flutter/widgets/entry_tag_button.dart index 1acc7156..0d21a6e4 100644 --- a/lib/passy_flutter/widgets/entry_tag_button.dart +++ b/lib/passy_flutter/widgets/entry_tag_button.dart @@ -36,7 +36,8 @@ class EntryTagButton extends StatelessWidget { right: PassyTheme.passyPadding.right), child: Text( tag, - style: const TextStyle(color: PassyTheme.darkContentColor), + style: const TextStyle( + color: PassyTheme.darkContentColor, height: 0.01), ), ), )); From 4e947c4be3dc37f7cd918f3732e2e91a83ca3e9d Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Wed, 20 Mar 2024 22:34:50 +0000 Subject: [PATCH 114/131] Update entry_tag_list.dart --- lib/passy_flutter/widgets/entry_tag_list.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/passy_flutter/widgets/entry_tag_list.dart b/lib/passy_flutter/widgets/entry_tag_list.dart index 576f7246..ee9015b5 100644 --- a/lib/passy_flutter/widgets/entry_tag_list.dart +++ b/lib/passy_flutter/widgets/entry_tag_list.dart @@ -45,7 +45,7 @@ class _EntryTagList extends State { @override Widget build(BuildContext context) { - WidgetsBinding.instance.addPostFrameCallback((_) { + WidgetsBinding.instance.scheduleFrameCallback((_) { if (!mounted) return; setState(() { showScrollbar = (_key.currentContext?.size?.width ?? 0) == From 33af8c1bf36ef6638c1a587e9ee7fda3a05d44d6 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Wed, 20 Mar 2024 22:42:38 +0000 Subject: [PATCH 115/131] Unlock Screen automatic authentication popup fixed --- lib/screens/unlock_screen.dart | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/screens/unlock_screen.dart b/lib/screens/unlock_screen.dart index 230c67e5..6368e8cd 100644 --- a/lib/screens/unlock_screen.dart +++ b/lib/screens/unlock_screen.dart @@ -127,7 +127,13 @@ class _UnlockScreen extends State with WidgetsBindingObserver { @override void didChangeAppLifecycleState(AppLifecycleState state) async { super.didChangeAppLifecycleState(state); - if (_unlockScreenOn) return; + if (_unlockScreenOn) { + if (state == AppLifecycleState.resumed) { + _passwordFocus.requestFocus(); + await _bioAuth(); + } + return; + } if (!UnlockScreen.shouldLockScreen) return; LoadedAccount? account = data.loadedAccount; if (account == null) return; @@ -136,10 +142,6 @@ class _UnlockScreen extends State with WidgetsBindingObserver { if ((state != AppLifecycleState.resumed) && (state != AppLifecycleState.inactive)) return; setState(() => _unlockScreenOn = true); - if (state == AppLifecycleState.resumed) { - _passwordFocus.requestFocus(); - await _bioAuth(); - } } @override From 54e8d89fb3f23564c6e5663ca3b4bd098d6d795b Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Fri, 22 Mar 2024 01:46:32 +0000 Subject: [PATCH 116/131] Last username saved on biometric authentication --- lib/screens/login_screen.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/screens/login_screen.dart b/lib/screens/login_screen.dart index 2141352d..b6a7bcca 100644 --- a/lib/screens/login_screen.dart +++ b/lib/screens/login_screen.dart @@ -72,6 +72,10 @@ class _LoginScreen extends State { _username, encrypter, key, encryptedPassword: encrypt(storageData.password, encrypter: encrypter)); + data.info.value.lastUsername = _username; + Navigator.pushNamed(context, SplashScreen.routeName); + await data.info.save(); + Navigator.pop(context); if (isAutofill) { Navigator.pushNamed( context, From bc7754259beea5edfcb7070c84c06c2102779d87 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Fri, 22 Mar 2024 01:51:30 +0000 Subject: [PATCH 117/131] Update login_screen.dart --- lib/screens/login_screen.dart | 87 +++++++++++++++++------------------ 1 file changed, 43 insertions(+), 44 deletions(-) diff --git a/lib/screens/login_screen.dart b/lib/screens/login_screen.dart index b6a7bcca..d7d3baf9 100644 --- a/lib/screens/login_screen.dart +++ b/lib/screens/login_screen.dart @@ -195,53 +195,52 @@ class _LoginScreen extends State { } data.info.value.lastUsername = _username; Navigator.pushNamed(context, SplashScreen.routeName); - data.info.save().whenComplete(() async { - try { - enc.Key key = _derivedPassword == null - ? enc.Key.fromUtf8( - _password + (' ' * (32 - utf8.encode(_password).length))) - : enc.Key(Uint8List.fromList(_derivedPassword)); - LoadedAccount _account = await data.loadAccount( - data.info.value.lastUsername, - _derivedPassword == null - ? getPassyEncrypter(_password) - : getPassyEncrypterFromBytes( - Uint8List.fromList(_derivedPassword)), - key, - encryptedPassword: - encrypt(_password, encrypter: enc.Encrypter(enc.AES(key)))); - Navigator.pop(context); - if (isAutofill) { - Navigator.pushNamed( - context, - SearchScreen.routeName, - arguments: SearchScreenArgs( - entryType: null, - builder: _buildPasswords, - isAutofill: true, - ), - ); - return; - } - _account.startAutoSync(); - if (Platform.isAndroid) { - FlutterSecureScreen.singleton - .setAndroidScreenSecure(_account.protectScreen); - } - Navigator.pushReplacementNamed(context, MainScreen.routeName); - } catch (e, s) { - showSnackBar( - message: localizations.couldNotLogin, - icon: const Icon(Icons.lock_rounded, - color: PassyTheme.darkContentColor), - action: SnackBarAction( - label: localizations.details, - onPressed: () => Navigator.pushNamed(context, LogScreen.routeName, - arguments: e.toString() + '\n' + s.toString()), + await data.info.save(); + try { + enc.Key key = _derivedPassword == null + ? enc.Key.fromUtf8( + _password + (' ' * (32 - utf8.encode(_password).length))) + : enc.Key(Uint8List.fromList(_derivedPassword)); + LoadedAccount _account = await data.loadAccount( + data.info.value.lastUsername, + _derivedPassword == null + ? getPassyEncrypter(_password) + : getPassyEncrypterFromBytes( + Uint8List.fromList(_derivedPassword)), + key, + encryptedPassword: + encrypt(_password, encrypter: enc.Encrypter(enc.AES(key)))); + Navigator.pop(context); + if (isAutofill) { + Navigator.pushNamed( + context, + SearchScreen.routeName, + arguments: SearchScreenArgs( + entryType: null, + builder: _buildPasswords, + isAutofill: true, ), ); + return; + } + _account.startAutoSync(); + if (Platform.isAndroid) { + FlutterSecureScreen.singleton + .setAndroidScreenSecure(_account.protectScreen); } - }); + Navigator.pushReplacementNamed(context, MainScreen.routeName); + } catch (e, s) { + showSnackBar( + message: localizations.couldNotLogin, + icon: + const Icon(Icons.lock_rounded, color: PassyTheme.darkContentColor), + action: SnackBarAction( + label: localizations.details, + onPressed: () => Navigator.pushNamed(context, LogScreen.routeName, + arguments: e.toString() + '\n' + s.toString()), + ), + ); + } } void updateBioAuthButton() { From 5f4d64ff30cc223ef3d7d310e9a28a4502770e09 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Fri, 22 Mar 2024 01:54:57 +0000 Subject: [PATCH 118/131] Only saving last username when required --- lib/screens/login_screen.dart | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/screens/login_screen.dart b/lib/screens/login_screen.dart index d7d3baf9..2f40768b 100644 --- a/lib/screens/login_screen.dart +++ b/lib/screens/login_screen.dart @@ -65,6 +65,11 @@ class _LoginScreen extends State { .toString()) { Navigator.popUntil( context, (route) => route.settings.name == LoginScreen.routeName); + Navigator.pushNamed(context, SplashScreen.routeName); + if (data.info.value.lastUsername != _username) { + data.info.value.lastUsername = _username; + await data.info.save(); + } enc.Key key = await derivePassword(storageData.password, derivationType: derivationType, derivationInfo: derivationInfo); enc.Encrypter encrypter = getPassyEncrypterFromBytes(key.bytes); @@ -72,9 +77,6 @@ class _LoginScreen extends State { _username, encrypter, key, encryptedPassword: encrypt(storageData.password, encrypter: encrypter)); - data.info.value.lastUsername = _username; - Navigator.pushNamed(context, SplashScreen.routeName); - await data.info.save(); Navigator.pop(context); if (isAutofill) { Navigator.pushNamed( @@ -193,9 +195,11 @@ class _LoginScreen extends State { }); return; } - data.info.value.lastUsername = _username; Navigator.pushNamed(context, SplashScreen.routeName); - await data.info.save(); + if (data.info.value.lastUsername != _username) { + data.info.value.lastUsername = _username; + await data.info.save(); + } try { enc.Key key = _derivedPassword == null ? enc.Key.fromUtf8( From 5ea0e56abcd3a3ffe751a5eab56fda1ed34843f7 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 23 Mar 2024 11:21:11 +0000 Subject: [PATCH 119/131] Synchronization version changed --- lib/passy_data/common.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/passy_data/common.dart b/lib/passy_data/common.dart index 47d602fe..71e59efd 100644 --- a/lib/passy_data/common.dart +++ b/lib/passy_data/common.dart @@ -18,8 +18,8 @@ import 'glare/glare_client.dart'; import 'key_derivation_type.dart'; const String passyVersion = '1.8.0'; -const String syncVersion = '2.1.0'; const String accountVersion = '2.4.0'; +const String syncVersion = '2.1.1'; bool isSnap() { return Platform.environment['SNAP_NAME'] == 'passy'; From 7a8c02589f07cf425b4fac79e931703eb6fbaa15 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 23 Mar 2024 11:21:20 +0000 Subject: [PATCH 120/131] Account version changed --- lib/passy_data/common.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/passy_data/common.dart b/lib/passy_data/common.dart index 71e59efd..cce94f57 100644 --- a/lib/passy_data/common.dart +++ b/lib/passy_data/common.dart @@ -18,8 +18,8 @@ import 'glare/glare_client.dart'; import 'key_derivation_type.dart'; const String passyVersion = '1.8.0'; -const String accountVersion = '2.4.0'; const String syncVersion = '2.1.1'; +const String accountVersion = '2.4.1'; bool isSnap() { return Platform.environment['SNAP_NAME'] == 'passy'; From b9010ba7c0f674926bc7c6ae7544424f3f1a3215 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 23 Mar 2024 11:56:24 +0000 Subject: [PATCH 121/131] Windows pipeline fix 1 --- .github/workflows/build-windows-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-windows-ci.yml b/.github/workflows/build-windows-ci.yml index 7cc95a3b..2cc60a34 100644 --- a/.github/workflows/build-windows-ci.yml +++ b/.github/workflows/build-windows-ci.yml @@ -26,7 +26,7 @@ jobs: run: | cd .. mkdir -p build/windows-portable/Passy - cp -r Passy/build/windows/runner/Release/. build/windows-portable/Passy + cp -r Passy/build/windows/x64/runner/Release/. build/windows-portable/Passy - name: Upload Windows portable build uses: actions/upload-artifact@v3 with: From f31e20f6708e20ac2ef53fe0931eb932a19992c8 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 23 Mar 2024 11:58:34 +0000 Subject: [PATCH 122/131] Update build-all-ci.yml --- .github/workflows/build-all-ci.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-all-ci.yml b/.github/workflows/build-all-ci.yml index f5be0afa..ca58ce54 100644 --- a/.github/workflows/build-all-ci.yml +++ b/.github/workflows/build-all-ci.yml @@ -18,6 +18,11 @@ jobs: run: | sudo apt-get update sudo apt-get -y install clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev binutils coreutils desktop-file-utils fakeroot fuse libgdk-pixbuf2.0-dev patchelf python3-pip python3-setuptools squashfs-tools strace util-linux zsync + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'adopt' - name: Install flutter shell: bash run: | @@ -158,7 +163,7 @@ jobs: run: | cd .. mkdir -p build/windows-portable/Passy - cp -r Passy/build/windows/runner/Release/. build/windows-portable/Passy + cp -r Passy/build/windows/x64/runner/Release/. build/windows-portable/Passy - name: Upload Windows portable build uses: actions/upload-artifact@v3 with: From 6450724b5cd58290fa37d89e1d817d314cf5ce20 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 23 Mar 2024 12:14:15 +0000 Subject: [PATCH 123/131] Updated actions to use Node.js 20 --- .github/workflows/build-all-ci.yml | 20 ++++++++++---------- .github/workflows/build-linux-armv7-ci.yml | 4 ++-- .github/workflows/build-windows-ci.yml | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/build-all-ci.yml b/.github/workflows/build-all-ci.yml index ca58ce54..7f7dfddb 100644 --- a/.github/workflows/build-all-ci.yml +++ b/.github/workflows/build-all-ci.yml @@ -12,14 +12,14 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install dependencies shell: bash run: | sudo apt-get update sudo apt-get -y install clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev binutils coreutils desktop-file-utils fakeroot fuse libgdk-pixbuf2.0-dev patchelf python3-pip python3-setuptools squashfs-tools strace util-linux zsync - name: Set up JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: '17' distribution: 'adopt' @@ -42,7 +42,7 @@ jobs: mkdir build cp Passy/build/app/outputs/flutter-apk/app-release.apk build/Passy-Android.apk - name: Upload Android build - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: android path: /home/runner/work/Passy/build/Passy-Android.apk @@ -51,7 +51,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install dependencies shell: bash run: | @@ -93,12 +93,12 @@ jobs: filename: Passy-Linux-AppImage.zip path: Passy-Latest-x86_64.AppImage - name: Upload Linux bundle build - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: linux-bundle path: /home/runner/work/Passy/build/linux-bundle/Passy-Linux-Bundle.zip - name: Upload Linux AppImage build - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: linux-appimage path: /home/runner/work/Passy/build/Passy-Linux-AppImage.zip @@ -130,12 +130,12 @@ jobs: filename: Passy-Linux-AppImage.zip path: Passy-Latest-x86_64.AppImage - name: Upload Linux bundle no updates popup build - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: linux-bundle-no-updates-popup path: /home/runner/work/Passy/build/linux-bundle/Passy-Linux-Bundle.zip - name: Upload Linux AppImage no updates popup build - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: linux-appimage-no-updates-popup path: /home/runner/work/Passy/build/Passy-Linux-AppImage.zip @@ -144,7 +144,7 @@ jobs: runs-on: windows-latest steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install flutter shell: bash run: | @@ -165,7 +165,7 @@ jobs: mkdir -p build/windows-portable/Passy cp -r Passy/build/windows/x64/runner/Release/. build/windows-portable/Passy - name: Upload Windows portable build - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: windows-portable path: D:\a\Passy\build\windows-portable diff --git a/.github/workflows/build-linux-armv7-ci.yml b/.github/workflows/build-linux-armv7-ci.yml index 6b93df46..8249d5c1 100644 --- a/.github/workflows/build-linux-armv7-ci.yml +++ b/.github/workflows/build-linux-armv7-ci.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up QEMU uses: docker/setup-qemu-action@v2.1.0 - name: Create build directory @@ -42,7 +42,7 @@ jobs: filename: Passy-CLI-Linux-ARMv7.zip path: cli - name: Upload Passy CLI build - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: passy-cli-linux-armv7 path: /home/runner/work/Passy/build/Passy-CLI-Linux-ARMv7.zip diff --git a/.github/workflows/build-windows-ci.yml b/.github/workflows/build-windows-ci.yml index 2cc60a34..0049893d 100644 --- a/.github/workflows/build-windows-ci.yml +++ b/.github/workflows/build-windows-ci.yml @@ -7,7 +7,7 @@ jobs: runs-on: windows-latest steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install flutter shell: bash run: | @@ -28,7 +28,7 @@ jobs: mkdir -p build/windows-portable/Passy cp -r Passy/build/windows/x64/runner/Release/. build/windows-portable/Passy - name: Upload Windows portable build - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: windows-portable path: D:\a\Passy\build\windows-portable From a7f8fc56fdae0ad494d2d75196e5e5b25bba293a Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 23 Mar 2024 12:46:30 +0000 Subject: [PATCH 124/131] History saved on tag rename --- lib/passy_data/loaded_account.dart | 23 ++++++++++++++++++- .../passy_entries_encrypted_csv_file.dart | 7 +++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/lib/passy_data/loaded_account.dart b/lib/passy_data/loaded_account.dart index f201d237..ce16a3ac 100644 --- a/lib/passy_data/loaded_account.dart +++ b/lib/passy_data/loaded_account.dart @@ -1109,13 +1109,34 @@ class LoadedAccount { required String tag, required String newTag, }) async { - await Future.wait([ + List> keyLists = await Future.wait([ _passwords.renameTag(tag: tag, newTag: newTag), _notes.renameTag(tag: tag, newTag: newTag), _paymentCards.renameTag(tag: tag, newTag: newTag), _identities.renameTag(tag: tag, newTag: newTag), _idCards.renameTag(tag: tag, newTag: newTag), ]); + for (String passwordKey in keyLists[0]) { + _history.value.passwords[passwordKey] = EntryEvent(passwordKey, + status: EntryStatus.alive, lastModified: DateTime.now().toUtc()); + } + for (String notesKey in keyLists[1]) { + _history.value.notes[notesKey] = EntryEvent(notesKey, + status: EntryStatus.alive, lastModified: DateTime.now().toUtc()); + } + for (String paymentCardKey in keyLists[2]) { + _history.value.paymentCards[paymentCardKey] = EntryEvent(paymentCardKey, + status: EntryStatus.alive, lastModified: DateTime.now().toUtc()); + } + for (String identityKey in keyLists[3]) { + _history.value.identities[identityKey] = EntryEvent(identityKey, + status: EntryStatus.alive, lastModified: DateTime.now().toUtc()); + } + for (String idCardKey in keyLists[4]) { + _history.value.idCards[idCardKey] = EntryEvent(idCardKey, + status: EntryStatus.alive, lastModified: DateTime.now().toUtc()); + } + await _history.save(); } // Passwords wrappers diff --git a/lib/passy_data/passy_entries_encrypted_csv_file.dart b/lib/passy_data/passy_entries_encrypted_csv_file.dart index 2421ab10..55209653 100644 --- a/lib/passy_data/passy_entries_encrypted_csv_file.dart +++ b/lib/passy_data/passy_entries_encrypted_csv_file.dart @@ -379,10 +379,11 @@ class PassyEntriesEncryptedCSVFile> { await _raf.close(); } - Future renameTag({ + Future> renameTag({ required String tag, required String newTag, }) async { + List keys = []; RandomAccessFile _raf = await _file.open(); if (skipLine(_raf, lineDelimiter: ',') == -1) { await _raf.close(); @@ -408,12 +409,15 @@ class PassyEntriesEncryptedCSVFile> { return true; } var tagList = _csv[_tagIndex]; + bool _changed = false; for (dynamic oldTag in (tagList as List).toList()) { oldTag = oldTag.toString(); if (oldTag != tag) continue; + _changed = true; tagList.remove(tag); tagList.add(newTag); } + if (_changed) keys.add(_decoded[0]); entry = _encodeEntryForSaving(_csv); await _tempRaf.writeString(entry); if (skipLine(_raf, lineDelimiter: ',') == -1) return true; @@ -424,5 +428,6 @@ class PassyEntriesEncryptedCSVFile> { await _file.delete(); await _tempFile.copy(_file.absolute.path); await _tempFile.delete(); + return keys; } } From 6639f91bbe7f57a273f0f9e19babfbd7beee1476 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 23 Mar 2024 12:50:44 +0000 Subject: [PATCH 125/131] Update passy_entries_encrypted_csv_file.dart --- lib/passy_data/passy_entries_encrypted_csv_file.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/passy_data/passy_entries_encrypted_csv_file.dart b/lib/passy_data/passy_entries_encrypted_csv_file.dart index 55209653..30328aa3 100644 --- a/lib/passy_data/passy_entries_encrypted_csv_file.dart +++ b/lib/passy_data/passy_entries_encrypted_csv_file.dart @@ -387,7 +387,7 @@ class PassyEntriesEncryptedCSVFile> { RandomAccessFile _raf = await _file.open(); if (skipLine(_raf, lineDelimiter: ',') == -1) { await _raf.close(); - return; + return const []; } File _tempFile; { From 49a25e11df4540c9ea53e9c9e0dd4f81a5473db3 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 23 Mar 2024 12:59:46 +0000 Subject: [PATCH 126/131] Entry Tag List scrollbar bottom padding reduced for mobile --- lib/passy_flutter/widgets/entry_tag_list.dart | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/lib/passy_flutter/widgets/entry_tag_list.dart b/lib/passy_flutter/widgets/entry_tag_list.dart index ee9015b5..1cdacb1d 100644 --- a/lib/passy_flutter/widgets/entry_tag_list.dart +++ b/lib/passy_flutter/widgets/entry_tag_list.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:passy/common/common.dart'; import 'package:passy/passy_flutter/passy_flutter.dart'; @@ -35,6 +37,8 @@ class EntryTagList extends StatefulWidget { class _EntryTagList extends State { final ScrollController _scrollController = ScrollController(); final GlobalKey _key = GlobalKey(); + static final double _bottomPadding = + (Platform.isAndroid || Platform.isIOS) ? 6 : 14; bool showScrollbar = false; @@ -47,10 +51,12 @@ class _EntryTagList extends State { Widget build(BuildContext context) { WidgetsBinding.instance.scheduleFrameCallback((_) { if (!mounted) return; - setState(() { - showScrollbar = (_key.currentContext?.size?.width ?? 0) == - MediaQuery.of(context).size.width; - }); + try { + setState(() { + showScrollbar = (_key.currentContext?.size?.width ?? 0) == + MediaQuery.of(context).size.width; + }); + } catch (_) {} }); List notSelectedButtons = []; @@ -62,7 +68,7 @@ class _EntryTagList extends State { padding: EdgeInsets.only( left: PassyTheme.passyPadding.left / 2, right: PassyTheme.passyPadding.right / 2, - bottom: showScrollbar ? 14 : 0), + bottom: showScrollbar ? _bottomPadding : 0), child: EntryTagButton( tag, isSelected: true, @@ -83,7 +89,7 @@ class _EntryTagList extends State { padding: EdgeInsets.only( left: PassyTheme.passyPadding.left / 2, right: PassyTheme.passyPadding.right / 2, - bottom: showScrollbar ? 14 : 0), + bottom: showScrollbar ? _bottomPadding : 0), child: EntryTagButton( tag, onPressed: () { @@ -111,14 +117,15 @@ class _EntryTagList extends State { padding: EdgeInsets.only( left: PassyTheme.passyPadding.left / 2, right: PassyTheme.passyPadding.right / 2, - bottom: showScrollbar ? 14 : 0), + bottom: showScrollbar ? _bottomPadding : 0), child: Text(localizations.noTags), ), if (selectedButtons.isEmpty && notSelectedButtons.isEmpty) const SizedBox(width: 10), if (widget.showAddButton) Padding( - padding: EdgeInsets.only(bottom: showScrollbar ? 14 : 0), + padding: EdgeInsets.only( + bottom: showScrollbar ? _bottomPadding : 0), child: FloatingActionButton( heroTag: null, child: const Icon(Icons.add), @@ -140,7 +147,7 @@ class _EntryTagList extends State { padding: EdgeInsets.only( left: PassyTheme.passyPadding.left / 2, right: PassyTheme.passyPadding.right / 2, - bottom: showScrollbar ? 14 : 0), + bottom: showScrollbar ? _bottomPadding : 0), child: const SizedBox( height: 36, width: 2, @@ -160,7 +167,7 @@ class _EntryTagList extends State { return SizedBox( key: _key, - height: showScrollbar ? 50 : 36, + height: showScrollbar ? 50 : (50 - _bottomPadding), child: PrimaryScrollController( controller: _scrollController, child: showScrollbar From 5125643b8908bda3934bc02afaddeea7c7041bf8 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 23 Mar 2024 13:04:39 +0000 Subject: [PATCH 127/131] Left and right padding added for create tag button --- lib/passy_flutter/widgets/entry_tag_list.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/passy_flutter/widgets/entry_tag_list.dart b/lib/passy_flutter/widgets/entry_tag_list.dart index 1cdacb1d..62489638 100644 --- a/lib/passy_flutter/widgets/entry_tag_list.dart +++ b/lib/passy_flutter/widgets/entry_tag_list.dart @@ -125,6 +125,8 @@ class _EntryTagList extends State { if (widget.showAddButton) Padding( padding: EdgeInsets.only( + left: PassyTheme.passyPadding.left / 2, + right: PassyTheme.passyPadding.right / 2, bottom: showScrollbar ? _bottomPadding : 0), child: FloatingActionButton( heroTag: null, From 921e4ada86670a1345d4fa9c47823e480bfaa286 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 23 Mar 2024 18:41:33 +0000 Subject: [PATCH 128/131] tag added to AppStream --- linux_assets/com.glitterware.passy.appdata.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/linux_assets/com.glitterware.passy.appdata.xml b/linux_assets/com.glitterware.passy.appdata.xml index 445eab31..f63c733a 100644 --- a/linux_assets/com.glitterware.passy.appdata.xml +++ b/linux_assets/com.glitterware.passy.appdata.xml @@ -4,6 +4,8 @@ CC0-1.0 GPL-3.0 Passy + GlitterWare + GlitterWare Offline password manager with cross-platform synchronization

Store passwords, payment cards notes, ID cards and identities offline and safe, synchronized between all of your devices.

From 64bf2d887317aee399ce57984ead32c6fecbe868 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 23 Mar 2024 18:42:59 +0000 Subject: [PATCH 129/131] ARMv7 workflow fixed --- .github/workflows/armv7.sh | 2 +- .github/workflows/build-linux-armv7-ci.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/armv7.sh b/.github/workflows/armv7.sh index e030207c..cb702b54 100755 --- a/.github/workflows/armv7.sh +++ b/.github/workflows/armv7.sh @@ -27,7 +27,7 @@ flutter doctor export LAST_PWD=$PWD cd submodules/flutter/bin/cache rm -rf dart-sdk -wget https://storage.googleapis.com/dart-archive/channels/stable/release/3.1.0/sdk/dartsdk-linux-arm-release.zip +wget https://storage.googleapis.com/dart-archive/channels/stable/release/3.3.1/sdk/dartsdk-linux-arm-release.zip unzip dartsdk-linux-arm-release.zip rm dartsdk-linux-arm-release.zip cd $LAST_PWD diff --git a/.github/workflows/build-linux-armv7-ci.yml b/.github/workflows/build-linux-armv7-ci.yml index 8249d5c1..fea578bc 100644 --- a/.github/workflows/build-linux-armv7-ci.yml +++ b/.github/workflows/build-linux-armv7-ci.yml @@ -22,7 +22,7 @@ jobs: mkdir build mkdir bin cd bin - wget https://raw.githubusercontent.com/GlitterWare/Passy/4a813c8b87960ff5bf6f7fe9a5fbebab7c5ad353/.github/workflows/armv7.sh + wget https://raw.githubusercontent.com/GlitterWare/Passy/$GITHUB_SHA/.github/workflows/armv7.sh chmod +x armv7.sh - name: Install dependencies shell: bash From 8ea2058edc4a64de648061eac84e4705cd432cc2 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 23 Mar 2024 19:01:50 +0000 Subject: [PATCH 130/131] Screenshot captions implemented --- linux_assets/com.glitterware.passy.appdata.xml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/linux_assets/com.glitterware.passy.appdata.xml b/linux_assets/com.glitterware.passy.appdata.xml index f63c733a..1ba769e2 100644 --- a/linux_assets/com.glitterware.passy.appdata.xml +++ b/linux_assets/com.glitterware.passy.appdata.xml @@ -17,24 +17,39 @@ https://glitterware.github.io/Passy + Passy login screen. https://github.com/GlitterWare/Passy/raw/273e012d77e0667486388d94c9f5d129a2911350/screenshots/1920x1080/1.png + Passy main screen. https://github.com/GlitterWare/Passy/raw/273e012d77e0667486388d94c9f5d129a2911350/screenshots/1920x1080/2.png + Host connection QR code. https://github.com/GlitterWare/Passy/raw/273e012d77e0667486388d94c9f5d129a2911350/screenshots/1920x1080/3.png + + + Add password screen. https://github.com/GlitterWare/Passy/raw/273e012d77e0667486388d94c9f5d129a2911350/screenshots/1920x1080/4.png + Password generator. https://github.com/GlitterWare/Passy/raw/273e012d77e0667486388d94c9f5d129a2911350/screenshots/1920x1080/5.png + + + Custom field screen. https://github.com/GlitterWare/Passy/raw/273e012d77e0667486388d94c9f5d129a2911350/screenshots/1920x1080/6.png + Date selection. https://github.com/GlitterWare/Passy/raw/273e012d77e0667486388d94c9f5d129a2911350/screenshots/1920x1080/7.png + + + Password entry screen. https://github.com/GlitterWare/Passy/raw/273e012d77e0667486388d94c9f5d129a2911350/screenshots/1920x1080/8.png + Password view dialog. https://github.com/GlitterWare/Passy/raw/273e012d77e0667486388d94c9f5d129a2911350/screenshots/1920x1080/9.png From a0ba674befb2fb32e85a7834bc9be1870efd6580 Mon Sep 17 00:00:00 2001 From: GleammerRay <101527589+GleammerRay@users.noreply.github.com> Date: Sat, 23 Mar 2024 19:13:15 +0000 Subject: [PATCH 131/131] AppStream Developer ID implemented --- linux_assets/com.glitterware.passy.appdata.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/linux_assets/com.glitterware.passy.appdata.xml b/linux_assets/com.glitterware.passy.appdata.xml index 1ba769e2..d4f5bcb5 100644 --- a/linux_assets/com.glitterware.passy.appdata.xml +++ b/linux_assets/com.glitterware.passy.appdata.xml @@ -4,7 +4,9 @@ CC0-1.0 GPL-3.0 Passy - GlitterWare + + GlitterWare + GlitterWare Offline password manager with cross-platform synchronization