mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2025-12-10 16:07:58 -06:00
Merged develop into main
This commit is contained in:
17
lib/core/bloc/paperless_server_information_cubit.dart
Normal file
17
lib/core/bloc/paperless_server_information_cubit.dart
Normal file
@@ -0,0 +1,17 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:paperless_mobile/core/model/paperless_server_information.dart';
|
||||
import 'package:paperless_mobile/core/service/paperless_server_information_service.dart';
|
||||
|
||||
@singleton
|
||||
class PaperlessServerInformationCubit
|
||||
extends Cubit<PaperlessServerInformation> {
|
||||
final PaperlessServerInformationService service;
|
||||
|
||||
PaperlessServerInformationCubit(this.service)
|
||||
: super(PaperlessServerInformation());
|
||||
|
||||
Future<void> updateStatus() async {
|
||||
emit(await service.getInformation());
|
||||
}
|
||||
}
|
||||
9
lib/core/model/github_error_report.model.dart
Normal file
9
lib/core/model/github_error_report.model.dart
Normal file
@@ -0,0 +1,9 @@
|
||||
class GithubErrorReport {
|
||||
final String? shortDescription;
|
||||
final String? longDescription;
|
||||
|
||||
GithubErrorReport({
|
||||
this.shortDescription,
|
||||
this.longDescription,
|
||||
});
|
||||
}
|
||||
15
lib/core/model/paperless_server_information.dart
Normal file
15
lib/core/model/paperless_server_information.dart
Normal file
@@ -0,0 +1,15 @@
|
||||
class PaperlessServerInformation {
|
||||
static const String versionHeader = 'x-version';
|
||||
static const String apiVersionHeader = 'x-api-version';
|
||||
static const String hostHeader = 'x-served-by';
|
||||
final String? version;
|
||||
final int? apiVersion;
|
||||
final String? username;
|
||||
final String? host;
|
||||
PaperlessServerInformation({
|
||||
this.host,
|
||||
this.username,
|
||||
this.version = 'unknown',
|
||||
this.apiVersion = 1,
|
||||
});
|
||||
}
|
||||
61
lib/core/service/github_issue_service.dart
Normal file
61
lib/core/service/github_issue_service.dart
Normal file
@@ -0,0 +1,61 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:paperless_mobile/core/model/github_error_report.model.dart';
|
||||
import 'package:paperless_mobile/core/widgets/error_report_page.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:paperless_mobile/extensions/dart_extensions.dart';
|
||||
|
||||
class GithubIssueService {
|
||||
static void openCreateGithubIssue({
|
||||
String? title,
|
||||
String? body,
|
||||
List<String>? labels,
|
||||
String? milestone,
|
||||
List<String>? assignees,
|
||||
String? project,
|
||||
}) {
|
||||
final Uri uri = Uri(
|
||||
scheme: "https",
|
||||
host: "github.com",
|
||||
path: "astubenbord/paperless-mobile/issues/new",
|
||||
queryParameters: {}
|
||||
..tryPutIfAbsent('title', () => title)
|
||||
//..tryPutIfAbsent('body', () => body) //TODO: Figure out how to pass long body via url
|
||||
..tryPutIfAbsent('labels', () => labels?.join(','))
|
||||
..tryPutIfAbsent('milestone', () => milestone)
|
||||
..tryPutIfAbsent('assignees', () => assignees?.join(','))
|
||||
..tryPutIfAbsent('project', () => project),
|
||||
);
|
||||
log("[GitHubIssueService] Creating GitHub issue: " + uri.toString());
|
||||
launchUrl(
|
||||
uri,
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
}
|
||||
|
||||
static void createIssueFromError(
|
||||
BuildContext context, {
|
||||
StackTrace? stackTrace,
|
||||
}) async {
|
||||
final errorDescription = await Navigator.push<GithubErrorReport>(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => ErrorReportPage(
|
||||
stackTrace: stackTrace,
|
||||
),
|
||||
),
|
||||
);
|
||||
if (errorDescription == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
return openCreateGithubIssue(
|
||||
title: errorDescription.shortDescription,
|
||||
body: errorDescription.longDescription ?? '',
|
||||
labels: ['error report'],
|
||||
);
|
||||
}
|
||||
}
|
||||
35
lib/core/service/paperless_server_information_service.dart
Normal file
35
lib/core/service/paperless_server_information_service.dart
Normal file
@@ -0,0 +1,35 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:http/http.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:paperless_mobile/core/model/paperless_server_information.dart';
|
||||
import 'package:paperless_mobile/core/store/local_vault.dart';
|
||||
|
||||
@injectable
|
||||
class PaperlessServerInformationService {
|
||||
final BaseClient client;
|
||||
final LocalVault localStore;
|
||||
|
||||
PaperlessServerInformationService(
|
||||
@Named("timeoutClient") this.client,
|
||||
this.localStore,
|
||||
);
|
||||
|
||||
Future<PaperlessServerInformation> getInformation() async {
|
||||
final response = await client.get(Uri.parse("/api/ui_settings/"));
|
||||
final version =
|
||||
response.headers[PaperlessServerInformation.versionHeader] ?? 'unknown';
|
||||
final apiVersion = int.tryParse(
|
||||
response.headers[PaperlessServerInformation.apiVersionHeader] ?? '1');
|
||||
final String username =
|
||||
jsonDecode(utf8.decode(response.bodyBytes))['username'];
|
||||
final String? host =
|
||||
response.headers[PaperlessServerInformation.hostHeader];
|
||||
return PaperlessServerInformation(
|
||||
username: username,
|
||||
version: version,
|
||||
apiVersion: apiVersion,
|
||||
host: host,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:paperless_mobile/core/bloc/document_status_cubit.dart';
|
||||
import 'package:paperless_mobile/core/model/document_processing_status.dart';
|
||||
import 'package:paperless_mobile/di_initializer.dart';
|
||||
@@ -9,7 +10,6 @@ import 'package:paperless_mobile/features/documents/model/document.model.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/paged_search_result.dart';
|
||||
import 'package:paperless_mobile/features/login/model/authentication_information.dart';
|
||||
import 'package:paperless_mobile/util.dart';
|
||||
import 'package:http_interceptor/http_interceptor.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:web_socket_channel/io.dart';
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ Future<T> getSingleResult<T>(
|
||||
jsonDecode(utf8.decode(response.bodyBytes)) as JSON,
|
||||
);
|
||||
}
|
||||
return Future.error(errorCode);
|
||||
throw ErrorMessage(errorCode);
|
||||
}
|
||||
|
||||
Future<List<T>> getCollection<T>(
|
||||
@@ -52,7 +52,7 @@ Future<List<T>> getCollection<T>(
|
||||
}
|
||||
}
|
||||
}
|
||||
return Future.error(errorCode);
|
||||
throw ErrorMessage(errorCode);
|
||||
}
|
||||
|
||||
List<T> _collectionFromJson<T>(
|
||||
|
||||
164
lib/core/widgets/error_report_page.dart
Normal file
164
lib/core/widgets/error_report_page.dart
Normal file
@@ -0,0 +1,164 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.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:form_builder_validators/form_builder_validators.dart';
|
||||
import 'package:paperless_mobile/core/model/github_error_report.model.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
|
||||
class ErrorReportPage extends StatefulWidget {
|
||||
final StackTrace? stackTrace;
|
||||
const ErrorReportPage({super.key, this.stackTrace});
|
||||
|
||||
@override
|
||||
State<ErrorReportPage> createState() => _ErrorReportPageState();
|
||||
}
|
||||
|
||||
class _ErrorReportPageState extends State<ErrorReportPage> {
|
||||
final GlobalKey<FormBuilderState> _formKey = GlobalKey();
|
||||
|
||||
static const String shortDescriptionKey = "shortDescription";
|
||||
static const String longDescriptionKey = "longDescription";
|
||||
|
||||
bool _stackTraceCopied = false;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
resizeToAvoidBottomInset: true,
|
||||
appBar: AppBar(
|
||||
title: Text("Report error"),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: _onSubmit,
|
||||
child: Text("Submit"),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: FormBuilder(
|
||||
key: _formKey,
|
||||
child: ListView(
|
||||
children: [
|
||||
Text(
|
||||
"""Oops, an error has occurred!
|
||||
In order to improve the app and prevent messages like these, it is greatly appreciated if you report this error with a description of what happened and the actions leading up to this window.
|
||||
Please fill the fields below and create a new issue in GitHub. Thanks!
|
||||
Note: If you have the GitHub Android app installed, the descriptions will not be taken into account! Skip these here and fill them in the GitHub issues form after submitting this report.""",
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
).padded(),
|
||||
Text(
|
||||
"Description",
|
||||
style: Theme.of(context).textTheme.subtitle1,
|
||||
).padded(),
|
||||
FormBuilderTextField(
|
||||
name: shortDescriptionKey,
|
||||
decoration: const InputDecoration(
|
||||
label: Text("Short Description"),
|
||||
hintText:
|
||||
"Please provide a brief description of what went wrong."),
|
||||
).padded(),
|
||||
FormBuilderTextField(
|
||||
name: shortDescriptionKey,
|
||||
maxLines: null,
|
||||
keyboardType: TextInputType.multiline,
|
||||
decoration: const InputDecoration(
|
||||
label: Text("Detailled Description"),
|
||||
hintText:
|
||||
"Please describe the exact actions taken that caused this error. Provide as much details as possible.",
|
||||
),
|
||||
).padded(),
|
||||
if (widget.stackTrace != null) ...[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Stack Trace",
|
||||
style: Theme.of(context).textTheme.subtitle1,
|
||||
).padded(
|
||||
const EdgeInsets.only(top: 8.0, left: 8.0, right: 8.0)),
|
||||
TextButton.icon(
|
||||
label: const Text("Copy"),
|
||||
icon: const Icon(Icons.copy),
|
||||
onPressed: _copyStackTrace,
|
||||
),
|
||||
],
|
||||
),
|
||||
Text(
|
||||
"Since stack traces cannot be attached to the GitHub issue url, please copy the content of the stackTrace and paste it in the issue description. This will greatly increase the chance of quickly resolving the issue!",
|
||||
style: Theme.of(context).textTheme.caption,
|
||||
).padded(),
|
||||
Text(
|
||||
widget.stackTrace.toString(),
|
||||
style: Theme.of(context).textTheme.overline,
|
||||
).padded(),
|
||||
]
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _copyStackTrace() {
|
||||
Clipboard.setData(
|
||||
ClipboardData(text: '```${widget.stackTrace.toString()}```'),
|
||||
).then(
|
||||
(_) {
|
||||
setState(() => _stackTraceCopied = true);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text(
|
||||
"Stack trace copied to clipboard.",
|
||||
),
|
||||
duration: Duration(seconds: 2),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _onSubmit() async {
|
||||
if (_formKey.currentState?.saveAndValidate() ?? false) {
|
||||
final fk = _formKey.currentState!.value;
|
||||
if (!_stackTraceCopied) {
|
||||
final continueSubmission = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text("Continue without stack trace?"),
|
||||
content: const Text(
|
||||
"It seems you have not yet copied the stack trace. The stack trace provides valuable insights into where an error came from and how it could be fixed. Are you sure you want to continue without providing the stack trace?",
|
||||
),
|
||||
actionsAlignment: MainAxisAlignment.end,
|
||||
actions: [
|
||||
TextButton(
|
||||
child: const Text("Yes, continue"),
|
||||
onPressed: () => Navigator.pop(context, true),
|
||||
),
|
||||
TextButton(
|
||||
child: const Text("No, copy stack trace"),
|
||||
onPressed: () {
|
||||
_copyStackTrace();
|
||||
Navigator.pop(context, true);
|
||||
},
|
||||
),
|
||||
TextButton(
|
||||
child: const Text("Cancel"),
|
||||
onPressed: () => Navigator.pop(context, false),
|
||||
),
|
||||
],
|
||||
),
|
||||
) ??
|
||||
false;
|
||||
if (!continueSubmission) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
Navigator.pop(
|
||||
context,
|
||||
GithubErrorReport(
|
||||
shortDescription: fk[shortDescriptionKey],
|
||||
longDescription: fk[longDescriptionKey],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -234,8 +234,8 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
||||
Future<void> _assignAsn(DocumentModel document) async {
|
||||
try {
|
||||
await BlocProvider.of<DocumentsCubit>(context).assignAsn(document);
|
||||
} on ErrorMessage catch (error) {
|
||||
showError(context, error);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
showError(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -409,8 +409,8 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
||||
try {
|
||||
await BlocProvider.of<DocumentsCubit>(context).removeDocument(document);
|
||||
showSnackBar(context, S.of(context).documentDeleteSuccessMessage);
|
||||
} on ErrorMessage catch (error) {
|
||||
showError(context, error);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
showError(context, error, stackTrace);
|
||||
} finally {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
|
||||
@@ -80,8 +80,8 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
|
||||
try {
|
||||
await getIt<DocumentsCubit>().updateDocument(updatedDocument);
|
||||
showSnackBar(context, S.of(context).documentUpdateErrorMessage);
|
||||
} on ErrorMessage catch (error) {
|
||||
showError(context, error);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
showError(context, error, stackTrace);
|
||||
} finally {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
|
||||
import 'package:paperless_mobile/core/logic/error_code_localization_mapper.dart';
|
||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||
import 'package:paperless_mobile/core/service/github_issue_service.dart';
|
||||
import 'package:paperless_mobile/core/widgets/offline_banner.dart';
|
||||
import 'package:paperless_mobile/di_initializer.dart';
|
||||
import 'package:paperless_mobile/features/labels/correspondent/bloc/correspondents_cubit.dart';
|
||||
@@ -53,8 +54,8 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
||||
Future<void> _initDocuments() async {
|
||||
try {
|
||||
BlocProvider.of<DocumentsCubit>(context).loadDocuments();
|
||||
} on ErrorMessage catch (error) {
|
||||
showError(context, error);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
showError(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,8 +74,8 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
||||
}
|
||||
try {
|
||||
await documentsCubit.loadMore();
|
||||
} on ErrorMessage catch (error) {
|
||||
showError(context, error);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
showError(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,8 +88,8 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
||||
await BlocProvider.of<DocumentsCubit>(context).updateCurrentFilter(
|
||||
(filter) => filter.copyWith(page: 1),
|
||||
);
|
||||
} on ErrorMessage catch (error) {
|
||||
showError(context, error);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
showError(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,8 +130,7 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
||||
controller: _panelController,
|
||||
defaultPanelState: PanelState.CLOSED,
|
||||
minHeight: 48,
|
||||
maxHeight: MediaQuery.of(context).size.height -
|
||||
kBottomNavigationBarHeight,
|
||||
maxHeight: (MediaQuery.of(context).size.height * 3) / 4,
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(16),
|
||||
topRight: Radius.circular(16),
|
||||
@@ -192,7 +192,7 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
||||
onRefresh: _onRefresh,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.only(
|
||||
bottom: 142,
|
||||
bottom: 48 + kBottomNavigationBarHeight + 48,
|
||||
), // Prevents panel from hiding scrollable content
|
||||
child: CustomScrollView(
|
||||
slivers: [
|
||||
@@ -210,7 +210,12 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
||||
),
|
||||
],
|
||||
),
|
||||
child
|
||||
child,
|
||||
// SliverToBoxAdapter(
|
||||
// child: SizedBox(
|
||||
// height: MediaQuery.of(context).size.height / 3,
|
||||
// ),
|
||||
// )
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:paperless_mobile/core/logic/error_code_localization_mapper.dart';
|
||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||
@@ -138,12 +137,12 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
||||
const SizedBox(
|
||||
height: 16.0,
|
||||
),
|
||||
_buildSortByChipsList(context, state),
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(S.of(context).documentsFilterPageSearchLabel),
|
||||
).padded(const EdgeInsets.only(left: 8.0)),
|
||||
_buildQueryFormField(state),
|
||||
_buildSortByChipsList(context, state),
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(S.of(context).documentsFilterPageAdvancedLabel),
|
||||
@@ -536,8 +535,8 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
||||
BlocProvider.of<SavedViewCubit>(context).resetSelection();
|
||||
FocusScope.of(context).unfocus();
|
||||
widget.panelController.close();
|
||||
} on ErrorMessage catch (error) {
|
||||
showError(context, error);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
showError(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_state.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/selection/bulk_delete_confirmation_dialog.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/selection/saved_view_selection_widget.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/sort_documents_button.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
import 'package:paperless_mobile/util.dart';
|
||||
|
||||
@@ -28,12 +29,14 @@ class _DocumentsPageAppBarState extends State<DocumentsPageAppBar> {
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<DocumentsCubit, DocumentsState>(
|
||||
builder: (context, documentsState) {
|
||||
if (documentsState.selection.isNotEmpty) {
|
||||
final hasSelection = documentsState.selection.isNotEmpty;
|
||||
if (hasSelection) {
|
||||
return SliverAppBar(
|
||||
expandedHeight: kToolbarHeight + _flexibleAreaHeight,
|
||||
snap: true,
|
||||
floating: true,
|
||||
pinned: true,
|
||||
expandedHeight: kToolbarHeight,
|
||||
flexibleSpace: _buildFlexibleArea(false),
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: () =>
|
||||
@@ -51,13 +54,10 @@ class _DocumentsPageAppBarState extends State<DocumentsPageAppBar> {
|
||||
} else {
|
||||
return SliverAppBar(
|
||||
expandedHeight: kToolbarHeight + _flexibleAreaHeight,
|
||||
snap: true,
|
||||
floating: true,
|
||||
pinned: true,
|
||||
flexibleSpace: const FlexibleSpaceBar(
|
||||
background: Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: SavedViewSelectionWidget(height: _flexibleAreaHeight),
|
||||
),
|
||||
),
|
||||
flexibleSpace: _buildFlexibleArea(true),
|
||||
title: BlocBuilder<DocumentsCubit, DocumentsState>(
|
||||
builder: (context, state) {
|
||||
return Text(
|
||||
@@ -65,13 +65,30 @@ class _DocumentsPageAppBarState extends State<DocumentsPageAppBar> {
|
||||
);
|
||||
},
|
||||
),
|
||||
actions: widget.actions,
|
||||
actions: [
|
||||
...widget.actions,
|
||||
],
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildFlexibleArea(bool enabled) {
|
||||
return FlexibleSpaceBar(
|
||||
background: Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
//TODO: replace with sorting stuff...
|
||||
SavedViewSelectionWidget(height: 48, enabled: enabled),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _onDelete(BuildContext context, DocumentsState documentsState) async {
|
||||
final shouldDelete = await showDialog<bool>(
|
||||
context: context,
|
||||
@@ -87,8 +104,8 @@ class _DocumentsPageAppBarState extends State<DocumentsPageAppBar> {
|
||||
context,
|
||||
S.of(context).documentsPageBulkDeleteSuccessfulText,
|
||||
);
|
||||
} on ErrorMessage catch (error) {
|
||||
showError(context, error);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
showError(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||
@@ -17,9 +15,11 @@ class SavedViewSelectionWidget extends StatelessWidget {
|
||||
const SavedViewSelectionWidget({
|
||||
Key? key,
|
||||
required this.height,
|
||||
required this.enabled,
|
||||
}) : super(key: key);
|
||||
|
||||
final double height;
|
||||
final bool enabled;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -33,7 +33,7 @@ class SavedViewSelectionWidget extends StatelessWidget {
|
||||
return Text(S.of(context).savedViewsEmptyStateText);
|
||||
}
|
||||
return SizedBox(
|
||||
height: 48.0,
|
||||
height: height,
|
||||
child: ListView.separated(
|
||||
itemCount: state.value.length,
|
||||
scrollDirection: Axis.horizontal,
|
||||
@@ -44,8 +44,10 @@ class SavedViewSelectionWidget extends StatelessWidget {
|
||||
child: FilterChip(
|
||||
label: Text(state.value.values.toList()[index].name),
|
||||
selected: view.id == state.selectedSavedViewId,
|
||||
onSelected: (isSelected) =>
|
||||
_onSelected(isSelected, context, view),
|
||||
onSelected: enabled
|
||||
? (isSelected) =>
|
||||
_onSelected(isSelected, context, view)
|
||||
: null,
|
||||
),
|
||||
);
|
||||
},
|
||||
@@ -65,7 +67,7 @@ class SavedViewSelectionWidget extends StatelessWidget {
|
||||
),
|
||||
TextButton.icon(
|
||||
icon: const Icon(Icons.add),
|
||||
onPressed: () => _onCreatePressed(context),
|
||||
onPressed: enabled ? () => _onCreatePressed(context) : null,
|
||||
label: Text(S.of(context).savedViewCreateNewLabel),
|
||||
),
|
||||
],
|
||||
@@ -85,8 +87,8 @@ class SavedViewSelectionWidget extends StatelessWidget {
|
||||
if (newView != null) {
|
||||
try {
|
||||
await BlocProvider.of<SavedViewCubit>(context).add(newView);
|
||||
} on ErrorMessage catch (error) {
|
||||
showError(context, error);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
showError(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -102,8 +104,8 @@ class SavedViewSelectionWidget extends StatelessWidget {
|
||||
BlocProvider.of<DocumentsCubit>(context).updateFilter();
|
||||
BlocProvider.of<SavedViewCubit>(context).selectView(null);
|
||||
}
|
||||
} on ErrorMessage catch (error) {
|
||||
showError(context, error);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
showError(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,8 +119,8 @@ class SavedViewSelectionWidget extends StatelessWidget {
|
||||
if (delete) {
|
||||
try {
|
||||
BlocProvider.of<SavedViewCubit>(context).remove(view);
|
||||
} on ErrorMessage catch (error) {
|
||||
showError(context, error);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
showError(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,8 +50,8 @@ class _SortDocumentsButtonState extends State<SortDocumentsButton> {
|
||||
sortOrder: state.filter.sortOrder.toggle(),
|
||||
),
|
||||
);
|
||||
} on ErrorMessage catch (error) {
|
||||
showError(context, error);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
showError(context, error, stackTrace);
|
||||
} finally {
|
||||
setState(() => _isLoading = false);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
|
||||
import 'package:paperless_mobile/core/logic/error_code_localization_mapper.dart';
|
||||
import 'package:paperless_mobile/core/bloc/paperless_server_information_cubit.dart';
|
||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||
import 'package:paperless_mobile/core/widgets/offline_banner.dart';
|
||||
import 'package:paperless_mobile/di_initializer.dart';
|
||||
@@ -32,7 +32,7 @@ class _HomePageState extends State<HomePage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
initializeLabelData(context);
|
||||
_initializeData(context);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -41,7 +41,7 @@ class _HomePageState extends State<HomePage> {
|
||||
//Only re-initialize data if the connectivity changed from not connected to connected
|
||||
listenWhen: (previous, current) => current == ConnectivityState.connected,
|
||||
listener: (context, state) {
|
||||
initializeLabelData(context);
|
||||
_initializeData(context);
|
||||
},
|
||||
builder: (context, connectivityState) {
|
||||
return Scaffold(
|
||||
@@ -73,15 +73,17 @@ class _HomePageState extends State<HomePage> {
|
||||
);
|
||||
}
|
||||
|
||||
initializeLabelData(BuildContext context) {
|
||||
_initializeData(BuildContext context) async {
|
||||
try {
|
||||
await BlocProvider.of<PaperlessServerInformationCubit>(context)
|
||||
.updateStatus();
|
||||
BlocProvider.of<DocumentTypeCubit>(context).initialize();
|
||||
BlocProvider.of<CorrespondentCubit>(context).initialize();
|
||||
BlocProvider.of<TagCubit>(context).initialize();
|
||||
BlocProvider.of<StoragePathCubit>(context).initialize();
|
||||
BlocProvider.of<SavedViewCubit>(context).initialize();
|
||||
} on ErrorMessage catch (error) {
|
||||
showError(context, error);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
showError(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,14 +20,28 @@ class BottomNavBar extends StatelessWidget {
|
||||
destinations: [
|
||||
NavigationDestination(
|
||||
icon: const Icon(Icons.description),
|
||||
selectedIcon: Icon(
|
||||
Icons.description,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
label: S.of(context).bottomNavDocumentsPageLabel,
|
||||
),
|
||||
NavigationDestination(
|
||||
icon: const Icon(Icons.document_scanner),
|
||||
selectedIcon: Icon(
|
||||
Icons.document_scanner,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
label: S.of(context).bottomNavScannerPageLabel,
|
||||
),
|
||||
NavigationDestination(
|
||||
icon: const Icon(Icons.sell),
|
||||
icon: const Icon(
|
||||
Icons.sell,
|
||||
),
|
||||
selectedIcon: Icon(
|
||||
Icons.sell,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
label: S.of(context).bottomNavLabelsPageLabel,
|
||||
),
|
||||
],
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/core/bloc/paperless_server_information_cubit.dart';
|
||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||
import 'package:paperless_mobile/core/model/paperless_server_information.dart';
|
||||
import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart';
|
||||
import 'package:paperless_mobile/di_initializer.dart';
|
||||
import 'package:paperless_mobile/features/labels/correspondent/bloc/correspondents_cubit.dart';
|
||||
@@ -24,6 +26,12 @@ class InfoDrawer extends StatelessWidget {
|
||||
child: ListView(
|
||||
children: [
|
||||
DrawerHeader(
|
||||
padding: EdgeInsets.only(
|
||||
top: 8,
|
||||
left: 8,
|
||||
bottom: 0,
|
||||
right: 8,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -39,24 +47,66 @@ class InfoDrawer extends StatelessWidget {
|
||||
Text(
|
||||
S.of(context).appTitleText,
|
||||
style: Theme.of(context).textTheme.headline5!.copyWith(
|
||||
color:
|
||||
Theme.of(context).colorScheme.onPrimaryContainer),
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onPrimaryContainer,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.bottomRight,
|
||||
child: BlocBuilder<AuthenticationCubit, AuthenticationState>(
|
||||
child: BlocBuilder<PaperlessServerInformationCubit,
|
||||
PaperlessServerInformation>(
|
||||
builder: (context, state) {
|
||||
return Text(
|
||||
state.authentication?.serverUrl
|
||||
.replaceAll(RegExp(r'https?://'), "") ??
|
||||
"",
|
||||
textAlign: TextAlign.end,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onPrimaryContainer),
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
dense: true,
|
||||
title: Text(
|
||||
S.of(context).appDrawerHeaderLoggedInAsText +
|
||||
(state.username ?? '?'),
|
||||
style: Theme.of(context).textTheme.bodyText2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
textAlign: TextAlign.end,
|
||||
maxLines: 1,
|
||||
),
|
||||
subtitle: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
state.host ?? '',
|
||||
style: Theme.of(context).textTheme.bodyText2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
textAlign: TextAlign.end,
|
||||
maxLines: 1,
|
||||
),
|
||||
Text(
|
||||
'${S.of(context).serverInformationPaperlessVersionText} ${state.version} (API v${state.apiVersion})',
|
||||
style: Theme.of(context).textTheme.caption,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
textAlign: TextAlign.end,
|
||||
maxLines: 1,
|
||||
),
|
||||
],
|
||||
),
|
||||
// title: RichText(
|
||||
|
||||
// text: TextSpan(
|
||||
// children: [
|
||||
// TextSpan(
|
||||
// text:
|
||||
// style:
|
||||
// Theme.of(context).textTheme.bodyText2,
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
isThreeLine: true,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
@@ -134,8 +184,8 @@ class InfoDrawer extends StatelessWidget {
|
||||
getIt<DocumentTypeCubit>().reset();
|
||||
getIt<TagCubit>().reset();
|
||||
getIt<DocumentScannerCubit>().reset();
|
||||
} on ErrorMessage catch (error) {
|
||||
showError(context, error);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
showError(context, error, stackTrace);
|
||||
}
|
||||
},
|
||||
),
|
||||
|
||||
@@ -38,8 +38,8 @@ class EditCorrespondentPage extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
Navigator.pop(context);
|
||||
} on ErrorMessage catch (error) {
|
||||
showError(context, error);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
showError(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,8 +59,8 @@ class CorrespondentWidget extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
afterSelected?.call();
|
||||
} on ErrorMessage catch (error) {
|
||||
showError(context, error);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
showError(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,8 +54,8 @@ class DocumentTypeWidget extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
afterSelected?.call();
|
||||
} on ErrorMessage catch (error) {
|
||||
showError(context, error);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
showError(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,8 +43,8 @@ class EditStoragePathPage extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
Navigator.pop(context);
|
||||
} on ErrorMessage catch (error) {
|
||||
showError(context, error);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
showError(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,8 +58,8 @@ class StoragePathWidget extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
afterSelected?.call();
|
||||
} on ErrorMessage catch (error) {
|
||||
showError(context, error);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
showError(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,8 +57,8 @@ class EditTagPage extends StatelessWidget {
|
||||
}
|
||||
cubit.updateFilter(filter: updatedFilter);
|
||||
Navigator.pop(context);
|
||||
} on ErrorMessage catch (error) {
|
||||
showError(context, error);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
showError(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ class TagWidget extends StatelessWidget {
|
||||
tag.name,
|
||||
style: TextStyle(color: tag.textColor),
|
||||
),
|
||||
checkmarkColor: tag.textColor,
|
||||
backgroundColor: tag.color,
|
||||
side: BorderSide.none,
|
||||
);
|
||||
@@ -57,8 +58,8 @@ class TagWidget extends StatelessWidget {
|
||||
if (afterTagTapped != null) {
|
||||
afterTagTapped!();
|
||||
}
|
||||
} on ErrorMessage catch (error) {
|
||||
showError(context, error);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
showError(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,8 +146,8 @@ class _EditLabelPageState<T extends Label> extends State<EditLabelPage<T>> {
|
||||
Navigator.pop(context);
|
||||
} on PaperlessValidationErrors catch (errorMessages) {
|
||||
setState(() => _errors = errorMessages);
|
||||
} on ErrorMessage catch (error) {
|
||||
showError(context, error);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
showError(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,10 +97,10 @@ class _LoginPageState extends State<LoginPage> {
|
||||
clientCertificate:
|
||||
form[ClientCertificateFormField.fkClientCertificate],
|
||||
);
|
||||
} on ErrorMessage catch (error) {
|
||||
showError(context, error);
|
||||
} catch (unknownError) {
|
||||
showSnackBar(context, unknownError.toString());
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
showError(context, error, stackTrace);
|
||||
} catch (unknownError, stackTrace) {
|
||||
showError(context, ErrorMessage.unknown(), stackTrace);
|
||||
} finally {
|
||||
setState(() => _isLoginLoading = false);
|
||||
}
|
||||
|
||||
@@ -212,12 +212,12 @@ class _DocumentUploadPageState extends State<DocumentUploadPage> {
|
||||
showSnackBar(context, S.of(context).documentUploadSuccessText);
|
||||
Navigator.pop(context);
|
||||
widget.afterUpload?.call();
|
||||
} on ErrorMessage catch (error) {
|
||||
showError(context, error);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
showError(context, error, stackTrace);
|
||||
} on PaperlessValidationErrors catch (errorMessages) {
|
||||
setState(() => _errors = errorMessages);
|
||||
} catch (other) {
|
||||
showSnackBar(context, other.toString());
|
||||
} catch (unknownError, stackTrace) {
|
||||
showError(context, ErrorMessage.unknown(), stackTrace);
|
||||
} finally {
|
||||
setState(() {
|
||||
_isUploadLoading = false;
|
||||
@@ -233,8 +233,8 @@ class _DocumentUploadPageState extends State<DocumentUploadPage> {
|
||||
onPressed: () async {
|
||||
try {
|
||||
getIt<DocumentsCubit>().reloadDocuments();
|
||||
} on ErrorMessage catch (error) {
|
||||
showError(context, error);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
showError(context, error, stackTrace);
|
||||
}
|
||||
},
|
||||
label:
|
||||
|
||||
@@ -33,43 +33,13 @@ class ScannerPage extends StatefulWidget {
|
||||
|
||||
class _ScannerPageState extends State<ScannerPage>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late final AnimationController _fabPulsingController;
|
||||
late final Animation _animation;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_fabPulsingController =
|
||||
AnimationController(vsync: this, duration: const Duration(seconds: 1))
|
||||
..repeat(reverse: true);
|
||||
_animation = Tween(begin: 1.0, end: 1.2).animate(_fabPulsingController)
|
||||
..addListener(() => setState((() {})));
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_fabPulsingController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
drawer: const InfoDrawer(),
|
||||
floatingActionButton: BlocBuilder<DocumentScannerCubit, List<File>>(
|
||||
builder: (context, state) {
|
||||
final fab = FloatingActionButton(
|
||||
onPressed: () => _openDocumentScanner(context),
|
||||
child: const Icon(Icons.add_a_photo_outlined),
|
||||
);
|
||||
if (state.isEmpty) {
|
||||
return Transform.scale(
|
||||
child: fab,
|
||||
scale: _animation.value,
|
||||
);
|
||||
}
|
||||
return fab;
|
||||
},
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: () => _openDocumentScanner(context),
|
||||
child: const Icon(Icons.add_a_photo_outlined),
|
||||
),
|
||||
appBar: _buildAppBar(context),
|
||||
body: Padding(
|
||||
@@ -201,8 +171,8 @@ class _ScannerPageState extends State<ScannerPage>
|
||||
try {
|
||||
BlocProvider.of<DocumentScannerCubit>(context)
|
||||
.removeScan(index);
|
||||
} on ErrorMessage catch (error) {
|
||||
showError(context, error);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
showError(context, error, stackTrace);
|
||||
}
|
||||
},
|
||||
index: index,
|
||||
@@ -214,8 +184,8 @@ class _ScannerPageState extends State<ScannerPage>
|
||||
void _reset(BuildContext context) {
|
||||
try {
|
||||
BlocProvider.of<DocumentScannerCubit>(context).reset();
|
||||
} on ErrorMessage catch (error) {
|
||||
showError(context, error);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
showError(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
"genericActionUploadLabel": "Hochladen",
|
||||
"genericActionUpdateLabel": "Aktualisieren",
|
||||
"appDrawerSettingsLabel": "Einstellungen",
|
||||
"appDrawerAboutLabel": "Über",
|
||||
"appDrawerAboutLabel": "Über diese App",
|
||||
"appDrawerAboutInfoLoadingText": "Lade Anwendungsinformationen...",
|
||||
"appDrawerReportBugLabel": "Einen Fehler melden",
|
||||
"appDrawerLogoutLabel": "Verbindung trennen",
|
||||
@@ -186,8 +186,11 @@
|
||||
"referencedDocumentsReadOnlyHintText": "Dies ist eine schreibgeschützte Ansicht! Dokumente können nicht bearbeitet oder entfernt werden.",
|
||||
"editLabelPageConfirmDeletionDialogTitle": "Löschen bestätigen",
|
||||
"editLabelPageDeletionDialogText": "Dieser Kennzeichner wird von Dokumenten referenziert. Durch das Löschen dieses Kennzeichners werden alle Referenzen entfernt. Fortfahren?",
|
||||
"settingsPageStorageSettingsLabel": "Storage",
|
||||
"settingsPageStorageSettingsDescriptionText": "Manage files and storage space",
|
||||
"documentUpdateErrorMessage": "Document successfully updated.",
|
||||
"errorMessageMissingClientCertificate": "Ein Client Zerfitikat wurde erwartet, aber nicht gesendet. Bitte konfiguriere ein gültiges Zertifikat."
|
||||
"settingsPageStorageSettingsLabel": "Speicher",
|
||||
"settingsPageStorageSettingsDescriptionText": "Dateien und Speicherplatz verwalten",
|
||||
"documentUpdateErrorMessage": "Dokument erfolgreich aktualisiert.",
|
||||
"errorMessageMissingClientCertificate": "Ein Client Zerfitikat wurde erwartet, aber nicht gesendet. Bitte konfiguriere ein gültiges Zertifikat.",
|
||||
"serverInformationPaperlessVersionText": "Paperless Server-Version",
|
||||
"errorReportLabel": "MELDEN",
|
||||
"appDrawerHeaderLoggedInAsText": "Eingeloggt als "
|
||||
}
|
||||
@@ -39,7 +39,7 @@
|
||||
"genericActionUploadLabel": "Upload",
|
||||
"genericActionUpdateLabel": "Update",
|
||||
"appDrawerSettingsLabel": "Settings",
|
||||
"appDrawerAboutLabel": "About",
|
||||
"appDrawerAboutLabel": "About this app",
|
||||
"appDrawerAboutInfoLoadingText": "Retrieving application information...",
|
||||
"appDrawerReportBugLabel": "Report a Bug",
|
||||
"appDrawerLogoutLabel": "Disconnect",
|
||||
@@ -190,5 +190,8 @@
|
||||
"settingsPageStorageSettingsLabel": "Storage",
|
||||
"settingsPageStorageSettingsDescriptionText": "Manage files and storage space",
|
||||
"documentUpdateErrorMessage": "Document successfully updated.",
|
||||
"errorMessageMissingClientCertificate": "A client certificate was expected but not sent. Please provide a valid client certificate."
|
||||
"errorMessageMissingClientCertificate": "A client certificate was expected but not sent. Please provide a valid client certificate.",
|
||||
"serverInformationPaperlessVersionText": "Paperless server version",
|
||||
"errorReportLabel": "REPORT",
|
||||
"appDrawerHeaderLoggedInAsText": "Logged in as "
|
||||
}
|
||||
@@ -1,22 +1,22 @@
|
||||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:encrypted_shared_preferences/encrypted_shared_preferences.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:flutter_native_splash/flutter_native_splash.dart';
|
||||
import 'package:form_builder_validators/form_builder_validators.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:intl/intl_standalone.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
|
||||
import 'package:paperless_mobile/core/bloc/label_bloc_provider.dart';
|
||||
import 'package:paperless_mobile/core/bloc/paperless_server_information_cubit.dart';
|
||||
import 'package:paperless_mobile/core/global/asset_images.dart';
|
||||
import 'package:paperless_mobile/core/global/constants.dart';
|
||||
import 'package:paperless_mobile/core/global/http_self_signed_certificate_override.dart';
|
||||
import 'package:paperless_mobile/core/logic/error_code_localization_mapper.dart';
|
||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||
import 'package:paperless_mobile/core/service/file_service.dart';
|
||||
import 'package:paperless_mobile/core/util.dart';
|
||||
import 'package:paperless_mobile/di_initializer.dart';
|
||||
import 'package:paperless_mobile/features/app_intro/application_intro_slideshow.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||
@@ -28,11 +28,6 @@ import 'package:paperless_mobile/features/settings/bloc/application_settings_cub
|
||||
import 'package:paperless_mobile/features/settings/model/application_settings_state.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
import 'package:paperless_mobile/util.dart';
|
||||
import 'package:form_builder_validators/form_builder_validators.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:intl/intl_standalone.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
|
||||
|
||||
void main() async {
|
||||
@@ -46,29 +41,14 @@ void main() async {
|
||||
configureDependencies();
|
||||
// Remove temporarily downloaded files.
|
||||
(await FileService.temporaryDirectory).deleteSync(recursive: true);
|
||||
if (kDebugMode) {
|
||||
_printDeviceInformation();
|
||||
}
|
||||
kPackageInfo = await PackageInfo.fromPlatform();
|
||||
// Load application settings and stored authentication data
|
||||
getIt<ConnectivityCubit>().initialize();
|
||||
await getIt<ApplicationSettingsCubit>().initialize();
|
||||
await getIt<AuthenticationCubit>().initialize();
|
||||
// Ogaylesgo
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
void _printDeviceInformation() async {
|
||||
final tempPath = await FileService.temporaryDirectory;
|
||||
log('[DEVICE INFO] Temporary ${tempPath.absolute}');
|
||||
final docsPath = await FileService.documentsDirectory;
|
||||
log('[DEVICE INFO] Documents ${docsPath?.absolute}');
|
||||
final downloadPath = await FileService.downloadsDirectory;
|
||||
log('[DEVICE INFO] Download ${downloadPath?.absolute}');
|
||||
final scanPath = await FileService.scanDirectory;
|
||||
log('[DEVICE INFO] Scan ${scanPath?.absolute}');
|
||||
}
|
||||
|
||||
class MyApp extends StatefulWidget {
|
||||
const MyApp({Key? key}) : super(key: key);
|
||||
|
||||
@@ -83,6 +63,7 @@ class _MyAppState extends State<MyApp> {
|
||||
providers: [
|
||||
BlocProvider.value(value: getIt<ConnectivityCubit>()),
|
||||
BlocProvider.value(value: getIt<AuthenticationCubit>()),
|
||||
BlocProvider.value(value: getIt<PaperlessServerInformationCubit>()),
|
||||
],
|
||||
child: BlocBuilder<ApplicationSettingsCubit, ApplicationSettingsState>(
|
||||
bloc: getIt<ApplicationSettingsCubit>(),
|
||||
@@ -186,7 +167,6 @@ class _AuthenticationWrapperState extends State<AuthenticationWrapper> {
|
||||
super.initState();
|
||||
// For sharing files coming from outside the app while the app is still opened
|
||||
ReceiveSharingIntent.getMediaStream().listen(handleReceivedFiles);
|
||||
|
||||
// For sharing files coming from outside the app while the app is closed
|
||||
ReceiveSharingIntent.getInitialMedia().then(handleReceivedFiles);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
@@ -7,20 +8,56 @@ import 'package:paperless_mobile/core/logic/error_code_localization_mapper.dart'
|
||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:paperless_mobile/core/service/github_issue_service.dart';
|
||||
import 'package:paperless_mobile/generated/intl/messages_de.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
final dateFormat = DateFormat("yyyy-MM-dd");
|
||||
final GlobalKey<ScaffoldState> rootScaffoldKey = GlobalKey<ScaffoldState>();
|
||||
late PackageInfo kPackageInfo;
|
||||
|
||||
void showSnackBar(BuildContext context, String message, [String? details]) {
|
||||
void showSnackBar(
|
||||
BuildContext context,
|
||||
String message, {
|
||||
String? details,
|
||||
SnackBarAction? action,
|
||||
}) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(message + (details != null ? ' ($details)' : ''))),
|
||||
SnackBar(
|
||||
content: Text(
|
||||
message + (details != null ? ' ($details)' : ''),
|
||||
),
|
||||
action: action,
|
||||
duration: const Duration(seconds: 5),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void showError(BuildContext context, ErrorMessage error) {
|
||||
showSnackBar(context, translateError(context, error.code), error.details);
|
||||
void showError(
|
||||
BuildContext context,
|
||||
ErrorMessage error, [
|
||||
StackTrace? stackTrace,
|
||||
]) {
|
||||
showSnackBar(
|
||||
context,
|
||||
translateError(context, error.code),
|
||||
details: error.details,
|
||||
action: SnackBarAction(
|
||||
label: S.of(context).errorReportLabel,
|
||||
textColor: Colors.amber,
|
||||
onPressed: () => GithubIssueService.createIssueFromError(
|
||||
context,
|
||||
stackTrace: stackTrace,
|
||||
),
|
||||
),
|
||||
);
|
||||
log(
|
||||
"An error has occurred.",
|
||||
error: error,
|
||||
stackTrace: stackTrace,
|
||||
time: DateTime.now(),
|
||||
);
|
||||
}
|
||||
|
||||
bool isNotNull(dynamic value) {
|
||||
|
||||
@@ -17,7 +17,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.0.4+2
|
||||
version: 1.0.5+3
|
||||
|
||||
environment:
|
||||
sdk: ">=2.17.0 <3.0.0"
|
||||
@@ -60,7 +60,7 @@ dependencies:
|
||||
equatable: ^2.0.3
|
||||
flutter_form_builder: ^7.5.0
|
||||
form_builder_extra_fields:
|
||||
git:
|
||||
git:
|
||||
url: https://github.com/flutter-form-builder-ecosystem/form_builder_extra_fields.git
|
||||
ref: main
|
||||
form_builder_validators: ^8.3.0
|
||||
|
||||
Reference in New Issue
Block a user