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

View File

@@ -0,0 +1,5 @@
class ServerMessageException implements Exception {
final String message;
ServerMessageException(this.message);
}

View File

@@ -2,6 +2,7 @@ import 'dart:io';
import 'package:dio/dio.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';
class DioHttpErrorInterceptor extends Interceptor {
@@ -16,14 +17,15 @@ class DioHttpErrorInterceptor extends Interceptor {
return _handlePlainError(data, handler, err);
}
} else if (err.response?.statusCode == 403) {
handler.reject(
DioError(
var data = err.response!.data;
if (data is Map && data.containsKey("detail")) {
handler.reject(DioError(
requestOptions: err.requestOptions,
error: const PaperlessServerException(ErrorCode.notAuthorized),
error: ServerMessageException(data['detail']),
response: err.response,
),
);
return;
));
return;
}
} else if (err.error is SocketException) {
final ex = err.error as SocketException;
if (ex.osError?.errorCode == _OsErrorCodes.serverUnreachable.code) {
@@ -84,8 +86,7 @@ class DioHttpErrorInterceptor extends Interceptor {
}
enum _OsErrorCodes {
serverUnreachable(101),
hostNotFound(7);
serverUnreachable(101);
const _OsErrorCodes(this.code);
final int code;

View File

@@ -3,6 +3,7 @@ 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/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';
@@ -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/view/document_search_page.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/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/view/saved_view_details_page.dart';
import 'package:paperless_mobile/routes/document_details_route.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) {
final currentUser =
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<LocalNotificationService>()),
Provider.value(value: context.read<CacheManager>()),
Provider.value(value: context.read<ConnectivityCubit>()),
if (context.read<ApiVersion>().hasMultiUserSupport)
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(
MaterialPageRoute(
builder: (_) => MultiProvider(
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<DocumentChangedNotifier>()),
Provider.value(value: context.read<PaperlessDocumentsApi>()),
Provider.value(value: context.read<CacheManager>()),
Provider.value(value: context.read<ConnectivityCubit>()),
],
child: SavedViewDetailsPage(
onDelete: context.read<SavedViewCubit>().remove,
),
builder: (_, child) {
return BlocProvider(
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(),
),
),
),
);
}

View File

@@ -1,5 +1,4 @@
import 'dart:async';
import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:paperless_api/paperless_api.dart';

View File

