mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2025-12-09 22:07:53 -06:00
Initial commit
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_paperless_mobile/features/labels/storage_path/bloc/storage_path_cubit.dart';
|
||||
import 'package:flutter_paperless_mobile/features/labels/storage_path/model/storage_path.model.dart';
|
||||
import 'package:flutter_paperless_mobile/features/labels/storage_path/view/widgets/storage_path_autofill_form_builder_field.dart';
|
||||
import 'package:flutter_paperless_mobile/features/labels/view/pages/add_label_page.dart';
|
||||
import 'package:flutter_paperless_mobile/generated/l10n.dart';
|
||||
|
||||
class AddStoragePathPage extends StatelessWidget {
|
||||
final String? initalValue;
|
||||
const AddStoragePathPage({Key? key, this.initalValue}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AddLabelPage<StoragePath>(
|
||||
addLabelStr: S.of(context).addStoragePathPageTitle,
|
||||
fromJson: StoragePath.fromJson,
|
||||
cubit: BlocProvider.of<StoragePathCubit>(context),
|
||||
initialName: initalValue,
|
||||
additionalFields: const [
|
||||
StoragePathAutofillFormBuilderField(name: StoragePath.pathKey),
|
||||
SizedBox(height: 120.0),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_paperless_mobile/core/logic/error_code_localization_mapper.dart';
|
||||
import 'package:flutter_paperless_mobile/core/model/error_message.dart';
|
||||
import 'package:flutter_paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||
import 'package:flutter_paperless_mobile/features/documents/model/query_parameters/storage_path_query.dart';
|
||||
import 'package:flutter_paperless_mobile/features/labels/storage_path/bloc/storage_path_cubit.dart';
|
||||
import 'package:flutter_paperless_mobile/features/labels/storage_path/model/storage_path.model.dart';
|
||||
import 'package:flutter_paperless_mobile/features/labels/storage_path/view/widgets/storage_path_autofill_form_builder_field.dart';
|
||||
import 'package:flutter_paperless_mobile/features/labels/view/pages/edit_label_page.dart';
|
||||
import 'package:flutter_paperless_mobile/util.dart';
|
||||
|
||||
class EditStoragePathPage extends StatelessWidget {
|
||||
final StoragePath storagePath;
|
||||
const EditStoragePathPage({super.key, required this.storagePath});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return EditLabelPage<StoragePath>(
|
||||
label: storagePath,
|
||||
onSubmit: BlocProvider.of<StoragePathCubit>(context).replace,
|
||||
onDelete: (correspondent) => _onDelete(correspondent, context),
|
||||
fromJson: StoragePath.fromJson,
|
||||
additionalFields: [
|
||||
StoragePathAutofillFormBuilderField(
|
||||
name: StoragePath.pathKey,
|
||||
initialValue: storagePath.path,
|
||||
),
|
||||
const SizedBox(height: 120.0),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onDelete(StoragePath path, BuildContext context) async {
|
||||
try {
|
||||
await BlocProvider.of<StoragePathCubit>(context).remove(path);
|
||||
final cubit = BlocProvider.of<DocumentsCubit>(context);
|
||||
if (cubit.state.filter.storagePath.id == path.id) {
|
||||
cubit.updateFilter(
|
||||
filter: cubit.state.filter.copyWith(storagePath: const StoragePathQuery.unset()));
|
||||
}
|
||||
} on ErrorMessage catch (e) {
|
||||
showSnackBar(context, translateError(context, e.code));
|
||||
} finally {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/src/widgets/container.dart';
|
||||
import 'package:flutter/src/widgets/framework.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:flutter_paperless_mobile/features/labels/storage_path/model/storage_path.model.dart';
|
||||
import 'package:flutter_paperless_mobile/generated/l10n.dart';
|
||||
import 'package:form_builder_validators/form_builder_validators.dart';
|
||||
|
||||
class StoragePathAutofillFormBuilderField extends StatefulWidget {
|
||||
final String name;
|
||||
final String? initialValue;
|
||||
const StoragePathAutofillFormBuilderField({
|
||||
super.key,
|
||||
required this.name,
|
||||
this.initialValue,
|
||||
});
|
||||
|
||||
@override
|
||||
State<StoragePathAutofillFormBuilderField> createState() =>
|
||||
_StoragePathAutofillFormBuilderFieldState();
|
||||
}
|
||||
|
||||
class _StoragePathAutofillFormBuilderFieldState extends State<StoragePathAutofillFormBuilderField> {
|
||||
late final TextEditingController _textEditingController;
|
||||
|
||||
late String _exampleOutput;
|
||||
late bool _showClearIcon;
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_textEditingController = TextEditingController.fromValue(
|
||||
TextEditingValue(text: widget.initialValue ?? ''),
|
||||
)..addListener(() {
|
||||
setState(() {
|
||||
_showClearIcon = _textEditingController.text.isNotEmpty;
|
||||
});
|
||||
});
|
||||
_exampleOutput = _buildExampleOutput(widget.initialValue ?? '');
|
||||
_showClearIcon = widget.initialValue?.isNotEmpty ?? false;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FormBuilderField<String>(
|
||||
name: widget.name,
|
||||
initialValue: widget.initialValue ?? '',
|
||||
builder: (field) => Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TextFormField(
|
||||
controller: _textEditingController,
|
||||
validator: FormBuilderValidators.required(), //TODO: INTL
|
||||
decoration: InputDecoration(
|
||||
label: Text(S.of(context).documentStoragePathPropertyLabel),
|
||||
suffixIcon: _showClearIcon
|
||||
? IconButton(
|
||||
icon: const Icon(Icons.clear),
|
||||
onPressed: () => _resetfield(field),
|
||||
)
|
||||
: null),
|
||||
onChanged: field.didChange,
|
||||
),
|
||||
const SizedBox(height: 8.0),
|
||||
Text(
|
||||
"Select to autofill path variable",
|
||||
style: Theme.of(context).textTheme.caption,
|
||||
),
|
||||
Wrap(
|
||||
alignment: WrapAlignment.start,
|
||||
spacing: 8.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),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _addParameterToInput(String param, FormFieldState<String> field) {
|
||||
final text = (field.value ?? "") + param;
|
||||
field.didChange(text);
|
||||
_textEditingController.text = text;
|
||||
}
|
||||
|
||||
String _buildExampleOutput(String input) {
|
||||
return input
|
||||
.replaceAll("{asn}", "1234")
|
||||
.replaceAll("{correspondent}", "My Bank")
|
||||
.replaceAll("{document_type}", "Invoice")
|
||||
.replaceAll("{tag_list}", "TODO,University,Work")
|
||||
.replaceAll("{created}", "2020-02-10")
|
||||
.replaceAll("{created_year}", "2020")
|
||||
.replaceAll("{created_month}", "02")
|
||||
.replaceAll("{created_day}", "10")
|
||||
.replaceAll("{added}", "2029-12-24")
|
||||
.replaceAll("{added_year}", "2029")
|
||||
.replaceAll("{added_month}", "12")
|
||||
.replaceAll("{added_day}", "24");
|
||||
}
|
||||
|
||||
void _resetfield(FormFieldState<String> field) {
|
||||
field.didChange("");
|
||||
_textEditingController.clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_paperless_mobile/di_initializer.dart';
|
||||
import 'package:flutter_paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||
import 'package:flutter_paperless_mobile/features/documents/model/query_parameters/storage_path_query.dart';
|
||||
import 'package:flutter_paperless_mobile/features/labels/storage_path/bloc/storage_path_cubit.dart';
|
||||
import 'package:flutter_paperless_mobile/features/labels/storage_path/model/storage_path.model.dart';
|
||||
|
||||
class StoragePathWidget extends StatelessWidget {
|
||||
final int? pathId;
|
||||
final void Function()? afterSelected;
|
||||
final Color? textColor;
|
||||
final bool isClickable;
|
||||
|
||||
const StoragePathWidget({
|
||||
Key? key,
|
||||
required this.pathId,
|
||||
this.afterSelected,
|
||||
this.textColor,
|
||||
this.isClickable = true,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AbsorbPointer(
|
||||
absorbing: !isClickable,
|
||||
child: BlocBuilder<StoragePathCubit, Map<int, StoragePath>>(
|
||||
builder: (context, state) {
|
||||
return GestureDetector(
|
||||
onTap: () => _addStoragePathToFilter(context),
|
||||
child: Text(
|
||||
(state[pathId]?.name) ?? "-",
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
color: textColor ?? Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _addStoragePathToFilter(BuildContext context) {
|
||||
final cubit = getIt<DocumentsCubit>();
|
||||
if (cubit.state.filter.correspondent.id == pathId) {
|
||||
cubit.updateFilter(
|
||||
filter: cubit.state.filter.copyWith(storagePath: const StoragePathQuery.unset()));
|
||||
} else {
|
||||
cubit.updateFilter(
|
||||
filter: cubit.state.filter.copyWith(
|
||||
storagePath: StoragePathQuery.fromId(pathId),
|
||||
),
|
||||
);
|
||||
}
|
||||
afterSelected?.call();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user