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

@@ -214,4 +214,4 @@ Made with [contrib.rocks](https://contrib.rocks).
## Troubleshooting
#### Suggestions are not selectable in any of the label form fields
This is a known issue and it has to do with accessibility features of Android. Password managers such as Bitwarden often caused this issue. Luckily, this can be resolved by turning off the accessibility features in these apps. This could also be observed with apps that are allowed to display over other apps, such as emulations of the dynamic island on android.
This is a known issue and it has to do with accessibility features of Android. Password managers such as Bitwarden often caused this issue. Luckily, this can be resolved by turning off the accessibility features in these apps. This could also be observed with apps that are allowed to display over other apps, such as emulations of the dynamic island on android.

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

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