Removed unused files, code cleanup

This commit is contained in:
Anton Stubenbord
2023-01-22 14:34:58 +01:00
parent b370fa4164
commit 9bfb6aa661
42 changed files with 248 additions and 589 deletions

View File

@@ -1,7 +1,6 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/bloc/paperless_server_information_state.dart';
import 'package:paperless_mobile/core/security/session_manager.dart';
class PaperlessServerInformationCubit
extends Cubit<PaperlessServerInformationState> {

View File

@@ -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,
});
}

View File

@@ -1,5 +0,0 @@
extension DateComparisons on DateTime {
bool isEqualToIgnoringDate(DateTime other) {
return day == other.day && month == other.month && year == other.year;
}
}

View File

@@ -1,6 +1,5 @@
import 'package:hydrated_bloc/hydrated_bloc.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';
extension AddressableHydratedStorage on Storage {

View File

@@ -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,
);
}
}

View File

@@ -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;
}
}

View File

@@ -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")),
],
);
}
}

View File

@@ -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/similar_documents/cubit/similar_documents_cubit.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:share_plus/share_plus.dart';
import 'package:badges/badges.dart' as b;

View File

@@ -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/list/document_list_item.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';
class SimilarDocumentsView extends StatefulWidget {

View File

@@ -5,6 +5,7 @@ import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/service/file_service.dart';
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
import 'package:paperless_mobile/generated/l10n.dart';
import 'package:paperless_mobile/helpers/message_helpers.dart';
import 'package:paperless_mobile/util.dart';
import 'package:provider/provider.dart';

View File

@@ -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/view/widgets/label_form_field.dart';
import 'package:paperless_mobile/generated/l10n.dart';
import 'package:paperless_mobile/helpers/message_helpers.dart';
import 'package:paperless_mobile/util.dart';
class DocumentUploadPreparationPage extends StatefulWidget {

View File

@@ -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/view/widgets/label_form_field.dart';
import 'package:paperless_mobile/generated/l10n.dart';
import 'package:paperless_mobile/helpers/message_helpers.dart';
import 'package:paperless_mobile/util.dart';
class DocumentEditPage extends StatefulWidget {

View File

@@ -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/tasks/cubit/task_status_cubit.dart';
import 'package:paperless_mobile/generated/l10n.dart';
import 'package:paperless_mobile/helpers/message_helpers.dart';
import 'package:paperless_mobile/util.dart';
class DocumentFilterIntent {

View File

@@ -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 [],
);
}
}

View File

@@ -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();
});
}
}

View File

