Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a93dbbe30d | ||
|
|
22a535a6fd |
@@ -521,10 +521,15 @@ class TxMetaController extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Replace labels on every transaction in [txs] (per-id overrides).
|
/// Replace labels on every transaction in [txs] (per-id overrides).
|
||||||
|
///
|
||||||
|
/// When [matchPattern] is set (apply-to-matched), also refreshes the
|
||||||
|
/// merchant rule so future transactions matching the pattern inherit the
|
||||||
|
/// full label set (parent + sub-labels).
|
||||||
Future<void> setLabelsForMany(
|
Future<void> setLabelsForMany(
|
||||||
List<BankTransaction> txs,
|
List<BankTransaction> txs,
|
||||||
List<String> labelIds,
|
List<String> labelIds, {
|
||||||
) async {
|
String? matchPattern,
|
||||||
|
}) async {
|
||||||
if (txs.isEmpty) return;
|
if (txs.isEmpty) return;
|
||||||
|
|
||||||
final expanded = expandWithAncestors([
|
final expanded = expandWithAncestors([
|
||||||
@@ -545,6 +550,22 @@ class TxMetaController extends ChangeNotifier {
|
|||||||
: current.copyWith(labelIds: unique);
|
: current.copyWith(labelIds: unique);
|
||||||
}
|
}
|
||||||
_byId = nextById;
|
_byId = nextById;
|
||||||
|
|
||||||
|
final pattern = (matchPattern ?? '').trim().toLowerCase();
|
||||||
|
if (pattern.isNotEmpty) {
|
||||||
|
final nextRules = Map<String, List<String>>.of(_labelsByMerchant);
|
||||||
|
if (unique.isEmpty) {
|
||||||
|
nextRules.remove(pattern);
|
||||||
|
for (final tx in txs) {
|
||||||
|
nextRules.remove(tx.merchantKey);
|
||||||
|
nextRules.remove(tx.descriptionKey);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nextRules[pattern] = unique;
|
||||||
|
}
|
||||||
|
_labelsByMerchant = nextRules;
|
||||||
|
}
|
||||||
|
|
||||||
_schedulePersist();
|
_schedulePersist();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
library;
|
library;
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:pocketbase/pocketbase.dart';
|
import 'package:pocketbase/pocketbase.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
@@ -46,6 +47,8 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||||||
email: _emailController.text,
|
email: _emailController.text,
|
||||||
password: _passwordController.text,
|
password: _passwordController.text,
|
||||||
);
|
);
|
||||||
|
// Lets iOS/Android password managers offer to save / confirm the login.
|
||||||
|
TextInput.finishAutofillContext();
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
if (!widget.asRoot) {
|
if (!widget.asRoot) {
|
||||||
Navigator.of(context).pop(true);
|
Navigator.of(context).pop(true);
|
||||||
@@ -96,7 +99,8 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
Form(
|
AutofillGroup(
|
||||||
|
child: Form(
|
||||||
key: _formKey,
|
key: _formKey,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
@@ -106,6 +110,10 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||||||
keyboardType: TextInputType.emailAddress,
|
keyboardType: TextInputType.emailAddress,
|
||||||
textInputAction: TextInputAction.next,
|
textInputAction: TextInputAction.next,
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
|
autofillHints: const [
|
||||||
|
AutofillHints.email,
|
||||||
|
AutofillHints.username,
|
||||||
|
],
|
||||||
validator: (value) {
|
validator: (value) {
|
||||||
final text = value?.trim() ?? '';
|
final text = value?.trim() ?? '';
|
||||||
if (text.isEmpty) return 'Enter your email';
|
if (text.isEmpty) return 'Enter your email';
|
||||||
@@ -118,6 +126,9 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||||||
controller: _passwordController,
|
controller: _passwordController,
|
||||||
obscureText: _obscurePassword,
|
obscureText: _obscurePassword,
|
||||||
textInputAction: TextInputAction.done,
|
textInputAction: TextInputAction.done,
|
||||||
|
autofillHints: const [AutofillHints.password],
|
||||||
|
enableSuggestions: false,
|
||||||
|
autocorrect: false,
|
||||||
onFieldSubmitted: (_) => _submit(),
|
onFieldSubmitted: (_) => _submit(),
|
||||||
validator: (value) {
|
validator: (value) {
|
||||||
if (value == null || value.isEmpty) {
|
if (value == null || value.isEmpty) {
|
||||||
@@ -160,6 +171,7 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -188,6 +188,7 @@ class _TransactionDetailScreenState extends State<TransactionDetailScreen> {
|
|||||||
await meta.setLabelsForMany(
|
await meta.setLabelsForMany(
|
||||||
targets,
|
targets,
|
||||||
_draftLabelIds.toList(growable: false),
|
_draftLabelIds.toList(growable: false),
|
||||||
|
matchPattern: toMatched ? _pattern : null,
|
||||||
);
|
);
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ class AppTextField extends StatelessWidget {
|
|||||||
this.keyboardType,
|
this.keyboardType,
|
||||||
this.validator,
|
this.validator,
|
||||||
this.autofocus = false,
|
this.autofocus = false,
|
||||||
|
this.autofillHints,
|
||||||
|
this.obscureText = false,
|
||||||
|
this.enableSuggestions,
|
||||||
|
this.autocorrect,
|
||||||
});
|
});
|
||||||
|
|
||||||
final TextEditingController controller;
|
final TextEditingController controller;
|
||||||
@@ -24,6 +28,10 @@ class AppTextField extends StatelessWidget {
|
|||||||
final TextInputType? keyboardType;
|
final TextInputType? keyboardType;
|
||||||
final String? Function(String?)? validator;
|
final String? Function(String?)? validator;
|
||||||
final bool autofocus;
|
final bool autofocus;
|
||||||
|
final Iterable<String>? autofillHints;
|
||||||
|
final bool obscureText;
|
||||||
|
final bool? enableSuggestions;
|
||||||
|
final bool? autocorrect;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -31,8 +39,12 @@ class AppTextField extends StatelessWidget {
|
|||||||
controller: controller,
|
controller: controller,
|
||||||
maxLines: maxLines,
|
maxLines: maxLines,
|
||||||
autofocus: autofocus,
|
autofocus: autofocus,
|
||||||
|
obscureText: obscureText,
|
||||||
textInputAction: textInputAction,
|
textInputAction: textInputAction,
|
||||||
keyboardType: keyboardType,
|
keyboardType: keyboardType,
|
||||||
|
autofillHints: autofillHints,
|
||||||
|
enableSuggestions: enableSuggestions ?? !obscureText,
|
||||||
|
autocorrect: autocorrect ?? !obscureText,
|
||||||
validator: validator,
|
validator: validator,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: label,
|
labelText: label,
|
||||||
|
|||||||
Reference in New Issue
Block a user