@@ -1,7 +1,6 @@
import 'dart:async';
import 'package:flutter/widgets.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/repository/label_repository_state.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> {
final PaperlessLabelsApi _api;
LabelRepository(this._api) : super(const LabelRepositoryState());
LabelRepository(this._api) : super(const LabelRepositoryState()) {
initialize();
}
Future<void> initialize() {
debugPrint("Initializing labels...");

View File

@@ -7,7 +7,9 @@ import 'package:paperless_mobile/core/repository/saved_view_repository_state.dar
class SavedViewRepository extends PersistentRepository<SavedViewRepositoryState> {
final PaperlessSavedViewsApi _api;
SavedViewRepository(this._api) : super(const SavedViewRepositoryState());
SavedViewRepository(this._api) : super(const SavedViewRepositoryState()) {
initialize();
}
Future<void> initialize() {
return findAll();

View File

@@ -1,4 +1,3 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:paperless_mobile/core/model/github_error_report.model.dart';

View File

@@ -1,16 +1,8 @@
import 'dart:convert';
import 'dart:io';
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:web_socket_channel/io.dart';
// import 'package:web_socket_channel/io.dart';
abstract class StatusService {
Future<void> startListeningBeforeDocumentUpload(
@@ -19,7 +11,7 @@ abstract class StatusService {
class WebSocketStatusService implements StatusService {
late WebSocket? socket;
late IOWebSocketChannel? _channel;
// late IOWebSocketChannel? _channel;
WebSocketStatusService();

View File

@@ -72,7 +72,5 @@ String translateError(BuildContext context, ErrorCode code) {
return S.of(context)!.couldNotLoadSuggestions;
case ErrorCode.acknowledgeTasksError:
return S.of(context)!.couldNotAcknowledgeTasks;
case ErrorCode.notAuthorized:
return "You do not have the permission to perform this action."; //TODO: INTL
}
}

View File

@@ -1,5 +1,3 @@
import 'package:paperless_api/paperless_api.dart';
import 'package:rxdart/subjects.dart';
typedef JSON = Map<String, dynamic>;
typedef PaperlessValidationErrors = Map<String, String>;

View File

@@ -1,6 +1,4 @@
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';
class DialogCancelButton extends StatelessWidget {

View File

@@ -1,6 +1,4 @@
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';
enum DialogConfirmButtonStyle {

View File

@@ -181,8 +181,7 @@ class FormBuilderColorPickerField extends FormBuilderField<Color> {
);
@override
FormBuilderColorPickerFieldState createState() =>
FormBuilderColorPickerFieldState();
FormBuilderColorPickerFieldState createState() => FormBuilderColorPickerFieldState();
}
class FormBuilderColorPickerFieldState
@@ -217,8 +216,6 @@ class FormBuilderColorPickerFieldState
final selected = await showDialog<bool>(
context: context,
builder: (BuildContext context) {
final materialLocalizations = S.of(context)!;
return AlertDialog(
// title: null, //const Text('Pick a color!'),
content: _buildColorPicker(),

View File

@@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';

View File

@@ -27,8 +27,7 @@ import 'package:flutter/services.dart';
typedef ChipsInputSuggestions<T> = Future<List<T>> Function(String query);
typedef ChipSelected<T> = void Function(T data, bool selected);
typedef ChipsBuilder<T> = Widget Function(
BuildContext context, ChipsInputState<T> state, T data);
typedef ChipsBuilder<T> = Widget Function(BuildContext context, ChipsInputState<T> state, T data);
class ChipsInput<T> extends StatefulWidget {
const ChipsInput({
@@ -71,8 +70,7 @@ class ChipsInputState<T> extends State<ChipsInput<T>> {
TextEditingValue get currentTextEditingValue => _value;
bool get _hasInputConnection =>
_connection != null && (_connection?.attached ?? false);
bool get _hasInputConnection => _connection != null && (_connection?.attached ?? false);
void requestKeyboard() {
if (_focusNode.hasFocus) {
@@ -191,8 +189,7 @@ class ChipsInputState<T> extends State<ChipsInput<T>> {
child: ListView.builder(
itemCount: _suggestions.length,
itemBuilder: (BuildContext context, int index) {
return widget.suggestionBuilder(
context, this, _suggestions[index]);
return widget.suggestionBuilder(context, this, _suggestions[index]);
},
),
),
@@ -213,14 +210,11 @@ class ChipsInputState<T> extends State<ChipsInput<T>> {
}
int _countReplacements(TextEditingValue value) {
return value.text.codeUnits
.where((ch) => ch == kObjectReplacementChar)
.length;
return value.text.codeUnits.where((ch) => ch == kObjectReplacementChar).length;
}
void _updateTextInputState() {
final text =
String.fromCharCodes(_chips.map((_) => kObjectReplacementChar));
final text = String.fromCharCodes(_chips.map((_) => kObjectReplacementChar));
_value = TextEditingValue(
text: text,
selection: TextSelection.collapsed(offset: text.length),
@@ -233,35 +227,30 @@ class ChipsInputState<T> extends State<ChipsInput<T>> {
final localId = ++_searchId;
final results = await widget.findSuggestions(value);
if (_searchId == localId && mounted) {
setState(() => _suggestions = results
.where((profile) => !_chips.contains(profile))
.toList(growable: false));
setState(() => _suggestions =
results.where((profile) => !_chips.contains(profile)).toList(growable: false));
}
}
}
class _TextCaret extends StatefulWidget {
const _TextCaret({
this.duration = const Duration(milliseconds: 500),
this.resumed = false,
});
final Duration duration;
final bool resumed;
@override
_TextCursorState createState() => _TextCursorState();
}
class _TextCursorState extends State<_TextCaret>
with SingleTickerProviderStateMixin {
class _TextCursorState extends State<_TextCaret> with SingleTickerProviderStateMixin {
bool _displayed = false;
late Timer _timer;
@override
void initState() {
super.initState();
_timer = Timer.periodic(widget.duration, _onTimer);
}
void _onTimer(Timer timer) {

View File

@@ -251,7 +251,6 @@ abstract class SearchDelegate<T> {
///
/// Setting the query string programmatically moves the cursor to the end of the text field.
set query(String value) {
assert(query != null);
_queryTextController.text = value;
if (_queryTextController.text.isNotEmpty) {
_queryTextController.selection = TextSelection.fromPosition(

View File

@@ -7,7 +7,6 @@ import 'dart:ui';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
const int _kOpenViewMilliseconds = 600;
const Duration _kOpenViewDuration =
@@ -1649,7 +1648,6 @@ class SearchBarTheme extends InheritedWidget {
final SearchBarTheme? searchBarTheme =
context.dependOnInheritedWidgetOfExactType<SearchBarTheme>();
return searchBarTheme?.data ?? const SearchBarThemeData();
;
}
@override

View File

@@ -1,6 +1,5 @@
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:paperless_mobile/constants.dart';
import 'package:paperless_mobile/core/widgets/paperless_logo.dart';

View File

@@ -50,7 +50,7 @@ class _ApplicationIntroSlideshowState extends State<ApplicationIntroSlideshow> {
image: AssetImages.organizeDocuments.image,
),
),
bodyWidget: Column(
bodyWidget: const Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
@@ -70,7 +70,7 @@ class _ApplicationIntroSlideshowState extends State<ApplicationIntroSlideshow> {
padding: const EdgeInsets.all(8.0),
child: Image(image: AssetImages.secureDocuments.image),
),
bodyWidget: Column(
bodyWidget: const Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
@@ -90,8 +90,8 @@ class _ApplicationIntroSlideshowState extends State<ApplicationIntroSlideshow> {
padding: const EdgeInsets.all(8.0),
child: Image(image: AssetImages.success.image),
),
bodyWidget: Column(
children: const [
bodyWidget: const Column(
children: [
BiometricAuthenticationSetting(),
LanguageSelectionSetting(),
ThemeModeSetting(),

View File

@@ -2,7 +2,6 @@ import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:collection/collection.dart';
import 'package:equatable/equatable.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
import 'package:paperless_mobile/core/repository/label_repository.dart';

View File

@@ -17,6 +17,7 @@ class BulkEditLabelBottomSheet<T extends Label> extends StatefulWidget {
final LabelOptionsSelector<T> availableOptionsSelector;
final void Function(int? selectedId) onSubmit;
final int? initialValue;
final bool canCreateNewLabel;
const BulkEditLabelBottomSheet({
super.key,
@@ -26,6 +27,7 @@ class BulkEditLabelBottomSheet<T extends Label> extends StatefulWidget {
required this.availableOptionsSelector,
required this.onSubmit,
this.initialValue,
required this.canCreateNewLabel,
});
@override
@@ -58,6 +60,7 @@ class _BulkEditLabelBottomSheetState<T extends Label> extends State<BulkEditLabe
initialValue: widget.initialValue != null
? IdQueryParameter.fromId(widget.initialValue!)
: const IdQueryParameter.unset(),
canCreateNewLabel: widget.canCreateNewLabel,
name: "labelFormField",
options: widget.availableOptionsSelector(state),
labelText: widget.formFieldLabel,

View File

@@ -1,6 +1,4 @@
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_confirm_button.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';

View File

@@ -26,8 +26,8 @@ class _FullscreenBulkEditTagsWidgetState
/// Tags not assigned to at least one document in the selection
late final List<int> _nonSharedTags;
List<int> _addTags = [];
List<int> _removeTags = [];
final List<int> _addTags = [];
final List<int> _removeTags = [];
late List<int> _filteredTags;
@override

View File

@@ -41,7 +41,7 @@ class _SelectFileTypeDialogState extends State<SelectFileTypeDialog> {
},
title: Text(S.of(context)!.archivedPdf),
),
Divider(),
const Divider(),
CheckboxListTile(
controlAffinity: ListTileControlAffinity.leading,
value: _rememberSelection,

View File

@@ -1,13 +1,9 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hive_flutter/adapters.dart';
import 'package:open_filex/open_filex.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/database/tables/global_settings.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/widgets/material/colored_tab_bar.dart';
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
@@ -57,7 +53,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
Widget build(BuildContext context) {
final apiVersion = context.watch<ApiVersion>();
final tabLength = 4 + (apiVersion.supportsPermissions ? 1 : 0);
final tabLength = 4 + (apiVersion.hasMultiUserSupport ? 1 : 0);
return WillPopScope(
onWillPop: () async {
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(
child: Text(
"Permissions",
@@ -260,13 +256,9 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
}
Widget _buildEditButton() {
bool canEdit = context.watchInternetConnection;
final apiVersion = context.watch<ApiVersion>();
if (apiVersion.supportsPermissions) {
canEdit =
LocalUserAccount.current.paperlessUser.hasPermission(UserPermissions.changeDocument);
}
bool canEdit = context.watchInternetConnection &&
LocalUserAccount.current.paperlessUser
.hasPermission(PermissionAction.change, PermissionTarget.document);
if (!canEdit) {
return const SizedBox.shrink();
}
@@ -281,7 +273,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
verticalOffset: 40,
child: FloatingActionButton(
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) {
final isConnected = connectivityState.isConnected;
final canDelete = LocalUserAccount.current.paperlessUser
.hasPermission(UserPermissions.deleteDocument);
final canDelete = isConnected &&
LocalUserAccount.current.paperlessUser
.hasPermission(PermissionAction.delete, PermissionTarget.document);
return Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
IconButton(
tooltip: S.of(context)!.deleteDocumentTooltip,
icon: const Icon(Icons.delete),
onPressed: (isConnected && canDelete) ? () => _onDelete(state.document) : null,
onPressed: canDelete ? () => _onDelete(state.document) : null,
).paddedSymmetrically(horizontal: 4),
DocumentDownloadButton(
document: state.document,

View File

@@ -3,6 +3,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:paperless_api/paperless_api.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/extensions/flutter_extensions.dart';
import 'package:paperless_mobile/features/document_details/cubit/document_details_cubit.dart';
@@ -17,8 +18,7 @@ class ArchiveSerialNumberField extends StatefulWidget {
});
@override
State<ArchiveSerialNumberField> createState() =>
_ArchiveSerialNumberFieldState();
State<ArchiveSerialNumberField> createState() => _ArchiveSerialNumberFieldState();
}
class _ArchiveSerialNumberFieldState extends State<ArchiveSerialNumberField> {
@@ -39,20 +39,21 @@ class _ArchiveSerialNumberFieldState extends State<ArchiveSerialNumberField> {
void _clearButtonListener() {
setState(() {
_showClearButton = _asnEditingController.text.isNotEmpty;
_canUpdate = int.tryParse(_asnEditingController.text) !=
widget.document.archiveSerialNumber;
_canUpdate = int.tryParse(_asnEditingController.text) != widget.document.archiveSerialNumber;
});
}
@override
Widget build(BuildContext context) {
final userCanEditDocument = LocalUserAccount.current.paperlessUser.hasPermission(
PermissionAction.change,
PermissionTarget.document,
);
return BlocListener<DocumentDetailsCubit, DocumentDetailsState>(
listenWhen: (previous, current) =>
previous.document.archiveSerialNumber !=
current.document.archiveSerialNumber,
previous.document.archiveSerialNumber != current.document.archiveSerialNumber,
listener: (context, state) {
_asnEditingController.text =
state.document.archiveSerialNumber?.toString() ?? '';
_asnEditingController.text = state.document.archiveSerialNumber?.toString() ?? '';
setState(() {
_canUpdate = false;
});
@@ -61,6 +62,7 @@ class _ArchiveSerialNumberFieldState extends State<ArchiveSerialNumberField> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextFormField(
enabled: userCanEditDocument,
controller: _asnEditingController,
keyboardType: TextInputType.number,
onChanged: (value) {
@@ -78,15 +80,13 @@ class _ArchiveSerialNumberFieldState extends State<ArchiveSerialNumberField> {
IconButton(
icon: const Icon(Icons.clear),
color: Theme.of(context).colorScheme.primary,
onPressed: _asnEditingController.clear,
onPressed: userCanEditDocument ? _asnEditingController.clear : null,
),
IconButton(
icon: const Icon(Icons.plus_one_rounded),
color: Theme.of(context).colorScheme.primary,
onPressed:
context.watchInternetConnection && !_showClearButton
? _onAutoAssign
: null,
context.watchInternetConnection && !_showClearButton ? _onAutoAssign : null,
).paddedOnly(right: 8),
],
),
@@ -97,9 +97,7 @@ class _ArchiveSerialNumberFieldState extends State<ArchiveSerialNumberField> {
),
TextButton.icon(
icon: const Icon(Icons.done),
onPressed: context.watchInternetConnection && _canUpdate
? _onSubmitted
: null,
onPressed: context.watchInternetConnection && _canUpdate ? _onSubmitted : null,
label: Text(S.of(context)!.save),
).padded(),
],

View File

@@ -24,7 +24,7 @@ class DetailsItem extends StatelessWidget {
}
DetailsItem.text(
String text, {
String text, {super.key,
required this.label,
required BuildContext context,
}) : content = Text(

View File

@@ -29,8 +29,8 @@ class _DocumentMetaDataWidgetState extends State<DocumentMetaDataWidget> {
builder: (context, state) {
debugPrint("Building state...");
if (state.metaData == null) {
return SliverToBoxAdapter(
child: const Center(
return const SliverToBoxAdapter(
child: Center(
child: CircularProgressIndicator(),
),
);

View File

@@ -4,7 +4,6 @@ import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/widgets/highlighted_text.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/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/view/widgets/label_text.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';

View File

@@ -1,5 +1,4 @@
import 'dart:async';
import 'dart:developer';
import 'package:flutter/material.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: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/workarounds/colored_chip.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_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/view/widgets/label_form_field.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
@@ -117,6 +116,11 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
name: fkCorrespondent,
prefixIcon: const Icon(Icons.person_outlined),
allowSelectUnassigned: true,
canCreateNewLabel:
LocalUserAccount.current.paperlessUser.hasPermission(
PermissionAction.add,
PermissionTarget.correspondent,
),
),
if (_filteredSuggestions?.hasSuggestedCorrespondents ?? false)
_buildSuggestionsSkeleton<int>(
@@ -144,6 +148,11 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
initialName: currentInput,
),
),
canCreateNewLabel:
LocalUserAccount.current.paperlessUser.hasPermission(
PermissionAction.add,
PermissionTarget.documentType,
),
addLabelText: S.of(context)!.addDocumentType,
labelText: S.of(context)!.documentType,
initialValue: state.document.documentType != null
@@ -177,6 +186,11 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
value: context.read<LabelRepository>(),
child: AddStoragePathPage(initalName: initialValue),
),
canCreateNewLabel:
LocalUserAccount.current.paperlessUser.hasPermission(
PermissionAction.add,
PermissionTarget.storagePath,
),
addLabelText: S.of(context)!.addStoragePath,
labelText: S.of(context)!.storagePath,
options: state.storagePaths,

View File

@@ -98,7 +98,7 @@ class _ScannedImageItemState extends State<ScannedImageItem> {
alignment: Alignment.bottomCenter,
child: TextButton(
onPressed: widget.onDelete,
child: Text("Remove"),
child: const Text("Remove"),
),
),
],

View File

@@ -3,15 +3,7 @@ import 'dart:async';
import 'package:collection/collection.dart';
import 'package:flutter/material.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/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/features/document_search/cubit/document_search_cubit.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/generated/l10n/app_localizations.dart';
import 'package:paperless_mobile/routes/document_details_route.dart';
import 'dart:math' as math;
import 'package:provider/provider.dart';
class DocumentSearchPage extends StatefulWidget {
const DocumentSearchPage({super.key});

View File

@@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hive_flutter/adapters.dart';
import 'package:paperless_mobile/core/config/hive/hive_config.dart';
import 'package:paperless_mobile/core/database/tables/local_user_account.dart';

View File

@@ -35,6 +35,7 @@ class DocumentUploadCubit extends Cubit<DocumentUploadState> {
int? correspondent,
Iterable<int> tags = const [],
DateTime? createdAt,
int? asn,
}) async {
return await _documentApi.create(
bytes,
@@ -44,6 +45,7 @@ class DocumentUploadCubit extends Cubit<DocumentUploadState> {
documentType: documentType,
tags: tags,
createdAt: createdAt,
asn: asn,
);
}

View File

@@ -7,6 +7,7 @@ import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:intl/intl.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/type/types.dart';
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
@@ -32,7 +33,6 @@ class DocumentUploadPreparationPage extends StatefulWidget {
final String? filename;
final String? fileExtension;
const DocumentUploadPreparationPage({
Key? key,
required this.fileBytes,
@@ -193,6 +193,10 @@ class _DocumentUploadPreparationPageState extends State<DocumentUploadPreparatio
options: state.correspondents,
prefixIcon: const Icon(Icons.person_outline),
allowSelectUnassigned: true,
canCreateNewLabel: LocalUserAccount.current.paperlessUser.hasPermission(
PermissionAction.add,
PermissionTarget.correspondent,
),
),
// Document type
LabelFormField<DocumentType>(
@@ -208,6 +212,10 @@ class _DocumentUploadPreparationPageState extends State<DocumentUploadPreparatio
options: state.documentTypes,
prefixIcon: const Icon(Icons.description_outlined),
allowSelectUnassigned: true,
canCreateNewLabel: LocalUserAccount.current.paperlessUser.hasPermission(
PermissionAction.add,
PermissionTarget.documentType,
),
),
TagsFormField(
name: DocumentModel.tagsKey,
@@ -239,10 +247,14 @@ class _DocumentUploadPreparationPageState extends State<DocumentUploadPreparatio
final createdAt = fv[DocumentModel.createdKey] as DateTime?;
final title = fv[DocumentModel.titleKey] as String;
final docType = fv[DocumentModel.documentTypeKey] as SetIdQueryParameter;
final tags = fv[DocumentModel.tagsKey] as IdsTagsQuery;
final correspondent = fv[DocumentModel.correspondentKey] as SetIdQueryParameter;
final docType = (fv[DocumentModel.documentTypeKey] as IdQueryParameter?)
?.whenOrNull(fromId: (id) => id);
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(
widget.fileBytes,
filename: _padWithExtension(
@@ -250,10 +262,11 @@ class _DocumentUploadPreparationPageState extends State<DocumentUploadPreparatio
widget.fileExtension,
),
title: title,
documentType: docType.id,
correspondent: correspondent.id,
tags: tags.include,
documentType: docType,
correspondent: correspondent,
tags: tags,
createdAt: createdAt,
asn: asn,
);
showSnackBar(
context,

View File

@@ -1,6 +1,5 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
class DocumentView extends StatefulWidget {
final Future<Uint8List> documentBytes;

View File

@@ -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/delegate/customizable_sliver_persistent_header_delegate.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/extensions/flutter_extensions.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/sort_documents_button.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/tasks/cubit/task_status_cubit.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
import 'package:paperless_mobile/helpers/message_helpers.dart';
import 'package:paperless_mobile/routes/document_details_route.dart';
class DocumentFilterIntent {
final DocumentFilter? filter;
@@ -370,21 +367,7 @@ class _DocumentsPageState extends State<DocumentsPage> with SingleTickerProvider
}
void _onCreateSavedView(DocumentFilter filter) async {
final newView = await Navigator.of(context).push<SavedView?>(
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,
);
},
),
),
);
final newView = await pushAddSavedViewRoute(context, filter: filter);
if (newView != null) {
try {
await context.read<SavedViewCubit>().add(newView);

View File

@@ -1,5 +1,4 @@
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/items/document_item.dart';
import 'package:paperless_mobile/features/labels/correspondent/view/widgets/correspondent_widget.dart';

View File

@@ -61,8 +61,8 @@ class DocumentsListLoadingWidget extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
TagsPlaceholder(count: 2, dense: true),
SizedBox(height: 2),
const TagsPlaceholder(count: 2, dense: true),
const SizedBox(height: 2),
TextPlaceholder(
length: 250,
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize!,

View File

@@ -1,10 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_form_builder/flutter_form_builder.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/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/view/widgets/label_form_field.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
@@ -156,6 +154,10 @@ class _DocumentFilterFormState extends State<DocumentFilterForm> {
initialValue: widget.initialFilter.documentType,
prefixIcon: const Icon(Icons.description_outlined),
allowSelectUnassigned: false,
canCreateNewLabel: LocalUserAccount.current.paperlessUser.hasPermission(
PermissionAction.add,
PermissionTarget.documentType,
),
);
}
@@ -167,6 +169,10 @@ class _DocumentFilterFormState extends State<DocumentFilterForm> {
initialValue: widget.initialFilter.correspondent,
prefixIcon: const Icon(Icons.person_outline),
allowSelectUnassigned: false,
canCreateNewLabel: LocalUserAccount.current.paperlessUser.hasPermission(
PermissionAction.add,
PermissionTarget.correspondent,
),
);
}
@@ -178,6 +184,10 @@ class _DocumentFilterFormState extends State<DocumentFilterForm> {
initialValue: widget.initialFilter.storagePath,
prefixIcon: const Icon(Icons.folder_outlined),
allowSelectUnassigned: false,
canCreateNewLabel: LocalUserAccount.current.paperlessUser.hasPermission(
PermissionAction.add,
PermissionTarget.storagePath,
),
);
}

View File

@@ -1,7 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.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/generated/l10n/app_localizations.dart';

View File

@@ -18,6 +18,7 @@ class EditLabelPage<T extends Label> extends StatelessWidget {
final List<Widget> additionalFields;
final Future<T> Function(BuildContext context, T label) onSubmit;
final Future<void> Function(BuildContext context, T label) onDelete;
final bool canDelete;
const EditLabelPage({
super.key,
@@ -26,6 +27,7 @@ class EditLabelPage<T extends Label> extends StatelessWidget {
this.additionalFields = const [],
required this.onSubmit,
required this.onDelete,
required this.canDelete,
});
@override
@@ -40,6 +42,7 @@ class EditLabelPage<T extends Label> extends StatelessWidget {
fromJsonT: fromJsonT,
onSubmit: onSubmit,
onDelete: onDelete,
canDelete: canDelete,
),
);
}
@@ -51,6 +54,7 @@ class EditLabelForm<T extends Label> extends StatelessWidget {
final List<Widget> additionalFields;
final Future<T> Function(BuildContext context, T label) onSubmit;
final Future<void> Function(BuildContext context, T label) onDelete;
final bool canDelete;
const EditLabelForm({
super.key,
@@ -59,6 +63,7 @@ class EditLabelForm<T extends Label> extends StatelessWidget {
required this.additionalFields,
required this.onSubmit,
required this.onDelete,
required this.canDelete,
});
@override
@@ -68,7 +73,7 @@ class EditLabelForm<T extends Label> extends StatelessWidget {
title: Text(S.of(context)!.edit),
actions: [
IconButton(
onPressed: () => _onDelete(context),
onPressed: canDelete ? () => _onDelete(context) : null,
icon: const Icon(Icons.delete),
),
],

View File

@@ -1,6 +1,7 @@
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/features/edit_label/cubit/edit_label_cubit.dart';
import 'package:paperless_mobile/features/edit_label/view/edit_label_page.dart';
@@ -19,10 +20,12 @@ class EditCorrespondentPage extends StatelessWidget {
return EditLabelPage<Correspondent>(
label: correspondent,
fromJsonT: Correspondent.fromJson,
onSubmit: (context, label) =>
context.read<EditLabelCubit>().replaceCorrespondent(label),
onDelete: (context, label) =>
context.read<EditLabelCubit>().removeCorrespondent(label),
onSubmit: (context, label) => context.read<EditLabelCubit>().replaceCorrespondent(label),
onDelete: (context, label) => context.read<EditLabelCubit>().removeCorrespondent(label),
canDelete: LocalUserAccount.current.paperlessUser.hasPermission(
PermissionAction.delete,
PermissionTarget.correspondent,
),
);
}),
);

View File

@@ -1,7 +1,7 @@
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.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/view/edit_label_page.dart';
@@ -18,10 +18,12 @@ class EditDocumentTypePage extends StatelessWidget {
child: EditLabelPage<DocumentType>(
label: documentType,
fromJsonT: DocumentType.fromJson,
onSubmit: (context, label) =>
context.read<EditLabelCubit>().replaceDocumentType(label),
onDelete: (context, label) =>
context.read<EditLabelCubit>().removeDocumentType(label),
onSubmit: (context, label) => context.read<EditLabelCubit>().replaceDocumentType(label),
onDelete: (context, label) => context.read<EditLabelCubit>().removeDocumentType(label),
canDelete: LocalUserAccount.current.paperlessUser.hasPermission(
PermissionAction.delete,
PermissionTarget.documentType,
),
),
);
}

View File

@@ -1,6 +1,7 @@
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/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/labels/storage_path/view/widgets/storage_path_autofill_form_builder_field.dart';
@@ -18,10 +19,12 @@ class EditStoragePathPage extends StatelessWidget {
child: EditLabelPage<StoragePath>(
label: storagePath,
fromJsonT: StoragePath.fromJson,
onSubmit: (context, label) =>
context.read<EditLabelCubit>().replaceStoragePath(label),
onDelete: (context, label) =>
context.read<EditLabelCubit>().removeStoragePath(label),
onSubmit: (context, label) => context.read<EditLabelCubit>().replaceStoragePath(label),
onDelete: (context, label) => context.read<EditLabelCubit>().removeStoragePath(label),
canDelete: LocalUserAccount.current.paperlessUser.hasPermission(
PermissionAction.delete,
PermissionTarget.storagePath,
),
additionalFields: [
StoragePathAutofillFormBuilderField(
name: StoragePath.pathKey,

View File

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_form_builder/flutter_form_builder.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/features/edit_label/cubit/edit_label_cubit.dart';
import 'package:paperless_mobile/features/edit_label/view/edit_label_page.dart';
@@ -21,10 +22,12 @@ class EditTagPage extends StatelessWidget {
child: EditLabelPage<Tag>(
label: tag,
fromJsonT: Tag.fromJson,
onSubmit: (context, label) =>
context.read<EditLabelCubit>().replaceTag(label),
onDelete: (context, label) =>
context.read<EditLabelCubit>().removeTag(label),
onSubmit: (context, label) => context.read<EditLabelCubit>().replaceTag(label),
onDelete: (context, label) => context.read<EditLabelCubit>().removeTag(label),
canDelete: LocalUserAccount.current.paperlessUser.hasPermission(
PermissionAction.delete,
PermissionTarget.tag,
),
additionalFields: [
FormBuilderColorPickerField(
initialValue: tag.color,

View File

@@ -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/helpers/message_helpers.dart';
import 'package:paperless_mobile/constants.dart';
class SubmitButtonConfig<T extends Label> {
final Widget icon;

View File

@@ -6,38 +6,26 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:hive/hive.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/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_account.dart';
import 'package:paperless_mobile/core/global/constants.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/user_repository.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/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_upload/cubit/document_upload_cubit.dart';
import 'package:paperless_mobile/features/document_upload/view/document_upload_preparation_page.dart';
import 'package:paperless_mobile/features/documents/cubit/documents_cubit.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/inbox/cubit/inbox_cubit.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/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/saved_view/cubit/saved_view_cubit.dart';
import 'package:paperless_mobile/features/sharing/share_intent_queue.dart';
import 'package:paperless_mobile/features/tasks/cubit/task_status_cubit.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
import 'package:paperless_mobile/helpers/message_helpers.dart';
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
import 'package:responsive_builder/responsive_builder.dart';
@@ -196,7 +184,8 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
),
label: S.of(context)!.documents,
),
if (LocalUserAccount.current.paperlessUser.hasPermission(UserPermissions.addDocument))
if (LocalUserAccount.current.paperlessUser
.hasPermission(PermissionAction.add, PermissionTarget.document))
RouteDescription(
icon: const Icon(Icons.document_scanner_outlined),
selectedIcon: Icon(
@@ -222,32 +211,31 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
label: S.of(context)!.inbox,
badgeBuilder: (icon) => BlocBuilder<InboxCubit, InboxState>(
builder: (context, state) {
if (state.itemsInInboxCount > 0) {
return Badge.count(
count: state.itemsInInboxCount,
child: icon,
);
}
return icon;
return Badge.count(
isLabelVisible: state.itemsInInboxCount > 0,
count: state.itemsInInboxCount,
child: icon,
);
},
),
),
];
final routes = <Widget>[
const DocumentsPage(),
if (LocalUserAccount.current.paperlessUser.hasPermission(UserPermissions.addDocument))
if (LocalUserAccount.current.paperlessUser
.hasPermission(PermissionAction.add, PermissionTarget.document))
const ScannerPage(),
const LabelsPage(),
const InboxPage(),
];
return MultiBlocListener(
listeners: [
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,
listener: (context, state) {
_initializeData(context);
context.read<LabelRepository>().initialize();
context.read<SavedViewRepository>().initialize();
},
),
BlocListener<TaskStatusCubit, TaskStatusState>(
@@ -299,14 +287,4 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
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;
});
}
}

View File

@@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:hive_flutter/adapters.dart';
import 'package:paperless_api/paperless_api.dart';
@@ -45,9 +44,10 @@ class HomeRoute extends StatelessWidget {
return GlobalSettingsBuilder(
builder: (context, settings) {
final currentLocalUserId = settings.currentLoggedInUser!;
final apiVersion = ApiVersion(paperlessApiVersion);
return MultiProvider(
providers: [
Provider.value(value: ApiVersion(paperlessApiVersion)),
Provider.value(value: apiVersion),
Provider<CacheManager>(
create: (context) => CacheManager(
Config(
@@ -87,7 +87,7 @@ class HomeRoute extends StatelessWidget {
apiVersion: paperlessApiVersion,
),
),
if (paperlessApiVersion >= 3)
if (apiVersion.hasMultiUserSupport)
ProxyProvider<SessionManager, PaperlessUserApiV3>(
update: (context, value, previous) => PaperlessUserApiV3Impl(
value.client,
@@ -98,7 +98,7 @@ class HomeRoute extends StatelessWidget {
return MultiProvider(
providers: [
ProxyProvider<PaperlessLabelsApi, LabelRepository>(
update: (context, value, previous) => LabelRepository(value)..initialize(),
update: (context, value, previous) => LabelRepository(value),
),
ProxyProvider<PaperlessSavedViewsApi, SavedViewRepository>(
update: (context, value, previous) => SavedViewRepository(value)..initialize(),

View File

@@ -3,6 +3,5 @@ class ApiVersion {
ApiVersion(this.version);
bool get supportsPermissions => version >= 3;
bool get hasMultiUserSupport => version >= 3;
}

View File

@@ -1,11 +1,9 @@
import 'package:flutter/material.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/saved_view_repository.dart';
import 'package:paperless_mobile/extensions/flutter_extensions.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/generated/l10n/app_localizations.dart';

View File

@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:intl/intl.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_confirm_button.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/features/app_drawer/view/app_drawer.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/view/widgets/inbox_empty_widget.dart';
import 'package:paperless_mobile/features/inbox/view/widgets/inbox_item.dart';
@@ -26,10 +26,8 @@ class InboxPage extends StatefulWidget {
State<InboxPage> createState() => _InboxPageState();
}
class _InboxPageState extends State<InboxPage>
with DocumentPagingViewMixin<InboxPage, InboxCubit> {
final SliverOverlapAbsorberHandle searchBarHandle =
SliverOverlapAbsorberHandle();
class _InboxPageState extends State<InboxPage> with DocumentPagingViewMixin<InboxPage, InboxCubit> {
final SliverOverlapAbsorberHandle searchBarHandle = SliverOverlapAbsorberHandle();
@override
final pagingScrollController = ScrollController();
@@ -80,8 +78,7 @@ class _InboxPageState extends State<InboxPage>
} else if (state.documents.isEmpty) {
return Center(
child: InboxEmptyWidget(
emptyStateRefreshIndicatorKey:
_emptyStateRefreshIndicatorKey,
emptyStateRefreshIndicatorKey: _emptyStateRefreshIndicatorKey,
),
);
} else {
@@ -92,8 +89,7 @@ class _InboxPageState extends State<InboxPage>
SliverToBoxAdapter(
child: HintCard(
show: !state.isHintAcknowledged,
hintText:
S.of(context)!.swipeLeftToMarkADocumentAsSeen,
hintText: S.of(context)!.swipeLeftToMarkADocumentAsSeen,
onHintAcknowledged: () =>
context.read<InboxCubit>().acknowledgeHint(),
),
@@ -108,13 +104,10 @@ class _InboxPageState extends State<InboxPage>
child: Align(
alignment: Alignment.centerLeft,
child: ClipRRect(
borderRadius:
BorderRadius.circular(32.0),
borderRadius: BorderRadius.circular(32.0),
child: Text(
entry.key,
style: Theme.of(context)
.textTheme
.bodySmall,
style: Theme.of(context).textTheme.bodySmall,
textAlign: TextAlign.center,
).padded(),
),
@@ -182,7 +175,7 @@ class _InboxPageState extends State<InboxPage>
],
).padded(),
confirmDismiss: (_) => _onItemDismissed(doc),
key: UniqueKey(),
key: ValueKey(doc.id),
child: InboxItem(document: doc),
);
}
@@ -227,14 +220,15 @@ class _InboxPageState extends State<InboxPage>
return true;
} on PaperlessServerException catch (error, stackTrace) {
showErrorMessage(context, error, stackTrace);
return false;
} on ServerMessageException catch (error) {
showGenericError(context, error.message);
} catch (error) {
showErrorMessage(
context,
const PaperlessServerException.unknown(),
);
return false;
}
return false;
}
Future<void> _onUndoMarkAsSeen(
@@ -242,9 +236,7 @@ class _InboxPageState extends State<InboxPage>
Iterable<int> removedTags,
) async {
try {
await context
.read<InboxCubit>()
.undoRemoveFromInbox(document, removedTags);
await context.read<InboxCubit>().undoRemoveFromInbox(document, removedTags);
} on PaperlessServerException catch (error, stackTrace) {
showErrorMessage(context, error, stackTrace);
}

View File

@@ -2,6 +2,7 @@ import 'package:collection/collection.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/navigation/push_routes.dart';
import 'package:paperless_mobile/core/workarounds/colored_chip.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/generated/l10n/app_localizations.dart';
import 'package:paperless_mobile/routes/document_details_route.dart';
class InboxItem extends StatefulWidget {
static const a4AspectRatio = 1 / 1.4142;
@@ -108,8 +107,8 @@ class _InboxItemState extends State<InboxItem> {
],
),
),
SizedBox(
height: 56,
LimitedBox(
maxHeight: 56,
child: _buildActions(context),
),
],
@@ -121,58 +120,46 @@ class _InboxItemState extends State<InboxItem> {
}
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(
borderRadius: BorderRadius.circular(32),
);
final actions = [
_buildAssignAsnAction(chipShape, context),
const SizedBox(width: 8.0),
ColoredChipWrapper(
child: ActionChip(
avatar: const Icon(Icons.delete_outline),
shape: chipShape,
label: Text(S.of(context)!.deleteDocument),
onPressed: () async {
final shouldDelete = await showDialog<bool>(
context: context,
builder: (context) => DeleteDocumentConfirmationDialog(document: widget.document),
) ??
false;
if (shouldDelete) {
context.read<InboxCubit>().delete(widget.document);
}
},
if (canEdit) _buildAssignAsnAction(chipShape, context),
if (canEdit && canDelete) const SizedBox(width: 8.0),
if (canDelete)
ColoredChipWrapper(
child: ActionChip(
avatar: const Icon(Icons.delete_outline),
shape: chipShape,
label: Text(S.of(context)!.deleteDocument),
onPressed: () async {
final shouldDelete = await showDialog<bool>(
context: context,
builder: (context) =>
DeleteDocumentConfirmationDialog(document: widget.document),
) ??
false;
if (shouldDelete) {
context.read<InboxCubit>().delete(widget.document);
}
},
),
),
),
];
// return FutureBuilder<FieldSuggestions>(
// 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,
// ),
// ];
// }
// }
if (actions.isEmpty) {
return const SizedBox.shrink();
}
return Row(
children: [
Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.bolt_outlined),
const Icon(Icons.auto_awesome),
ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 50,
@@ -229,6 +216,7 @@ class _InboxItemState extends State<InboxItem> {
setState(() {
_isAsnAssignLoading = true;
});
context.read<InboxCubit>().assignAsn(widget.document).whenComplete(
() => setState(() => _isAsnAssignLoading = false),
);

View File

@@ -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/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/inbox/view/widgets/inbox_item.dart';
class InboxListLoadingWidget extends StatelessWidget {
const InboxListLoadingWidget({super.key});
@@ -48,10 +47,10 @@ class InboxListLoadingWidget extends StatelessWidget {
),
),
const SizedBox(width: 8),
Flexible(
const Flexible(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
children: [
Spacer(),
TextPlaceholder(length: 200, fontSize: 14),
Spacer(),

View File

@@ -1,4 +1,3 @@
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/repository/label_repository.dart';

View File

@@ -1,10 +1,9 @@
import 'dart:developer';
import 'package:animations/animations.dart';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.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/features/labels/tags/view/widgets/fullscreen_tags_form.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
@@ -72,7 +71,11 @@ class TagsFormField extends StatelessWidget {
onSubmit: closeForm,
initialValue: field.value,
allowOnlySelection: allowOnlySelection,
allowCreation: allowCreation,
allowCreation: allowCreation &&
LocalUserAccount.current.paperlessUser.hasPermission(
PermissionAction.add,
PermissionTarget.tag,
),
allowExclude: allowExclude,
),
onClosed: (data) {

View File

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:paperless_api/paperless_api.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/repository/label_repository.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_tag_page.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/generated/l10n/app_localizations.dart';
@@ -148,6 +148,10 @@ class _LabelsPageState extends State<LabelsPage> with SingleTickerProviderStateM
correspondent: IdQueryParameter.fromId(label.id!),
pageSize: label.documentCount ?? 0,
),
canEdit: LocalUserAccount.current.paperlessUser.hasPermission(
PermissionAction.change, PermissionTarget.correspondent),
canAddNew: LocalUserAccount.current.paperlessUser.hasPermission(
PermissionAction.add, PermissionTarget.correspondent),
onEdit: _openEditCorrespondentPage,
emptyStateActionButtonLabel: S.of(context)!.addNewCorrespondent,
emptyStateDescription: S.of(context)!.noCorrespondentsSetUp,
@@ -169,6 +173,10 @@ class _LabelsPageState extends State<LabelsPage> with SingleTickerProviderStateM
documentType: IdQueryParameter.fromId(label.id!),
pageSize: label.documentCount ?? 0,
),
canEdit: LocalUserAccount.current.paperlessUser.hasPermission(
PermissionAction.change, PermissionTarget.documentType),
canAddNew: LocalUserAccount.current.paperlessUser.hasPermission(
PermissionAction.add, PermissionTarget.documentType),
onEdit: _openEditDocumentTypePage,
emptyStateActionButtonLabel: S.of(context)!.addNewDocumentType,
emptyStateDescription: S.of(context)!.noDocumentTypesSetUp,
@@ -190,6 +198,10 @@ class _LabelsPageState extends State<LabelsPage> with SingleTickerProviderStateM
tags: TagsQuery.ids(include: [label.id!]),
pageSize: label.documentCount ?? 0,
),
canEdit: LocalUserAccount.current.paperlessUser
.hasPermission(PermissionAction.change, PermissionTarget.tag),
canAddNew: LocalUserAccount.current.paperlessUser
.hasPermission(PermissionAction.add, PermissionTarget.tag),
onEdit: _openEditTagPage,
leadingBuilder: (t) => CircleAvatar(
backgroundColor: t.color,
@@ -221,6 +233,10 @@ class _LabelsPageState extends State<LabelsPage> with SingleTickerProviderStateM
storagePath: IdQueryParameter.fromId(label.id!),
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),
emptyStateActionButtonLabel: S.of(context)!.addNewStoragePath,
emptyStateDescription: S.of(context)!.noStoragePathsSetUp,

View File

@@ -16,6 +16,7 @@ class FullscreenLabelForm<T extends Label> extends StatefulWidget {
final String? addNewLabelText;
final bool autofocus;
final bool allowSelectUnassigned;
final bool canCreateNewLabel;
FullscreenLabelForm({
super.key,
@@ -29,6 +30,7 @@ class FullscreenLabelForm<T extends Label> extends StatefulWidget {
this.addNewLabelText,
this.autofocus = true,
this.allowSelectUnassigned = true,
required this.canCreateNewLabel,
}) : assert(
!(initialValue?.isOnlyAssigned() ?? false) || showAnyAssignedOption,
),

View File

@@ -1,5 +1,3 @@
import 'dart:developer';
import 'package:animations/animations.dart';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
@@ -28,6 +26,7 @@ class LabelFormField<T extends Label> extends StatelessWidget {
final List<T> suggestions;
final String? addLabelText;
final bool allowSelectUnassigned;
final bool canCreateNewLabel;
const LabelFormField({
Key? key,
@@ -44,6 +43,7 @@ class LabelFormField<T extends Label> extends StatelessWidget {
this.suggestions = const [],
this.addLabelText,
required this.allowSelectUnassigned,
required this.canCreateNewLabel,
}) : super(key: key);
String _buildText(BuildContext context, IdQueryParameter? value) {
@@ -103,6 +103,7 @@ class LabelFormField<T extends Label> extends StatelessWidget {
),
openBuilder: (context, closeForm) => FullscreenLabelForm<T>(
allowSelectUnassigned: allowSelectUnassigned,
canCreateNewLabel: canCreateNewLabel,
addNewLabelText: addLabelText,
leadingIcon: prefixIcon,
onCreateNewLabel: addLabelPageBuilder != null

View File

@@ -1,19 +1,14 @@
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_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/global_settings.dart';
import 'package:paperless_mobile/core/navigation/push_routes.dart';
import 'package:paperless_mobile/helpers/format_helpers.dart';
class LabelItem<T extends Label> extends StatelessWidget {
final T label;
final String name;
final Widget content;
final void Function(T) onOpenEditPage;
final void Function(T)? onOpenEditPage;
final DocumentFilter Function(T) filterBuilder;
final Widget? leading;
@@ -33,38 +28,25 @@ class LabelItem<T extends Label> extends StatelessWidget {
title: Text(name),
subtitle: content,
leading: leading,
onTap: () => onOpenEditPage(label),
onTap: onOpenEditPage != null ? () => onOpenEditPage!(label) : null,
trailing: _buildReferencedDocumentsWidget(context),
isThreeLine: true,
);
}
Widget _buildReferencedDocumentsWidget(BuildContext context) {
final canOpen = (label.documentCount ?? 0) > 0 &&
LocalUserAccount.current.paperlessUser
.hasPermission(PermissionAction.view, PermissionTarget.document);
return TextButton.icon(
label: const Icon(Icons.link),
icon: Text(formatMaxCount(label.documentCount)),
onPressed: (label.documentCount ?? 0) == 0
? null
: () {
final currentUser = Hive.box<GlobalSettings>(HiveBoxes.globalSettings)
.getValue()!
.currentLoggedInUser!;
onPressed: canOpen
? () {
final filter = filterBuilder(label);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => BlocProvider(
create: (context) => LinkedDocumentsCubit(
filter,
context.read(),
context.read(),
context.read(),
),
child: const LinkedDocumentsPage(),
),
),
);
},
pushLinkedDocumentsView(context, filter: filter);
}
: null,
);
}
}

View File

@@ -11,7 +11,9 @@ class LabelTabView<T extends Label> extends StatelessWidget {
final Map<int, T> labels;
final DocumentFilter Function(Label) filterBuilder;
final void Function(T) onEdit;
final bool canEdit;
final void Function() onAddNew;
final bool canAddNew;
/// Displayed as the subtitle of the [ListTile]
final Widget Function(T)? contentBuilder;
@@ -33,6 +35,8 @@ class LabelTabView<T extends Label> extends StatelessWidget {
required this.onAddNew,
required this.emptyStateActionButtonLabel,
required this.labels,
required this.canEdit,
required this.canAddNew,
});
@override
@@ -54,7 +58,7 @@ class LabelTabView<T extends Label> extends StatelessWidget {
textAlign: TextAlign.center,
),
TextButton(
onPressed: onAddNew,
onPressed: canAddNew ? onAddNew : null,
child: Text(emptyStateActionButtonLabel),
),
].padded(),
@@ -70,14 +74,11 @@ class LabelTabView<T extends Label> extends StatelessWidget {
name: l.name,
content: contentBuilder?.call(l) ??
Text(
translateMatchingAlgorithmName(
context, l.matchingAlgorithm) +
((l.match?.isNotEmpty ?? false)
? ": ${l.match}"
: ""),
translateMatchingAlgorithmName(context, l.matchingAlgorithm) +
((l.match?.isNotEmpty ?? false) ? ": ${l.match}" : ""),
maxLines: 2,
),
onOpenEditPage: onEdit,
onOpenEditPage: canEdit ? onEdit : null,
filterBuilder: filterBuilder,
leading: leadingBuilder?.call(l),
label: l,

View File

@@ -20,6 +20,5 @@ class LabelText<T extends Label> extends StatelessWidget {
maxLines: 1,
overflow: TextOverflow.ellipsis,
);
;
}
}

View File

@@ -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/generated/l10n/app_localizations.dart';
import 'package:paperless_mobile/routes/document_details_route.dart';
class LinkedDocumentsPage extends StatefulWidget {
const LinkedDocumentsPage({super.key});

View File

@@ -179,6 +179,7 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
return;
}
}
final userCredentialsBox = await _getUserCredentialsBox();
final authentication = userCredentialsBox.get(globalSettings.currentLoggedInUser!);
await userCredentialsBox.close();

View File

@@ -1,5 +1,4 @@
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/features/login/model/client_certificate.dart';

View File

@@ -1,4 +1,3 @@
import 'dart:convert';
import 'dart:typed_data';
import 'package:hive_flutter/adapters.dart';

View File

@@ -1,4 +1,3 @@
import 'dart:convert';
import 'dart:typed_data';
class ClientCertificateFormModel {

View File

@@ -4,12 +4,9 @@ import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.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/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';

View File

@@ -1,4 +1,3 @@
import 'dart:developer';
import 'package:flutter/material.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!)) {
return S.of(context)!.serverAddressMustIncludeAScheme;
}
return null;
},
decoration: InputDecoration(
hintText: "http://192.168.1.50:8000",

View File

@@ -39,6 +39,7 @@ class _UserCredentialsFormFieldState extends State<UserCredentialsFormField> {
if (value?.trim().isEmpty ?? true) {
return S.of(context)!.usernameMustNotBeEmpty;
}
return null;
},
autofillHints: const [AutofillHints.username],
decoration: InputDecoration(
@@ -56,6 +57,7 @@ class _UserCredentialsFormFieldState extends State<UserCredentialsFormField> {
if (value?.trim().isEmpty ?? true) {
return S.of(context)!.passwordMustNotBeEmpty;
}
return null;
},
),
].map((child) => child.padded()).toList(),

View File

@@ -1,7 +1,6 @@
import 'package:flutter/material.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/widgets/paperless_logo.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';

View File

@@ -1,7 +1,6 @@
import 'dart:convert';
import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:open_filex/open_filex.dart';

View File

@@ -1,5 +1,4 @@
import 'package:equatable/equatable.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:paperless_api/paperless_api.dart';
///

View File

@@ -3,7 +3,6 @@ import 'dart:async';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.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';
part 'saved_view_state.dart';

View File

@@ -1,15 +1,9 @@
import 'package:flutter/material.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/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/widgets/hint_card.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';
class SavedViewList extends StatelessWidget {
@@ -23,7 +17,7 @@ class SavedViewList extends StatelessWidget {
builder: (context, state) {
return state.when(
initial: () => SliverToBoxAdapter(child: Container()),
loading: () => SliverToBoxAdapter(
loading: () => const SliverToBoxAdapter(
child: Center(
child: Text("Saved views loading..."), //TODO: INTL
),
@@ -55,7 +49,7 @@ class SavedViewList extends StatelessWidget {
),
);
},
error: () => Center(
error: () => const Center(
child: Text(
"An error occurred while trying to load the saved views.",
),

View File

@@ -1,5 +1,4 @@
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:json_annotation/json_annotation.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/notifier/document_changed_notifier.dart';

View File

@@ -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/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/routes/document_details_route.dart';
class SavedViewDetailsPage extends StatefulWidget {
final Future<void> Function(SavedView savedView) onDelete;

View File

@@ -1,6 +1,5 @@
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hive_flutter/adapters.dart';
import 'package:paperless_mobile/core/config/hive/hive_config.dart';
import 'package:paperless_mobile/core/database/tables/global_settings.dart';
@@ -49,7 +48,6 @@ class ManageAccountsPage extends StatelessWidget {
children: [
_buildAccountTile(context, globalSettings.currentLoggedInUser!,
box.get(globalSettings.currentLoggedInUser!)!, globalSettings),
// if (otherAccounts.isNotEmpty) Text("Other accounts"),
Column(
children: [
for (int index = 0; index < otherAccounts.length; index++)
@@ -69,17 +67,11 @@ class ManageAccountsPage extends StatelessWidget {
_onAddAccount(context, globalSettings.currentLoggedInUser!);
},
),
Consumer<ApiVersion>(
builder: (context, value, child) {
if (value.version >= 3) {
return const ListTile(
leading: Icon(Icons.admin_panel_settings),
title: Text("Manage permissions"), //TODO : INTL
);
}
return const SizedBox.shrink();
},
)
if (context.watch<ApiVersion>().hasMultiUserSupport)
const ListTile(
leading: Icon(Icons.admin_panel_settings),
title: Text("Manage permissions"), //TODO: INTL
),
],
);
},
@@ -106,9 +98,7 @@ class ManageAccountsPage extends StatelessWidget {
if (account.paperlessUser.fullName != null) Text(account.paperlessUser.fullName!),
Text(
account.serverUrl.replaceFirst(RegExp(r'https://?'), ''),
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
),
style: TextStyle(color: theme.colorScheme.primary),
),
],
),

View File

@@ -10,10 +10,10 @@ class SecuritySettingsPage extends StatelessWidget {
return Scaffold(
appBar: AppBar(
title: Text(S.of(context)!.security),
actions: [
actions: const [
Padding(
padding: const EdgeInsets.all(16.0),
child: const Icon(Icons.person_outline),
padding: EdgeInsets.all(16.0),
child: Icon(Icons.person_outline),
)
],
),

View File

@@ -13,9 +13,9 @@ class ClearCacheSetting extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListTile(
title: Text("Clear downloaded files"), //TODO: INTL
title: const Text("Clear downloaded files"), //TODO: INTL
subtitle:
Text("Deletes all files downloaded from this app."), //TODO: INTL
const Text("Deletes all files downloaded from this app."), //TODO: INTL
onTap: () async {
final dir = await FileService.downloadsDirectory;
final deletedSize = _dirSize(dir);
@@ -36,8 +36,8 @@ class ClearDownloadsSetting extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListTile(
title: Text("Clear downloads"), //TODO: INTL
subtitle: Text(
title: const Text("Clear downloads"), //TODO: INTL
subtitle: const Text(
"Remove downloaded files, scans and clear the cache's content"), //TODO: INTL
onTap: () {
FileService.documentsDirectory;

View File

@@ -1,13 +1,9 @@
import 'dart:io';
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/core/config/hive/hive_config.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/database/tables/global_settings.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/radio_settings_dialog.dart';
@@ -69,7 +65,7 @@ class ColorSchemeOptionSetting extends StatelessWidget {
bool _isBelowAndroid12() {
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 false;

View File

@@ -1,6 +1,4 @@
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:paperless_mobile/core/config/hive/hive_config.dart';
import 'package:paperless_mobile/core/database/tables/global_settings.dart';

View File

@@ -1,7 +1,6 @@
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_confirm_button.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
class RadioSettingsDialog<T> extends StatefulWidget {
final List<RadioOption<T>> options;

View File

@@ -1,5 +1,4 @@
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/radio_settings_dialog.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';

View File

@@ -1,8 +1,5 @@
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/repository/user_repository.dart';
class UserAvatar extends StatelessWidget {
final String userId;

View File

@@ -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/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_settings.dart';
class UserAccountBuilder extends StatelessWidget {
final Widget Function(

View File

@@ -1,5 +1,4 @@
import 'dart:collection';
import 'dart:developer';
import 'package:flutter/widgets.dart';
import 'package:receive_sharing_intent/receive_sharing_intent.dart';

View File

@@ -2,9 +2,7 @@ import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/service/github_issue_service.dart';
import 'package:paperless_mobile/core/translation/error_code_localization_mapper.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
class SnackBarActionConfig {
final String label;
@@ -67,13 +65,13 @@ void showGenericError(
showSnackBar(
context,
error.toString(),
action: SnackBarActionConfig(
label: S.of(context)!.report,
onPressed: () => GithubIssueService.createIssueFromError(
context,
stackTrace: stackTrace,
),
),
// action: SnackBarActionConfig(
// label: S.of(context)!.report,
// onPressed: () => GithubIssueService.createIssueFromError(
// context,
// stackTrace: stackTrace,
// ),
// ),
);
log(
"An error has occurred.",

View File

@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:io';
import 'package:connectivity_plus/connectivity_plus.dart';
@@ -56,10 +57,8 @@ String get defaultPreferredLocaleSubtag {
Future<void> _initHive() async {
await Hive.initFlutter();
// //TODO: REMOVE!
// await getApplicationDocumentsDirectory().then((value) => value.delete(recursive: true));
registerHiveAdapters();
// await getApplicationDocumentsDirectory().then((value) => value.deleteSync(recursive: true));
await Hive.openBox<LocalUserAccount>(HiveBoxes.localUserAccount);
await Hive.openBox<LocalUserAppState>(HiveBoxes.localUserAppState);
final globalSettingsBox = await Hive.openBox<GlobalSettings>(HiveBoxes.globalSettings);
@@ -126,9 +125,7 @@ void main() async {
providers: [
ChangeNotifierProvider.value(value: sessionManager),
Provider<LocalAuthenticationService>.value(value: localAuthService),
Provider<ConnectivityStatusService>.value(
value: connectivityStatusService,
),
Provider<ConnectivityStatusService>.value(value: connectivityStatusService),
Provider<LocalNotificationService>.value(value: localNotificationService),
Provider.value(value: DocumentChangedNotifier()),
],
@@ -136,12 +133,7 @@ void main() async {
providers: [
BlocProvider<ConnectivityCubit>.value(value: connectivityCubit),
BlocProvider(
create: (context) => AuthenticationCubit(
localAuthService,
apiFactory,
sessionManager,
),
child: Container(),
create: (context) => AuthenticationCubit(localAuthService, apiFactory, sessionManager),
)
],
child: PaperlessMobileEntrypoint(
@@ -215,6 +207,7 @@ class AuthenticationWrapper extends StatefulWidget {
}
class _AuthenticationWrapperState extends State<AuthenticationWrapper> {
late final StreamSubscription _shareMediaSubscription;
@override
void didChangeDependencies() {
super.didChangeDependencies();
@@ -223,6 +216,12 @@ class _AuthenticationWrapperState extends State<AuthenticationWrapper> {
});
}
@override
void dispose() {
_shareMediaSubscription.cancel();
super.dispose();
}
@override
void initState() {
super.initState();
@@ -233,7 +232,8 @@ class _AuthenticationWrapperState extends State<AuthenticationWrapper> {
}
initializeDateFormatting();
// For sharing files coming from outside the app while the app is still opened
ReceiveSharingIntent.getMediaStream().listen(ShareIntentQueue.instance.addAll);
_shareMediaSubscription =
ReceiveSharingIntent.getMediaStream().listen(ShareIntentQueue.instance.addAll);
// For sharing files coming from outside the app while the app is closed
ReceiveSharingIntent.getInitialMedia().then(ShareIntentQueue.instance.addAll);
}

View File

@@ -43,7 +43,7 @@ ThemeData buildTheme({
inputDecorationTheme: _defaultInputDecorationTheme,
listTileTheme: _defaultListTileTheme,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
appBarTheme: AppBarTheme(
appBarTheme: const AppBarTheme(
scrolledUnderElevation: 0,
),
chipTheme: ChipThemeData(

View File

@@ -1,6 +1,5 @@
import 'package:hive/hive.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_api/src/models/user_model.dart';
class PaperlessApiHiveTypeIds {
PaperlessApiHiveTypeIds._();
@@ -56,7 +55,6 @@ void registerPaperlessApiHiveTypeAdapters() {
// Users and permissions
Hive.registerAdapter(UserModelV3Adapter());
Hive.registerAdapter(UserModelV2Adapter());
Hive.registerAdapter(UserPermissionsAdapter());
Hive.registerAdapter(InheritedPermissionsAdapter());
Hive.registerAdapter(GroupModelAdapter());
Hive.registerAdapter(PermissionsAdapter());

View File

@@ -1,7 +1,5 @@
import 'package:json_annotation/json_annotation.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
extends JsonConverter<DateRangeQuery, Map<String, dynamic>> {

View File

@@ -1,4 +1,3 @@
import 'dart:developer';
import 'package:json_annotation/json_annotation.dart';

View File

@@ -1,9 +1,10 @@
// ignore_for_file: unused_field
import 'package:equatable/equatable.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:paperless_api/paperless_api.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/models/query_parameters/tags_query/tags_query.dart';
part 'filter_rule_model.g.dart';

View File

@@ -11,7 +11,7 @@ class GroupModel with _$GroupModel {
const factory GroupModel({
@HiveField(0) required int id,
@HiveField(1) required String name,
@HiveField(2) required List<UserPermissions> permissions,
@HiveField(2) required List<String> permissions,
}) = _GroupModel;
factory GroupModel.fromJson(Map<String, dynamic> json) => _$GroupModelFromJson(json);

View File

@@ -25,7 +25,7 @@ mixin _$GroupModel {
@HiveField(1)
String get name => throw _privateConstructorUsedError;
@HiveField(2)
List<UserPermissions> get permissions => throw _privateConstructorUsedError;
List<String> get permissions => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
@@ -42,7 +42,7 @@ abstract class $GroupModelCopyWith<$Res> {
$Res call(
{@HiveField(0) int id,
@HiveField(1) String name,
@HiveField(2) List<UserPermissions> permissions});
@HiveField(2) List<String> permissions});
}
/// @nodoc
@@ -74,7 +74,7 @@ class _$GroupModelCopyWithImpl<$Res, $Val extends GroupModel>
permissions: null == permissions
? _value.permissions
: permissions // ignore: cast_nullable_to_non_nullable
as List<UserPermissions>,
as List<String>,
) as $Val);
}
}
@@ -90,7 +90,7 @@ abstract class _$$_GroupModelCopyWith<$Res>
$Res call(
{@HiveField(0) int id,
@HiveField(1) String name,
@HiveField(2) List<UserPermissions> permissions});
@HiveField(2) List<String> permissions});
}
/// @nodoc
@@ -120,7 +120,7 @@ class __$$_GroupModelCopyWithImpl<$Res>
permissions: null == permissions
? _value._permissions
: permissions // ignore: cast_nullable_to_non_nullable
as List<UserPermissions>,
as List<String>,
));
}
}
@@ -131,7 +131,7 @@ class _$_GroupModel implements _GroupModel {
const _$_GroupModel(
{@HiveField(0) required this.id,
@HiveField(1) required this.name,
@HiveField(2) required final List<UserPermissions> permissions})
@HiveField(2) required final List<String> permissions})
: _permissions = permissions;
factory _$_GroupModel.fromJson(Map<String, dynamic> json) =>
@@ -143,10 +143,10 @@ class _$_GroupModel implements _GroupModel {
@override
@HiveField(1)
final String name;
final List<UserPermissions> _permissions;
final List<String> _permissions;
@override
@HiveField(2)
List<UserPermissions> get permissions {
List<String> get permissions {
if (_permissions is EqualUnmodifiableListView) return _permissions;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_permissions);
@@ -189,10 +189,9 @@ class _$_GroupModel implements _GroupModel {
abstract class _GroupModel implements GroupModel {
const factory _GroupModel(
{@HiveField(0) required final int id,
@HiveField(1) required final String name,
@HiveField(2) required final List<UserPermissions> permissions}) =
_$_GroupModel;
{@HiveField(0) required final int id,
@HiveField(1) required final String name,
@HiveField(2) required final List<String> permissions}) = _$_GroupModel;
factory _GroupModel.fromJson(Map<String, dynamic> json) =
_$_GroupModel.fromJson;
@@ -205,7 +204,7 @@ abstract class _GroupModel implements GroupModel {
String get name;
@override
@HiveField(2)
List<UserPermissions> get permissions;
List<String> get permissions;
@override
@JsonKey(ignore: true)
_$$_GroupModelCopyWith<_$_GroupModel> get copyWith =>

View File

@@ -1,7 +1,5 @@
import 'dart:developer';
import 'dart:ui';
import 'package:flutter/foundation.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:paperless_api/src/converters/hex_color_json_converter.dart';
import 'package:paperless_api/src/models/labels/label_model.dart';
@@ -24,7 +22,7 @@ class Tag extends Label {
final bool isInboxTag;
Tag({
const Tag({
super.id,
required super.name,
super.documentCount,

View File

@@ -53,6 +53,5 @@ enum ErrorCode {
requestTimedOut,
unsupportedFileFormat,
missingClientCertificate,
acknowledgeTasksError,
notAuthorized;
acknowledgeTasksError;
}

View File

@@ -1,117 +1,30 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:hive/hive.dart';
import 'package:paperless_api/config/hive/hive_type_ids.dart';
part 'user_permissions.g.dart';
@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);
enum PermissionAction {
add("add"),
change("change"),
delete("delete"),
view("view");
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);
}

View File

@@ -1,5 +1,4 @@
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 'date_range_query.dart';

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