chore+fix+feat: Apply dart fixes after upgrade to flutter 3.10, add permission checks, make most api calls work again

This commit is contained in:
Anton Stubenbord
2023-05-12 00:16:30 +02:00
parent c8ff261fc7
commit 39342eecf1
114 changed files with 546 additions and 685 deletions
@@ -0,0 +1,5 @@
class ServerMessageException implements Exception {
final String message;
ServerMessageException(this.message);
}
@@ -2,6 +2,7 @@ import 'dart:io';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/exception/server_message_exception.dart';
import 'package:paperless_mobile/core/type/types.dart'; import 'package:paperless_mobile/core/type/types.dart';
class DioHttpErrorInterceptor extends Interceptor { class DioHttpErrorInterceptor extends Interceptor {
@@ -16,14 +17,15 @@ class DioHttpErrorInterceptor extends Interceptor {
return _handlePlainError(data, handler, err); return _handlePlainError(data, handler, err);
} }
} else if (err.response?.statusCode == 403) { } else if (err.response?.statusCode == 403) {
handler.reject( var data = err.response!.data;
DioError( if (data is Map && data.containsKey("detail")) {
handler.reject(DioError(
requestOptions: err.requestOptions, requestOptions: err.requestOptions,
error: const PaperlessServerException(ErrorCode.notAuthorized), error: ServerMessageException(data['detail']),
response: err.response, response: err.response,
), ));
);
return; return;
}
} else if (err.error is SocketException) { } else if (err.error is SocketException) {
final ex = err.error as SocketException; final ex = err.error as SocketException;
if (ex.osError?.errorCode == _OsErrorCodes.serverUnreachable.code) { if (ex.osError?.errorCode == _OsErrorCodes.serverUnreachable.code) {
@@ -84,8 +86,7 @@ class DioHttpErrorInterceptor extends Interceptor {
} }
enum _OsErrorCodes { enum _OsErrorCodes {
serverUnreachable(101), serverUnreachable(101);
hostNotFound(7);
const _OsErrorCodes(this.code); const _OsErrorCodes(this.code);
final int code; final int code;
+60 -4
View File
@@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
import 'package:paperless_mobile/core/config/hive/hive_config.dart'; import 'package:paperless_mobile/core/config/hive/hive_config.dart';
import 'package:paperless_mobile/core/database/tables/global_settings.dart'; import 'package:paperless_mobile/core/database/tables/global_settings.dart';
import 'package:paperless_mobile/core/database/tables/local_user_app_state.dart'; import 'package:paperless_mobile/core/database/tables/local_user_app_state.dart';
@@ -12,13 +13,19 @@ import 'package:paperless_mobile/core/repository/user_repository.dart';
import 'package:paperless_mobile/features/document_search/cubit/document_search_cubit.dart'; import 'package:paperless_mobile/features/document_search/cubit/document_search_cubit.dart';
import 'package:paperless_mobile/features/document_search/view/document_search_page.dart'; import 'package:paperless_mobile/features/document_search/view/document_search_page.dart';
import 'package:paperless_mobile/features/home/view/model/api_version.dart'; import 'package:paperless_mobile/features/home/view/model/api_version.dart';
import 'package:paperless_mobile/features/linked_documents/cubit/linked_documents_cubit.dart';
import 'package:paperless_mobile/features/linked_documents/view/linked_documents_page.dart';
import 'package:paperless_mobile/features/notifications/services/local_notification_service.dart'; import 'package:paperless_mobile/features/notifications/services/local_notification_service.dart';
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.dart'; import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.dart';
import 'package:paperless_mobile/features/saved_view/view/add_saved_view_page.dart';
import 'package:paperless_mobile/features/saved_view_details/cubit/saved_view_details_cubit.dart'; import 'package:paperless_mobile/features/saved_view_details/cubit/saved_view_details_cubit.dart';
import 'package:paperless_mobile/features/saved_view_details/view/saved_view_details_page.dart'; import 'package:paperless_mobile/features/saved_view_details/view/saved_view_details_page.dart';
import 'package:paperless_mobile/routes/document_details_route.dart'; import 'package:paperless_mobile/routes/document_details_route.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
// These are convenience methods for nativating to views without having to pass providers around explicitly.
// Providers unfortunately have to be passed to the routes since they are children of the Navigator, not ancestors.
Future<void> pushDocumentSearchPage(BuildContext context) { Future<void> pushDocumentSearchPage(BuildContext context) {
final currentUser = final currentUser =
Hive.box<GlobalSettings>(HiveBoxes.globalSettings).getValue()!.currentLoggedInUser; Hive.box<GlobalSettings>(HiveBoxes.globalSettings).getValue()!.currentLoggedInUser;
@@ -64,6 +71,7 @@ Future<void> pushDocumentDetailsRoute(
Provider.value(value: context.read<PaperlessDocumentsApi>()), Provider.value(value: context.read<PaperlessDocumentsApi>()),
Provider.value(value: context.read<LocalNotificationService>()), Provider.value(value: context.read<LocalNotificationService>()),
Provider.value(value: context.read<CacheManager>()), Provider.value(value: context.read<CacheManager>()),
Provider.value(value: context.read<ConnectivityCubit>()),
if (context.read<ApiVersion>().hasMultiUserSupport) if (context.read<ApiVersion>().hasMultiUserSupport)
Provider.value(value: context.read<UserRepository>()), Provider.value(value: context.read<UserRepository>()),
], ],
@@ -76,19 +84,23 @@ Future<void> pushDocumentDetailsRoute(
); );
} }
Future<void> pushSavedViewDetailsRoute(BuildContext context, {required SavedView savedView}) { Future<void> pushSavedViewDetailsRoute(
BuildContext context, {
required SavedView savedView,
}) {
final apiVersion = context.read<ApiVersion>();
return Navigator.of(context).push( return Navigator.of(context).push(
MaterialPageRoute( MaterialPageRoute(
builder: (_) => MultiProvider( builder: (_) => MultiProvider(
providers: [ providers: [
Provider.value(value: apiVersion),
if (apiVersion.hasMultiUserSupport) Provider.value(value: context.read<UserRepository>()),
Provider.value(value: context.read<LabelRepository>()), Provider.value(value: context.read<LabelRepository>()),
Provider.value(value: context.read<DocumentChangedNotifier>()), Provider.value(value: context.read<DocumentChangedNotifier>()),
Provider.value(value: context.read<PaperlessDocumentsApi>()), Provider.value(value: context.read<PaperlessDocumentsApi>()),
Provider.value(value: context.read<CacheManager>()), Provider.value(value: context.read<CacheManager>()),
Provider.value(value: context.read<ConnectivityCubit>()),
], ],
child: SavedViewDetailsPage(
onDelete: context.read<SavedViewCubit>().remove,
),
builder: (_, child) { builder: (_, child) {
return BlocProvider( return BlocProvider(
create: (context) => SavedViewDetailsCubit( create: (context) => SavedViewDetailsCubit(
@@ -105,3 +117,47 @@ Future<void> pushSavedViewDetailsRoute(BuildContext context, {required SavedView
), ),
); );
} }
Future<SavedView?> pushAddSavedViewRoute(BuildContext context, {required DocumentFilter filter}) {
return Navigator.of(context).push<SavedView?>(
MaterialPageRoute(
builder: (_) => AddSavedViewPage(
currentFilter: filter,
correspondents: context.read<LabelRepository>().state.correspondents,
documentTypes: context.read<LabelRepository>().state.documentTypes,
storagePaths: context.read<LabelRepository>().state.storagePaths,
tags: context.read<LabelRepository>().state.tags,
),
),
);
}
Future<void> pushLinkedDocumentsView(BuildContext context, {required DocumentFilter filter}) {
return Navigator.push(
context,
MaterialPageRoute(
builder: (_) => MultiProvider(
providers: [
Provider.value(value: context.read<ApiVersion>()),
Provider.value(value: context.read<LabelRepository>()),
Provider.value(value: context.read<DocumentChangedNotifier>()),
Provider.value(value: context.read<PaperlessDocumentsApi>()),
Provider.value(value: context.read<LocalNotificationService>()),
Provider.value(value: context.read<CacheManager>()),
Provider.value(value: context.read<ConnectivityCubit>()),
if (context.read<ApiVersion>().hasMultiUserSupport)
Provider.value(value: context.read<UserRepository>()),
],
builder: (context, _) => BlocProvider(
create: (context) => LinkedDocumentsCubit(
filter,
context.read(),
context.read(),
context.read(),
),
child: const LinkedDocumentsPage(),
),
),
),
);
}
@@ -1,5 +1,4 @@
import 'dart:async'; import 'dart:async';
import 'dart:developer';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
+3 -2
View File
@@ -1,7 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/repository/label_repository_state.dart'; import 'package:paperless_mobile/core/repository/label_repository_state.dart';
import 'package:paperless_mobile/core/repository/persistent_repository.dart'; import 'package:paperless_mobile/core/repository/persistent_repository.dart';
@@ -9,7 +8,9 @@ import 'package:paperless_mobile/core/repository/persistent_repository.dart';
class LabelRepository extends PersistentRepository<LabelRepositoryState> { class LabelRepository extends PersistentRepository<LabelRepositoryState> {
final PaperlessLabelsApi _api; final PaperlessLabelsApi _api;
LabelRepository(this._api) : super(const LabelRepositoryState()); LabelRepository(this._api) : super(const LabelRepositoryState()) {
initialize();
}
Future<void> initialize() { Future<void> initialize() {
debugPrint("Initializing labels..."); debugPrint("Initializing labels...");
@@ -7,7 +7,9 @@ import 'package:paperless_mobile/core/repository/saved_view_repository_state.dar
class SavedViewRepository extends PersistentRepository<SavedViewRepositoryState> { class SavedViewRepository extends PersistentRepository<SavedViewRepositoryState> {
final PaperlessSavedViewsApi _api; final PaperlessSavedViewsApi _api;
SavedViewRepository(this._api) : super(const SavedViewRepositoryState()); SavedViewRepository(this._api) : super(const SavedViewRepositoryState()) {
initialize();
}
Future<void> initialize() { Future<void> initialize() {
return findAll(); return findAll();
@@ -1,4 +1,3 @@
import 'dart:developer';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:paperless_mobile/core/model/github_error_report.model.dart'; import 'package:paperless_mobile/core/model/github_error_report.model.dart';
+2 -10
View File
@@ -1,16 +1,8 @@
import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart';
import 'package:paperless_api/paperless_api.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/features/login/model/authentication_information.dart';
import 'package:paperless_mobile/constants.dart';
import 'package:paperless_mobile/core/database/tables/user_credentials.dart'; import 'package:paperless_mobile/core/database/tables/user_credentials.dart';
import 'package:web_socket_channel/io.dart'; // import 'package:web_socket_channel/io.dart';
abstract class StatusService { abstract class StatusService {
Future<void> startListeningBeforeDocumentUpload( Future<void> startListeningBeforeDocumentUpload(
@@ -19,7 +11,7 @@ abstract class StatusService {
class WebSocketStatusService implements StatusService { class WebSocketStatusService implements StatusService {
late WebSocket? socket; late WebSocket? socket;
late IOWebSocketChannel? _channel; // late IOWebSocketChannel? _channel;
WebSocketStatusService(); WebSocketStatusService();
@@ -72,7 +72,5 @@ String translateError(BuildContext context, ErrorCode code) {
return S.of(context)!.couldNotLoadSuggestions; return S.of(context)!.couldNotLoadSuggestions;
case ErrorCode.acknowledgeTasksError: case ErrorCode.acknowledgeTasksError:
return S.of(context)!.couldNotAcknowledgeTasks; return S.of(context)!.couldNotAcknowledgeTasks;
case ErrorCode.notAuthorized:
return "You do not have the permission to perform this action."; //TODO: INTL
} }
} }
-2
View File
@@ -1,5 +1,3 @@
import 'package:paperless_api/paperless_api.dart';
import 'package:rxdart/subjects.dart';
typedef JSON = Map<String, dynamic>; typedef JSON = Map<String, dynamic>;
typedef PaperlessValidationErrors = Map<String, String>; typedef PaperlessValidationErrors = Map<String, String>;
@@ -1,6 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:flutter/src/widgets/placeholder.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
class DialogCancelButton extends StatelessWidget { class DialogCancelButton extends StatelessWidget {
@@ -1,6 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:flutter/src/widgets/placeholder.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
enum DialogConfirmButtonStyle { enum DialogConfirmButtonStyle {
@@ -181,8 +181,7 @@ class FormBuilderColorPickerField extends FormBuilderField<Color> {
); );
@override @override
FormBuilderColorPickerFieldState createState() => FormBuilderColorPickerFieldState createState() => FormBuilderColorPickerFieldState();
FormBuilderColorPickerFieldState();
} }
class FormBuilderColorPickerFieldState class FormBuilderColorPickerFieldState
@@ -217,8 +216,6 @@ class FormBuilderColorPickerFieldState
final selected = await showDialog<bool>( final selected = await showDialog<bool>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
final materialLocalizations = S.of(context)!;
return AlertDialog( return AlertDialog(
// title: null, //const Text('Pick a color!'), // title: null, //const Text('Pick a color!'),
content: _buildColorPicker(), content: _buildColorPicker(),
-1
View File
@@ -1,5 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:paperless_mobile/extensions/flutter_extensions.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
+8 -19
View File
@@ -27,8 +27,7 @@ import 'package:flutter/services.dart';
typedef ChipsInputSuggestions<T> = Future<List<T>> Function(String query); typedef ChipsInputSuggestions<T> = Future<List<T>> Function(String query);
typedef ChipSelected<T> = void Function(T data, bool selected); typedef ChipSelected<T> = void Function(T data, bool selected);
typedef ChipsBuilder<T> = Widget Function( typedef ChipsBuilder<T> = Widget Function(BuildContext context, ChipsInputState<T> state, T data);
BuildContext context, ChipsInputState<T> state, T data);
class ChipsInput<T> extends StatefulWidget { class ChipsInput<T> extends StatefulWidget {
const ChipsInput({ const ChipsInput({
@@ -71,8 +70,7 @@ class ChipsInputState<T> extends State<ChipsInput<T>> {
TextEditingValue get currentTextEditingValue => _value; TextEditingValue get currentTextEditingValue => _value;
bool get _hasInputConnection => bool get _hasInputConnection => _connection != null && (_connection?.attached ?? false);
_connection != null && (_connection?.attached ?? false);
void requestKeyboard() { void requestKeyboard() {
if (_focusNode.hasFocus) { if (_focusNode.hasFocus) {
@@ -191,8 +189,7 @@ class ChipsInputState<T> extends State<ChipsInput<T>> {
child: ListView.builder( child: ListView.builder(
itemCount: _suggestions.length, itemCount: _suggestions.length,
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
return widget.suggestionBuilder( return widget.suggestionBuilder(context, this, _suggestions[index]);
context, this, _suggestions[index]);
}, },
), ),
), ),
@@ -213,14 +210,11 @@ class ChipsInputState<T> extends State<ChipsInput<T>> {
} }
int _countReplacements(TextEditingValue value) { int _countReplacements(TextEditingValue value) {
return value.text.codeUnits return value.text.codeUnits.where((ch) => ch == kObjectReplacementChar).length;
.where((ch) => ch == kObjectReplacementChar)
.length;
} }
void _updateTextInputState() { void _updateTextInputState() {
final text = final text = String.fromCharCodes(_chips.map((_) => kObjectReplacementChar));
String.fromCharCodes(_chips.map((_) => kObjectReplacementChar));
_value = TextEditingValue( _value = TextEditingValue(
text: text, text: text,
selection: TextSelection.collapsed(offset: text.length), selection: TextSelection.collapsed(offset: text.length),
@@ -233,35 +227,30 @@ class ChipsInputState<T> extends State<ChipsInput<T>> {
final localId = ++_searchId; final localId = ++_searchId;
final results = await widget.findSuggestions(value); final results = await widget.findSuggestions(value);
if (_searchId == localId && mounted) { if (_searchId == localId && mounted) {
setState(() => _suggestions = results setState(() => _suggestions =
.where((profile) => !_chips.contains(profile)) results.where((profile) => !_chips.contains(profile)).toList(growable: false));
.toList(growable: false));
} }
} }
} }
class _TextCaret extends StatefulWidget { class _TextCaret extends StatefulWidget {
const _TextCaret({ const _TextCaret({
this.duration = const Duration(milliseconds: 500),
this.resumed = false, this.resumed = false,
}); });
final Duration duration;
final bool resumed; final bool resumed;
@override @override
_TextCursorState createState() => _TextCursorState(); _TextCursorState createState() => _TextCursorState();
} }
class _TextCursorState extends State<_TextCaret> class _TextCursorState extends State<_TextCaret> with SingleTickerProviderStateMixin {
with SingleTickerProviderStateMixin {
bool _displayed = false; bool _displayed = false;
late Timer _timer; late Timer _timer;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_timer = Timer.periodic(widget.duration, _onTimer);
} }
void _onTimer(Timer timer) { void _onTimer(Timer timer) {
@@ -251,7 +251,6 @@ abstract class SearchDelegate<T> {
/// ///
/// Setting the query string programmatically moves the cursor to the end of the text field. /// Setting the query string programmatically moves the cursor to the end of the text field.
set query(String value) { set query(String value) {
assert(query != null);
_queryTextController.text = value; _queryTextController.text = value;
if (_queryTextController.text.isNotEmpty) { if (_queryTextController.text.isNotEmpty) {
_queryTextController.selection = TextSelection.fromPosition( _queryTextController.selection = TextSelection.fromPosition(
@@ -7,7 +7,6 @@ import 'dart:ui';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
const int _kOpenViewMilliseconds = 600; const int _kOpenViewMilliseconds = 600;
const Duration _kOpenViewDuration = const Duration _kOpenViewDuration =
@@ -1649,7 +1648,6 @@ class SearchBarTheme extends InheritedWidget {
final SearchBarTheme? searchBarTheme = final SearchBarTheme? searchBarTheme =
context.dependOnInheritedWidgetOfExactType<SearchBarTheme>(); context.dependOnInheritedWidgetOfExactType<SearchBarTheme>();
return searchBarTheme?.data ?? const SearchBarThemeData(); return searchBarTheme?.data ?? const SearchBarThemeData();
;
} }
@override @override
@@ -1,6 +1,5 @@
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:paperless_mobile/constants.dart'; import 'package:paperless_mobile/constants.dart';
import 'package:paperless_mobile/core/widgets/paperless_logo.dart'; import 'package:paperless_mobile/core/widgets/paperless_logo.dart';
@@ -50,7 +50,7 @@ class _ApplicationIntroSlideshowState extends State<ApplicationIntroSlideshow> {
image: AssetImages.organizeDocuments.image, image: AssetImages.organizeDocuments.image,
), ),
), ),
bodyWidget: Column( bodyWidget: const Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
@@ -70,7 +70,7 @@ class _ApplicationIntroSlideshowState extends State<ApplicationIntroSlideshow> {
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Image(image: AssetImages.secureDocuments.image), child: Image(image: AssetImages.secureDocuments.image),
), ),
bodyWidget: Column( bodyWidget: const Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
@@ -90,8 +90,8 @@ class _ApplicationIntroSlideshowState extends State<ApplicationIntroSlideshow> {
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Image(image: AssetImages.success.image), child: Image(image: AssetImages.success.image),
), ),
bodyWidget: Column( bodyWidget: const Column(
children: const [ children: [
BiometricAuthenticationSetting(), BiometricAuthenticationSetting(),
LanguageSelectionSetting(), LanguageSelectionSetting(),
ThemeModeSetting(), ThemeModeSetting(),
@@ -2,7 +2,6 @@ import 'dart:async';
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:equatable/equatable.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart'; import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
import 'package:paperless_mobile/core/repository/label_repository.dart'; import 'package:paperless_mobile/core/repository/label_repository.dart';
@@ -17,6 +17,7 @@ class BulkEditLabelBottomSheet<T extends Label> extends StatefulWidget {
final LabelOptionsSelector<T> availableOptionsSelector; final LabelOptionsSelector<T> availableOptionsSelector;
final void Function(int? selectedId) onSubmit; final void Function(int? selectedId) onSubmit;
final int? initialValue; final int? initialValue;
final bool canCreateNewLabel;
const BulkEditLabelBottomSheet({ const BulkEditLabelBottomSheet({
super.key, super.key,
@@ -26,6 +27,7 @@ class BulkEditLabelBottomSheet<T extends Label> extends StatefulWidget {
required this.availableOptionsSelector, required this.availableOptionsSelector,
required this.onSubmit, required this.onSubmit,
this.initialValue, this.initialValue,
required this.canCreateNewLabel,
}); });
@override @override
@@ -58,6 +60,7 @@ class _BulkEditLabelBottomSheetState<T extends Label> extends State<BulkEditLabe
initialValue: widget.initialValue != null initialValue: widget.initialValue != null
? IdQueryParameter.fromId(widget.initialValue!) ? IdQueryParameter.fromId(widget.initialValue!)
: const IdQueryParameter.unset(), : const IdQueryParameter.unset(),
canCreateNewLabel: widget.canCreateNewLabel,
name: "labelFormField", name: "labelFormField",
options: widget.availableOptionsSelector(state), options: widget.availableOptionsSelector(state),
labelText: widget.formFieldLabel, labelText: widget.formFieldLabel,
@@ -1,6 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:flutter/src/widgets/placeholder.dart';
import 'package:paperless_mobile/core/widgets/dialog_utils/dialog_cancel_button.dart'; import 'package:paperless_mobile/core/widgets/dialog_utils/dialog_cancel_button.dart';
import 'package:paperless_mobile/core/widgets/dialog_utils/dialog_confirm_button.dart'; import 'package:paperless_mobile/core/widgets/dialog_utils/dialog_confirm_button.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
@@ -26,8 +26,8 @@ class _FullscreenBulkEditTagsWidgetState
/// Tags not assigned to at least one document in the selection /// Tags not assigned to at least one document in the selection
late final List<int> _nonSharedTags; late final List<int> _nonSharedTags;
List<int> _addTags = []; final List<int> _addTags = [];
List<int> _removeTags = []; final List<int> _removeTags = [];
late List<int> _filteredTags; late List<int> _filteredTags;
@override @override
@@ -41,7 +41,7 @@ class _SelectFileTypeDialogState extends State<SelectFileTypeDialog> {
}, },
title: Text(S.of(context)!.archivedPdf), title: Text(S.of(context)!.archivedPdf),
), ),
Divider(), const Divider(),
CheckboxListTile( CheckboxListTile(
controlAffinity: ListTileControlAffinity.leading, controlAffinity: ListTileControlAffinity.leading,
value: _rememberSelection, value: _rememberSelection,
@@ -1,13 +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:hive_flutter/adapters.dart';
import 'package:open_filex/open_filex.dart'; import 'package:open_filex/open_filex.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart'; import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
import 'package:paperless_mobile/core/config/hive/hive_config.dart';
import 'package:paperless_mobile/core/database/tables/global_settings.dart';
import 'package:paperless_mobile/core/database/tables/local_user_account.dart'; import 'package:paperless_mobile/core/database/tables/local_user_account.dart';
import 'package:paperless_mobile/core/repository/user_repository.dart';
import 'package:paperless_mobile/core/translation/error_code_localization_mapper.dart'; import 'package:paperless_mobile/core/translation/error_code_localization_mapper.dart';
import 'package:paperless_mobile/core/widgets/material/colored_tab_bar.dart'; import 'package:paperless_mobile/core/widgets/material/colored_tab_bar.dart';
import 'package:paperless_mobile/extensions/flutter_extensions.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.dart';
@@ -57,7 +53,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final apiVersion = context.watch<ApiVersion>(); final apiVersion = context.watch<ApiVersion>();
final tabLength = 4 + (apiVersion.supportsPermissions ? 1 : 0); final tabLength = 4 + (apiVersion.hasMultiUserSupport ? 1 : 0);
return WillPopScope( return WillPopScope(
onWillPop: () async { onWillPop: () async {
Navigator.of(context).pop(context.read<DocumentDetailsCubit>().state.document); Navigator.of(context).pop(context.read<DocumentDetailsCubit>().state.document);
@@ -155,7 +151,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
), ),
), ),
), ),
if (apiVersion.supportsPermissions) if (apiVersion.hasMultiUserSupport)
Tab( Tab(
child: Text( child: Text(
"Permissions", "Permissions",
@@ -260,13 +256,9 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
} }
Widget _buildEditButton() { Widget _buildEditButton() {
bool canEdit = context.watchInternetConnection; bool canEdit = context.watchInternetConnection &&
final apiVersion = context.watch<ApiVersion>(); LocalUserAccount.current.paperlessUser
.hasPermission(PermissionAction.change, PermissionTarget.document);
if (apiVersion.supportsPermissions) {
canEdit =
LocalUserAccount.current.paperlessUser.hasPermission(UserPermissions.changeDocument);
}
if (!canEdit) { if (!canEdit) {
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
@@ -281,7 +273,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
verticalOffset: 40, verticalOffset: 40,
child: FloatingActionButton( child: FloatingActionButton(
child: const Icon(Icons.edit), child: const Icon(Icons.edit),
onPressed: canEdit ? () => _onEdit(state.document) : null, onPressed: () => _onEdit(state.document),
), ),
); );
}, },
@@ -296,15 +288,16 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
builder: (context, connectivityState) { builder: (context, connectivityState) {
final isConnected = connectivityState.isConnected; final isConnected = connectivityState.isConnected;
final canDelete = LocalUserAccount.current.paperlessUser final canDelete = isConnected &&
.hasPermission(UserPermissions.deleteDocument); LocalUserAccount.current.paperlessUser
.hasPermission(PermissionAction.delete, PermissionTarget.document);
return Row( return Row(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: [ children: [
IconButton( IconButton(
tooltip: S.of(context)!.deleteDocumentTooltip, tooltip: S.of(context)!.deleteDocumentTooltip,
icon: const Icon(Icons.delete), icon: const Icon(Icons.delete),
onPressed: (isConnected && canDelete) ? () => _onDelete(state.document) : null, onPressed: canDelete ? () => _onDelete(state.document) : null,
).paddedSymmetrically(horizontal: 4), ).paddedSymmetrically(horizontal: 4),
DocumentDownloadButton( DocumentDownloadButton(
document: state.document, document: state.document,
@@ -3,6 +3,7 @@ import 'package:flutter/services.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/core/bloc/connectivity_cubit.dart'; import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
import 'package:paperless_mobile/core/database/tables/local_user_account.dart';
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/features/document_details/cubit/document_details_cubit.dart'; import 'package:paperless_mobile/features/document_details/cubit/document_details_cubit.dart';
@@ -17,8 +18,7 @@ class ArchiveSerialNumberField extends StatefulWidget {
}); });
@override @override
State<ArchiveSerialNumberField> createState() => State<ArchiveSerialNumberField> createState() => _ArchiveSerialNumberFieldState();
_ArchiveSerialNumberFieldState();
} }
class _ArchiveSerialNumberFieldState extends State<ArchiveSerialNumberField> { class _ArchiveSerialNumberFieldState extends State<ArchiveSerialNumberField> {
@@ -39,20 +39,21 @@ class _ArchiveSerialNumberFieldState extends State<ArchiveSerialNumberField> {
void _clearButtonListener() { void _clearButtonListener() {
setState(() { setState(() {
_showClearButton = _asnEditingController.text.isNotEmpty; _showClearButton = _asnEditingController.text.isNotEmpty;
_canUpdate = int.tryParse(_asnEditingController.text) != _canUpdate = int.tryParse(_asnEditingController.text) != widget.document.archiveSerialNumber;
widget.document.archiveSerialNumber;
}); });
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final userCanEditDocument = LocalUserAccount.current.paperlessUser.hasPermission(
PermissionAction.change,
PermissionTarget.document,
);
return BlocListener<DocumentDetailsCubit, DocumentDetailsState>( return BlocListener<DocumentDetailsCubit, DocumentDetailsState>(
listenWhen: (previous, current) => listenWhen: (previous, current) =>
previous.document.archiveSerialNumber != previous.document.archiveSerialNumber != current.document.archiveSerialNumber,
current.document.archiveSerialNumber,
listener: (context, state) { listener: (context, state) {
_asnEditingController.text = _asnEditingController.text = state.document.archiveSerialNumber?.toString() ?? '';
state.document.archiveSerialNumber?.toString() ?? '';
setState(() { setState(() {
_canUpdate = false; _canUpdate = false;
}); });
@@ -61,6 +62,7 @@ class _ArchiveSerialNumberFieldState extends State<ArchiveSerialNumberField> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
TextFormField( TextFormField(
enabled: userCanEditDocument,
controller: _asnEditingController, controller: _asnEditingController,
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
onChanged: (value) { onChanged: (value) {
@@ -78,15 +80,13 @@ class _ArchiveSerialNumberFieldState extends State<ArchiveSerialNumberField> {
IconButton( IconButton(
icon: const Icon(Icons.clear), icon: const Icon(Icons.clear),
color: Theme.of(context).colorScheme.primary, color: Theme.of(context).colorScheme.primary,
onPressed: _asnEditingController.clear, onPressed: userCanEditDocument ? _asnEditingController.clear : null,
), ),
IconButton( IconButton(
icon: const Icon(Icons.plus_one_rounded), icon: const Icon(Icons.plus_one_rounded),
color: Theme.of(context).colorScheme.primary, color: Theme.of(context).colorScheme.primary,
onPressed: onPressed:
context.watchInternetConnection && !_showClearButton context.watchInternetConnection && !_showClearButton ? _onAutoAssign : null,
? _onAutoAssign
: null,
).paddedOnly(right: 8), ).paddedOnly(right: 8),
], ],
), ),
@@ -97,9 +97,7 @@ class _ArchiveSerialNumberFieldState extends State<ArchiveSerialNumberField> {
), ),
TextButton.icon( TextButton.icon(
icon: const Icon(Icons.done), icon: const Icon(Icons.done),
onPressed: context.watchInternetConnection && _canUpdate onPressed: context.watchInternetConnection && _canUpdate ? _onSubmitted : null,
? _onSubmitted
: null,
label: Text(S.of(context)!.save), label: Text(S.of(context)!.save),
).padded(), ).padded(),
], ],
@@ -24,7 +24,7 @@ class DetailsItem extends StatelessWidget {
} }
DetailsItem.text( DetailsItem.text(
String text, { String text, {super.key,
required this.label, required this.label,
required BuildContext context, required BuildContext context,
}) : content = Text( }) : content = Text(
@@ -29,8 +29,8 @@ class _DocumentMetaDataWidgetState extends State<DocumentMetaDataWidget> {
builder: (context, state) { builder: (context, state) {
debugPrint("Building state..."); debugPrint("Building state...");
if (state.metaData == null) { if (state.metaData == null) {
return SliverToBoxAdapter( return const SliverToBoxAdapter(
child: const Center( child: Center(
child: CircularProgressIndicator(), child: CircularProgressIndicator(),
), ),
); );
@@ -4,7 +4,6 @@ import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/widgets/highlighted_text.dart'; import 'package:paperless_mobile/core/widgets/highlighted_text.dart';
import 'package:paperless_mobile/extensions/flutter_extensions.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.dart';
import 'package:paperless_mobile/features/document_details/view/widgets/details_item.dart'; import 'package:paperless_mobile/features/document_details/view/widgets/details_item.dart';
import 'package:paperless_mobile/features/labels/storage_path/view/widgets/storage_path_widget.dart';
import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_widget.dart'; import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_widget.dart';
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/generated/l10n/app_localizations.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
@@ -1,5 +1,4 @@
import 'dart:async'; import 'dart:async';
import 'dart:developer';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
@@ -8,6 +7,7 @@ import 'package:flutter_typeahead/flutter_typeahead.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/database/tables/local_user_account.dart';
import 'package:paperless_mobile/core/repository/label_repository.dart'; import 'package:paperless_mobile/core/repository/label_repository.dart';
import 'package:paperless_mobile/core/workarounds/colored_chip.dart'; import 'package:paperless_mobile/core/workarounds/colored_chip.dart';
import 'package:paperless_mobile/extensions/flutter_extensions.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.dart';
@@ -16,7 +16,6 @@ import 'package:paperless_mobile/features/edit_label/view/impl/add_correspondent
import 'package:paperless_mobile/features/edit_label/view/impl/add_document_type_page.dart'; import 'package:paperless_mobile/features/edit_label/view/impl/add_document_type_page.dart';
import 'package:paperless_mobile/features/edit_label/view/impl/add_storage_path_page.dart'; import 'package:paperless_mobile/features/edit_label/view/impl/add_storage_path_page.dart';
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/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/app_localizations.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
@@ -117,6 +116,11 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
name: fkCorrespondent, name: fkCorrespondent,
prefixIcon: const Icon(Icons.person_outlined), prefixIcon: const Icon(Icons.person_outlined),
allowSelectUnassigned: true, allowSelectUnassigned: true,
canCreateNewLabel:
LocalUserAccount.current.paperlessUser.hasPermission(
PermissionAction.add,
PermissionTarget.correspondent,
),
), ),
if (_filteredSuggestions?.hasSuggestedCorrespondents ?? false) if (_filteredSuggestions?.hasSuggestedCorrespondents ?? false)
_buildSuggestionsSkeleton<int>( _buildSuggestionsSkeleton<int>(
@@ -144,6 +148,11 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
initialName: currentInput, initialName: currentInput,
), ),
), ),
canCreateNewLabel:
LocalUserAccount.current.paperlessUser.hasPermission(
PermissionAction.add,
PermissionTarget.documentType,
),
addLabelText: S.of(context)!.addDocumentType, addLabelText: S.of(context)!.addDocumentType,
labelText: S.of(context)!.documentType, labelText: S.of(context)!.documentType,
initialValue: state.document.documentType != null initialValue: state.document.documentType != null
@@ -177,6 +186,11 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
value: context.read<LabelRepository>(), value: context.read<LabelRepository>(),
child: AddStoragePathPage(initalName: initialValue), child: AddStoragePathPage(initalName: initialValue),
), ),
canCreateNewLabel:
LocalUserAccount.current.paperlessUser.hasPermission(
PermissionAction.add,
PermissionTarget.storagePath,
),
addLabelText: S.of(context)!.addStoragePath, addLabelText: S.of(context)!.addStoragePath,
labelText: S.of(context)!.storagePath, labelText: S.of(context)!.storagePath,
options: state.storagePaths, options: state.storagePaths,
@@ -98,7 +98,7 @@ class _ScannedImageItemState extends State<ScannedImageItem> {
alignment: Alignment.bottomCenter, alignment: Alignment.bottomCenter,
child: TextButton( child: TextButton(
onPressed: widget.onDelete, onPressed: widget.onDelete,
child: Text("Remove"), child: const Text("Remove"),
), ),
), ),
], ],
@@ -3,15 +3,7 @@ import 'dart:async';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
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:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:hive/hive.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/config/hive/hive_config.dart';
import 'package:paperless_mobile/core/database/tables/global_settings.dart';
import 'package:paperless_mobile/core/database/tables/local_user_app_state.dart';
import 'package:paperless_mobile/core/navigation/push_routes.dart'; import 'package:paperless_mobile/core/navigation/push_routes.dart';
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
import 'package:paperless_mobile/core/repository/label_repository.dart';
import 'package:paperless_mobile/extensions/flutter_extensions.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.dart';
import 'package:paperless_mobile/features/document_search/cubit/document_search_cubit.dart'; import 'package:paperless_mobile/features/document_search/cubit/document_search_cubit.dart';
import 'package:paperless_mobile/features/document_search/view/remove_history_entry_dialog.dart'; import 'package:paperless_mobile/features/document_search/view/remove_history_entry_dialog.dart';
@@ -19,10 +11,8 @@ import 'package:paperless_mobile/features/documents/view/widgets/adaptive_docume
import 'package:paperless_mobile/features/documents/view/widgets/selection/view_type_selection_widget.dart'; import 'package:paperless_mobile/features/documents/view/widgets/selection/view_type_selection_widget.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
import 'package:paperless_mobile/routes/document_details_route.dart';
import 'dart:math' as math; import 'dart:math' as math;
import 'package:provider/provider.dart';
class DocumentSearchPage extends StatefulWidget { class DocumentSearchPage extends StatefulWidget {
const DocumentSearchPage({super.key}); const DocumentSearchPage({super.key});
@@ -1,5 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hive_flutter/adapters.dart'; import 'package:hive_flutter/adapters.dart';
import 'package:paperless_mobile/core/config/hive/hive_config.dart'; import 'package:paperless_mobile/core/config/hive/hive_config.dart';
import 'package:paperless_mobile/core/database/tables/local_user_account.dart'; import 'package:paperless_mobile/core/database/tables/local_user_account.dart';
@@ -35,6 +35,7 @@ class DocumentUploadCubit extends Cubit<DocumentUploadState> {
int? correspondent, int? correspondent,
Iterable<int> tags = const [], Iterable<int> tags = const [],
DateTime? createdAt, DateTime? createdAt,
int? asn,
}) async { }) async {
return await _documentApi.create( return await _documentApi.create(
bytes, bytes,
@@ -44,6 +45,7 @@ class DocumentUploadCubit extends Cubit<DocumentUploadState> {
documentType: documentType, documentType: documentType,
tags: tags, tags: tags,
createdAt: createdAt, createdAt: createdAt,
asn: asn,
); );
} }
@@ -7,6 +7,7 @@ import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:intl/date_symbol_data_local.dart'; import 'package:intl/date_symbol_data_local.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/database/tables/local_user_account.dart';
import 'package:paperless_mobile/core/repository/label_repository.dart'; import 'package:paperless_mobile/core/repository/label_repository.dart';
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';
@@ -32,7 +33,6 @@ class DocumentUploadPreparationPage extends StatefulWidget {
final String? filename; final String? filename;
final String? fileExtension; final String? fileExtension;
const DocumentUploadPreparationPage({ const DocumentUploadPreparationPage({
Key? key, Key? key,
required this.fileBytes, required this.fileBytes,
@@ -193,6 +193,10 @@ class _DocumentUploadPreparationPageState extends State<DocumentUploadPreparatio
options: state.correspondents, options: state.correspondents,
prefixIcon: const Icon(Icons.person_outline), prefixIcon: const Icon(Icons.person_outline),
allowSelectUnassigned: true, allowSelectUnassigned: true,
canCreateNewLabel: LocalUserAccount.current.paperlessUser.hasPermission(
PermissionAction.add,
PermissionTarget.correspondent,
),
), ),
// Document type // Document type
LabelFormField<DocumentType>( LabelFormField<DocumentType>(
@@ -208,6 +212,10 @@ class _DocumentUploadPreparationPageState extends State<DocumentUploadPreparatio
options: state.documentTypes, options: state.documentTypes,
prefixIcon: const Icon(Icons.description_outlined), prefixIcon: const Icon(Icons.description_outlined),
allowSelectUnassigned: true, allowSelectUnassigned: true,
canCreateNewLabel: LocalUserAccount.current.paperlessUser.hasPermission(
PermissionAction.add,
PermissionTarget.documentType,
),
), ),
TagsFormField( TagsFormField(
name: DocumentModel.tagsKey, name: DocumentModel.tagsKey,
@@ -239,10 +247,14 @@ class _DocumentUploadPreparationPageState extends State<DocumentUploadPreparatio
final createdAt = fv[DocumentModel.createdKey] as DateTime?; final createdAt = fv[DocumentModel.createdKey] as DateTime?;
final title = fv[DocumentModel.titleKey] as String; final title = fv[DocumentModel.titleKey] as String;
final docType = fv[DocumentModel.documentTypeKey] as SetIdQueryParameter; final docType = (fv[DocumentModel.documentTypeKey] as IdQueryParameter?)
final tags = fv[DocumentModel.tagsKey] as IdsTagsQuery; ?.whenOrNull(fromId: (id) => id);
final correspondent = fv[DocumentModel.correspondentKey] as SetIdQueryParameter; final tags = (fv[DocumentModel.tagsKey] as TagsQuery?)
?.whenOrNull(ids: (include, exclude) => include) ??
[];
final correspondent = (fv[DocumentModel.correspondentKey] as IdQueryParameter?)
?.whenOrNull(fromId: (id) => id);
final asn = fv[DocumentModel.asnKey] as int?;
final taskId = await cubit.upload( final taskId = await cubit.upload(
widget.fileBytes, widget.fileBytes,
filename: _padWithExtension( filename: _padWithExtension(
@@ -250,10 +262,11 @@ class _DocumentUploadPreparationPageState extends State<DocumentUploadPreparatio
widget.fileExtension, widget.fileExtension,
), ),
title: title, title: title,
documentType: docType.id, documentType: docType,
correspondent: correspondent.id, correspondent: correspondent,
tags: tags.include, tags: tags,
createdAt: createdAt, createdAt: createdAt,
asn: asn,
); );
showSnackBar( showSnackBar(
context, context,
@@ -1,6 +1,5 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
class DocumentView extends StatefulWidget { class DocumentView extends StatefulWidget {
final Future<Uint8List> documentBytes; final Future<Uint8List> documentBytes;
@@ -6,7 +6,6 @@ import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart'; import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
import 'package:paperless_mobile/core/delegate/customizable_sliver_persistent_header_delegate.dart'; import 'package:paperless_mobile/core/delegate/customizable_sliver_persistent_header_delegate.dart';
import 'package:paperless_mobile/core/navigation/push_routes.dart'; import 'package:paperless_mobile/core/navigation/push_routes.dart';
import 'package:paperless_mobile/core/repository/label_repository.dart';
import 'package:paperless_mobile/core/widgets/material/colored_tab_bar.dart'; import 'package:paperless_mobile/core/widgets/material/colored_tab_bar.dart';
import 'package:paperless_mobile/extensions/flutter_extensions.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.dart';
import 'package:paperless_mobile/features/app_drawer/view/app_drawer.dart'; import 'package:paperless_mobile/features/app_drawer/view/app_drawer.dart';
@@ -19,12 +18,10 @@ import 'package:paperless_mobile/features/documents/view/widgets/selection/docum
import 'package:paperless_mobile/features/documents/view/widgets/selection/view_type_selection_widget.dart'; import 'package:paperless_mobile/features/documents/view/widgets/selection/view_type_selection_widget.dart';
import 'package:paperless_mobile/features/documents/view/widgets/sort_documents_button.dart'; import 'package:paperless_mobile/features/documents/view/widgets/sort_documents_button.dart';
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.dart'; import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.dart';
import 'package:paperless_mobile/features/saved_view/view/add_saved_view_page.dart';
import 'package:paperless_mobile/features/saved_view/view/saved_view_list.dart'; import 'package:paperless_mobile/features/saved_view/view/saved_view_list.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/app_localizations.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
import 'package:paperless_mobile/helpers/message_helpers.dart'; import 'package:paperless_mobile/helpers/message_helpers.dart';
import 'package:paperless_mobile/routes/document_details_route.dart';
class DocumentFilterIntent { class DocumentFilterIntent {
final DocumentFilter? filter; final DocumentFilter? filter;
@@ -370,21 +367,7 @@ class _DocumentsPageState extends State<DocumentsPage> with SingleTickerProvider
} }
void _onCreateSavedView(DocumentFilter filter) async { void _onCreateSavedView(DocumentFilter filter) async {
final newView = await Navigator.of(context).push<SavedView?>( final newView = await pushAddSavedViewRoute(context, filter: filter);
MaterialPageRoute(
builder: (context) => BlocBuilder<SavedViewCubit, SavedViewState>(
builder: (context, state) {
return AddSavedViewPage(
currentFilter: filter,
correspondents: context.read<LabelRepository>().state.correspondents,
documentTypes: context.read<LabelRepository>().state.documentTypes,
storagePaths: context.read<LabelRepository>().state.storagePaths,
tags: context.read<LabelRepository>().state.tags,
);
},
),
),
);
if (newView != null) { if (newView != null) {
try { try {
await context.read<SavedViewCubit>().add(newView); await context.read<SavedViewCubit>().add(newView);
@@ -1,5 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/features/documents/view/widgets/document_preview.dart'; import 'package:paperless_mobile/features/documents/view/widgets/document_preview.dart';
import 'package:paperless_mobile/features/documents/view/widgets/items/document_item.dart'; import 'package:paperless_mobile/features/documents/view/widgets/items/document_item.dart';
import 'package:paperless_mobile/features/labels/correspondent/view/widgets/correspondent_widget.dart'; import 'package:paperless_mobile/features/labels/correspondent/view/widgets/correspondent_widget.dart';
@@ -61,8 +61,8 @@ class DocumentsListLoadingWidget extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
TagsPlaceholder(count: 2, dense: true), const TagsPlaceholder(count: 2, dense: true),
SizedBox(height: 2), const SizedBox(height: 2),
TextPlaceholder( TextPlaceholder(
length: 250, length: 250,
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize!, fontSize: Theme.of(context).textTheme.labelSmall!.fontSize!,
@@ -1,10 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/database/tables/local_user_account.dart';
import 'package:paperless_mobile/core/widgets/form_builder_fields/extended_date_range_form_field/form_builder_extended_date_range_picker.dart'; import 'package:paperless_mobile/core/widgets/form_builder_fields/extended_date_range_form_field/form_builder_extended_date_range_picker.dart';
import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart';
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/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/app_localizations.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
@@ -156,6 +154,10 @@ class _DocumentFilterFormState extends State<DocumentFilterForm> {
initialValue: widget.initialFilter.documentType, initialValue: widget.initialFilter.documentType,
prefixIcon: const Icon(Icons.description_outlined), prefixIcon: const Icon(Icons.description_outlined),
allowSelectUnassigned: false, allowSelectUnassigned: false,
canCreateNewLabel: LocalUserAccount.current.paperlessUser.hasPermission(
PermissionAction.add,
PermissionTarget.documentType,
),
); );
} }
@@ -167,6 +169,10 @@ class _DocumentFilterFormState extends State<DocumentFilterForm> {
initialValue: widget.initialFilter.correspondent, initialValue: widget.initialFilter.correspondent,
prefixIcon: const Icon(Icons.person_outline), prefixIcon: const Icon(Icons.person_outline),
allowSelectUnassigned: false, allowSelectUnassigned: false,
canCreateNewLabel: LocalUserAccount.current.paperlessUser.hasPermission(
PermissionAction.add,
PermissionTarget.correspondent,
),
); );
} }
@@ -178,6 +184,10 @@ class _DocumentFilterFormState extends State<DocumentFilterForm> {
initialValue: widget.initialFilter.storagePath, initialValue: widget.initialFilter.storagePath,
prefixIcon: const Icon(Icons.folder_outlined), prefixIcon: const Icon(Icons.folder_outlined),
allowSelectUnassigned: false, allowSelectUnassigned: false,
canCreateNewLabel: LocalUserAccount.current.paperlessUser.hasPermission(
PermissionAction.add,
PermissionTarget.storagePath,
),
); );
} }
@@ -1,7 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/widgets/form_builder_fields/form_builder_type_ahead.dart';
import 'package:paperless_mobile/features/documents/cubit/documents_cubit.dart'; import 'package:paperless_mobile/features/documents/cubit/documents_cubit.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
@@ -18,6 +18,7 @@ class EditLabelPage<T extends Label> extends StatelessWidget {
final List<Widget> additionalFields; final List<Widget> additionalFields;
final Future<T> Function(BuildContext context, T label) onSubmit; final Future<T> Function(BuildContext context, T label) onSubmit;
final Future<void> Function(BuildContext context, T label) onDelete; final Future<void> Function(BuildContext context, T label) onDelete;
final bool canDelete;
const EditLabelPage({ const EditLabelPage({
super.key, super.key,
@@ -26,6 +27,7 @@ class EditLabelPage<T extends Label> extends StatelessWidget {
this.additionalFields = const [], this.additionalFields = const [],
required this.onSubmit, required this.onSubmit,
required this.onDelete, required this.onDelete,
required this.canDelete,
}); });
@override @override
@@ -40,6 +42,7 @@ class EditLabelPage<T extends Label> extends StatelessWidget {
fromJsonT: fromJsonT, fromJsonT: fromJsonT,
onSubmit: onSubmit, onSubmit: onSubmit,
onDelete: onDelete, onDelete: onDelete,
canDelete: canDelete,
), ),
); );
} }
@@ -51,6 +54,7 @@ class EditLabelForm<T extends Label> extends StatelessWidget {
final List<Widget> additionalFields; final List<Widget> additionalFields;
final Future<T> Function(BuildContext context, T label) onSubmit; final Future<T> Function(BuildContext context, T label) onSubmit;
final Future<void> Function(BuildContext context, T label) onDelete; final Future<void> Function(BuildContext context, T label) onDelete;
final bool canDelete;
const EditLabelForm({ const EditLabelForm({
super.key, super.key,
@@ -59,6 +63,7 @@ class EditLabelForm<T extends Label> extends StatelessWidget {
required this.additionalFields, required this.additionalFields,
required this.onSubmit, required this.onSubmit,
required this.onDelete, required this.onDelete,
required this.canDelete,
}); });
@override @override
@@ -68,7 +73,7 @@ class EditLabelForm<T extends Label> extends StatelessWidget {
title: Text(S.of(context)!.edit), title: Text(S.of(context)!.edit),
actions: [ actions: [
IconButton( IconButton(
onPressed: () => _onDelete(context), onPressed: canDelete ? () => _onDelete(context) : null,
icon: const Icon(Icons.delete), icon: const Icon(Icons.delete),
), ),
], ],
@@ -1,6 +1,7 @@
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/core/database/tables/local_user_account.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/edit_label_page.dart'; import 'package:paperless_mobile/features/edit_label/view/edit_label_page.dart';
@@ -19,10 +20,12 @@ class EditCorrespondentPage extends StatelessWidget {
return EditLabelPage<Correspondent>( return EditLabelPage<Correspondent>(
label: correspondent, label: correspondent,
fromJsonT: Correspondent.fromJson, fromJsonT: Correspondent.fromJson,
onSubmit: (context, label) => onSubmit: (context, label) => context.read<EditLabelCubit>().replaceCorrespondent(label),
context.read<EditLabelCubit>().replaceCorrespondent(label), onDelete: (context, label) => context.read<EditLabelCubit>().removeCorrespondent(label),
onDelete: (context, label) => canDelete: LocalUserAccount.current.paperlessUser.hasPermission(
context.read<EditLabelCubit>().removeCorrespondent(label), PermissionAction.delete,
PermissionTarget.correspondent,
),
); );
}), }),
); );
@@ -1,7 +1,7 @@
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.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/core/repository/label_repository.dart'; import 'package:paperless_mobile/core/database/tables/local_user_account.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/edit_label_page.dart'; import 'package:paperless_mobile/features/edit_label/view/edit_label_page.dart';
@@ -18,10 +18,12 @@ class EditDocumentTypePage extends StatelessWidget {
child: EditLabelPage<DocumentType>( child: EditLabelPage<DocumentType>(
label: documentType, label: documentType,
fromJsonT: DocumentType.fromJson, fromJsonT: DocumentType.fromJson,
onSubmit: (context, label) => onSubmit: (context, label) => context.read<EditLabelCubit>().replaceDocumentType(label),
context.read<EditLabelCubit>().replaceDocumentType(label), onDelete: (context, label) => context.read<EditLabelCubit>().removeDocumentType(label),
onDelete: (context, label) => canDelete: LocalUserAccount.current.paperlessUser.hasPermission(
context.read<EditLabelCubit>().removeDocumentType(label), PermissionAction.delete,
PermissionTarget.documentType,
),
), ),
); );
} }
@@ -1,6 +1,7 @@
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/core/database/tables/local_user_account.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/edit_label_page.dart'; import 'package:paperless_mobile/features/edit_label/view/edit_label_page.dart';
import 'package:paperless_mobile/features/labels/storage_path/view/widgets/storage_path_autofill_form_builder_field.dart'; import 'package:paperless_mobile/features/labels/storage_path/view/widgets/storage_path_autofill_form_builder_field.dart';
@@ -18,10 +19,12 @@ class EditStoragePathPage extends StatelessWidget {
child: EditLabelPage<StoragePath>( child: EditLabelPage<StoragePath>(
label: storagePath, label: storagePath,
fromJsonT: StoragePath.fromJson, fromJsonT: StoragePath.fromJson,
onSubmit: (context, label) => onSubmit: (context, label) => context.read<EditLabelCubit>().replaceStoragePath(label),
context.read<EditLabelCubit>().replaceStoragePath(label), onDelete: (context, label) => context.read<EditLabelCubit>().removeStoragePath(label),
onDelete: (context, label) => canDelete: LocalUserAccount.current.paperlessUser.hasPermission(
context.read<EditLabelCubit>().removeStoragePath(label), PermissionAction.delete,
PermissionTarget.storagePath,
),
additionalFields: [ additionalFields: [
StoragePathAutofillFormBuilderField( StoragePathAutofillFormBuilderField(
name: StoragePath.pathKey, name: StoragePath.pathKey,
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/database/tables/local_user_account.dart';
import 'package:paperless_mobile/core/widgets/form_builder_fields/form_builder_color_picker.dart'; import 'package:paperless_mobile/core/widgets/form_builder_fields/form_builder_color_picker.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/edit_label_page.dart'; import 'package:paperless_mobile/features/edit_label/view/edit_label_page.dart';
@@ -21,10 +22,12 @@ class EditTagPage extends StatelessWidget {
child: EditLabelPage<Tag>( child: EditLabelPage<Tag>(
label: tag, label: tag,
fromJsonT: Tag.fromJson, fromJsonT: Tag.fromJson,
onSubmit: (context, label) => onSubmit: (context, label) => context.read<EditLabelCubit>().replaceTag(label),
context.read<EditLabelCubit>().replaceTag(label), onDelete: (context, label) => context.read<EditLabelCubit>().removeTag(label),
onDelete: (context, label) => canDelete: LocalUserAccount.current.paperlessUser.hasPermission(
context.read<EditLabelCubit>().removeTag(label), PermissionAction.delete,
PermissionTarget.tag,
),
additionalFields: [ additionalFields: [
FormBuilderColorPickerField( FormBuilderColorPickerField(
initialValue: tag.color, initialValue: tag.color,
@@ -8,7 +8,6 @@ import 'package:paperless_mobile/extensions/flutter_extensions.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
import 'package:paperless_mobile/helpers/message_helpers.dart'; import 'package:paperless_mobile/helpers/message_helpers.dart';
import 'package:paperless_mobile/constants.dart';
class SubmitButtonConfig<T extends Label> { class SubmitButtonConfig<T extends Label> {
final Widget icon; final Widget icon;
+9 -31
View File
@@ -6,38 +6,26 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:fluttertoast/fluttertoast.dart'; import 'package:fluttertoast/fluttertoast.dart';
import 'package:hive/hive.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart'; import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
import 'package:paperless_mobile/core/config/hive/hive_config.dart'; import 'package:paperless_mobile/core/database/tables/local_user_account.dart';
import 'package:paperless_mobile/core/database/tables/global_settings.dart';
import 'package:paperless_mobile/core/database/tables/local_user_app_state.dart';
import 'package:paperless_mobile/core/global/constants.dart'; import 'package:paperless_mobile/core/global/constants.dart';
import 'package:paperless_mobile/core/repository/label_repository.dart'; import 'package:paperless_mobile/core/repository/label_repository.dart';
import 'package:paperless_mobile/core/repository/saved_view_repository.dart'; import 'package:paperless_mobile/core/repository/saved_view_repository.dart';
import 'package:paperless_mobile/core/repository/user_repository.dart';
import 'package:paperless_mobile/core/service/file_description.dart'; import 'package:paperless_mobile/core/service/file_description.dart';
import 'package:paperless_mobile/core/translation/error_code_localization_mapper.dart'; import 'package:paperless_mobile/core/translation/error_code_localization_mapper.dart';
import 'package:paperless_mobile/features/document_scan/cubit/document_scanner_cubit.dart';
import 'package:paperless_mobile/features/document_scan/view/scanner_page.dart'; import 'package:paperless_mobile/features/document_scan/view/scanner_page.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/cubit/documents_cubit.dart';
import 'package:paperless_mobile/features/documents/view/pages/documents_page.dart'; import 'package:paperless_mobile/features/documents/view/pages/documents_page.dart';
import 'package:paperless_mobile/features/home/view/route_description.dart'; import 'package:paperless_mobile/features/home/view/route_description.dart';
import 'package:paperless_mobile/features/inbox/cubit/inbox_cubit.dart'; import 'package:paperless_mobile/features/inbox/cubit/inbox_cubit.dart';
import 'package:paperless_mobile/features/inbox/view/pages/inbox_page.dart'; import 'package:paperless_mobile/features/inbox/view/pages/inbox_page.dart';
import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart';
import 'package:paperless_mobile/features/labels/view/pages/labels_page.dart'; import 'package:paperless_mobile/features/labels/view/pages/labels_page.dart';
import 'package:paperless_mobile/features/login/cubit/authentication_cubit.dart';
import 'package:paperless_mobile/core/database/tables/local_user_account.dart';
import 'package:paperless_mobile/features/notifications/services/local_notification_service.dart'; import 'package:paperless_mobile/features/notifications/services/local_notification_service.dart';
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.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/app_localizations.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
import 'package:paperless_mobile/helpers/message_helpers.dart';
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';
@@ -196,7 +184,8 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
), ),
label: S.of(context)!.documents, label: S.of(context)!.documents,
), ),
if (LocalUserAccount.current.paperlessUser.hasPermission(UserPermissions.addDocument)) if (LocalUserAccount.current.paperlessUser
.hasPermission(PermissionAction.add, PermissionTarget.document))
RouteDescription( RouteDescription(
icon: const Icon(Icons.document_scanner_outlined), icon: const Icon(Icons.document_scanner_outlined),
selectedIcon: Icon( selectedIcon: Icon(
@@ -222,32 +211,31 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
label: S.of(context)!.inbox, label: S.of(context)!.inbox,
badgeBuilder: (icon) => BlocBuilder<InboxCubit, InboxState>( badgeBuilder: (icon) => BlocBuilder<InboxCubit, InboxState>(
builder: (context, state) { builder: (context, state) {
if (state.itemsInInboxCount > 0) {
return Badge.count( return Badge.count(
isLabelVisible: state.itemsInInboxCount > 0,
count: state.itemsInInboxCount, count: state.itemsInInboxCount,
child: icon, child: icon,
); );
}
return icon;
}, },
), ),
), ),
]; ];
final routes = <Widget>[ final routes = <Widget>[
const DocumentsPage(), const DocumentsPage(),
if (LocalUserAccount.current.paperlessUser.hasPermission(UserPermissions.addDocument)) if (LocalUserAccount.current.paperlessUser
.hasPermission(PermissionAction.add, PermissionTarget.document))
const ScannerPage(), const ScannerPage(),
const LabelsPage(), const LabelsPage(),
const InboxPage(), const InboxPage(),
]; ];
return MultiBlocListener( return MultiBlocListener(
listeners: [ listeners: [
BlocListener<ConnectivityCubit, ConnectivityState>( BlocListener<ConnectivityCubit, ConnectivityState>(
//Only re-initialize data if the connectivity changed from not connected to connected // If app was started offline, load data once it comes back online.
listenWhen: (previous, current) => current == ConnectivityState.connected, listenWhen: (previous, current) => current == ConnectivityState.connected,
listener: (context, state) { listener: (context, state) {
_initializeData(context); context.read<LabelRepository>().initialize();
context.read<SavedViewRepository>().initialize();
}, },
), ),
BlocListener<TaskStatusCubit, TaskStatusState>( BlocListener<TaskStatusCubit, TaskStatusState>(
@@ -299,14 +287,4 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
setState(() => _currentIndex = index); setState(() => _currentIndex = index);
} }
} }
void _initializeData(BuildContext context) {
Future.wait([
context.read<LabelRepository>().initialize(),
context.read<SavedViewRepository>().findAll(),
]).onError<PaperlessServerException>((error, stackTrace) {
showErrorMessage(context, error, stackTrace);
throw error;
});
}
} }
+4 -4
View File
@@ -1,5 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:hive_flutter/adapters.dart'; import 'package:hive_flutter/adapters.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
@@ -45,9 +44,10 @@ class HomeRoute extends StatelessWidget {
return GlobalSettingsBuilder( return GlobalSettingsBuilder(
builder: (context, settings) { builder: (context, settings) {
final currentLocalUserId = settings.currentLoggedInUser!; final currentLocalUserId = settings.currentLoggedInUser!;
final apiVersion = ApiVersion(paperlessApiVersion);
return MultiProvider( return MultiProvider(
providers: [ providers: [
Provider.value(value: ApiVersion(paperlessApiVersion)), Provider.value(value: apiVersion),
Provider<CacheManager>( Provider<CacheManager>(
create: (context) => CacheManager( create: (context) => CacheManager(
Config( Config(
@@ -87,7 +87,7 @@ class HomeRoute extends StatelessWidget {
apiVersion: paperlessApiVersion, apiVersion: paperlessApiVersion,
), ),
), ),
if (paperlessApiVersion >= 3) if (apiVersion.hasMultiUserSupport)
ProxyProvider<SessionManager, PaperlessUserApiV3>( ProxyProvider<SessionManager, PaperlessUserApiV3>(
update: (context, value, previous) => PaperlessUserApiV3Impl( update: (context, value, previous) => PaperlessUserApiV3Impl(
value.client, value.client,
@@ -98,7 +98,7 @@ class HomeRoute extends StatelessWidget {
return MultiProvider( return MultiProvider(
providers: [ providers: [
ProxyProvider<PaperlessLabelsApi, LabelRepository>( ProxyProvider<PaperlessLabelsApi, LabelRepository>(
update: (context, value, previous) => LabelRepository(value)..initialize(), update: (context, value, previous) => LabelRepository(value),
), ),
ProxyProvider<PaperlessSavedViewsApi, SavedViewRepository>( ProxyProvider<PaperlessSavedViewsApi, SavedViewRepository>(
update: (context, value, previous) => SavedViewRepository(value)..initialize(), update: (context, value, previous) => SavedViewRepository(value)..initialize(),
@@ -3,6 +3,5 @@ class ApiVersion {
ApiVersion(this.version); ApiVersion(this.version);
bool get supportsPermissions => version >= 3;
bool get hasMultiUserSupport => version >= 3; bool get hasMultiUserSupport => version >= 3;
} }
@@ -1,11 +1,9 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/repository/label_repository.dart'; import 'package:paperless_mobile/core/repository/label_repository.dart';
import 'package:paperless_mobile/core/repository/saved_view_repository.dart'; import 'package:paperless_mobile/core/repository/saved_view_repository.dart';
import 'package:paperless_mobile/extensions/flutter_extensions.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.dart';
import 'package:paperless_mobile/features/login/cubit/authentication_cubit.dart'; import 'package:paperless_mobile/features/login/cubit/authentication_cubit.dart';
import 'package:paperless_mobile/core/database/tables/global_settings.dart';
import 'package:paperless_mobile/features/settings/view/widgets/user_settings_builder.dart'; import 'package:paperless_mobile/features/settings/view/widgets/user_settings_builder.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
+12 -20
View File
@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/exception/server_message_exception.dart';
import 'package:paperless_mobile/core/widgets/dialog_utils/dialog_cancel_button.dart'; import 'package:paperless_mobile/core/widgets/dialog_utils/dialog_cancel_button.dart';
import 'package:paperless_mobile/core/widgets/dialog_utils/dialog_confirm_button.dart'; import 'package:paperless_mobile/core/widgets/dialog_utils/dialog_confirm_button.dart';
import 'package:paperless_mobile/core/widgets/hint_card.dart'; import 'package:paperless_mobile/core/widgets/hint_card.dart';
@@ -10,7 +11,6 @@ import 'package:paperless_mobile/extensions/dart_extensions.dart';
import 'package:paperless_mobile/extensions/flutter_extensions.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.dart';
import 'package:paperless_mobile/features/app_drawer/view/app_drawer.dart'; import 'package:paperless_mobile/features/app_drawer/view/app_drawer.dart';
import 'package:paperless_mobile/features/document_search/view/sliver_search_bar.dart'; import 'package:paperless_mobile/features/document_search/view/sliver_search_bar.dart';
import 'package:paperless_mobile/features/documents/view/widgets/placeholder/documents_list_loading_widget.dart';
import 'package:paperless_mobile/features/inbox/cubit/inbox_cubit.dart'; import 'package:paperless_mobile/features/inbox/cubit/inbox_cubit.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';
@@ -26,10 +26,8 @@ class InboxPage extends StatefulWidget {
State<InboxPage> createState() => _InboxPageState(); State<InboxPage> createState() => _InboxPageState();
} }
class _InboxPageState extends State<InboxPage> class _InboxPageState extends State<InboxPage> with DocumentPagingViewMixin<InboxPage, InboxCubit> {
with DocumentPagingViewMixin<InboxPage, InboxCubit> { final SliverOverlapAbsorberHandle searchBarHandle = SliverOverlapAbsorberHandle();
final SliverOverlapAbsorberHandle searchBarHandle =
SliverOverlapAbsorberHandle();
@override @override
final pagingScrollController = ScrollController(); final pagingScrollController = ScrollController();
@@ -80,8 +78,7 @@ class _InboxPageState extends State<InboxPage>
} else if (state.documents.isEmpty) { } else if (state.documents.isEmpty) {
return Center( return Center(
child: InboxEmptyWidget( child: InboxEmptyWidget(
emptyStateRefreshIndicatorKey: emptyStateRefreshIndicatorKey: _emptyStateRefreshIndicatorKey,
_emptyStateRefreshIndicatorKey,
), ),
); );
} else { } else {
@@ -92,8 +89,7 @@ class _InboxPageState extends State<InboxPage>
SliverToBoxAdapter( SliverToBoxAdapter(
child: HintCard( child: HintCard(
show: !state.isHintAcknowledged, show: !state.isHintAcknowledged,
hintText: hintText: S.of(context)!.swipeLeftToMarkADocumentAsSeen,
S.of(context)!.swipeLeftToMarkADocumentAsSeen,
onHintAcknowledged: () => onHintAcknowledged: () =>
context.read<InboxCubit>().acknowledgeHint(), context.read<InboxCubit>().acknowledgeHint(),
), ),
@@ -108,13 +104,10 @@ class _InboxPageState extends State<InboxPage>
child: Align( child: Align(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: ClipRRect( child: ClipRRect(
borderRadius: borderRadius: BorderRadius.circular(32.0),
BorderRadius.circular(32.0),
child: Text( child: Text(
entry.key, entry.key,
style: Theme.of(context) style: Theme.of(context).textTheme.bodySmall,
.textTheme
.bodySmall,
textAlign: TextAlign.center, textAlign: TextAlign.center,
).padded(), ).padded(),
), ),
@@ -182,7 +175,7 @@ class _InboxPageState extends State<InboxPage>
], ],
).padded(), ).padded(),
confirmDismiss: (_) => _onItemDismissed(doc), confirmDismiss: (_) => _onItemDismissed(doc),
key: UniqueKey(), key: ValueKey(doc.id),
child: InboxItem(document: doc), child: InboxItem(document: doc),
); );
} }
@@ -227,14 +220,15 @@ class _InboxPageState extends State<InboxPage>
return true; return true;
} on PaperlessServerException catch (error, stackTrace) { } on PaperlessServerException catch (error, stackTrace) {
showErrorMessage(context, error, stackTrace); showErrorMessage(context, error, stackTrace);
return false; } on ServerMessageException catch (error) {
showGenericError(context, error.message);
} catch (error) { } catch (error) {
showErrorMessage( showErrorMessage(
context, context,
const PaperlessServerException.unknown(), const PaperlessServerException.unknown(),
); );
return false;
} }
return false;
} }
Future<void> _onUndoMarkAsSeen( Future<void> _onUndoMarkAsSeen(
@@ -242,9 +236,7 @@ class _InboxPageState extends State<InboxPage>
Iterable<int> removedTags, Iterable<int> removedTags,
) async { ) async {
try { try {
await context await context.read<InboxCubit>().undoRemoveFromInbox(document, removedTags);
.read<InboxCubit>()
.undoRemoveFromInbox(document, removedTags);
} on PaperlessServerException catch (error, stackTrace) { } on PaperlessServerException catch (error, stackTrace) {
showErrorMessage(context, error, stackTrace); showErrorMessage(context, error, stackTrace);
} }
+17 -29
View File
@@ -2,6 +2,7 @@ import 'package:collection/collection.dart';
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/core/database/tables/local_user_account.dart';
import 'package:paperless_mobile/core/navigation/push_routes.dart'; import 'package:paperless_mobile/core/navigation/push_routes.dart';
import 'package:paperless_mobile/core/workarounds/colored_chip.dart'; import 'package:paperless_mobile/core/workarounds/colored_chip.dart';
import 'package:paperless_mobile/extensions/flutter_extensions.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.dart';
@@ -12,8 +13,6 @@ 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/generated/l10n/app_localizations.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
import 'package:paperless_mobile/routes/document_details_route.dart';
class InboxItem extends StatefulWidget { class InboxItem extends StatefulWidget {
static const a4AspectRatio = 1 / 1.4142; static const a4AspectRatio = 1 / 1.4142;
@@ -108,8 +107,8 @@ class _InboxItemState extends State<InboxItem> {
], ],
), ),
), ),
SizedBox( LimitedBox(
height: 56, maxHeight: 56,
child: _buildActions(context), child: _buildActions(context),
), ),
], ],
@@ -121,12 +120,17 @@ class _InboxItemState extends State<InboxItem> {
} }
Widget _buildActions(BuildContext context) { Widget _buildActions(BuildContext context) {
final canEdit = LocalUserAccount.current.paperlessUser
.hasPermission(PermissionAction.change, PermissionTarget.document);
final canDelete = LocalUserAccount.current.paperlessUser
.hasPermission(PermissionAction.delete, PermissionTarget.document);
final chipShape = RoundedRectangleBorder( final chipShape = RoundedRectangleBorder(
borderRadius: BorderRadius.circular(32), borderRadius: BorderRadius.circular(32),
); );
final actions = [ final actions = [
_buildAssignAsnAction(chipShape, context), if (canEdit) _buildAssignAsnAction(chipShape, context),
const SizedBox(width: 8.0), if (canEdit && canDelete) const SizedBox(width: 8.0),
if (canDelete)
ColoredChipWrapper( ColoredChipWrapper(
child: ActionChip( child: ActionChip(
avatar: const Icon(Icons.delete_outline), avatar: const Icon(Icons.delete_outline),
@@ -135,7 +139,8 @@ class _InboxItemState extends State<InboxItem> {
onPressed: () async { onPressed: () async {
final shouldDelete = await showDialog<bool>( final shouldDelete = await showDialog<bool>(
context: context, context: context,
builder: (context) => DeleteDocumentConfirmationDialog(document: widget.document), builder: (context) =>
DeleteDocumentConfirmationDialog(document: widget.document),
) ?? ) ??
false; false;
if (shouldDelete) { if (shouldDelete) {
@@ -145,34 +150,16 @@ class _InboxItemState extends State<InboxItem> {
), ),
), ),
]; ];
if (actions.isEmpty) {
// return FutureBuilder<FieldSuggestions>( return const SizedBox.shrink();
// future: _fieldSuggestions, }
// builder: (context, snapshot) {
// List<Widget>? suggestions;
// if (!snapshot.hasData) {
// suggestions = [
// const SizedBox(width: 4),
// ];
// } else {
// if (snapshot.data!.hasSuggestions) {
// suggestions = [
// const SizedBox(width: 4),
// ..._buildSuggestionChips(
// chipShape,
// snapshot.data!,
// context.watch<InboxCubit>().state,
// ),
// ];
// }
// }
return Row( return Row(
children: [ children: [
Row( Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
const Icon(Icons.bolt_outlined), const Icon(Icons.auto_awesome),
ConstrainedBox( ConstrainedBox(
constraints: const BoxConstraints( constraints: const BoxConstraints(
maxWidth: 50, maxWidth: 50,
@@ -229,6 +216,7 @@ class _InboxItemState extends State<InboxItem> {
setState(() { setState(() {
_isAsnAssignLoading = true; _isAsnAssignLoading = true;
}); });
context.read<InboxCubit>().assignAsn(widget.document).whenComplete( context.read<InboxCubit>().assignAsn(widget.document).whenComplete(
() => setState(() => _isAsnAssignLoading = false), () => setState(() => _isAsnAssignLoading = false),
); );
@@ -3,7 +3,6 @@ import 'package:paperless_mobile/core/widgets/shimmer_placeholder.dart';
import 'package:paperless_mobile/extensions/flutter_extensions.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.dart';
import 'package:paperless_mobile/features/documents/view/widgets/placeholder/tags_placeholder.dart'; import 'package:paperless_mobile/features/documents/view/widgets/placeholder/tags_placeholder.dart';
import 'package:paperless_mobile/features/documents/view/widgets/placeholder/text_placeholder.dart'; import 'package:paperless_mobile/features/documents/view/widgets/placeholder/text_placeholder.dart';
import 'package:paperless_mobile/features/inbox/view/widgets/inbox_item.dart';
class InboxListLoadingWidget extends StatelessWidget { class InboxListLoadingWidget extends StatelessWidget {
const InboxListLoadingWidget({super.key}); const InboxListLoadingWidget({super.key});
@@ -48,10 +47,10 @@ class InboxListLoadingWidget extends StatelessWidget {
), ),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
Flexible( const Flexible(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: const [ children: [
Spacer(), Spacer(),
TextPlaceholder(length: 200, fontSize: 14), TextPlaceholder(length: 200, fontSize: 14),
Spacer(), Spacer(),
@@ -1,4 +1,3 @@
import 'package:equatable/equatable.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/core/repository/label_repository.dart'; import 'package:paperless_mobile/core/repository/label_repository.dart';
@@ -1,10 +1,9 @@
import 'dart:developer';
import 'package:animations/animations.dart'; import 'package:animations/animations.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/database/tables/local_user_account.dart';
import 'package:paperless_mobile/core/workarounds/colored_chip.dart'; import 'package:paperless_mobile/core/workarounds/colored_chip.dart';
import 'package:paperless_mobile/features/labels/tags/view/widgets/fullscreen_tags_form.dart'; import 'package:paperless_mobile/features/labels/tags/view/widgets/fullscreen_tags_form.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
@@ -72,7 +71,11 @@ class TagsFormField extends StatelessWidget {
onSubmit: closeForm, onSubmit: closeForm,
initialValue: field.value, initialValue: field.value,
allowOnlySelection: allowOnlySelection, allowOnlySelection: allowOnlySelection,
allowCreation: allowCreation, allowCreation: allowCreation &&
LocalUserAccount.current.paperlessUser.hasPermission(
PermissionAction.add,
PermissionTarget.tag,
),
allowExclude: allowExclude, allowExclude: allowExclude,
), ),
onClosed: (data) { onClosed: (data) {
@@ -2,6 +2,7 @@ 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/core/bloc/connectivity_cubit.dart'; import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
import 'package:paperless_mobile/core/database/tables/local_user_account.dart';
import 'package:paperless_mobile/core/delegate/customizable_sliver_persistent_header_delegate.dart'; import 'package:paperless_mobile/core/delegate/customizable_sliver_persistent_header_delegate.dart';
import 'package:paperless_mobile/core/repository/label_repository.dart'; import 'package:paperless_mobile/core/repository/label_repository.dart';
import 'package:paperless_mobile/core/widgets/material/colored_tab_bar.dart'; import 'package:paperless_mobile/core/widgets/material/colored_tab_bar.dart';
@@ -16,7 +17,6 @@ import 'package:paperless_mobile/features/edit_label/view/impl/edit_document_typ
import 'package:paperless_mobile/features/edit_label/view/impl/edit_storage_path_page.dart'; import 'package:paperless_mobile/features/edit_label/view/impl/edit_storage_path_page.dart';
import 'package:paperless_mobile/features/edit_label/view/impl/edit_tag_page.dart'; import 'package:paperless_mobile/features/edit_label/view/impl/edit_tag_page.dart';
import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart'; import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart';
import 'package:paperless_mobile/features/labels/cubit/label_cubit_mixin.dart';
import 'package:paperless_mobile/features/labels/view/widgets/label_tab_view.dart'; import 'package:paperless_mobile/features/labels/view/widgets/label_tab_view.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
@@ -148,6 +148,10 @@ class _LabelsPageState extends State<LabelsPage> with SingleTickerProviderStateM
correspondent: IdQueryParameter.fromId(label.id!), correspondent: IdQueryParameter.fromId(label.id!),
pageSize: label.documentCount ?? 0, pageSize: label.documentCount ?? 0,
), ),
canEdit: LocalUserAccount.current.paperlessUser.hasPermission(
PermissionAction.change, PermissionTarget.correspondent),
canAddNew: LocalUserAccount.current.paperlessUser.hasPermission(
PermissionAction.add, PermissionTarget.correspondent),
onEdit: _openEditCorrespondentPage, onEdit: _openEditCorrespondentPage,
emptyStateActionButtonLabel: S.of(context)!.addNewCorrespondent, emptyStateActionButtonLabel: S.of(context)!.addNewCorrespondent,
emptyStateDescription: S.of(context)!.noCorrespondentsSetUp, emptyStateDescription: S.of(context)!.noCorrespondentsSetUp,
@@ -169,6 +173,10 @@ class _LabelsPageState extends State<LabelsPage> with SingleTickerProviderStateM
documentType: IdQueryParameter.fromId(label.id!), documentType: IdQueryParameter.fromId(label.id!),
pageSize: label.documentCount ?? 0, pageSize: label.documentCount ?? 0,
), ),
canEdit: LocalUserAccount.current.paperlessUser.hasPermission(
PermissionAction.change, PermissionTarget.documentType),
canAddNew: LocalUserAccount.current.paperlessUser.hasPermission(
PermissionAction.add, PermissionTarget.documentType),
onEdit: _openEditDocumentTypePage, onEdit: _openEditDocumentTypePage,
emptyStateActionButtonLabel: S.of(context)!.addNewDocumentType, emptyStateActionButtonLabel: S.of(context)!.addNewDocumentType,
emptyStateDescription: S.of(context)!.noDocumentTypesSetUp, emptyStateDescription: S.of(context)!.noDocumentTypesSetUp,
@@ -190,6 +198,10 @@ class _LabelsPageState extends State<LabelsPage> with SingleTickerProviderStateM
tags: TagsQuery.ids(include: [label.id!]), tags: TagsQuery.ids(include: [label.id!]),
pageSize: label.documentCount ?? 0, pageSize: label.documentCount ?? 0,
), ),
canEdit: LocalUserAccount.current.paperlessUser
.hasPermission(PermissionAction.change, PermissionTarget.tag),
canAddNew: LocalUserAccount.current.paperlessUser
.hasPermission(PermissionAction.add, PermissionTarget.tag),
onEdit: _openEditTagPage, onEdit: _openEditTagPage,
leadingBuilder: (t) => CircleAvatar( leadingBuilder: (t) => CircleAvatar(
backgroundColor: t.color, backgroundColor: t.color,
@@ -221,6 +233,10 @@ class _LabelsPageState extends State<LabelsPage> with SingleTickerProviderStateM
storagePath: IdQueryParameter.fromId(label.id!), storagePath: IdQueryParameter.fromId(label.id!),
pageSize: label.documentCount ?? 0, pageSize: label.documentCount ?? 0,
), ),
canEdit: LocalUserAccount.current.paperlessUser.hasPermission(
PermissionAction.change, PermissionTarget.storagePath),
canAddNew: LocalUserAccount.current.paperlessUser.hasPermission(
PermissionAction.add, PermissionTarget.storagePath),
contentBuilder: (path) => Text(path.path), contentBuilder: (path) => Text(path.path),
emptyStateActionButtonLabel: S.of(context)!.addNewStoragePath, emptyStateActionButtonLabel: S.of(context)!.addNewStoragePath,
emptyStateDescription: S.of(context)!.noStoragePathsSetUp, emptyStateDescription: S.of(context)!.noStoragePathsSetUp,
@@ -16,6 +16,7 @@ class FullscreenLabelForm<T extends Label> extends StatefulWidget {
final String? addNewLabelText; final String? addNewLabelText;
final bool autofocus; final bool autofocus;
final bool allowSelectUnassigned; final bool allowSelectUnassigned;
final bool canCreateNewLabel;
FullscreenLabelForm({ FullscreenLabelForm({
super.key, super.key,
@@ -29,6 +30,7 @@ class FullscreenLabelForm<T extends Label> extends StatefulWidget {
this.addNewLabelText, this.addNewLabelText,
this.autofocus = true, this.autofocus = true,
this.allowSelectUnassigned = true, this.allowSelectUnassigned = true,
required this.canCreateNewLabel,
}) : assert( }) : assert(
!(initialValue?.isOnlyAssigned() ?? false) || showAnyAssignedOption, !(initialValue?.isOnlyAssigned() ?? false) || showAnyAssignedOption,
), ),
@@ -1,5 +1,3 @@
import 'dart:developer';
import 'package:animations/animations.dart'; import 'package:animations/animations.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@@ -28,6 +26,7 @@ class LabelFormField<T extends Label> extends StatelessWidget {
final List<T> suggestions; final List<T> suggestions;
final String? addLabelText; final String? addLabelText;
final bool allowSelectUnassigned; final bool allowSelectUnassigned;
final bool canCreateNewLabel;
const LabelFormField({ const LabelFormField({
Key? key, Key? key,
@@ -44,6 +43,7 @@ class LabelFormField<T extends Label> extends StatelessWidget {
this.suggestions = const [], this.suggestions = const [],
this.addLabelText, this.addLabelText,
required this.allowSelectUnassigned, required this.allowSelectUnassigned,
required this.canCreateNewLabel,
}) : super(key: key); }) : super(key: key);
String _buildText(BuildContext context, IdQueryParameter? value) { String _buildText(BuildContext context, IdQueryParameter? value) {
@@ -103,6 +103,7 @@ class LabelFormField<T extends Label> extends StatelessWidget {
), ),
openBuilder: (context, closeForm) => FullscreenLabelForm<T>( openBuilder: (context, closeForm) => FullscreenLabelForm<T>(
allowSelectUnassigned: allowSelectUnassigned, allowSelectUnassigned: allowSelectUnassigned,
canCreateNewLabel: canCreateNewLabel,
addNewLabelText: addLabelText, addNewLabelText: addLabelText,
leadingIcon: prefixIcon, leadingIcon: prefixIcon,
onCreateNewLabel: addLabelPageBuilder != null onCreateNewLabel: addLabelPageBuilder != null
@@ -1,19 +1,14 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hive/hive.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/config/hive/hive_config.dart';
import 'package:paperless_mobile/features/linked_documents/cubit/linked_documents_cubit.dart';
import 'package:paperless_mobile/features/linked_documents/view/linked_documents_page.dart';
import 'package:paperless_mobile/core/database/tables/local_user_account.dart'; import 'package:paperless_mobile/core/database/tables/local_user_account.dart';
import 'package:paperless_mobile/core/database/tables/global_settings.dart'; import 'package:paperless_mobile/core/navigation/push_routes.dart';
import 'package:paperless_mobile/helpers/format_helpers.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;
final String name; final String name;
final Widget content; final Widget content;
final void Function(T) onOpenEditPage; final void Function(T)? onOpenEditPage;
final DocumentFilter Function(T) filterBuilder; final DocumentFilter Function(T) filterBuilder;
final Widget? leading; final Widget? leading;
@@ -33,38 +28,25 @@ class LabelItem<T extends Label> extends StatelessWidget {
title: Text(name), title: Text(name),
subtitle: content, subtitle: content,
leading: leading, leading: leading,
onTap: () => onOpenEditPage(label), onTap: onOpenEditPage != null ? () => onOpenEditPage!(label) : null,
trailing: _buildReferencedDocumentsWidget(context), trailing: _buildReferencedDocumentsWidget(context),
isThreeLine: true, isThreeLine: true,
); );
} }
Widget _buildReferencedDocumentsWidget(BuildContext context) { Widget _buildReferencedDocumentsWidget(BuildContext context) {
final canOpen = (label.documentCount ?? 0) > 0 &&
LocalUserAccount.current.paperlessUser
.hasPermission(PermissionAction.view, PermissionTarget.document);
return TextButton.icon( return TextButton.icon(
label: const Icon(Icons.link), label: const Icon(Icons.link),
icon: Text(formatMaxCount(label.documentCount)), icon: Text(formatMaxCount(label.documentCount)),
onPressed: (label.documentCount ?? 0) == 0 onPressed: canOpen
? null ? () {
: () {
final currentUser = Hive.box<GlobalSettings>(HiveBoxes.globalSettings)
.getValue()!
.currentLoggedInUser!;
final filter = filterBuilder(label); final filter = filterBuilder(label);
Navigator.push( pushLinkedDocumentsView(context, filter: filter);
context, }
MaterialPageRoute( : null,
builder: (context) => BlocProvider(
create: (context) => LinkedDocumentsCubit(
filter,
context.read(),
context.read(),
context.read(),
),
child: const LinkedDocumentsPage(),
),
),
);
},
); );
} }
} }
@@ -11,7 +11,9 @@ class LabelTabView<T extends Label> extends StatelessWidget {
final Map<int, T> labels; final Map<int, T> labels;
final DocumentFilter Function(Label) filterBuilder; final DocumentFilter Function(Label) filterBuilder;
final void Function(T) onEdit; final void Function(T) onEdit;
final bool canEdit;
final void Function() onAddNew; final void Function() onAddNew;
final bool canAddNew;
/// Displayed as the subtitle of the [ListTile] /// Displayed as the subtitle of the [ListTile]
final Widget Function(T)? contentBuilder; final Widget Function(T)? contentBuilder;
@@ -33,6 +35,8 @@ class LabelTabView<T extends Label> extends StatelessWidget {
required this.onAddNew, required this.onAddNew,
required this.emptyStateActionButtonLabel, required this.emptyStateActionButtonLabel,
required this.labels, required this.labels,
required this.canEdit,
required this.canAddNew,
}); });
@override @override
@@ -54,7 +58,7 @@ class LabelTabView<T extends Label> extends StatelessWidget {
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
TextButton( TextButton(
onPressed: onAddNew, onPressed: canAddNew ? onAddNew : null,
child: Text(emptyStateActionButtonLabel), child: Text(emptyStateActionButtonLabel),
), ),
].padded(), ].padded(),
@@ -70,14 +74,11 @@ class LabelTabView<T extends Label> extends StatelessWidget {
name: l.name, name: l.name,
content: contentBuilder?.call(l) ?? content: contentBuilder?.call(l) ??
Text( Text(
translateMatchingAlgorithmName( translateMatchingAlgorithmName(context, l.matchingAlgorithm) +
context, l.matchingAlgorithm) + ((l.match?.isNotEmpty ?? false) ? ": ${l.match}" : ""),
((l.match?.isNotEmpty ?? false)
? ": ${l.match}"
: ""),
maxLines: 2, maxLines: 2,
), ),
onOpenEditPage: onEdit, onOpenEditPage: canEdit ? onEdit : null,
filterBuilder: filterBuilder, filterBuilder: filterBuilder,
leading: leadingBuilder?.call(l), leading: leadingBuilder?.call(l),
label: l, label: l,
@@ -20,6 +20,5 @@ class LabelText<T extends Label> extends StatelessWidget {
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
); );
;
} }
} }
@@ -8,7 +8,6 @@ import 'package:paperless_mobile/features/linked_documents/cubit/linked_document
import 'package:paperless_mobile/features/paged_document_view/view/document_paging_view_mixin.dart'; import 'package:paperless_mobile/features/paged_document_view/view/document_paging_view_mixin.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
import 'package:paperless_mobile/routes/document_details_route.dart';
class LinkedDocumentsPage extends StatefulWidget { class LinkedDocumentsPage extends StatefulWidget {
const LinkedDocumentsPage({super.key}); const LinkedDocumentsPage({super.key});
@@ -179,6 +179,7 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
return; return;
} }
} }
final userCredentialsBox = await _getUserCredentialsBox(); final userCredentialsBox = await _getUserCredentialsBox();
final authentication = userCredentialsBox.get(globalSettings.currentLoggedInUser!); final authentication = userCredentialsBox.get(globalSettings.currentLoggedInUser!);
await userCredentialsBox.close(); await userCredentialsBox.close();
@@ -1,5 +1,4 @@
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:paperless_mobile/core/config/hive/hive_config.dart'; import 'package:paperless_mobile/core/config/hive/hive_config.dart';
import 'package:paperless_mobile/features/login/model/client_certificate.dart'; import 'package:paperless_mobile/features/login/model/client_certificate.dart';
@@ -1,4 +1,3 @@
import 'dart:convert';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:hive_flutter/adapters.dart'; import 'package:hive_flutter/adapters.dart';
@@ -1,4 +1,3 @@
import 'dart:convert';
import 'dart:typed_data'; import 'dart:typed_data';
class ClientCertificateFormModel { class ClientCertificateFormModel {
@@ -4,12 +4,9 @@ import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:paperless_mobile/extensions/flutter_extensions.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.dart';
import 'package:paperless_mobile/features/login/model/client_certificate.dart';
import 'package:paperless_mobile/features/login/model/client_certificate_form_model.dart'; import 'package:paperless_mobile/features/login/model/client_certificate_form_model.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
import 'package:paperless_mobile/constants.dart';
import 'package:permission_handler/permission_handler.dart';
import 'obscured_input_text_form_field.dart'; import 'obscured_input_text_form_field.dart';
@@ -1,4 +1,3 @@
import 'dart:developer';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart';
@@ -49,6 +48,7 @@ class _ServerAddressFormFieldState extends State<ServerAddressFormField> {
if (!RegExp(r"https?://.*").hasMatch(value!)) { if (!RegExp(r"https?://.*").hasMatch(value!)) {
return S.of(context)!.serverAddressMustIncludeAScheme; return S.of(context)!.serverAddressMustIncludeAScheme;
} }
return null;
}, },
decoration: InputDecoration( decoration: InputDecoration(
hintText: "http://192.168.1.50:8000", hintText: "http://192.168.1.50:8000",
@@ -39,6 +39,7 @@ class _UserCredentialsFormFieldState extends State<UserCredentialsFormField> {
if (value?.trim().isEmpty ?? true) { if (value?.trim().isEmpty ?? true) {
return S.of(context)!.usernameMustNotBeEmpty; return S.of(context)!.usernameMustNotBeEmpty;
} }
return null;
}, },
autofillHints: const [AutofillHints.username], autofillHints: const [AutofillHints.username],
decoration: InputDecoration( decoration: InputDecoration(
@@ -56,6 +57,7 @@ class _UserCredentialsFormFieldState extends State<UserCredentialsFormField> {
if (value?.trim().isEmpty ?? true) { if (value?.trim().isEmpty ?? true) {
return S.of(context)!.passwordMustNotBeEmpty; return S.of(context)!.passwordMustNotBeEmpty;
} }
return null;
}, },
), ),
].map((child) => child.padded()).toList(), ].map((child) => child.padded()).toList(),
@@ -1,7 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:paperless_mobile/core/service/connectivity_status_service.dart'; import 'package:paperless_mobile/core/service/connectivity_status_service.dart';
import 'package:paperless_mobile/core/widgets/paperless_logo.dart';
import 'package:paperless_mobile/extensions/flutter_extensions.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.dart';
import 'package:paperless_mobile/features/login/model/client_certificate.dart'; import 'package:paperless_mobile/features/login/model/client_certificate.dart';
import 'package:paperless_mobile/features/login/model/client_certificate_form_model.dart'; import 'package:paperless_mobile/features/login/model/client_certificate_form_model.dart';
@@ -1,7 +1,6 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:developer'; import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:open_filex/open_filex.dart'; import 'package:open_filex/open_filex.dart';
@@ -1,5 +1,4 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
/// ///
@@ -3,7 +3,6 @@ import 'dart:async';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/repository/label_repository.dart';
import 'package:paperless_mobile/core/repository/saved_view_repository.dart'; import 'package:paperless_mobile/core/repository/saved_view_repository.dart';
part 'saved_view_state.dart'; part 'saved_view_state.dart';
@@ -1,15 +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:hive/hive.dart';
import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart'; import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
import 'package:paperless_mobile/core/config/hive/hive_config.dart';
import 'package:paperless_mobile/core/database/tables/global_settings.dart';
import 'package:paperless_mobile/core/database/tables/local_user_app_state.dart';
import 'package:paperless_mobile/core/navigation/push_routes.dart'; import 'package:paperless_mobile/core/navigation/push_routes.dart';
import 'package:paperless_mobile/core/widgets/hint_card.dart'; import 'package:paperless_mobile/core/widgets/hint_card.dart';
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.dart'; import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.dart';
import 'package:paperless_mobile/features/saved_view_details/cubit/saved_view_details_cubit.dart';
import 'package:paperless_mobile/features/saved_view_details/view/saved_view_details_page.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
class SavedViewList extends StatelessWidget { class SavedViewList extends StatelessWidget {
@@ -23,7 +17,7 @@ class SavedViewList extends StatelessWidget {
builder: (context, state) { builder: (context, state) {
return state.when( return state.when(
initial: () => SliverToBoxAdapter(child: Container()), initial: () => SliverToBoxAdapter(child: Container()),
loading: () => SliverToBoxAdapter( loading: () => const SliverToBoxAdapter(
child: Center( child: Center(
child: Text("Saved views loading..."), //TODO: INTL child: Text("Saved views loading..."), //TODO: INTL
), ),
@@ -55,7 +49,7 @@ class SavedViewList extends StatelessWidget {
), ),
); );
}, },
error: () => Center( error: () => const Center(
child: Text( child: Text(
"An error occurred while trying to load the saved views.", "An error occurred while trying to load the saved views.",
), ),
@@ -1,5 +1,4 @@
import 'package:hydrated_bloc/hydrated_bloc.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/database/tables/local_user_app_state.dart'; import 'package:paperless_mobile/core/database/tables/local_user_app_state.dart';
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart'; import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
@@ -9,7 +9,6 @@ import 'package:paperless_mobile/features/documents/view/widgets/selection/confi
import 'package:paperless_mobile/features/documents/view/widgets/selection/view_type_selection_widget.dart'; import 'package:paperless_mobile/features/documents/view/widgets/selection/view_type_selection_widget.dart';
import 'package:paperless_mobile/features/paged_document_view/view/document_paging_view_mixin.dart'; import 'package:paperless_mobile/features/paged_document_view/view/document_paging_view_mixin.dart';
import 'package:paperless_mobile/features/saved_view_details/cubit/saved_view_details_cubit.dart'; import 'package:paperless_mobile/features/saved_view_details/cubit/saved_view_details_cubit.dart';
import 'package:paperless_mobile/routes/document_details_route.dart';
class SavedViewDetailsPage extends StatefulWidget { class SavedViewDetailsPage extends StatefulWidget {
final Future<void> Function(SavedView savedView) onDelete; final Future<void> Function(SavedView savedView) onDelete;
@@ -1,6 +1,5 @@
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hive_flutter/adapters.dart'; import 'package:hive_flutter/adapters.dart';
import 'package:paperless_mobile/core/config/hive/hive_config.dart'; import 'package:paperless_mobile/core/config/hive/hive_config.dart';
import 'package:paperless_mobile/core/database/tables/global_settings.dart'; import 'package:paperless_mobile/core/database/tables/global_settings.dart';
@@ -49,7 +48,6 @@ class ManageAccountsPage extends StatelessWidget {
children: [ children: [
_buildAccountTile(context, globalSettings.currentLoggedInUser!, _buildAccountTile(context, globalSettings.currentLoggedInUser!,
box.get(globalSettings.currentLoggedInUser!)!, globalSettings), box.get(globalSettings.currentLoggedInUser!)!, globalSettings),
// if (otherAccounts.isNotEmpty) Text("Other accounts"),
Column( Column(
children: [ children: [
for (int index = 0; index < otherAccounts.length; index++) for (int index = 0; index < otherAccounts.length; index++)
@@ -69,17 +67,11 @@ class ManageAccountsPage extends StatelessWidget {
_onAddAccount(context, globalSettings.currentLoggedInUser!); _onAddAccount(context, globalSettings.currentLoggedInUser!);
}, },
), ),
Consumer<ApiVersion>( if (context.watch<ApiVersion>().hasMultiUserSupport)
builder: (context, value, child) { const ListTile(
if (value.version >= 3) {
return const ListTile(
leading: Icon(Icons.admin_panel_settings), leading: Icon(Icons.admin_panel_settings),
title: Text("Manage permissions"), //TODO : INTL title: Text("Manage permissions"), //TODO: INTL
); ),
}
return const SizedBox.shrink();
},
)
], ],
); );
}, },
@@ -106,9 +98,7 @@ class ManageAccountsPage extends StatelessWidget {
if (account.paperlessUser.fullName != null) Text(account.paperlessUser.fullName!), if (account.paperlessUser.fullName != null) Text(account.paperlessUser.fullName!),
Text( Text(
account.serverUrl.replaceFirst(RegExp(r'https://?'), ''), account.serverUrl.replaceFirst(RegExp(r'https://?'), ''),
style: TextStyle( style: TextStyle(color: theme.colorScheme.primary),
color: Theme.of(context).colorScheme.primary,
),
), ),
], ],
), ),
@@ -10,10 +10,10 @@ class SecuritySettingsPage extends StatelessWidget {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(S.of(context)!.security), title: Text(S.of(context)!.security),
actions: [ actions: const [
Padding( Padding(
padding: const EdgeInsets.all(16.0), padding: EdgeInsets.all(16.0),
child: const Icon(Icons.person_outline), child: Icon(Icons.person_outline),
) )
], ],
), ),
@@ -13,9 +13,9 @@ class ClearCacheSetting extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ListTile( return ListTile(
title: Text("Clear downloaded files"), //TODO: INTL title: const Text("Clear downloaded files"), //TODO: INTL
subtitle: subtitle:
Text("Deletes all files downloaded from this app."), //TODO: INTL const Text("Deletes all files downloaded from this app."), //TODO: INTL
onTap: () async { onTap: () async {
final dir = await FileService.downloadsDirectory; final dir = await FileService.downloadsDirectory;
final deletedSize = _dirSize(dir); final deletedSize = _dirSize(dir);
@@ -36,8 +36,8 @@ class ClearDownloadsSetting extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ListTile( return ListTile(
title: Text("Clear downloads"), //TODO: INTL title: const Text("Clear downloads"), //TODO: INTL
subtitle: Text( subtitle: const Text(
"Remove downloaded files, scans and clear the cache's content"), //TODO: INTL "Remove downloaded files, scans and clear the cache's content"), //TODO: INTL
onTap: () { onTap: () {
FileService.documentsDirectory; FileService.documentsDirectory;
@@ -1,13 +1,9 @@
import 'dart:io'; import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hive_flutter/adapters.dart';
import 'package:paperless_mobile/constants.dart'; import 'package:paperless_mobile/constants.dart';
import 'package:paperless_mobile/core/config/hive/hive_config.dart';
import 'package:paperless_mobile/core/translation/color_scheme_option_localization_mapper.dart'; import 'package:paperless_mobile/core/translation/color_scheme_option_localization_mapper.dart';
import 'package:paperless_mobile/core/widgets/hint_card.dart'; import 'package:paperless_mobile/core/widgets/hint_card.dart';
import 'package:paperless_mobile/core/database/tables/global_settings.dart';
import 'package:paperless_mobile/features/settings/model/color_scheme_option.dart'; import 'package:paperless_mobile/features/settings/model/color_scheme_option.dart';
import 'package:paperless_mobile/features/settings/view/widgets/global_settings_builder.dart'; import 'package:paperless_mobile/features/settings/view/widgets/global_settings_builder.dart';
import 'package:paperless_mobile/features/settings/view/widgets/radio_settings_dialog.dart'; import 'package:paperless_mobile/features/settings/view/widgets/radio_settings_dialog.dart';
@@ -69,7 +65,7 @@ class ColorSchemeOptionSetting extends StatelessWidget {
bool _isBelowAndroid12() { bool _isBelowAndroid12() {
if (Platform.isAndroid) { if (Platform.isAndroid) {
final int version = int.tryParse(androidInfo!.version.release ?? '0') ?? 0; final int version = int.tryParse(androidInfo!.version.release) ?? 0;
return version < 12; return version < 12;
} }
return false; return false;
@@ -1,6 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:flutter/src/widgets/placeholder.dart';
import 'package:hive_flutter/adapters.dart'; import 'package:hive_flutter/adapters.dart';
import 'package:paperless_mobile/core/config/hive/hive_config.dart'; import 'package:paperless_mobile/core/config/hive/hive_config.dart';
import 'package:paperless_mobile/core/database/tables/global_settings.dart'; import 'package:paperless_mobile/core/database/tables/global_settings.dart';
@@ -1,7 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:paperless_mobile/core/widgets/dialog_utils/dialog_cancel_button.dart'; import 'package:paperless_mobile/core/widgets/dialog_utils/dialog_cancel_button.dart';
import 'package:paperless_mobile/core/widgets/dialog_utils/dialog_confirm_button.dart'; import 'package:paperless_mobile/core/widgets/dialog_utils/dialog_confirm_button.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
class RadioSettingsDialog<T> extends StatefulWidget { class RadioSettingsDialog<T> extends StatefulWidget {
final List<RadioOption<T>> options; final List<RadioOption<T>> options;
@@ -1,5 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:paperless_mobile/features/settings/view/widgets/global_settings_builder.dart'; import 'package:paperless_mobile/features/settings/view/widgets/global_settings_builder.dart';
import 'package:paperless_mobile/features/settings/view/widgets/radio_settings_dialog.dart'; import 'package:paperless_mobile/features/settings/view/widgets/radio_settings_dialog.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
@@ -1,8 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/database/tables/local_user_account.dart'; import 'package:paperless_mobile/core/database/tables/local_user_account.dart';
import 'package:paperless_mobile/core/repository/user_repository.dart';
class UserAvatar extends StatelessWidget { class UserAvatar extends StatelessWidget {
final String userId; final String userId;
@@ -3,7 +3,6 @@ import 'package:hive_flutter/adapters.dart';
import 'package:paperless_mobile/core/config/hive/hive_config.dart'; import 'package:paperless_mobile/core/config/hive/hive_config.dart';
import 'package:paperless_mobile/core/database/tables/local_user_account.dart'; import 'package:paperless_mobile/core/database/tables/local_user_account.dart';
import 'package:paperless_mobile/core/database/tables/global_settings.dart'; import 'package:paperless_mobile/core/database/tables/global_settings.dart';
import 'package:paperless_mobile/core/database/tables/local_user_settings.dart';
class UserAccountBuilder extends StatelessWidget { class UserAccountBuilder extends StatelessWidget {
final Widget Function( final Widget Function(
@@ -1,5 +1,4 @@
import 'dart:collection'; import 'dart:collection';
import 'dart:developer';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:receive_sharing_intent/receive_sharing_intent.dart'; import 'package:receive_sharing_intent/receive_sharing_intent.dart';
+7 -9
View File
@@ -2,9 +2,7 @@ import 'dart:developer';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:paperless_api/paperless_api.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/core/translation/error_code_localization_mapper.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
class SnackBarActionConfig { class SnackBarActionConfig {
final String label; final String label;
@@ -67,13 +65,13 @@ void showGenericError(
showSnackBar( showSnackBar(
context, context,
error.toString(), error.toString(),
action: SnackBarActionConfig( // action: SnackBarActionConfig(
label: S.of(context)!.report, // label: S.of(context)!.report,
onPressed: () => GithubIssueService.createIssueFromError( // onPressed: () => GithubIssueService.createIssueFromError(
context, // context,
stackTrace: stackTrace, // stackTrace: stackTrace,
), // ),
), // ),
); );
log( log(
"An error has occurred.", "An error has occurred.",
+12 -12
View File
@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:connectivity_plus/connectivity_plus.dart';
@@ -56,10 +57,8 @@ String get defaultPreferredLocaleSubtag {
Future<void> _initHive() async { Future<void> _initHive() async {
await Hive.initFlutter(); await Hive.initFlutter();
// //TODO: REMOVE!
// await getApplicationDocumentsDirectory().then((value) => value.delete(recursive: true));
registerHiveAdapters(); registerHiveAdapters();
// await getApplicationDocumentsDirectory().then((value) => value.deleteSync(recursive: true));
await Hive.openBox<LocalUserAccount>(HiveBoxes.localUserAccount); await Hive.openBox<LocalUserAccount>(HiveBoxes.localUserAccount);
await Hive.openBox<LocalUserAppState>(HiveBoxes.localUserAppState); await Hive.openBox<LocalUserAppState>(HiveBoxes.localUserAppState);
final globalSettingsBox = await Hive.openBox<GlobalSettings>(HiveBoxes.globalSettings); final globalSettingsBox = await Hive.openBox<GlobalSettings>(HiveBoxes.globalSettings);
@@ -126,9 +125,7 @@ void main() async {
providers: [ providers: [
ChangeNotifierProvider.value(value: sessionManager), ChangeNotifierProvider.value(value: sessionManager),
Provider<LocalAuthenticationService>.value(value: localAuthService), Provider<LocalAuthenticationService>.value(value: localAuthService),
Provider<ConnectivityStatusService>.value( Provider<ConnectivityStatusService>.value(value: connectivityStatusService),
value: connectivityStatusService,
),
Provider<LocalNotificationService>.value(value: localNotificationService), Provider<LocalNotificationService>.value(value: localNotificationService),
Provider.value(value: DocumentChangedNotifier()), Provider.value(value: DocumentChangedNotifier()),
], ],
@@ -136,12 +133,7 @@ void main() async {
providers: [ providers: [
BlocProvider<ConnectivityCubit>.value(value: connectivityCubit), BlocProvider<ConnectivityCubit>.value(value: connectivityCubit),
BlocProvider( BlocProvider(
create: (context) => AuthenticationCubit( create: (context) => AuthenticationCubit(localAuthService, apiFactory, sessionManager),
localAuthService,
apiFactory,
sessionManager,
),
child: Container(),
) )
], ],
child: PaperlessMobileEntrypoint( child: PaperlessMobileEntrypoint(
@@ -215,6 +207,7 @@ class AuthenticationWrapper extends StatefulWidget {
} }
class _AuthenticationWrapperState extends State<AuthenticationWrapper> { class _AuthenticationWrapperState extends State<AuthenticationWrapper> {
late final StreamSubscription _shareMediaSubscription;
@override @override
void didChangeDependencies() { void didChangeDependencies() {
super.didChangeDependencies(); super.didChangeDependencies();
@@ -223,6 +216,12 @@ class _AuthenticationWrapperState extends State<AuthenticationWrapper> {
}); });
} }
@override
void dispose() {
_shareMediaSubscription.cancel();
super.dispose();
}
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@@ -233,6 +232,7 @@ class _AuthenticationWrapperState extends State<AuthenticationWrapper> {
} }
initializeDateFormatting(); initializeDateFormatting();
// For sharing files coming from outside the app while the app is still opened // For sharing files coming from outside the app while the app is still opened
_shareMediaSubscription =
ReceiveSharingIntent.getMediaStream().listen(ShareIntentQueue.instance.addAll); ReceiveSharingIntent.getMediaStream().listen(ShareIntentQueue.instance.addAll);
// For sharing files coming from outside the app while the app is closed // For sharing files coming from outside the app while the app is closed
ReceiveSharingIntent.getInitialMedia().then(ShareIntentQueue.instance.addAll); ReceiveSharingIntent.getInitialMedia().then(ShareIntentQueue.instance.addAll);
+1 -1
View File
@@ -43,7 +43,7 @@ ThemeData buildTheme({
inputDecorationTheme: _defaultInputDecorationTheme, inputDecorationTheme: _defaultInputDecorationTheme,
listTileTheme: _defaultListTileTheme, listTileTheme: _defaultListTileTheme,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
appBarTheme: AppBarTheme( appBarTheme: const AppBarTheme(
scrolledUnderElevation: 0, scrolledUnderElevation: 0,
), ),
chipTheme: ChipThemeData( chipTheme: ChipThemeData(
@@ -1,6 +1,5 @@
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_api/src/models/user_model.dart';
class PaperlessApiHiveTypeIds { class PaperlessApiHiveTypeIds {
PaperlessApiHiveTypeIds._(); PaperlessApiHiveTypeIds._();
@@ -56,7 +55,6 @@ void registerPaperlessApiHiveTypeAdapters() {
// Users and permissions // Users and permissions
Hive.registerAdapter(UserModelV3Adapter()); Hive.registerAdapter(UserModelV3Adapter());
Hive.registerAdapter(UserModelV2Adapter()); Hive.registerAdapter(UserModelV2Adapter());
Hive.registerAdapter(UserPermissionsAdapter());
Hive.registerAdapter(InheritedPermissionsAdapter()); Hive.registerAdapter(InheritedPermissionsAdapter());
Hive.registerAdapter(GroupModelAdapter()); Hive.registerAdapter(GroupModelAdapter());
Hive.registerAdapter(PermissionsAdapter()); Hive.registerAdapter(PermissionsAdapter());
@@ -1,7 +1,5 @@
import 'package:json_annotation/json_annotation.dart'; import 'package:json_annotation/json_annotation.dart';
import 'package:paperless_api/src/models/models.dart'; import 'package:paperless_api/src/models/models.dart';
import 'package:paperless_api/src/models/query_parameters/date_range_queries/absolute_date_range_query.dart';
import 'package:paperless_api/src/models/query_parameters/date_range_queries/relative_date_range_query.dart';
class DateRangeQueryJsonConverter class DateRangeQueryJsonConverter
extends JsonConverter<DateRangeQuery, Map<String, dynamic>> { extends JsonConverter<DateRangeQuery, Map<String, dynamic>> {
@@ -1,4 +1,3 @@
import 'dart:developer';
import 'package:json_annotation/json_annotation.dart'; import 'package:json_annotation/json_annotation.dart';
@@ -1,9 +1,10 @@
// ignore_for_file: unused_field
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:json_annotation/json_annotation.dart'; import 'package:json_annotation/json_annotation.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_api/src/constants.dart'; import 'package:paperless_api/src/constants.dart';
import 'package:paperless_api/src/converters/local_date_time_json_converter.dart'; import 'package:paperless_api/src/converters/local_date_time_json_converter.dart';
import 'package:paperless_api/src/models/query_parameters/tags_query/tags_query.dart';
part 'filter_rule_model.g.dart'; part 'filter_rule_model.g.dart';
@@ -11,7 +11,7 @@ class GroupModel with _$GroupModel {
const factory GroupModel({ const factory GroupModel({
@HiveField(0) required int id, @HiveField(0) required int id,
@HiveField(1) required String name, @HiveField(1) required String name,
@HiveField(2) required List<UserPermissions> permissions, @HiveField(2) required List<String> permissions,
}) = _GroupModel; }) = _GroupModel;
factory GroupModel.fromJson(Map<String, dynamic> json) => _$GroupModelFromJson(json); factory GroupModel.fromJson(Map<String, dynamic> json) => _$GroupModelFromJson(json);
@@ -25,7 +25,7 @@ mixin _$GroupModel {
@HiveField(1) @HiveField(1)
String get name => throw _privateConstructorUsedError; String get name => throw _privateConstructorUsedError;
@HiveField(2) @HiveField(2)
List<UserPermissions> get permissions => throw _privateConstructorUsedError; List<String> get permissions => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError; Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true) @JsonKey(ignore: true)
@@ -42,7 +42,7 @@ abstract class $GroupModelCopyWith<$Res> {
$Res call( $Res call(
{@HiveField(0) int id, {@HiveField(0) int id,
@HiveField(1) String name, @HiveField(1) String name,
@HiveField(2) List<UserPermissions> permissions}); @HiveField(2) List<String> permissions});
} }
/// @nodoc /// @nodoc
@@ -74,7 +74,7 @@ class _$GroupModelCopyWithImpl<$Res, $Val extends GroupModel>
permissions: null == permissions permissions: null == permissions
? _value.permissions ? _value.permissions
: permissions // ignore: cast_nullable_to_non_nullable : permissions // ignore: cast_nullable_to_non_nullable
as List<UserPermissions>, as List<String>,
) as $Val); ) as $Val);
} }
} }
@@ -90,7 +90,7 @@ abstract class _$$_GroupModelCopyWith<$Res>
$Res call( $Res call(
{@HiveField(0) int id, {@HiveField(0) int id,
@HiveField(1) String name, @HiveField(1) String name,
@HiveField(2) List<UserPermissions> permissions}); @HiveField(2) List<String> permissions});
} }
/// @nodoc /// @nodoc
@@ -120,7 +120,7 @@ class __$$_GroupModelCopyWithImpl<$Res>
permissions: null == permissions permissions: null == permissions
? _value._permissions ? _value._permissions
: permissions // ignore: cast_nullable_to_non_nullable : permissions // ignore: cast_nullable_to_non_nullable
as List<UserPermissions>, as List<String>,
)); ));
} }
} }
@@ -131,7 +131,7 @@ class _$_GroupModel implements _GroupModel {
const _$_GroupModel( const _$_GroupModel(
{@HiveField(0) required this.id, {@HiveField(0) required this.id,
@HiveField(1) required this.name, @HiveField(1) required this.name,
@HiveField(2) required final List<UserPermissions> permissions}) @HiveField(2) required final List<String> permissions})
: _permissions = permissions; : _permissions = permissions;
factory _$_GroupModel.fromJson(Map<String, dynamic> json) => factory _$_GroupModel.fromJson(Map<String, dynamic> json) =>
@@ -143,10 +143,10 @@ class _$_GroupModel implements _GroupModel {
@override @override
@HiveField(1) @HiveField(1)
final String name; final String name;
final List<UserPermissions> _permissions; final List<String> _permissions;
@override @override
@HiveField(2) @HiveField(2)
List<UserPermissions> get permissions { List<String> get permissions {
if (_permissions is EqualUnmodifiableListView) return _permissions; if (_permissions is EqualUnmodifiableListView) return _permissions;
// ignore: implicit_dynamic_type // ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_permissions); return EqualUnmodifiableListView(_permissions);
@@ -191,8 +191,7 @@ abstract class _GroupModel implements GroupModel {
const factory _GroupModel( const factory _GroupModel(
{@HiveField(0) required final int id, {@HiveField(0) required final int id,
@HiveField(1) required final String name, @HiveField(1) required final String name,
@HiveField(2) required final List<UserPermissions> permissions}) = @HiveField(2) required final List<String> permissions}) = _$_GroupModel;
_$_GroupModel;
factory _GroupModel.fromJson(Map<String, dynamic> json) = factory _GroupModel.fromJson(Map<String, dynamic> json) =
_$_GroupModel.fromJson; _$_GroupModel.fromJson;
@@ -205,7 +204,7 @@ abstract class _GroupModel implements GroupModel {
String get name; String get name;
@override @override
@HiveField(2) @HiveField(2)
List<UserPermissions> get permissions; List<String> get permissions;
@override @override
@JsonKey(ignore: true) @JsonKey(ignore: true)
_$$_GroupModelCopyWith<_$_GroupModel> get copyWith => _$$_GroupModelCopyWith<_$_GroupModel> get copyWith =>
@@ -1,7 +1,5 @@
import 'dart:developer';
import 'dart:ui'; import 'dart:ui';
import 'package:flutter/foundation.dart';
import 'package:json_annotation/json_annotation.dart'; import 'package:json_annotation/json_annotation.dart';
import 'package:paperless_api/src/converters/hex_color_json_converter.dart'; import 'package:paperless_api/src/converters/hex_color_json_converter.dart';
import 'package:paperless_api/src/models/labels/label_model.dart'; import 'package:paperless_api/src/models/labels/label_model.dart';
@@ -24,7 +22,7 @@ class Tag extends Label {
final bool isInboxTag; final bool isInboxTag;
Tag({ const Tag({
super.id, super.id,
required super.name, required super.name,
super.documentCount, super.documentCount,
@@ -53,6 +53,5 @@ enum ErrorCode {
requestTimedOut, requestTimedOut,
unsupportedFileFormat, unsupportedFileFormat,
missingClientCertificate, missingClientCertificate,
acknowledgeTasksError, acknowledgeTasksError;
notAuthorized;
} }
@@ -1,117 +1,30 @@
import 'package:freezed_annotation/freezed_annotation.dart'; enum PermissionAction {
import 'package:hive/hive.dart'; add("add"),
import 'package:paperless_api/config/hive/hive_type_ids.dart'; change("change"),
part 'user_permissions.g.dart'; delete("delete"),
view("view");
@HiveType(typeId: PaperlessApiHiveTypeIds.userPermissions)
@JsonEnum(valueField: "value")
enum UserPermissions {
@HiveField(0)
addCorrespondent("add_correspondent"),
@HiveField(1)
addDocument("add_document"),
@HiveField(2)
addDocumenttype("add_documenttype"),
@HiveField(3)
addGroup("add_group"),
@HiveField(4)
addMailaccount("add_mailaccount"),
@HiveField(5)
addMailrule("add_mailrule"),
@HiveField(6)
addNote("add_note"),
@HiveField(7)
addPaperlesstask("add_paperlesstask"),
@HiveField(8)
addSavedview("add_savedview"),
@HiveField(9)
addStoragepath("add_storagepath"),
@HiveField(10)
addTag("add_tag"),
@HiveField(11)
addUisettings("add_uisettings"),
@HiveField(12)
addUser("add_user"),
@HiveField(13)
changeCorrespondent("change_correspondent"),
@HiveField(14)
changeDocument("change_document"),
@HiveField(15)
changeDocumenttype("change_documenttype"),
@HiveField(16)
changeGroup("change_group"),
@HiveField(17)
changeMailaccount("change_mailaccount"),
@HiveField(18)
changeMailrule("change_mailrule"),
@HiveField(19)
changeNote("change_note"),
@HiveField(20)
changePaperlesstask("change_paperlesstask"),
@HiveField(21)
changeSavedview("change_savedview"),
@HiveField(22)
changeStoragepath("change_storagepath"),
@HiveField(23)
changeTag("change_tag"),
@HiveField(24)
changeUisettings("change_uisettings"),
@HiveField(25)
changeUser("change_user"),
@HiveField(26)
deleteCorrespondent("delete_correspondent"),
@HiveField(27)
deleteDocument("delete_document"),
@HiveField(28)
deleteDocumenttype("delete_documenttype"),
@HiveField(29)
deleteGroup("delete_group"),
@HiveField(30)
deleteMailaccount("delete_mailaccount"),
@HiveField(31)
deleteMailrule("delete_mailrule"),
@HiveField(32)
deleteNote("delete_note"),
@HiveField(33)
deletePaperlesstask("delete_paperlesstask"),
@HiveField(34)
deleteSavedview("delete_savedview"),
@HiveField(35)
deleteStoragepath("delete_storagepath"),
@HiveField(36)
deleteTag("delete_tag"),
@HiveField(37)
deleteUisettings("delete_uisettings"),
@HiveField(38)
deleteUser("delete_user"),
@HiveField(39)
viewCorrespondent("view_correspondent"),
@HiveField(40)
viewDocument("view_document"),
@HiveField(41)
viewDocumenttype("view_documenttype"),
@HiveField(42)
viewGroup("view_group"),
@HiveField(43)
viewMailaccount("view_mailaccount"),
@HiveField(44)
viewMailrule("view_mailrule"),
@HiveField(45)
viewNote("view_note"),
@HiveField(46)
viewPaperlesstask("view_paperlesstask"),
@HiveField(47)
viewSavedview("view_savedview"),
@HiveField(48)
viewStoragepath("view_storagepath"),
@HiveField(49)
viewTag("view_tag"),
@HiveField(50)
viewUisettings("view_uisettings"),
@HiveField(51)
viewUser("view_user");
const UserPermissions(this.value);
final String value; final String value;
const PermissionAction(this.value);
}
enum PermissionTarget {
correspondent("correspondent"),
document("document"),
documentType("documenttype"),
group("group"),
mailAccount("mailaccount"),
mailrule("mailrule"),
note("note"),
paperlesstask("paperlesstask"),
savedView("savedview"),
storagePath("storagepath"),
tag("tag"),
uiSettings("uisettings"),
user("user"),
logentry("logentry"),
permission("permission");
final String value;
const PermissionTarget(this.value);
} }
@@ -1,5 +1,4 @@
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:paperless_api/config/hive/hive_type_ids.dart';
import 'package:paperless_api/src/models/query_parameters/date_range_queries/date_range_query_field.dart'; import 'package:paperless_api/src/models/query_parameters/date_range_queries/date_range_query_field.dart';
import 'date_range_query.dart'; import 'date_range_query.dart';

Some files were not shown because too many files have changed in this diff Show More