diff --git a/lib/core/translation/matching_algorithm_localization_mapper.dart b/lib/core/translation/matching_algorithm_localization_mapper.dart index e8efa1d..9d2fd54 100644 --- a/lib/core/translation/matching_algorithm_localization_mapper.dart +++ b/lib/core/translation/matching_algorithm_localization_mapper.dart @@ -2,8 +2,10 @@ import 'package:flutter/material.dart'; import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_mobile/generated/l10n.dart'; -String translateMatchingAlgorithm( - BuildContext context, MatchingAlgorithm algorithm) { +String translateMatchingAlgorithmDescription( + BuildContext context, + MatchingAlgorithm algorithm, +) { switch (algorithm) { case MatchingAlgorithm.anyWord: return S.of(context).matchingAlgorithmAnyDescription; @@ -19,3 +21,23 @@ String translateMatchingAlgorithm( return S.of(context).matchingAlgorithmAutoDescription; } } + +String translateMatchingAlgorithmName( + BuildContext context, + MatchingAlgorithm algorithm, +) { + switch (algorithm) { + case MatchingAlgorithm.anyWord: + return S.of(context).matchingAlgorithmAnyName; + case MatchingAlgorithm.allWords: + return S.of(context).matchingAlgorithmAllName; + case MatchingAlgorithm.exactMatch: + return S.of(context).matchingAlgorithmExactName; + case MatchingAlgorithm.regex: + return S.of(context).matchingAlgorithmRegexName; + case MatchingAlgorithm.fuzzy: + return S.of(context).matchingAlgorithmFuzzyName; + case MatchingAlgorithm.auto: + return S.of(context).matchingAlgorithmAutoName; + } +} diff --git a/lib/core/widgets/form_builder_fields/extended_date_range_form_field/relative_date_range_picker_helper.dart b/lib/core/widgets/form_builder_fields/extended_date_range_form_field/relative_date_range_picker_helper.dart index 2c5c536..59a9c64 100644 --- a/lib/core/widgets/form_builder_fields/extended_date_range_form_field/relative_date_range_picker_helper.dart +++ b/lib/core/widgets/form_builder_fields/extended_date_range_form_field/relative_date_range_picker_helper.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:paperless_api/paperless_api.dart'; +import 'package:paperless_mobile/core/workarounds/colored_chip.dart'; import 'package:paperless_mobile/generated/l10n.dart'; class RelativeDateRangePickerHelper extends StatefulWidget { @@ -28,15 +29,17 @@ class _RelativeDateRangePickerHelperState separatorBuilder: (context, index) => const SizedBox(width: 8.0), itemBuilder: (context, index) { final option = _options[index]; - return FilterChip( - label: Text(option.title), - onSelected: (isSelected) { - final value = - isSelected ? option.value : const RelativeDateRangeQuery(); - widget.field.didChange(value); - widget.onChanged?.call(value); - }, - selected: widget.field.value == option.value, + return ColoredChipWrapper( + child: FilterChip( + label: Text(option.title), + onSelected: (isSelected) { + final value = + isSelected ? option.value : const RelativeDateRangeQuery(); + widget.field.didChange(value); + widget.onChanged?.call(value); + }, + selected: widget.field.value == option.value, + ), ); }, scrollDirection: Axis.horizontal, diff --git a/lib/core/workarounds/colored_chip.dart b/lib/core/workarounds/colored_chip.dart new file mode 100644 index 0000000..584d7d3 --- /dev/null +++ b/lib/core/workarounds/colored_chip.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; + +class ColoredChipWrapper extends StatelessWidget { + final Color? backgroundColor; + final Widget child; + const ColoredChipWrapper({ + super.key, + this.backgroundColor, + required this.child, + }); + + @override + Widget build(BuildContext context) { + Color color = backgroundColor ?? Colors.lightGreen[50]!; + + return Theme( + data: Theme.of(context).copyWith( + canvasColor: color, + ), + child: child, + ); + } +} diff --git a/lib/features/document_details/view/pages/document_details_page.dart b/lib/features/document_details/view/pages/document_details_page.dart index 19156c3..fae201e 100644 --- a/lib/features/document_details/view/pages/document_details_page.dart +++ b/lib/features/document_details/view/pages/document_details_page.dart @@ -438,7 +438,6 @@ class _DocumentDetailsPageState extends State { child: TagsWidget( isClickable: widget.isLabelClickable, tagIds: document.tags, - isSelectedPredicate: (_) => false, onTagSelected: (int tagId) {}, ), ), diff --git a/lib/features/documents/bloc/documents_cubit.dart b/lib/features/documents/bloc/documents_cubit.dart index bd1004a..1eb3e32 100644 --- a/lib/features/documents/bloc/documents_cubit.dart +++ b/lib/features/documents/bloc/documents_cubit.dart @@ -95,7 +95,7 @@ class DocumentsCubit extends HydratedCubit } void unselectView() { - emit(state.copyWith(selectedSavedViewId: null)); + emit(state.copyWith(selectedSavedViewId: () => null)); } @override diff --git a/lib/features/documents/bloc/documents_state.dart b/lib/features/documents/bloc/documents_state.dart index 3ae8ee9..2850d7c 100644 --- a/lib/features/documents/bloc/documents_state.dart +++ b/lib/features/documents/bloc/documents_state.dart @@ -25,7 +25,7 @@ class DocumentsState extends DocumentsPagedState { List>? value, DocumentFilter? filter, List? selection, - int? selectedSavedViewId, + int? Function()? selectedSavedViewId, }) { return DocumentsState( hasLoaded: hasLoaded ?? this.hasLoaded, @@ -33,7 +33,9 @@ class DocumentsState extends DocumentsPagedState { value: value ?? this.value, filter: filter ?? this.filter, selection: selection ?? this.selection, - selectedSavedViewId: selectedSavedViewId ?? this.selectedSavedViewId, + selectedSavedViewId: selectedSavedViewId != null + ? selectedSavedViewId.call() + : this.selectedSavedViewId, ); } diff --git a/lib/features/documents/view/pages/document_edit_page.dart b/lib/features/documents/view/pages/document_edit_page.dart index 0ea8cb7..9c63df4 100644 --- a/lib/features/documents/view/pages/document_edit_page.dart +++ b/lib/features/documents/view/pages/document_edit_page.dart @@ -11,6 +11,7 @@ import 'package:paperless_mobile/core/repository/label_repository.dart'; import 'package:paperless_mobile/core/repository/state/impl/correspondent_repository_state.dart'; import 'package:paperless_mobile/core/repository/state/impl/document_type_repository_state.dart'; import 'package:paperless_mobile/core/repository/state/impl/storage_path_repository_state.dart'; +import 'package:paperless_mobile/core/workarounds/colored_chip.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.dart'; import 'package:paperless_mobile/features/edit_document/cubit/edit_document_cubit.dart'; import 'package:paperless_mobile/features/edit_label/view/impl/add_correspondent_page.dart'; @@ -305,6 +306,9 @@ class _DocumentEditPageState extends State { ); } + /// + /// Item builder is typically some sort of [Chip]. + /// Widget _buildSuggestionsSkeleton({ required Iterable suggestions, required ItemBuilder itemBuilder, @@ -321,8 +325,9 @@ class _DocumentEditPageState extends State { child: ListView.separated( scrollDirection: Axis.horizontal, itemCount: suggestions.length, - itemBuilder: (context, index) => - itemBuilder(context, suggestions.elementAt(index)), + itemBuilder: (context, index) => ColoredChipWrapper( + child: itemBuilder(context, suggestions.elementAt(index)), + ), separatorBuilder: (BuildContext context, int index) => const SizedBox(width: 4.0), ), diff --git a/lib/features/documents/view/widgets/grid/document_grid_item.dart b/lib/features/documents/view/widgets/grid/document_grid_item.dart index d460444..00a50be 100644 --- a/lib/features/documents/view/widgets/grid/document_grid_item.dart +++ b/lib/features/documents/view/widgets/grid/document_grid_item.dart @@ -72,7 +72,6 @@ class DocumentGridItem extends StatelessWidget { TagsWidget( tagIds: document.tags, isMultiLine: false, - isSelectedPredicate: isTagSelectedPredicate, onTagSelected: onTagSelected, ), const Spacer(), diff --git a/lib/features/documents/view/widgets/list/document_list_item.dart b/lib/features/documents/view/widgets/list/document_list_item.dart index afe76b8..0c0490b 100644 --- a/lib/features/documents/view/widgets/list/document_list_item.dart +++ b/lib/features/documents/view/widgets/list/document_list_item.dart @@ -73,7 +73,6 @@ class DocumentListItem extends StatelessWidget { isClickable: isLabelClickable, tagIds: document.tags, isMultiLine: false, - isSelectedPredicate: isTagSelectedPredicate, onTagSelected: (id) => onTagSelected?.call(id), ), ), diff --git a/lib/features/edit_label/view/label_form.dart b/lib/features/edit_label/view/label_form.dart index 1ca0d25..b284b51 100644 --- a/lib/features/edit_label/view/label_form.dart +++ b/lib/features/edit_label/view/label_form.dart @@ -46,8 +46,17 @@ class LabelForm extends StatefulWidget { class _LabelFormState extends State> { final _formKey = GlobalKey(); + late bool _enableMatchFormField; + PaperlessValidationErrors _errors = {}; + @override + void initState() { + super.initState(); + _enableMatchFormField = + widget.initialValue?.matchingAlgorithm != MatchingAlgorithm.auto; + } + @override Widget build(BuildContext context) { return Scaffold( @@ -71,15 +80,6 @@ class _LabelFormState extends State> { initialValue: widget.initialValue?.name, onChanged: (val) => setState(() => _errors = {}), ), - FormBuilderTextField( - name: Label.matchKey, - decoration: InputDecoration( - labelText: S.of(context).labelMatchPropertyLabel, - errorText: _errors[Label.matchKey], - ), - initialValue: widget.initialValue?.match, - onChanged: (val) => setState(() => _errors = {}), - ), FormBuilderDropdown( name: Label.matchingAlgorithmKey, initialValue: widget.initialValue?.matchingAlgorithm.value ?? @@ -88,16 +88,32 @@ class _LabelFormState extends State> { labelText: S.of(context).labelMatchingAlgorithmPropertyLabel, errorText: _errors[Label.matchingAlgorithmKey], ), - onChanged: (val) => setState(() => _errors = {}), + onChanged: (val) { + setState(() { + _errors = {}; + _enableMatchFormField = val != MatchingAlgorithm.auto.value; + }); + }, items: MatchingAlgorithm.values .map( (algo) => DropdownMenuItem( - child: Text(translateMatchingAlgorithm(context, algo)), + child: Text( + translateMatchingAlgorithmDescription(context, algo)), value: algo.value, ), ) .toList(), ), + if (_enableMatchFormField) + FormBuilderTextField( + name: Label.matchKey, + decoration: InputDecoration( + labelText: S.of(context).labelMatchPropertyLabel, + errorText: _errors[Label.matchKey], + ), + initialValue: widget.initialValue?.match, + onChanged: (val) => setState(() => _errors = {}), + ), FormBuilderCheckbox( name: Label.isInsensitiveKey, initialValue: widget.initialValue?.isInsensitive ?? true, @@ -117,6 +133,11 @@ class _LabelFormState extends State> { ...widget.initialValue?.toJson() ?? {}, ..._formKey.currentState!.value }; + if (mergedJson[Label.matchingAlgorithmKey] == + MatchingAlgorithm.auto.value) { + // If auto is selected, the match will be removed. + mergedJson[Label.matchKey] = ''; + } final createdLabel = await widget.submitButtonConfig .onSubmit(widget.fromJsonT(mergedJson)); Navigator.pop(context, createdLabel); diff --git a/lib/features/home/view/widget/info_drawer.dart b/lib/features/home/view/widget/info_drawer.dart index 51e212a..2900dc6 100644 --- a/lib/features/home/view/widget/info_drawer.dart +++ b/lib/features/home/view/widget/info_drawer.dart @@ -219,7 +219,7 @@ class _InfoDrawerState extends State { ], ), decoration: BoxDecoration( - color: Theme.of(context).colorScheme.primaryContainer, + color: Theme.of(context).colorScheme.surfaceVariant, ), ), ...[ diff --git a/lib/features/inbox/view/widgets/inbox_item.dart b/lib/features/inbox/view/widgets/inbox_item.dart index 7502ef2..2367594 100644 --- a/lib/features/inbox/view/widgets/inbox_item.dart +++ b/lib/features/inbox/view/widgets/inbox_item.dart @@ -4,6 +4,7 @@ import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_mobile/core/repository/provider/label_repositories_provider.dart'; import 'package:paperless_mobile/core/repository/state/impl/correspondent_repository_state.dart'; import 'package:paperless_mobile/core/repository/state/impl/document_type_repository_state.dart'; +import 'package:paperless_mobile/core/workarounds/colored_chip.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.dart'; import 'package:paperless_mobile/features/document_details/bloc/document_details_cubit.dart'; import 'package:paperless_mobile/features/document_details/view/pages/document_details_page.dart'; @@ -111,21 +112,23 @@ class _InboxItemState extends State { final actions = [ _buildAssignAsnAction(chipShape, context), const SizedBox(width: 4.0), - ActionChip( - avatar: const Icon(Icons.delete_outline), - shape: chipShape, - label: const Text("Delete document"), - onPressed: () async { - final shouldDelete = await showDialog( - context: context, - builder: (context) => - DeleteDocumentConfirmationDialog(document: widget.document), - ) ?? - false; - if (shouldDelete) { - context.read().delete(widget.document); - } - }, + ColoredChipWrapper( + child: ActionChip( + avatar: const Icon(Icons.delete_outline), + shape: chipShape, + label: const Text("Delete document"), + onPressed: () async { + final shouldDelete = await showDialog( + context: context, + builder: (context) => DeleteDocumentConfirmationDialog( + document: widget.document), + ) ?? + false; + if (shouldDelete) { + context.read().delete(widget.document); + } + }, + ), ), ]; // return FutureBuilder( @@ -191,31 +194,33 @@ class _InboxItemState extends State { BuildContext context, ) { final hasAsn = widget.document.archiveSerialNumber != null; - return ActionChip( - avatar: _isAsnAssignLoading - ? const CircularProgressIndicator() - : hasAsn - ? null - : const Icon(Icons.archive_outlined), - shape: chipShape, - label: hasAsn - ? Text( - '${S.of(context).documentArchiveSerialNumberPropertyShortLabel} #${widget.document.archiveSerialNumber}', - ) - : const Text("Assign ASN"), - onPressed: !hasAsn - ? () { - setState(() { - _isAsnAssignLoading = true; - }); - context - .read() - .assignAsn(widget.document) - .whenComplete( - () => setState(() => _isAsnAssignLoading = false), - ); - } - : null, + return ColoredChipWrapper( + child: ActionChip( + avatar: _isAsnAssignLoading + ? const CircularProgressIndicator() + : hasAsn + ? null + : const Icon(Icons.archive_outlined), + shape: chipShape, + label: hasAsn + ? Text( + '${S.of(context).documentArchiveSerialNumberPropertyShortLabel} #${widget.document.archiveSerialNumber}', + ) + : const Text("Assign ASN"), + onPressed: !hasAsn + ? () { + setState(() { + _isAsnAssignLoading = true; + }); + context + .read() + .assignAsn(widget.document) + .whenComplete( + () => setState(() => _isAsnAssignLoading = false), + ); + } + : null, + ), ); } @@ -224,7 +229,6 @@ class _InboxItemState extends State { tagIds: widget.document.tags, isMultiLine: false, isClickable: false, - isSelectedPredicate: (_) => false, showShortNames: true, dense: true, ); diff --git a/lib/features/labels/storage_path/view/widgets/storage_path_autofill_form_builder_field.dart b/lib/features/labels/storage_path/view/widgets/storage_path_autofill_form_builder_field.dart index b415fcf..fad0335 100644 --- a/lib/features/labels/storage_path/view/widgets/storage_path_autofill_form_builder_field.dart +++ b/lib/features/labels/storage_path/view/widgets/storage_path_autofill_form_builder_field.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:paperless_mobile/core/workarounds/colored_chip.dart'; import 'package:paperless_mobile/generated/l10n.dart'; class StoragePathAutofillFormBuilderField extends StatefulWidget { @@ -61,71 +62,78 @@ class _StoragePathAutofillFormBuilderFieldState "Select to autofill path variable", style: Theme.of(context).textTheme.bodySmall, ), - Wrap( - alignment: WrapAlignment.start, - spacing: 4.0, - runSpacing: 4.0, - children: [ - InputChip( - label: Text( - S.of(context).documentArchiveSerialNumberPropertyLongLabel), - onPressed: () => _addParameterToInput("{asn}", field), - ), - InputChip( - label: Text(S.of(context).documentCorrespondentPropertyLabel), - onPressed: () => _addParameterToInput("{correspondent}", field), - ), - InputChip( - label: Text(S.of(context).documentDocumentTypePropertyLabel), - onPressed: () => _addParameterToInput("{document_type}", field), - ), - InputChip( - label: Text(S.of(context).documentTagsPropertyLabel), - onPressed: () => _addParameterToInput("{tag_list}", field), - ), - InputChip( - label: Text(S.of(context).documentTitlePropertyLabel), - onPressed: () => _addParameterToInput("{title}", field), - ), - InputChip( - label: Text(S.of(context).documentCreatedPropertyLabel), - onPressed: () => _addParameterToInput("{created}", field), - ), - InputChip( - label: Text(S.of(context).documentCreatedPropertyLabel + - " (${S.of(context).storagePathParameterYearLabel})"), - onPressed: () => _addParameterToInput("{created_year}", field), - ), - InputChip( - label: Text(S.of(context).documentCreatedPropertyLabel + - " (${S.of(context).storagePathParameterMonthLabel})"), - onPressed: () => _addParameterToInput("{created_month}", field), - ), - InputChip( - label: Text(S.of(context).documentCreatedPropertyLabel + - " (${S.of(context).storagePathParameterDayLabel})"), - onPressed: () => _addParameterToInput("{created_day}", field), - ), - InputChip( - label: Text(S.of(context).documentCreatedPropertyLabel), - onPressed: () => _addParameterToInput("{added}", field), - ), - InputChip( - label: Text(S.of(context).documentCreatedPropertyLabel + - " (${S.of(context).storagePathParameterYearLabel})"), - onPressed: () => _addParameterToInput("{added_year}", field), - ), - InputChip( - label: Text(S.of(context).documentCreatedPropertyLabel + - " (${S.of(context).storagePathParameterMonthLabel})"), - onPressed: () => _addParameterToInput("{added_month}", field), - ), - InputChip( - label: Text(S.of(context).documentCreatedPropertyLabel + - " (${S.of(context).storagePathParameterDayLabel})"), - onPressed: () => _addParameterToInput("{added_day}", field), - ), - ], + ColoredChipWrapper( + child: Wrap( + alignment: WrapAlignment.start, + spacing: 4.0, + runSpacing: 4.0, + children: [ + InputChip( + label: Text(S + .of(context) + .documentArchiveSerialNumberPropertyLongLabel), + onPressed: () => _addParameterToInput("{asn}", field), + ), + InputChip( + label: Text(S.of(context).documentCorrespondentPropertyLabel), + onPressed: () => + _addParameterToInput("{correspondent}", field), + ), + InputChip( + label: Text(S.of(context).documentDocumentTypePropertyLabel), + onPressed: () => + _addParameterToInput("{document_type}", field), + ), + InputChip( + label: Text(S.of(context).documentTagsPropertyLabel), + onPressed: () => _addParameterToInput("{tag_list}", field), + ), + InputChip( + label: Text(S.of(context).documentTitlePropertyLabel), + onPressed: () => _addParameterToInput("{title}", field), + ), + InputChip( + label: Text(S.of(context).documentCreatedPropertyLabel), + onPressed: () => _addParameterToInput("{created}", field), + ), + InputChip( + label: Text(S.of(context).documentCreatedPropertyLabel + + " (${S.of(context).storagePathParameterYearLabel})"), + onPressed: () => + _addParameterToInput("{created_year}", field), + ), + InputChip( + label: Text(S.of(context).documentCreatedPropertyLabel + + " (${S.of(context).storagePathParameterMonthLabel})"), + onPressed: () => + _addParameterToInput("{created_month}", field), + ), + InputChip( + label: Text(S.of(context).documentCreatedPropertyLabel + + " (${S.of(context).storagePathParameterDayLabel})"), + onPressed: () => _addParameterToInput("{created_day}", field), + ), + InputChip( + label: Text(S.of(context).documentCreatedPropertyLabel), + onPressed: () => _addParameterToInput("{added}", field), + ), + InputChip( + label: Text(S.of(context).documentCreatedPropertyLabel + + " (${S.of(context).storagePathParameterYearLabel})"), + onPressed: () => _addParameterToInput("{added_year}", field), + ), + InputChip( + label: Text(S.of(context).documentCreatedPropertyLabel + + " (${S.of(context).storagePathParameterMonthLabel})"), + onPressed: () => _addParameterToInput("{added_month}", field), + ), + InputChip( + label: Text(S.of(context).documentCreatedPropertyLabel + + " (${S.of(context).storagePathParameterDayLabel})"), + onPressed: () => _addParameterToInput("{added_day}", field), + ), + ], + ), ) ], ), diff --git a/lib/features/labels/tags/view/widgets/tag_widget.dart b/lib/features/labels/tags/view/widgets/tag_widget.dart index 4e21648..2b266f0 100644 --- a/lib/features/labels/tags/view/widgets/tag_widget.dart +++ b/lib/features/labels/tags/view/widgets/tag_widget.dart @@ -1,11 +1,10 @@ import 'package:flutter/material.dart'; import 'package:paperless_api/paperless_api.dart'; +import 'package:paperless_mobile/core/workarounds/colored_chip.dart'; class TagWidget extends StatelessWidget { final Tag tag; - final VoidCallback? afterTagTapped; final VoidCallback onSelected; - final bool isSelected; final bool isClickable; final bool showShortName; final bool dense; @@ -13,10 +12,8 @@ class TagWidget extends StatelessWidget { const TagWidget({ super.key, required this.tag, - required this.afterTagTapped, this.isClickable = true, required this.onSelected, - required this.isSelected, this.showShortName = false, this.dense = false, }); @@ -27,24 +24,25 @@ class TagWidget extends StatelessWidget { padding: const EdgeInsets.only(right: 4.0), child: AbsorbPointer( absorbing: !isClickable, - child: FilterChip( - labelPadding: - dense ? const EdgeInsets.symmetric(horizontal: 2) : null, - padding: dense ? const EdgeInsets.all(4) : null, - selected: isSelected, - selectedColor: tag.color, - onSelected: (_) => onSelected(), - visualDensity: const VisualDensity(vertical: -2), - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - label: Text( - showShortName && tag.name.length > 6 - ? '${tag.name.substring(0, 6)}...' - : tag.name, - style: TextStyle(color: tag.textColor), + child: ColoredChipWrapper( + child: FilterChip( + labelPadding: + dense ? const EdgeInsets.symmetric(horizontal: 2) : null, + padding: dense ? const EdgeInsets.all(4) : null, + selectedColor: tag.color, + onSelected: (_) => onSelected(), + visualDensity: const VisualDensity(vertical: -2), + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + label: Text( + showShortName && tag.name.length > 6 + ? '${tag.name.substring(0, 6)}...' + : tag.name, + style: TextStyle(color: tag.textColor), + ), + checkmarkColor: tag.textColor, + backgroundColor: tag.color, + side: BorderSide.none, ), - checkmarkColor: tag.textColor, - backgroundColor: tag.color, - side: BorderSide.none, ), ), ); diff --git a/lib/features/labels/tags/view/widgets/tags_form_field.dart b/lib/features/labels/tags/view/widgets/tags_form_field.dart index 2bf73ff..c1e2a4f 100644 --- a/lib/features/labels/tags/view/widgets/tags_form_field.dart +++ b/lib/features/labels/tags/view/widgets/tags_form_field.dart @@ -7,6 +7,7 @@ import 'package:flutter_typeahead/flutter_typeahead.dart'; import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_mobile/core/repository/label_repository.dart'; import 'package:paperless_mobile/core/repository/state/impl/tag_repository_state.dart'; +import 'package:paperless_mobile/core/workarounds/colored_chip.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.dart'; import 'package:paperless_mobile/features/edit_label/view/impl/add_tag_page.dart'; import 'package:paperless_mobile/generated/l10n.dart'; @@ -263,13 +264,15 @@ class _TagFormFieldState extends State { } Widget _buildNotAssignedTag(FormFieldState field) { - return InputChip( - label: Text( - S.of(context).labelNotAssignedText, + return ColoredChipWrapper( + child: InputChip( + label: Text( + S.of(context).labelNotAssignedText, + ), + backgroundColor: + Theme.of(context).colorScheme.onSurface.withOpacity(0.12), + onDeleted: () => field.didChange(const IdsTagsQuery()), ), - backgroundColor: - Theme.of(context).colorScheme.onSurface.withOpacity(0.12), - onDeleted: () => field.didChange(const IdsTagsQuery()), ); } @@ -283,33 +286,37 @@ class _TagFormFieldState extends State { if (tag == null) { return Container(); } - return InputChip( - label: Text( - tag.name, - style: TextStyle( - color: tag.textColor, - decorationColor: tag.textColor, - decoration: !isIncludedTag ? TextDecoration.lineThrough : null, - decorationThickness: 2.0, + return ColoredChipWrapper( + child: InputChip( + label: Text( + tag.name, + style: TextStyle( + color: tag.textColor, + decorationColor: tag.textColor, + decoration: !isIncludedTag ? TextDecoration.lineThrough : null, + decorationThickness: 2.0, + ), + ), + onPressed: widget.excludeAllowed + ? () => field.didChange(currentQuery.withIdQueryToggled(tag.id!)) + : null, + backgroundColor: tag.color, + deleteIconColor: tag.textColor, + onDeleted: () => field.didChange( + (field.value as IdsTagsQuery).withIdsRemoved([tag.id!]), ), - ), - onPressed: widget.excludeAllowed - ? () => field.didChange(currentQuery.withIdQueryToggled(tag.id!)) - : null, - backgroundColor: tag.color, - deleteIconColor: tag.textColor, - onDeleted: () => field.didChange( - (field.value as IdsTagsQuery).withIdsRemoved([tag.id!]), ), ); } Widget _buildAnyAssignedTag(FormFieldState field) { - return InputChip( - label: Text(S.of(context).labelAnyAssignedText), - backgroundColor: - Theme.of(context).colorScheme.onSurfaceVariant.withOpacity(0.12), - onDeleted: () => field.didChange(const IdsTagsQuery()), + return ColoredChipWrapper( + child: InputChip( + label: Text(S.of(context).labelAnyAssignedText), + backgroundColor: + Theme.of(context).colorScheme.onSurfaceVariant.withOpacity(0.12), + onDeleted: () => field.didChange(const IdsTagsQuery()), + ), ); } } diff --git a/lib/features/labels/tags/view/widgets/tags_widget.dart b/lib/features/labels/tags/view/widgets/tags_widget.dart index ce5a147..cd4d937 100644 --- a/lib/features/labels/tags/view/widgets/tags_widget.dart +++ b/lib/features/labels/tags/view/widgets/tags_widget.dart @@ -9,20 +9,16 @@ import 'package:paperless_mobile/features/labels/tags/view/widgets/tag_widget.da class TagsWidget extends StatelessWidget { final Iterable tagIds; final bool isMultiLine; - final VoidCallback? afterTagTapped; final void Function(int tagId)? onTagSelected; final bool isClickable; - final bool Function(int id) isSelectedPredicate; final bool showShortNames; final bool dense; const TagsWidget({ Key? key, required this.tagIds, - this.afterTagTapped, this.isMultiLine = true, this.isClickable = true, - required this.isSelectedPredicate, this.onTagSelected, this.showShortNames = false, this.dense = true, @@ -38,9 +34,7 @@ class TagsWidget extends StatelessWidget { .map( (id) => TagWidget( tag: state.getLabel(id)!, - afterTagTapped: afterTagTapped, isClickable: isClickable, - isSelected: isSelectedPredicate(id), onSelected: () => onTagSelected?.call(id), showShortName: showShortNames, dense: dense, diff --git a/lib/features/labels/view/pages/labels_page.dart b/lib/features/labels/view/pages/labels_page.dart index 7c05c57..480ebb6 100644 --- a/lib/features/labels/view/pages/labels_page.dart +++ b/lib/features/labels/view/pages/labels_page.dart @@ -174,7 +174,6 @@ class _LabelsPageState extends State ) : null, ), - contentBuilder: (t) => Text(t.match ?? ''), emptyStateActionButtonLabel: S.of(context).labelsPageTagsEmptyStateAddNewLabel, emptyStateDescription: diff --git a/lib/features/labels/view/widgets/label_tab_view.dart b/lib/features/labels/view/widgets/label_tab_view.dart index 2f2628e..76133a6 100644 --- a/lib/features/labels/view/widgets/label_tab_view.dart +++ b/lib/features/labels/view/widgets/label_tab_view.dart @@ -68,21 +68,22 @@ class LabelTabView extends StatelessWidget { connectivityState.isConnected, child: ListView( children: labels - .map( - (l) => LabelItem( - name: l.name, - content: contentBuilder?.call(l) ?? - Text( - "${translateMatchingAlgorithm(context, l.matchingAlgorithm)}\n" - "${l.match}", - maxLines: 2, - ), - onOpenEditPage: onEdit, - filterBuilder: filterBuilder, - leading: leadingBuilder?.call(l), - label: l, - ), - ) + .map((l) => LabelItem( + name: l.name, + content: contentBuilder?.call(l) ?? + Text( + translateMatchingAlgorithmName( + context, l.matchingAlgorithm) + + ((l.match?.isNotEmpty ?? false) + ? ": ${l.match}" + : ""), + maxLines: 2, + ), + onOpenEditPage: onEdit, + filterBuilder: filterBuilder, + leading: leadingBuilder?.call(l), + label: l, + )) .toList(), ), ); diff --git a/lib/features/saved_view/view/saved_view_selection_widget.dart b/lib/features/saved_view/view/saved_view_selection_widget.dart index 29da7b6..0d96af6 100644 --- a/lib/features/saved_view/view/saved_view_selection_widget.dart +++ b/lib/features/saved_view/view/saved_view_selection_widget.dart @@ -182,15 +182,15 @@ class SavedViewSelectionWidget extends StatelessWidget { } void _onSelected( - bool isSelected, + bool selectionIntent, BuildContext context, SavedView view, ) async { - if (isSelected) { + if (selectionIntent) { context.read().selectView(view.id!); } else { - context.read().resetFilter(); context.read().unselectView(); + context.read().resetFilter(); } } diff --git a/lib/l10n/intl_cs.arb b/lib/l10n/intl_cs.arb index b8d70bc..8cacf39 100644 --- a/lib/l10n/intl_cs.arb +++ b/lib/l10n/intl_cs.arb @@ -1,6 +1,6 @@ { "@@locale": "cs", - "aboutDialogDevelopedByText": "Vyvíjí", + "aboutDialogDevelopedByText": "Vyvíjí {name}", "@aboutDialogDevelopedByText": { "placeholders": { "name": {} @@ -490,16 +490,28 @@ "@loginPageUsernameValidatorMessageText": {}, "matchingAlgorithmAllDescription": "", "@matchingAlgorithmAllDescription": {}, + "matchingAlgorithmAllName": "", + "@matchingAlgorithmAllName": {}, "matchingAlgorithmAnyDescription": "", "@matchingAlgorithmAnyDescription": {}, + "matchingAlgorithmAnyName": "", + "@matchingAlgorithmAnyName": {}, "matchingAlgorithmAutoDescription": "", "@matchingAlgorithmAutoDescription": {}, + "matchingAlgorithmAutoName": "", + "@matchingAlgorithmAutoName": {}, "matchingAlgorithmExactDescription": "", "@matchingAlgorithmExactDescription": {}, + "matchingAlgorithmExactName": "", + "@matchingAlgorithmExactName": {}, "matchingAlgorithmFuzzyDescription": "", "@matchingAlgorithmFuzzyDescription": {}, + "matchingAlgorithmFuzzyName": "", + "@matchingAlgorithmFuzzyName": {}, "matchingAlgorithmRegexDescription": "", "@matchingAlgorithmRegexDescription": {}, + "matchingAlgorithmRegexName": "", + "@matchingAlgorithmRegexName": {}, "offlineWidgetText": "Nezdařilo se vytvořit připojení k internetu.", "@offlineWidgetText": {}, "onboardingDoneButtonLabel": "Hotovo", diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index 15267ad..203078e 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -1,6 +1,6 @@ { "@@locale": "de", - "aboutDialogDevelopedByText": "Entwickelt von", + "aboutDialogDevelopedByText": "Entwickelt von {name}", "@aboutDialogDevelopedByText": { "placeholders": { "name": {} @@ -488,18 +488,30 @@ "@loginPageUsernameLabel": {}, "loginPageUsernameValidatorMessageText": "Nutzername darf nicht leer sein.", "@loginPageUsernameValidatorMessageText": {}, - "matchingAlgorithmAllDescription": "Alle: Dokument enthält alle folgenden Wörter", + "matchingAlgorithmAllDescription": "Dokument enthält alle folgenden Wörter", "@matchingAlgorithmAllDescription": {}, - "matchingAlgorithmAnyDescription": "Irgendein Wort: Dokument enthält eins der folgenden Wörter", + "matchingAlgorithmAllName": "Alle", + "@matchingAlgorithmAllName": {}, + "matchingAlgorithmAnyDescription": "Dokument enthält eins der folgenden Wörter", "@matchingAlgorithmAnyDescription": {}, - "matchingAlgorithmAutoDescription": "Auto: Zuweisung automatisch erlernen", + "matchingAlgorithmAnyName": "Irgendein Wort", + "@matchingAlgorithmAnyName": {}, + "matchingAlgorithmAutoDescription": "Zuweisung automatisch erlernen", "@matchingAlgorithmAutoDescription": {}, - "matchingAlgorithmExactDescription": "Exakt: Dokument enthält die folgende Zeichenkette", + "matchingAlgorithmAutoName": "Auto", + "@matchingAlgorithmAutoName": {}, + "matchingAlgorithmExactDescription": "Dokument enthält die folgende Zeichenkette", "@matchingAlgorithmExactDescription": {}, - "matchingAlgorithmFuzzyDescription": "Ungenau: Dokument enthält ein zum folgenden Wort ähnliches Wort", + "matchingAlgorithmExactName": "Exakt", + "@matchingAlgorithmExactName": {}, + "matchingAlgorithmFuzzyDescription": "Dokument enthält ein zum folgenden Wort ähnliches Wort", "@matchingAlgorithmFuzzyDescription": {}, - "matchingAlgorithmRegexDescription": "Regulärer Ausdruck: Dokument passt zum folgenden Ausdruck", + "matchingAlgorithmFuzzyName": "Ungenau", + "@matchingAlgorithmFuzzyName": {}, + "matchingAlgorithmRegexDescription": "Dokument passt zum folgenden Ausdruck", "@matchingAlgorithmRegexDescription": {}, + "matchingAlgorithmRegexName": "Regulärer Ausdruck", + "@matchingAlgorithmRegexName": {}, "offlineWidgetText": "Es konte keine Verbindung zum Internet hergestellt werden.", "@offlineWidgetText": {}, "onboardingDoneButtonLabel": "Fertig", diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 902171a..5c2c4ee 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -488,18 +488,30 @@ "@loginPageUsernameLabel": {}, "loginPageUsernameValidatorMessageText": "Username must not be empty.", "@loginPageUsernameValidatorMessageText": {}, - "matchingAlgorithmAllDescription": "All: Document contains all of these words", + "matchingAlgorithmAllDescription": "Document contains all of these words", "@matchingAlgorithmAllDescription": {}, - "matchingAlgorithmAnyDescription": "Any: Document contains any of these words", + "matchingAlgorithmAllName": "All", + "@matchingAlgorithmAllName": {}, + "matchingAlgorithmAnyDescription": "Document contains any of these words", "@matchingAlgorithmAnyDescription": {}, - "matchingAlgorithmAutoDescription": "Auto: Learn matching automatically", + "matchingAlgorithmAnyName": "Any", + "@matchingAlgorithmAnyName": {}, + "matchingAlgorithmAutoDescription": "Learn matching automatically", "@matchingAlgorithmAutoDescription": {}, - "matchingAlgorithmExactDescription": "Exact: Document contains this string", + "matchingAlgorithmAutoName": "Auto", + "@matchingAlgorithmAutoName": {}, + "matchingAlgorithmExactDescription": "Document contains this string", "@matchingAlgorithmExactDescription": {}, - "matchingAlgorithmFuzzyDescription": "Fuzzy: Document contains a word similar to this word", + "matchingAlgorithmExactName": "Exact", + "@matchingAlgorithmExactName": {}, + "matchingAlgorithmFuzzyDescription": "Document contains a word similar to this word", "@matchingAlgorithmFuzzyDescription": {}, - "matchingAlgorithmRegexDescription": "Regular Expression: Document matches this regular expression", + "matchingAlgorithmFuzzyName": "Fuzzy", + "@matchingAlgorithmFuzzyName": {}, + "matchingAlgorithmRegexDescription": "Document matches this regular expression", "@matchingAlgorithmRegexDescription": {}, + "matchingAlgorithmRegexName": "Regular Expression", + "@matchingAlgorithmRegexName": {}, "offlineWidgetText": "An internet connection could not be established.", "@offlineWidgetText": {}, "onboardingDoneButtonLabel": "Done", diff --git a/lib/l10n/intl_tr.arb b/lib/l10n/intl_tr.arb index 9f48598..0d0ca6b 100644 --- a/lib/l10n/intl_tr.arb +++ b/lib/l10n/intl_tr.arb @@ -490,16 +490,28 @@ "@loginPageUsernameValidatorMessageText": {}, "matchingAlgorithmAllDescription": "", "@matchingAlgorithmAllDescription": {}, + "matchingAlgorithmAllName": "", + "@matchingAlgorithmAllName": {}, "matchingAlgorithmAnyDescription": "", "@matchingAlgorithmAnyDescription": {}, + "matchingAlgorithmAnyName": "", + "@matchingAlgorithmAnyName": {}, "matchingAlgorithmAutoDescription": "", "@matchingAlgorithmAutoDescription": {}, + "matchingAlgorithmAutoName": "", + "@matchingAlgorithmAutoName": {}, "matchingAlgorithmExactDescription": "", "@matchingAlgorithmExactDescription": {}, + "matchingAlgorithmExactName": "", + "@matchingAlgorithmExactName": {}, "matchingAlgorithmFuzzyDescription": "", "@matchingAlgorithmFuzzyDescription": {}, + "matchingAlgorithmFuzzyName": "", + "@matchingAlgorithmFuzzyName": {}, "matchingAlgorithmRegexDescription": "", "@matchingAlgorithmRegexDescription": {}, + "matchingAlgorithmRegexName": "", + "@matchingAlgorithmRegexName": {}, "offlineWidgetText": "İnternet bağlantısı kurulamadı.", "@offlineWidgetText": {}, "onboardingDoneButtonLabel": "Bitti", diff --git a/lib/main.dart b/lib/main.dart index 5c302a8..e4156b2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -204,54 +204,20 @@ class PaperlessMobileEntrypoint extends StatefulWidget { } class _PaperlessMobileEntrypointState extends State { - final _lightTheme = ThemeData( - brightness: Brightness.light, + final _lightTheme = ThemeData.from( + colorScheme: ColorScheme.fromSeed( + seedColor: Colors.lightGreen, + brightness: Brightness.light, + ), useMaterial3: true, - colorSchemeSeed: Colors.lightGreen, - appBarTheme: const AppBarTheme( - scrolledUnderElevation: 0.0, - ), - inputDecorationTheme: InputDecorationTheme( - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(16), - ), - contentPadding: const EdgeInsets.symmetric( - horizontal: 16.0, - vertical: 16.0, - ), - ), - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - chipTheme: ChipThemeData( - backgroundColor: Colors.lightGreen[50], - ), - listTileTheme: const ListTileThemeData( - tileColor: Colors.transparent, - ), ); - final _darkTheme = ThemeData( - brightness: Brightness.dark, + final _darkTheme = ThemeData.from( + colorScheme: ColorScheme.fromSeed( + seedColor: Colors.lightGreen, + brightness: Brightness.dark, + ), useMaterial3: true, - colorSchemeSeed: Colors.lightGreen, - appBarTheme: const AppBarTheme( - scrolledUnderElevation: 0.0, - ), - inputDecorationTheme: InputDecorationTheme( - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(16), - ), - contentPadding: const EdgeInsets.symmetric( - horizontal: 16.0, - vertical: 16.0, - ), - ), - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - chipTheme: ChipThemeData( - backgroundColor: Colors.green[900], - ), - listTileTheme: const ListTileThemeData( - tileColor: Colors.transparent, - ), ); @override @@ -269,8 +235,39 @@ class _PaperlessMobileEntrypointState extends State { return MaterialApp( debugShowCheckedModeBanner: true, title: "Paperless Mobile", - theme: _lightTheme, - darkTheme: _darkTheme, + theme: _lightTheme.copyWith( + inputDecorationTheme: InputDecorationTheme( + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + ), + contentPadding: const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 16.0, + ), + ), + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + listTileTheme: const ListTileThemeData( + tileColor: Colors.transparent, + ), + ), + darkTheme: _darkTheme.copyWith( + inputDecorationTheme: InputDecorationTheme( + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + ), + contentPadding: const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 16.0, + ), + ), + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + chipTheme: ChipThemeData( + backgroundColor: Colors.lightGreen[50], + ), + listTileTheme: const ListTileThemeData( + tileColor: Colors.transparent, + ), + ), themeMode: settings.preferredThemeMode, supportedLocales: S.delegate.supportedLocales, locale: Locale.fromSubtags(