@@ -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/view/label_form.dart';
import 'package:paperless_mobile/generated/l10n.dart';
import 'package:paperless_mobile/helpers/message_helpers.dart';
import 'package:paperless_mobile/util.dart';
class EditLabelPage<T extends Label> extends StatelessWidget {

View File

@@ -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/extensions/flutter_extensions.dart';
import 'package:paperless_mobile/generated/l10n.dart';
import 'package:paperless_mobile/helpers/message_helpers.dart';
import 'package:paperless_mobile/util.dart';
class SubmitButtonConfig<T extends Label> {

View File

@@ -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/tasks/cubit/task_status_cubit.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:receive_sharing_intent/receive_sharing_intent.dart';
import 'package:responsive_builder/responsive_builder.dart';
@@ -136,7 +137,7 @@ class _HomePageState extends State<HomePage> {
toastLength: Toast.LENGTH_LONG,
);
}
} catch (e, stackTrace) {
} catch (e) {
Fluttertoast.showToast(
msg: S.of(context).receiveSharedFilePermissionDeniedMessage,
toastLength: Toast.LENGTH_LONG,
@@ -236,7 +237,6 @@ class _HomePageState extends State<HomePage> {
builder: (context, sizingInformation) {
if (!sizingInformation.isMobile) {
return Scaffold(
key: rootScaffoldKey,
drawer: const AppDrawer(),
body: Row(
children: [
@@ -257,7 +257,6 @@ class _HomePageState extends State<HomePage> {
);
}
return Scaffold(
key: rootScaffoldKey,
bottomNavigationBar: NavigationBar(
elevation: 4.0,
selectedIndex: _currentIndex,

View File

@@ -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/view/settings_page.dart';
import 'package:paperless_mobile/generated/l10n.dart';
import 'package:paperless_mobile/helpers/message_helpers.dart';
import 'package:paperless_mobile/util.dart';
import 'package:url_launcher/link.dart';
import 'package:url_launcher/url_launcher_string.dart';

View File

@@ -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_item.dart';
import 'package:paperless_mobile/generated/l10n.dart';
import 'package:paperless_mobile/helpers/message_helpers.dart';
import 'package:paperless_mobile/util.dart';
class InboxPage extends StatefulWidget {

View File

@@ -1,8 +1,9 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.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_preview/view/pages/linked_documents_page.dart';
import 'package:paperless_mobile/features/linked_documents/bloc/linked_documents_cubit.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 {
final T label;
@@ -37,7 +38,7 @@ class LabelItem<T extends Label> extends StatelessWidget {
Widget _buildReferencedDocumentsWidget(BuildContext context) {
return TextButton.icon(
label: const Icon(Icons.link),
icon: Text(_formatDocumentCount(label.documentCount)),
icon: Text(formatMaxCount(label.documentCount)),
onPressed: (label.documentCount ?? 0) == 0
? 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);
}
}

View File

@@ -1,6 +1,6 @@
import 'package:flutter_bloc/flutter_bloc.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> {
final PaperlessDocumentsApi _api;

View File

@@ -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/view/pages/document_details_page.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_preview/bloc/state/linked_documents_state.dart';
import 'package:paperless_mobile/features/linked_documents/bloc/linked_documents_cubit.dart';
import 'package:paperless_mobile/features/linked_documents/bloc/state/linked_documents_state.dart';
import 'package:paperless_mobile/generated/l10n.dart';
class LinkedDocumentsPage extends StatefulWidget {

View File

@@ -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/login_pages/server_connection_page.dart';
import 'package:paperless_mobile/generated/l10n.dart';
import 'package:paperless_mobile/helpers/message_helpers.dart';
import 'package:paperless_mobile/util.dart';
import 'widgets/never_scrollable_scroll_behavior.dart';

View File

@@ -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/view/add_saved_view_page.dart';
import 'package:paperless_mobile/generated/l10n.dart';
import 'package:paperless_mobile/helpers/message_helpers.dart';
import 'package:paperless_mobile/util.dart';
import 'package:shimmer/shimmer.dart';

View File

@@ -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/service/file_service.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/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/documents/view/pages/document_view.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/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/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:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
@@ -218,7 +219,7 @@ class _ScannerPageState extends State<ScannerPage>
mainAxisSpacing: 10,
),
itemBuilder: (context, index) {
return GridImageItemWidget(
return ScannedImageItem(
file: scans[index],
onDelete: () async {
try {

View File

@@ -7,7 +7,7 @@ import 'package:photo_view/photo_view.dart';
typedef DeleteCallback = void Function();
typedef OnImageOperation = void Function(File);
class GridImageItemWidget extends StatefulWidget {
class ScannedImageItem extends StatefulWidget {
final File file;
final DeleteCallback onDelete;
//final OnImageOperation onImageOperation;
@@ -15,7 +15,7 @@ class GridImageItemWidget extends StatefulWidget {
final int index;
final int totalNumberOfFiles;
const GridImageItemWidget({
const ScannedImageItem({
Key? key,
required this.file,
required this.onDelete,
@@ -25,10 +25,10 @@ class GridImageItemWidget extends StatefulWidget {
}) : super(key: key);
@override
State<GridImageItemWidget> createState() => _GridImageItemWidgetState();
State<ScannedImageItem> createState() => _ScannedImageItemState();
}
class _GridImageItemWidgetState extends State<GridImageItemWidget> {
class _ScannedImageItemState extends State<ScannedImageItem> {
@override
Widget build(BuildContext context) {
return GestureDetector(

View File

@@ -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!"),
);
}),
);
}
}

View File

@@ -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"),
),
],
);
}
}

View File

@@ -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());
}

View File

@@ -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 {}

View File

@@ -0,0 +1,3 @@
String extractFilenameFromPath(String path) {
return path.split(RegExp('[./]')).reversed.skip(1).first;
}

View 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);
}

View 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;
}

View 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(),
);
}

View 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;
}

View File

@@ -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;
}

View File

@@ -5,18 +5,34 @@ packages:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: "0c80aeab9bc807ab10022cd3b2f4cf2ecdf231949dc1ddd9442406a003f19201"
sha256: "8c7478991c7bbde2c1e18034ac697723176a5d3e7e0ca06c7f9aed69b6f388d7"
url: "https://pub.dev"
source: hosted
version: "52.0.0"
version: "51.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: cd8ee83568a77f3ae6b913a36093a1c9b1264e7cb7f834d9ddd2311dade9c1f4
sha256: "120fe7ce25377ba616bb210e7584983b163861f45d6ec446744d507e3943881b"
url: "https://pub.dev"
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:
dependency: transitive
description:
@@ -305,6 +321,22 @@ packages:
url: "https://pub.dev"
source: hosted
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:
dependency: transitive
description:
@@ -1276,6 +1308,14 @@ packages:
url: "https://pub.dev"
source: hosted
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:
dependency: transitive
description:

View File

@@ -100,6 +100,7 @@ dev_dependencies:
intl_utils: ^2.7.0
flutter_lints: ^1.0.0
json_serializable: ^6.5.4
dart_code_metrics: ^5.4.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec