mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2025-12-09 06:07:54 -06:00
Removed unused files, code cleanup
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/bloc/paperless_server_information_state.dart';
|
import 'package:paperless_mobile/core/bloc/paperless_server_information_state.dart';
|
||||||
import 'package:paperless_mobile/core/security/session_manager.dart';
|
|
||||||
|
|
||||||
class PaperlessServerInformationCubit
|
class PaperlessServerInformationCubit
|
||||||
extends Cubit<PaperlessServerInformationState> {
|
extends Cubit<PaperlessServerInformationState> {
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
import 'package:paperless_api/paperless_api.dart';
|
|
||||||
|
|
||||||
class PaperlessStatisticsState {
|
|
||||||
final bool isLoaded;
|
|
||||||
final PaperlessServerStatisticsModel? statistics;
|
|
||||||
|
|
||||||
PaperlessStatisticsState({
|
|
||||||
required this.isLoaded,
|
|
||||||
this.statistics,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
extension DateComparisons on DateTime {
|
|
||||||
bool isEqualToIgnoringDate(DateTime other) {
|
|
||||||
return day == other.day && month == other.month && year == other.year;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import 'package:hydrated_bloc/hydrated_bloc.dart';
|
import 'package:hydrated_bloc/hydrated_bloc.dart';
|
||||||
import 'package:paperless_mobile/features/login/bloc/authentication_state.dart';
|
import 'package:paperless_mobile/features/login/bloc/authentication_state.dart';
|
||||||
import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/settings/model/application_settings_state.dart';
|
import 'package:paperless_mobile/features/settings/model/application_settings_state.dart';
|
||||||
|
|
||||||
extension AddressableHydratedStorage on Storage {
|
extension AddressableHydratedStorage on Storage {
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:paperless_mobile/features/login/model/client_certificate.dart';
|
|
||||||
|
|
||||||
extension ClientCertificateHandlingSecurityContext on SecurityContext {
|
|
||||||
SecurityContext withClientCertificate(ClientCertificate? clientCertificate) {
|
|
||||||
if (clientCertificate == null) return this;
|
|
||||||
return this
|
|
||||||
..usePrivateKeyBytes(
|
|
||||||
clientCertificate.bytes,
|
|
||||||
password: clientCertificate.passphrase,
|
|
||||||
)
|
|
||||||
..useCertificateChainBytes(
|
|
||||||
clientCertificate.bytes,
|
|
||||||
password: clientCertificate.passphrase,
|
|
||||||
)
|
|
||||||
..setTrustedCertificatesBytes(
|
|
||||||
clientCertificate.bytes,
|
|
||||||
password: clientCertificate.passphrase,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
extension SizeLimitedString on String {
|
|
||||||
String withLengthLimitedTo(int length, [String overflow = "..."]) {
|
|
||||||
return this.length > length
|
|
||||||
? '${substring(0, length - overflow.length)}$overflow'
|
|
||||||
: this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class WelcomeIntroSlide extends StatelessWidget {
|
|
||||||
const WelcomeIntroSlide({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Column(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"Welcome to Paperless Mobile!",
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: Theme.of(context).textTheme.titleLarge,
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
child: Text(
|
|
||||||
"Manage, share and create documents on the go without any compromises!",
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(color: Theme.of(context).hintColor),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Align(child: Image.asset("assets/logos/paperless_logo_green.png")),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -26,7 +26,7 @@ import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_widget.d
|
|||||||
import 'package:paperless_mobile/features/labels/view/widgets/label_text.dart';
|
import 'package:paperless_mobile/features/labels/view/widgets/label_text.dart';
|
||||||
import 'package:paperless_mobile/features/similar_documents/cubit/similar_documents_cubit.dart';
|
import 'package:paperless_mobile/features/similar_documents/cubit/similar_documents_cubit.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
import 'package:paperless_mobile/util.dart';
|
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:share_plus/share_plus.dart';
|
import 'package:share_plus/share_plus.dart';
|
||||||
import 'package:badges/badges.dart' as b;
|
import 'package:badges/badges.dart' as b;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import 'package:paperless_mobile/core/widgets/hint_card.dart';
|
|||||||
import 'package:paperless_mobile/features/documents/view/widgets/documents_empty_state.dart';
|
import 'package:paperless_mobile/features/documents/view/widgets/documents_empty_state.dart';
|
||||||
import 'package:paperless_mobile/features/documents/view/widgets/list/document_list_item.dart';
|
import 'package:paperless_mobile/features/documents/view/widgets/list/document_list_item.dart';
|
||||||
import 'package:paperless_mobile/features/similar_documents/cubit/similar_documents_cubit.dart';
|
import 'package:paperless_mobile/features/similar_documents/cubit/similar_documents_cubit.dart';
|
||||||
|
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||||
import 'package:paperless_mobile/util.dart';
|
import 'package:paperless_mobile/util.dart';
|
||||||
|
|
||||||
class SimilarDocumentsView extends StatefulWidget {
|
class SimilarDocumentsView extends StatefulWidget {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import 'package:paperless_api/paperless_api.dart';
|
|||||||
import 'package:paperless_mobile/core/service/file_service.dart';
|
import 'package:paperless_mobile/core/service/file_service.dart';
|
||||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||||
import 'package:paperless_mobile/util.dart';
|
import 'package:paperless_mobile/util.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import 'package:paperless_mobile/features/edit_label/view/impl/add_document_type
|
|||||||
import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_form_field.dart';
|
import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_form_field.dart';
|
||||||
import 'package:paperless_mobile/features/labels/view/widgets/label_form_field.dart';
|
import 'package:paperless_mobile/features/labels/view/widgets/label_form_field.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||||
import 'package:paperless_mobile/util.dart';
|
import 'package:paperless_mobile/util.dart';
|
||||||
|
|
||||||
class DocumentUploadPreparationPage extends StatefulWidget {
|
class DocumentUploadPreparationPage extends StatefulWidget {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import 'package:paperless_mobile/features/edit_label/view/impl/add_storage_path_
|
|||||||
import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_form_field.dart';
|
import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_form_field.dart';
|
||||||
import 'package:paperless_mobile/features/labels/view/widgets/label_form_field.dart';
|
import 'package:paperless_mobile/features/labels/view/widgets/label_form_field.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||||
import 'package:paperless_mobile/util.dart';
|
import 'package:paperless_mobile/util.dart';
|
||||||
|
|
||||||
class DocumentEditPage extends StatefulWidget {
|
class DocumentEditPage extends StatefulWidget {
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import 'package:paperless_mobile/features/settings/model/application_settings_st
|
|||||||
import 'package:paperless_mobile/features/settings/model/view_type.dart';
|
import 'package:paperless_mobile/features/settings/model/view_type.dart';
|
||||||
import 'package:paperless_mobile/features/tasks/cubit/task_status_cubit.dart';
|
import 'package:paperless_mobile/features/tasks/cubit/task_status_cubit.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||||
import 'package:paperless_mobile/util.dart';
|
import 'package:paperless_mobile/util.dart';
|
||||||
|
|
||||||
class DocumentFilterIntent {
|
class DocumentFilterIntent {
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
|
||||||
|
|
||||||
class OrderByDropdown extends StatefulWidget {
|
|
||||||
static const fkOrderBy = "orderBy";
|
|
||||||
const OrderByDropdown({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<OrderByDropdown> createState() => _OrderByDropdownState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _OrderByDropdownState extends State<OrderByDropdown> {
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return FormBuilderDropdown<SortField>(
|
|
||||||
name: OrderByDropdown.fkOrderBy,
|
|
||||||
items: const [],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,156 +0,0 @@
|
|||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
|
||||||
import 'package:paperless_mobile/core/widgets/offline_banner.dart';
|
|
||||||
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/saved_view/view/saved_view_selection_widget.dart';
|
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
|
||||||
import 'package:paperless_mobile/util.dart';
|
|
||||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
|
||||||
|
|
||||||
class DocumentsPageAppBar extends StatefulWidget with PreferredSizeWidget {
|
|
||||||
final List<Widget> actions;
|
|
||||||
final bool isOffline;
|
|
||||||
|
|
||||||
const DocumentsPageAppBar({
|
|
||||||
super.key,
|
|
||||||
required this.isOffline,
|
|
||||||
this.actions = const [],
|
|
||||||
});
|
|
||||||
@override
|
|
||||||
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
|
|
||||||
@override
|
|
||||||
State<DocumentsPageAppBar> createState() => _DocumentsPageAppBarState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _DocumentsPageAppBarState extends State<DocumentsPageAppBar> {
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
const savedViewWidgetHeight = 48.0;
|
|
||||||
final flexibleAreaHeight = kToolbarHeight -
|
|
||||||
16 +
|
|
||||||
savedViewWidgetHeight +
|
|
||||||
(widget.isOffline ? 24 : 0);
|
|
||||||
return BlocBuilder<DocumentsCubit, DocumentsState>(
|
|
||||||
builder: (context, documentsState) {
|
|
||||||
final hasSelection = documentsState.selection.isNotEmpty;
|
|
||||||
// final PreferredSize? loadingWidget = documentsState.isLoading
|
|
||||||
// ? const PreferredSize(
|
|
||||||
// child: LinearProgressIndicator(),
|
|
||||||
// preferredSize: Size.fromHeight(4.0),
|
|
||||||
// )
|
|
||||||
// : null;
|
|
||||||
if (hasSelection) {
|
|
||||||
return SliverAppBar(
|
|
||||||
// bottom: loadingWidget,
|
|
||||||
expandedHeight: kToolbarHeight + flexibleAreaHeight,
|
|
||||||
snap: true,
|
|
||||||
floating: true,
|
|
||||||
pinned: true,
|
|
||||||
flexibleSpace: _buildFlexibleArea(
|
|
||||||
false,
|
|
||||||
documentsState.filter,
|
|
||||||
savedViewWidgetHeight,
|
|
||||||
),
|
|
||||||
leading: IconButton(
|
|
||||||
icon: const Icon(Icons.close),
|
|
||||||
onPressed: () => context.read<DocumentsCubit>().resetSelection(),
|
|
||||||
),
|
|
||||||
title: Text(
|
|
||||||
'${documentsState.selection.length} ${S.of(context).documentsSelectedText}'),
|
|
||||||
actions: [
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Icons.delete),
|
|
||||||
onPressed: () => _onDelete(context, documentsState),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return SliverAppBar(
|
|
||||||
// bottom: loadingWidget,
|
|
||||||
expandedHeight: kToolbarHeight + flexibleAreaHeight,
|
|
||||||
snap: true,
|
|
||||||
floating: true,
|
|
||||||
pinned: true,
|
|
||||||
flexibleSpace: _buildFlexibleArea(
|
|
||||||
true,
|
|
||||||
documentsState.filter,
|
|
||||||
savedViewWidgetHeight,
|
|
||||||
),
|
|
||||||
title: Text(
|
|
||||||
'${S.of(context).documentsPageTitle} (${_formatDocumentCount(documentsState.count)})',
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
...widget.actions,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildFlexibleArea(
|
|
||||||
bool enabled,
|
|
||||||
DocumentFilter filter,
|
|
||||||
double savedViewHeight,
|
|
||||||
) {
|
|
||||||
return FlexibleSpaceBar(
|
|
||||||
background: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
|
||||||
children: [
|
|
||||||
if (widget.isOffline) const OfflineBanner(),
|
|
||||||
SavedViewSelectionWidget(
|
|
||||||
height: savedViewHeight,
|
|
||||||
enabled: enabled,
|
|
||||||
currentFilter: filter,
|
|
||||||
).paddedSymmetrically(horizontal: 8.0),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onDelete(BuildContext context, DocumentsState documentsState) async {
|
|
||||||
final shouldDelete = await showDialog<bool>(
|
|
||||||
context: context,
|
|
||||||
builder: (context) =>
|
|
||||||
BulkDeleteConfirmationDialog(state: documentsState)) ??
|
|
||||||
false;
|
|
||||||
if (shouldDelete) {
|
|
||||||
try {
|
|
||||||
await context
|
|
||||||
.read<DocumentsCubit>()
|
|
||||||
.bulkRemove(documentsState.selection);
|
|
||||||
showSnackBar(
|
|
||||||
context,
|
|
||||||
S.of(context).documentsPageBulkDeleteSuccessfulText,
|
|
||||||
);
|
|
||||||
} on PaperlessServerException catch (error, stackTrace) {
|
|
||||||
showErrorMessage(context, error, stackTrace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String _formatDocumentCount(int count) {
|
|
||||||
return count > 99 ? "99+" : count.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ScrollListener extends ChangeNotifier {
|
|
||||||
double top = 0;
|
|
||||||
double _last = 0;
|
|
||||||
|
|
||||||
ScrollListener.initialise(ScrollController controller, [double height = 56]) {
|
|
||||||
controller.addListener(() {
|
|
||||||
final current = controller.offset;
|
|
||||||
top += _last - current;
|
|
||||||
if (top <= -height) top = -height;
|
|
||||||
if (top >= 0) top = 0;
|
|
||||||
_last = current;
|
|
||||||
if (top <= 0 && top >= -height) notifyListeners();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,6 +9,7 @@ import 'package:paperless_mobile/core/repository/state/repository_state.dart';
|
|||||||
import 'package:paperless_mobile/features/edit_label/cubit/edit_label_cubit.dart';
|
import 'package:paperless_mobile/features/edit_label/cubit/edit_label_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/edit_label/view/label_form.dart';
|
import 'package:paperless_mobile/features/edit_label/view/label_form.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||||
import 'package:paperless_mobile/util.dart';
|
import 'package:paperless_mobile/util.dart';
|
||||||
|
|
||||||
class EditLabelPage<T extends Label> extends StatelessWidget {
|
class EditLabelPage<T extends Label> extends StatelessWidget {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import 'package:paperless_mobile/core/translation/matching_algorithm_localizatio
|
|||||||
import 'package:paperless_mobile/core/type/types.dart';
|
import 'package:paperless_mobile/core/type/types.dart';
|
||||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||||
import 'package:paperless_mobile/util.dart';
|
import 'package:paperless_mobile/util.dart';
|
||||||
|
|
||||||
class SubmitButtonConfig<T extends Label> {
|
class SubmitButtonConfig<T extends Label> {
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ import 'package:paperless_mobile/features/scan/view/scanner_page.dart';
|
|||||||
import 'package:paperless_mobile/features/sharing/share_intent_queue.dart';
|
import 'package:paperless_mobile/features/sharing/share_intent_queue.dart';
|
||||||
import 'package:paperless_mobile/features/tasks/cubit/task_status_cubit.dart';
|
import 'package:paperless_mobile/features/tasks/cubit/task_status_cubit.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
import 'package:paperless_mobile/util.dart';
|
import 'package:paperless_mobile/helpers/file_helpers.dart';
|
||||||
|
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||||
import 'package:path/path.dart' as p;
|
import 'package:path/path.dart' as p;
|
||||||
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
|
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
|
||||||
import 'package:responsive_builder/responsive_builder.dart';
|
import 'package:responsive_builder/responsive_builder.dart';
|
||||||
@@ -136,7 +137,7 @@ class _HomePageState extends State<HomePage> {
|
|||||||
toastLength: Toast.LENGTH_LONG,
|
toastLength: Toast.LENGTH_LONG,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (e, stackTrace) {
|
} catch (e) {
|
||||||
Fluttertoast.showToast(
|
Fluttertoast.showToast(
|
||||||
msg: S.of(context).receiveSharedFilePermissionDeniedMessage,
|
msg: S.of(context).receiveSharedFilePermissionDeniedMessage,
|
||||||
toastLength: Toast.LENGTH_LONG,
|
toastLength: Toast.LENGTH_LONG,
|
||||||
@@ -236,7 +237,6 @@ class _HomePageState extends State<HomePage> {
|
|||||||
builder: (context, sizingInformation) {
|
builder: (context, sizingInformation) {
|
||||||
if (!sizingInformation.isMobile) {
|
if (!sizingInformation.isMobile) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
key: rootScaffoldKey,
|
|
||||||
drawer: const AppDrawer(),
|
drawer: const AppDrawer(),
|
||||||
body: Row(
|
body: Row(
|
||||||
children: [
|
children: [
|
||||||
@@ -257,7 +257,6 @@ class _HomePageState extends State<HomePage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
key: rootScaffoldKey,
|
|
||||||
bottomNavigationBar: NavigationBar(
|
bottomNavigationBar: NavigationBar(
|
||||||
elevation: 4.0,
|
elevation: 4.0,
|
||||||
selectedIndex: _currentIndex,
|
selectedIndex: _currentIndex,
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import 'package:paperless_mobile/features/login/bloc/authentication_cubit.dart';
|
|||||||
import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart';
|
import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/settings/view/settings_page.dart';
|
import 'package:paperless_mobile/features/settings/view/settings_page.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||||
import 'package:paperless_mobile/util.dart';
|
import 'package:paperless_mobile/util.dart';
|
||||||
import 'package:url_launcher/link.dart';
|
import 'package:url_launcher/link.dart';
|
||||||
import 'package:url_launcher/url_launcher_string.dart';
|
import 'package:url_launcher/url_launcher_string.dart';
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import 'package:paperless_mobile/features/inbox/bloc/state/inbox_state.dart';
|
|||||||
import 'package:paperless_mobile/features/inbox/view/widgets/inbox_empty_widget.dart';
|
import 'package:paperless_mobile/features/inbox/view/widgets/inbox_empty_widget.dart';
|
||||||
import 'package:paperless_mobile/features/inbox/view/widgets/inbox_item.dart';
|
import 'package:paperless_mobile/features/inbox/view/widgets/inbox_item.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||||
import 'package:paperless_mobile/util.dart';
|
import 'package:paperless_mobile/util.dart';
|
||||||
|
|
||||||
class InboxPage extends StatefulWidget {
|
class InboxPage extends StatefulWidget {
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
|
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/features/linked_documents_preview/bloc/linked_documents_cubit.dart';
|
import 'package:paperless_mobile/features/linked_documents/bloc/linked_documents_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/linked_documents_preview/view/pages/linked_documents_page.dart';
|
import 'package:paperless_mobile/features/linked_documents/view/pages/linked_documents_page.dart';
|
||||||
|
import 'package:paperless_mobile/helpers/format_helpers.dart';
|
||||||
|
|
||||||
class LabelItem<T extends Label> extends StatelessWidget {
|
class LabelItem<T extends Label> extends StatelessWidget {
|
||||||
final T label;
|
final T label;
|
||||||
@@ -37,7 +38,7 @@ class LabelItem<T extends Label> extends StatelessWidget {
|
|||||||
Widget _buildReferencedDocumentsWidget(BuildContext context) {
|
Widget _buildReferencedDocumentsWidget(BuildContext context) {
|
||||||
return TextButton.icon(
|
return TextButton.icon(
|
||||||
label: const Icon(Icons.link),
|
label: const Icon(Icons.link),
|
||||||
icon: Text(_formatDocumentCount(label.documentCount)),
|
icon: Text(formatMaxCount(label.documentCount)),
|
||||||
onPressed: (label.documentCount ?? 0) == 0
|
onPressed: (label.documentCount ?? 0) == 0
|
||||||
? null
|
? null
|
||||||
: () {
|
: () {
|
||||||
@@ -57,11 +58,4 @@ class LabelItem<T extends Label> extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
String _formatDocumentCount(int? count) {
|
|
||||||
if ((count ?? 0) > 99) {
|
|
||||||
return "99+";
|
|
||||||
}
|
|
||||||
return (count ?? 0).toString().padLeft(3);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/features/linked_documents_preview/bloc/state/linked_documents_state.dart';
|
import 'package:paperless_mobile/features/linked_documents/bloc/state/linked_documents_state.dart';
|
||||||
|
|
||||||
class LinkedDocumentsCubit extends Cubit<LinkedDocumentsState> {
|
class LinkedDocumentsCubit extends Cubit<LinkedDocumentsState> {
|
||||||
final PaperlessDocumentsApi _api;
|
final PaperlessDocumentsApi _api;
|
||||||
@@ -5,8 +5,8 @@ import 'package:paperless_mobile/core/widgets/documents_list_loading_widget.dart
|
|||||||
import 'package:paperless_mobile/features/document_details/bloc/document_details_cubit.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';
|
import 'package:paperless_mobile/features/document_details/view/pages/document_details_page.dart';
|
||||||
import 'package:paperless_mobile/features/documents/view/widgets/list/document_list_item.dart';
|
import 'package:paperless_mobile/features/documents/view/widgets/list/document_list_item.dart';
|
||||||
import 'package:paperless_mobile/features/linked_documents_preview/bloc/linked_documents_cubit.dart';
|
import 'package:paperless_mobile/features/linked_documents/bloc/linked_documents_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/linked_documents_preview/bloc/state/linked_documents_state.dart';
|
import 'package:paperless_mobile/features/linked_documents/bloc/state/linked_documents_state.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
|
||||||
class LinkedDocumentsPage extends StatefulWidget {
|
class LinkedDocumentsPage extends StatefulWidget {
|
||||||
@@ -9,6 +9,7 @@ import 'package:paperless_mobile/features/login/view/widgets/form_fields/server_
|
|||||||
import 'package:paperless_mobile/features/login/view/widgets/form_fields/user_credentials_form_field.dart';
|
import 'package:paperless_mobile/features/login/view/widgets/form_fields/user_credentials_form_field.dart';
|
||||||
import 'package:paperless_mobile/features/login/view/widgets/login_pages/server_connection_page.dart';
|
import 'package:paperless_mobile/features/login/view/widgets/login_pages/server_connection_page.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||||
import 'package:paperless_mobile/util.dart';
|
import 'package:paperless_mobile/util.dart';
|
||||||
|
|
||||||
import 'widgets/never_scrollable_scroll_behavior.dart';
|
import 'widgets/never_scrollable_scroll_behavior.dart';
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.dart
|
|||||||
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_state.dart';
|
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_state.dart';
|
||||||
import 'package:paperless_mobile/features/saved_view/view/add_saved_view_page.dart';
|
import 'package:paperless_mobile/features/saved_view/view/add_saved_view_page.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||||
import 'package:paperless_mobile/util.dart';
|
import 'package:paperless_mobile/util.dart';
|
||||||
import 'package:shimmer/shimmer.dart';
|
import 'package:shimmer/shimmer.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -17,17 +17,18 @@ import 'package:paperless_mobile/core/repository/state/impl/document_type_reposi
|
|||||||
import 'package:paperless_mobile/core/repository/state/impl/tag_repository_state.dart';
|
import 'package:paperless_mobile/core/repository/state/impl/tag_repository_state.dart';
|
||||||
import 'package:paperless_mobile/core/service/file_service.dart';
|
import 'package:paperless_mobile/core/service/file_service.dart';
|
||||||
import 'package:paperless_mobile/core/store/local_vault.dart';
|
import 'package:paperless_mobile/core/store/local_vault.dart';
|
||||||
import 'package:paperless_mobile/core/widgets/hint_card.dart';
|
|
||||||
import 'package:paperless_mobile/core/widgets/offline_banner.dart';
|
import 'package:paperless_mobile/core/widgets/offline_banner.dart';
|
||||||
import 'package:paperless_mobile/features/document_upload/cubit/document_upload_cubit.dart';
|
import 'package:paperless_mobile/features/document_upload/cubit/document_upload_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/document_upload/view/document_upload_preparation_page.dart';
|
import 'package:paperless_mobile/features/document_upload/view/document_upload_preparation_page.dart';
|
||||||
import 'package:paperless_mobile/features/documents/view/pages/document_view.dart';
|
import 'package:paperless_mobile/features/documents/view/pages/document_view.dart';
|
||||||
import 'package:paperless_mobile/features/home/view/widget/app_drawer.dart';
|
import 'package:paperless_mobile/features/home/view/widget/app_drawer.dart';
|
||||||
import 'package:paperless_mobile/features/scan/bloc/document_scanner_cubit.dart';
|
import 'package:paperless_mobile/features/scan/bloc/document_scanner_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/scan/view/widgets/grid_image_item_widget.dart';
|
import 'package:paperless_mobile/features/scan/view/widgets/scanned_image_item.dart';
|
||||||
import 'package:paperless_mobile/features/tasks/cubit/task_status_cubit.dart';
|
import 'package:paperless_mobile/features/tasks/cubit/task_status_cubit.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
import 'package:paperless_mobile/util.dart';
|
import 'package:paperless_mobile/helpers/file_helpers.dart';
|
||||||
|
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||||
|
import 'package:paperless_mobile/helpers/permission_helpers.dart';
|
||||||
import 'package:path/path.dart' as p;
|
import 'package:path/path.dart' as p;
|
||||||
import 'package:pdf/pdf.dart';
|
import 'package:pdf/pdf.dart';
|
||||||
import 'package:pdf/widgets.dart' as pw;
|
import 'package:pdf/widgets.dart' as pw;
|
||||||
@@ -218,7 +219,7 @@ class _ScannerPageState extends State<ScannerPage>
|
|||||||
mainAxisSpacing: 10,
|
mainAxisSpacing: 10,
|
||||||
),
|
),
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
return GridImageItemWidget(
|
return ScannedImageItem(
|
||||||
file: scans[index],
|
file: scans[index],
|
||||||
onDelete: () async {
|
onDelete: () async {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import 'package:photo_view/photo_view.dart';
|
|||||||
typedef DeleteCallback = void Function();
|
typedef DeleteCallback = void Function();
|
||||||
typedef OnImageOperation = void Function(File);
|
typedef OnImageOperation = void Function(File);
|
||||||
|
|
||||||
class GridImageItemWidget extends StatefulWidget {
|
class ScannedImageItem extends StatefulWidget {
|
||||||
final File file;
|
final File file;
|
||||||
final DeleteCallback onDelete;
|
final DeleteCallback onDelete;
|
||||||
//final OnImageOperation onImageOperation;
|
//final OnImageOperation onImageOperation;
|
||||||
@@ -15,7 +15,7 @@ class GridImageItemWidget extends StatefulWidget {
|
|||||||
final int index;
|
final int index;
|
||||||
final int totalNumberOfFiles;
|
final int totalNumberOfFiles;
|
||||||
|
|
||||||
const GridImageItemWidget({
|
const ScannedImageItem({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.file,
|
required this.file,
|
||||||
required this.onDelete,
|
required this.onDelete,
|
||||||
@@ -25,10 +25,10 @@ class GridImageItemWidget extends StatefulWidget {
|
|||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<GridImageItemWidget> createState() => _GridImageItemWidgetState();
|
State<ScannedImageItem> createState() => _ScannedImageItemState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _GridImageItemWidgetState extends State<GridImageItemWidget> {
|
class _ScannedImageItemState extends State<ScannedImageItem> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:paperless_mobile/util.dart';
|
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
|
||||||
|
|
||||||
typedef OnImageScannedCallback = void Function(File);
|
|
||||||
|
|
||||||
class ScannerWidget extends StatefulWidget {
|
|
||||||
final OnImageScannedCallback onImageScannedCallback;
|
|
||||||
const ScannerWidget({
|
|
||||||
Key? key,
|
|
||||||
required this.onImageScannedCallback,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_ScannerWidgetState createState() => _ScannerWidgetState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ScannerWidgetState extends State<ScannerWidget> {
|
|
||||||
List<File> documents = List.empty(growable: true);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(title: const Text("Scan document")),
|
|
||||||
body: FutureBuilder<bool>(
|
|
||||||
future: askForPermission(Permission.camera),
|
|
||||||
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
|
|
||||||
if (!snapshot.hasData) {
|
|
||||||
return const Center(child: CircularProgressIndicator());
|
|
||||||
}
|
|
||||||
if (snapshot.data!) {
|
|
||||||
return Container();
|
|
||||||
}
|
|
||||||
return const Center(
|
|
||||||
child: Text("No camera permissions, please enable in settings!"),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:intl/intl.dart';
|
|
||||||
|
|
||||||
class UploadDialog extends StatefulWidget {
|
|
||||||
const UploadDialog({
|
|
||||||
Key? key,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<UploadDialog> createState() => _UploadDialogState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _UploadDialogState extends State<UploadDialog> {
|
|
||||||
late TextEditingController _controller;
|
|
||||||
final _formKey = GlobalKey<FormState>();
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
final DateFormat format = DateFormat("yyyy_MM_dd_hh_mm_ss");
|
|
||||||
final today = format.format(DateTime.now());
|
|
||||||
_controller = TextEditingController.fromValue(
|
|
||||||
TextEditingValue(text: "Scan_$today.pdf"));
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return AlertDialog(
|
|
||||||
title: const Text("Upload to paperless-ng"),
|
|
||||||
content: Form(
|
|
||||||
key: _formKey,
|
|
||||||
child: TextFormField(
|
|
||||||
controller: _controller,
|
|
||||||
validator: (text) {
|
|
||||||
if (text == null || text.isEmpty) {
|
|
||||||
return "Filename must be specified!";
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
child: const Text("Cancel"),
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
if (!_formKey.currentState!.validate()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var txt = _controller.text;
|
|
||||||
if (!txt.endsWith(".pdf")) {
|
|
||||||
txt += ".pdf";
|
|
||||||
}
|
|
||||||
Navigator.of(context).pop(txt);
|
|
||||||
},
|
|
||||||
child: const Text("Upload"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
import 'package:bloc/bloc.dart';
|
|
||||||
import 'package:equatable/equatable.dart';
|
|
||||||
|
|
||||||
part 'tasks_state.dart';
|
|
||||||
|
|
||||||
class TasksCubit extends Cubit<TasksState> {
|
|
||||||
TasksCubit() : super(TasksInitial());
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
part of 'tasks_cubit.dart';
|
|
||||||
|
|
||||||
abstract class TasksState extends Equatable {
|
|
||||||
const TasksState();
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object> get props => [];
|
|
||||||
}
|
|
||||||
|
|
||||||
class TasksInitial extends TasksState {}
|
|
||||||
3
lib/helpers/file_helpers.dart
Normal file
3
lib/helpers/file_helpers.dart
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
String extractFilenameFromPath(String path) {
|
||||||
|
return path.split(RegExp('[./]')).reversed.skip(1).first;
|
||||||
|
}
|
||||||
6
lib/helpers/format_helpers.dart
Normal file
6
lib/helpers/format_helpers.dart
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
String formatMaxCount(int? count, [int maxCount = 99]) {
|
||||||
|
if ((count ?? 0) > maxCount) {
|
||||||
|
return "$maxCount+";
|
||||||
|
}
|
||||||
|
return (count ?? 0).toString().padLeft(maxCount.toString().length);
|
||||||
|
}
|
||||||
38
lib/helpers/image_helpers.dart
Normal file
38
lib/helpers/image_helpers.dart
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// Taken from https://github.com/flutter/flutter/issues/26127#issuecomment-782083060
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
Future<void> loadImage(ImageProvider provider) {
|
||||||
|
final config = ImageConfiguration(
|
||||||
|
bundle: rootBundle,
|
||||||
|
devicePixelRatio: window.devicePixelRatio,
|
||||||
|
platform: defaultTargetPlatform,
|
||||||
|
);
|
||||||
|
final Completer<void> completer = Completer();
|
||||||
|
final ImageStream stream = provider.resolve(config);
|
||||||
|
|
||||||
|
late final ImageStreamListener listener;
|
||||||
|
|
||||||
|
listener = ImageStreamListener((ImageInfo image, bool sync) {
|
||||||
|
debugPrint("Image ${image.debugLabel} finished loading");
|
||||||
|
completer.complete();
|
||||||
|
stream.removeListener(listener);
|
||||||
|
}, onError: (dynamic exception, StackTrace? stackTrace) {
|
||||||
|
completer.complete();
|
||||||
|
stream.removeListener(listener);
|
||||||
|
FlutterError.reportError(FlutterErrorDetails(
|
||||||
|
context: ErrorDescription('image failed to load'),
|
||||||
|
library: 'image resource service',
|
||||||
|
exception: exception,
|
||||||
|
stack: stackTrace,
|
||||||
|
silent: true,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
stream.addListener(listener);
|
||||||
|
return completer.future;
|
||||||
|
}
|
||||||
111
lib/helpers/message_helpers.dart
Normal file
111
lib/helpers/message_helpers.dart
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
|
import 'package:paperless_mobile/core/service/github_issue_service.dart';
|
||||||
|
import 'package:paperless_mobile/core/translation/error_code_localization_mapper.dart';
|
||||||
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
|
||||||
|
class SnackBarActionConfig {
|
||||||
|
final String label;
|
||||||
|
final VoidCallback onPressed;
|
||||||
|
|
||||||
|
SnackBarActionConfig({
|
||||||
|
required this.label,
|
||||||
|
required this.onPressed,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void showSnackBar(
|
||||||
|
BuildContext context,
|
||||||
|
String message, {
|
||||||
|
String? details,
|
||||||
|
SnackBarActionConfig? action,
|
||||||
|
Duration duration = const Duration(seconds: 5),
|
||||||
|
}) {
|
||||||
|
ScaffoldMessenger.of(context)
|
||||||
|
..hideCurrentSnackBar()
|
||||||
|
..showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: (details != null)
|
||||||
|
? RichText(
|
||||||
|
maxLines: 5,
|
||||||
|
text: TextSpan(
|
||||||
|
text: message,
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||||
|
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||||
|
),
|
||||||
|
children: <TextSpan>[
|
||||||
|
TextSpan(
|
||||||
|
text: "\n$details",
|
||||||
|
style: const TextStyle(
|
||||||
|
fontStyle: FontStyle.italic,
|
||||||
|
fontSize: 10,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Text(message),
|
||||||
|
action: action != null
|
||||||
|
? SnackBarAction(
|
||||||
|
label: action.label,
|
||||||
|
onPressed: action.onPressed,
|
||||||
|
textColor: Theme.of(context).colorScheme.onInverseSurface,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
duration: duration,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void showGenericError(
|
||||||
|
BuildContext context,
|
||||||
|
dynamic error, [
|
||||||
|
StackTrace? stackTrace,
|
||||||
|
]) {
|
||||||
|
showSnackBar(
|
||||||
|
context,
|
||||||
|
error.toString(),
|
||||||
|
action: SnackBarActionConfig(
|
||||||
|
label: S.of(context).errorReportLabel,
|
||||||
|
onPressed: () => GithubIssueService.createIssueFromError(
|
||||||
|
context,
|
||||||
|
stackTrace: stackTrace,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
log(
|
||||||
|
"An error has occurred.",
|
||||||
|
error: error,
|
||||||
|
stackTrace: stackTrace,
|
||||||
|
time: DateTime.now(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void showLocalizedError(
|
||||||
|
BuildContext context,
|
||||||
|
String localizedMessage, [
|
||||||
|
StackTrace? stackTrace,
|
||||||
|
]) {
|
||||||
|
showSnackBar(context, localizedMessage);
|
||||||
|
log(localizedMessage, stackTrace: stackTrace);
|
||||||
|
}
|
||||||
|
|
||||||
|
void showErrorMessage(
|
||||||
|
BuildContext context,
|
||||||
|
PaperlessServerException error, [
|
||||||
|
StackTrace? stackTrace,
|
||||||
|
]) {
|
||||||
|
showSnackBar(
|
||||||
|
context,
|
||||||
|
translateError(context, error.code),
|
||||||
|
details: error.details,
|
||||||
|
);
|
||||||
|
log(
|
||||||
|
"An error has occurred.",
|
||||||
|
error: error,
|
||||||
|
stackTrace: stackTrace,
|
||||||
|
time: DateTime.now(),
|
||||||
|
);
|
||||||
|
}
|
||||||
14
lib/helpers/permission_helpers.dart
Normal file
14
lib/helpers/permission_helpers.dart
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
|
|
||||||
|
Future<bool> askForPermission(Permission permission) async {
|
||||||
|
final status = await permission.request();
|
||||||
|
log("Permission requested, new status is $status");
|
||||||
|
// If user has permanently declined permission, open settings.
|
||||||
|
if (status == PermissionStatus.permanentlyDenied) {
|
||||||
|
await openAppSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
return status == PermissionStatus.granted;
|
||||||
|
}
|
||||||
182
lib/util.dart
182
lib/util.dart
@@ -1,183 +1 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'dart:developer';
|
|
||||||
import 'dart:io';
|
|
||||||
import 'dart:ui';
|
|
||||||
|
|
||||||
import 'package:device_info_plus/device_info_plus.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:intl/intl.dart';
|
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
|
||||||
import 'package:paperless_mobile/core/translation/error_code_localization_mapper.dart';
|
|
||||||
import 'package:paperless_mobile/core/service/github_issue_service.dart';
|
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
|
||||||
|
|
||||||
final dateFormat = DateFormat("yyyy-MM-dd");
|
|
||||||
final GlobalKey<ScaffoldState> rootScaffoldKey = GlobalKey<ScaffoldState>();
|
|
||||||
|
|
||||||
class SnackBarActionConfig {
|
|
||||||
final String label;
|
|
||||||
final VoidCallback onPressed;
|
|
||||||
|
|
||||||
SnackBarActionConfig({
|
|
||||||
required this.label,
|
|
||||||
required this.onPressed,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void showSnackBar(
|
|
||||||
BuildContext context,
|
|
||||||
String message, {
|
|
||||||
String? details,
|
|
||||||
SnackBarActionConfig? action,
|
|
||||||
Duration duration = const Duration(seconds: 5),
|
|
||||||
}) {
|
|
||||||
ScaffoldMessenger.of(context)
|
|
||||||
..hideCurrentSnackBar()
|
|
||||||
..showSnackBar(
|
|
||||||
SnackBar(
|
|
||||||
content: (details != null)
|
|
||||||
? RichText(
|
|
||||||
maxLines: 5,
|
|
||||||
text: TextSpan(
|
|
||||||
text: message,
|
|
||||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
|
||||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
|
||||||
),
|
|
||||||
children: <TextSpan>[
|
|
||||||
TextSpan(
|
|
||||||
text: "\n$details",
|
|
||||||
style: const TextStyle(
|
|
||||||
fontStyle: FontStyle.italic,
|
|
||||||
fontSize: 10,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: Text(message),
|
|
||||||
action: action != null
|
|
||||||
? SnackBarAction(
|
|
||||||
label: action.label,
|
|
||||||
onPressed: action.onPressed,
|
|
||||||
textColor: Theme.of(context).colorScheme.onInverseSurface,
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
duration: duration,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void showGenericError(
|
|
||||||
BuildContext context,
|
|
||||||
dynamic error, [
|
|
||||||
StackTrace? stackTrace,
|
|
||||||
]) {
|
|
||||||
showSnackBar(
|
|
||||||
context,
|
|
||||||
error.toString(),
|
|
||||||
action: SnackBarActionConfig(
|
|
||||||
label: S.of(context).errorReportLabel,
|
|
||||||
onPressed: () => GithubIssueService.createIssueFromError(
|
|
||||||
context,
|
|
||||||
stackTrace: stackTrace,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
log(
|
|
||||||
"An error has occurred.",
|
|
||||||
error: error,
|
|
||||||
stackTrace: stackTrace,
|
|
||||||
time: DateTime.now(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void showLocalizedError(
|
|
||||||
BuildContext context,
|
|
||||||
String localizedMessage, [
|
|
||||||
StackTrace? stackTrace,
|
|
||||||
]) {
|
|
||||||
showSnackBar(context, localizedMessage);
|
|
||||||
log(localizedMessage, stackTrace: stackTrace);
|
|
||||||
}
|
|
||||||
|
|
||||||
void showErrorMessage(
|
|
||||||
BuildContext context,
|
|
||||||
PaperlessServerException error, [
|
|
||||||
StackTrace? stackTrace,
|
|
||||||
]) {
|
|
||||||
showSnackBar(
|
|
||||||
context,
|
|
||||||
translateError(context, error.code),
|
|
||||||
details: error.details,
|
|
||||||
);
|
|
||||||
log(
|
|
||||||
"An error has occurred.",
|
|
||||||
error: error,
|
|
||||||
stackTrace: stackTrace,
|
|
||||||
time: DateTime.now(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isNotNull(dynamic value) {
|
|
||||||
return value != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
String formatDate(DateTime date) {
|
|
||||||
return dateFormat.format(date);
|
|
||||||
}
|
|
||||||
|
|
||||||
String? formatDateNullable(DateTime? date) {
|
|
||||||
if (date == null) return null;
|
|
||||||
return dateFormat.format(date);
|
|
||||||
}
|
|
||||||
|
|
||||||
String extractFilenameFromPath(String path) {
|
|
||||||
return path.split(RegExp('[./]')).reversed.skip(1).first;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Taken from https://github.com/flutter/flutter/issues/26127#issuecomment-782083060
|
|
||||||
Future<void> loadImage(ImageProvider provider) {
|
|
||||||
final config = ImageConfiguration(
|
|
||||||
bundle: rootBundle,
|
|
||||||
devicePixelRatio: window.devicePixelRatio,
|
|
||||||
platform: defaultTargetPlatform,
|
|
||||||
);
|
|
||||||
final Completer<void> completer = Completer();
|
|
||||||
final ImageStream stream = provider.resolve(config);
|
|
||||||
|
|
||||||
late final ImageStreamListener listener;
|
|
||||||
|
|
||||||
listener = ImageStreamListener((ImageInfo image, bool sync) {
|
|
||||||
debugPrint("Image ${image.debugLabel} finished loading");
|
|
||||||
completer.complete();
|
|
||||||
stream.removeListener(listener);
|
|
||||||
}, onError: (dynamic exception, StackTrace? stackTrace) {
|
|
||||||
completer.complete();
|
|
||||||
stream.removeListener(listener);
|
|
||||||
FlutterError.reportError(FlutterErrorDetails(
|
|
||||||
context: ErrorDescription('image failed to load'),
|
|
||||||
library: 'image resource service',
|
|
||||||
exception: exception,
|
|
||||||
stack: stackTrace,
|
|
||||||
silent: true,
|
|
||||||
));
|
|
||||||
});
|
|
||||||
|
|
||||||
stream.addListener(listener);
|
|
||||||
return completer.future;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<bool> askForPermission(Permission permission) async {
|
|
||||||
final status = await permission.request();
|
|
||||||
log("Permission requested, new status is $status");
|
|
||||||
// If user has permanently declined permission, open settings.
|
|
||||||
if (status == PermissionStatus.permanentlyDenied) {
|
|
||||||
await openAppSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
return status == PermissionStatus.granted;
|
|
||||||
}
|
|
||||||
|
|||||||
48
pubspec.lock
48
pubspec.lock
@@ -5,18 +5,34 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: _fe_analyzer_shared
|
name: _fe_analyzer_shared
|
||||||
sha256: "0c80aeab9bc807ab10022cd3b2f4cf2ecdf231949dc1ddd9442406a003f19201"
|
sha256: "8c7478991c7bbde2c1e18034ac697723176a5d3e7e0ca06c7f9aed69b6f388d7"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "52.0.0"
|
version: "51.0.0"
|
||||||
analyzer:
|
analyzer:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: analyzer
|
name: analyzer
|
||||||
sha256: cd8ee83568a77f3ae6b913a36093a1c9b1264e7cb7f834d9ddd2311dade9c1f4
|
sha256: "120fe7ce25377ba616bb210e7584983b163861f45d6ec446744d507e3943881b"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.4.0"
|
version: "5.3.1"
|
||||||
|
analyzer_plugin:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: analyzer_plugin
|
||||||
|
sha256: c1d5f167683de03d5ab6c3b53fc9aeefc5d59476e7810ba7bbddff50c6f4392d
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.11.2"
|
||||||
|
ansicolor:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: ansicolor
|
||||||
|
sha256: "607f8fa9786f392043f169898923e6c59b4518242b68b8862eb8a8b7d9c30b4a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.1"
|
||||||
archive:
|
archive:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -305,6 +321,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.17.2"
|
version: "0.17.2"
|
||||||
|
dart_code_metrics:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: dart_code_metrics
|
||||||
|
sha256: "95f22e95638c0dfb0cb4e3ba45e00bb06dd509c98f06d4c0fa45340b0a5392e0"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.4.0"
|
||||||
|
dart_code_metrics_presets:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: dart_code_metrics_presets
|
||||||
|
sha256: "43dc1fdcb424fc3aa79964304d09eeda4f199351c52cdc854f8228a9d0296b60"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
dart_style:
|
dart_style:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1276,6 +1308,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.3"
|
version: "2.1.3"
|
||||||
|
pub_updater:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pub_updater
|
||||||
|
sha256: "00e42b515aa046b171d05bbe2dd566c0feaab7808c33c5bacb5beff93cf16561"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.3"
|
||||||
pubspec_parse:
|
pubspec_parse:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ dev_dependencies:
|
|||||||
intl_utils: ^2.7.0
|
intl_utils: ^2.7.0
|
||||||
flutter_lints: ^1.0.0
|
flutter_lints: ^1.0.0
|
||||||
json_serializable: ^6.5.4
|
json_serializable: ^6.5.4
|
||||||
|
dart_code_metrics: ^5.4.0
|
||||||
|
|
||||||
# For information on the generic Dart part of this file, see the
|
# For information on the generic Dart part of this file, see the
|
||||||
# following page: https://dart.dev/tools/pub/pubspec
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
|
|||||||
Reference in New Issue
Block a user