From 4e87a196f9f9ca1f8cda4e1e25160609cfc88cdc Mon Sep 17 00:00:00 2001 From: Anton Stubenbord Date: Sun, 30 Apr 2023 17:42:59 +0200 Subject: [PATCH 01/26] feat: add permission/user/group models, start integration into code --- .../database/tables/local_user_account.dart | 13 +- .../login/cubit/authentication_cubit.dart | 239 +++-- .../login/cubit/authentication_state.dart | 15 +- .../settings/view/manage_accounts_page.dart | 24 +- lib/main.dart | 2 +- .../lib/config/hive/hive_type_ids.dart | 14 + .../lib/src/models/document_model.dart | 10 + .../lib/src/models/group_model.dart | 18 + .../lib/src/models/group_model.freezed.dart | 213 +++++ .../paperless_api/lib/src/models/models.dart | 5 + .../permissions/inherited_permissions.dart | 235 +++++ .../src/models/permissions/permissions.dart | 17 + .../permissions/permissions.freezed.dart | 195 ++++ .../models/permissions/user_permissions.dart | 117 +++ .../lib/src/models/user_model.dart | 72 ++ .../lib/src/models/user_model.freezed.dart | 845 ++++++++++++++++++ .../lib/src/modules/modules.dart | 4 + .../modules/user_api/paperless_user_api.dart | 6 + .../user_api/paperless_user_api_v2_impl.dart | 27 + .../user_api/paperless_user_api_v3.dart | 10 + .../user_api/paperless_user_api_v3_impl.dart | 50 ++ 21 files changed, 1975 insertions(+), 156 deletions(-) create mode 100644 packages/paperless_api/lib/src/models/group_model.dart create mode 100644 packages/paperless_api/lib/src/models/group_model.freezed.dart create mode 100644 packages/paperless_api/lib/src/models/permissions/inherited_permissions.dart create mode 100644 packages/paperless_api/lib/src/models/permissions/permissions.dart create mode 100644 packages/paperless_api/lib/src/models/permissions/permissions.freezed.dart create mode 100644 packages/paperless_api/lib/src/models/permissions/user_permissions.dart create mode 100644 packages/paperless_api/lib/src/models/user_model.dart create mode 100644 packages/paperless_api/lib/src/models/user_model.freezed.dart create mode 100644 packages/paperless_api/lib/src/modules/user_api/paperless_user_api.dart create mode 100644 packages/paperless_api/lib/src/modules/user_api/paperless_user_api_v2_impl.dart create mode 100644 packages/paperless_api/lib/src/modules/user_api/paperless_user_api_v3.dart create mode 100644 packages/paperless_api/lib/src/modules/user_api/paperless_user_api_v3_impl.dart diff --git a/lib/core/database/tables/local_user_account.dart b/lib/core/database/tables/local_user_account.dart index a8bed4e..c2228ce 100644 --- a/lib/core/database/tables/local_user_account.dart +++ b/lib/core/database/tables/local_user_account.dart @@ -1,6 +1,7 @@ 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_settings.dart'; +import 'package:paperless_api/paperless_api.dart'; part 'local_user_account.g.dart'; @@ -9,23 +10,19 @@ class LocalUserAccount extends HiveObject { @HiveField(0) final String serverUrl; - @HiveField(1) - final String username; - - @HiveField(2) - final String? fullName; - @HiveField(3) final String id; @HiveField(4) LocalUserSettings settings; + @HiveField(5) + UserModel paperlessUser; + LocalUserAccount({ required this.id, required this.serverUrl, - required this.username, required this.settings, - this.fullName, + required this.paperlessUser, }); } diff --git a/lib/features/login/cubit/authentication_cubit.dart b/lib/features/login/cubit/authentication_cubit.dart index 1ead7c1..fcd10a6 100644 --- a/lib/features/login/cubit/authentication_cubit.dart +++ b/lib/features/login/cubit/authentication_cubit.dart @@ -8,7 +8,6 @@ import 'package:hydrated_bloc/hydrated_bloc.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/local_user_app_state.dart'; -import 'package:paperless_mobile/core/interceptor/dio_http_error_interceptor.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/security/session_manager.dart'; @@ -29,6 +28,8 @@ class AuthenticationCubit extends Cubit { final LabelRepository _labelRepository; final SavedViewRepository _savedViewRepository; final PaperlessServerStatsApi _serverStatsApi; + final PaperlessUserApi _userApi; + final PaperlessUserApiV3? _userApiV3; AuthenticationCubit( this._localAuthService, @@ -37,7 +38,10 @@ class AuthenticationCubit extends Cubit { this._labelRepository, this._savedViewRepository, this._serverStatsApi, - ) : super(const AuthenticationState()); + this._userApi, { + PaperlessUserApiV3? userApiV3, + }) : _userApiV3 = userApiV3, + super(const AuthenticationState()); Future login({ required LoginFormCredentials credentials, @@ -45,89 +49,49 @@ class AuthenticationCubit extends Cubit { ClientCertificate? clientCertificate, }) async { assert(credentials.username != null && credentials.password != null); + final localUserId = "${credentials.username}@$serverUrl"; - _sessionManager.updateSettings( - baseUrl: serverUrl, - clientCertificate: clientCertificate, - ); - final token = await _authApi.login( - username: credentials.username!, - password: credentials.password!, - ); - _sessionManager.updateSettings( - baseUrl: serverUrl, - clientCertificate: clientCertificate, - authToken: token, + final serverUser = await _addUser( + localUserId, + serverUrl, + credentials, + clientCertificate, + _sessionManager, ); - final userAccountBox = Hive.box(HiveBoxes.localUserAccount); - final userStateBox = Hive.box(HiveBoxes.localUserAppState); - - final userId = "${credentials.username}@$serverUrl"; - - if (userAccountBox.containsKey(userId)) { - throw Exception("User with id $userId already exists!"); - } - - final fullName = await _fetchFullName(); - // Create user account - await userAccountBox.put( - userId, - LocalUserAccount( - id: userId, - settings: LocalUserSettings(), - serverUrl: serverUrl, - username: credentials.username!, - fullName: fullName, - ), - ); - - // Create user state - await userStateBox.put( - userId, - LocalUserAppState(userId: userId), - ); - - // Save credentials in encrypted box - final userCredentialsBox = await _getUserCredentialsBox(); - await userCredentialsBox.put( - userId, - UserCredentials( - token: token, - clientCertificate: clientCertificate, - ), - ); - userCredentialsBox.close(); + final response = await _sessionManager.client.get("/api/"); + final apiVersion = response.headers["x-api-version"] as int; // Mark logged in user as currently active user. final globalSettings = Hive.box(HiveBoxes.globalSettings).getValue()!; - globalSettings.currentLoggedInUser = userId; + globalSettings.currentLoggedInUser = localUserId; await globalSettings.save(); emit( AuthenticationState( isAuthenticated: true, username: credentials.username, - userId: userId, - fullName: fullName, + localUserId: localUserId, + fullName: serverUser.fullName, + apiVersion: apiVersion, ), ); } /// Switches to another account if it exists. - Future switchAccount(String userId) async { + Future switchAccount(String localUserId) async { final globalSettings = Hive.box(HiveBoxes.globalSettings).getValue()!; - if (globalSettings.currentLoggedInUser == userId) { + if (globalSettings.currentLoggedInUser == localUserId) { return; } final userAccountBox = Hive.box(HiveBoxes.localUserAccount); - if (!userAccountBox.containsKey(userId)) { - debugPrint("User $userId not yet registered."); + if (!userAccountBox.containsKey(localUserId)) { + debugPrint("User $localUserId not yet registered."); return; } - final account = userAccountBox.get(userId)!; + final account = userAccountBox.get(localUserId)!; if (account.settings.isBiometricAuthenticationEnabled) { final authenticated = @@ -139,12 +103,12 @@ class AuthenticationCubit extends Cubit { } final credentialsBox = await _getUserCredentialsBox(); - if (!credentialsBox.containsKey(userId)) { + if (!credentialsBox.containsKey(localUserId)) { await credentialsBox.close(); - debugPrint("Invalid authentication for $userId"); + debugPrint("Invalid authentication for $localUserId"); return; } - final credentials = credentialsBox.get(userId); + final credentials = credentialsBox.get(localUserId); await credentialsBox.close(); await _resetExternalState(); @@ -157,15 +121,19 @@ class AuthenticationCubit extends Cubit { ); await _reloadRepositories(); - globalSettings.currentLoggedInUser = userId; + globalSettings.currentLoggedInUser = localUserId; await globalSettings.save(); + final response = await _sessionManager.client.get("/api/"); + final apiVersion = response.headers["x-api-version"] as int; + emit( AuthenticationState( isAuthenticated: true, - username: account.username, - fullName: account.fullName, - userId: userId, + username: account.paperlessUser.username, + fullName: account.paperlessUser.fullName, + localUserId: localUserId, + apiVersion: apiVersion, ), ); } @@ -177,62 +145,16 @@ class AuthenticationCubit extends Cubit { required bool enableBiometricAuthentication, }) async { assert(credentials.password != null && credentials.username != null); - final userId = "${credentials.username}@$serverUrl"; - - final userAccountsBox = Hive.box(HiveBoxes.localUserAccount); - final userStateBox = Hive.box(HiveBoxes.localUserAppState); - - if (userAccountsBox.containsKey(userId)) { - throw Exception("User already exists"); - } - // Creates a parallel session to get token and disposes of resources after. - final sessionManager = SessionManager([ - DioHttpErrorInterceptor(), - ]); - sessionManager.updateSettings( - clientCertificate: clientCertificate, - baseUrl: serverUrl, - ); - final authApi = PaperlessAuthenticationApiImpl(sessionManager.client); - - final token = await authApi.login( - username: credentials.username!, - password: credentials.password!, - ); - sessionManager.resetSettings(); - - final fullName = await _fetchFullName(); - - await userAccountsBox.put( - userId, - LocalUserAccount( - id: userId, - serverUrl: serverUrl, - username: credentials.username!, - settings: LocalUserSettings( - isBiometricAuthenticationEnabled: enableBiometricAuthentication, - ), - fullName: fullName, - ), + final localUserId = "${credentials.username}@$serverUrl"; + await _addUser( + localUserId, + serverUrl, + credentials, + clientCertificate, + _sessionManager, ); - await userStateBox.put( - userId, - LocalUserAppState( - userId: userId, - ), - ); - - final userCredentialsBox = await _getUserCredentialsBox(); - await userCredentialsBox.put( - userId, - UserCredentials( - token: token, - clientCertificate: clientCertificate, - ), - ); - await userCredentialsBox.close(); - return userId; + return localUserId; } Future removeAccount(String userId) async { @@ -287,11 +209,16 @@ class AuthenticationCubit extends Cubit { baseUrl: userAccount.serverUrl, serverInformation: PaperlessServerInformationModel(), ); + final response = await _sessionManager.client.get("/api/"); + final apiVersion = response.headers["x-api-version"] as int; + emit( AuthenticationState( isAuthenticated: true, showBiometricAuthenticationScreen: false, - username: userAccount.username, + username: userAccount.paperlessUser.username, + apiVersion: apiVersion, + fullName: userAccount.paperlessUser.fullName, ), ); } @@ -343,12 +270,68 @@ class AuthenticationCubit extends Cubit { ]); } - Future _fetchFullName() async { - try { - final uiSettings = await _serverStatsApi.getUiSettings(); - return uiSettings.displayName; - } catch (error) { - return null; + Future _addUser( + String localUserId, + String serverUrl, + LoginFormCredentials credentials, + ClientCertificate? clientCert, + SessionManager sessionManager, + ) async { + assert(credentials.username != null && credentials.password != null); + + sessionManager.updateSettings( + baseUrl: serverUrl, + clientCertificate: clientCert, + ); + final authApi = PaperlessAuthenticationApiImpl(sessionManager.client); + + final token = await authApi.login( + username: credentials.username!, + password: credentials.password!, + ); + sessionManager.updateSettings( + baseUrl: serverUrl, + clientCertificate: clientCert, + authToken: token, + ); + + final userAccountBox = Hive.box(HiveBoxes.localUserAccount); + final userStateBox = Hive.box(HiveBoxes.localUserAppState); + + if (userAccountBox.containsKey(localUserId)) { + throw Exception("User with id $localUserId already exists!"); } + + final serverUserId = await _userApi.findCurrentUserId(); + final serverUser = await _userApi.find(serverUserId); + + // Create user account + await userAccountBox.put( + localUserId, + LocalUserAccount( + id: localUserId, + settings: LocalUserSettings(), + serverUrl: serverUrl, + paperlessUser: serverUser, + ), + ); + + // Create user state + await userStateBox.put( + localUserId, + LocalUserAppState(userId: localUserId), + ); + + // Save credentials in encrypted box + final userCredentialsBox = await _getUserCredentialsBox(); + await userCredentialsBox.put( + localUserId, + UserCredentials( + token: token, + clientCertificate: clientCert, + ), + ); + userCredentialsBox.close(); + return serverUser; } } diff --git a/lib/features/login/cubit/authentication_state.dart b/lib/features/login/cubit/authentication_state.dart index cced26f..8b76cd6 100644 --- a/lib/features/login/cubit/authentication_state.dart +++ b/lib/features/login/cubit/authentication_state.dart @@ -5,14 +5,16 @@ class AuthenticationState with EquatableMixin { final bool isAuthenticated; final String? username; final String? fullName; - final String? userId; + final String? localUserId; + final int? apiVersion; const AuthenticationState({ this.isAuthenticated = false, this.showBiometricAuthenticationScreen = false, this.username, this.fullName, - this.userId, + this.localUserId, + this.apiVersion, }); AuthenticationState copyWith({ @@ -20,7 +22,8 @@ class AuthenticationState with EquatableMixin { bool? showBiometricAuthenticationScreen, String? username, String? fullName, - String? userId, + String? localUserId, + int? apiVersion, }) { return AuthenticationState( isAuthenticated: isAuthenticated ?? this.isAuthenticated, @@ -28,16 +31,18 @@ class AuthenticationState with EquatableMixin { showBiometricAuthenticationScreen ?? this.showBiometricAuthenticationScreen, username: username ?? this.username, fullName: fullName ?? this.fullName, - userId: userId ?? this.userId, + localUserId: localUserId ?? this.localUserId, + apiVersion: apiVersion ?? this.apiVersion, ); } @override List get props => [ - userId, + localUserId, username, fullName, isAuthenticated, showBiometricAuthenticationScreen, + apiVersion, ]; } diff --git a/lib/features/settings/view/manage_accounts_page.dart b/lib/features/settings/view/manage_accounts_page.dart index 1337487..9165ed0 100644 --- a/lib/features/settings/view/manage_accounts_page.dart +++ b/lib/features/settings/view/manage_accounts_page.dart @@ -1,17 +1,13 @@ -import 'dart:ui'; - import 'package:collection/collection.dart'; -import 'package:flutter/cupertino.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/extensions/flutter_extensions.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/features/login/cubit/authentication_cubit.dart'; import 'package:paperless_mobile/features/login/model/login_form_credentials.dart'; -import 'package:paperless_mobile/core/database/tables/local_user_account.dart'; import 'package:paperless_mobile/features/login/view/login_page.dart'; -import 'package:paperless_mobile/core/database/tables/global_settings.dart'; import 'package:paperless_mobile/features/settings/view/dialogs/switch_account_dialog.dart'; import 'package:paperless_mobile/features/settings/view/pages/switching_accounts_page.dart'; import 'package:paperless_mobile/features/settings/view/widgets/global_settings_builder.dart'; @@ -33,18 +29,18 @@ class ManageAccountsPage extends StatelessWidget { .whereNot((element) => element == globalSettings.currentLoggedInUser) .toList(); return SimpleDialog( - insetPadding: EdgeInsets.all(24), - contentPadding: EdgeInsets.all(8), + insetPadding: const EdgeInsets.all(24), + contentPadding: const EdgeInsets.all(8), title: Stack( alignment: Alignment.center, children: [ - Align( + const Align( alignment: Alignment.centerLeft, child: CloseButton(), ), Center(child: Text(S.of(context)!.accounts)), ], - ), //TODO: INTL + ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(24), ), @@ -103,7 +99,7 @@ class ManageAccountsPage extends StatelessWidget { ), ], ), - isThreeLine: true, + isThreeLine: account.fullName != null, leading: UserAvatar( account: account, userId: userId, @@ -116,7 +112,7 @@ class ManageAccountsPage extends StatelessWidget { PopupMenuItem( child: ListTile( title: Text(S.of(context)!.switchAccount), - leading: Icon(Icons.switch_account_rounded), + leading: const Icon(Icons.switch_account_rounded), ), value: 0, ), @@ -124,7 +120,7 @@ class ManageAccountsPage extends StatelessWidget { PopupMenuItem( child: ListTile( title: Text(S.of(context)!.remove), - leading: Icon( + leading: const Icon( Icons.person_remove, color: Colors.red, ), @@ -135,7 +131,7 @@ class ManageAccountsPage extends StatelessWidget { PopupMenuItem( child: ListTile( title: Text(S.of(context)!.logout), - leading: Icon( + leading: const Icon( Icons.person_remove, color: Colors.red, ), diff --git a/lib/main.dart b/lib/main.dart index 78efaec..f8f9e1d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -297,7 +297,7 @@ class _AuthenticationWrapperState extends State { builder: (context, authentication) { if (authentication.isAuthenticated) { return HomeRoute( - key: ValueKey(authentication.userId), + key: ValueKey(authentication.localUserId), ); } else if (authentication.showBiometricAuthenticationScreen) { return const VerifyIdentityPage(); diff --git a/packages/paperless_api/lib/config/hive/hive_type_ids.dart b/packages/paperless_api/lib/config/hive/hive_type_ids.dart index 254ac8b..c2a3887 100644 --- a/packages/paperless_api/lib/config/hive/hive_type_ids.dart +++ b/packages/paperless_api/lib/config/hive/hive_type_ids.dart @@ -1,5 +1,6 @@ import 'package:hive/hive.dart'; import 'package:paperless_api/paperless_api.dart'; +import 'package:paperless_api/src/models/user_model.dart'; class PaperlessApiHiveTypeIds { PaperlessApiHiveTypeIds._(); @@ -24,6 +25,12 @@ class PaperlessApiHiveTypeIds { static const int anyAssignedIdQueryParameter = 118; static const int setIdQueryParameter = 119; static const int notAssignedTagsQuery = 120; + static const int userModelv3 = 121; + static const int userPermissions = 122; + static const int inheritedPermissions = 123; + static const int groupModel = 124; + static const int permissions = 125; + static const int userModelv2 = 126; } void registerPaperlessApiHiveTypeAdapters() { @@ -46,4 +53,11 @@ void registerPaperlessApiHiveTypeAdapters() { Hive.registerAdapter(UnsetIdQueryParameterAdapter()); Hive.registerAdapter(AnyAssignedIdQueryParameterAdapter()); Hive.registerAdapter(NotAssignedIdQueryParameterAdapter()); + // Users and permissions + Hive.registerAdapter(UserModelV3Adapter()); + Hive.registerAdapter(UserModelV2Adapter()); + Hive.registerAdapter(UserPermissionsAdapter()); + Hive.registerAdapter(InheritedPermissionsAdapter()); + Hive.registerAdapter(GroupModelAdapter()); + Hive.registerAdapter(PermissionsAdapter()); } diff --git a/packages/paperless_api/lib/src/models/document_model.dart b/packages/paperless_api/lib/src/models/document_model.dart index 838a93c..f53bd5b 100644 --- a/packages/paperless_api/lib/src/models/document_model.dart +++ b/packages/paperless_api/lib/src/models/document_model.dart @@ -2,6 +2,7 @@ import 'package:equatable/equatable.dart'; import 'package:json_annotation/json_annotation.dart'; +import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/src/converters/local_date_time_json_converter.dart'; import 'package:paperless_api/src/models/search_hit.dart'; @@ -44,6 +45,12 @@ class DocumentModel extends Equatable { ) final SearchHit? searchHit; + final int? owner; + final bool? userCanChange; + + // Only present if full_perms=true + final Permissions? permissions; + const DocumentModel({ required this.id, required this.title, @@ -59,6 +66,9 @@ class DocumentModel extends Equatable { this.archivedFileName, this.storagePath, this.searchHit, + this.owner, + this.userCanChange, + this.permissions, }); factory DocumentModel.fromJson(Map json) => _$DocumentModelFromJson(json); diff --git a/packages/paperless_api/lib/src/models/group_model.dart b/packages/paperless_api/lib/src/models/group_model.dart new file mode 100644 index 0000000..f932e4a --- /dev/null +++ b/packages/paperless_api/lib/src/models/group_model.dart @@ -0,0 +1,18 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:hive/hive.dart'; +import 'package:paperless_api/paperless_api.dart'; + +part 'group_model.freezed.dart'; +part 'group_model.g.dart'; + +@freezed +@HiveType(typeId: PaperlessApiHiveTypeIds.groupModel) +class GroupModel with _$GroupModel { + const factory GroupModel({ + @HiveField(0) required int id, + @HiveField(1) required String name, + @HiveField(2) required List permissions, + }) = _GroupModel; + + factory GroupModel.fromJson(Map json) => _$GroupModelFromJson(json); +} diff --git a/packages/paperless_api/lib/src/models/group_model.freezed.dart b/packages/paperless_api/lib/src/models/group_model.freezed.dart new file mode 100644 index 0000000..d91b6bc --- /dev/null +++ b/packages/paperless_api/lib/src/models/group_model.freezed.dart @@ -0,0 +1,213 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'group_model.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + +GroupModel _$GroupModelFromJson(Map json) { + return _GroupModel.fromJson(json); +} + +/// @nodoc +mixin _$GroupModel { + @HiveField(0) + int get id => throw _privateConstructorUsedError; + @HiveField(1) + String get name => throw _privateConstructorUsedError; + @HiveField(2) + List get permissions => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $GroupModelCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $GroupModelCopyWith<$Res> { + factory $GroupModelCopyWith( + GroupModel value, $Res Function(GroupModel) then) = + _$GroupModelCopyWithImpl<$Res, GroupModel>; + @useResult + $Res call( + {@HiveField(0) int id, + @HiveField(1) String name, + @HiveField(2) List permissions}); +} + +/// @nodoc +class _$GroupModelCopyWithImpl<$Res, $Val extends GroupModel> + implements $GroupModelCopyWith<$Res> { + _$GroupModelCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? name = null, + Object? permissions = null, + }) { + return _then(_value.copyWith( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as int, + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + permissions: null == permissions + ? _value.permissions + : permissions // ignore: cast_nullable_to_non_nullable + as List, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$_GroupModelCopyWith<$Res> + implements $GroupModelCopyWith<$Res> { + factory _$$_GroupModelCopyWith( + _$_GroupModel value, $Res Function(_$_GroupModel) then) = + __$$_GroupModelCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {@HiveField(0) int id, + @HiveField(1) String name, + @HiveField(2) List permissions}); +} + +/// @nodoc +class __$$_GroupModelCopyWithImpl<$Res> + extends _$GroupModelCopyWithImpl<$Res, _$_GroupModel> + implements _$$_GroupModelCopyWith<$Res> { + __$$_GroupModelCopyWithImpl( + _$_GroupModel _value, $Res Function(_$_GroupModel) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? name = null, + Object? permissions = null, + }) { + return _then(_$_GroupModel( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as int, + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + permissions: null == permissions + ? _value._permissions + : permissions // ignore: cast_nullable_to_non_nullable + as List, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$_GroupModel implements _GroupModel { + const _$_GroupModel( + {@HiveField(0) required this.id, + @HiveField(1) required this.name, + @HiveField(2) required final List permissions}) + : _permissions = permissions; + + factory _$_GroupModel.fromJson(Map json) => + _$$_GroupModelFromJson(json); + + @override + @HiveField(0) + final int id; + @override + @HiveField(1) + final String name; + final List _permissions; + @override + @HiveField(2) + List get permissions { + if (_permissions is EqualUnmodifiableListView) return _permissions; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_permissions); + } + + @override + String toString() { + return 'GroupModel(id: $id, name: $name, permissions: $permissions)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$_GroupModel && + (identical(other.id, id) || other.id == id) && + (identical(other.name, name) || other.name == name) && + const DeepCollectionEquality() + .equals(other._permissions, _permissions)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash( + runtimeType, id, name, const DeepCollectionEquality().hash(_permissions)); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$_GroupModelCopyWith<_$_GroupModel> get copyWith => + __$$_GroupModelCopyWithImpl<_$_GroupModel>(this, _$identity); + + @override + Map toJson() { + return _$$_GroupModelToJson( + this, + ); + } +} + +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 permissions}) = + _$_GroupModel; + + factory _GroupModel.fromJson(Map json) = + _$_GroupModel.fromJson; + + @override + @HiveField(0) + int get id; + @override + @HiveField(1) + String get name; + @override + @HiveField(2) + List get permissions; + @override + @JsonKey(ignore: true) + _$$_GroupModelCopyWith<_$_GroupModel> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/packages/paperless_api/lib/src/models/models.dart b/packages/paperless_api/lib/src/models/models.dart index c1dd7f9..7421f2d 100644 --- a/packages/paperless_api/lib/src/models/models.dart +++ b/packages/paperless_api/lib/src/models/models.dart @@ -24,3 +24,8 @@ export 'saved_view_model.dart'; export 'task/task.dart'; export 'task/task_status.dart'; export 'field_suggestions.dart'; +export 'permissions/permissions.dart'; +export 'permissions/user_permissions.dart'; +export 'permissions/inherited_permissions.dart'; +export 'group_model.dart'; +export 'user_model.dart'; diff --git a/packages/paperless_api/lib/src/models/permissions/inherited_permissions.dart b/packages/paperless_api/lib/src/models/permissions/inherited_permissions.dart new file mode 100644 index 0000000..9336562 --- /dev/null +++ b/packages/paperless_api/lib/src/models/permissions/inherited_permissions.dart @@ -0,0 +1,235 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:hive/hive.dart'; +import 'package:paperless_api/config/hive/hive_type_ids.dart'; + +part 'inherited_permissions.g.dart'; + +@HiveType(typeId: PaperlessApiHiveTypeIds.inheritedPermissions) +@JsonEnum(valueField: "value") +enum InheritedPermissions { + @HiveField(0) + adminAddLogentry("admin.add_logentry"), + @HiveField(1) + adminChangeLogentry("admin.change_logentry"), + @HiveField(2) + adminDeleteLogentry("admin.delete_logentry"), + @HiveField(3) + adminViewLogentry("admin.view_logentry"), + @HiveField(4) + authAddGroup("auth.add_group"), + @HiveField(5) + authAddPermission("auth.add_permission"), + @HiveField(6) + authAddUser("auth.add_user"), + @HiveField(7) + authChangeGroup("auth.change_group"), + @HiveField(8) + authChangePermission("auth.change_permission"), + @HiveField(9) + authChangeUser("auth.change_user"), + @HiveField(10) + authDeleteGroup("auth.delete_group"), + @HiveField(11) + authDeletePermission("auth.delete_permission"), + @HiveField(12) + authDeleteUser("auth.delete_user"), + @HiveField(13) + authViewGroup("auth.view_group"), + @HiveField(14) + authViewPermission("auth.view_permission"), + @HiveField(15) + authViewUser("auth.view_user"), + @HiveField(16) + authtokenAddToken("authtoken.add_token"), + @HiveField(17) + authtokenAddTokenproxy("authtoken.add_tokenproxy"), + @HiveField(18) + authtokenChangeToken("authtoken.change_token"), + @HiveField(19) + authtokenChangeTokenproxy("authtoken.change_tokenproxy"), + @HiveField(20) + authtokenDeleteToken("authtoken.delete_token"), + @HiveField(21) + authtokenDeleteTokenproxy("authtoken.delete_tokenproxy"), + @HiveField(22) + authtokenViewToken("authtoken.view_token"), + @HiveField(23) + authtokenViewTokenproxy("authtoken.view_tokenproxy"), + @HiveField(24) + contenttypesAddContenttype("contenttypes.add_contenttype"), + @HiveField(25) + contenttypesChangeContenttype("contenttypes.change_contenttype"), + @HiveField(26) + contenttypesDeleteContenttype("contenttypes.delete_contenttype"), + @HiveField(27) + contenttypesViewContenttype("contenttypes.view_contenttype"), + @HiveField(28) + djangoCeleryResultsAddChordcounter("django_celery_results.add_chordcounter"), + @HiveField(29) + djangoCeleryResultsAddGroupresult("django_celery_results.add_groupresult"), + @HiveField(30) + djangoCeleryResultsAddTaskresult("django_celery_results.add_taskresult"), + @HiveField(31) + djangoCeleryResultsChangeChordcounter("django_celery_results.change_chordcounter"), + @HiveField(32) + djangoCeleryResultsChangeGroupresult("django_celery_results.change_groupresult"), + @HiveField(33) + djangoCeleryResultsChangeTaskresult("django_celery_results.change_taskresult"), + @HiveField(34) + djangoCeleryResultsDeleteChordcounter("django_celery_results.delete_chordcounter"), + @HiveField(35) + djangoCeleryResultsDeleteGroupresult("django_celery_results.delete_groupresult"), + @HiveField(36) + djangoCeleryResultsDeleteTaskresult("django_celery_results.delete_taskresult"), + @HiveField(37) + djangoCeleryResultsViewChordcounter("django_celery_results.view_chordcounter"), + @HiveField(38) + djangoCeleryResultsViewGroupresult("django_celery_results.view_groupresult"), + @HiveField(39) + djangoCeleryResultsViewTaskresult("django_celery_results.view_taskresult"), + @HiveField(40) + documentsAddComment("documents.add_comment"), + @HiveField(41) + documentsAddCorrespondent("documents.add_correspondent"), + @HiveField(42) + documentsAddDocument("documents.add_document"), + @HiveField(43) + documentsAddDocumenttype("documents.add_documenttype"), + @HiveField(44) + documentsAddLog("documents.add_log"), + @HiveField(45) + documentsAddNote("documents.add_note"), + @HiveField(46) + documentsAddPaperlesstask("documents.add_paperlesstask"), + @HiveField(47) + documentsAddSavedview("documents.add_savedview"), + @HiveField(48) + documentsAddSavedviewfilterrule("documents.add_savedviewfilterrule"), + @HiveField(49) + documentsAddStoragepath("documents.add_storagepath"), + @HiveField(50) + documentsAddTag("documents.add_tag"), + @HiveField(51) + documentsAddUisettings("documents.add_uisettings"), + @HiveField(52) + documentsChangeComment("documents.change_comment"), + @HiveField(53) + documentsChangeCorrespondent("documents.change_correspondent"), + @HiveField(54) + documentsChangeDocument("documents.change_document"), + @HiveField(55) + documentsChangeDocumenttype("documents.change_documenttype"), + @HiveField(56) + documentsChangeLog("documents.change_log"), + @HiveField(57) + documentsChangeNote("documents.change_note"), + @HiveField(58) + documentsChangePaperlesstask("documents.change_paperlesstask"), + @HiveField(59) + documentsChangeSavedview("documents.change_savedview"), + @HiveField(60) + documentsChangeSavedviewfilterrule("documents.change_savedviewfilterrule"), + @HiveField(61) + documentsChangeStoragepathdocuments("documents.change_storagepathdocuments.change_tag"), + @HiveField(62) + documentsChangeUisettings("documents.change_uisettings"), + @HiveField(63) + documentsDeleteComment("documents.delete_comment"), + @HiveField(64) + documentsDeleteCorrespondent("documents.delete_correspondent"), + @HiveField(65) + documentsDeleteDocument("documents.delete_document"), + @HiveField(66) + documentsDeleteDocumenttype("documents.delete_documenttype"), + @HiveField(67) + documentsDeleteLog("documents.delete_log"), + @HiveField(68) + documentsDeleteNote("documents.delete_note"), + @HiveField(69) + documentsDeletePaperlesstask("documents.delete_paperlesstask"), + @HiveField(70) + documentsDeleteSavedview("documents.delete_savedview"), + @HiveField(71) + documentsDeleteSavedviewfilterrule("documents.delete_savedviewfilterrule"), + @HiveField(72) + documentsDeleteStoragepath("documents.delete_storagepath"), + @HiveField(73) + documentsDeleteTag("documents.delete_tag"), + @HiveField(74) + documentsDeleteUisettings("documents.delete_uisettings"), + @HiveField(75) + documentsViewComment("documents.view_comment"), + @HiveField(76) + documentsViewCorrespondent("documents.view_correspondent"), + @HiveField(77) + documentsViewDocument("documents.view_document"), + @HiveField(78) + documentsViewDocumenttype("documents.view_documenttype"), + @HiveField(79) + documentsViewLog("documents.view_log"), + @HiveField(80) + documentsViewNote("documents.view_note"), + @HiveField(81) + documentsViewPaperlesstask("documents.view_paperlesstask"), + @HiveField(82) + documentsViewSavedview("documents.view_savedview"), + @HiveField(83) + documentsViewSavedviewfilterrule("documents.view_savedviewfilterrule"), + @HiveField(84) + documentsViewStoragepath("documents.view_storagepath"), + @HiveField(85) + documentsViewTag("documents.view_tag"), + @HiveField(86) + documentsViewUisettings("documents.view_uisettings"), + @HiveField(87) + guardianAddGroupobjectpermission("guardian.add_groupobjectpermission"), + @HiveField(88) + guardianAddUserobjectpermission("guardian.add_userobjectpermission"), + @HiveField(89) + guardianChangeGroupobjectpermission("guardian.change_groupobjectpermission"), + @HiveField(90) + guardianChangeUserobjectpermission("guardian.change_userobjectpermission"), + @HiveField(91) + guardianDeleteGroupobjectpermission("guardian.delete_groupobjectpermission"), + @HiveField(92) + guardianDeleteUserobjectpermission("guardian.delete_userobjectpermission"), + @HiveField(93) + guardianViewGroupobjectpermission("guardian.view_groupobjectpermission"), + @HiveField(94) + guardianViewUserobjectpermission("guardian.view_userobjectpermission"), + @HiveField(95) + paperlessMailAddMailaccount("paperless_mail.add_mailaccount"), + @HiveField(96) + paperlessMailAddMailrule("paperless_mail.add_mailrule"), + @HiveField(97) + paperlessMailAddProcessedmail("paperless_mail.add_processedmail"), + @HiveField(98) + paperlessMailChangeMailaccount("paperless_mail.change_mailaccount"), + @HiveField(99) + paperlessMailChangeMailrule("paperless_mail.change_mailrule"), + @HiveField(100) + paperlessMailChangeProcessedmail("paperless_mail.change_processedmail"), + @HiveField(101) + paperlessMailDeleteMailaccount("paperless_mail.delete_mailaccount"), + @HiveField(102) + paperlessMailDeleteMailrule("paperless_mail.delete_mailrule"), + @HiveField(103) + paperlessMailDeleteProcessedmail("paperless_mail.delete_processedmail"), + @HiveField(104) + paperlessMailViewMailaccount("paperless_mail.view_mailaccount"), + @HiveField(105) + paperlessMailViewMailrule("paperless_mail.view_mailrule"), + @HiveField(106) + paperlessMailViewProcessedmail("paperless_mail.view_processedmail"), + @HiveField(107) + sessionsAddSession("sessions.add_session"), + @HiveField(108) + sessionsChangeSession("sessions.change_session"), + @HiveField(109) + sessionsDeleteSession("sessions.delete_session"), + @HiveField(110) + sessionsViewSession("sessions.view_session"); + + const InheritedPermissions(this.value); + final String value; +} diff --git a/packages/paperless_api/lib/src/models/permissions/permissions.dart b/packages/paperless_api/lib/src/models/permissions/permissions.dart new file mode 100644 index 0000000..ea0d489 --- /dev/null +++ b/packages/paperless_api/lib/src/models/permissions/permissions.dart @@ -0,0 +1,17 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:hive/hive.dart'; +import 'package:paperless_api/paperless_api.dart'; + +part 'permissions.freezed.dart'; +part 'permissions.g.dart'; + +@HiveType(typeId: PaperlessApiHiveTypeIds.permissions) +@freezed +class Permissions with _$Permissions { + const factory Permissions({ + @HiveField(0) @Default([]) List view, + @HiveField(1) @Default([]) List change, + }) = _Permissions; + + factory Permissions.fromJson(Map json) => _$PermissionsFromJson(json); +} diff --git a/packages/paperless_api/lib/src/models/permissions/permissions.freezed.dart b/packages/paperless_api/lib/src/models/permissions/permissions.freezed.dart new file mode 100644 index 0000000..6b324a5 --- /dev/null +++ b/packages/paperless_api/lib/src/models/permissions/permissions.freezed.dart @@ -0,0 +1,195 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'permissions.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + +Permissions _$PermissionsFromJson(Map json) { + return _Permissions.fromJson(json); +} + +/// @nodoc +mixin _$Permissions { + @HiveField(0) + List get view => throw _privateConstructorUsedError; + @HiveField(1) + List get change => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $PermissionsCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $PermissionsCopyWith<$Res> { + factory $PermissionsCopyWith( + Permissions value, $Res Function(Permissions) then) = + _$PermissionsCopyWithImpl<$Res, Permissions>; + @useResult + $Res call({@HiveField(0) List view, @HiveField(1) List change}); +} + +/// @nodoc +class _$PermissionsCopyWithImpl<$Res, $Val extends Permissions> + implements $PermissionsCopyWith<$Res> { + _$PermissionsCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? view = null, + Object? change = null, + }) { + return _then(_value.copyWith( + view: null == view + ? _value.view + : view // ignore: cast_nullable_to_non_nullable + as List, + change: null == change + ? _value.change + : change // ignore: cast_nullable_to_non_nullable + as List, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$_PermissionsCopyWith<$Res> + implements $PermissionsCopyWith<$Res> { + factory _$$_PermissionsCopyWith( + _$_Permissions value, $Res Function(_$_Permissions) then) = + __$$_PermissionsCopyWithImpl<$Res>; + @override + @useResult + $Res call({@HiveField(0) List view, @HiveField(1) List change}); +} + +/// @nodoc +class __$$_PermissionsCopyWithImpl<$Res> + extends _$PermissionsCopyWithImpl<$Res, _$_Permissions> + implements _$$_PermissionsCopyWith<$Res> { + __$$_PermissionsCopyWithImpl( + _$_Permissions _value, $Res Function(_$_Permissions) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? view = null, + Object? change = null, + }) { + return _then(_$_Permissions( + view: null == view + ? _value._view + : view // ignore: cast_nullable_to_non_nullable + as List, + change: null == change + ? _value._change + : change // ignore: cast_nullable_to_non_nullable + as List, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$_Permissions implements _Permissions { + const _$_Permissions( + {@HiveField(0) final List view = const [], + @HiveField(1) final List change = const []}) + : _view = view, + _change = change; + + factory _$_Permissions.fromJson(Map json) => + _$$_PermissionsFromJson(json); + + final List _view; + @override + @JsonKey() + @HiveField(0) + List get view { + if (_view is EqualUnmodifiableListView) return _view; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_view); + } + + final List _change; + @override + @JsonKey() + @HiveField(1) + List get change { + if (_change is EqualUnmodifiableListView) return _change; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_change); + } + + @override + String toString() { + return 'Permissions(view: $view, change: $change)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$_Permissions && + const DeepCollectionEquality().equals(other._view, _view) && + const DeepCollectionEquality().equals(other._change, _change)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash( + runtimeType, + const DeepCollectionEquality().hash(_view), + const DeepCollectionEquality().hash(_change)); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$_PermissionsCopyWith<_$_Permissions> get copyWith => + __$$_PermissionsCopyWithImpl<_$_Permissions>(this, _$identity); + + @override + Map toJson() { + return _$$_PermissionsToJson( + this, + ); + } +} + +abstract class _Permissions implements Permissions { + const factory _Permissions( + {@HiveField(0) final List view, + @HiveField(1) final List change}) = _$_Permissions; + + factory _Permissions.fromJson(Map json) = + _$_Permissions.fromJson; + + @override + @HiveField(0) + List get view; + @override + @HiveField(1) + List get change; + @override + @JsonKey(ignore: true) + _$$_PermissionsCopyWith<_$_Permissions> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/packages/paperless_api/lib/src/models/permissions/user_permissions.dart b/packages/paperless_api/lib/src/models/permissions/user_permissions.dart new file mode 100644 index 0000000..14d0a54 --- /dev/null +++ b/packages/paperless_api/lib/src/models/permissions/user_permissions.dart @@ -0,0 +1,117 @@ +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); + + final String value; +} diff --git a/packages/paperless_api/lib/src/models/user_model.dart b/packages/paperless_api/lib/src/models/user_model.dart new file mode 100644 index 0000000..b02a377 --- /dev/null +++ b/packages/paperless_api/lib/src/models/user_model.dart @@ -0,0 +1,72 @@ +// ignore_for_file: invalid_annotation_target + +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:hive/hive.dart'; +import 'package:paperless_api/config/hive/hive_type_ids.dart'; +import 'package:paperless_api/src/models/permissions/inherited_permissions.dart'; +import 'package:paperless_api/src/models/permissions/user_permissions.dart'; + +part 'user_model.freezed.dart'; +part 'user_model.g.dart'; + +@freezed +class UserModel with _$UserModel { + const UserModel._(); + + @JsonSerializable(fieldRename: FieldRename.snake) + @HiveType(typeId: PaperlessApiHiveTypeIds.userModelv3) + const factory UserModel.v3({ + @HiveField(0) required int id, + @HiveField(1) required String username, + @HiveField(2) required String email, + @HiveField(3) String? firstName, + @HiveField(4) String? lastName, + @HiveField(5) DateTime? dateJoined, + @HiveField(6) @Default(true) bool isStaff, + @HiveField(7) @Default(true) bool isActive, + @HiveField(8) @Default(true) bool isSuperuser, + @HiveField(9) @Default([]) List groups, + @HiveField(10) @Default(UserPermissions.values) List userPermissions, + @HiveField(11) + @Default(InheritedPermissions.values) + List inheritedPermissions, + }) = UserModelV3; + + @JsonSerializable(fieldRename: FieldRename.snake) + @HiveType(typeId: PaperlessApiHiveTypeIds.userModelv2) + const factory UserModel.v2({ + @HiveField(0) @JsonKey(name: "user_id") required int id, + @HiveField(1) required String username, + @HiveField(2) String? displayName, + }) = UserModelV2; + + factory UserModel.fromJson(Map json) => _$UserModelFromJson(json); + + String? get fullName => map( + v2: (value) => value.displayName, + v3: (value) { + if (value.firstName == null && value.lastName == null) { + return null; + } + if (value.firstName == null) { + return value.lastName; + } + return value.firstName! + (value.lastName ?? ''); + }, + ); + + bool hasPermission(UserPermissions permission) { + return map( + v3: (value) { + if (value.isSuperuser) { + return true; + } + return value.userPermissions.contains(permission); + }, + v2: (value) { + // In previous versions, all users have access to all + return true; + }, + ); + } +} diff --git a/packages/paperless_api/lib/src/models/user_model.freezed.dart b/packages/paperless_api/lib/src/models/user_model.freezed.dart new file mode 100644 index 0000000..c675095 --- /dev/null +++ b/packages/paperless_api/lib/src/models/user_model.freezed.dart @@ -0,0 +1,845 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'user_model.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + +UserModel _$UserModelFromJson(Map json) { + switch (json['runtimeType']) { + case 'v3': + return UserModelV3.fromJson(json); + case 'v2': + return UserModelV2.fromJson(json); + + default: + throw CheckedFromJsonException(json, 'runtimeType', 'UserModel', + 'Invalid union type "${json['runtimeType']}"!'); + } +} + +/// @nodoc +mixin _$UserModel { + @HiveField(0) + int get id => throw _privateConstructorUsedError; + @HiveField(1) + String get username => throw _privateConstructorUsedError; + @optionalTypeArgs + TResult when({ + required TResult Function( + @HiveField(0) int id, + @HiveField(1) String username, + @HiveField(2) String email, + @HiveField(3) String? firstName, + @HiveField(4) String? lastName, + @HiveField(5) DateTime? dateJoined, + @HiveField(6) bool isStaff, + @HiveField(7) bool isActive, + @HiveField(8) bool isSuperuser, + @HiveField(9) List groups, + @HiveField(10) List userPermissions, + @HiveField(11) List inheritedPermissions) + v3, + required TResult Function(@HiveField(0) @JsonKey(name: "user_id") int id, + @HiveField(1) String username, @HiveField(2) String? displayName) + v2, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function( + @HiveField(0) int id, + @HiveField(1) String username, + @HiveField(2) String email, + @HiveField(3) String? firstName, + @HiveField(4) String? lastName, + @HiveField(5) DateTime? dateJoined, + @HiveField(6) bool isStaff, + @HiveField(7) bool isActive, + @HiveField(8) bool isSuperuser, + @HiveField(9) List groups, + @HiveField(10) List userPermissions, + @HiveField(11) List inheritedPermissions)? + v3, + TResult? Function(@HiveField(0) @JsonKey(name: "user_id") int id, + @HiveField(1) String username, @HiveField(2) String? displayName)? + v2, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen({ + TResult Function( + @HiveField(0) int id, + @HiveField(1) String username, + @HiveField(2) String email, + @HiveField(3) String? firstName, + @HiveField(4) String? lastName, + @HiveField(5) DateTime? dateJoined, + @HiveField(6) bool isStaff, + @HiveField(7) bool isActive, + @HiveField(8) bool isSuperuser, + @HiveField(9) List groups, + @HiveField(10) List userPermissions, + @HiveField(11) List inheritedPermissions)? + v3, + TResult Function(@HiveField(0) @JsonKey(name: "user_id") int id, + @HiveField(1) String username, @HiveField(2) String? displayName)? + v2, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map({ + required TResult Function(UserModelV3 value) v3, + required TResult Function(UserModelV2 value) v2, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(UserModelV3 value)? v3, + TResult? Function(UserModelV2 value)? v2, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap({ + TResult Function(UserModelV3 value)? v3, + TResult Function(UserModelV2 value)? v2, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $UserModelCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $UserModelCopyWith<$Res> { + factory $UserModelCopyWith(UserModel value, $Res Function(UserModel) then) = + _$UserModelCopyWithImpl<$Res, UserModel>; + @useResult + $Res call({@HiveField(0) int id, @HiveField(1) String username}); +} + +/// @nodoc +class _$UserModelCopyWithImpl<$Res, $Val extends UserModel> + implements $UserModelCopyWith<$Res> { + _$UserModelCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? username = null, + }) { + return _then(_value.copyWith( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as int, + username: null == username + ? _value.username + : username // ignore: cast_nullable_to_non_nullable + as String, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$UserModelV3CopyWith<$Res> + implements $UserModelCopyWith<$Res> { + factory _$$UserModelV3CopyWith( + _$UserModelV3 value, $Res Function(_$UserModelV3) then) = + __$$UserModelV3CopyWithImpl<$Res>; + @override + @useResult + $Res call( + {@HiveField(0) int id, + @HiveField(1) String username, + @HiveField(2) String email, + @HiveField(3) String? firstName, + @HiveField(4) String? lastName, + @HiveField(5) DateTime? dateJoined, + @HiveField(6) bool isStaff, + @HiveField(7) bool isActive, + @HiveField(8) bool isSuperuser, + @HiveField(9) List groups, + @HiveField(10) List userPermissions, + @HiveField(11) List inheritedPermissions}); +} + +/// @nodoc +class __$$UserModelV3CopyWithImpl<$Res> + extends _$UserModelCopyWithImpl<$Res, _$UserModelV3> + implements _$$UserModelV3CopyWith<$Res> { + __$$UserModelV3CopyWithImpl( + _$UserModelV3 _value, $Res Function(_$UserModelV3) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? username = null, + Object? email = null, + Object? firstName = freezed, + Object? lastName = freezed, + Object? dateJoined = freezed, + Object? isStaff = null, + Object? isActive = null, + Object? isSuperuser = null, + Object? groups = null, + Object? userPermissions = null, + Object? inheritedPermissions = null, + }) { + return _then(_$UserModelV3( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as int, + username: null == username + ? _value.username + : username // ignore: cast_nullable_to_non_nullable + as String, + email: null == email + ? _value.email + : email // ignore: cast_nullable_to_non_nullable + as String, + firstName: freezed == firstName + ? _value.firstName + : firstName // ignore: cast_nullable_to_non_nullable + as String?, + lastName: freezed == lastName + ? _value.lastName + : lastName // ignore: cast_nullable_to_non_nullable + as String?, + dateJoined: freezed == dateJoined + ? _value.dateJoined + : dateJoined // ignore: cast_nullable_to_non_nullable + as DateTime?, + isStaff: null == isStaff + ? _value.isStaff + : isStaff // ignore: cast_nullable_to_non_nullable + as bool, + isActive: null == isActive + ? _value.isActive + : isActive // ignore: cast_nullable_to_non_nullable + as bool, + isSuperuser: null == isSuperuser + ? _value.isSuperuser + : isSuperuser // ignore: cast_nullable_to_non_nullable + as bool, + groups: null == groups + ? _value._groups + : groups // ignore: cast_nullable_to_non_nullable + as List, + userPermissions: null == userPermissions + ? _value._userPermissions + : userPermissions // ignore: cast_nullable_to_non_nullable + as List, + inheritedPermissions: null == inheritedPermissions + ? _value._inheritedPermissions + : inheritedPermissions // ignore: cast_nullable_to_non_nullable + as List, + )); + } +} + +/// @nodoc + +@JsonSerializable(fieldRename: FieldRename.snake) +@HiveType(typeId: PaperlessApiHiveTypeIds.userModelv3) +class _$UserModelV3 extends UserModelV3 { + const _$UserModelV3( + {@HiveField(0) required this.id, + @HiveField(1) required this.username, + @HiveField(2) required this.email, + @HiveField(3) this.firstName, + @HiveField(4) this.lastName, + @HiveField(5) this.dateJoined, + @HiveField(6) this.isStaff = true, + @HiveField(7) this.isActive = true, + @HiveField(8) this.isSuperuser = true, + @HiveField(9) final List groups = const [], + @HiveField(10) final List userPermissions = + UserPermissions.values, + @HiveField(11) final List inheritedPermissions = + InheritedPermissions.values, + final String? $type}) + : _groups = groups, + _userPermissions = userPermissions, + _inheritedPermissions = inheritedPermissions, + $type = $type ?? 'v3', + super._(); + + factory _$UserModelV3.fromJson(Map json) => + _$$UserModelV3FromJson(json); + + @override + @HiveField(0) + final int id; + @override + @HiveField(1) + final String username; + @override + @HiveField(2) + final String email; + @override + @HiveField(3) + final String? firstName; + @override + @HiveField(4) + final String? lastName; + @override + @HiveField(5) + final DateTime? dateJoined; + @override + @JsonKey() + @HiveField(6) + final bool isStaff; + @override + @JsonKey() + @HiveField(7) + final bool isActive; + @override + @JsonKey() + @HiveField(8) + final bool isSuperuser; + final List _groups; + @override + @JsonKey() + @HiveField(9) + List get groups { + if (_groups is EqualUnmodifiableListView) return _groups; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_groups); + } + + final List _userPermissions; + @override + @JsonKey() + @HiveField(10) + List get userPermissions { + if (_userPermissions is EqualUnmodifiableListView) return _userPermissions; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_userPermissions); + } + + final List _inheritedPermissions; + @override + @JsonKey() + @HiveField(11) + List get inheritedPermissions { + if (_inheritedPermissions is EqualUnmodifiableListView) + return _inheritedPermissions; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_inheritedPermissions); + } + + @JsonKey(name: 'runtimeType') + final String $type; + + @override + String toString() { + return 'UserModel.v3(id: $id, username: $username, email: $email, firstName: $firstName, lastName: $lastName, dateJoined: $dateJoined, isStaff: $isStaff, isActive: $isActive, isSuperuser: $isSuperuser, groups: $groups, userPermissions: $userPermissions, inheritedPermissions: $inheritedPermissions)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$UserModelV3 && + (identical(other.id, id) || other.id == id) && + (identical(other.username, username) || + other.username == username) && + (identical(other.email, email) || other.email == email) && + (identical(other.firstName, firstName) || + other.firstName == firstName) && + (identical(other.lastName, lastName) || + other.lastName == lastName) && + (identical(other.dateJoined, dateJoined) || + other.dateJoined == dateJoined) && + (identical(other.isStaff, isStaff) || other.isStaff == isStaff) && + (identical(other.isActive, isActive) || + other.isActive == isActive) && + (identical(other.isSuperuser, isSuperuser) || + other.isSuperuser == isSuperuser) && + const DeepCollectionEquality().equals(other._groups, _groups) && + const DeepCollectionEquality() + .equals(other._userPermissions, _userPermissions) && + const DeepCollectionEquality() + .equals(other._inheritedPermissions, _inheritedPermissions)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash( + runtimeType, + id, + username, + email, + firstName, + lastName, + dateJoined, + isStaff, + isActive, + isSuperuser, + const DeepCollectionEquality().hash(_groups), + const DeepCollectionEquality().hash(_userPermissions), + const DeepCollectionEquality().hash(_inheritedPermissions)); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$UserModelV3CopyWith<_$UserModelV3> get copyWith => + __$$UserModelV3CopyWithImpl<_$UserModelV3>(this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function( + @HiveField(0) int id, + @HiveField(1) String username, + @HiveField(2) String email, + @HiveField(3) String? firstName, + @HiveField(4) String? lastName, + @HiveField(5) DateTime? dateJoined, + @HiveField(6) bool isStaff, + @HiveField(7) bool isActive, + @HiveField(8) bool isSuperuser, + @HiveField(9) List groups, + @HiveField(10) List userPermissions, + @HiveField(11) List inheritedPermissions) + v3, + required TResult Function(@HiveField(0) @JsonKey(name: "user_id") int id, + @HiveField(1) String username, @HiveField(2) String? displayName) + v2, + }) { + return v3(id, username, email, firstName, lastName, dateJoined, isStaff, + isActive, isSuperuser, groups, userPermissions, inheritedPermissions); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function( + @HiveField(0) int id, + @HiveField(1) String username, + @HiveField(2) String email, + @HiveField(3) String? firstName, + @HiveField(4) String? lastName, + @HiveField(5) DateTime? dateJoined, + @HiveField(6) bool isStaff, + @HiveField(7) bool isActive, + @HiveField(8) bool isSuperuser, + @HiveField(9) List groups, + @HiveField(10) List userPermissions, + @HiveField(11) List inheritedPermissions)? + v3, + TResult? Function(@HiveField(0) @JsonKey(name: "user_id") int id, + @HiveField(1) String username, @HiveField(2) String? displayName)? + v2, + }) { + return v3?.call( + id, + username, + email, + firstName, + lastName, + dateJoined, + isStaff, + isActive, + isSuperuser, + groups, + userPermissions, + inheritedPermissions); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function( + @HiveField(0) int id, + @HiveField(1) String username, + @HiveField(2) String email, + @HiveField(3) String? firstName, + @HiveField(4) String? lastName, + @HiveField(5) DateTime? dateJoined, + @HiveField(6) bool isStaff, + @HiveField(7) bool isActive, + @HiveField(8) bool isSuperuser, + @HiveField(9) List groups, + @HiveField(10) List userPermissions, + @HiveField(11) List inheritedPermissions)? + v3, + TResult Function(@HiveField(0) @JsonKey(name: "user_id") int id, + @HiveField(1) String username, @HiveField(2) String? displayName)? + v2, + required TResult orElse(), + }) { + if (v3 != null) { + return v3(id, username, email, firstName, lastName, dateJoined, isStaff, + isActive, isSuperuser, groups, userPermissions, inheritedPermissions); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(UserModelV3 value) v3, + required TResult Function(UserModelV2 value) v2, + }) { + return v3(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(UserModelV3 value)? v3, + TResult? Function(UserModelV2 value)? v2, + }) { + return v3?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(UserModelV3 value)? v3, + TResult Function(UserModelV2 value)? v2, + required TResult orElse(), + }) { + if (v3 != null) { + return v3(this); + } + return orElse(); + } + + @override + Map toJson() { + return _$$UserModelV3ToJson( + this, + ); + } +} + +abstract class UserModelV3 extends UserModel { + const factory UserModelV3( + {@HiveField(0) + required final int id, + @HiveField(1) + required final String username, + @HiveField(2) + required final String email, + @HiveField(3) + final String? firstName, + @HiveField(4) + final String? lastName, + @HiveField(5) + final DateTime? dateJoined, + @HiveField(6) + final bool isStaff, + @HiveField(7) + final bool isActive, + @HiveField(8) + final bool isSuperuser, + @HiveField(9) + final List groups, + @HiveField(10) + final List userPermissions, + @HiveField(11) + final List inheritedPermissions}) = + _$UserModelV3; + const UserModelV3._() : super._(); + + factory UserModelV3.fromJson(Map json) = + _$UserModelV3.fromJson; + + @override + @HiveField(0) + int get id; + @override + @HiveField(1) + String get username; + @HiveField(2) + String get email; + @HiveField(3) + String? get firstName; + @HiveField(4) + String? get lastName; + @HiveField(5) + DateTime? get dateJoined; + @HiveField(6) + bool get isStaff; + @HiveField(7) + bool get isActive; + @HiveField(8) + bool get isSuperuser; + @HiveField(9) + List get groups; + @HiveField(10) + List get userPermissions; + @HiveField(11) + List get inheritedPermissions; + @override + @JsonKey(ignore: true) + _$$UserModelV3CopyWith<_$UserModelV3> get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$UserModelV2CopyWith<$Res> + implements $UserModelCopyWith<$Res> { + factory _$$UserModelV2CopyWith( + _$UserModelV2 value, $Res Function(_$UserModelV2) then) = + __$$UserModelV2CopyWithImpl<$Res>; + @override + @useResult + $Res call( + {@HiveField(0) @JsonKey(name: "user_id") int id, + @HiveField(1) String username, + @HiveField(2) String? displayName}); +} + +/// @nodoc +class __$$UserModelV2CopyWithImpl<$Res> + extends _$UserModelCopyWithImpl<$Res, _$UserModelV2> + implements _$$UserModelV2CopyWith<$Res> { + __$$UserModelV2CopyWithImpl( + _$UserModelV2 _value, $Res Function(_$UserModelV2) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? username = null, + Object? displayName = freezed, + }) { + return _then(_$UserModelV2( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as int, + username: null == username + ? _value.username + : username // ignore: cast_nullable_to_non_nullable + as String, + displayName: freezed == displayName + ? _value.displayName + : displayName // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc + +@JsonSerializable(fieldRename: FieldRename.snake) +@HiveType(typeId: PaperlessApiHiveTypeIds.userModelv2) +class _$UserModelV2 extends UserModelV2 { + const _$UserModelV2( + {@HiveField(0) @JsonKey(name: "user_id") required this.id, + @HiveField(1) required this.username, + @HiveField(2) this.displayName, + final String? $type}) + : $type = $type ?? 'v2', + super._(); + + factory _$UserModelV2.fromJson(Map json) => + _$$UserModelV2FromJson(json); + + @override + @HiveField(0) + @JsonKey(name: "user_id") + final int id; + @override + @HiveField(1) + final String username; + @override + @HiveField(2) + final String? displayName; + + @JsonKey(name: 'runtimeType') + final String $type; + + @override + String toString() { + return 'UserModel.v2(id: $id, username: $username, displayName: $displayName)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$UserModelV2 && + (identical(other.id, id) || other.id == id) && + (identical(other.username, username) || + other.username == username) && + (identical(other.displayName, displayName) || + other.displayName == displayName)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash(runtimeType, id, username, displayName); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$UserModelV2CopyWith<_$UserModelV2> get copyWith => + __$$UserModelV2CopyWithImpl<_$UserModelV2>(this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function( + @HiveField(0) int id, + @HiveField(1) String username, + @HiveField(2) String email, + @HiveField(3) String? firstName, + @HiveField(4) String? lastName, + @HiveField(5) DateTime? dateJoined, + @HiveField(6) bool isStaff, + @HiveField(7) bool isActive, + @HiveField(8) bool isSuperuser, + @HiveField(9) List groups, + @HiveField(10) List userPermissions, + @HiveField(11) List inheritedPermissions) + v3, + required TResult Function(@HiveField(0) @JsonKey(name: "user_id") int id, + @HiveField(1) String username, @HiveField(2) String? displayName) + v2, + }) { + return v2(id, username, displayName); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function( + @HiveField(0) int id, + @HiveField(1) String username, + @HiveField(2) String email, + @HiveField(3) String? firstName, + @HiveField(4) String? lastName, + @HiveField(5) DateTime? dateJoined, + @HiveField(6) bool isStaff, + @HiveField(7) bool isActive, + @HiveField(8) bool isSuperuser, + @HiveField(9) List groups, + @HiveField(10) List userPermissions, + @HiveField(11) List inheritedPermissions)? + v3, + TResult? Function(@HiveField(0) @JsonKey(name: "user_id") int id, + @HiveField(1) String username, @HiveField(2) String? displayName)? + v2, + }) { + return v2?.call(id, username, displayName); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function( + @HiveField(0) int id, + @HiveField(1) String username, + @HiveField(2) String email, + @HiveField(3) String? firstName, + @HiveField(4) String? lastName, + @HiveField(5) DateTime? dateJoined, + @HiveField(6) bool isStaff, + @HiveField(7) bool isActive, + @HiveField(8) bool isSuperuser, + @HiveField(9) List groups, + @HiveField(10) List userPermissions, + @HiveField(11) List inheritedPermissions)? + v3, + TResult Function(@HiveField(0) @JsonKey(name: "user_id") int id, + @HiveField(1) String username, @HiveField(2) String? displayName)? + v2, + required TResult orElse(), + }) { + if (v2 != null) { + return v2(id, username, displayName); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(UserModelV3 value) v3, + required TResult Function(UserModelV2 value) v2, + }) { + return v2(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(UserModelV3 value)? v3, + TResult? Function(UserModelV2 value)? v2, + }) { + return v2?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(UserModelV3 value)? v3, + TResult Function(UserModelV2 value)? v2, + required TResult orElse(), + }) { + if (v2 != null) { + return v2(this); + } + return orElse(); + } + + @override + Map toJson() { + return _$$UserModelV2ToJson( + this, + ); + } +} + +abstract class UserModelV2 extends UserModel { + const factory UserModelV2( + {@HiveField(0) @JsonKey(name: "user_id") required final int id, + @HiveField(1) required final String username, + @HiveField(2) final String? displayName}) = _$UserModelV2; + const UserModelV2._() : super._(); + + factory UserModelV2.fromJson(Map json) = + _$UserModelV2.fromJson; + + @override + @HiveField(0) + @JsonKey(name: "user_id") + int get id; + @override + @HiveField(1) + String get username; + @HiveField(2) + String? get displayName; + @override + @JsonKey(ignore: true) + _$$UserModelV2CopyWith<_$UserModelV2> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/packages/paperless_api/lib/src/modules/modules.dart b/packages/paperless_api/lib/src/modules/modules.dart index 8f98dd6..18ac2e2 100644 --- a/packages/paperless_api/lib/src/modules/modules.dart +++ b/packages/paperless_api/lib/src/modules/modules.dart @@ -11,3 +11,7 @@ export 'server_stats_api/paperless_server_stats_api.dart'; export 'server_stats_api/paperless_server_stats_api_impl.dart'; export 'tasks_api/paperless_tasks_api.dart'; export 'tasks_api/paperless_tasks_api_impl.dart'; +export 'user_api/paperless_user_api.dart'; +export 'user_api/paperless_user_api_v2_impl.dart'; +export 'user_api/paperless_user_api_v3.dart'; +export 'user_api/paperless_user_api_v3_impl.dart'; diff --git a/packages/paperless_api/lib/src/modules/user_api/paperless_user_api.dart b/packages/paperless_api/lib/src/modules/user_api/paperless_user_api.dart new file mode 100644 index 0000000..d6cbae2 --- /dev/null +++ b/packages/paperless_api/lib/src/modules/user_api/paperless_user_api.dart @@ -0,0 +1,6 @@ +import 'package:paperless_api/paperless_api.dart'; + +abstract class PaperlessUserApi { + Future findCurrentUserId(); + Future find(int id); +} diff --git a/packages/paperless_api/lib/src/modules/user_api/paperless_user_api_v2_impl.dart b/packages/paperless_api/lib/src/modules/user_api/paperless_user_api_v2_impl.dart new file mode 100644 index 0000000..2e63ba5 --- /dev/null +++ b/packages/paperless_api/lib/src/modules/user_api/paperless_user_api_v2_impl.dart @@ -0,0 +1,27 @@ +import 'package:dio/dio.dart'; +import 'package:paperless_api/paperless_api.dart'; +import 'package:paperless_api/src/modules/user_api/paperless_user_api.dart'; + +class PaperlessUserApiV2Impl implements PaperlessUserApi { + final Dio client; + + PaperlessUserApiV2Impl(this.client); + + @override + Future findCurrentUserId() async { + final response = await client.get("/api/ui_settings/"); + if (response.statusCode == 200) { + return response.data['user']['id']; + } + throw const PaperlessServerException.unknown(); + } + + @override + Future find(int id) async { + final response = await client.get("/api/ui_settings/"); + if (response.statusCode == 200) { + return UserModelV2.fromJson(response.data); + } + throw const PaperlessServerException.unknown(); + } +} diff --git a/packages/paperless_api/lib/src/modules/user_api/paperless_user_api_v3.dart b/packages/paperless_api/lib/src/modules/user_api/paperless_user_api_v3.dart new file mode 100644 index 0000000..3f8447f --- /dev/null +++ b/packages/paperless_api/lib/src/modules/user_api/paperless_user_api_v3.dart @@ -0,0 +1,10 @@ +import 'package:paperless_api/src/models/user_model.dart'; + +abstract class PaperlessUserApiV3 { + Future> findWhere({ + String startsWith, + String endsWith, + String contains, + String username, + }); +} diff --git a/packages/paperless_api/lib/src/modules/user_api/paperless_user_api_v3_impl.dart b/packages/paperless_api/lib/src/modules/user_api/paperless_user_api_v3_impl.dart new file mode 100644 index 0000000..a3ce6ff --- /dev/null +++ b/packages/paperless_api/lib/src/modules/user_api/paperless_user_api_v3_impl.dart @@ -0,0 +1,50 @@ +import 'package:dio/dio.dart'; +import 'package:paperless_api/paperless_api.dart'; +import 'package:paperless_api/src/modules/user_api/paperless_user_api.dart'; +import 'package:paperless_api/src/modules/user_api/paperless_user_api_v3.dart'; + +class PaperlessUserApiV3Impl implements PaperlessUserApi, PaperlessUserApiV3 { + final Dio dio; + + PaperlessUserApiV3Impl(this.dio); + + @override + Future find(int id) async { + final response = await dio.get("/api/users/$id/"); + if (response.statusCode == 200) { + return UserModelV3.fromJson(response.data); + } + throw const PaperlessServerException.unknown(); + } + + @override + Future> findWhere({ + String startsWith = '', + String endsWith = '', + String contains = '', + String username = '', + }) async { + final response = await dio.get("/api/users/", queryParameters: { + "username__istartswith": startsWith, + "username__iendswith": endsWith, + "username__icontains": contains, + "username__iexact": username, + }); + if (response.statusCode == 200) { + return PagedSearchResult.fromJson( + response.data, + UserModelV3.fromJson as UserModel Function(Object?), + ).results; + } + throw const PaperlessServerException.unknown(); + } + + @override + Future findCurrentUserId() async { + final response = await dio.get("/api/ui_settings/"); + if (response.statusCode == 200) { + return response.data['user_id']; + } + throw const PaperlessServerException.unknown(); + } +} From 88085b566264c93ae3811f6ac57b9e6ff7dcf845 Mon Sep 17 00:00:00 2001 From: Anton Stubenbord Date: Sun, 30 Apr 2023 20:49:36 +0200 Subject: [PATCH 02/26] feat: Restructure providers and repositories (WIP, not compiling) --- .../database/tables/local_user_account.dart | 8 +- lib/core/factory/paperless_api_factory.dart | 10 + .../factory/paperless_api_factory_impl.dart | 35 ++ .../repository/saved_view_repository.dart | 5 + lib/core/security/session_manager.dart | 20 +- lib/features/home/view/home_page.dart | 58 +-- lib/features/home/view/home_route.dart | 144 +++++- lib/features/inbox/cubit/inbox_cubit.dart | 12 +- .../login/cubit/authentication_cubit.dart | 85 +--- .../cubit/authentication_cubit.freezed.dart | 465 ++++++++++++++++++ .../login/cubit/authentication_state.dart | 53 +- .../login/cubit/old_authentication_state.dart | 48 ++ .../settings/view/widgets/user_avatar.dart | 1 + lib/main.dart | 101 ++-- 14 files changed, 776 insertions(+), 269 deletions(-) create mode 100644 lib/core/factory/paperless_api_factory.dart create mode 100644 lib/core/factory/paperless_api_factory_impl.dart create mode 100644 lib/features/login/cubit/authentication_cubit.freezed.dart create mode 100644 lib/features/login/cubit/old_authentication_state.dart diff --git a/lib/core/database/tables/local_user_account.dart b/lib/core/database/tables/local_user_account.dart index c2228ce..9c44504 100644 --- a/lib/core/database/tables/local_user_account.dart +++ b/lib/core/database/tables/local_user_account.dart @@ -14,15 +14,15 @@ class LocalUserAccount extends HiveObject { final String id; @HiveField(4) - LocalUserSettings settings; + final LocalUserSettings settings; - @HiveField(5) - UserModel paperlessUser; + @HiveField(6) + final int paperlessUserId; LocalUserAccount({ required this.id, required this.serverUrl, required this.settings, - required this.paperlessUser, + required this.paperlessUserId, }); } diff --git a/lib/core/factory/paperless_api_factory.dart b/lib/core/factory/paperless_api_factory.dart new file mode 100644 index 0000000..a273f7f --- /dev/null +++ b/lib/core/factory/paperless_api_factory.dart @@ -0,0 +1,10 @@ +import 'package:dio/dio.dart'; +import 'package:paperless_api/paperless_api.dart'; + +abstract class PaperlessApiFactory { + PaperlessDocumentsApi createDocumentsApi(Dio dio, {required int apiVersion}); + PaperlessSavedViewsApi createSavedViewsApi(Dio dio, {required int apiVersion}); + PaperlessLabelsApi createLabelsApi(Dio dio, {required int apiVersion}); + PaperlessServerStatsApi createServerStatsApi(Dio dio, {required int apiVersion}); + PaperlessTasksApi createTasksApi(Dio dio, {required int apiVersion}); +} diff --git a/lib/core/factory/paperless_api_factory_impl.dart b/lib/core/factory/paperless_api_factory_impl.dart new file mode 100644 index 0000000..d4b0232 --- /dev/null +++ b/lib/core/factory/paperless_api_factory_impl.dart @@ -0,0 +1,35 @@ +import 'package:dio/dio.dart'; +import 'package:paperless_api/paperless_api.dart'; +import 'package:paperless_mobile/core/factory/paperless_api_factory.dart'; +import 'package:paperless_mobile/core/security/session_manager.dart'; + +class PaperlessApiFactoryImpl implements PaperlessApiFactory { + final SessionManager sessionManager; + + PaperlessApiFactoryImpl(this.sessionManager); + + @override + PaperlessDocumentsApi createDocumentsApi(Dio dio, {required int apiVersion}) { + return PaperlessDocumentsApiImpl(dio); + } + + @override + PaperlessLabelsApi createLabelsApi(Dio dio, {required int apiVersion}) { + return PaperlessLabelApiImpl(dio); + } + + @override + PaperlessSavedViewsApi createSavedViewsApi(Dio dio, {required int apiVersion}) { + return PaperlessSavedViewsApiImpl(dio); + } + + @override + PaperlessServerStatsApi createServerStatsApi(Dio dio, {required int apiVersion}) { + return PaperlessServerStatsApiImpl(dio); + } + + @override + PaperlessTasksApi createTasksApi(Dio dio, {required int apiVersion}) { + return PaperlessTasksApiImpl(dio); + } +} diff --git a/lib/core/repository/saved_view_repository.dart b/lib/core/repository/saved_view_repository.dart index b84cdda..54ae9fe 100644 --- a/lib/core/repository/saved_view_repository.dart +++ b/lib/core/repository/saved_view_repository.dart @@ -1,5 +1,6 @@ import 'dart:async'; +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/saved_view_repository_state.dart'; @@ -25,6 +26,10 @@ class SavedViewRepository extends HydratedCubit { SavedViewRepository(this._api) : super(const SavedViewRepositoryState()); + Future initialize() { + return findAll(); + } + Future create(SavedView object) async { final created = await _api.save(object); final updatedState = {...state.savedViews}..putIfAbsent(created.id!, () => created); diff --git a/lib/core/security/session_manager.dart b/lib/core/security/session_manager.dart index efca93b..a3e4ab5 100644 --- a/lib/core/security/session_manager.dart +++ b/lib/core/security/session_manager.dart @@ -2,7 +2,7 @@ import 'dart:io'; import 'package:dio/dio.dart'; import 'package:dio/io.dart'; -import 'package:paperless_api/paperless_api.dart'; +import 'package:flutter/material.dart'; import 'package:paperless_mobile/core/interceptor/retry_on_connection_change_interceptor.dart'; import 'package:paperless_mobile/features/login/model/client_certificate.dart'; import 'package:pretty_dio_logger/pretty_dio_logger.dart'; @@ -10,15 +10,10 @@ import 'package:pretty_dio_logger/pretty_dio_logger.dart'; /// Manages the security context, authentication and base request URL for /// an underlying [Dio] client which is injected into all services /// requiring authenticated access to the Paperless HTTP API. -class SessionManager { - final Dio _client; - PaperlessServerInformationModel _serverInformation; +class SessionManager extends ValueNotifier { + Dio get client => value; - Dio get client => _client; - - SessionManager([List interceptors = const []]) - : _client = _initDio(interceptors), - _serverInformation = PaperlessServerInformationModel(); + SessionManager([List interceptors = const []]) : super(_initDio(interceptors)); static Dio _initDio(List interceptors) { //en- and decoded by utf8 by default @@ -48,7 +43,6 @@ class SessionManager { String? baseUrl, String? authToken, ClientCertificate? clientCertificate, - PaperlessServerInformationModel? serverInformation, }) { if (clientCertificate != null) { final context = SecurityContext() @@ -81,15 +75,13 @@ class SessionManager { }); } - if (serverInformation != null) { - _serverInformation = serverInformation; - } + notifyListeners(); } void resetSettings() { client.httpClientAdapter = IOHttpClientAdapter(); client.options.baseUrl = ''; client.options.headers.remove(HttpHeaders.authorizationHeader); - _serverInformation = PaperlessServerInformationModel(); + notifyListeners(); } } diff --git a/lib/features/home/view/home_page.dart b/lib/features/home/view/home_page.dart index ffc5793..cf075fa 100644 --- a/lib/features/home/view/home_page.dart +++ b/lib/features/home/view/home_page.dart @@ -52,35 +52,12 @@ class HomePage extends StatefulWidget { class _HomePageState extends State with WidgetsBindingObserver { int _currentIndex = 0; - final DocumentScannerCubit _scannerCubit = DocumentScannerCubit(); - late final DocumentsCubit _documentsCubit; - late final InboxCubit _inboxCubit; - late final SavedViewCubit _savedViewCubit; late Timer _inboxTimer; @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); - _initializeData(context); - final userId = - Hive.box(HiveBoxes.globalSettings).getValue()!.currentLoggedInUser; - _documentsCubit = DocumentsCubit( - context.read(), - context.read(), - context.read(), - Hive.box(HiveBoxes.localUserAppState).get(userId)!, - )..reload(); - _savedViewCubit = SavedViewCubit( - context.read(), - context.read(), - )..reload(); - _inboxCubit = InboxCubit( - context.read(), - context.read(), - context.read(), - context.read(), - ); _listenToInboxChanges(); WidgetsBinding.instance.addPostFrameCallback((timeStamp) { _listenForReceivedFiles(); @@ -97,7 +74,7 @@ class _HomePageState extends State with WidgetsBindingObserver { if (!mounted) { timer.cancel(); } else { - _inboxCubit.refreshItemsInInboxCount(); + context.read().refreshItemsInInboxCount(); } }); } @@ -127,9 +104,6 @@ class _HomePageState extends State with WidgetsBindingObserver { void dispose() { WidgetsBinding.instance.removeObserver(this); _inboxTimer.cancel(); - _inboxCubit.close(); - _documentsCubit.close(); - _savedViewCubit.close(); super.dispose(); } @@ -247,7 +221,6 @@ class _HomePageState extends State with WidgetsBindingObserver { ), label: S.of(context)!.inbox, badgeBuilder: (icon) => BlocBuilder( - bloc: _inboxCubit, builder: (context, state) { if (state.itemsInInboxCount > 0) { return Badge.count( @@ -261,31 +234,10 @@ class _HomePageState extends State with WidgetsBindingObserver { ), ]; final routes = [ - MultiBlocProvider( - // key: ValueKey(userId), - providers: [ - BlocProvider.value(value: _documentsCubit), - BlocProvider.value(value: _savedViewCubit), - ], - child: const DocumentsPage(), - ), - BlocProvider.value( - value: _scannerCubit, - child: const ScannerPage(), - ), - MultiBlocProvider( - // key: ValueKey(userId), - providers: [ - BlocProvider( - create: (context) => LabelCubit(context.read()), - ) - ], - child: const LabelsPage(), - ), - BlocProvider.value( - value: _inboxCubit, - child: const InboxPage(), - ), + const DocumentsPage(), + const ScannerPage(), + const LabelsPage(), + const InboxPage(), ]; return MultiBlocListener( diff --git a/lib/features/home/view/home_route.dart b/lib/features/home/view/home_route.dart index 4f06c9b..eaa97e7 100644 --- a/lib/features/home/view/home_route.dart +++ b/lib/features/home/view/home_route.dart @@ -1,29 +1,139 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/src/widgets/framework.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_flutter/adapters.dart'; +import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_mobile/core/bloc/server_information_cubit.dart'; +import 'package:paperless_mobile/core/config/hive/hive_config.dart'; +import 'package:paperless_mobile/core/database/tables/local_user_app_state.dart'; +import 'package:paperless_mobile/core/factory/paperless_api_factory.dart'; +import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart'; +import 'package:paperless_mobile/core/repository/label_repository.dart'; +import 'package:paperless_mobile/core/repository/saved_view_repository.dart'; +import 'package:paperless_mobile/core/security/session_manager.dart'; +import 'package:paperless_mobile/core/service/dio_file_service.dart'; +import 'package:paperless_mobile/features/document_scan/cubit/document_scanner_cubit.dart'; +import 'package:paperless_mobile/features/documents/cubit/documents_cubit.dart'; import 'package:paperless_mobile/features/home/view/home_page.dart'; +import 'package:paperless_mobile/features/inbox/cubit/inbox_cubit.dart'; +import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart'; +import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.dart'; +import 'package:paperless_mobile/features/settings/view/widgets/global_settings_builder.dart'; import 'package:paperless_mobile/features/tasks/cubit/task_status_cubit.dart'; +import 'package:provider/provider.dart'; class HomeRoute extends StatelessWidget { - const HomeRoute({super.key}); + final String localUserId; + final int paperlessApiVersion; + final PaperlessApiFactory paperlessProviderFactory; + + const HomeRoute({ + super.key, + required this.paperlessApiVersion, + required this.paperlessProviderFactory, + required this.localUserId, + }); @override Widget build(BuildContext context) { - return MultiBlocProvider( - providers: [ - BlocProvider( - create: (context) => TaskStatusCubit( - context.read(), - ), - ), - BlocProvider( - create: (context) => ServerInformationCubit( - context.read(), - )..updateInformation(), - ), - ], - child: HomePage(), + return GlobalSettingsBuilder( + builder: (context, settings) { + final currentLocalUserId = settings.currentLoggedInUser!; + return MultiProvider( + providers: [ + Provider( + create: (context) => CacheManager( + Config( + localUserId, + fileService: DioFileService(context.read().client), + ), + ), + ), + ProxyProvider( + update: (context, value, previous) => paperlessProviderFactory.createDocumentsApi( + value.client, + apiVersion: paperlessApiVersion, + ), + ), + ProxyProvider( + update: (context, value, previous) => paperlessProviderFactory.createLabelsApi( + value.client, + apiVersion: paperlessApiVersion, + ), + ), + ProxyProvider( + update: (context, value, previous) => paperlessProviderFactory.createSavedViewsApi( + value.client, + apiVersion: paperlessApiVersion, + ), + ), + ProxyProvider( + update: (context, value, previous) => paperlessProviderFactory.createServerStatsApi( + value.client, + apiVersion: paperlessApiVersion, + ), + ), + ProxyProvider( + update: (context, value, previous) => paperlessProviderFactory.createTasksApi( + value.client, + apiVersion: paperlessApiVersion, + ), + ), + ], + builder: (context, child) { + return MultiProvider( + providers: [ + ProxyProvider( + update: (context, value, previous) => LabelRepository(value)..initialize(), + ), + ProxyProvider( + update: (context, value, previous) => SavedViewRepository(value)..initialize(), + ), + ], + builder: (context, child) { + return MultiBlocProvider( + providers: [ + ProxyProvider3( + update: (context, docApi, notifier, labelRepo, previous) => DocumentsCubit( + docApi, + notifier, + labelRepo, + Hive.box(HiveBoxes.localUserAppState) + .get(currentLocalUserId)!, + )..reload(), + ), + Provider(create: (context) => DocumentScannerCubit()), + ProxyProvider4( + update: (context, docApi, statsApi, labelRepo, notifier, previous) => + InboxCubit( + docApi, + statsApi, + labelRepo, + notifier, + )..initialize(), + ), + ProxyProvider2( + update: (context, savedViewRepo, labelRepo, previous) => SavedViewCubit( + savedViewRepo, + labelRepo, + )..initialize(), + ), + ProxyProvider( + update: (context, value, previous) => ServerInformationCubit(value), + ), + ProxyProvider( + update: (context, value, previous) => LabelCubit(value), + ), + ], + child: const HomePage(), + ); + }, + ); + }, + ); + }, ); } } diff --git a/lib/features/inbox/cubit/inbox_cubit.dart b/lib/features/inbox/cubit/inbox_cubit.dart index 3ed0188..3a5cf89 100644 --- a/lib/features/inbox/cubit/inbox_cubit.dart +++ b/lib/features/inbox/cubit/inbox_cubit.dart @@ -52,16 +52,18 @@ class InboxCubit extends HydratedCubit with DocumentPagingBlocMixin emit(state.copyWith(labels: labels)); }, ); - - refreshItemsInInboxCount(false); - loadInbox(); } - void refreshItemsInInboxCount([bool shouldLoadInbox = true]) async { + Future initialize() async { + await refreshItemsInInboxCount(false); + await loadInbox(); + } + + Future refreshItemsInInboxCount([bool shouldLoadInbox = true]) async { final stats = await _statsApi.getServerStatistics(); if (stats.documentsInInbox != state.itemsInInboxCount && shouldLoadInbox) { - loadInbox(); + await loadInbox(); } emit( state.copyWith( diff --git a/lib/features/login/cubit/authentication_cubit.dart b/lib/features/login/cubit/authentication_cubit.dart index fcd10a6..47e9f84 100644 --- a/lib/features/login/cubit/authentication_cubit.dart +++ b/lib/features/login/cubit/authentication_cubit.dart @@ -3,13 +3,12 @@ import 'dart:typed_data'; import 'package:equatable/equatable.dart'; import 'package:flutter/widgets.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hive_flutter/adapters.dart'; import 'package:hydrated_bloc/hydrated_bloc.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/local_user_app_state.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/security/session_manager.dart'; import 'package:paperless_mobile/features/login/model/client_certificate.dart'; import 'package:paperless_mobile/features/login/model/login_form_credentials.dart'; @@ -19,29 +18,22 @@ import 'package:paperless_mobile/features/login/services/authentication_service. import 'package:paperless_mobile/core/database/tables/global_settings.dart'; import 'package:paperless_mobile/core/database/tables/local_user_settings.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; + part 'authentication_state.dart'; +part 'authentication_cubit.freezed.dart'; class AuthenticationCubit extends Cubit { + final PaperlessUserApi _userApi; final LocalAuthenticationService _localAuthService; final PaperlessAuthenticationApi _authApi; final SessionManager _sessionManager; - final LabelRepository _labelRepository; - final SavedViewRepository _savedViewRepository; - final PaperlessServerStatsApi _serverStatsApi; - final PaperlessUserApi _userApi; - final PaperlessUserApiV3? _userApiV3; AuthenticationCubit( this._localAuthService, this._authApi, this._sessionManager, - this._labelRepository, - this._savedViewRepository, - this._serverStatsApi, - this._userApi, { - PaperlessUserApiV3? userApiV3, - }) : _userApiV3 = userApiV3, - super(const AuthenticationState()); + this._userApi, + ) : super(const AuthenticationState.unauthenticated()); Future login({ required LoginFormCredentials credentials, @@ -66,14 +58,10 @@ class AuthenticationCubit extends Cubit { final globalSettings = Hive.box(HiveBoxes.globalSettings).getValue()!; globalSettings.currentLoggedInUser = localUserId; await globalSettings.save(); - emit( - AuthenticationState( - isAuthenticated: true, - username: credentials.username, - localUserId: localUserId, - fullName: serverUser.fullName, + AuthenticationState.authenticated( apiVersion: apiVersion, + localUserId: localUserId, ), ); } @@ -116,26 +104,18 @@ class AuthenticationCubit extends Cubit { _sessionManager.updateSettings( authToken: credentials!.token, clientCertificate: credentials.clientCertificate, - serverInformation: PaperlessServerInformationModel(), baseUrl: account.serverUrl, ); - await _reloadRepositories(); globalSettings.currentLoggedInUser = localUserId; await globalSettings.save(); final response = await _sessionManager.client.get("/api/"); final apiVersion = response.headers["x-api-version"] as int; - - emit( - AuthenticationState( - isAuthenticated: true, - username: account.paperlessUser.username, - fullName: account.paperlessUser.fullName, - localUserId: localUserId, - apiVersion: apiVersion, - ), - ); + emit(AuthenticationState.authenticated( + localUserId: localUserId, + apiVersion: apiVersion, + )); } Future addAccount({ @@ -179,19 +159,19 @@ class AuthenticationCubit extends Cubit { /// Future restoreSessionState() async { final globalSettings = Hive.box(HiveBoxes.globalSettings).getValue()!; - final userId = globalSettings.currentLoggedInUser; - if (userId == null) { + final localUserId = globalSettings.currentLoggedInUser; + if (localUserId == null) { // If there is nothing to restore, we can quit here. return; } - final userAccount = Hive.box(HiveBoxes.localUserAccount).get(userId)!; + final userAccount = Hive.box(HiveBoxes.localUserAccount).get(localUserId)!; if (userAccount.settings.isBiometricAuthenticationEnabled) { final localAuthSuccess = await _localAuthService.authenticateLocalUser("Authenticate to log back in"); //TODO: INTL if (!localAuthSuccess) { - emit(const AuthenticationState(showBiometricAuthenticationScreen: true)); + emit(const AuthenticationState.requriresLocalAuthentication()); return; } } @@ -207,18 +187,13 @@ class AuthenticationCubit extends Cubit { clientCertificate: authentication.clientCertificate, authToken: authentication.token, baseUrl: userAccount.serverUrl, - serverInformation: PaperlessServerInformationModel(), ); final response = await _sessionManager.client.get("/api/"); final apiVersion = response.headers["x-api-version"] as int; - emit( - AuthenticationState( - isAuthenticated: true, - showBiometricAuthenticationScreen: false, - username: userAccount.paperlessUser.username, + AuthenticationState.authenticated( apiVersion: apiVersion, - fullName: userAccount.paperlessUser.fullName, + localUserId: localUserId, ), ); } @@ -229,7 +204,7 @@ class AuthenticationCubit extends Cubit { globalSettings ..currentLoggedInUser = null ..save(); - emit(const AuthenticationState()); + emit(const AuthenticationState.unauthenticated()); } Future _getEncryptedBoxKey() async { @@ -254,23 +229,12 @@ class AuthenticationCubit extends Cubit { ); } - Future _resetExternalState() { + Future _resetExternalState() async { _sessionManager.resetSettings(); - return Future.wait([ - HydratedBloc.storage.clear(), - _labelRepository.clear(), - _savedViewRepository.clear(), - ]); + await HydratedBloc.storage.clear(); } - Future _reloadRepositories() { - return Future.wait([ - _labelRepository.initialize(), - _savedViewRepository.findAll(), - ]); - } - - Future _addUser( + Future _addUser( String localUserId, String serverUrl, LoginFormCredentials credentials, @@ -303,7 +267,6 @@ class AuthenticationCubit extends Cubit { } final serverUserId = await _userApi.findCurrentUserId(); - final serverUser = await _userApi.find(serverUserId); // Create user account await userAccountBox.put( @@ -312,7 +275,7 @@ class AuthenticationCubit extends Cubit { id: localUserId, settings: LocalUserSettings(), serverUrl: serverUrl, - paperlessUser: serverUser, + paperlessUserId: 1, ), ); @@ -332,6 +295,6 @@ class AuthenticationCubit extends Cubit { ), ); userCredentialsBox.close(); - return serverUser; + return serverUserId; } } diff --git a/lib/features/login/cubit/authentication_cubit.freezed.dart b/lib/features/login/cubit/authentication_cubit.freezed.dart new file mode 100644 index 0000000..629ef40 --- /dev/null +++ b/lib/features/login/cubit/authentication_cubit.freezed.dart @@ -0,0 +1,465 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'authentication_cubit.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + +/// @nodoc +mixin _$AuthenticationState { + @optionalTypeArgs + TResult when({ + required TResult Function() unauthenticated, + required TResult Function() requriresLocalAuthentication, + required TResult Function(String localUserId, int apiVersion) authenticated, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? unauthenticated, + TResult? Function()? requriresLocalAuthentication, + TResult? Function(String localUserId, int apiVersion)? authenticated, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? unauthenticated, + TResult Function()? requriresLocalAuthentication, + TResult Function(String localUserId, int apiVersion)? authenticated, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map({ + required TResult Function(_Unauthenticated value) unauthenticated, + required TResult Function(_RequiresLocalAuthentication value) + requriresLocalAuthentication, + required TResult Function(_Authenticated value) authenticated, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_Unauthenticated value)? unauthenticated, + TResult? Function(_RequiresLocalAuthentication value)? + requriresLocalAuthentication, + TResult? Function(_Authenticated value)? authenticated, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_Unauthenticated value)? unauthenticated, + TResult Function(_RequiresLocalAuthentication value)? + requriresLocalAuthentication, + TResult Function(_Authenticated value)? authenticated, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $AuthenticationStateCopyWith<$Res> { + factory $AuthenticationStateCopyWith( + AuthenticationState value, $Res Function(AuthenticationState) then) = + _$AuthenticationStateCopyWithImpl<$Res, AuthenticationState>; +} + +/// @nodoc +class _$AuthenticationStateCopyWithImpl<$Res, $Val extends AuthenticationState> + implements $AuthenticationStateCopyWith<$Res> { + _$AuthenticationStateCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; +} + +/// @nodoc +abstract class _$$_UnauthenticatedCopyWith<$Res> { + factory _$$_UnauthenticatedCopyWith( + _$_Unauthenticated value, $Res Function(_$_Unauthenticated) then) = + __$$_UnauthenticatedCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$_UnauthenticatedCopyWithImpl<$Res> + extends _$AuthenticationStateCopyWithImpl<$Res, _$_Unauthenticated> + implements _$$_UnauthenticatedCopyWith<$Res> { + __$$_UnauthenticatedCopyWithImpl( + _$_Unauthenticated _value, $Res Function(_$_Unauthenticated) _then) + : super(_value, _then); +} + +/// @nodoc + +class _$_Unauthenticated implements _Unauthenticated { + const _$_Unauthenticated(); + + @override + String toString() { + return 'AuthenticationState.unauthenticated()'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$_Unauthenticated); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() unauthenticated, + required TResult Function() requriresLocalAuthentication, + required TResult Function(String localUserId, int apiVersion) authenticated, + }) { + return unauthenticated(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? unauthenticated, + TResult? Function()? requriresLocalAuthentication, + TResult? Function(String localUserId, int apiVersion)? authenticated, + }) { + return unauthenticated?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? unauthenticated, + TResult Function()? requriresLocalAuthentication, + TResult Function(String localUserId, int apiVersion)? authenticated, + required TResult orElse(), + }) { + if (unauthenticated != null) { + return unauthenticated(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_Unauthenticated value) unauthenticated, + required TResult Function(_RequiresLocalAuthentication value) + requriresLocalAuthentication, + required TResult Function(_Authenticated value) authenticated, + }) { + return unauthenticated(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_Unauthenticated value)? unauthenticated, + TResult? Function(_RequiresLocalAuthentication value)? + requriresLocalAuthentication, + TResult? Function(_Authenticated value)? authenticated, + }) { + return unauthenticated?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_Unauthenticated value)? unauthenticated, + TResult Function(_RequiresLocalAuthentication value)? + requriresLocalAuthentication, + TResult Function(_Authenticated value)? authenticated, + required TResult orElse(), + }) { + if (unauthenticated != null) { + return unauthenticated(this); + } + return orElse(); + } +} + +abstract class _Unauthenticated implements AuthenticationState { + const factory _Unauthenticated() = _$_Unauthenticated; +} + +/// @nodoc +abstract class _$$_RequiresLocalAuthenticationCopyWith<$Res> { + factory _$$_RequiresLocalAuthenticationCopyWith( + _$_RequiresLocalAuthentication value, + $Res Function(_$_RequiresLocalAuthentication) then) = + __$$_RequiresLocalAuthenticationCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$_RequiresLocalAuthenticationCopyWithImpl<$Res> + extends _$AuthenticationStateCopyWithImpl<$Res, + _$_RequiresLocalAuthentication> + implements _$$_RequiresLocalAuthenticationCopyWith<$Res> { + __$$_RequiresLocalAuthenticationCopyWithImpl( + _$_RequiresLocalAuthentication _value, + $Res Function(_$_RequiresLocalAuthentication) _then) + : super(_value, _then); +} + +/// @nodoc + +class _$_RequiresLocalAuthentication implements _RequiresLocalAuthentication { + const _$_RequiresLocalAuthentication(); + + @override + String toString() { + return 'AuthenticationState.requriresLocalAuthentication()'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$_RequiresLocalAuthentication); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() unauthenticated, + required TResult Function() requriresLocalAuthentication, + required TResult Function(String localUserId, int apiVersion) authenticated, + }) { + return requriresLocalAuthentication(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? unauthenticated, + TResult? Function()? requriresLocalAuthentication, + TResult? Function(String localUserId, int apiVersion)? authenticated, + }) { + return requriresLocalAuthentication?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? unauthenticated, + TResult Function()? requriresLocalAuthentication, + TResult Function(String localUserId, int apiVersion)? authenticated, + required TResult orElse(), + }) { + if (requriresLocalAuthentication != null) { + return requriresLocalAuthentication(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_Unauthenticated value) unauthenticated, + required TResult Function(_RequiresLocalAuthentication value) + requriresLocalAuthentication, + required TResult Function(_Authenticated value) authenticated, + }) { + return requriresLocalAuthentication(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_Unauthenticated value)? unauthenticated, + TResult? Function(_RequiresLocalAuthentication value)? + requriresLocalAuthentication, + TResult? Function(_Authenticated value)? authenticated, + }) { + return requriresLocalAuthentication?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_Unauthenticated value)? unauthenticated, + TResult Function(_RequiresLocalAuthentication value)? + requriresLocalAuthentication, + TResult Function(_Authenticated value)? authenticated, + required TResult orElse(), + }) { + if (requriresLocalAuthentication != null) { + return requriresLocalAuthentication(this); + } + return orElse(); + } +} + +abstract class _RequiresLocalAuthentication implements AuthenticationState { + const factory _RequiresLocalAuthentication() = _$_RequiresLocalAuthentication; +} + +/// @nodoc +abstract class _$$_AuthenticatedCopyWith<$Res> { + factory _$$_AuthenticatedCopyWith( + _$_Authenticated value, $Res Function(_$_Authenticated) then) = + __$$_AuthenticatedCopyWithImpl<$Res>; + @useResult + $Res call({String localUserId, int apiVersion}); +} + +/// @nodoc +class __$$_AuthenticatedCopyWithImpl<$Res> + extends _$AuthenticationStateCopyWithImpl<$Res, _$_Authenticated> + implements _$$_AuthenticatedCopyWith<$Res> { + __$$_AuthenticatedCopyWithImpl( + _$_Authenticated _value, $Res Function(_$_Authenticated) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? localUserId = null, + Object? apiVersion = null, + }) { + return _then(_$_Authenticated( + localUserId: null == localUserId + ? _value.localUserId + : localUserId // ignore: cast_nullable_to_non_nullable + as String, + apiVersion: null == apiVersion + ? _value.apiVersion + : apiVersion // ignore: cast_nullable_to_non_nullable + as int, + )); + } +} + +/// @nodoc + +class _$_Authenticated implements _Authenticated { + const _$_Authenticated({required this.localUserId, required this.apiVersion}); + + @override + final String localUserId; + @override + final int apiVersion; + + @override + String toString() { + return 'AuthenticationState.authenticated(localUserId: $localUserId, apiVersion: $apiVersion)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$_Authenticated && + (identical(other.localUserId, localUserId) || + other.localUserId == localUserId) && + (identical(other.apiVersion, apiVersion) || + other.apiVersion == apiVersion)); + } + + @override + int get hashCode => Object.hash(runtimeType, localUserId, apiVersion); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$_AuthenticatedCopyWith<_$_Authenticated> get copyWith => + __$$_AuthenticatedCopyWithImpl<_$_Authenticated>(this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() unauthenticated, + required TResult Function() requriresLocalAuthentication, + required TResult Function(String localUserId, int apiVersion) authenticated, + }) { + return authenticated(localUserId, apiVersion); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? unauthenticated, + TResult? Function()? requriresLocalAuthentication, + TResult? Function(String localUserId, int apiVersion)? authenticated, + }) { + return authenticated?.call(localUserId, apiVersion); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? unauthenticated, + TResult Function()? requriresLocalAuthentication, + TResult Function(String localUserId, int apiVersion)? authenticated, + required TResult orElse(), + }) { + if (authenticated != null) { + return authenticated(localUserId, apiVersion); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_Unauthenticated value) unauthenticated, + required TResult Function(_RequiresLocalAuthentication value) + requriresLocalAuthentication, + required TResult Function(_Authenticated value) authenticated, + }) { + return authenticated(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_Unauthenticated value)? unauthenticated, + TResult? Function(_RequiresLocalAuthentication value)? + requriresLocalAuthentication, + TResult? Function(_Authenticated value)? authenticated, + }) { + return authenticated?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_Unauthenticated value)? unauthenticated, + TResult Function(_RequiresLocalAuthentication value)? + requriresLocalAuthentication, + TResult Function(_Authenticated value)? authenticated, + required TResult orElse(), + }) { + if (authenticated != null) { + return authenticated(this); + } + return orElse(); + } +} + +abstract class _Authenticated implements AuthenticationState { + const factory _Authenticated( + {required final String localUserId, + required final int apiVersion}) = _$_Authenticated; + + String get localUserId; + int get apiVersion; + @JsonKey(ignore: true) + _$$_AuthenticatedCopyWith<_$_Authenticated> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/features/login/cubit/authentication_state.dart b/lib/features/login/cubit/authentication_state.dart index 8b76cd6..21c886f 100644 --- a/lib/features/login/cubit/authentication_state.dart +++ b/lib/features/login/cubit/authentication_state.dart @@ -1,48 +1,11 @@ part of 'authentication_cubit.dart'; -class AuthenticationState with EquatableMixin { - final bool showBiometricAuthenticationScreen; - final bool isAuthenticated; - final String? username; - final String? fullName; - final String? localUserId; - final int? apiVersion; - - const AuthenticationState({ - this.isAuthenticated = false, - this.showBiometricAuthenticationScreen = false, - this.username, - this.fullName, - this.localUserId, - this.apiVersion, - }); - - AuthenticationState copyWith({ - bool? isAuthenticated, - bool? showBiometricAuthenticationScreen, - String? username, - String? fullName, - String? localUserId, - int? apiVersion, - }) { - return AuthenticationState( - isAuthenticated: isAuthenticated ?? this.isAuthenticated, - showBiometricAuthenticationScreen: - showBiometricAuthenticationScreen ?? this.showBiometricAuthenticationScreen, - username: username ?? this.username, - fullName: fullName ?? this.fullName, - localUserId: localUserId ?? this.localUserId, - apiVersion: apiVersion ?? this.apiVersion, - ); - } - - @override - List get props => [ - localUserId, - username, - fullName, - isAuthenticated, - showBiometricAuthenticationScreen, - apiVersion, - ]; +@freezed +class AuthenticationState with _$AuthenticationState { + const factory AuthenticationState.unauthenticated() = _Unauthenticated; + const factory AuthenticationState.requriresLocalAuthentication() = _RequiresLocalAuthentication; + const factory AuthenticationState.authenticated({ + required String localUserId, + required int apiVersion, + }) = _Authenticated; } diff --git a/lib/features/login/cubit/old_authentication_state.dart b/lib/features/login/cubit/old_authentication_state.dart new file mode 100644 index 0000000..6cdf17c --- /dev/null +++ b/lib/features/login/cubit/old_authentication_state.dart @@ -0,0 +1,48 @@ +import 'package:equatable/equatable.dart'; + +class OldAuthenticationState with EquatableMixin { + final bool showBiometricAuthenticationScreen; + final bool isAuthenticated; + final String? username; + final String? fullName; + final String? localUserId; + final int? apiVersion; + + const OldAuthenticationState({ + this.isAuthenticated = false, + this.showBiometricAuthenticationScreen = false, + this.username, + this.fullName, + this.localUserId, + this.apiVersion, + }); + + OldAuthenticationState copyWith({ + bool? isAuthenticated, + bool? showBiometricAuthenticationScreen, + String? username, + String? fullName, + String? localUserId, + int? apiVersion, + }) { + return OldAuthenticationState( + isAuthenticated: isAuthenticated ?? this.isAuthenticated, + showBiometricAuthenticationScreen: + showBiometricAuthenticationScreen ?? this.showBiometricAuthenticationScreen, + username: username ?? this.username, + fullName: fullName ?? this.fullName, + localUserId: localUserId ?? this.localUserId, + apiVersion: apiVersion ?? this.apiVersion, + ); + } + + @override + List get props => [ + localUserId, + username, + fullName, + isAuthenticated, + showBiometricAuthenticationScreen, + apiVersion, + ]; +} diff --git a/lib/features/settings/view/widgets/user_avatar.dart b/lib/features/settings/view/widgets/user_avatar.dart index 82a8356..8c11053 100644 --- a/lib/features/settings/view/widgets/user_avatar.dart +++ b/lib/features/settings/view/widgets/user_avatar.dart @@ -4,6 +4,7 @@ import 'package:paperless_mobile/core/database/tables/local_user_account.dart'; class UserAvatar extends StatelessWidget { final String userId; final LocalUserAccount account; + const UserAvatar({ super.key, required this.userId, diff --git a/lib/main.dart b/lib/main.dart index f8f9e1d..cd7e1e5 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -21,6 +21,8 @@ import 'package:paperless_mobile/core/config/hive/hive_config.dart'; import 'package:paperless_mobile/core/database/tables/global_settings.dart'; import 'package:paperless_mobile/core/database/tables/local_user_account.dart'; import 'package:paperless_mobile/core/database/tables/local_user_app_state.dart'; +import 'package:paperless_mobile/core/factory/paperless_api_factory.dart'; +import 'package:paperless_mobile/core/factory/paperless_api_factory_impl.dart'; import 'package:paperless_mobile/core/interceptor/dio_http_error_interceptor.dart'; import 'package:paperless_mobile/core/interceptor/language_header.interceptor.dart'; import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart'; @@ -89,10 +91,8 @@ void main() async { iosInfo = await DeviceInfoPlugin().iosInfo; } - // Initialize External dependencies final connectivity = Connectivity(); final localAuthentication = LocalAuthentication(); - // Initialize other utility classes final connectivityStatusService = ConnectivityStatusServiceImpl(connectivity); final localAuthService = LocalAuthenticationService(localAuthentication); @@ -111,40 +111,12 @@ void main() async { languageHeaderInterceptor, ]); - // Initialize Paperless APIs - final authApi = PaperlessAuthenticationApiImpl(sessionManager.client); - final documentsApi = PaperlessDocumentsApiImpl(sessionManager.client); - final labelsApi = PaperlessLabelApiImpl(sessionManager.client); - final statsApi = PaperlessServerStatsApiImpl(sessionManager.client); - final savedViewsApi = PaperlessSavedViewsApiImpl(sessionManager.client); - final tasksApi = PaperlessTasksApiImpl( - sessionManager.client, - ); - // Initialize Blocs/Cubits final connectivityCubit = ConnectivityCubit(connectivityStatusService); // Load application settings and stored authentication data await connectivityCubit.initialize(); - // Create repositories - final labelRepository = LabelRepository(labelsApi); - final savedViewRepository = SavedViewRepository(savedViewsApi); - - //Create cubits/blocs - final authCubit = AuthenticationCubit( - localAuthService, - authApi, - sessionManager, - labelRepository, - savedViewRepository, - statsApi, - ); - - if (globalSettings.currentLoggedInUser != null) { - await authCubit.restoreSessionState(); - } - final localNotificationService = LocalNotificationService(); await localNotificationService.initialize(); @@ -156,42 +128,20 @@ void main() async { runApp( MultiProvider( providers: [ + ChangeNotifierProvider.value(value: sessionManager), Provider.value(value: localAuthService), - Provider.value(value: authApi), - Provider.value(value: documentsApi), - Provider.value(value: labelsApi), - Provider.value(value: statsApi), - Provider.value(value: savedViewsApi), - Provider.value(value: tasksApi), - Provider( - create: (context) => cm.CacheManager( - cm.Config( - 'cacheKey', - fileService: DioFileService(sessionManager.client), - ), - ), - ), Provider.value( value: connectivityStatusService, ), Provider.value(value: localNotificationService), Provider.value(value: DocumentChangedNotifier()), ], - child: MultiRepositoryProvider( + child: MultiBlocProvider( providers: [ - RepositoryProvider.value( - value: labelRepository, - ), - RepositoryProvider.value( - value: savedViewRepository, - ), + BlocProvider.value(value: connectivityCubit), ], - child: MultiBlocProvider( - providers: [ - BlocProvider.value(value: authCubit), - BlocProvider.value(value: connectivityCubit), - ], - child: const PaperlessMobileEntrypoint(), + child: PaperlessMobileEntrypoint( + paperlessProviderFactory: PaperlessApiFactoryImpl(sessionManager), ), ), ), @@ -199,8 +149,10 @@ void main() async { } class PaperlessMobileEntrypoint extends StatefulWidget { + final PaperlessApiFactory paperlessProviderFactory; const PaperlessMobileEntrypoint({ Key? key, + required this.paperlessProviderFactory, }) : super(key: key); @override @@ -238,7 +190,9 @@ class _PaperlessMobileEntrypointState extends State { routes: { DocumentDetailsRoute.routeName: (context) => const DocumentDetailsRoute(), }, - home: const AuthenticationWrapper(), + home: AuthenticationWrapper( + paperlessProviderFactory: widget.paperlessProviderFactory, + ), ); }, ); @@ -248,7 +202,12 @@ class _PaperlessMobileEntrypointState extends State { } class AuthenticationWrapper extends StatefulWidget { - const AuthenticationWrapper({Key? key}) : super(key: key); + final PaperlessApiFactory paperlessProviderFactory; + + const AuthenticationWrapper({ + Key? key, + required this.paperlessProviderFactory, + }) : super(key: key); @override State createState() => _AuthenticationWrapperState(); @@ -295,17 +254,19 @@ class _AuthenticationWrapperState extends State { Widget build(BuildContext context) { return BlocBuilder( builder: (context, authentication) { - if (authentication.isAuthenticated) { - return HomeRoute( - key: ValueKey(authentication.localUserId), - ); - } else if (authentication.showBiometricAuthenticationScreen) { - return const VerifyIdentityPage(); - } - return LoginPage( - titleString: S.of(context)!.connectToPaperless, - submitText: S.of(context)!.signIn, - onSubmit: _onLogin, + return authentication.when( + unauthenticated: () => LoginPage( + titleString: S.of(context)!.connectToPaperless, + submitText: S.of(context)!.signIn, + onSubmit: _onLogin, + ), + requriresLocalAuthentication: () => const VerifyIdentityPage(), + authenticated: (localUserId, apiVersion) => HomeRoute( + key: ValueKey(localUserId), + paperlessApiVersion: apiVersion, + paperlessProviderFactory: widget.paperlessProviderFactory, + localUserId: localUserId, + ), ); }, ); From d5c68e023cd00ffe844b2dcf1756e50c60c505c8 Mon Sep 17 00:00:00 2001 From: Anton Stubenbord Date: Mon, 1 May 2023 23:05:54 +0200 Subject: [PATCH 03/26] feat: More resturcturings, adapt code to previous changes --- .../database/tables/local_user_account.dart | 6 +- lib/core/factory/paperless_api_factory.dart | 2 + .../factory/paperless_api_factory_impl.dart | 15 ++ lib/core/repository/label_repository.dart | 27 +-- .../repository/persistent_repository.dart | 32 ++++ .../repository/saved_view_repository.dart | 29 +--- lib/core/repository/user_repository.dart | 40 +++++ .../repository/user_repository.freezed.dart | 161 ++++++++++++++++++ .../repository/user_repository_state.dart | 11 ++ .../view/pages/document_details_page.dart | 19 +-- .../widgets/document_download_button.dart | 2 - .../document_scan/view/scanner_page.dart | 18 +- .../view/document_search_page.dart | 9 +- .../view/sliver_search_bar.dart | 5 +- .../document_upload_preparation_page.dart | 1 + .../documents/view/pages/documents_page.dart | 8 +- .../view/widgets/document_preview.dart | 1 - lib/features/home/view/home_route.dart | 12 +- .../inbox/view/widgets/inbox_item.dart | 11 +- .../view/linked_documents_page.dart | 9 +- .../login/cubit/authentication_cubit.dart | 45 +++-- .../saved_view/cubit/saved_view_cubit.dart | 27 +-- .../view/saved_view_details_page.dart | 11 +- .../settings/view/manage_accounts_page.dart | 6 +- lib/features/settings/view/settings_page.dart | 2 +- .../settings/view/widgets/user_avatar.dart | 5 +- .../view/similar_documents_view.dart | 19 +-- lib/main.dart | 24 ++- lib/routes/document_details_route.dart | 68 ++++++-- .../lib/src/models/user_model.dart | 2 +- .../paperless_server_stats_api_impl.dart | 7 +- .../modules/user_api/paperless_user_api.dart | 2 +- .../user_api/paperless_user_api_v2_impl.dart | 5 +- .../user_api/paperless_user_api_v3.dart | 4 +- .../user_api/paperless_user_api_v3_impl.dart | 30 +++- 35 files changed, 475 insertions(+), 200 deletions(-) create mode 100644 lib/core/repository/persistent_repository.dart create mode 100644 lib/core/repository/user_repository.dart create mode 100644 lib/core/repository/user_repository.freezed.dart create mode 100644 lib/core/repository/user_repository_state.dart diff --git a/lib/core/database/tables/local_user_account.dart b/lib/core/database/tables/local_user_account.dart index 9c44504..256dd02 100644 --- a/lib/core/database/tables/local_user_account.dart +++ b/lib/core/database/tables/local_user_account.dart @@ -16,13 +16,13 @@ class LocalUserAccount extends HiveObject { @HiveField(4) final LocalUserSettings settings; - @HiveField(6) - final int paperlessUserId; + @HiveField(7) + final UserModel paperlessUser; LocalUserAccount({ required this.id, required this.serverUrl, required this.settings, - required this.paperlessUserId, + required this.paperlessUser, }); } diff --git a/lib/core/factory/paperless_api_factory.dart b/lib/core/factory/paperless_api_factory.dart index a273f7f..4295977 100644 --- a/lib/core/factory/paperless_api_factory.dart +++ b/lib/core/factory/paperless_api_factory.dart @@ -7,4 +7,6 @@ abstract class PaperlessApiFactory { PaperlessLabelsApi createLabelsApi(Dio dio, {required int apiVersion}); PaperlessServerStatsApi createServerStatsApi(Dio dio, {required int apiVersion}); PaperlessTasksApi createTasksApi(Dio dio, {required int apiVersion}); + PaperlessAuthenticationApi createAuthenticationApi(Dio dio); + PaperlessUserApi createUserApi(Dio dio, {required int apiVersion}); } diff --git a/lib/core/factory/paperless_api_factory_impl.dart b/lib/core/factory/paperless_api_factory_impl.dart index d4b0232..40b718f 100644 --- a/lib/core/factory/paperless_api_factory_impl.dart +++ b/lib/core/factory/paperless_api_factory_impl.dart @@ -32,4 +32,19 @@ class PaperlessApiFactoryImpl implements PaperlessApiFactory { PaperlessTasksApi createTasksApi(Dio dio, {required int apiVersion}) { return PaperlessTasksApiImpl(dio); } + + @override + PaperlessAuthenticationApi createAuthenticationApi(Dio dio) { + return PaperlessAuthenticationApiImpl(dio); + } + + @override + PaperlessUserApi createUserApi(Dio dio, {required int apiVersion}) { + if (apiVersion == 3) { + return PaperlessUserApiV3Impl(dio); + } else if (apiVersion == 1 || apiVersion == 2) { + return PaperlessUserApiV2Impl(dio); + } + throw Exception("API $apiVersion not supported."); + } } diff --git a/lib/core/repository/label_repository.dart b/lib/core/repository/label_repository.dart index b497bb2..b57b19f 100644 --- a/lib/core/repository/label_repository.dart +++ b/lib/core/repository/label_repository.dart @@ -4,28 +4,13 @@ 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'; -class LabelRepository extends HydratedCubit { +class LabelRepository extends PersistentRepository { final PaperlessLabelsApi _api; - final Map _subscribers = {}; LabelRepository(this._api) : super(const LabelRepositoryState()); - void addListener( - Object source, { - required void Function(LabelRepositoryState) onChanged, - }) { - onChanged(state); - _subscribers.putIfAbsent(source, () { - return stream.listen((event) => onChanged(event)); - }); - } - - void removeListener(Object source) async { - await _subscribers[source]?.cancel(); - _subscribers.remove(source); - } - Future initialize() { debugPrint("Initializing labels..."); return Future.wait([ @@ -195,14 +180,6 @@ class LabelRepository extends HydratedCubit { return updated; } - @override - Future close() { - _subscribers.forEach((key, subscription) { - subscription.cancel(); - }); - return super.close(); - } - @override Future clear() async { await super.clear(); diff --git a/lib/core/repository/persistent_repository.dart b/lib/core/repository/persistent_repository.dart new file mode 100644 index 0000000..db63c13 --- /dev/null +++ b/lib/core/repository/persistent_repository.dart @@ -0,0 +1,32 @@ +import 'dart:async'; + +import 'package:hydrated_bloc/hydrated_bloc.dart'; + +abstract class PersistentRepository extends HydratedCubit { + final Map _subscribers = {}; + + PersistentRepository(T initialState) : super(initialState); + + void addListener( + Object source, { + required void Function(T) onChanged, + }) { + onChanged(state); + _subscribers.putIfAbsent(source, () { + return stream.listen((event) => onChanged(event)); + }); + } + + void removeListener(Object source) async { + await _subscribers[source]?.cancel(); + _subscribers.remove(source); + } + + @override + Future close() { + _subscribers.forEach((key, subscription) { + subscription.cancel(); + }); + return super.close(); + } +} diff --git a/lib/core/repository/saved_view_repository.dart b/lib/core/repository/saved_view_repository.dart index 54ae9fe..d458358 100644 --- a/lib/core/repository/saved_view_repository.dart +++ b/lib/core/repository/saved_view_repository.dart @@ -1,28 +1,11 @@ import 'dart:async'; -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/persistent_repository.dart'; import 'package:paperless_mobile/core/repository/saved_view_repository_state.dart'; -class SavedViewRepository extends HydratedCubit { +class SavedViewRepository extends PersistentRepository { final PaperlessSavedViewsApi _api; - final Map _subscribers = {}; - - void subscribe( - Object source, - void Function(Map) onChanged, - ) { - _subscribers.putIfAbsent(source, () { - onChanged(state.savedViews); - return stream.listen((event) => onChanged(event.savedViews)); - }); - } - - void unsubscribe(Object source) async { - await _subscribers[source]?.cancel(); - _subscribers.remove(source); - } SavedViewRepository(this._api) : super(const SavedViewRepositoryState()); @@ -63,14 +46,6 @@ class SavedViewRepository extends HydratedCubit { return found; } - @override - Future close() { - _subscribers.forEach((key, subscription) { - subscription.cancel(); - }); - return super.close(); - } - @override Future clear() async { await super.clear(); diff --git a/lib/core/repository/user_repository.dart b/lib/core/repository/user_repository.dart new file mode 100644 index 0000000..b1748c3 --- /dev/null +++ b/lib/core/repository/user_repository.dart @@ -0,0 +1,40 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:paperless_api/paperless_api.dart'; +import 'package:paperless_mobile/core/repository/persistent_repository.dart'; + +part 'user_repository_state.dart'; +part 'user_repository.freezed.dart'; +part 'user_repository.g.dart'; + +/// Repository for new users (API v3, server version 1.14.2+) +class UserRepository extends PersistentRepository { + final PaperlessUserApiV3 _userApiV3; + + UserRepository(this._userApiV3) : super(const UserRepositoryState()); + + Future initialize() async { + await findAll(); + } + + Future> findAll() async { + final users = await _userApiV3.findAll(); + emit(state.copyWith(users: {for (var e in users) e.id: e})); + return users; + } + + Future find(int id) async { + final user = await _userApiV3.find(id); + emit(state.copyWith(users: state.users..[id] = user)); + return user; + } + + @override + UserRepositoryState? fromJson(Map json) { + return UserRepositoryState.fromJson(json); + } + + @override + Map? toJson(UserRepositoryState state) { + return state.toJson(); + } +} diff --git a/lib/core/repository/user_repository.freezed.dart b/lib/core/repository/user_repository.freezed.dart new file mode 100644 index 0000000..e8b8b71 --- /dev/null +++ b/lib/core/repository/user_repository.freezed.dart @@ -0,0 +1,161 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'user_repository.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + +UserRepositoryState _$UserRepositoryStateFromJson(Map json) { + return _UserRepositoryState.fromJson(json); +} + +/// @nodoc +mixin _$UserRepositoryState { + Map get users => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $UserRepositoryStateCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $UserRepositoryStateCopyWith<$Res> { + factory $UserRepositoryStateCopyWith( + UserRepositoryState value, $Res Function(UserRepositoryState) then) = + _$UserRepositoryStateCopyWithImpl<$Res, UserRepositoryState>; + @useResult + $Res call({Map users}); +} + +/// @nodoc +class _$UserRepositoryStateCopyWithImpl<$Res, $Val extends UserRepositoryState> + implements $UserRepositoryStateCopyWith<$Res> { + _$UserRepositoryStateCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? users = null, + }) { + return _then(_value.copyWith( + users: null == users + ? _value.users + : users // ignore: cast_nullable_to_non_nullable + as Map, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$_UserRepositoryStateCopyWith<$Res> + implements $UserRepositoryStateCopyWith<$Res> { + factory _$$_UserRepositoryStateCopyWith(_$_UserRepositoryState value, + $Res Function(_$_UserRepositoryState) then) = + __$$_UserRepositoryStateCopyWithImpl<$Res>; + @override + @useResult + $Res call({Map users}); +} + +/// @nodoc +class __$$_UserRepositoryStateCopyWithImpl<$Res> + extends _$UserRepositoryStateCopyWithImpl<$Res, _$_UserRepositoryState> + implements _$$_UserRepositoryStateCopyWith<$Res> { + __$$_UserRepositoryStateCopyWithImpl(_$_UserRepositoryState _value, + $Res Function(_$_UserRepositoryState) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? users = null, + }) { + return _then(_$_UserRepositoryState( + users: null == users + ? _value._users + : users // ignore: cast_nullable_to_non_nullable + as Map, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$_UserRepositoryState implements _UserRepositoryState { + const _$_UserRepositoryState({final Map users = const {}}) + : _users = users; + + factory _$_UserRepositoryState.fromJson(Map json) => + _$$_UserRepositoryStateFromJson(json); + + final Map _users; + @override + @JsonKey() + Map get users { + if (_users is EqualUnmodifiableMapView) return _users; + // ignore: implicit_dynamic_type + return EqualUnmodifiableMapView(_users); + } + + @override + String toString() { + return 'UserRepositoryState(users: $users)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$_UserRepositoryState && + const DeepCollectionEquality().equals(other._users, _users)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => + Object.hash(runtimeType, const DeepCollectionEquality().hash(_users)); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$_UserRepositoryStateCopyWith<_$_UserRepositoryState> get copyWith => + __$$_UserRepositoryStateCopyWithImpl<_$_UserRepositoryState>( + this, _$identity); + + @override + Map toJson() { + return _$$_UserRepositoryStateToJson( + this, + ); + } +} + +abstract class _UserRepositoryState implements UserRepositoryState { + const factory _UserRepositoryState({final Map users}) = + _$_UserRepositoryState; + + factory _UserRepositoryState.fromJson(Map json) = + _$_UserRepositoryState.fromJson; + + @override + Map get users; + @override + @JsonKey(ignore: true) + _$$_UserRepositoryStateCopyWith<_$_UserRepositoryState> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/core/repository/user_repository_state.dart b/lib/core/repository/user_repository_state.dart new file mode 100644 index 0000000..3cd32ac --- /dev/null +++ b/lib/core/repository/user_repository_state.dart @@ -0,0 +1,11 @@ +part of 'user_repository.dart'; + +@freezed +class UserRepositoryState with _$UserRepositoryState { + const factory UserRepositoryState({ + @Default({}) Map users, + }) = _UserRepositoryState; + + factory UserRepositoryState.fromJson(Map json) => + _$UserRepositoryStateFromJson(json); +} diff --git a/lib/features/document_details/view/pages/document_details_page.dart b/lib/features/document_details/view/pages/document_details_page.dart index 465e2c1..0a90b4e 100644 --- a/lib/features/document_details/view/pages/document_details_page.dart +++ b/lib/features/document_details/view/pages/document_details_page.dart @@ -39,20 +39,13 @@ class DocumentDetailsPage extends StatefulWidget { } class _DocumentDetailsPageState extends State { - late Future _metaData; static const double _itemSpacing = 24; final _pagingScrollController = ScrollController(); - @override - void initState() { - super.initState(); - _loadMetaData(); - } - void _loadMetaData() { - _metaData = context - .read() - .getMetaData(context.read().state.document); + @override + void didChangeDependencies() { + super.didChangeDependencies(); } @override @@ -67,8 +60,7 @@ class _DocumentDetailsPageState extends State { child: BlocListener( listenWhen: (previous, current) => !previous.isConnected && current.isConnected, listener: (context, state) { - _loadMetaData(); - setState(() {}); + context.read().loadMetaData(); }, child: Scaffold( extendBodyBehindAppBar: false, @@ -98,7 +90,7 @@ class _DocumentDetailsPageState extends State { ), ), ), - Positioned.fill( + Positioned.fill( top: 0, child: Container( height: 100, @@ -285,7 +277,6 @@ class _DocumentDetailsPageState extends State { DocumentDownloadButton( document: state.document, enabled: isConnected, - metaData: _metaData, ), IconButton( tooltip: S.of(context)!.previewTooltip, diff --git a/lib/features/document_details/view/widgets/document_download_button.dart b/lib/features/document_details/view/widgets/document_download_button.dart index 217e129..de696de 100644 --- a/lib/features/document_details/view/widgets/document_download_button.dart +++ b/lib/features/document_details/view/widgets/document_download_button.dart @@ -20,12 +20,10 @@ import 'package:permission_handler/permission_handler.dart'; class DocumentDownloadButton extends StatefulWidget { final DocumentModel? document; final bool enabled; - final Future metaData; const DocumentDownloadButton({ super.key, required this.document, this.enabled = true, - required this.metaData, }); @override diff --git a/lib/features/document_scan/view/scanner_page.dart b/lib/features/document_scan/view/scanner_page.dart index 1cc3d96..ae60b2e 100644 --- a/lib/features/document_scan/view/scanner_page.dart +++ b/lib/features/document_scan/view/scanner_page.dart @@ -38,12 +38,9 @@ class ScannerPage extends StatefulWidget { State createState() => _ScannerPageState(); } -class _ScannerPageState extends State - with SingleTickerProviderStateMixin { - final SliverOverlapAbsorberHandle searchBarHandle = - SliverOverlapAbsorberHandle(); - final SliverOverlapAbsorberHandle actionsHandle = - SliverOverlapAbsorberHandle(); +class _ScannerPageState extends State with SingleTickerProviderStateMixin { + final SliverOverlapAbsorberHandle searchBarHandle = SliverOverlapAbsorberHandle(); + final SliverOverlapAbsorberHandle actionsHandle = SliverOverlapAbsorberHandle(); @override Widget build(BuildContext context) { @@ -180,8 +177,7 @@ class _ScannerPageState extends State final success = await EdgeDetection.detectEdge(file.path); if (!success) { if (kDebugMode) { - dev.log( - '[ScannerPage] Scan either not successful or canceled by user.'); + dev.log('[ScannerPage] Scan either not successful or canceled by user.'); } return; } @@ -198,7 +194,7 @@ class _ScannerPageState extends State final uploadResult = await Navigator.of(context).push( MaterialPageRoute( builder: (_) => BlocProvider( - create: (context) => DocumentUploadCubit( + create: (_) => DocumentUploadCubit( context.read(), context.read(), ), @@ -212,9 +208,7 @@ class _ScannerPageState extends State if ((uploadResult?.success ?? false) && uploadResult?.taskId != null) { // For paperless version older than 1.11.3, task id will always be null! context.read().reset(); - context - .read() - .listenToTaskChanges(uploadResult!.taskId!); + context.read().listenToTaskChanges(uploadResult!.taskId!); } } diff --git a/lib/features/document_search/view/document_search_page.dart b/lib/features/document_search/view/document_search_page.dart index 3bebab7..f28a701 100644 --- a/lib/features/document_search/view/document_search_page.dart +++ b/lib/features/document_search/view/document_search_page.dart @@ -226,13 +226,10 @@ class _DocumentSearchPageState extends State { hasLoaded: state.hasLoaded, enableHeroAnimation: false, onTap: (document) { - Navigator.pushNamed( + pushDocumentDetailsRoute( context, - DocumentDetailsRoute.routeName, - arguments: DocumentDetailsRouteArguments( - document: document, - isLabelClickable: false, - ), + document: document, + isLabelClickable: false, ); }, correspondents: state.correspondents, diff --git a/lib/features/document_search/view/sliver_search_bar.dart b/lib/features/document_search/view/sliver_search_bar.dart index 4cb1bf8..0f36ff7 100644 --- a/lib/features/document_search/view/sliver_search_bar.dart +++ b/lib/features/document_search/view/sliver_search_bar.dart @@ -47,7 +47,10 @@ class SliverSearchBar extends StatelessWidget { Hive.box(HiveBoxes.localUserAccount).listenable(), builder: (context, box, _) { final account = box.get(settings.currentLoggedInUser!)!; - return UserAvatar(userId: settings.currentLoggedInUser!, account: account); + return UserAvatar( + userId: settings.currentLoggedInUser!, + account: account, + ); }, ); }, diff --git a/lib/features/document_upload/view/document_upload_preparation_page.dart b/lib/features/document_upload/view/document_upload_preparation_page.dart index e1dd0f2..ea72e5c 100644 --- a/lib/features/document_upload/view/document_upload_preparation_page.dart +++ b/lib/features/document_upload/view/document_upload_preparation_page.dart @@ -32,6 +32,7 @@ class DocumentUploadPreparationPage extends StatefulWidget { final String? filename; final String? fileExtension; + const DocumentUploadPreparationPage({ Key? key, required this.fileBytes, diff --git a/lib/features/documents/view/pages/documents_page.dart b/lib/features/documents/view/pages/documents_page.dart index 22b7363..c76f86b 100644 --- a/lib/features/documents/view/pages/documents_page.dart +++ b/lib/features/documents/view/pages/documents_page.dart @@ -5,6 +5,7 @@ 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/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'; import 'package:paperless_mobile/extensions/flutter_extensions.dart'; import 'package:paperless_mobile/features/app_drawer/view/app_drawer.dart'; @@ -443,12 +444,9 @@ class _DocumentsPageState extends State with SingleTickerProvider } void _openDetails(DocumentModel document) { - Navigator.pushNamed( + pushDocumentDetailsRoute( context, - DocumentDetailsRoute.routeName, - arguments: DocumentDetailsRouteArguments( - document: document, - ), + document: document, ); } diff --git a/lib/features/documents/view/widgets/document_preview.dart b/lib/features/documents/view/widgets/document_preview.dart index b505ea8..74da008 100644 --- a/lib/features/documents/view/widgets/document_preview.dart +++ b/lib/features/documents/view/widgets/document_preview.dart @@ -2,7 +2,6 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:paperless_api/paperless_api.dart'; -import 'package:paperless_mobile/features/documents/view/pages/document_view.dart'; import 'package:provider/provider.dart'; import 'package:shimmer/shimmer.dart'; diff --git a/lib/features/home/view/home_route.dart b/lib/features/home/view/home_route.dart index eaa97e7..eb08550 100644 --- a/lib/features/home/view/home_route.dart +++ b/lib/features/home/view/home_route.dart @@ -23,8 +23,13 @@ import 'package:paperless_mobile/features/tasks/cubit/task_status_cubit.dart'; import 'package:provider/provider.dart'; class HomeRoute extends StatelessWidget { + /// The id of the currently authenticated user (e.g. demo@paperless.example.com) final String localUserId; + + /// The Paperless API version of the currently connected instance final int paperlessApiVersion; + + // A factory providing the API implementations given an API version final PaperlessApiFactory paperlessProviderFactory; const HomeRoute({ @@ -44,6 +49,7 @@ class HomeRoute extends StatelessWidget { Provider( create: (context) => CacheManager( Config( + // Isolated cache per user. localUserId, fileService: DioFileService(context.read().client), ), @@ -121,11 +127,15 @@ class HomeRoute extends StatelessWidget { )..initialize(), ), ProxyProvider( - update: (context, value, previous) => ServerInformationCubit(value), + update: (context, value, previous) => + ServerInformationCubit(value)..updateInformation(), ), ProxyProvider( update: (context, value, previous) => LabelCubit(value), ), + ProxyProvider( + update: (context, value, previous) => TaskStatusCubit(value), + ), ], child: const HomePage(), ); diff --git a/lib/features/inbox/view/widgets/inbox_item.dart b/lib/features/inbox/view/widgets/inbox_item.dart index 6c12d56..235f9e1 100644 --- a/lib/features/inbox/view/widgets/inbox_item.dart +++ b/lib/features/inbox/view/widgets/inbox_item.dart @@ -37,14 +37,11 @@ class _InboxItemState extends State { builder: (context, state) { return GestureDetector( behavior: HitTestBehavior.translucent, - onTap: () async { - Navigator.pushNamed( + onTap: () { + pushDocumentDetailsRoute( context, - DocumentDetailsRoute.routeName, - arguments: DocumentDetailsRouteArguments( - document: widget.document, - isLabelClickable: false, - ), + document: widget.document, + isLabelClickable: false, ); }, child: SizedBox( diff --git a/lib/features/linked_documents/view/linked_documents_page.dart b/lib/features/linked_documents/view/linked_documents_page.dart index 2171f7c..e8a688b 100644 --- a/lib/features/linked_documents/view/linked_documents_page.dart +++ b/lib/features/linked_documents/view/linked_documents_page.dart @@ -52,13 +52,10 @@ class _LinkedDocumentsPageState extends State isLoading: state.isLoading, hasLoaded: state.hasLoaded, onTap: (document) { - Navigator.pushNamed( + pushDocumentDetailsRoute( context, - DocumentDetailsRoute.routeName, - arguments: DocumentDetailsRouteArguments( - document: document, - isLabelClickable: false, - ), + document: document, + isLabelClickable: false, ); }, correspondents: state.correspondents, diff --git a/lib/features/login/cubit/authentication_cubit.dart b/lib/features/login/cubit/authentication_cubit.dart index 47e9f84..042e093 100644 --- a/lib/features/login/cubit/authentication_cubit.dart +++ b/lib/features/login/cubit/authentication_cubit.dart @@ -1,7 +1,7 @@ import 'dart:convert'; import 'dart:typed_data'; -import 'package:equatable/equatable.dart'; +import 'package:dio/dio.dart'; import 'package:flutter/widgets.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hive_flutter/adapters.dart'; @@ -9,6 +9,7 @@ import 'package:hydrated_bloc/hydrated_bloc.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/local_user_app_state.dart'; +import 'package:paperless_mobile/core/factory/paperless_api_factory.dart'; import 'package:paperless_mobile/core/security/session_manager.dart'; import 'package:paperless_mobile/features/login/model/client_certificate.dart'; import 'package:paperless_mobile/features/login/model/login_form_credentials.dart'; @@ -23,16 +24,14 @@ part 'authentication_state.dart'; part 'authentication_cubit.freezed.dart'; class AuthenticationCubit extends Cubit { - final PaperlessUserApi _userApi; final LocalAuthenticationService _localAuthService; - final PaperlessAuthenticationApi _authApi; + final PaperlessApiFactory _apiFactory; final SessionManager _sessionManager; AuthenticationCubit( this._localAuthService, - this._authApi, + this._apiFactory, this._sessionManager, - this._userApi, ) : super(const AuthenticationState.unauthenticated()); Future login({ @@ -43,7 +42,7 @@ class AuthenticationCubit extends Cubit { assert(credentials.username != null && credentials.password != null); final localUserId = "${credentials.username}@$serverUrl"; - final serverUser = await _addUser( + await _addUser( localUserId, serverUrl, credentials, @@ -51,13 +50,13 @@ class AuthenticationCubit extends Cubit { _sessionManager, ); - final response = await _sessionManager.client.get("/api/"); - final apiVersion = response.headers["x-api-version"] as int; + final apiVersion = await _getApiVersion(_sessionManager.client); // Mark logged in user as currently active user. final globalSettings = Hive.box(HiveBoxes.globalSettings).getValue()!; globalSettings.currentLoggedInUser = localUserId; await globalSettings.save(); + emit( AuthenticationState.authenticated( apiVersion: apiVersion, @@ -110,8 +109,8 @@ class AuthenticationCubit extends Cubit { globalSettings.currentLoggedInUser = localUserId; await globalSettings.save(); - final response = await _sessionManager.client.get("/api/"); - final apiVersion = response.headers["x-api-version"] as int; + final apiVersion = await _getApiVersion(_sessionManager.client); + emit(AuthenticationState.authenticated( localUserId: localUserId, apiVersion: apiVersion, @@ -188,8 +187,7 @@ class AuthenticationCubit extends Cubit { authToken: authentication.token, baseUrl: userAccount.serverUrl, ); - final response = await _sessionManager.client.get("/api/"); - final apiVersion = response.headers["x-api-version"] as int; + final apiVersion = await _getApiVersion(_sessionManager.client); emit( AuthenticationState.authenticated( apiVersion: apiVersion, @@ -247,26 +245,34 @@ class AuthenticationCubit extends Cubit { baseUrl: serverUrl, clientCertificate: clientCert, ); - final authApi = PaperlessAuthenticationApiImpl(sessionManager.client); + + final authApi = _apiFactory.createAuthenticationApi(sessionManager.client); final token = await authApi.login( username: credentials.username!, password: credentials.password!, ); + sessionManager.updateSettings( baseUrl: serverUrl, clientCertificate: clientCert, authToken: token, ); - + final userAccountBox = Hive.box(HiveBoxes.localUserAccount); final userStateBox = Hive.box(HiveBoxes.localUserAppState); if (userAccountBox.containsKey(localUserId)) { throw Exception("User with id $localUserId already exists!"); } + final apiVersion = await _getApiVersion(sessionManager.client); - final serverUserId = await _userApi.findCurrentUserId(); + final serverUser = await _apiFactory + .createUserApi( + sessionManager.client, + apiVersion: apiVersion, + ) + .findCurrentUser(); // Create user account await userAccountBox.put( @@ -275,7 +281,7 @@ class AuthenticationCubit extends Cubit { id: localUserId, settings: LocalUserSettings(), serverUrl: serverUrl, - paperlessUserId: 1, + paperlessUser: serverUser, ), ); @@ -295,6 +301,11 @@ class AuthenticationCubit extends Cubit { ), ); userCredentialsBox.close(); - return serverUserId; + return serverUser.id; + } + + Future _getApiVersion(Dio dio) async { + final response = await dio.get("/api/"); + return int.parse(response.headers.value('x-api-version') ?? "3"); } } diff --git a/lib/features/saved_view/cubit/saved_view_cubit.dart b/lib/features/saved_view/cubit/saved_view_cubit.dart index 8b56964..76912e8 100644 --- a/lib/features/saved_view/cubit/saved_view_cubit.dart +++ b/lib/features/saved_view/cubit/saved_view_cubit.dart @@ -1,6 +1,5 @@ import 'dart:async'; -import 'package:equatable/equatable.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:paperless_api/paperless_api.dart'; @@ -37,18 +36,20 @@ class SavedViewCubit extends Cubit { }, ); - _savedViewRepository.subscribe(this, (views) { - emit( - state.maybeWhen( - loaded: - (savedViews, correspondents, documentTypes, tags, storagePaths) => - (state as _SavedViewLoadedState).copyWith( - savedViews: views, + _savedViewRepository.addListener( + this, + onChanged: (views) { + emit( + state.maybeWhen( + loaded: (savedViews, correspondents, documentTypes, tags, storagePaths) => + (state as _SavedViewLoadedState).copyWith( + savedViews: views.savedViews, + ), + orElse: () => state, ), - orElse: () => state, - ), - ); - }); + ); + }, + ); } Future add(SavedView view) async { @@ -77,7 +78,7 @@ class SavedViewCubit extends Cubit { @override Future close() { - _savedViewRepository.unsubscribe(this); + _savedViewRepository.removeListener(this); _labelRepository.removeListener(this); return super.close(); } diff --git a/lib/features/saved_view_details/view/saved_view_details_page.dart b/lib/features/saved_view_details/view/saved_view_details_page.dart index 8887803..7bc08d9 100644 --- a/lib/features/saved_view_details/view/saved_view_details_page.dart +++ b/lib/features/saved_view_details/view/saved_view_details_page.dart @@ -76,12 +76,13 @@ class _SavedViewDetailsPageState extends State isLoading: state.isLoading, hasLoaded: state.hasLoaded, onTap: (document) { - Navigator.pushNamed( + Navigator.push( context, - DocumentDetailsRoute.routeName, - arguments: DocumentDetailsRouteArguments( - document: document, - isLabelClickable: false, + MaterialPageRoute( + builder: (context) => DocumentDetailsRoute( + document: document, + isLabelClickable: false, + ), ), ); }, diff --git a/lib/features/settings/view/manage_accounts_page.dart b/lib/features/settings/view/manage_accounts_page.dart index 9165ed0..55b8a95 100644 --- a/lib/features/settings/view/manage_accounts_page.dart +++ b/lib/features/settings/view/manage_accounts_page.dart @@ -86,11 +86,11 @@ class ManageAccountsPage extends StatelessWidget { final child = SizedBox( width: double.maxFinite, child: ListTile( - title: Text(account.username), + title: Text(account.paperlessUser.username), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (account.fullName != null) Text(account.fullName!), + if (account.paperlessUser.fullName != null) Text(account.paperlessUser.fullName!), Text( account.serverUrl.replaceFirst(RegExp(r'https://?'), ''), style: TextStyle( @@ -99,7 +99,7 @@ class ManageAccountsPage extends StatelessWidget { ), ], ), - isThreeLine: account.fullName != null, + isThreeLine: account.paperlessUser.fullName != null, leading: UserAvatar( account: account, userId: userId, diff --git a/lib/features/settings/view/settings_page.dart b/lib/features/settings/view/settings_page.dart index 5ba5f4a..d3614fd 100644 --- a/lib/features/settings/view/settings_page.dart +++ b/lib/features/settings/view/settings_page.dart @@ -22,7 +22,7 @@ class SettingsPage extends StatelessWidget { final host = user!.serverUrl.replaceFirst(RegExp(r"https?://"), ""); return ListTile( title: Text( - S.of(context)!.loggedInAs(user.username) + "@$host", + S.of(context)!.loggedInAs(user.paperlessUser.username) + "@$host", style: Theme.of(context).textTheme.labelSmall, textAlign: TextAlign.center, ), diff --git a/lib/features/settings/view/widgets/user_avatar.dart b/lib/features/settings/view/widgets/user_avatar.dart index 8c11053..1e44bef 100644 --- a/lib/features/settings/view/widgets/user_avatar.dart +++ b/lib/features/settings/view/widgets/user_avatar.dart @@ -1,5 +1,8 @@ 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; @@ -16,7 +19,7 @@ class UserAvatar extends StatelessWidget { final backgroundColor = Colors.primaries[userId.hashCode % Colors.primaries.length]; final foregroundColor = backgroundColor.computeLuminance() > 0.5 ? Colors.black : Colors.white; return CircleAvatar( - child: Text((account.fullName ?? account.username) + child: Text((account.paperlessUser.fullName ?? account.paperlessUser.username) .split(" ") .take(2) .map((e) => e.substring(0, 1)) diff --git a/lib/features/similar_documents/view/similar_documents_view.dart b/lib/features/similar_documents/view/similar_documents_view.dart index 6f96453..3b9352f 100644 --- a/lib/features/similar_documents/view/similar_documents_view.dart +++ b/lib/features/similar_documents/view/similar_documents_view.dart @@ -36,10 +36,8 @@ class _SimilarDocumentsViewState extends State @override Widget build(BuildContext context) { return BlocConsumer( - listenWhen: (previous, current) => - !previous.isConnected && current.isConnected, - listener: (context, state) => - context.read().initialize(), + listenWhen: (previous, current) => !previous.isConnected && current.isConnected, + listener: (context, state) => context.read().initialize(), builder: (context, connectivity) { return BlocBuilder( builder: (context, state) { @@ -48,9 +46,7 @@ class _SimilarDocumentsViewState extends State child: OfflineWidget(), ); } - if (state.hasLoaded && - !state.isLoading && - state.documents.isEmpty) { + if (state.hasLoaded && !state.isLoading && state.documents.isEmpty) { return SliverToBoxAdapter( child: Center( child: Text(S.of(context)!.noItemsFound), @@ -65,13 +61,10 @@ class _SimilarDocumentsViewState extends State hasLoaded: state.hasLoaded, enableHeroAnimation: false, onTap: (document) { - Navigator.pushNamed( + pushDocumentDetailsRoute( context, - DocumentDetailsRoute.routeName, - arguments: DocumentDetailsRouteArguments( - document: document, - isLabelClickable: false, - ), + document: document, + isLabelClickable: false, ); }, correspondents: state.correspondents, diff --git a/lib/main.dart b/lib/main.dart index cd7e1e5..792b4b1 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -5,7 +5,6 @@ import 'package:device_info_plus/device_info_plus.dart'; import 'package:dynamic_color/dynamic_color.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_cache_manager/flutter_cache_manager.dart' as cm; import 'package:flutter_displaymode/flutter_displaymode.dart'; import 'package:flutter_native_splash/flutter_native_splash.dart'; import 'package:hive_flutter/adapters.dart'; @@ -26,11 +25,8 @@ import 'package:paperless_mobile/core/factory/paperless_api_factory_impl.dart'; import 'package:paperless_mobile/core/interceptor/dio_http_error_interceptor.dart'; import 'package:paperless_mobile/core/interceptor/language_header.interceptor.dart'; import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart'; -import 'package:paperless_mobile/core/repository/label_repository.dart'; -import 'package:paperless_mobile/core/repository/saved_view_repository.dart'; import 'package:paperless_mobile/core/security/session_manager.dart'; import 'package:paperless_mobile/core/service/connectivity_status_service.dart'; -import 'package:paperless_mobile/core/service/dio_file_service.dart'; import 'package:paperless_mobile/core/type/types.dart'; import 'package:paperless_mobile/features/app_intro/application_intro_slideshow.dart'; import 'package:paperless_mobile/features/home/view/home_route.dart'; @@ -125,6 +121,8 @@ void main() async { languageHeaderInterceptor.preferredLocaleSubtag = globalSettings.preferredLocaleSubtag; }); + final apiFactory = PaperlessApiFactoryImpl(sessionManager); + runApp( MultiProvider( providers: [ @@ -139,9 +137,17 @@ void main() async { child: MultiBlocProvider( providers: [ BlocProvider.value(value: connectivityCubit), + BlocProvider( + create: (context) => AuthenticationCubit( + localAuthService, + apiFactory, + sessionManager, + ), + child: Container(), + ) ], child: PaperlessMobileEntrypoint( - paperlessProviderFactory: PaperlessApiFactoryImpl(sessionManager), + paperlessProviderFactory: apiFactory, ), ), ), @@ -187,9 +193,6 @@ class _PaperlessMobileEntrypointState extends State { localizationsDelegates: const [ ...S.localizationsDelegates, ], - routes: { - DocumentDetailsRoute.routeName: (context) => const DocumentDetailsRoute(), - }, home: AuthenticationWrapper( paperlessProviderFactory: widget.paperlessProviderFactory, ), @@ -217,7 +220,10 @@ class _AuthenticationWrapperState extends State { @override void didChangeDependencies() { super.didChangeDependencies(); - FlutterNativeSplash.remove(); + context + .read() + .restoreSessionState() + .then((value) => FlutterNativeSplash.remove()); } @override diff --git a/lib/routes/document_details_route.dart b/lib/routes/document_details_route.dart index 41be2de..c2e1afa 100644 --- a/lib/routes/document_details_route.dart +++ b/lib/routes/document_details_route.dart @@ -1,34 +1,44 @@ +import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_cache_manager/flutter_cache_manager.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'; import 'package:paperless_mobile/features/document_details/cubit/document_details_cubit.dart'; import 'package:paperless_mobile/features/document_details/view/pages/document_details_page.dart'; +import 'package:paperless_mobile/features/notifications/services/local_notification_service.dart'; +import 'package:provider/provider.dart'; class DocumentDetailsRoute extends StatelessWidget { - static const String routeName = "/documentDetails"; - const DocumentDetailsRoute({super.key}); + final DocumentModel document; + final bool isLabelClickable; + final bool allowEdit; + final String? titleAndContentQueryString; + + const DocumentDetailsRoute({ + super.key, + required this.document, + this.isLabelClickable = true, + this.allowEdit = true, + this.titleAndContentQueryString, + }); @override Widget build(BuildContext context) { - final args = ModalRoute.of(context)!.settings.arguments - as DocumentDetailsRouteArguments; - return BlocProvider( - create: (context) => DocumentDetailsCubit( + create: (_) => DocumentDetailsCubit( context.read(), context.read(), context.read(), context.read(), - initialDocument: args.document, + initialDocument: document, ), - child: RepositoryProvider.value( - value: context.read(), - child: DocumentDetailsPage( - allowEdit: args.allowEdit, - isLabelClickable: args.isLabelClickable, - titleAndContentQueryString: args.titleAndContentQueryString, - ), + lazy: false, + child: DocumentDetailsPage( + allowEdit: allowEdit, + isLabelClickable: isLabelClickable, + titleAndContentQueryString: titleAndContentQueryString, ), ); } @@ -47,3 +57,33 @@ class DocumentDetailsRouteArguments { this.titleAndContentQueryString, }); } + +Future pushDocumentDetailsRoute( + BuildContext context, { + required DocumentModel document, + bool isLabelClickable = true, + bool allowEdit = true, + String? titleAndContentQueryString, +}) { + final LabelRepository labelRepository = context.read(); + final DocumentChangedNotifier changeNotifier = context.read(); + final PaperlessDocumentsApi documentsApi = context.read(); + final LocalNotificationService notificationservice = context.read(); + final CacheManager cacheManager = context.read(); + return Navigator.of(context).push(MaterialPageRoute( + builder: (_) => MultiProvider( + providers: [ + Provider.value(value: documentsApi), + Provider.value(value: labelRepository), + Provider.value(value: changeNotifier), + Provider.value(value: notificationservice), + Provider.value(value: cacheManager), + ], + child: DocumentDetailsRoute( + document: document, + allowEdit: allowEdit, + isLabelClickable: isLabelClickable, + ), + ), + )); +} diff --git a/packages/paperless_api/lib/src/models/user_model.dart b/packages/paperless_api/lib/src/models/user_model.dart index b02a377..53d9038 100644 --- a/packages/paperless_api/lib/src/models/user_model.dart +++ b/packages/paperless_api/lib/src/models/user_model.dart @@ -51,7 +51,7 @@ class UserModel with _$UserModel { if (value.firstName == null) { return value.lastName; } - return value.firstName! + (value.lastName ?? ''); + return "${value.firstName!} ${value.lastName ?? ''}"; }, ); diff --git a/packages/paperless_api/lib/src/modules/server_stats_api/paperless_server_stats_api_impl.dart b/packages/paperless_api/lib/src/modules/server_stats_api/paperless_server_stats_api_impl.dart index 9a2f8c0..92e033b 100644 --- a/packages/paperless_api/lib/src/modules/server_stats_api/paperless_server_stats_api_impl.dart +++ b/packages/paperless_api/lib/src/modules/server_stats_api/paperless_server_stats_api_impl.dart @@ -18,7 +18,12 @@ class PaperlessServerStatsApiImpl implements PaperlessServerStatsApi { @override Future getServerInformation() async { - final response = await client.get("/api/"); + final response = await client.get("/api/remote_version/"); + if (response.statusCode == 200) { + final version = response.data["version"] as String; + final updateAvailable = response.data["update_available"] as bool; + } + throw PaperlessServerException.unknown(); final version = response.headers[PaperlessServerInformationModel.versionHeader]?.first ?? 'unknown'; final apiVersion = int.tryParse( diff --git a/packages/paperless_api/lib/src/modules/user_api/paperless_user_api.dart b/packages/paperless_api/lib/src/modules/user_api/paperless_user_api.dart index d6cbae2..59c3171 100644 --- a/packages/paperless_api/lib/src/modules/user_api/paperless_user_api.dart +++ b/packages/paperless_api/lib/src/modules/user_api/paperless_user_api.dart @@ -2,5 +2,5 @@ import 'package:paperless_api/paperless_api.dart'; abstract class PaperlessUserApi { Future findCurrentUserId(); - Future find(int id); + Future findCurrentUser(); } diff --git a/packages/paperless_api/lib/src/modules/user_api/paperless_user_api_v2_impl.dart b/packages/paperless_api/lib/src/modules/user_api/paperless_user_api_v2_impl.dart index 2e63ba5..007a1c9 100644 --- a/packages/paperless_api/lib/src/modules/user_api/paperless_user_api_v2_impl.dart +++ b/packages/paperless_api/lib/src/modules/user_api/paperless_user_api_v2_impl.dart @@ -1,6 +1,5 @@ import 'package:dio/dio.dart'; import 'package:paperless_api/paperless_api.dart'; -import 'package:paperless_api/src/modules/user_api/paperless_user_api.dart'; class PaperlessUserApiV2Impl implements PaperlessUserApi { final Dio client; @@ -11,13 +10,13 @@ class PaperlessUserApiV2Impl implements PaperlessUserApi { Future findCurrentUserId() async { final response = await client.get("/api/ui_settings/"); if (response.statusCode == 200) { - return response.data['user']['id']; + return response.data['user_id']; } throw const PaperlessServerException.unknown(); } @override - Future find(int id) async { + Future findCurrentUser() async { final response = await client.get("/api/ui_settings/"); if (response.statusCode == 200) { return UserModelV2.fromJson(response.data); diff --git a/packages/paperless_api/lib/src/modules/user_api/paperless_user_api_v3.dart b/packages/paperless_api/lib/src/modules/user_api/paperless_user_api_v3.dart index 3f8447f..4d9dbc5 100644 --- a/packages/paperless_api/lib/src/modules/user_api/paperless_user_api_v3.dart +++ b/packages/paperless_api/lib/src/modules/user_api/paperless_user_api_v3.dart @@ -1,7 +1,9 @@ import 'package:paperless_api/src/models/user_model.dart'; abstract class PaperlessUserApiV3 { - Future> findWhere({ + Future find(int id); + Future> findAll(); + Future> findWhere({ String startsWith, String endsWith, String contains, diff --git a/packages/paperless_api/lib/src/modules/user_api/paperless_user_api_v3_impl.dart b/packages/paperless_api/lib/src/modules/user_api/paperless_user_api_v3_impl.dart index a3ce6ff..8e217c9 100644 --- a/packages/paperless_api/lib/src/modules/user_api/paperless_user_api_v3_impl.dart +++ b/packages/paperless_api/lib/src/modules/user_api/paperless_user_api_v3_impl.dart @@ -1,7 +1,5 @@ import 'package:dio/dio.dart'; import 'package:paperless_api/paperless_api.dart'; -import 'package:paperless_api/src/modules/user_api/paperless_user_api.dart'; -import 'package:paperless_api/src/modules/user_api/paperless_user_api_v3.dart'; class PaperlessUserApiV3Impl implements PaperlessUserApi, PaperlessUserApiV3 { final Dio dio; @@ -9,7 +7,7 @@ class PaperlessUserApiV3Impl implements PaperlessUserApi, PaperlessUserApiV3 { PaperlessUserApiV3Impl(this.dio); @override - Future find(int id) async { + Future find(int id) async { final response = await dio.get("/api/users/$id/"); if (response.statusCode == 200) { return UserModelV3.fromJson(response.data); @@ -18,7 +16,7 @@ class PaperlessUserApiV3Impl implements PaperlessUserApi, PaperlessUserApiV3 { } @override - Future> findWhere({ + Future> findWhere({ String startsWith = '', String endsWith = '', String contains = '', @@ -31,9 +29,9 @@ class PaperlessUserApiV3Impl implements PaperlessUserApi, PaperlessUserApiV3 { "username__iexact": username, }); if (response.statusCode == 200) { - return PagedSearchResult.fromJson( + return PagedSearchResult.fromJson( response.data, - UserModelV3.fromJson as UserModel Function(Object?), + UserModelV3.fromJson as UserModelV3 Function(Object?), ).results; } throw const PaperlessServerException.unknown(); @@ -43,8 +41,26 @@ class PaperlessUserApiV3Impl implements PaperlessUserApi, PaperlessUserApiV3 { Future findCurrentUserId() async { final response = await dio.get("/api/ui_settings/"); if (response.statusCode == 200) { - return response.data['user_id']; + return response.data['user']['id']; } throw const PaperlessServerException.unknown(); } + + @override + Future> findAll() async { + final response = await dio.get("/api/users/"); + if (response.statusCode == 200) { + return PagedSearchResult.fromJson( + response.data, + UserModelV3.fromJson as UserModelV3 Function(Object?), + ).results; + } + throw const PaperlessServerException.unknown(); + } + + @override + Future findCurrentUser() async { + final id = await findCurrentUserId(); + return find(id); + } } From f388f77d639dc4cc7b77e290e8c2254f2ce173e8 Mon Sep 17 00:00:00 2001 From: Anton Stubenbord Date: Thu, 11 May 2023 12:37:17 +0200 Subject: [PATCH 04/26] fix: Adapt to new provider strucutre --- lib/core/bloc/server_information_cubit.dart | 17 - lib/core/bloc/server_information_state.dart | 11 - .../database/tables/local_user_account.dart | 6 +- .../database/tables/local_user_app_state.dart | 7 + .../dio_http_error_interceptor.dart | 12 +- lib/core/navigation/push_routes.dart | 107 ++ .../error_code_localization_mapper.dart | 2 + lib/features/app_drawer/view/app_drawer.dart | 28 +- .../view/pages/document_details_page.dart | 78 +- .../widgets/document_meta_data_widget.dart | 6 +- .../widgets/document_permissions_widget.dart | 12 + .../view/document_edit_page.dart | 9 +- .../document_scan/view/scanner_page.dart | 2 - .../view/document_search_page.dart | 23 +- .../view/sliver_search_bar.dart | 11 +- .../documents/cubit/documents_cubit.dart | 12 +- .../documents/view/pages/document_view.dart | 49 +- .../documents/view/pages/documents_page.dart | 9 +- .../sort_field_selection_bottom_sheet.dart | 16 +- .../view/widgets/sort_documents_button.dart | 18 +- lib/features/home/view/home_page.dart | 9 +- lib/features/home/view/home_route.dart | 27 +- lib/features/home/view/model/api_version.dart | 8 + .../inbox/view/widgets/inbox_item.dart | 1 + .../view/linked_documents_page.dart | 1 + .../login/cubit/authentication_cubit.dart | 54 +- .../saved_view/cubit/saved_view_cubit.dart | 41 +- .../cubit/saved_view_cubit.freezed.dart | 993 ++---------------- .../saved_view/cubit/saved_view_state.dart | 30 +- .../saved_view/view/saved_view_list.dart | 50 +- .../view/saved_view_details_page.dart | 11 +- .../settings/view/manage_accounts_page.dart | 13 + lib/features/settings/view/settings_page.dart | 20 +- .../view/similar_documents_view.dart | 3 +- lib/main.dart | 9 +- lib/routes/document_details_route.dart | 38 - .../models/paperless_server_exception.dart | 3 +- .../paperless_server_information_model.dart | 24 +- .../permissions/inherited_permissions.dart | 4 +- .../lib/src/models/user_model.dart | 2 +- .../paperless_documents_api_impl.dart | 1 - .../paperless_server_stats_api_impl.dart | 15 +- .../user_api/paperless_user_api_v3_impl.dart | 2 +- 43 files changed, 540 insertions(+), 1254 deletions(-) delete mode 100644 lib/core/bloc/server_information_cubit.dart delete mode 100644 lib/core/bloc/server_information_state.dart create mode 100644 lib/core/navigation/push_routes.dart create mode 100644 lib/features/document_details/view/widgets/document_permissions_widget.dart create mode 100644 lib/features/home/view/model/api_version.dart diff --git a/lib/core/bloc/server_information_cubit.dart b/lib/core/bloc/server_information_cubit.dart deleted file mode 100644 index 942c2d3..0000000 --- a/lib/core/bloc/server_information_cubit.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:paperless_api/paperless_api.dart'; -import 'package:paperless_mobile/core/bloc/server_information_state.dart'; - -class ServerInformationCubit extends Cubit { - final PaperlessServerStatsApi _api; - - ServerInformationCubit(this._api) : super(ServerInformationState()); - - Future updateInformation() async { - final information = await _api.getServerInformation(); - emit(ServerInformationState( - isLoaded: true, - information: information, - )); - } -} diff --git a/lib/core/bloc/server_information_state.dart b/lib/core/bloc/server_information_state.dart deleted file mode 100644 index 401ed3f..0000000 --- a/lib/core/bloc/server_information_state.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:paperless_api/paperless_api.dart'; - -class ServerInformationState { - final bool isLoaded; - final PaperlessServerInformationModel? information; - - ServerInformationState({ - this.isLoaded = false, - this.information, - }); -} diff --git a/lib/core/database/tables/local_user_account.dart b/lib/core/database/tables/local_user_account.dart index 256dd02..f5de026 100644 --- a/lib/core/database/tables/local_user_account.dart +++ b/lib/core/database/tables/local_user_account.dart @@ -1,5 +1,6 @@ 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'; import 'package:paperless_mobile/core/database/tables/local_user_settings.dart'; import 'package:paperless_api/paperless_api.dart'; @@ -17,7 +18,7 @@ class LocalUserAccount extends HiveObject { final LocalUserSettings settings; @HiveField(7) - final UserModel paperlessUser; + UserModel paperlessUser; LocalUserAccount({ required this.id, @@ -25,4 +26,7 @@ class LocalUserAccount extends HiveObject { required this.settings, required this.paperlessUser, }); + + static LocalUserAccount get current => Hive.box(HiveBoxes.localUserAccount) + .get(Hive.box(HiveBoxes.globalSettings).getValue()!.currentLoggedInUser)!; } diff --git a/lib/core/database/tables/local_user_app_state.dart b/lib/core/database/tables/local_user_app_state.dart index 8dd9603..ffb5b43 100644 --- a/lib/core/database/tables/local_user_app_state.dart +++ b/lib/core/database/tables/local_user_app_state.dart @@ -1,6 +1,7 @@ import 'package:hive_flutter/adapters.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/features/settings/model/view_type.dart'; part 'local_user_app_state.g.dart'; @@ -37,4 +38,10 @@ class LocalUserAppState extends HiveObject { this.documentSearchViewType = ViewType.list, this.savedViewsViewType = ViewType.list, }); + + static LocalUserAppState get current { + final currentLocalUserId = + Hive.box(HiveBoxes.globalSettings).getValue()!.currentLoggedInUser!; + return Hive.box(HiveBoxes.localUserAppState).get(currentLocalUserId)!; + } } diff --git a/lib/core/interceptor/dio_http_error_interceptor.dart b/lib/core/interceptor/dio_http_error_interceptor.dart index b5a0700..8cb0d38 100644 --- a/lib/core/interceptor/dio_http_error_interceptor.dart +++ b/lib/core/interceptor/dio_http_error_interceptor.dart @@ -15,6 +15,15 @@ class DioHttpErrorInterceptor extends Interceptor { } else if (data is String) { return _handlePlainError(data, handler, err); } + } else if (err.response?.statusCode == 403) { + handler.reject( + DioError( + requestOptions: err.requestOptions, + error: const PaperlessServerException(ErrorCode.notAuthorized), + response: err.response, + ), + ); + return; } else if (err.error is SocketException) { final ex = err.error as SocketException; if (ex.osError?.errorCode == _OsErrorCodes.serverUnreachable.code) { @@ -67,8 +76,7 @@ class DioHttpErrorInterceptor extends Interceptor { DioError( requestOptions: err.requestOptions, type: DioErrorType.badResponse, - error: const PaperlessServerException( - ErrorCode.missingClientCertificate), + error: const PaperlessServerException(ErrorCode.missingClientCertificate), ), ); } diff --git a/lib/core/navigation/push_routes.dart b/lib/core/navigation/push_routes.dart new file mode 100644 index 0000000..4a8615f --- /dev/null +++ b/lib/core/navigation/push_routes.dart @@ -0,0 +1,107 @@ +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/notifier/document_changed_notifier.dart'; +import 'package:paperless_mobile/core/repository/label_repository.dart'; +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/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_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'; + +Future pushDocumentSearchPage(BuildContext context) { + final currentUser = + Hive.box(HiveBoxes.globalSettings).getValue()!.currentLoggedInUser; + return Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => MultiProvider( + providers: [ + Provider.value(value: context.read()), + Provider.value(value: context.read()), + Provider.value(value: context.read()), + Provider.value(value: context.read()), + ], + builder: (context, _) { + return BlocProvider( + create: (context) => DocumentSearchCubit( + context.read(), + context.read(), + context.read(), + Hive.box(HiveBoxes.localUserAppState).get(currentUser)!, + ), + child: const DocumentSearchPage(), + ); + }, + ), + ), + ); +} + +Future pushDocumentDetailsRoute( + BuildContext context, { + required DocumentModel document, + bool isLabelClickable = true, + bool allowEdit = true, + String? titleAndContentQueryString, +}) { + return Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => MultiProvider( + providers: [ + Provider.value(value: context.read()), + Provider.value(value: context.read()), + Provider.value(value: context.read()), + Provider.value(value: context.read()), + Provider.value(value: context.read()), + Provider.value(value: context.read()), + if (context.read().hasMultiUserSupport) + Provider.value(value: context.read()), + ], + child: DocumentDetailsRoute( + document: document, + isLabelClickable: isLabelClickable, + ), + ), + ), + ); +} + +Future pushSavedViewDetailsRoute(BuildContext context, {required SavedView savedView}) { + return Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => MultiProvider( + providers: [ + Provider.value(value: context.read()), + Provider.value(value: context.read()), + Provider.value(value: context.read()), + Provider.value(value: context.read()), + ], + child: SavedViewDetailsPage( + onDelete: context.read().remove, + ), + builder: (_, child) { + return BlocProvider( + create: (context) => SavedViewDetailsCubit( + context.read(), + context.read(), + context.read(), + LocalUserAppState.current, + savedView: savedView, + ), + child: SavedViewDetailsPage(onDelete: context.read().remove), + ); + }, + ), + ), + ); +} diff --git a/lib/core/translation/error_code_localization_mapper.dart b/lib/core/translation/error_code_localization_mapper.dart index 4b2be11..9143850 100644 --- a/lib/core/translation/error_code_localization_mapper.dart +++ b/lib/core/translation/error_code_localization_mapper.dart @@ -72,5 +72,7 @@ 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 } } diff --git a/lib/features/app_drawer/view/app_drawer.dart b/lib/features/app_drawer/view/app_drawer.dart index 1a778a5..08d1479 100644 --- a/lib/features/app_drawer/view/app_drawer.dart +++ b/lib/features/app_drawer/view/app_drawer.dart @@ -3,14 +3,13 @@ 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/bloc/server_information_cubit.dart'; import 'package:paperless_mobile/core/widgets/paperless_logo.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.dart'; +import 'package:paperless_mobile/features/home/view/model/api_version.dart'; import 'package:paperless_mobile/features/settings/view/settings_page.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; +import 'package:provider/provider.dart'; -import 'package:url_launcher/link.dart'; -import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher_string.dart'; class AppDrawer extends StatelessWidget { @@ -42,7 +41,16 @@ class AppDrawer extends StatelessWidget { ListTile( dense: true, leading: const Icon(Icons.bug_report_outlined), - title: Text(S.of(context)!.reportABug), + title: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(S.of(context)!.reportABug), + const Icon( + Icons.open_in_new, + size: 16, + ) + ], + ), onTap: () { launchUrlString( 'https://github.com/astubenbord/paperless-mobile/issues/new', @@ -64,7 +72,7 @@ class AppDrawer extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(S.of(context)!.donateCoffee), - Icon( + const Icon( Icons.open_in_new, size: 16, ) @@ -85,8 +93,8 @@ class AppDrawer extends StatelessWidget { ), onTap: () => Navigator.of(context).push( MaterialPageRoute( - builder: (_) => BlocProvider.value( - value: context.read(), + builder: (_) => Provider.value( + value: context.read(), child: const SettingsPage(), ), ), @@ -148,19 +156,19 @@ class AppDrawer extends StatelessWidget { return RichText( text: TextSpan( children: [ - TextSpan( + const TextSpan( text: 'Onboarding images by ', ), TextSpan( text: 'pch.vector', - style: TextStyle(color: Colors.blue), + style: const TextStyle(color: Colors.blue), recognizer: TapGestureRecognizer() ..onTap = () { launchUrlString( 'https://www.freepik.com/free-vector/business-team-working-cogwheel-mechanism-together_8270974.htm#query=setting&position=4&from_view=author'); }, ), - TextSpan( + const TextSpan( text: ' on Freepik.', ), ], diff --git a/lib/features/document_details/view/pages/document_details_page.dart b/lib/features/document_details/view/pages/document_details_page.dart index 0a90b4e..aeced88 100644 --- a/lib/features/document_details/view/pages/document_details_page.dart +++ b/lib/features/document_details/view/pages/document_details_page.dart @@ -1,8 +1,13 @@ 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'; @@ -11,19 +16,20 @@ import 'package:paperless_mobile/features/document_details/view/widgets/document import 'package:paperless_mobile/features/document_details/view/widgets/document_download_button.dart'; import 'package:paperless_mobile/features/document_details/view/widgets/document_meta_data_widget.dart'; import 'package:paperless_mobile/features/document_details/view/widgets/document_overview_widget.dart'; +import 'package:paperless_mobile/features/document_details/view/widgets/document_permissions_widget.dart'; import 'package:paperless_mobile/features/document_details/view/widgets/document_share_button.dart'; import 'package:paperless_mobile/features/document_edit/cubit/document_edit_cubit.dart'; import 'package:paperless_mobile/features/document_edit/view/document_edit_page.dart'; import 'package:paperless_mobile/features/documents/view/pages/document_view.dart'; import 'package:paperless_mobile/features/documents/view/widgets/delete_document_confirmation_dialog.dart'; import 'package:paperless_mobile/features/documents/view/widgets/document_preview.dart'; +import 'package:paperless_mobile/features/home/view/model/api_version.dart'; import 'package:paperless_mobile/features/similar_documents/cubit/similar_documents_cubit.dart'; import 'package:paperless_mobile/features/similar_documents/view/similar_documents_view.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; import 'package:paperless_mobile/helpers/message_helpers.dart'; class DocumentDetailsPage extends StatefulWidget { - final bool allowEdit; final bool isLabelClickable; final String? titleAndContentQueryString; @@ -31,7 +37,6 @@ class DocumentDetailsPage extends StatefulWidget { Key? key, this.isLabelClickable = true, this.titleAndContentQueryString, - this.allowEdit = true, }) : super(key: key); @override @@ -50,13 +55,16 @@ class _DocumentDetailsPageState extends State { @override Widget build(BuildContext context) { + final apiVersion = context.watch(); + + final tabLength = 4 + (apiVersion.supportsPermissions ? 1 : 0); return WillPopScope( onWillPop: () async { Navigator.of(context).pop(context.read().state.document); return false; }, child: DefaultTabController( - length: 4, + length: tabLength, child: BlocListener( listenWhen: (previous, current) => !previous.isConnected && current.isConnected, listener: (context, state) { @@ -65,7 +73,7 @@ class _DocumentDetailsPageState extends State { child: Scaffold( extendBodyBehindAppBar: false, floatingActionButtonLocation: FloatingActionButtonLocation.endDocked, - floatingActionButton: widget.allowEdit ? _buildEditButton() : null, + floatingActionButton: _buildEditButton(), bottomNavigationBar: _buildBottomAppBar(), body: NestedScrollView( headerSliverBuilder: (context, innerBoxIsScrolled) => [ @@ -90,7 +98,7 @@ class _DocumentDetailsPageState extends State { ), ), ), - Positioned.fill( + Positioned.fill( top: 0, child: Container( height: 100, @@ -147,6 +155,15 @@ class _DocumentDetailsPageState extends State { ), ), ), + if (apiVersion.supportsPermissions) + Tab( + child: Text( + "Permissions", + style: TextStyle( + color: Theme.of(context).colorScheme.onPrimaryContainer, + ), + ), + ), ], ), ), @@ -220,6 +237,15 @@ class _DocumentDetailsPageState extends State { ), ], ), + CustomScrollView( + controller: _pagingScrollController, + slivers: [ + SliverOverlapInjector( + handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context), + ), + const DocumentPermissionsWidget(), + ], + ), ], ), ), @@ -234,25 +260,29 @@ class _DocumentDetailsPageState extends State { } Widget _buildEditButton() { + bool canEdit = context.watchInternetConnection; + final apiVersion = context.watch(); + + if (apiVersion.supportsPermissions) { + canEdit = + LocalUserAccount.current.paperlessUser.hasPermission(UserPermissions.changeDocument); + } + if (!canEdit) { + return const SizedBox.shrink(); + } return BlocBuilder( builder: (context, state) { // final _filteredSuggestions = // state.suggestions?.documentDifference(state.document); - return BlocBuilder( - builder: (context, connectivityState) { - if (!connectivityState.isConnected) { - return const SizedBox.shrink(); - } - return Tooltip( - message: S.of(context)!.editDocumentTooltip, - preferBelow: false, - verticalOffset: 40, - child: FloatingActionButton( - child: const Icon(Icons.edit), - onPressed: () => _onEdit(state.document), - ), - ); - }, + + return Tooltip( + message: S.of(context)!.editDocumentTooltip, + preferBelow: false, + verticalOffset: 40, + child: FloatingActionButton( + child: const Icon(Icons.edit), + onPressed: canEdit ? () => _onEdit(state.document) : null, + ), ); }, ); @@ -265,14 +295,16 @@ class _DocumentDetailsPageState extends State { child: BlocBuilder( builder: (context, connectivityState) { final isConnected = connectivityState.isConnected; + + final canDelete = LocalUserAccount.current.paperlessUser + .hasPermission(UserPermissions.deleteDocument); return Row( mainAxisAlignment: MainAxisAlignment.start, children: [ IconButton( tooltip: S.of(context)!.deleteDocumentTooltip, icon: const Icon(Icons.delete), - onPressed: - widget.allowEdit && isConnected ? () => _onDelete(state.document) : null, + onPressed: (isConnected && canDelete) ? () => _onDelete(state.document) : null, ).paddedSymmetrically(horizontal: 4), DocumentDownloadButton( document: state.document, @@ -374,7 +406,7 @@ class _DocumentDetailsPageState extends State { Future _onOpen(DocumentModel document) async { Navigator.of(context).push( MaterialPageRoute( - builder: (context) => DocumentView( + builder: (_) => DocumentView( documentBytes: context.read().getPreview(document.id), ), ), diff --git a/lib/features/document_details/view/widgets/document_meta_data_widget.dart b/lib/features/document_details/view/widgets/document_meta_data_widget.dart index 07e26dd..24806c6 100644 --- a/lib/features/document_details/view/widgets/document_meta_data_widget.dart +++ b/lib/features/document_details/view/widgets/document_meta_data_widget.dart @@ -29,7 +29,11 @@ class _DocumentMetaDataWidgetState extends State { builder: (context, state) { debugPrint("Building state..."); if (state.metaData == null) { - return const Center(child: CircularProgressIndicator()); + return SliverToBoxAdapter( + child: const Center( + child: CircularProgressIndicator(), + ), + ); } return SliverList( delegate: SliverChildListDelegate( diff --git a/lib/features/document_details/view/widgets/document_permissions_widget.dart b/lib/features/document_details/view/widgets/document_permissions_widget.dart new file mode 100644 index 0000000..655fbf6 --- /dev/null +++ b/lib/features/document_details/view/widgets/document_permissions_widget.dart @@ -0,0 +1,12 @@ +import 'package:flutter/material.dart'; + +class DocumentPermissionsWidget extends StatelessWidget { + const DocumentPermissionsWidget({super.key}); + + @override + Widget build(BuildContext context) { + return const SliverToBoxAdapter( + child: Placeholder(), + ); + } +} diff --git a/lib/features/document_edit/view/document_edit_page.dart b/lib/features/document_edit/view/document_edit_page.dart index f40ca2d..baf6127 100644 --- a/lib/features/document_edit/view/document_edit_page.dart +++ b/lib/features/document_edit/view/document_edit_page.dart @@ -264,9 +264,12 @@ class _DocumentEditPageState extends State { var mergedDocument = document.copyWith( title: values[fkTitle], created: values[fkCreatedDate], - documentType: () => (values[fkDocumentType] as SetIdQueryParameter).id, - correspondent: () => (values[fkCorrespondent] as SetIdQueryParameter).id, - storagePath: () => (values[fkStoragePath] as SetIdQueryParameter).id, + documentType: () => + (values[fkDocumentType] as IdQueryParameter).whenOrNull(fromId: (id) => id), + correspondent: () => + (values[fkCorrespondent] as IdQueryParameter).whenOrNull(fromId: (id) => id), + storagePath: () => + (values[fkStoragePath] as IdQueryParameter).whenOrNull(fromId: (id) => id), tags: (values[fkTags] as IdsTagsQuery).include, content: values[fkContent], ); diff --git a/lib/features/document_scan/view/scanner_page.dart b/lib/features/document_scan/view/scanner_page.dart index ae60b2e..d396903 100644 --- a/lib/features/document_scan/view/scanner_page.dart +++ b/lib/features/document_scan/view/scanner_page.dart @@ -11,7 +11,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/global/constants.dart'; -import 'package:paperless_mobile/core/repository/label_repository.dart'; import 'package:paperless_mobile/core/service/file_description.dart'; import 'package:paperless_mobile/core/service/file_service.dart'; import 'package:paperless_mobile/features/app_drawer/view/app_drawer.dart'; @@ -23,7 +22,6 @@ import 'package:paperless_mobile/features/document_upload/view/document_upload_p import 'package:paperless_mobile/features/documents/view/pages/document_view.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/file_helpers.dart'; import 'package:paperless_mobile/helpers/message_helpers.dart'; import 'package:paperless_mobile/helpers/permission_helpers.dart'; import 'package:path/path.dart' as p; diff --git a/lib/features/document_search/view/document_search_page.dart b/lib/features/document_search/view/document_search_page.dart index f28a701..14803f3 100644 --- a/lib/features/document_search/view/document_search_page.dart +++ b/lib/features/document_search/view/document_search_page.dart @@ -3,10 +3,15 @@ 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'; @@ -17,23 +22,7 @@ import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; import 'package:paperless_mobile/routes/document_details_route.dart'; import 'dart:math' as math; -Future showDocumentSearchPage(BuildContext context) { - final currentUser = - Hive.box(HiveBoxes.globalSettings).getValue()!.currentLoggedInUser; - return Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => BlocProvider( - create: (context) => DocumentSearchCubit( - context.read(), - context.read(), - context.read(), - Hive.box(HiveBoxes.localUserAppState).get(currentUser)!, - ), - child: const DocumentSearchPage(), - ), - ), - ); -} +import 'package:provider/provider.dart'; class DocumentSearchPage extends StatefulWidget { const DocumentSearchPage({super.key}); diff --git a/lib/features/document_search/view/sliver_search_bar.dart b/lib/features/document_search/view/sliver_search_bar.dart index 0f36ff7..ba1feba 100644 --- a/lib/features/document_search/view/sliver_search_bar.dart +++ b/lib/features/document_search/view/sliver_search_bar.dart @@ -1,16 +1,17 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:hive_flutter/adapters.dart'; -import 'package:paperless_mobile/core/bloc/server_information_cubit.dart'; import 'package:paperless_mobile/core/config/hive/hive_config.dart'; import 'package:paperless_mobile/core/database/tables/local_user_account.dart'; import 'package:paperless_mobile/core/delegate/customizable_sliver_persistent_header_delegate.dart'; +import 'package:paperless_mobile/core/navigation/push_routes.dart'; import 'package:paperless_mobile/core/widgets/material/search/m3_search_bar.dart' as s; -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/settings/view/manage_accounts_page.dart'; import 'package:paperless_mobile/features/settings/view/widgets/global_settings_builder.dart'; import 'package:paperless_mobile/features/settings/view/widgets/user_avatar.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; +import 'package:provider/provider.dart'; class SliverSearchBar extends StatelessWidget { final bool floating; @@ -34,7 +35,7 @@ class SliverSearchBar extends StatelessWidget { child: s.SearchBar( height: kToolbarHeight, supportingText: S.of(context)!.searchDocuments, - onTap: () => showDocumentSearchPage(context), + onTap: () => pushDocumentSearchPage(context), leadingIcon: IconButton( icon: const Icon(Icons.menu), onPressed: Scaffold.of(context).openDrawer, @@ -58,8 +59,8 @@ class SliverSearchBar extends StatelessWidget { onPressed: () { showDialog( context: context, - builder: (_) => BlocProvider.value( - value: context.read(), + builder: (_) => Provider.value( + value: context.read(), child: const ManageAccountsPage(), ), ); diff --git a/lib/features/documents/cubit/documents_cubit.dart b/lib/features/documents/cubit/documents_cubit.dart index 36297f5..78226a8 100644 --- a/lib/features/documents/cubit/documents_cubit.dart +++ b/lib/features/documents/cubit/documents_cubit.dart @@ -14,7 +14,7 @@ import 'package:paperless_mobile/features/settings/model/view_type.dart'; part 'documents_cubit.g.dart'; part 'documents_state.dart'; -class DocumentsCubit extends Cubit with DocumentPagingBlocMixin { +class DocumentsCubit extends HydratedCubit with DocumentPagingBlocMixin { @override final PaperlessDocumentsApi api; @@ -123,4 +123,14 @@ class DocumentsCubit extends Cubit with DocumentPagingBlocMixin _userState.currentDocumentFilter = filter; await _userState.save(); } + + @override + DocumentsState? fromJson(Map json) { + return DocumentsState.fromJson(json); + } + + @override + Map? toJson(DocumentsState state) { + return state.toJson(); + } } diff --git a/lib/features/documents/view/pages/document_view.dart b/lib/features/documents/view/pages/document_view.dart index 09131cf..e7fa0e8 100644 --- a/lib/features/documents/view/pages/document_view.dart +++ b/lib/features/documents/view/pages/document_view.dart @@ -2,8 +2,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; -import 'package:pdfx/pdfx.dart'; - class DocumentView extends StatefulWidget { final Future documentBytes; @@ -17,36 +15,37 @@ class DocumentView extends StatefulWidget { } class _DocumentViewState extends State { - late PdfController _pdfController; + // late PdfController _pdfController; @override void initState() { super.initState(); - _pdfController = PdfController( - document: PdfDocument.openData( - widget.documentBytes, - ), - ); + // _pdfController = PdfController( + // document: PdfDocument.openData( + // widget.documentBytes, + // ), + // ); } @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text(S.of(context)!.preview), - ), - body: PdfView( - builders: PdfViewBuilders( - options: const DefaultBuilderOptions( - loaderSwitchDuration: Duration(milliseconds: 500), - ), - pageLoaderBuilder: (context) => const Center( - child: CircularProgressIndicator(), - ), - ), - scrollDirection: Axis.vertical, - controller: _pdfController, - ), - ); + return Container(); + // return Scaffold( + // appBar: AppBar( + // title: Text(S.of(context)!.preview), + // ), + // body: PdfView( + // builders: PdfViewBuilders( + // options: const DefaultBuilderOptions( + // loaderSwitchDuration: Duration(milliseconds: 500), + // ), + // pageLoaderBuilder: (context) => const Center( + // child: CircularProgressIndicator(), + // ), + // ), + // scrollDirection: Axis.vertical, + // controller: _pdfController, + // ), + // ); } } diff --git a/lib/features/documents/view/pages/documents_page.dart b/lib/features/documents/view/pages/documents_page.dart index c76f86b..2d9d359 100644 --- a/lib/features/documents/view/pages/documents_page.dart +++ b/lib/features/documents/view/pages/documents_page.dart @@ -5,6 +5,7 @@ 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/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'; @@ -375,10 +376,10 @@ class _DocumentsPageState extends State with SingleTickerProvider builder: (context, state) { return AddSavedViewPage( currentFilter: filter, - correspondents: state.correspondents, - documentTypes: state.documentTypes, - storagePaths: state.storagePaths, - tags: state.tags, + correspondents: context.read().state.correspondents, + documentTypes: context.read().state.documentTypes, + storagePaths: context.read().state.storagePaths, + tags: context.read().state.tags, ); }, ), diff --git a/lib/features/documents/view/widgets/search/sort_field_selection_bottom_sheet.dart b/lib/features/documents/view/widgets/search/sort_field_selection_bottom_sheet.dart index 51669bc..f74b619 100644 --- a/lib/features/documents/view/widgets/search/sort_field_selection_bottom_sheet.dart +++ b/lib/features/documents/view/widgets/search/sort_field_selection_bottom_sheet.dart @@ -1,10 +1,8 @@ import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_mobile/core/translation/sort_field_localization_mapper.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.dart'; -import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; class SortFieldSelectionBottomSheet extends StatefulWidget { @@ -29,12 +27,10 @@ class SortFieldSelectionBottomSheet extends StatefulWidget { }); @override - State createState() => - _SortFieldSelectionBottomSheetState(); + State createState() => _SortFieldSelectionBottomSheetState(); } -class _SortFieldSelectionBottomSheetState - extends State { +class _SortFieldSelectionBottomSheetState extends State { late SortField? _currentSortField; late SortOrder _currentSortOrder; @@ -62,8 +58,8 @@ class _SortFieldSelectionBottomSheetState ), TextButton( child: Text(S.of(context)!.apply), - onPressed: () { - widget.onSubmit( + onPressed: () async { + await widget.onSubmit( _currentSortField, _currentSortOrder, ); @@ -131,7 +127,9 @@ class _SortFieldSelectionBottomSheetState contentPadding: const EdgeInsets.only(left: 32, right: 16), title: Text(translateSortField(context, field)), trailing: _currentSortField == field ? const Icon(Icons.done) : null, - onTap: () => setState(() => _currentSortField = field), + onTap: () { + setState(() => _currentSortField = field); + }, ); } } diff --git a/lib/features/documents/view/widgets/sort_documents_button.dart b/lib/features/documents/view/widgets/sort_documents_button.dart index 822c69c..95909ab 100644 --- a/lib/features/documents/view/widgets/sort_documents_button.dart +++ b/lib/features/documents/view/widgets/sort_documents_button.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.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/translation/sort_field_localization_mapper.dart'; import 'package:paperless_mobile/features/documents/cubit/documents_cubit.dart'; import 'package:paperless_mobile/features/documents/view/widgets/search/sort_field_selection_bottom_sheet.dart'; @@ -21,6 +20,7 @@ class SortDocumentsButton extends StatelessWidget { if (state.filter.sortField == null) { return const SizedBox.shrink(); } + print(state.filter.sortField); return TextButton.icon( icon: Icon(state.filter.sortOrder == SortOrder.ascending ? Icons.arrow_upward @@ -49,14 +49,14 @@ class SortDocumentsButton extends StatelessWidget { child: SortFieldSelectionBottomSheet( initialSortField: state.filter.sortField, initialSortOrder: state.filter.sortOrder, - onSubmit: (field, order) => context - .read() - .updateCurrentFilter( - (filter) => filter.copyWith( - sortField: field, - sortOrder: order, - ), - ), + onSubmit: (field, order) { + return context.read().updateCurrentFilter( + (filter) => filter.copyWith( + sortField: field, + sortOrder: order, + ), + ); + }, correspondents: state.correspondents, documentTypes: state.documentTypes, storagePaths: state.storagePaths, diff --git a/lib/features/home/view/home_page.dart b/lib/features/home/view/home_page.dart index cf075fa..83b5f3b 100644 --- a/lib/features/home/view/home_page.dart +++ b/lib/features/home/view/home_page.dart @@ -9,13 +9,13 @@ 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/bloc/server_information_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/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'; @@ -44,7 +44,8 @@ import 'package:responsive_builder/responsive_builder.dart'; /// Wrapper around all functionality for a logged in user. /// Performs initialization logic. class HomePage extends StatefulWidget { - const HomePage({Key? key}) : super(key: key); + final int paperlessApiVersion; + const HomePage({Key? key, required this.paperlessApiVersion}) : super(key: key); @override _HomePageState createState() => _HomePageState(); @@ -186,8 +187,6 @@ class _HomePageState extends State with WidgetsBindingObserver { @override Widget build(BuildContext context) { - final userId = - Hive.box(HiveBoxes.globalSettings).getValue()!.currentLoggedInUser!; final destinations = [ RouteDescription( icon: const Icon(Icons.description_outlined), @@ -235,6 +234,7 @@ class _HomePageState extends State with WidgetsBindingObserver { ]; final routes = [ const DocumentsPage(), + if (LocalUserAccount.current.paperlessUser.hasPermission(UserPermissions.changeDocument)) const ScannerPage(), const LabelsPage(), const InboxPage(), @@ -303,7 +303,6 @@ class _HomePageState extends State with WidgetsBindingObserver { Future.wait([ context.read().initialize(), context.read().findAll(), - context.read().updateInformation(), ]).onError((error, stackTrace) { showErrorMessage(context, error, stackTrace); throw error; diff --git a/lib/features/home/view/home_route.dart b/lib/features/home/view/home_route.dart index eb08550..3d7928d 100644 --- a/lib/features/home/view/home_route.dart +++ b/lib/features/home/view/home_route.dart @@ -3,18 +3,19 @@ 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'; -import 'package:paperless_mobile/core/bloc/server_information_cubit.dart'; import 'package:paperless_mobile/core/config/hive/hive_config.dart'; import 'package:paperless_mobile/core/database/tables/local_user_app_state.dart'; import 'package:paperless_mobile/core/factory/paperless_api_factory.dart'; import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart'; import 'package:paperless_mobile/core/repository/label_repository.dart'; import 'package:paperless_mobile/core/repository/saved_view_repository.dart'; +import 'package:paperless_mobile/core/repository/user_repository.dart'; import 'package:paperless_mobile/core/security/session_manager.dart'; import 'package:paperless_mobile/core/service/dio_file_service.dart'; import 'package:paperless_mobile/features/document_scan/cubit/document_scanner_cubit.dart'; import 'package:paperless_mobile/features/documents/cubit/documents_cubit.dart'; import 'package:paperless_mobile/features/home/view/home_page.dart'; +import 'package:paperless_mobile/features/home/view/model/api_version.dart'; import 'package:paperless_mobile/features/inbox/cubit/inbox_cubit.dart'; import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart'; import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.dart'; @@ -46,6 +47,7 @@ class HomeRoute extends StatelessWidget { final currentLocalUserId = settings.currentLoggedInUser!; return MultiProvider( providers: [ + Provider.value(value: ApiVersion(paperlessApiVersion)), Provider( create: (context) => CacheManager( Config( @@ -85,6 +87,12 @@ class HomeRoute extends StatelessWidget { apiVersion: paperlessApiVersion, ), ), + if (paperlessApiVersion >= 3) + ProxyProvider( + update: (context, value, previous) => PaperlessUserApiV3Impl( + value.client, + ), + ), ], builder: (context, child) { return MultiProvider( @@ -97,7 +105,7 @@ class HomeRoute extends StatelessWidget { ), ], builder: (context, child) { - return MultiBlocProvider( + return MultiProvider( providers: [ ProxyProvider3( @@ -120,24 +128,23 @@ class HomeRoute extends StatelessWidget { notifier, )..initialize(), ), - ProxyProvider2( - update: (context, savedViewRepo, labelRepo, previous) => SavedViewCubit( + ProxyProvider( + update: (context, savedViewRepo, previous) => SavedViewCubit( savedViewRepo, - labelRepo, )..initialize(), ), - ProxyProvider( - update: (context, value, previous) => - ServerInformationCubit(value)..updateInformation(), - ), ProxyProvider( update: (context, value, previous) => LabelCubit(value), ), ProxyProvider( update: (context, value, previous) => TaskStatusCubit(value), ), + if (paperlessApiVersion >= 3) + ProxyProvider( + update: (context, value, previous) => UserRepository(value)..initialize(), + ), ], - child: const HomePage(), + child: HomePage(paperlessApiVersion: paperlessApiVersion), ); }, ); diff --git a/lib/features/home/view/model/api_version.dart b/lib/features/home/view/model/api_version.dart new file mode 100644 index 0000000..f44beaf --- /dev/null +++ b/lib/features/home/view/model/api_version.dart @@ -0,0 +1,8 @@ +class ApiVersion { + final int version; + + ApiVersion(this.version); + + bool get supportsPermissions => version >= 3; + bool get hasMultiUserSupport => version >= 3; +} diff --git a/lib/features/inbox/view/widgets/inbox_item.dart b/lib/features/inbox/view/widgets/inbox_item.dart index 235f9e1..910d487 100644 --- a/lib/features/inbox/view/widgets/inbox_item.dart +++ b/lib/features/inbox/view/widgets/inbox_item.dart @@ -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/navigation/push_routes.dart'; import 'package:paperless_mobile/core/workarounds/colored_chip.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.dart'; import 'package:paperless_mobile/features/documents/view/widgets/delete_document_confirmation_dialog.dart'; diff --git a/lib/features/linked_documents/view/linked_documents_page.dart b/lib/features/linked_documents/view/linked_documents_page.dart index e8a688b..eeaa148 100644 --- a/lib/features/linked_documents/view/linked_documents_page.dart +++ b/lib/features/linked_documents/view/linked_documents_page.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart'; +import 'package:paperless_mobile/core/navigation/push_routes.dart'; import 'package:paperless_mobile/features/documents/view/widgets/adaptive_documents_view.dart'; import 'package:paperless_mobile/features/documents/view/widgets/selection/view_type_selection_widget.dart'; import 'package:paperless_mobile/features/linked_documents/cubit/linked_documents_cubit.dart'; diff --git a/lib/features/login/cubit/authentication_cubit.dart b/lib/features/login/cubit/authentication_cubit.dart index 042e093..0dc1d76 100644 --- a/lib/features/login/cubit/authentication_cubit.dart +++ b/lib/features/login/cubit/authentication_cubit.dart @@ -3,25 +3,24 @@ import 'dart:typed_data'; import 'package:dio/dio.dart'; import 'package:flutter/widgets.dart'; +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hive_flutter/adapters.dart'; import 'package:hydrated_bloc/hydrated_bloc.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_account.dart'; import 'package:paperless_mobile/core/database/tables/local_user_app_state.dart'; +import 'package:paperless_mobile/core/database/tables/local_user_settings.dart'; +import 'package:paperless_mobile/core/database/tables/user_credentials.dart'; import 'package:paperless_mobile/core/factory/paperless_api_factory.dart'; import 'package:paperless_mobile/core/security/session_manager.dart'; import 'package:paperless_mobile/features/login/model/client_certificate.dart'; import 'package:paperless_mobile/features/login/model/login_form_credentials.dart'; -import 'package:paperless_mobile/core/database/tables/local_user_account.dart'; -import 'package:paperless_mobile/core/database/tables/user_credentials.dart'; import 'package:paperless_mobile/features/login/services/authentication_service.dart'; -import 'package:paperless_mobile/core/database/tables/global_settings.dart'; -import 'package:paperless_mobile/core/database/tables/local_user_settings.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; -part 'authentication_state.dart'; part 'authentication_cubit.freezed.dart'; +part 'authentication_state.dart'; class AuthenticationCubit extends Cubit { final LocalAuthenticationService _localAuthService; @@ -111,6 +110,12 @@ class AuthenticationCubit extends Cubit { final apiVersion = await _getApiVersion(_sessionManager.client); + await _updateRemoteUser( + _sessionManager, + Hive.box(HiveBoxes.localUserAccount).get(localUserId)!, + apiVersion, + ); + emit(AuthenticationState.authenticated( localUserId: localUserId, apiVersion: apiVersion, @@ -163,10 +168,10 @@ class AuthenticationCubit extends Cubit { // If there is nothing to restore, we can quit here. return; } + final localUserAccountBox = Hive.box(HiveBoxes.localUserAccount); + final localUserAccount = localUserAccountBox.get(localUserId)!; - final userAccount = Hive.box(HiveBoxes.localUserAccount).get(localUserId)!; - - if (userAccount.settings.isBiometricAuthenticationEnabled) { + if (localUserAccount.settings.isBiometricAuthenticationEnabled) { final localAuthSuccess = await _localAuthService.authenticateLocalUser("Authenticate to log back in"); //TODO: INTL if (!localAuthSuccess) { @@ -176,18 +181,23 @@ class AuthenticationCubit extends Cubit { } final userCredentialsBox = await _getUserCredentialsBox(); final authentication = userCredentialsBox.get(globalSettings.currentLoggedInUser!); - await userCredentialsBox.close(); if (authentication == null) { - throw Exception("User should be authenticated but no authentication information was found."); + throw Exception( + "User should be authenticated but no authentication information was found."); //TODO: INTL } _sessionManager.updateSettings( clientCertificate: authentication.clientCertificate, authToken: authentication.token, - baseUrl: userAccount.serverUrl, + baseUrl: localUserAccount.serverUrl, ); final apiVersion = await _getApiVersion(_sessionManager.client); + await _updateRemoteUser( + _sessionManager, + localUserAccount, + apiVersion, + ); emit( AuthenticationState.authenticated( apiVersion: apiVersion, @@ -258,7 +268,7 @@ class AuthenticationCubit extends Cubit { clientCertificate: clientCert, authToken: token, ); - + final userAccountBox = Hive.box(HiveBoxes.localUserAccount); final userStateBox = Hive.box(HiveBoxes.localUserAppState); @@ -308,4 +318,20 @@ class AuthenticationCubit extends Cubit { final response = await dio.get("/api/"); return int.parse(response.headers.value('x-api-version') ?? "3"); } + + /// Fetches possibly updated (permissions, name, updated server version and thus new user model, ...) remote user data. + Future _updateRemoteUser( + SessionManager sessionManager, + LocalUserAccount localUserAccount, + int apiVersion, + ) async { + final updatedPaperlessUser = await _apiFactory + .createUserApi( + sessionManager.client, + apiVersion: apiVersion, + ) + .findCurrentUser(); + localUserAccount.paperlessUser = updatedPaperlessUser; + await localUserAccount.save(); + } } diff --git a/lib/features/saved_view/cubit/saved_view_cubit.dart b/lib/features/saved_view/cubit/saved_view_cubit.dart index 76912e8..ca2bff6 100644 --- a/lib/features/saved_view/cubit/saved_view_cubit.dart +++ b/lib/features/saved_view/cubit/saved_view_cubit.dart @@ -11,38 +11,14 @@ part 'saved_view_cubit.freezed.dart'; class SavedViewCubit extends Cubit { final SavedViewRepository _savedViewRepository; - final LabelRepository _labelRepository; - - SavedViewCubit(this._savedViewRepository, this._labelRepository) - : super( - SavedViewState.initial( - correspondents: _labelRepository.state.correspondents, - documentTypes: _labelRepository.state.documentTypes, - storagePaths: _labelRepository.state.storagePaths, - tags: _labelRepository.state.tags, - ), - ) { - _labelRepository.addListener( - this, - onChanged: (labels) { - emit( - state.copyWith( - correspondents: labels.correspondents, - documentTypes: labels.documentTypes, - tags: labels.tags, - storagePaths: labels.storagePaths, - ), - ); - }, - ); + SavedViewCubit(this._savedViewRepository) : super(const SavedViewState.initial()) { _savedViewRepository.addListener( this, onChanged: (views) { emit( state.maybeWhen( - loaded: (savedViews, correspondents, documentTypes, tags, storagePaths) => - (state as _SavedViewLoadedState).copyWith( + loaded: (savedViews) => (state as _SavedViewLoadedState).copyWith( savedViews: views.savedViews, ), orElse: () => state, @@ -64,13 +40,11 @@ class SavedViewCubit extends Cubit { final views = await _savedViewRepository.findAll(); final values = {for (var element in views) element.id!: element}; if (!isClosed) { - emit(SavedViewState.loaded( - savedViews: values, - correspondents: state.correspondents, - documentTypes: state.documentTypes, - storagePaths: state.storagePaths, - tags: state.tags, - )); + emit( + SavedViewState.loaded( + savedViews: values, + ), + ); } } @@ -79,7 +53,6 @@ class SavedViewCubit extends Cubit { @override Future close() { _savedViewRepository.removeListener(this); - _labelRepository.removeListener(this); return super.close(); } } diff --git a/lib/features/saved_view/cubit/saved_view_cubit.freezed.dart b/lib/features/saved_view/cubit/saved_view_cubit.freezed.dart index 64fd200..ab53977 100644 --- a/lib/features/saved_view/cubit/saved_view_cubit.freezed.dart +++ b/lib/features/saved_view/cubit/saved_view_cubit.freezed.dart @@ -16,97 +16,28 @@ final _privateConstructorUsedError = UnsupportedError( /// @nodoc mixin _$SavedViewState { - Map get correspondents => - throw _privateConstructorUsedError; - Map get documentTypes => - throw _privateConstructorUsedError; - Map get tags => throw _privateConstructorUsedError; - Map get storagePaths => throw _privateConstructorUsedError; @optionalTypeArgs TResult when({ - required TResult Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths) - initial, - required TResult Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths) - loading, - required TResult Function( - Map savedViews, - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths) - loaded, - required TResult Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths) - error, + required TResult Function() initial, + required TResult Function() loading, + required TResult Function(Map savedViews) loaded, + required TResult Function() error, }) => throw _privateConstructorUsedError; @optionalTypeArgs TResult? whenOrNull({ - TResult? Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - initial, - TResult? Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - loading, - TResult? Function( - Map savedViews, - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - loaded, - TResult? Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - error, + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function(Map savedViews)? loaded, + TResult? Function()? error, }) => throw _privateConstructorUsedError; @optionalTypeArgs TResult maybeWhen({ - TResult Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - initial, - TResult Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - loading, - TResult Function( - Map savedViews, - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - loaded, - TResult Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - error, + TResult Function()? initial, + TResult Function()? loading, + TResult Function(Map savedViews)? loaded, + TResult Function()? error, required TResult orElse(), }) => throw _privateConstructorUsedError; @@ -135,10 +66,6 @@ mixin _$SavedViewState { required TResult orElse(), }) => throw _privateConstructorUsedError; - - @JsonKey(ignore: true) - $SavedViewStateCopyWith get copyWith => - throw _privateConstructorUsedError; } /// @nodoc @@ -146,12 +73,6 @@ abstract class $SavedViewStateCopyWith<$Res> { factory $SavedViewStateCopyWith( SavedViewState value, $Res Function(SavedViewState) then) = _$SavedViewStateCopyWithImpl<$Res, SavedViewState>; - @useResult - $Res call( - {Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths}); } /// @nodoc @@ -163,49 +84,13 @@ class _$SavedViewStateCopyWithImpl<$Res, $Val extends SavedViewState> final $Val _value; // ignore: unused_field final $Res Function($Val) _then; - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? correspondents = null, - Object? documentTypes = null, - Object? tags = null, - Object? storagePaths = null, - }) { - return _then(_value.copyWith( - correspondents: null == correspondents - ? _value.correspondents - : correspondents // ignore: cast_nullable_to_non_nullable - as Map, - documentTypes: null == documentTypes - ? _value.documentTypes - : documentTypes // ignore: cast_nullable_to_non_nullable - as Map, - tags: null == tags - ? _value.tags - : tags // ignore: cast_nullable_to_non_nullable - as Map, - storagePaths: null == storagePaths - ? _value.storagePaths - : storagePaths // ignore: cast_nullable_to_non_nullable - as Map, - ) as $Val); - } } /// @nodoc -abstract class _$$_SavedViewIntialStateCopyWith<$Res> - implements $SavedViewStateCopyWith<$Res> { +abstract class _$$_SavedViewIntialStateCopyWith<$Res> { factory _$$_SavedViewIntialStateCopyWith(_$_SavedViewIntialState value, $Res Function(_$_SavedViewIntialState) then) = __$$_SavedViewIntialStateCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths}); } /// @nodoc @@ -215,211 +100,60 @@ class __$$_SavedViewIntialStateCopyWithImpl<$Res> __$$_SavedViewIntialStateCopyWithImpl(_$_SavedViewIntialState _value, $Res Function(_$_SavedViewIntialState) _then) : super(_value, _then); - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? correspondents = null, - Object? documentTypes = null, - Object? tags = null, - Object? storagePaths = null, - }) { - return _then(_$_SavedViewIntialState( - correspondents: null == correspondents - ? _value._correspondents - : correspondents // ignore: cast_nullable_to_non_nullable - as Map, - documentTypes: null == documentTypes - ? _value._documentTypes - : documentTypes // ignore: cast_nullable_to_non_nullable - as Map, - tags: null == tags - ? _value._tags - : tags // ignore: cast_nullable_to_non_nullable - as Map, - storagePaths: null == storagePaths - ? _value._storagePaths - : storagePaths // ignore: cast_nullable_to_non_nullable - as Map, - )); - } } /// @nodoc class _$_SavedViewIntialState implements _SavedViewIntialState { - const _$_SavedViewIntialState( - {required final Map correspondents, - required final Map documentTypes, - required final Map tags, - required final Map storagePaths}) - : _correspondents = correspondents, - _documentTypes = documentTypes, - _tags = tags, - _storagePaths = storagePaths; - - final Map _correspondents; - @override - Map get correspondents { - if (_correspondents is EqualUnmodifiableMapView) return _correspondents; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_correspondents); - } - - final Map _documentTypes; - @override - Map get documentTypes { - if (_documentTypes is EqualUnmodifiableMapView) return _documentTypes; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_documentTypes); - } - - final Map _tags; - @override - Map get tags { - if (_tags is EqualUnmodifiableMapView) return _tags; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_tags); - } - - final Map _storagePaths; - @override - Map get storagePaths { - if (_storagePaths is EqualUnmodifiableMapView) return _storagePaths; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_storagePaths); - } + const _$_SavedViewIntialState(); @override String toString() { - return 'SavedViewState.initial(correspondents: $correspondents, documentTypes: $documentTypes, tags: $tags, storagePaths: $storagePaths)'; + return 'SavedViewState.initial()'; } @override bool operator ==(dynamic other) { return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$_SavedViewIntialState && - const DeepCollectionEquality() - .equals(other._correspondents, _correspondents) && - const DeepCollectionEquality() - .equals(other._documentTypes, _documentTypes) && - const DeepCollectionEquality().equals(other._tags, _tags) && - const DeepCollectionEquality() - .equals(other._storagePaths, _storagePaths)); + (other.runtimeType == runtimeType && other is _$_SavedViewIntialState); } @override - int get hashCode => Object.hash( - runtimeType, - const DeepCollectionEquality().hash(_correspondents), - const DeepCollectionEquality().hash(_documentTypes), - const DeepCollectionEquality().hash(_tags), - const DeepCollectionEquality().hash(_storagePaths)); - - @JsonKey(ignore: true) - @override - @pragma('vm:prefer-inline') - _$$_SavedViewIntialStateCopyWith<_$_SavedViewIntialState> get copyWith => - __$$_SavedViewIntialStateCopyWithImpl<_$_SavedViewIntialState>( - this, _$identity); + int get hashCode => runtimeType.hashCode; @override @optionalTypeArgs TResult when({ - required TResult Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths) - initial, - required TResult Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths) - loading, - required TResult Function( - Map savedViews, - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths) - loaded, - required TResult Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths) - error, + required TResult Function() initial, + required TResult Function() loading, + required TResult Function(Map savedViews) loaded, + required TResult Function() error, }) { - return initial(correspondents, documentTypes, tags, storagePaths); + return initial(); } @override @optionalTypeArgs TResult? whenOrNull({ - TResult? Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - initial, - TResult? Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - loading, - TResult? Function( - Map savedViews, - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - loaded, - TResult? Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - error, + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function(Map savedViews)? loaded, + TResult? Function()? error, }) { - return initial?.call(correspondents, documentTypes, tags, storagePaths); + return initial?.call(); } @override @optionalTypeArgs TResult maybeWhen({ - TResult Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - initial, - TResult Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - loading, - TResult Function( - Map savedViews, - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - loaded, - TResult Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - error, + TResult Function()? initial, + TResult Function()? loading, + TResult Function(Map savedViews)? loaded, + TResult Function()? error, required TResult orElse(), }) { if (initial != null) { - return initial(correspondents, documentTypes, tags, storagePaths); + return initial(); } return orElse(); } @@ -463,40 +197,14 @@ class _$_SavedViewIntialState implements _SavedViewIntialState { } abstract class _SavedViewIntialState implements SavedViewState { - const factory _SavedViewIntialState( - {required final Map correspondents, - required final Map documentTypes, - required final Map tags, - required final Map storagePaths}) = - _$_SavedViewIntialState; - - @override - Map get correspondents; - @override - Map get documentTypes; - @override - Map get tags; - @override - Map get storagePaths; - @override - @JsonKey(ignore: true) - _$$_SavedViewIntialStateCopyWith<_$_SavedViewIntialState> get copyWith => - throw _privateConstructorUsedError; + const factory _SavedViewIntialState() = _$_SavedViewIntialState; } /// @nodoc -abstract class _$$_SavedViewLoadingStateCopyWith<$Res> - implements $SavedViewStateCopyWith<$Res> { +abstract class _$$_SavedViewLoadingStateCopyWith<$Res> { factory _$$_SavedViewLoadingStateCopyWith(_$_SavedViewLoadingState value, $Res Function(_$_SavedViewLoadingState) then) = __$$_SavedViewLoadingStateCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths}); } /// @nodoc @@ -506,211 +214,60 @@ class __$$_SavedViewLoadingStateCopyWithImpl<$Res> __$$_SavedViewLoadingStateCopyWithImpl(_$_SavedViewLoadingState _value, $Res Function(_$_SavedViewLoadingState) _then) : super(_value, _then); - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? correspondents = null, - Object? documentTypes = null, - Object? tags = null, - Object? storagePaths = null, - }) { - return _then(_$_SavedViewLoadingState( - correspondents: null == correspondents - ? _value._correspondents - : correspondents // ignore: cast_nullable_to_non_nullable - as Map, - documentTypes: null == documentTypes - ? _value._documentTypes - : documentTypes // ignore: cast_nullable_to_non_nullable - as Map, - tags: null == tags - ? _value._tags - : tags // ignore: cast_nullable_to_non_nullable - as Map, - storagePaths: null == storagePaths - ? _value._storagePaths - : storagePaths // ignore: cast_nullable_to_non_nullable - as Map, - )); - } } /// @nodoc class _$_SavedViewLoadingState implements _SavedViewLoadingState { - const _$_SavedViewLoadingState( - {required final Map correspondents, - required final Map documentTypes, - required final Map tags, - required final Map storagePaths}) - : _correspondents = correspondents, - _documentTypes = documentTypes, - _tags = tags, - _storagePaths = storagePaths; - - final Map _correspondents; - @override - Map get correspondents { - if (_correspondents is EqualUnmodifiableMapView) return _correspondents; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_correspondents); - } - - final Map _documentTypes; - @override - Map get documentTypes { - if (_documentTypes is EqualUnmodifiableMapView) return _documentTypes; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_documentTypes); - } - - final Map _tags; - @override - Map get tags { - if (_tags is EqualUnmodifiableMapView) return _tags; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_tags); - } - - final Map _storagePaths; - @override - Map get storagePaths { - if (_storagePaths is EqualUnmodifiableMapView) return _storagePaths; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_storagePaths); - } + const _$_SavedViewLoadingState(); @override String toString() { - return 'SavedViewState.loading(correspondents: $correspondents, documentTypes: $documentTypes, tags: $tags, storagePaths: $storagePaths)'; + return 'SavedViewState.loading()'; } @override bool operator ==(dynamic other) { return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$_SavedViewLoadingState && - const DeepCollectionEquality() - .equals(other._correspondents, _correspondents) && - const DeepCollectionEquality() - .equals(other._documentTypes, _documentTypes) && - const DeepCollectionEquality().equals(other._tags, _tags) && - const DeepCollectionEquality() - .equals(other._storagePaths, _storagePaths)); + (other.runtimeType == runtimeType && other is _$_SavedViewLoadingState); } @override - int get hashCode => Object.hash( - runtimeType, - const DeepCollectionEquality().hash(_correspondents), - const DeepCollectionEquality().hash(_documentTypes), - const DeepCollectionEquality().hash(_tags), - const DeepCollectionEquality().hash(_storagePaths)); - - @JsonKey(ignore: true) - @override - @pragma('vm:prefer-inline') - _$$_SavedViewLoadingStateCopyWith<_$_SavedViewLoadingState> get copyWith => - __$$_SavedViewLoadingStateCopyWithImpl<_$_SavedViewLoadingState>( - this, _$identity); + int get hashCode => runtimeType.hashCode; @override @optionalTypeArgs TResult when({ - required TResult Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths) - initial, - required TResult Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths) - loading, - required TResult Function( - Map savedViews, - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths) - loaded, - required TResult Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths) - error, + required TResult Function() initial, + required TResult Function() loading, + required TResult Function(Map savedViews) loaded, + required TResult Function() error, }) { - return loading(correspondents, documentTypes, tags, storagePaths); + return loading(); } @override @optionalTypeArgs TResult? whenOrNull({ - TResult? Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - initial, - TResult? Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - loading, - TResult? Function( - Map savedViews, - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - loaded, - TResult? Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - error, + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function(Map savedViews)? loaded, + TResult? Function()? error, }) { - return loading?.call(correspondents, documentTypes, tags, storagePaths); + return loading?.call(); } @override @optionalTypeArgs TResult maybeWhen({ - TResult Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - initial, - TResult Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - loading, - TResult Function( - Map savedViews, - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - loaded, - TResult Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - error, + TResult Function()? initial, + TResult Function()? loading, + TResult Function(Map savedViews)? loaded, + TResult Function()? error, required TResult orElse(), }) { if (loading != null) { - return loading(correspondents, documentTypes, tags, storagePaths); + return loading(); } return orElse(); } @@ -754,41 +311,16 @@ class _$_SavedViewLoadingState implements _SavedViewLoadingState { } abstract class _SavedViewLoadingState implements SavedViewState { - const factory _SavedViewLoadingState( - {required final Map correspondents, - required final Map documentTypes, - required final Map tags, - required final Map storagePaths}) = - _$_SavedViewLoadingState; - - @override - Map get correspondents; - @override - Map get documentTypes; - @override - Map get tags; - @override - Map get storagePaths; - @override - @JsonKey(ignore: true) - _$$_SavedViewLoadingStateCopyWith<_$_SavedViewLoadingState> get copyWith => - throw _privateConstructorUsedError; + const factory _SavedViewLoadingState() = _$_SavedViewLoadingState; } /// @nodoc -abstract class _$$_SavedViewLoadedStateCopyWith<$Res> - implements $SavedViewStateCopyWith<$Res> { +abstract class _$$_SavedViewLoadedStateCopyWith<$Res> { factory _$$_SavedViewLoadedStateCopyWith(_$_SavedViewLoadedState value, $Res Function(_$_SavedViewLoadedState) then) = __$$_SavedViewLoadedStateCopyWithImpl<$Res>; - @override @useResult - $Res call( - {Map savedViews, - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths}); + $Res call({Map savedViews}); } /// @nodoc @@ -803,32 +335,12 @@ class __$$_SavedViewLoadedStateCopyWithImpl<$Res> @override $Res call({ Object? savedViews = null, - Object? correspondents = null, - Object? documentTypes = null, - Object? tags = null, - Object? storagePaths = null, }) { return _then(_$_SavedViewLoadedState( savedViews: null == savedViews ? _value._savedViews : savedViews // ignore: cast_nullable_to_non_nullable as Map, - correspondents: null == correspondents - ? _value._correspondents - : correspondents // ignore: cast_nullable_to_non_nullable - as Map, - documentTypes: null == documentTypes - ? _value._documentTypes - : documentTypes // ignore: cast_nullable_to_non_nullable - as Map, - tags: null == tags - ? _value._tags - : tags // ignore: cast_nullable_to_non_nullable - as Map, - storagePaths: null == storagePaths - ? _value._storagePaths - : storagePaths // ignore: cast_nullable_to_non_nullable - as Map, )); } } @@ -836,17 +348,8 @@ class __$$_SavedViewLoadedStateCopyWithImpl<$Res> /// @nodoc class _$_SavedViewLoadedState implements _SavedViewLoadedState { - const _$_SavedViewLoadedState( - {required final Map savedViews, - required final Map correspondents, - required final Map documentTypes, - required final Map tags, - required final Map storagePaths}) - : _savedViews = savedViews, - _correspondents = correspondents, - _documentTypes = documentTypes, - _tags = tags, - _storagePaths = storagePaths; + const _$_SavedViewLoadedState({required final Map savedViews}) + : _savedViews = savedViews; final Map _savedViews; @override @@ -856,41 +359,9 @@ class _$_SavedViewLoadedState implements _SavedViewLoadedState { return EqualUnmodifiableMapView(_savedViews); } - final Map _correspondents; - @override - Map get correspondents { - if (_correspondents is EqualUnmodifiableMapView) return _correspondents; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_correspondents); - } - - final Map _documentTypes; - @override - Map get documentTypes { - if (_documentTypes is EqualUnmodifiableMapView) return _documentTypes; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_documentTypes); - } - - final Map _tags; - @override - Map get tags { - if (_tags is EqualUnmodifiableMapView) return _tags; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_tags); - } - - final Map _storagePaths; - @override - Map get storagePaths { - if (_storagePaths is EqualUnmodifiableMapView) return _storagePaths; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_storagePaths); - } - @override String toString() { - return 'SavedViewState.loaded(savedViews: $savedViews, correspondents: $correspondents, documentTypes: $documentTypes, tags: $tags, storagePaths: $storagePaths)'; + return 'SavedViewState.loaded(savedViews: $savedViews)'; } @override @@ -899,24 +370,12 @@ class _$_SavedViewLoadedState implements _SavedViewLoadedState { (other.runtimeType == runtimeType && other is _$_SavedViewLoadedState && const DeepCollectionEquality() - .equals(other._savedViews, _savedViews) && - const DeepCollectionEquality() - .equals(other._correspondents, _correspondents) && - const DeepCollectionEquality() - .equals(other._documentTypes, _documentTypes) && - const DeepCollectionEquality().equals(other._tags, _tags) && - const DeepCollectionEquality() - .equals(other._storagePaths, _storagePaths)); + .equals(other._savedViews, _savedViews)); } @override int get hashCode => Object.hash( - runtimeType, - const DeepCollectionEquality().hash(_savedViews), - const DeepCollectionEquality().hash(_correspondents), - const DeepCollectionEquality().hash(_documentTypes), - const DeepCollectionEquality().hash(_tags), - const DeepCollectionEquality().hash(_storagePaths)); + runtimeType, const DeepCollectionEquality().hash(_savedViews)); @JsonKey(ignore: true) @override @@ -928,102 +387,36 @@ class _$_SavedViewLoadedState implements _SavedViewLoadedState { @override @optionalTypeArgs TResult when({ - required TResult Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths) - initial, - required TResult Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths) - loading, - required TResult Function( - Map savedViews, - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths) - loaded, - required TResult Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths) - error, + required TResult Function() initial, + required TResult Function() loading, + required TResult Function(Map savedViews) loaded, + required TResult Function() error, }) { - return loaded( - savedViews, correspondents, documentTypes, tags, storagePaths); + return loaded(savedViews); } @override @optionalTypeArgs TResult? whenOrNull({ - TResult? Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - initial, - TResult? Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - loading, - TResult? Function( - Map savedViews, - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - loaded, - TResult? Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - error, + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function(Map savedViews)? loaded, + TResult? Function()? error, }) { - return loaded?.call( - savedViews, correspondents, documentTypes, tags, storagePaths); + return loaded?.call(savedViews); } @override @optionalTypeArgs TResult maybeWhen({ - TResult Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - initial, - TResult Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - loading, - TResult Function( - Map savedViews, - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - loaded, - TResult Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - error, + TResult Function()? initial, + TResult Function()? loading, + TResult Function(Map savedViews)? loaded, + TResult Function()? error, required TResult orElse(), }) { if (loaded != null) { - return loaded( - savedViews, correspondents, documentTypes, tags, storagePaths); + return loaded(savedViews); } return orElse(); } @@ -1068,41 +461,20 @@ class _$_SavedViewLoadedState implements _SavedViewLoadedState { abstract class _SavedViewLoadedState implements SavedViewState { const factory _SavedViewLoadedState( - {required final Map savedViews, - required final Map correspondents, - required final Map documentTypes, - required final Map tags, - required final Map storagePaths}) = + {required final Map savedViews}) = _$_SavedViewLoadedState; Map get savedViews; - @override - Map get correspondents; - @override - Map get documentTypes; - @override - Map get tags; - @override - Map get storagePaths; - @override @JsonKey(ignore: true) _$$_SavedViewLoadedStateCopyWith<_$_SavedViewLoadedState> get copyWith => throw _privateConstructorUsedError; } /// @nodoc -abstract class _$$_SavedViewErrorStateCopyWith<$Res> - implements $SavedViewStateCopyWith<$Res> { +abstract class _$$_SavedViewErrorStateCopyWith<$Res> { factory _$$_SavedViewErrorStateCopyWith(_$_SavedViewErrorState value, $Res Function(_$_SavedViewErrorState) then) = __$$_SavedViewErrorStateCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths}); } /// @nodoc @@ -1112,211 +484,60 @@ class __$$_SavedViewErrorStateCopyWithImpl<$Res> __$$_SavedViewErrorStateCopyWithImpl(_$_SavedViewErrorState _value, $Res Function(_$_SavedViewErrorState) _then) : super(_value, _then); - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? correspondents = null, - Object? documentTypes = null, - Object? tags = null, - Object? storagePaths = null, - }) { - return _then(_$_SavedViewErrorState( - correspondents: null == correspondents - ? _value._correspondents - : correspondents // ignore: cast_nullable_to_non_nullable - as Map, - documentTypes: null == documentTypes - ? _value._documentTypes - : documentTypes // ignore: cast_nullable_to_non_nullable - as Map, - tags: null == tags - ? _value._tags - : tags // ignore: cast_nullable_to_non_nullable - as Map, - storagePaths: null == storagePaths - ? _value._storagePaths - : storagePaths // ignore: cast_nullable_to_non_nullable - as Map, - )); - } } /// @nodoc class _$_SavedViewErrorState implements _SavedViewErrorState { - const _$_SavedViewErrorState( - {required final Map correspondents, - required final Map documentTypes, - required final Map tags, - required final Map storagePaths}) - : _correspondents = correspondents, - _documentTypes = documentTypes, - _tags = tags, - _storagePaths = storagePaths; - - final Map _correspondents; - @override - Map get correspondents { - if (_correspondents is EqualUnmodifiableMapView) return _correspondents; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_correspondents); - } - - final Map _documentTypes; - @override - Map get documentTypes { - if (_documentTypes is EqualUnmodifiableMapView) return _documentTypes; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_documentTypes); - } - - final Map _tags; - @override - Map get tags { - if (_tags is EqualUnmodifiableMapView) return _tags; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_tags); - } - - final Map _storagePaths; - @override - Map get storagePaths { - if (_storagePaths is EqualUnmodifiableMapView) return _storagePaths; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_storagePaths); - } + const _$_SavedViewErrorState(); @override String toString() { - return 'SavedViewState.error(correspondents: $correspondents, documentTypes: $documentTypes, tags: $tags, storagePaths: $storagePaths)'; + return 'SavedViewState.error()'; } @override bool operator ==(dynamic other) { return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$_SavedViewErrorState && - const DeepCollectionEquality() - .equals(other._correspondents, _correspondents) && - const DeepCollectionEquality() - .equals(other._documentTypes, _documentTypes) && - const DeepCollectionEquality().equals(other._tags, _tags) && - const DeepCollectionEquality() - .equals(other._storagePaths, _storagePaths)); + (other.runtimeType == runtimeType && other is _$_SavedViewErrorState); } @override - int get hashCode => Object.hash( - runtimeType, - const DeepCollectionEquality().hash(_correspondents), - const DeepCollectionEquality().hash(_documentTypes), - const DeepCollectionEquality().hash(_tags), - const DeepCollectionEquality().hash(_storagePaths)); - - @JsonKey(ignore: true) - @override - @pragma('vm:prefer-inline') - _$$_SavedViewErrorStateCopyWith<_$_SavedViewErrorState> get copyWith => - __$$_SavedViewErrorStateCopyWithImpl<_$_SavedViewErrorState>( - this, _$identity); + int get hashCode => runtimeType.hashCode; @override @optionalTypeArgs TResult when({ - required TResult Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths) - initial, - required TResult Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths) - loading, - required TResult Function( - Map savedViews, - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths) - loaded, - required TResult Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths) - error, + required TResult Function() initial, + required TResult Function() loading, + required TResult Function(Map savedViews) loaded, + required TResult Function() error, }) { - return error(correspondents, documentTypes, tags, storagePaths); + return error(); } @override @optionalTypeArgs TResult? whenOrNull({ - TResult? Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - initial, - TResult? Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - loading, - TResult? Function( - Map savedViews, - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - loaded, - TResult? Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - error, + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function(Map savedViews)? loaded, + TResult? Function()? error, }) { - return error?.call(correspondents, documentTypes, tags, storagePaths); + return error?.call(); } @override @optionalTypeArgs TResult maybeWhen({ - TResult Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - initial, - TResult Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - loading, - TResult Function( - Map savedViews, - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - loaded, - TResult Function( - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths)? - error, + TResult Function()? initial, + TResult Function()? loading, + TResult Function(Map savedViews)? loaded, + TResult Function()? error, required TResult orElse(), }) { if (error != null) { - return error(correspondents, documentTypes, tags, storagePaths); + return error(); } return orElse(); } @@ -1360,23 +581,5 @@ class _$_SavedViewErrorState implements _SavedViewErrorState { } abstract class _SavedViewErrorState implements SavedViewState { - const factory _SavedViewErrorState( - {required final Map correspondents, - required final Map documentTypes, - required final Map tags, - required final Map storagePaths}) = - _$_SavedViewErrorState; - - @override - Map get correspondents; - @override - Map get documentTypes; - @override - Map get tags; - @override - Map get storagePaths; - @override - @JsonKey(ignore: true) - _$$_SavedViewErrorStateCopyWith<_$_SavedViewErrorState> get copyWith => - throw _privateConstructorUsedError; + const factory _SavedViewErrorState() = _$_SavedViewErrorState; } diff --git a/lib/features/saved_view/cubit/saved_view_state.dart b/lib/features/saved_view/cubit/saved_view_state.dart index b886840..c73f123 100644 --- a/lib/features/saved_view/cubit/saved_view_state.dart +++ b/lib/features/saved_view/cubit/saved_view_state.dart @@ -2,32 +2,12 @@ part of 'saved_view_cubit.dart'; @freezed class SavedViewState with _$SavedViewState { - const factory SavedViewState.initial({ - required Map correspondents, - required Map documentTypes, - required Map tags, - required Map storagePaths, - }) = _SavedViewIntialState; + const factory SavedViewState.initial() = _SavedViewIntialState; - const factory SavedViewState.loading({ - required Map correspondents, - required Map documentTypes, - required Map tags, - required Map storagePaths, - }) = _SavedViewLoadingState; + const factory SavedViewState.loading() = _SavedViewLoadingState; - const factory SavedViewState.loaded({ - required Map savedViews, - required Map correspondents, - required Map documentTypes, - required Map tags, - required Map storagePaths, - }) = _SavedViewLoadedState; + const factory SavedViewState.loaded({required Map savedViews}) = + _SavedViewLoadedState; - const factory SavedViewState.error({ - required Map correspondents, - required Map documentTypes, - required Map tags, - required Map storagePaths, - }) = _SavedViewErrorState; + const factory SavedViewState.error() = _SavedViewErrorState; } diff --git a/lib/features/saved_view/view/saved_view_list.dart b/lib/features/saved_view/view/saved_view_list.dart index 29b263f..5f6ac15 100644 --- a/lib/features/saved_view/view/saved_view_list.dart +++ b/lib/features/saved_view/view/saved_view_list.dart @@ -5,6 +5,7 @@ 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'; @@ -21,17 +22,13 @@ class SavedViewList extends StatelessWidget { return BlocBuilder( builder: (context, state) { return state.when( - initial: (correspondents, documentTypes, tags, storagePaths) => Container(), - loading: (correspondents, documentTypes, tags, storagePaths) => Center( - child: Text("Saved views loading..."), //TODO: INTL + initial: () => SliverToBoxAdapter(child: Container()), + loading: () => SliverToBoxAdapter( + child: Center( + child: Text("Saved views loading..."), //TODO: INTL + ), ), - loaded: ( - savedViews, - correspondents, - documentTypes, - tags, - storagePaths, - ) { + loaded: (savedViews) { if (savedViews.isEmpty) { return SliverToBoxAdapter( child: HintCard( @@ -50,30 +47,7 @@ class SavedViewList extends StatelessWidget { S.of(context)!.nFiltersSet(view.filterRules.length), ), onTap: () { - Navigator.of(context).push( - MaterialPageRoute( - builder: (ctxt) => MultiBlocProvider( - providers: [ - BlocProvider( - create: (context) => SavedViewDetailsCubit( - ctxt.read(), - ctxt.read(), - context.read(), - Hive.box(HiveBoxes.localUserAppState).get( - Hive.box(HiveBoxes.globalSettings) - .getValue()! - .currentLoggedInUser!, - )!, - savedView: view, - ), - ), - ], - child: SavedViewDetailsPage( - onDelete: context.read().remove, - ), - ), - ), - ); + pushSavedViewDetailsRoute(context, savedView: view); }, ); }, @@ -81,13 +55,7 @@ class SavedViewList extends StatelessWidget { ), ); }, - error: ( - correspondents, - documentTypes, - tags, - storagePaths, - ) => - Center( + error: () => Center( child: Text( "An error occurred while trying to load the saved views.", ), diff --git a/lib/features/saved_view_details/view/saved_view_details_page.dart b/lib/features/saved_view_details/view/saved_view_details_page.dart index 7bc08d9..234d21f 100644 --- a/lib/features/saved_view_details/view/saved_view_details_page.dart +++ b/lib/features/saved_view_details/view/saved_view_details_page.dart @@ -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/navigation/push_routes.dart'; import 'package:paperless_mobile/features/documents/view/widgets/adaptive_documents_view.dart'; import 'package:paperless_mobile/features/documents/view/widgets/documents_empty_state.dart'; import 'package:paperless_mobile/features/documents/view/widgets/selection/confirm_delete_saved_view_dialog.dart'; @@ -76,14 +77,10 @@ class _SavedViewDetailsPageState extends State isLoading: state.isLoading, hasLoaded: state.hasLoaded, onTap: (document) { - Navigator.push( + pushDocumentDetailsRoute( context, - MaterialPageRoute( - builder: (context) => DocumentDetailsRoute( - document: document, - isLabelClickable: false, - ), - ), + document: document, + isLabelClickable: false, ); }, viewType: state.viewType, diff --git a/lib/features/settings/view/manage_accounts_page.dart b/lib/features/settings/view/manage_accounts_page.dart index 55b8a95..edec819 100644 --- a/lib/features/settings/view/manage_accounts_page.dart +++ b/lib/features/settings/view/manage_accounts_page.dart @@ -5,6 +5,7 @@ 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'; import 'package:paperless_mobile/core/database/tables/local_user_account.dart'; +import 'package:paperless_mobile/features/home/view/model/api_version.dart'; import 'package:paperless_mobile/features/login/cubit/authentication_cubit.dart'; import 'package:paperless_mobile/features/login/model/login_form_credentials.dart'; import 'package:paperless_mobile/features/login/view/login_page.dart'; @@ -13,6 +14,7 @@ import 'package:paperless_mobile/features/settings/view/pages/switching_accounts import 'package:paperless_mobile/features/settings/view/widgets/global_settings_builder.dart'; import 'package:paperless_mobile/features/settings/view/widgets/user_avatar.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; +import 'package:provider/provider.dart'; class ManageAccountsPage extends StatelessWidget { const ManageAccountsPage({super.key}); @@ -67,6 +69,17 @@ class ManageAccountsPage extends StatelessWidget { _onAddAccount(context, globalSettings.currentLoggedInUser!); }, ), + Consumer( + 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(); + }, + ) ], ); }, diff --git a/lib/features/settings/view/settings_page.dart b/lib/features/settings/view/settings_page.dart index d3614fd..a7d9f9b 100644 --- a/lib/features/settings/view/settings_page.dart +++ b/lib/features/settings/view/settings_page.dart @@ -1,7 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:paperless_mobile/core/bloc/server_information_cubit.dart'; -import 'package:paperless_mobile/core/bloc/server_information_state.dart'; +import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_mobile/features/settings/view/pages/application_settings_page.dart'; import 'package:paperless_mobile/features/settings/view/pages/security_settings_page.dart'; import 'package:paperless_mobile/features/settings/view/widgets/user_settings_builder.dart'; @@ -26,13 +24,21 @@ class SettingsPage extends StatelessWidget { style: Theme.of(context).textTheme.labelSmall, textAlign: TextAlign.center, ), - subtitle: BlocBuilder( - builder: (context, state) { + subtitle: FutureBuilder( + builder: (context, snapshot) { + if (!snapshot.hasData) { + return Text( + "Loading server information...", + style: Theme.of(context).textTheme.labelSmall, + textAlign: TextAlign.center, + ); + } + final serverData = snapshot.data!; return Text( S.of(context)!.paperlessServerVersion + ' ' + - state.information!.version.toString() + - ' (API v${state.information!.apiVersion})', + serverData.version.toString() + + ' (API v${serverData.apiVersion})', style: Theme.of(context).textTheme.labelSmall, textAlign: TextAlign.center, ); diff --git a/lib/features/similar_documents/view/similar_documents_view.dart b/lib/features/similar_documents/view/similar_documents_view.dart index 3b9352f..a12a466 100644 --- a/lib/features/similar_documents/view/similar_documents_view.dart +++ b/lib/features/similar_documents/view/similar_documents_view.dart @@ -2,14 +2,13 @@ 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/navigation/push_routes.dart'; import 'package:paperless_mobile/core/widgets/offline_widget.dart'; import 'package:paperless_mobile/features/documents/view/widgets/adaptive_documents_view.dart'; -import 'package:paperless_mobile/features/documents/view/widgets/documents_empty_state.dart'; import 'package:paperless_mobile/features/paged_document_view/view/document_paging_view_mixin.dart'; import 'package:paperless_mobile/features/similar_documents/cubit/similar_documents_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 SimilarDocumentsView extends StatefulWidget { final ScrollController pagingScrollController; diff --git a/lib/main.dart b/lib/main.dart index 792b4b1..eea1556 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -41,7 +41,6 @@ import 'package:paperless_mobile/features/settings/view/widgets/global_settings_ import 'package:paperless_mobile/features/sharing/share_intent_queue.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'; import 'package:paperless_mobile/theme.dart'; import 'package:path_provider/path_provider.dart'; import 'package:provider/provider.dart'; @@ -122,7 +121,6 @@ void main() async { }); final apiFactory = PaperlessApiFactoryImpl(sessionManager); - runApp( MultiProvider( providers: [ @@ -220,10 +218,9 @@ class _AuthenticationWrapperState extends State { @override void didChangeDependencies() { super.didChangeDependencies(); - context - .read() - .restoreSessionState() - .then((value) => FlutterNativeSplash.remove()); + context.read().restoreSessionState().then((value) { + FlutterNativeSplash.remove(); + }); } @override diff --git a/lib/routes/document_details_route.dart b/lib/routes/document_details_route.dart index c2e1afa..a90001e 100644 --- a/lib/routes/document_details_route.dart +++ b/lib/routes/document_details_route.dart @@ -1,26 +1,19 @@ import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_cache_manager/flutter_cache_manager.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'; import 'package:paperless_mobile/features/document_details/cubit/document_details_cubit.dart'; import 'package:paperless_mobile/features/document_details/view/pages/document_details_page.dart'; -import 'package:paperless_mobile/features/notifications/services/local_notification_service.dart'; -import 'package:provider/provider.dart'; class DocumentDetailsRoute extends StatelessWidget { final DocumentModel document; final bool isLabelClickable; - final bool allowEdit; final String? titleAndContentQueryString; const DocumentDetailsRoute({ super.key, required this.document, this.isLabelClickable = true, - this.allowEdit = true, this.titleAndContentQueryString, }); @@ -36,7 +29,6 @@ class DocumentDetailsRoute extends StatelessWidget { ), lazy: false, child: DocumentDetailsPage( - allowEdit: allowEdit, isLabelClickable: isLabelClickable, titleAndContentQueryString: titleAndContentQueryString, ), @@ -57,33 +49,3 @@ class DocumentDetailsRouteArguments { this.titleAndContentQueryString, }); } - -Future pushDocumentDetailsRoute( - BuildContext context, { - required DocumentModel document, - bool isLabelClickable = true, - bool allowEdit = true, - String? titleAndContentQueryString, -}) { - final LabelRepository labelRepository = context.read(); - final DocumentChangedNotifier changeNotifier = context.read(); - final PaperlessDocumentsApi documentsApi = context.read(); - final LocalNotificationService notificationservice = context.read(); - final CacheManager cacheManager = context.read(); - return Navigator.of(context).push(MaterialPageRoute( - builder: (_) => MultiProvider( - providers: [ - Provider.value(value: documentsApi), - Provider.value(value: labelRepository), - Provider.value(value: changeNotifier), - Provider.value(value: notificationservice), - Provider.value(value: cacheManager), - ], - child: DocumentDetailsRoute( - document: document, - allowEdit: allowEdit, - isLabelClickable: isLabelClickable, - ), - ), - )); -} diff --git a/packages/paperless_api/lib/src/models/paperless_server_exception.dart b/packages/paperless_api/lib/src/models/paperless_server_exception.dart index 10ada4d..3d180de 100644 --- a/packages/paperless_api/lib/src/models/paperless_server_exception.dart +++ b/packages/paperless_api/lib/src/models/paperless_server_exception.dart @@ -53,5 +53,6 @@ enum ErrorCode { requestTimedOut, unsupportedFileFormat, missingClientCertificate, - acknowledgeTasksError; + acknowledgeTasksError, + notAuthorized; } diff --git a/packages/paperless_api/lib/src/models/paperless_server_information_model.dart b/packages/paperless_api/lib/src/models/paperless_server_information_model.dart index 16eb623..73bb983 100644 --- a/packages/paperless_api/lib/src/models/paperless_server_information_model.dart +++ b/packages/paperless_api/lib/src/models/paperless_server_information_model.dart @@ -3,25 +3,17 @@ import 'package:paperless_api/src/request_utils.dart'; class PaperlessServerInformationModel { static const String versionHeader = 'x-version'; static const String apiVersionHeader = 'x-api-version'; - static const String hostHeader = 'x-served-by'; - final String? version; - final int? apiVersion; - final String? username; - final String? host; - - String? get userInitials { - return username?.substring(0, 1).toUpperCase(); - } + final String version; + final int apiVersion; + final bool isUpdateAvailable; PaperlessServerInformationModel({ - this.host, - this.username, - this.version = 'unknown', - this.apiVersion = 1, + required this.version, + required this.apiVersion, + required this.isUpdateAvailable, }); - int compareToOtherVersion(String? other) { - return getExtendedVersionNumber(version ?? '0.0.0') - .compareTo(getExtendedVersionNumber(other ?? '0.0.0')); + int compareToOtherVersion(String other) { + return getExtendedVersionNumber(version).compareTo(getExtendedVersionNumber(other)); } } diff --git a/packages/paperless_api/lib/src/models/permissions/inherited_permissions.dart b/packages/paperless_api/lib/src/models/permissions/inherited_permissions.dart index 9336562..dc8a272 100644 --- a/packages/paperless_api/lib/src/models/permissions/inherited_permissions.dart +++ b/packages/paperless_api/lib/src/models/permissions/inherited_permissions.dart @@ -130,7 +130,9 @@ enum InheritedPermissions { @HiveField(60) documentsChangeSavedviewfilterrule("documents.change_savedviewfilterrule"), @HiveField(61) - documentsChangeStoragepathdocuments("documents.change_storagepathdocuments.change_tag"), + documentsChangeStoragepath("documents.change_storagepath"), + @HiveField(111) + documentsChangeTag("documents.change_tag"), @HiveField(62) documentsChangeUisettings("documents.change_uisettings"), @HiveField(63) diff --git a/packages/paperless_api/lib/src/models/user_model.dart b/packages/paperless_api/lib/src/models/user_model.dart index 53d9038..acfba27 100644 --- a/packages/paperless_api/lib/src/models/user_model.dart +++ b/packages/paperless_api/lib/src/models/user_model.dart @@ -64,7 +64,7 @@ class UserModel with _$UserModel { return value.userPermissions.contains(permission); }, v2: (value) { - // In previous versions, all users have access to all + // In previous versions, all users have all permissions. return true; }, ); diff --git a/packages/paperless_api/lib/src/modules/documents_api/paperless_documents_api_impl.dart b/packages/paperless_api/lib/src/modules/documents_api/paperless_documents_api_impl.dart index e2845c7..1b1fe42 100644 --- a/packages/paperless_api/lib/src/modules/documents_api/paperless_documents_api_impl.dart +++ b/packages/paperless_api/lib/src/modules/documents_api/paperless_documents_api_impl.dart @@ -78,7 +78,6 @@ class PaperlessDocumentsApiImpl implements PaperlessDocumentsApi { throw const PaperlessServerException(ErrorCode.documentUpdateFailed); } } on DioError catch (err) { - //TODO: Handle 403 permission errors for 1.14.0 throw err.error!; } } diff --git a/packages/paperless_api/lib/src/modules/server_stats_api/paperless_server_stats_api_impl.dart b/packages/paperless_api/lib/src/modules/server_stats_api/paperless_server_stats_api_impl.dart index 92e033b..e9c4ff5 100644 --- a/packages/paperless_api/lib/src/modules/server_stats_api/paperless_server_stats_api_impl.dart +++ b/packages/paperless_api/lib/src/modules/server_stats_api/paperless_server_stats_api_impl.dart @@ -22,16 +22,13 @@ class PaperlessServerStatsApiImpl implements PaperlessServerStatsApi { if (response.statusCode == 200) { final version = response.data["version"] as String; final updateAvailable = response.data["update_available"] as bool; + return PaperlessServerInformationModel( + apiVersion: int.parse(response.headers.value('x-api-version')!), + version: version, + isUpdateAvailable: updateAvailable, + ); } - throw PaperlessServerException.unknown(); - final version = - response.headers[PaperlessServerInformationModel.versionHeader]?.first ?? 'unknown'; - final apiVersion = int.tryParse( - response.headers[PaperlessServerInformationModel.apiVersionHeader]?.first ?? '1'); - return PaperlessServerInformationModel( - version: version, - apiVersion: apiVersion, - ); + throw const PaperlessServerException.unknown(); } @override diff --git a/packages/paperless_api/lib/src/modules/user_api/paperless_user_api_v3_impl.dart b/packages/paperless_api/lib/src/modules/user_api/paperless_user_api_v3_impl.dart index 8e217c9..e0d2246 100644 --- a/packages/paperless_api/lib/src/modules/user_api/paperless_user_api_v3_impl.dart +++ b/packages/paperless_api/lib/src/modules/user_api/paperless_user_api_v3_impl.dart @@ -52,7 +52,7 @@ class PaperlessUserApiV3Impl implements PaperlessUserApi, PaperlessUserApiV3 { if (response.statusCode == 200) { return PagedSearchResult.fromJson( response.data, - UserModelV3.fromJson as UserModelV3 Function(Object?), + (json) => UserModelV3.fromJson(json as dynamic), ).results; } throw const PaperlessServerException.unknown(); From fd1c0ced35f17a0139cda36b0928a583de0c5b68 Mon Sep 17 00:00:00 2001 From: Anton Stubenbord Date: Thu, 11 May 2023 16:23:15 +0200 Subject: [PATCH 05/26] fix: Add owner to labels --- .../cubit/document_details_cubit.dart | 1 + .../widgets/document_download_button.dart | 2 +- .../view/widgets/document_share_button.dart | 2 +- lib/features/home/view/home_page.dart | 19 +- .../models/labels/correspondent_model.dart | 6 +- .../models/labels/document_type_model.dart | 5 +- .../lib/src/models/labels/label_model.dart | 4 + .../src/models/labels/storage_path_model.dart | 5 +- .../lib/src/models/labels/tag_model.dart | 60 +---- pubspec.lock | 254 +++++++----------- pubspec.yaml | 23 +- 11 files changed, 155 insertions(+), 226 deletions(-) diff --git a/lib/features/document_details/cubit/document_details_cubit.dart b/lib/features/document_details/cubit/document_details_cubit.dart index 51c3d52..89b850c 100644 --- a/lib/features/document_details/cubit/document_details_cubit.dart +++ b/lib/features/document_details/cubit/document_details_cubit.dart @@ -12,6 +12,7 @@ import 'package:paperless_mobile/core/service/file_description.dart'; import 'package:paperless_mobile/core/service/file_service.dart'; import 'package:paperless_mobile/features/notifications/services/local_notification_service.dart'; import 'package:share_plus/share_plus.dart'; +import 'package:cross_file/cross_file.dart'; part 'document_details_cubit.freezed.dart'; part 'document_details_state.dart'; diff --git a/lib/features/document_details/view/widgets/document_download_button.dart b/lib/features/document_details/view/widgets/document_download_button.dart index de696de..657cf5e 100644 --- a/lib/features/document_details/view/widgets/document_download_button.dart +++ b/lib/features/document_details/view/widgets/document_download_button.dart @@ -79,7 +79,7 @@ class _DocumentDownloadButtonState extends State { break; } - if (Platform.isAndroid && androidInfo!.version.sdkInt! <= 29) { + if (Platform.isAndroid && androidInfo!.version.sdkInt <= 29) { final isGranted = await askForPermission(Permission.storage); if (!isGranted) { return; diff --git a/lib/features/document_details/view/widgets/document_share_button.dart b/lib/features/document_details/view/widgets/document_share_button.dart index addd4f2..d473f2d 100644 --- a/lib/features/document_details/view/widgets/document_share_button.dart +++ b/lib/features/document_details/view/widgets/document_share_button.dart @@ -78,7 +78,7 @@ class _DocumentShareButtonState extends State { break; } - if (Platform.isAndroid && androidInfo!.version.sdkInt! < 30) { + if (Platform.isAndroid && androidInfo!.version.sdkInt < 30) { final isGranted = await askForPermission(Permission.storage); if (!isGranted) { return; diff --git a/lib/features/home/view/home_page.dart b/lib/features/home/view/home_page.dart index 83b5f3b..e1f4eaf 100644 --- a/lib/features/home/view/home_page.dart +++ b/lib/features/home/view/home_page.dart @@ -196,14 +196,15 @@ class _HomePageState extends State with WidgetsBindingObserver { ), label: S.of(context)!.documents, ), - RouteDescription( - icon: const Icon(Icons.document_scanner_outlined), - selectedIcon: Icon( - Icons.document_scanner, - color: Theme.of(context).colorScheme.primary, + if (LocalUserAccount.current.paperlessUser.hasPermission(UserPermissions.addDocument)) + RouteDescription( + icon: const Icon(Icons.document_scanner_outlined), + selectedIcon: Icon( + Icons.document_scanner, + color: Theme.of(context).colorScheme.primary, + ), + label: S.of(context)!.scanner, ), - label: S.of(context)!.scanner, - ), RouteDescription( icon: const Icon(Icons.sell_outlined), selectedIcon: Icon( @@ -234,8 +235,8 @@ class _HomePageState extends State with WidgetsBindingObserver { ]; final routes = [ const DocumentsPage(), - if (LocalUserAccount.current.paperlessUser.hasPermission(UserPermissions.changeDocument)) - const ScannerPage(), + if (LocalUserAccount.current.paperlessUser.hasPermission(UserPermissions.addDocument)) + const ScannerPage(), const LabelsPage(), const InboxPage(), ]; diff --git a/packages/paperless_api/lib/src/models/labels/correspondent_model.dart b/packages/paperless_api/lib/src/models/labels/correspondent_model.dart index 2a7523b..7a97202 100644 --- a/packages/paperless_api/lib/src/models/labels/correspondent_model.dart +++ b/packages/paperless_api/lib/src/models/labels/correspondent_model.dart @@ -11,14 +11,16 @@ class Correspondent extends Label { final DateTime? lastCorrespondence; const Correspondent({ - super.id, + this.lastCorrespondence, required super.name, + super.id, super.slug, super.match, super.matchingAlgorithm, super.isInsensitive, super.documentCount, - this.lastCorrespondence, + super.owner, + super.userCanChange, }); factory Correspondent.fromJson(Map json) => diff --git a/packages/paperless_api/lib/src/models/labels/document_type_model.dart b/packages/paperless_api/lib/src/models/labels/document_type_model.dart index 085f822..efd7285 100644 --- a/packages/paperless_api/lib/src/models/labels/document_type_model.dart +++ b/packages/paperless_api/lib/src/models/labels/document_type_model.dart @@ -13,10 +13,11 @@ class DocumentType extends Label { super.matchingAlgorithm, super.isInsensitive, super.documentCount, + super.owner, + super.userCanChange, }); - factory DocumentType.fromJson(Map json) => - _$DocumentTypeFromJson(json); + factory DocumentType.fromJson(Map json) => _$DocumentTypeFromJson(json); @override String get queryEndpoint => 'document_types'; diff --git a/packages/paperless_api/lib/src/models/labels/label_model.dart b/packages/paperless_api/lib/src/models/labels/label_model.dart index 1b78ccd..61d33eb 100644 --- a/packages/paperless_api/lib/src/models/labels/label_model.dart +++ b/packages/paperless_api/lib/src/models/labels/label_model.dart @@ -19,6 +19,8 @@ abstract class Label extends Equatable implements Comparable { final MatchingAlgorithm matchingAlgorithm; final bool? isInsensitive; final int? documentCount; + final int? owner; + final bool? userCanChange; const Label({ this.id, @@ -28,6 +30,8 @@ abstract class Label extends Equatable implements Comparable { this.isInsensitive = true, this.documentCount, this.slug, + this.owner, + this.userCanChange, }); Label copyWith({ diff --git a/packages/paperless_api/lib/src/models/labels/storage_path_model.dart b/packages/paperless_api/lib/src/models/labels/storage_path_model.dart index 09566cc..b05e53e 100644 --- a/packages/paperless_api/lib/src/models/labels/storage_path_model.dart +++ b/packages/paperless_api/lib/src/models/labels/storage_path_model.dart @@ -17,10 +17,11 @@ class StoragePath extends Label { super.matchingAlgorithm, super.isInsensitive, super.documentCount, + super.owner, + super.userCanChange, }); - factory StoragePath.fromJson(Map json) => - _$StoragePathFromJson(json); + factory StoragePath.fromJson(Map json) => _$StoragePathFromJson(json); @override String toString() { diff --git a/packages/paperless_api/lib/src/models/labels/tag_model.dart b/packages/paperless_api/lib/src/models/labels/tag_model.dart index 414ea79..f317d32 100644 --- a/packages/paperless_api/lib/src/models/labels/tag_model.dart +++ b/packages/paperless_api/lib/src/models/labels/tag_model.dart @@ -11,71 +11,33 @@ part 'tag_model.g.dart'; @HexColorJsonConverter() @JsonSerializable( - fieldRename: FieldRename.snake, explicitToJson: true, constructor: "_") + fieldRename: FieldRename.snake, + explicitToJson: true, +) class Tag extends Label { static const colorKey = 'color'; static const isInboxTagKey = 'is_inbox_tag'; static const textColorKey = 'text_color'; static const legacyColourKey = 'colour'; - final Color? textColor; + final Color? color; final bool isInboxTag; - @protected - @JsonKey(name: colorKey) - Color? colorv2; - - @protected - @Deprecated( - "Alias for the field color. Deprecated since Paperless API v2. Please use the color getter to access the background color of this tag.", - ) - @JsonKey(name: legacyColourKey) - Color? colorv1; - - Color? get color => colorv2 ?? colorv1; - - /// Constructor to use for serialization. - Tag._({ + Tag({ super.id, required super.name, super.documentCount, - super.isInsensitive = true, + super.isInsensitive, super.match, - super.matchingAlgorithm, + super.matchingAlgorithm = MatchingAlgorithm.defaultValue, super.slug, - this.colorv1, - this.colorv2, + this.color, this.textColor, this.isInboxTag = false, - }) { - colorv1 ??= colorv2; - colorv2 ??= colorv1; - } - - Tag({ - int? id, - required String name, - int? documentCount, - bool? isInsensitive, - String? match, - MatchingAlgorithm matchingAlgorithm = MatchingAlgorithm.defaultValue, - String? slug, - Color? color, - Color? textColor, - bool? isInboxTag, - }) : this._( - id: id, - name: name, - documentCount: documentCount, - isInsensitive: isInsensitive, - match: match, - matchingAlgorithm: matchingAlgorithm, - slug: slug, - textColor: textColor, - colorv1: color, - colorv2: color, - ); + super.owner, + super.userCanChange, + }); @override String toString() => name; diff --git a/pubspec.lock b/pubspec.lock index 4526cf1..3243aad 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -61,10 +61,10 @@ packages: dependency: transitive description: name: async - sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.10.0" + version: "2.11.0" auto_route: dependency: transitive description: @@ -97,6 +97,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.3" + bidi: + dependency: transitive + description: + name: bidi + sha256: dc00274c7edabae2ab30c676e736ea1eb0b1b7a1b436cb5fe372e431ccb39ab0 + url: "https://pub.dev" + source: hosted + version: "2.0.6" bloc: dependency: transitive description: @@ -213,10 +221,10 @@ packages: dependency: transitive description: name: characters - sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" checked_yaml: dependency: transitive description: @@ -245,18 +253,18 @@ packages: dependency: "direct main" description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.17.1" connectivity_plus: dependency: "direct main" description: name: connectivity_plus - sha256: "8875e8ed511a49f030e313656154e4bbbcef18d68dfd32eb853fac10bce48e96" + sha256: "45262924896ff72a8cd92b722bb7e3d5020f9e0724531a3e10e22ddae2005991" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "4.0.0" connectivity_plus_platform_interface: dependency: transitive description: @@ -349,50 +357,18 @@ packages: dependency: "direct main" description: name: device_info_plus - sha256: b809c4ed5f7fcdb325ccc70b80ad934677dc4e2aa414bf46859a42bfdfafcbb6 + sha256: f52ab3b76b36ede4d135aab80194df8925b553686f0fa12226b4e2d658e45903 url: "https://pub.dev" source: hosted - version: "4.1.3" - device_info_plus_linux: - dependency: transitive - description: - name: device_info_plus_linux - sha256: "77a8b3c4af06bc46507f89304d9f49dfc64b4ae004b994532ed23b34adeae4b3" - url: "https://pub.dev" - source: hosted - version: "3.0.0" - device_info_plus_macos: - dependency: transitive - description: - name: device_info_plus_macos - sha256: "37961762fbd46d3620c7b69ca606671014db55fc1b7a11e696fd90ed2e8fe03d" - url: "https://pub.dev" - source: hosted - version: "3.0.0" + version: "8.2.2" device_info_plus_platform_interface: dependency: transitive description: name: device_info_plus_platform_interface - sha256: "83fdba24fcf6846d3b10f10dfdc8b6c6d7ada5f8ed21d62ea2909c2dfa043773" + sha256: d3b01d5868b50ae571cd1dc6e502fc94d956b665756180f7b16ead09e836fd64 url: "https://pub.dev" source: hosted - version: "3.0.0" - device_info_plus_web: - dependency: transitive - description: - name: device_info_plus_web - sha256: "5890f6094df108181c7a29720bc23d0fd6159f17d82787fac093d1fefcaf6325" - url: "https://pub.dev" - source: hosted - version: "3.0.0" - device_info_plus_windows: - dependency: transitive - description: - name: device_info_plus_windows - sha256: "23a2874af0e23ee6e3a2a0ebcecec3a9da13241f2cb93a93a44c8764df123dd7" - url: "https://pub.dev" - source: hosted - version: "4.1.0" + version: "7.0.0" diff_match_patch: dependency: transitive description: @@ -442,14 +418,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.5" - extension: - dependency: transitive - description: - name: extension - sha256: "7df1ee2de6ccd05fd3e4d63acb194ae97e6525a2e8da067161c0cccb6a0a5305" - url: "https://pub.dev" - source: hosted - version: "0.5.0" fake_async: dependency: transitive description: @@ -478,10 +446,10 @@ packages: dependency: "direct main" description: name: file_picker - sha256: d090ae03df98b0247b82e5928f44d1b959867049d18d73635e2e0bc3f49542b9 + sha256: c7a8e25ca60e7f331b153b0cb3d405828f18d3e72a6fa1d9440c86556fffc877 url: "https://pub.dev" source: hosted - version: "5.2.5" + version: "5.3.0" fixnum: dependency: transitive description: @@ -544,10 +512,10 @@ packages: dependency: "direct main" description: name: flutter_form_builder - sha256: "9551c7379adc01a3a3a1100057396407c9534ea8adc937d14a0edd96fcd9e1dc" + sha256: "236c96dad143a0e67c0f11522606d6b17b6510e97530cb73af355b018ded7c10" url: "https://pub.dev" source: hosted - version: "7.8.0" + version: "8.0.0" flutter_html: dependency: "direct main" description: @@ -645,18 +613,18 @@ packages: dependency: "direct main" description: name: flutter_native_splash - sha256: "6777a3abb974021a39b5fdd2d46a03ca390e03903b6351f21d10e7ecc969f12d" + sha256: e301ae206ff0fb09b67d3716009c6c28c2da57a0ad164827b178421bb9d601f7 url: "https://pub.dev" source: hosted - version: "2.2.16" + version: "2.2.18" flutter_plugin_android_lifecycle: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: "60fc7b78455b94e6de2333d2f95196d32cf5c22f4b0b0520a628804cb463503b" + sha256: "96af49aa6b57c10a312106ad6f71deed5a754029c24789bbf620ba784f0bd0b0" url: "https://pub.dev" source: hosted - version: "2.0.7" + version: "2.0.14" flutter_secure_storage: dependency: "direct main" description: @@ -860,10 +828,10 @@ packages: dependency: "direct main" description: name: image - sha256: "8e9d133755c3e84c73288363e6343157c383a0c6c56fc51afcc5d4d7180306d6" + sha256: a72242c9a0ffb65d03de1b7113bc4e189686fc07c7147b8b41811d0dd0e0d9bf url: "https://pub.dev" source: hosted - version: "3.3.0" + version: "4.0.17" integration_test: dependency: "direct dev" description: flutter @@ -873,10 +841,10 @@ packages: dependency: "direct main" description: name: intl - sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91" + sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6 url: "https://pub.dev" source: hosted - version: "0.17.0" + version: "0.18.0" introduction_screen: dependency: "direct main" description: @@ -905,10 +873,10 @@ packages: dependency: transitive description: name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.6.5" + version: "0.6.7" json_annotation: dependency: "direct main" description: @@ -985,10 +953,10 @@ packages: dependency: transitive description: name: matcher - sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" + sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" url: "https://pub.dev" source: hosted - version: "0.12.13" + version: "0.12.15" material_color_utilities: dependency: transitive description: @@ -1001,10 +969,10 @@ packages: dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.1" mime: dependency: transitive description: @@ -1089,50 +1057,18 @@ packages: dependency: "direct main" description: name: package_info_plus - sha256: f62d7253edc197fe3c88d7c2ddab82d68f555e778d55390ccc3537eca8e8d637 + sha256: d39e8fbff4c5aef4592737e25ad6ac500df006ce7a7a8e1f838ce1256e167542 url: "https://pub.dev" source: hosted - version: "1.4.3+1" - package_info_plus_linux: - dependency: transitive - description: - name: package_info_plus_linux - sha256: "04b575f44233d30edbb80a94e57cad9107aada334fc02aabb42b6becd13c43fc" - url: "https://pub.dev" - source: hosted - version: "1.0.5" - package_info_plus_macos: - dependency: transitive - description: - name: package_info_plus_macos - sha256: a2ad8b4acf4cd479d4a0afa5a74ea3f5b1c7563b77e52cc32b3ee6956d5482a6 - url: "https://pub.dev" - source: hosted - version: "1.3.0" + version: "4.0.0" package_info_plus_platform_interface: dependency: transitive description: name: package_info_plus_platform_interface - sha256: f7a0c8f1e7e981bc65f8b64137a53fd3c195b18d429fba960babc59a5a1c7ae8 + sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6" url: "https://pub.dev" source: hosted - version: "1.0.2" - package_info_plus_web: - dependency: transitive - description: - name: package_info_plus_web - sha256: f0829327eb534789e0a16ccac8936a80beed4e2401c4d3a74f3f39094a822d3b - url: "https://pub.dev" - source: hosted - version: "1.0.6" - package_info_plus_windows: - dependency: transitive - description: - name: package_info_plus_windows - sha256: "79524f11c42dd9078b96d797b3cf79c0a2883a50c4920dc43da8562c115089bc" - url: "https://pub.dev" - source: hosted - version: "2.1.0" + version: "2.0.1" paperless_api: dependency: "direct main" description: @@ -1144,10 +1080,10 @@ packages: dependency: transitive description: name: path - sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" url: "https://pub.dev" source: hosted - version: "1.8.2" + version: "1.8.3" path_drawing: dependency: transitive description: @@ -1208,26 +1144,18 @@ packages: dependency: transitive description: name: path_provider_windows - sha256: bcabbe399d4042b8ee687e17548d5d3f527255253b4a639f5f8d2094a9c2b45c + sha256: d3f80b32e83ec208ac95253e0cd4d298e104fbc63cb29c5c69edaed43b0c69d6 url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.6" pdf: dependency: "direct main" description: name: pdf - sha256: "10659b915e65832b106f6d1d213e09b789cc1f24bf282ee911e49db35b96be4d" + sha256: "70d84154dc5b6ddf28eee6c012510a4cbbebb3a1879c0957e05364a95e8f3832" url: "https://pub.dev" source: hosted - version: "3.8.4" - pdfx: - dependency: "direct main" - description: - name: pdfx - sha256: cad7eab6358a89922c8ea592b738891b3984edab43300558d25dc9b15a94a35e - url: "https://pub.dev" - source: hosted - version: "2.3.0" + version: "3.10.3" pedantic: dependency: transitive description: @@ -1280,10 +1208,10 @@ packages: dependency: transitive description: name: petitparser - sha256: "49392a45ced973e8d94a85fdb21293fbb40ba805fc49f2965101ae748a3683b4" + sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 url: "https://pub.dev" source: hosted - version: "5.1.0" + version: "5.4.0" photo_view: dependency: "direct main" description: @@ -1304,10 +1232,10 @@ packages: dependency: transitive description: name: plugin_platform_interface - sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a + sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.4" pool: dependency: transitive description: @@ -1400,10 +1328,26 @@ packages: dependency: "direct main" description: name: share_plus - sha256: "8c6892037b1824e2d7e8f59d54b3105932899008642e6372e5079c6939b4b625" + sha256: f582d5741930f3ad1bf0211d358eddc0508cc346e5b4b248bd1e569c995ebb7a url: "https://pub.dev" source: hosted - version: "6.3.1" + version: "4.5.3" + share_plus_linux: + dependency: transitive + description: + name: share_plus_linux + sha256: dc32bf9f1151b9864bb86a997c61a487967a08f2e0b4feaa9a10538712224da4 + url: "https://pub.dev" + source: hosted + version: "3.0.1" + share_plus_macos: + dependency: transitive + description: + name: share_plus_macos + sha256: "44daa946f2845045ecd7abb3569b61cd9a55ae9cc4cbec9895b2067b270697ae" + url: "https://pub.dev" + source: hosted + version: "3.0.1" share_plus_platform_interface: dependency: transitive description: @@ -1412,6 +1356,22 @@ packages: url: "https://pub.dev" source: hosted version: "3.2.0" + share_plus_web: + dependency: transitive + description: + name: share_plus_web + sha256: eaef05fa8548b372253e772837dd1fbe4ce3aca30ea330765c945d7d4f7c9935 + url: "https://pub.dev" + source: hosted + version: "3.1.0" + share_plus_windows: + dependency: transitive + description: + name: share_plus_windows + sha256: "3a21515ae7d46988d42130cd53294849e280a5de6ace24bae6912a1bffd757d4" + url: "https://pub.dev" + source: hosted + version: "3.0.1" shelf: dependency: transitive description: @@ -1573,26 +1533,26 @@ packages: dependency: transitive description: name: test - sha256: a5fcd2d25eeadbb6589e80198a47d6a464ba3e2049da473943b8af9797900c2d + sha256: "3dac9aecf2c3991d09b9cdde4f98ded7b30804a88a0d7e4e7e1678e78d6b97f4" url: "https://pub.dev" source: hosted - version: "1.22.0" + version: "1.24.1" test_api: dependency: transitive description: name: test_api - sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 + sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb url: "https://pub.dev" source: hosted - version: "0.4.16" + version: "0.5.1" test_core: dependency: transitive description: name: test_core - sha256: "0ef9755ec6d746951ba0aabe62f874b707690b5ede0fecc818b138fcc9b14888" + sha256: "5138dbffb77b2289ecb12b81c11ba46036590b72a64a7a90d6ffb880f1a29e93" url: "https://pub.dev" source: hosted - version: "0.4.20" + version: "0.5.1" timezone: dependency: transitive description: @@ -1621,18 +1581,10 @@ packages: dependency: transitive description: name: universal_io - sha256: "79f78ddad839ee3aae3ec7c01eb4575faf0d5c860f8e5223bc9f9c17f7f03cef" + sha256: "06866290206d196064fd61df4c7aea1ffe9a4e7c4ccaa8fcded42dd41948005d" url: "https://pub.dev" source: hosted - version: "2.0.4" - universal_platform: - dependency: transitive - description: - name: universal_platform - sha256: d315be0f6641898b280ffa34e2ddb14f3d12b1a37882557869646e0cc363d0cc - url: "https://pub.dev" - source: hosted - version: "1.0.0+1" + version: "2.2.0" url_launcher: dependency: "direct main" description: @@ -1717,10 +1669,10 @@ packages: dependency: transitive description: name: vm_service - sha256: e7fb6c2282f7631712b69c19d1bff82f3767eea33a2321c14fa59ad67ea391c7 + sha256: f6deed8ed625c52864792459709183da231ebf66ff0cf09e69b573227c377efe url: "https://pub.dev" source: hosted - version: "9.4.0" + version: "11.3.0" watcher: dependency: transitive description: @@ -1741,10 +1693,10 @@ packages: dependency: transitive description: name: webdriver - sha256: ef67178f0cc7e32c1494645b11639dd1335f1d18814aa8435113a92e9ef9d841 + sha256: "3c923e918918feeb90c4c9fdf1fe39220fa4c0e8e2c0fffaded174498ef86c49" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" webkit_inspection_protocol: dependency: transitive description: @@ -1757,10 +1709,10 @@ packages: dependency: transitive description: name: win32 - sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46 + sha256: "5a751eddf9db89b3e5f9d50c20ab8612296e4e8db69009788d6c8b060a84191c" url: "https://pub.dev" source: hosted - version: "3.1.3" + version: "4.1.4" xdg_directories: dependency: transitive description: @@ -1773,10 +1725,10 @@ packages: dependency: transitive description: name: xml - sha256: "979ee37d622dec6365e2efa4d906c37470995871fe9ae080d967e192d88286b5" + sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" url: "https://pub.dev" source: hosted - version: "6.2.2" + version: "6.3.0" yaml: dependency: transitive description: @@ -1786,5 +1738,5 @@ packages: source: hosted version: "3.1.1" sdks: - dart: ">2.19.0 <3.0.0" + dart: ">=3.0.0-0 <4.0.0" flutter: ">=3.7.0" diff --git a/pubspec.yaml b/pubspec.yaml index 4170fc2..cba1680 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -34,14 +34,17 @@ dependencies: flutter_localizations: sdk: flutter permission_handler: ^10.2.0 - pdf: ^3.8.1 - pdfx: ^2.3.0 + pdf: ^3.10.2 + # pdfx: + # git: + # url: "https://github.com/ScerIO/packages.flutter" + # path: packages/pdfx edge_detection: git: url: https://github.com/sawankumarbundelkhandi/edge_detection ref: master path_provider: ^2.0.10 - image: ^3.1.3 + image: ^4.0.17 photo_view: ^0.14.0 intl: any #^0.18.0 flutter_svg: ^1.0.3 @@ -54,14 +57,13 @@ dependencies: shimmer: ^2.0.0 flutter_bloc: ^8.1.1 equatable: ^2.0.3 - flutter_form_builder: ^7.5.0 - #form_builder_validators: ^8.4.0 - package_info_plus: ^1.4.3+1 + flutter_form_builder: ^8.0.0 + package_info_plus: ^4.0.0 font_awesome_flutter: ^10.1.0 local_auth: ^2.1.2 - connectivity_plus: ^3.0.3 + connectivity_plus: ^4.0.0 flutter_native_splash: ^2.2.11 - share_plus: ^6.2.0 + share_plus: ^4.5.3 introduction_screen: ^3.0.2 receive_sharing_intent: ^1.4.5 uuid: ^3.0.6 @@ -79,7 +81,7 @@ dependencies: json_annotation: ^4.7.0 pretty_dio_logger: ^1.2.0-beta-1 collection: ^1.17.0 - device_info_plus: ^4.1.3 + device_info_plus: ^8.0.0 flutter_local_notifications: ^13.0.0 responsive_builder: ^0.4.3 open_filex: ^4.3.2 @@ -91,6 +93,9 @@ dependencies: hive_flutter: ^1.1.0 flutter_secure_storage: ^8.0.0 +dependency_overrides: + intl: ^0.18.0 + dev_dependencies: integration_test: sdk: flutter From c8ff261fc76d0f1ebd4b3f4b68d5ad88e89a4dc5 Mon Sep 17 00:00:00 2001 From: Anton Stubenbord Date: Thu, 11 May 2023 16:24:03 +0200 Subject: [PATCH 06/26] feat: Add asn to upload multipart request --- .../src/modules/documents_api/paperless_documents_api.dart | 1 + .../modules/documents_api/paperless_documents_api_impl.dart | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/packages/paperless_api/lib/src/modules/documents_api/paperless_documents_api.dart b/packages/paperless_api/lib/src/modules/documents_api/paperless_documents_api.dart index 83f256e..5e463e9 100644 --- a/packages/paperless_api/lib/src/modules/documents_api/paperless_documents_api.dart +++ b/packages/paperless_api/lib/src/modules/documents_api/paperless_documents_api.dart @@ -13,6 +13,7 @@ abstract class PaperlessDocumentsApi { int? documentType, int? correspondent, Iterable tags = const [], + int? asn, }); Future update(DocumentModel doc); Future findNextAsn(); diff --git a/packages/paperless_api/lib/src/modules/documents_api/paperless_documents_api_impl.dart b/packages/paperless_api/lib/src/modules/documents_api/paperless_documents_api_impl.dart index 1b1fe42..0f6d251 100644 --- a/packages/paperless_api/lib/src/modules/documents_api/paperless_documents_api_impl.dart +++ b/packages/paperless_api/lib/src/modules/documents_api/paperless_documents_api_impl.dart @@ -20,6 +20,7 @@ class PaperlessDocumentsApiImpl implements PaperlessDocumentsApi { int? documentType, int? correspondent, Iterable tags = const [], + int? asn, }) async { final formData = FormData(); formData.files.add( @@ -41,6 +42,9 @@ class PaperlessDocumentsApiImpl implements PaperlessDocumentsApi { if (documentType != null) { formData.fields.add(MapEntry('document_type', jsonEncode(documentType))); } + if (asn != null) { + formData.fields.add(MapEntry('archive_serial_number', jsonEncode(asn))); + } for (final tag in tags) { formData.fields.add(MapEntry('tags', tag.toString())); } From 0c649c8dda3131798a0bd399ea334b63a1e75c14 Mon Sep 17 00:00:00 2001 From: Anton Stubenbord Date: Thu, 11 May 2023 16:25:05 +0200 Subject: [PATCH 07/26] fix: Fixed bug where user could not close app from documents page using back button --- lib/features/documents/view/pages/documents_page.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/features/documents/view/pages/documents_page.dart b/lib/features/documents/view/pages/documents_page.dart index 2d9d359..dd772de 100644 --- a/lib/features/documents/view/pages/documents_page.dart +++ b/lib/features/documents/view/pages/documents_page.dart @@ -150,8 +150,9 @@ class _DocumentsPageState extends State with SingleTickerProvider onWillPop: () async { if (context.read().state.selection.isNotEmpty) { context.read().resetSelection(); + return false; } - return false; + return true; }, child: Stack( children: [ From 39342eecf115d89424845cb540594377806496a1 Mon Sep 17 00:00:00 2001 From: Anton Stubenbord Date: Fri, 12 May 2023 00:16:30 +0200 Subject: [PATCH 08/26] chore+fix+feat: Apply dart fixes after upgrade to flutter 3.10, add permission checks, make most api calls work again --- README.md | 2 +- .../exception/server_message_exception.dart | 5 + .../dio_http_error_interceptor.dart | 17 ++- lib/core/navigation/push_routes.dart | 64 +++++++- .../notifier/document_changed_notifier.dart | 1 - lib/core/repository/label_repository.dart | 5 +- .../repository/saved_view_repository.dart | 4 +- lib/core/service/github_issue_service.dart | 1 - lib/core/service/status_service.dart | 12 +- .../error_code_localization_mapper.dart | 2 - lib/core/type/types.dart | 2 - .../dialog_utils/dialog_cancel_button.dart | 2 - .../dialog_utils/dialog_confirm_button.dart | 2 - .../form_builder_color_picker.dart | 5 +- lib/core/widgets/hint_card.dart | 1 - lib/core/widgets/material/chips_input.dart | 27 +--- .../widgets/material/search/m3_search.dart | 1 - .../material/search/search_anchor.dart | 2 - lib/features/app_drawer/view/app_drawer.dart | 1 - .../application_intro_slideshow.dart | 8 +- .../cubit/document_bulk_action_cubit.dart | 1 - .../widgets/bulk_edit_label_bottom_sheet.dart | 3 + .../confirm_bulk_modify_tags_dialog.dart | 2 - .../fullscreen_bulk_edit_tags_widget.dart | 4 +- .../view/dialogs/select_file_type_dialog.dart | 2 +- .../view/pages/document_details_page.dart | 27 ++-- .../widgets/archive_serial_number_field.dart | 28 ++-- .../view/widgets/details_item.dart | 2 +- .../widgets/document_meta_data_widget.dart | 4 +- .../widgets/document_overview_widget.dart | 1 - .../view/document_edit_page.dart | 18 ++- .../view/widgets/scanned_image_item.dart | 2 +- .../view/document_search_page.dart | 10 -- .../view/sliver_search_bar.dart | 1 - .../cubit/document_upload_cubit.dart | 2 + .../document_upload_preparation_page.dart | 29 +++- .../documents/view/pages/document_view.dart | 1 - .../documents/view/pages/documents_page.dart | 19 +-- .../widgets/items/document_grid_item.dart | 1 - .../documents_list_loading_widget.dart | 4 +- .../widgets/search/document_filter_form.dart | 16 +- .../widgets/search/text_query_form_field.dart | 1 - .../edit_label/view/edit_label_page.dart | 7 +- .../view/impl/edit_correspondent_page.dart | 11 +- .../view/impl/edit_document_type_page.dart | 12 +- .../view/impl/edit_storage_path_page.dart | 11 +- .../edit_label/view/impl/edit_tag_page.dart | 11 +- lib/features/edit_label/view/label_form.dart | 1 - lib/features/home/view/home_page.dart | 48 ++---- lib/features/home/view/home_route.dart | 8 +- lib/features/home/view/model/api_version.dart | 1 - .../view/widget/verify_identity_page.dart | 2 - lib/features/inbox/view/pages/inbox_page.dart | 32 ++-- .../inbox/view/widgets/inbox_item.dart | 76 ++++------ .../widgets/inbox_list_loading_widget.dart | 5 +- .../labels/cubit/label_cubit_mixin.dart | 1 - .../tags/view/widgets/tags_form_field.dart | 9 +- .../labels/view/pages/labels_page.dart | 18 ++- .../view/widgets/fullscreen_label_form.dart | 2 + .../labels/view/widgets/label_form_field.dart | 5 +- .../labels/view/widgets/label_item.dart | 40 ++--- .../labels/view/widgets/label_tab_view.dart | 15 +- .../labels/view/widgets/label_text.dart | 1 - .../view/linked_documents_page.dart | 1 - .../login/cubit/authentication_cubit.dart | 1 + .../model/authentication_information.dart | 1 - .../login/model/client_certificate.dart | 1 - .../model/client_certificate_form_model.dart | 1 - .../client_certificate_form_field.dart | 3 - .../server_address_form_field.dart | 2 +- .../user_credentials_form_field.dart | 2 + .../login_pages/server_connection_page.dart | 1 - .../services/local_notification_service.dart | 1 - .../cubit/paged_documents_state.dart | 1 - .../saved_view/cubit/saved_view_cubit.dart | 1 - .../saved_view/view/saved_view_list.dart | 10 +- .../cubit/saved_view_details_cubit.dart | 1 - .../view/saved_view_details_page.dart | 1 - .../settings/view/manage_accounts_page.dart | 22 +-- .../view/pages/security_settings_page.dart | 6 +- .../view/widgets/clear_storage_settings.dart | 8 +- .../widgets/color_scheme_option_setting.dart | 6 +- .../view/widgets/global_settings_builder.dart | 2 - .../view/widgets/radio_settings_dialog.dart | 1 - .../view/widgets/theme_mode_setting.dart | 1 - .../settings/view/widgets/user_avatar.dart | 3 - .../view/widgets/user_settings_builder.dart | 1 - lib/features/sharing/share_intent_queue.dart | 1 - lib/helpers/message_helpers.dart | 16 +- lib/main.dart | 26 ++-- lib/theme.dart | 2 +- .../lib/config/hive/hive_type_ids.dart | 2 - .../date_range_query_json_converter.dart | 2 - .../local_date_time_json_converter.dart | 1 - .../lib/src/models/filter_rule_model.dart | 3 +- .../lib/src/models/group_model.dart | 2 +- .../lib/src/models/group_model.freezed.dart | 25 ++-- .../lib/src/models/labels/tag_model.dart | 4 +- .../models/paperless_server_exception.dart | 3 +- .../models/permissions/user_permissions.dart | 141 ++++-------------- .../unset_date_range_query.dart | 1 - .../tags_query/tags_query.dart | 1 - .../lib/src/models/user_model.dart | 43 ++++-- .../lib/src/models/user_model.freezed.dart | 33 ++-- .../paperless_documents_api_impl.dart | 4 +- .../tasks_api/paperless_tasks_api_impl.dart | 1 - .../paperless_api/test/saved_view_test.dart | 17 +-- .../animated_touch_bubble_part.dart | 4 +- .../edge_detection_shape.dart | 2 - .../lib/edge_detection_shape/magnifier.dart | 15 +- .../magnifier_painter.dart | 2 +- .../example/lib/main.dart | 10 +- .../example/lib/scan.dart | 6 +- .../example/pubspec.lock | 128 ++++++++-------- 114 files changed, 546 insertions(+), 685 deletions(-) create mode 100644 lib/core/exception/server_message_exception.dart diff --git a/README.md b/README.md index 208c748..81ba32c 100644 --- a/README.md +++ b/README.md @@ -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. \ No newline at end of file diff --git a/lib/core/exception/server_message_exception.dart b/lib/core/exception/server_message_exception.dart new file mode 100644 index 0000000..678bcbe --- /dev/null +++ b/lib/core/exception/server_message_exception.dart @@ -0,0 +1,5 @@ +class ServerMessageException implements Exception { + final String message; + + ServerMessageException(this.message); +} diff --git a/lib/core/interceptor/dio_http_error_interceptor.dart b/lib/core/interceptor/dio_http_error_interceptor.dart index 8cb0d38..211aa45 100644 --- a/lib/core/interceptor/dio_http_error_interceptor.dart +++ b/lib/core/interceptor/dio_http_error_interceptor.dart @@ -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; diff --git a/lib/core/navigation/push_routes.dart b/lib/core/navigation/push_routes.dart index 4a8615f..fc3e9d6 100644 --- a/lib/core/navigation/push_routes.dart +++ b/lib/core/navigation/push_routes.dart @@ -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 pushDocumentSearchPage(BuildContext context) { final currentUser = Hive.box(HiveBoxes.globalSettings).getValue()!.currentLoggedInUser; @@ -64,6 +71,7 @@ Future pushDocumentDetailsRoute( Provider.value(value: context.read()), Provider.value(value: context.read()), Provider.value(value: context.read()), + Provider.value(value: context.read()), if (context.read().hasMultiUserSupport) Provider.value(value: context.read()), ], @@ -76,19 +84,23 @@ Future pushDocumentDetailsRoute( ); } -Future pushSavedViewDetailsRoute(BuildContext context, {required SavedView savedView}) { +Future pushSavedViewDetailsRoute( + BuildContext context, { + required SavedView savedView, +}) { + final apiVersion = context.read(); return Navigator.of(context).push( MaterialPageRoute( builder: (_) => MultiProvider( providers: [ + Provider.value(value: apiVersion), + if (apiVersion.hasMultiUserSupport) Provider.value(value: context.read()), Provider.value(value: context.read()), Provider.value(value: context.read()), Provider.value(value: context.read()), Provider.value(value: context.read()), + Provider.value(value: context.read()), ], - child: SavedViewDetailsPage( - onDelete: context.read().remove, - ), builder: (_, child) { return BlocProvider( create: (context) => SavedViewDetailsCubit( @@ -105,3 +117,47 @@ Future pushSavedViewDetailsRoute(BuildContext context, {required SavedView ), ); } + +Future pushAddSavedViewRoute(BuildContext context, {required DocumentFilter filter}) { + return Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => AddSavedViewPage( + currentFilter: filter, + correspondents: context.read().state.correspondents, + documentTypes: context.read().state.documentTypes, + storagePaths: context.read().state.storagePaths, + tags: context.read().state.tags, + ), + ), + ); +} + +Future pushLinkedDocumentsView(BuildContext context, {required DocumentFilter filter}) { + return Navigator.push( + context, + MaterialPageRoute( + builder: (_) => MultiProvider( + providers: [ + Provider.value(value: context.read()), + Provider.value(value: context.read()), + Provider.value(value: context.read()), + Provider.value(value: context.read()), + Provider.value(value: context.read()), + Provider.value(value: context.read()), + Provider.value(value: context.read()), + if (context.read().hasMultiUserSupport) + Provider.value(value: context.read()), + ], + builder: (context, _) => BlocProvider( + create: (context) => LinkedDocumentsCubit( + filter, + context.read(), + context.read(), + context.read(), + ), + child: const LinkedDocumentsPage(), + ), + ), + ), + ); +} diff --git a/lib/core/notifier/document_changed_notifier.dart b/lib/core/notifier/document_changed_notifier.dart index 392d1bb..04a9781 100644 --- a/lib/core/notifier/document_changed_notifier.dart +++ b/lib/core/notifier/document_changed_notifier.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:developer'; import 'package:flutter/foundation.dart'; import 'package:paperless_api/paperless_api.dart'; diff --git a/lib/core/repository/label_repository.dart b/lib/core/repository/label_repository.dart index b57b19f..1ba16b1 100644 --- a/lib/core/repository/label_repository.dart +++ b/lib/core/repository/label_repository.dart @@ -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 { final PaperlessLabelsApi _api; - LabelRepository(this._api) : super(const LabelRepositoryState()); + LabelRepository(this._api) : super(const LabelRepositoryState()) { + initialize(); + } Future initialize() { debugPrint("Initializing labels..."); diff --git a/lib/core/repository/saved_view_repository.dart b/lib/core/repository/saved_view_repository.dart index d458358..181d2fc 100644 --- a/lib/core/repository/saved_view_repository.dart +++ b/lib/core/repository/saved_view_repository.dart @@ -7,7 +7,9 @@ import 'package:paperless_mobile/core/repository/saved_view_repository_state.dar class SavedViewRepository extends PersistentRepository { final PaperlessSavedViewsApi _api; - SavedViewRepository(this._api) : super(const SavedViewRepositoryState()); + SavedViewRepository(this._api) : super(const SavedViewRepositoryState()) { + initialize(); + } Future initialize() { return findAll(); diff --git a/lib/core/service/github_issue_service.dart b/lib/core/service/github_issue_service.dart index b4e1d79..3d7e435 100644 --- a/lib/core/service/github_issue_service.dart +++ b/lib/core/service/github_issue_service.dart @@ -1,4 +1,3 @@ -import 'dart:developer'; import 'package:flutter/material.dart'; import 'package:paperless_mobile/core/model/github_error_report.model.dart'; diff --git a/lib/core/service/status_service.dart b/lib/core/service/status_service.dart index 4e42b3d..ea16bee 100644 --- a/lib/core/service/status_service.dart +++ b/lib/core/service/status_service.dart @@ -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 startListeningBeforeDocumentUpload( @@ -19,7 +11,7 @@ abstract class StatusService { class WebSocketStatusService implements StatusService { late WebSocket? socket; - late IOWebSocketChannel? _channel; + // late IOWebSocketChannel? _channel; WebSocketStatusService(); diff --git a/lib/core/translation/error_code_localization_mapper.dart b/lib/core/translation/error_code_localization_mapper.dart index 9143850..4b2be11 100644 --- a/lib/core/translation/error_code_localization_mapper.dart +++ b/lib/core/translation/error_code_localization_mapper.dart @@ -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 } } diff --git a/lib/core/type/types.dart b/lib/core/type/types.dart index a133cbf..ce6ae47 100644 --- a/lib/core/type/types.dart +++ b/lib/core/type/types.dart @@ -1,5 +1,3 @@ -import 'package:paperless_api/paperless_api.dart'; -import 'package:rxdart/subjects.dart'; typedef JSON = Map; typedef PaperlessValidationErrors = Map; diff --git a/lib/core/widgets/dialog_utils/dialog_cancel_button.dart b/lib/core/widgets/dialog_utils/dialog_cancel_button.dart index 1fede76..a299169 100644 --- a/lib/core/widgets/dialog_utils/dialog_cancel_button.dart +++ b/lib/core/widgets/dialog_utils/dialog_cancel_button.dart @@ -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 { diff --git a/lib/core/widgets/dialog_utils/dialog_confirm_button.dart b/lib/core/widgets/dialog_utils/dialog_confirm_button.dart index bb9f02d..f1083c4 100644 --- a/lib/core/widgets/dialog_utils/dialog_confirm_button.dart +++ b/lib/core/widgets/dialog_utils/dialog_confirm_button.dart @@ -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 { diff --git a/lib/core/widgets/form_builder_fields/form_builder_color_picker.dart b/lib/core/widgets/form_builder_fields/form_builder_color_picker.dart index 893b360..912d33d 100644 --- a/lib/core/widgets/form_builder_fields/form_builder_color_picker.dart +++ b/lib/core/widgets/form_builder_fields/form_builder_color_picker.dart @@ -181,8 +181,7 @@ class FormBuilderColorPickerField extends FormBuilderField { ); @override - FormBuilderColorPickerFieldState createState() => - FormBuilderColorPickerFieldState(); + FormBuilderColorPickerFieldState createState() => FormBuilderColorPickerFieldState(); } class FormBuilderColorPickerFieldState @@ -217,8 +216,6 @@ class FormBuilderColorPickerFieldState final selected = await showDialog( context: context, builder: (BuildContext context) { - final materialLocalizations = S.of(context)!; - return AlertDialog( // title: null, //const Text('Pick a color!'), content: _buildColorPicker(), diff --git a/lib/core/widgets/hint_card.dart b/lib/core/widgets/hint_card.dart index 46da68b..c041436 100644 --- a/lib/core/widgets/hint_card.dart +++ b/lib/core/widgets/hint_card.dart @@ -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'; diff --git a/lib/core/widgets/material/chips_input.dart b/lib/core/widgets/material/chips_input.dart index 9e6bfc8..d8f5ef6 100644 --- a/lib/core/widgets/material/chips_input.dart +++ b/lib/core/widgets/material/chips_input.dart @@ -27,8 +27,7 @@ import 'package:flutter/services.dart'; typedef ChipsInputSuggestions = Future> Function(String query); typedef ChipSelected = void Function(T data, bool selected); -typedef ChipsBuilder = Widget Function( - BuildContext context, ChipsInputState state, T data); +typedef ChipsBuilder = Widget Function(BuildContext context, ChipsInputState state, T data); class ChipsInput extends StatefulWidget { const ChipsInput({ @@ -71,8 +70,7 @@ class ChipsInputState extends State> { 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 extends State> { 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 extends State> { } 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 extends State> { 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) { diff --git a/lib/core/widgets/material/search/m3_search.dart b/lib/core/widgets/material/search/m3_search.dart index fdc5f75..9e7b78d 100644 --- a/lib/core/widgets/material/search/m3_search.dart +++ b/lib/core/widgets/material/search/m3_search.dart @@ -251,7 +251,6 @@ abstract class SearchDelegate { /// /// 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( diff --git a/lib/core/widgets/material/search/search_anchor.dart b/lib/core/widgets/material/search/search_anchor.dart index 1bfd31a..6acadaa 100644 --- a/lib/core/widgets/material/search/search_anchor.dart +++ b/lib/core/widgets/material/search/search_anchor.dart @@ -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(); return searchBarTheme?.data ?? const SearchBarThemeData(); - ; } @override diff --git a/lib/features/app_drawer/view/app_drawer.dart b/lib/features/app_drawer/view/app_drawer.dart index 08d1479..103e314 100644 --- a/lib/features/app_drawer/view/app_drawer.dart +++ b/lib/features/app_drawer/view/app_drawer.dart @@ -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'; diff --git a/lib/features/app_intro/application_intro_slideshow.dart b/lib/features/app_intro/application_intro_slideshow.dart index cdaf9e5..5168f25 100644 --- a/lib/features/app_intro/application_intro_slideshow.dart +++ b/lib/features/app_intro/application_intro_slideshow.dart @@ -50,7 +50,7 @@ class _ApplicationIntroSlideshowState extends State { image: AssetImages.organizeDocuments.image, ), ), - bodyWidget: Column( + bodyWidget: const Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ @@ -70,7 +70,7 @@ class _ApplicationIntroSlideshowState extends State { 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 { padding: const EdgeInsets.all(8.0), child: Image(image: AssetImages.success.image), ), - bodyWidget: Column( - children: const [ + bodyWidget: const Column( + children: [ BiometricAuthenticationSetting(), LanguageSelectionSetting(), ThemeModeSetting(), diff --git a/lib/features/document_bulk_action/cubit/document_bulk_action_cubit.dart b/lib/features/document_bulk_action/cubit/document_bulk_action_cubit.dart index 6382c7f..6dc5679 100644 --- a/lib/features/document_bulk_action/cubit/document_bulk_action_cubit.dart +++ b/lib/features/document_bulk_action/cubit/document_bulk_action_cubit.dart @@ -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'; diff --git a/lib/features/document_bulk_action/view/widgets/bulk_edit_label_bottom_sheet.dart b/lib/features/document_bulk_action/view/widgets/bulk_edit_label_bottom_sheet.dart index 77112c4..e99051e 100644 --- a/lib/features/document_bulk_action/view/widgets/bulk_edit_label_bottom_sheet.dart +++ b/lib/features/document_bulk_action/view/widgets/bulk_edit_label_bottom_sheet.dart @@ -17,6 +17,7 @@ class BulkEditLabelBottomSheet extends StatefulWidget { final LabelOptionsSelector availableOptionsSelector; final void Function(int? selectedId) onSubmit; final int? initialValue; + final bool canCreateNewLabel; const BulkEditLabelBottomSheet({ super.key, @@ -26,6 +27,7 @@ class BulkEditLabelBottomSheet extends StatefulWidget { required this.availableOptionsSelector, required this.onSubmit, this.initialValue, + required this.canCreateNewLabel, }); @override @@ -58,6 +60,7 @@ class _BulkEditLabelBottomSheetState extends State _nonSharedTags; - List _addTags = []; - List _removeTags = []; + final List _addTags = []; + final List _removeTags = []; late List _filteredTags; @override diff --git a/lib/features/document_details/view/dialogs/select_file_type_dialog.dart b/lib/features/document_details/view/dialogs/select_file_type_dialog.dart index adb0842..bf56325 100644 --- a/lib/features/document_details/view/dialogs/select_file_type_dialog.dart +++ b/lib/features/document_details/view/dialogs/select_file_type_dialog.dart @@ -41,7 +41,7 @@ class _SelectFileTypeDialogState extends State { }, title: Text(S.of(context)!.archivedPdf), ), - Divider(), + const Divider(), CheckboxListTile( controlAffinity: ListTileControlAffinity.leading, value: _rememberSelection, diff --git a/lib/features/document_details/view/pages/document_details_page.dart b/lib/features/document_details/view/pages/document_details_page.dart index aeced88..e05af25 100644 --- a/lib/features/document_details/view/pages/document_details_page.dart +++ b/lib/features/document_details/view/pages/document_details_page.dart @@ -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 { Widget build(BuildContext context) { final apiVersion = context.watch(); - final tabLength = 4 + (apiVersion.supportsPermissions ? 1 : 0); + final tabLength = 4 + (apiVersion.hasMultiUserSupport ? 1 : 0); return WillPopScope( onWillPop: () async { Navigator.of(context).pop(context.read().state.document); @@ -155,7 +151,7 @@ class _DocumentDetailsPageState extends State { ), ), ), - if (apiVersion.supportsPermissions) + if (apiVersion.hasMultiUserSupport) Tab( child: Text( "Permissions", @@ -260,13 +256,9 @@ class _DocumentDetailsPageState extends State { } Widget _buildEditButton() { - bool canEdit = context.watchInternetConnection; - final apiVersion = context.watch(); - - 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 { 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 { 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, diff --git a/lib/features/document_details/view/widgets/archive_serial_number_field.dart b/lib/features/document_details/view/widgets/archive_serial_number_field.dart index d3597ea..f5c5f52 100644 --- a/lib/features/document_details/view/widgets/archive_serial_number_field.dart +++ b/lib/features/document_details/view/widgets/archive_serial_number_field.dart @@ -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 createState() => - _ArchiveSerialNumberFieldState(); + State createState() => _ArchiveSerialNumberFieldState(); } class _ArchiveSerialNumberFieldState extends State { @@ -39,20 +39,21 @@ class _ArchiveSerialNumberFieldState extends State { 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( 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 { crossAxisAlignment: CrossAxisAlignment.start, children: [ TextFormField( + enabled: userCanEditDocument, controller: _asnEditingController, keyboardType: TextInputType.number, onChanged: (value) { @@ -78,15 +80,13 @@ class _ArchiveSerialNumberFieldState extends State { 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 { ), 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(), ], diff --git a/lib/features/document_details/view/widgets/details_item.dart b/lib/features/document_details/view/widgets/details_item.dart index e50f24c..4003cd0 100644 --- a/lib/features/document_details/view/widgets/details_item.dart +++ b/lib/features/document_details/view/widgets/details_item.dart @@ -24,7 +24,7 @@ class DetailsItem extends StatelessWidget { } DetailsItem.text( - String text, { + String text, {super.key, required this.label, required BuildContext context, }) : content = Text( diff --git a/lib/features/document_details/view/widgets/document_meta_data_widget.dart b/lib/features/document_details/view/widgets/document_meta_data_widget.dart index 24806c6..b2b55ce 100644 --- a/lib/features/document_details/view/widgets/document_meta_data_widget.dart +++ b/lib/features/document_details/view/widgets/document_meta_data_widget.dart @@ -29,8 +29,8 @@ class _DocumentMetaDataWidgetState extends State { builder: (context, state) { debugPrint("Building state..."); if (state.metaData == null) { - return SliverToBoxAdapter( - child: const Center( + return const SliverToBoxAdapter( + child: Center( child: CircularProgressIndicator(), ), ); diff --git a/lib/features/document_details/view/widgets/document_overview_widget.dart b/lib/features/document_details/view/widgets/document_overview_widget.dart index 908c974..0f3edd8 100644 --- a/lib/features/document_details/view/widgets/document_overview_widget.dart +++ b/lib/features/document_details/view/widgets/document_overview_widget.dart @@ -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'; diff --git a/lib/features/document_edit/view/document_edit_page.dart b/lib/features/document_edit/view/document_edit_page.dart index baf6127..0d836e9 100644 --- a/lib/features/document_edit/view/document_edit_page.dart +++ b/lib/features/document_edit/view/document_edit_page.dart @@ -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 { name: fkCorrespondent, prefixIcon: const Icon(Icons.person_outlined), allowSelectUnassigned: true, + canCreateNewLabel: + LocalUserAccount.current.paperlessUser.hasPermission( + PermissionAction.add, + PermissionTarget.correspondent, + ), ), if (_filteredSuggestions?.hasSuggestedCorrespondents ?? false) _buildSuggestionsSkeleton( @@ -144,6 +148,11 @@ class _DocumentEditPageState extends State { 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 { value: context.read(), 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, diff --git a/lib/features/document_scan/view/widgets/scanned_image_item.dart b/lib/features/document_scan/view/widgets/scanned_image_item.dart index 62931bf..adb597f 100644 --- a/lib/features/document_scan/view/widgets/scanned_image_item.dart +++ b/lib/features/document_scan/view/widgets/scanned_image_item.dart @@ -98,7 +98,7 @@ class _ScannedImageItemState extends State { alignment: Alignment.bottomCenter, child: TextButton( onPressed: widget.onDelete, - child: Text("Remove"), + child: const Text("Remove"), ), ), ], diff --git a/lib/features/document_search/view/document_search_page.dart b/lib/features/document_search/view/document_search_page.dart index 14803f3..131393d 100644 --- a/lib/features/document_search/view/document_search_page.dart +++ b/lib/features/document_search/view/document_search_page.dart @@ -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}); diff --git a/lib/features/document_search/view/sliver_search_bar.dart b/lib/features/document_search/view/sliver_search_bar.dart index ba1feba..ae29581 100644 --- a/lib/features/document_search/view/sliver_search_bar.dart +++ b/lib/features/document_search/view/sliver_search_bar.dart @@ -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'; diff --git a/lib/features/document_upload/cubit/document_upload_cubit.dart b/lib/features/document_upload/cubit/document_upload_cubit.dart index 91eb17a..e066c15 100644 --- a/lib/features/document_upload/cubit/document_upload_cubit.dart +++ b/lib/features/document_upload/cubit/document_upload_cubit.dart @@ -35,6 +35,7 @@ class DocumentUploadCubit extends Cubit { int? correspondent, Iterable tags = const [], DateTime? createdAt, + int? asn, }) async { return await _documentApi.create( bytes, @@ -44,6 +45,7 @@ class DocumentUploadCubit extends Cubit { documentType: documentType, tags: tags, createdAt: createdAt, + asn: asn, ); } diff --git a/lib/features/document_upload/view/document_upload_preparation_page.dart b/lib/features/document_upload/view/document_upload_preparation_page.dart index ea72e5c..cb5be3a 100644 --- a/lib/features/document_upload/view/document_upload_preparation_page.dart +++ b/lib/features/document_upload/view/document_upload_preparation_page.dart @@ -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( @@ -208,6 +212,10 @@ class _DocumentUploadPreparationPageState extends State 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 documentBytes; diff --git a/lib/features/documents/view/pages/documents_page.dart b/lib/features/documents/view/pages/documents_page.dart index 2d9d359..cf19ee5 100644 --- a/lib/features/documents/view/pages/documents_page.dart +++ b/lib/features/documents/view/pages/documents_page.dart @@ -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 with SingleTickerProvider } void _onCreateSavedView(DocumentFilter filter) async { - final newView = await Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => BlocBuilder( - builder: (context, state) { - return AddSavedViewPage( - currentFilter: filter, - correspondents: context.read().state.correspondents, - documentTypes: context.read().state.documentTypes, - storagePaths: context.read().state.storagePaths, - tags: context.read().state.tags, - ); - }, - ), - ), - ); + final newView = await pushAddSavedViewRoute(context, filter: filter); if (newView != null) { try { await context.read().add(newView); diff --git a/lib/features/documents/view/widgets/items/document_grid_item.dart b/lib/features/documents/view/widgets/items/document_grid_item.dart index 21f05a4..506acbe 100644 --- a/lib/features/documents/view/widgets/items/document_grid_item.dart +++ b/lib/features/documents/view/widgets/items/document_grid_item.dart @@ -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'; diff --git a/lib/features/documents/view/widgets/placeholder/documents_list_loading_widget.dart b/lib/features/documents/view/widgets/placeholder/documents_list_loading_widget.dart index 492c618..36e5023 100644 --- a/lib/features/documents/view/widgets/placeholder/documents_list_loading_widget.dart +++ b/lib/features/documents/view/widgets/placeholder/documents_list_loading_widget.dart @@ -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!, diff --git a/lib/features/documents/view/widgets/search/document_filter_form.dart b/lib/features/documents/view/widgets/search/document_filter_form.dart index 27afa04..978c7db 100644 --- a/lib/features/documents/view/widgets/search/document_filter_form.dart +++ b/lib/features/documents/view/widgets/search/document_filter_form.dart @@ -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 { 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 { 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 { initialValue: widget.initialFilter.storagePath, prefixIcon: const Icon(Icons.folder_outlined), allowSelectUnassigned: false, + canCreateNewLabel: LocalUserAccount.current.paperlessUser.hasPermission( + PermissionAction.add, + PermissionTarget.storagePath, + ), ); } diff --git a/lib/features/documents/view/widgets/search/text_query_form_field.dart b/lib/features/documents/view/widgets/search/text_query_form_field.dart index d6af5b9..f94805c 100644 --- a/lib/features/documents/view/widgets/search/text_query_form_field.dart +++ b/lib/features/documents/view/widgets/search/text_query_form_field.dart @@ -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'; diff --git a/lib/features/edit_label/view/edit_label_page.dart b/lib/features/edit_label/view/edit_label_page.dart index e090727..7b3044f 100644 --- a/lib/features/edit_label/view/edit_label_page.dart +++ b/lib/features/edit_label/view/edit_label_page.dart @@ -18,6 +18,7 @@ class EditLabelPage extends StatelessWidget { final List additionalFields; final Future Function(BuildContext context, T label) onSubmit; final Future Function(BuildContext context, T label) onDelete; + final bool canDelete; const EditLabelPage({ super.key, @@ -26,6 +27,7 @@ class EditLabelPage extends StatelessWidget { this.additionalFields = const [], required this.onSubmit, required this.onDelete, + required this.canDelete, }); @override @@ -40,6 +42,7 @@ class EditLabelPage extends StatelessWidget { fromJsonT: fromJsonT, onSubmit: onSubmit, onDelete: onDelete, + canDelete: canDelete, ), ); } @@ -51,6 +54,7 @@ class EditLabelForm extends StatelessWidget { final List additionalFields; final Future Function(BuildContext context, T label) onSubmit; final Future Function(BuildContext context, T label) onDelete; + final bool canDelete; const EditLabelForm({ super.key, @@ -59,6 +63,7 @@ class EditLabelForm extends StatelessWidget { required this.additionalFields, required this.onSubmit, required this.onDelete, + required this.canDelete, }); @override @@ -68,7 +73,7 @@ class EditLabelForm extends StatelessWidget { title: Text(S.of(context)!.edit), actions: [ IconButton( - onPressed: () => _onDelete(context), + onPressed: canDelete ? () => _onDelete(context) : null, icon: const Icon(Icons.delete), ), ], diff --git a/lib/features/edit_label/view/impl/edit_correspondent_page.dart b/lib/features/edit_label/view/impl/edit_correspondent_page.dart index f0a6e98..70a8272 100644 --- a/lib/features/edit_label/view/impl/edit_correspondent_page.dart +++ b/lib/features/edit_label/view/impl/edit_correspondent_page.dart @@ -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( label: correspondent, fromJsonT: Correspondent.fromJson, - onSubmit: (context, label) => - context.read().replaceCorrespondent(label), - onDelete: (context, label) => - context.read().removeCorrespondent(label), + onSubmit: (context, label) => context.read().replaceCorrespondent(label), + onDelete: (context, label) => context.read().removeCorrespondent(label), + canDelete: LocalUserAccount.current.paperlessUser.hasPermission( + PermissionAction.delete, + PermissionTarget.correspondent, + ), ); }), ); diff --git a/lib/features/edit_label/view/impl/edit_document_type_page.dart b/lib/features/edit_label/view/impl/edit_document_type_page.dart index c9b6e25..1be9b23 100644 --- a/lib/features/edit_label/view/impl/edit_document_type_page.dart +++ b/lib/features/edit_label/view/impl/edit_document_type_page.dart @@ -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( label: documentType, fromJsonT: DocumentType.fromJson, - onSubmit: (context, label) => - context.read().replaceDocumentType(label), - onDelete: (context, label) => - context.read().removeDocumentType(label), + onSubmit: (context, label) => context.read().replaceDocumentType(label), + onDelete: (context, label) => context.read().removeDocumentType(label), + canDelete: LocalUserAccount.current.paperlessUser.hasPermission( + PermissionAction.delete, + PermissionTarget.documentType, + ), ), ); } diff --git a/lib/features/edit_label/view/impl/edit_storage_path_page.dart b/lib/features/edit_label/view/impl/edit_storage_path_page.dart index fb3786e..8bd95a2 100644 --- a/lib/features/edit_label/view/impl/edit_storage_path_page.dart +++ b/lib/features/edit_label/view/impl/edit_storage_path_page.dart @@ -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( label: storagePath, fromJsonT: StoragePath.fromJson, - onSubmit: (context, label) => - context.read().replaceStoragePath(label), - onDelete: (context, label) => - context.read().removeStoragePath(label), + onSubmit: (context, label) => context.read().replaceStoragePath(label), + onDelete: (context, label) => context.read().removeStoragePath(label), + canDelete: LocalUserAccount.current.paperlessUser.hasPermission( + PermissionAction.delete, + PermissionTarget.storagePath, + ), additionalFields: [ StoragePathAutofillFormBuilderField( name: StoragePath.pathKey, diff --git a/lib/features/edit_label/view/impl/edit_tag_page.dart b/lib/features/edit_label/view/impl/edit_tag_page.dart index 9c2d79e..3a99726 100644 --- a/lib/features/edit_label/view/impl/edit_tag_page.dart +++ b/lib/features/edit_label/view/impl/edit_tag_page.dart @@ -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( label: tag, fromJsonT: Tag.fromJson, - onSubmit: (context, label) => - context.read().replaceTag(label), - onDelete: (context, label) => - context.read().removeTag(label), + onSubmit: (context, label) => context.read().replaceTag(label), + onDelete: (context, label) => context.read().removeTag(label), + canDelete: LocalUserAccount.current.paperlessUser.hasPermission( + PermissionAction.delete, + PermissionTarget.tag, + ), additionalFields: [ FormBuilderColorPickerField( initialValue: tag.color, diff --git a/lib/features/edit_label/view/label_form.dart b/lib/features/edit_label/view/label_form.dart index 52689fb..366a436 100644 --- a/lib/features/edit_label/view/label_form.dart +++ b/lib/features/edit_label/view/label_form.dart @@ -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 { final Widget icon; diff --git a/lib/features/home/view/home_page.dart b/lib/features/home/view/home_page.dart index e1f4eaf..6bac0f8 100644 --- a/lib/features/home/view/home_page.dart +++ b/lib/features/home/view/home_page.dart @@ -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 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 with WidgetsBindingObserver { label: S.of(context)!.inbox, badgeBuilder: (icon) => BlocBuilder( 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 = [ 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( - //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().initialize(); + context.read().initialize(); }, ), BlocListener( @@ -299,14 +287,4 @@ class _HomePageState extends State with WidgetsBindingObserver { setState(() => _currentIndex = index); } } - - void _initializeData(BuildContext context) { - Future.wait([ - context.read().initialize(), - context.read().findAll(), - ]).onError((error, stackTrace) { - showErrorMessage(context, error, stackTrace); - throw error; - }); - } } diff --git a/lib/features/home/view/home_route.dart b/lib/features/home/view/home_route.dart index 3d7928d..0324c3c 100644 --- a/lib/features/home/view/home_route.dart +++ b/lib/features/home/view/home_route.dart @@ -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( create: (context) => CacheManager( Config( @@ -87,7 +87,7 @@ class HomeRoute extends StatelessWidget { apiVersion: paperlessApiVersion, ), ), - if (paperlessApiVersion >= 3) + if (apiVersion.hasMultiUserSupport) ProxyProvider( update: (context, value, previous) => PaperlessUserApiV3Impl( value.client, @@ -98,7 +98,7 @@ class HomeRoute extends StatelessWidget { return MultiProvider( providers: [ ProxyProvider( - update: (context, value, previous) => LabelRepository(value)..initialize(), + update: (context, value, previous) => LabelRepository(value), ), ProxyProvider( update: (context, value, previous) => SavedViewRepository(value)..initialize(), diff --git a/lib/features/home/view/model/api_version.dart b/lib/features/home/view/model/api_version.dart index f44beaf..a7cabd1 100644 --- a/lib/features/home/view/model/api_version.dart +++ b/lib/features/home/view/model/api_version.dart @@ -3,6 +3,5 @@ class ApiVersion { ApiVersion(this.version); - bool get supportsPermissions => version >= 3; bool get hasMultiUserSupport => version >= 3; } diff --git a/lib/features/home/view/widget/verify_identity_page.dart b/lib/features/home/view/widget/verify_identity_page.dart index 45e1e42..f1d3330 100644 --- a/lib/features/home/view/widget/verify_identity_page.dart +++ b/lib/features/home/view/widget/verify_identity_page.dart @@ -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'; diff --git a/lib/features/inbox/view/pages/inbox_page.dart b/lib/features/inbox/view/pages/inbox_page.dart index 0bed511..aa6192a 100644 --- a/lib/features/inbox/view/pages/inbox_page.dart +++ b/lib/features/inbox/view/pages/inbox_page.dart @@ -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 createState() => _InboxPageState(); } -class _InboxPageState extends State - with DocumentPagingViewMixin { - final SliverOverlapAbsorberHandle searchBarHandle = - SliverOverlapAbsorberHandle(); +class _InboxPageState extends State with DocumentPagingViewMixin { + final SliverOverlapAbsorberHandle searchBarHandle = SliverOverlapAbsorberHandle(); @override final pagingScrollController = ScrollController(); @@ -80,8 +78,7 @@ class _InboxPageState extends State } else if (state.documents.isEmpty) { return Center( child: InboxEmptyWidget( - emptyStateRefreshIndicatorKey: - _emptyStateRefreshIndicatorKey, + emptyStateRefreshIndicatorKey: _emptyStateRefreshIndicatorKey, ), ); } else { @@ -92,8 +89,7 @@ class _InboxPageState extends State SliverToBoxAdapter( child: HintCard( show: !state.isHintAcknowledged, - hintText: - S.of(context)!.swipeLeftToMarkADocumentAsSeen, + hintText: S.of(context)!.swipeLeftToMarkADocumentAsSeen, onHintAcknowledged: () => context.read().acknowledgeHint(), ), @@ -108,13 +104,10 @@ class _InboxPageState extends State 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 ], ).padded(), confirmDismiss: (_) => _onItemDismissed(doc), - key: UniqueKey(), + key: ValueKey(doc.id), child: InboxItem(document: doc), ); } @@ -227,14 +220,15 @@ class _InboxPageState extends State 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 _onUndoMarkAsSeen( @@ -242,9 +236,7 @@ class _InboxPageState extends State Iterable removedTags, ) async { try { - await context - .read() - .undoRemoveFromInbox(document, removedTags); + await context.read().undoRemoveFromInbox(document, removedTags); } on PaperlessServerException catch (error, stackTrace) { showErrorMessage(context, error, stackTrace); } diff --git a/lib/features/inbox/view/widgets/inbox_item.dart b/lib/features/inbox/view/widgets/inbox_item.dart index 910d487..23bf0a2 100644 --- a/lib/features/inbox/view/widgets/inbox_item.dart +++ b/lib/features/inbox/view/widgets/inbox_item.dart @@ -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 { ], ), ), - SizedBox( - height: 56, + LimitedBox( + maxHeight: 56, child: _buildActions(context), ), ], @@ -121,58 +120,46 @@ class _InboxItemState extends State { } 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( - context: context, - builder: (context) => DeleteDocumentConfirmationDialog(document: widget.document), - ) ?? - false; - if (shouldDelete) { - context.read().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( + context: context, + builder: (context) => + DeleteDocumentConfirmationDialog(document: widget.document), + ) ?? + false; + if (shouldDelete) { + context.read().delete(widget.document); + } + }, + ), ), - ), ]; - - // return FutureBuilder( - // future: _fieldSuggestions, - // builder: (context, snapshot) { - // List? 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().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 { setState(() { _isAsnAssignLoading = true; }); + context.read().assignAsn(widget.document).whenComplete( () => setState(() => _isAsnAssignLoading = false), ); diff --git a/lib/features/inbox/view/widgets/inbox_list_loading_widget.dart b/lib/features/inbox/view/widgets/inbox_list_loading_widget.dart index ce9280c..cdd89ea 100644 --- a/lib/features/inbox/view/widgets/inbox_list_loading_widget.dart +++ b/lib/features/inbox/view/widgets/inbox_list_loading_widget.dart @@ -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(), diff --git a/lib/features/labels/cubit/label_cubit_mixin.dart b/lib/features/labels/cubit/label_cubit_mixin.dart index 032d30d..020df25 100644 --- a/lib/features/labels/cubit/label_cubit_mixin.dart +++ b/lib/features/labels/cubit/label_cubit_mixin.dart @@ -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'; diff --git a/lib/features/labels/tags/view/widgets/tags_form_field.dart b/lib/features/labels/tags/view/widgets/tags_form_field.dart index e4a2f62..a385ad6 100644 --- a/lib/features/labels/tags/view/widgets/tags_form_field.dart +++ b/lib/features/labels/tags/view/widgets/tags_form_field.dart @@ -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) { diff --git a/lib/features/labels/view/pages/labels_page.dart b/lib/features/labels/view/pages/labels_page.dart index ef28ab2..6c93123 100644 --- a/lib/features/labels/view/pages/labels_page.dart +++ b/lib/features/labels/view/pages/labels_page.dart @@ -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 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 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 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 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, diff --git a/lib/features/labels/view/widgets/fullscreen_label_form.dart b/lib/features/labels/view/widgets/fullscreen_label_form.dart index 414a40a..8a949c2 100644 --- a/lib/features/labels/view/widgets/fullscreen_label_form.dart +++ b/lib/features/labels/view/widgets/fullscreen_label_form.dart @@ -16,6 +16,7 @@ class FullscreenLabelForm extends StatefulWidget { final String? addNewLabelText; final bool autofocus; final bool allowSelectUnassigned; + final bool canCreateNewLabel; FullscreenLabelForm({ super.key, @@ -29,6 +30,7 @@ class FullscreenLabelForm extends StatefulWidget { this.addNewLabelText, this.autofocus = true, this.allowSelectUnassigned = true, + required this.canCreateNewLabel, }) : assert( !(initialValue?.isOnlyAssigned() ?? false) || showAnyAssignedOption, ), diff --git a/lib/features/labels/view/widgets/label_form_field.dart b/lib/features/labels/view/widgets/label_form_field.dart index 29a839d..46a4504 100644 --- a/lib/features/labels/view/widgets/label_form_field.dart +++ b/lib/features/labels/view/widgets/label_form_field.dart @@ -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 extends StatelessWidget { final List suggestions; final String? addLabelText; final bool allowSelectUnassigned; + final bool canCreateNewLabel; const LabelFormField({ Key? key, @@ -44,6 +43,7 @@ class LabelFormField 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 extends StatelessWidget { ), openBuilder: (context, closeForm) => FullscreenLabelForm( allowSelectUnassigned: allowSelectUnassigned, + canCreateNewLabel: canCreateNewLabel, addNewLabelText: addLabelText, leadingIcon: prefixIcon, onCreateNewLabel: addLabelPageBuilder != null diff --git a/lib/features/labels/view/widgets/label_item.dart b/lib/features/labels/view/widgets/label_item.dart index e8faa79..52c950b 100644 --- a/lib/features/labels/view/widgets/label_item.dart +++ b/lib/features/labels/view/widgets/label_item.dart @@ -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 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 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(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, ); } } diff --git a/lib/features/labels/view/widgets/label_tab_view.dart b/lib/features/labels/view/widgets/label_tab_view.dart index 5174e96..4918fb0 100644 --- a/lib/features/labels/view/widgets/label_tab_view.dart +++ b/lib/features/labels/view/widgets/label_tab_view.dart @@ -11,7 +11,9 @@ class LabelTabView extends StatelessWidget { final Map 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 extends StatelessWidget { required this.onAddNew, required this.emptyStateActionButtonLabel, required this.labels, + required this.canEdit, + required this.canAddNew, }); @override @@ -54,7 +58,7 @@ class LabelTabView extends StatelessWidget { textAlign: TextAlign.center, ), TextButton( - onPressed: onAddNew, + onPressed: canAddNew ? onAddNew : null, child: Text(emptyStateActionButtonLabel), ), ].padded(), @@ -70,14 +74,11 @@ class LabelTabView 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, diff --git a/lib/features/labels/view/widgets/label_text.dart b/lib/features/labels/view/widgets/label_text.dart index a6e2df8..bf15b42 100644 --- a/lib/features/labels/view/widgets/label_text.dart +++ b/lib/features/labels/view/widgets/label_text.dart @@ -20,6 +20,5 @@ class LabelText extends StatelessWidget { maxLines: 1, overflow: TextOverflow.ellipsis, ); - ; } } diff --git a/lib/features/linked_documents/view/linked_documents_page.dart b/lib/features/linked_documents/view/linked_documents_page.dart index eeaa148..70c1bf2 100644 --- a/lib/features/linked_documents/view/linked_documents_page.dart +++ b/lib/features/linked_documents/view/linked_documents_page.dart @@ -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}); diff --git a/lib/features/login/cubit/authentication_cubit.dart b/lib/features/login/cubit/authentication_cubit.dart index 0dc1d76..cdfb5d3 100644 --- a/lib/features/login/cubit/authentication_cubit.dart +++ b/lib/features/login/cubit/authentication_cubit.dart @@ -179,6 +179,7 @@ class AuthenticationCubit extends Cubit { return; } } + final userCredentialsBox = await _getUserCredentialsBox(); final authentication = userCredentialsBox.get(globalSettings.currentLoggedInUser!); await userCredentialsBox.close(); diff --git a/lib/features/login/model/authentication_information.dart b/lib/features/login/model/authentication_information.dart index 7c1c192..1743f1f 100644 --- a/lib/features/login/model/authentication_information.dart +++ b/lib/features/login/model/authentication_information.dart @@ -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'; diff --git a/lib/features/login/model/client_certificate.dart b/lib/features/login/model/client_certificate.dart index e3af6df..8c920ba 100644 --- a/lib/features/login/model/client_certificate.dart +++ b/lib/features/login/model/client_certificate.dart @@ -1,4 +1,3 @@ -import 'dart:convert'; import 'dart:typed_data'; import 'package:hive_flutter/adapters.dart'; diff --git a/lib/features/login/model/client_certificate_form_model.dart b/lib/features/login/model/client_certificate_form_model.dart index cac452d..afb9ddb 100644 --- a/lib/features/login/model/client_certificate_form_model.dart +++ b/lib/features/login/model/client_certificate_form_model.dart @@ -1,4 +1,3 @@ -import 'dart:convert'; import 'dart:typed_data'; class ClientCertificateFormModel { diff --git a/lib/features/login/view/widgets/form_fields/client_certificate_form_field.dart b/lib/features/login/view/widgets/form_fields/client_certificate_form_field.dart index 560c478..c492b76 100644 --- a/lib/features/login/view/widgets/form_fields/client_certificate_form_field.dart +++ b/lib/features/login/view/widgets/form_fields/client_certificate_form_field.dart @@ -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'; diff --git a/lib/features/login/view/widgets/form_fields/server_address_form_field.dart b/lib/features/login/view/widgets/form_fields/server_address_form_field.dart index dbd9609..9d0b5a0 100644 --- a/lib/features/login/view/widgets/form_fields/server_address_form_field.dart +++ b/lib/features/login/view/widgets/form_fields/server_address_form_field.dart @@ -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 { if (!RegExp(r"https?://.*").hasMatch(value!)) { return S.of(context)!.serverAddressMustIncludeAScheme; } + return null; }, decoration: InputDecoration( hintText: "http://192.168.1.50:8000", diff --git a/lib/features/login/view/widgets/form_fields/user_credentials_form_field.dart b/lib/features/login/view/widgets/form_fields/user_credentials_form_field.dart index a70be87..a517fc6 100644 --- a/lib/features/login/view/widgets/form_fields/user_credentials_form_field.dart +++ b/lib/features/login/view/widgets/form_fields/user_credentials_form_field.dart @@ -39,6 +39,7 @@ class _UserCredentialsFormFieldState extends State { 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 { if (value?.trim().isEmpty ?? true) { return S.of(context)!.passwordMustNotBeEmpty; } + return null; }, ), ].map((child) => child.padded()).toList(), diff --git a/lib/features/login/view/widgets/login_pages/server_connection_page.dart b/lib/features/login/view/widgets/login_pages/server_connection_page.dart index 550ed90..eaf6b66 100644 --- a/lib/features/login/view/widgets/login_pages/server_connection_page.dart +++ b/lib/features/login/view/widgets/login_pages/server_connection_page.dart @@ -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'; diff --git a/lib/features/notifications/services/local_notification_service.dart b/lib/features/notifications/services/local_notification_service.dart index 97d0cff..089132c 100644 --- a/lib/features/notifications/services/local_notification_service.dart +++ b/lib/features/notifications/services/local_notification_service.dart @@ -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'; diff --git a/lib/features/paged_document_view/cubit/paged_documents_state.dart b/lib/features/paged_document_view/cubit/paged_documents_state.dart index d956cb3..298248e 100644 --- a/lib/features/paged_document_view/cubit/paged_documents_state.dart +++ b/lib/features/paged_document_view/cubit/paged_documents_state.dart @@ -1,5 +1,4 @@ import 'package:equatable/equatable.dart'; -import 'package:json_annotation/json_annotation.dart'; import 'package:paperless_api/paperless_api.dart'; /// diff --git a/lib/features/saved_view/cubit/saved_view_cubit.dart b/lib/features/saved_view/cubit/saved_view_cubit.dart index ca2bff6..ce4a290 100644 --- a/lib/features/saved_view/cubit/saved_view_cubit.dart +++ b/lib/features/saved_view/cubit/saved_view_cubit.dart @@ -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'; diff --git a/lib/features/saved_view/view/saved_view_list.dart b/lib/features/saved_view/view/saved_view_list.dart index 5f6ac15..7d349c4 100644 --- a/lib/features/saved_view/view/saved_view_list.dart +++ b/lib/features/saved_view/view/saved_view_list.dart @@ -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.", ), diff --git a/lib/features/saved_view_details/cubit/saved_view_details_cubit.dart b/lib/features/saved_view_details/cubit/saved_view_details_cubit.dart index 4f5d393..1dc9448 100644 --- a/lib/features/saved_view_details/cubit/saved_view_details_cubit.dart +++ b/lib/features/saved_view_details/cubit/saved_view_details_cubit.dart @@ -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'; diff --git a/lib/features/saved_view_details/view/saved_view_details_page.dart b/lib/features/saved_view_details/view/saved_view_details_page.dart index 234d21f..d19e0e1 100644 --- a/lib/features/saved_view_details/view/saved_view_details_page.dart +++ b/lib/features/saved_view_details/view/saved_view_details_page.dart @@ -9,7 +9,6 @@ import 'package:paperless_mobile/features/documents/view/widgets/selection/confi import 'package:paperless_mobile/features/documents/view/widgets/selection/view_type_selection_widget.dart'; import 'package:paperless_mobile/features/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 Function(SavedView savedView) onDelete; diff --git a/lib/features/settings/view/manage_accounts_page.dart b/lib/features/settings/view/manage_accounts_page.dart index edec819..2ab1f96 100644 --- a/lib/features/settings/view/manage_accounts_page.dart +++ b/lib/features/settings/view/manage_accounts_page.dart @@ -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( - 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().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), ), ], ), diff --git a/lib/features/settings/view/pages/security_settings_page.dart b/lib/features/settings/view/pages/security_settings_page.dart index da28480..c98b2c8 100644 --- a/lib/features/settings/view/pages/security_settings_page.dart +++ b/lib/features/settings/view/pages/security_settings_page.dart @@ -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), ) ], ), diff --git a/lib/features/settings/view/widgets/clear_storage_settings.dart b/lib/features/settings/view/widgets/clear_storage_settings.dart index 78709bf..a8f3ea4 100644 --- a/lib/features/settings/view/widgets/clear_storage_settings.dart +++ b/lib/features/settings/view/widgets/clear_storage_settings.dart @@ -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; diff --git a/lib/features/settings/view/widgets/color_scheme_option_setting.dart b/lib/features/settings/view/widgets/color_scheme_option_setting.dart index af6f3b0..0b29cc3 100644 --- a/lib/features/settings/view/widgets/color_scheme_option_setting.dart +++ b/lib/features/settings/view/widgets/color_scheme_option_setting.dart @@ -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; diff --git a/lib/features/settings/view/widgets/global_settings_builder.dart b/lib/features/settings/view/widgets/global_settings_builder.dart index e8425d3..d24f9bd 100644 --- a/lib/features/settings/view/widgets/global_settings_builder.dart +++ b/lib/features/settings/view/widgets/global_settings_builder.dart @@ -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'; diff --git a/lib/features/settings/view/widgets/radio_settings_dialog.dart b/lib/features/settings/view/widgets/radio_settings_dialog.dart index 96329bc..658fdeb 100644 --- a/lib/features/settings/view/widgets/radio_settings_dialog.dart +++ b/lib/features/settings/view/widgets/radio_settings_dialog.dart @@ -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 extends StatefulWidget { final List> options; diff --git a/lib/features/settings/view/widgets/theme_mode_setting.dart b/lib/features/settings/view/widgets/theme_mode_setting.dart index a0e490d..a4b563b 100644 --- a/lib/features/settings/view/widgets/theme_mode_setting.dart +++ b/lib/features/settings/view/widgets/theme_mode_setting.dart @@ -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'; diff --git a/lib/features/settings/view/widgets/user_avatar.dart b/lib/features/settings/view/widgets/user_avatar.dart index 1e44bef..41be64a 100644 --- a/lib/features/settings/view/widgets/user_avatar.dart +++ b/lib/features/settings/view/widgets/user_avatar.dart @@ -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; diff --git a/lib/features/settings/view/widgets/user_settings_builder.dart b/lib/features/settings/view/widgets/user_settings_builder.dart index b4247fc..0684251 100644 --- a/lib/features/settings/view/widgets/user_settings_builder.dart +++ b/lib/features/settings/view/widgets/user_settings_builder.dart @@ -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( diff --git a/lib/features/sharing/share_intent_queue.dart b/lib/features/sharing/share_intent_queue.dart index b22fb04..79ccd60 100644 --- a/lib/features/sharing/share_intent_queue.dart +++ b/lib/features/sharing/share_intent_queue.dart @@ -1,5 +1,4 @@ import 'dart:collection'; -import 'dart:developer'; import 'package:flutter/widgets.dart'; import 'package:receive_sharing_intent/receive_sharing_intent.dart'; diff --git a/lib/helpers/message_helpers.dart b/lib/helpers/message_helpers.dart index 4e5d977..09ddaee 100644 --- a/lib/helpers/message_helpers.dart +++ b/lib/helpers/message_helpers.dart @@ -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.", diff --git a/lib/main.dart b/lib/main.dart index eea1556..69b10a2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -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 _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(HiveBoxes.localUserAccount); await Hive.openBox(HiveBoxes.localUserAppState); final globalSettingsBox = await Hive.openBox(HiveBoxes.globalSettings); @@ -126,9 +125,7 @@ void main() async { providers: [ ChangeNotifierProvider.value(value: sessionManager), Provider.value(value: localAuthService), - Provider.value( - value: connectivityStatusService, - ), + Provider.value(value: connectivityStatusService), Provider.value(value: localNotificationService), Provider.value(value: DocumentChangedNotifier()), ], @@ -136,12 +133,7 @@ void main() async { providers: [ BlocProvider.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 { + late final StreamSubscription _shareMediaSubscription; @override void didChangeDependencies() { super.didChangeDependencies(); @@ -223,6 +216,12 @@ class _AuthenticationWrapperState extends State { }); } + @override + void dispose() { + _shareMediaSubscription.cancel(); + super.dispose(); + } + @override void initState() { super.initState(); @@ -233,7 +232,8 @@ class _AuthenticationWrapperState extends State { } 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); } diff --git a/lib/theme.dart b/lib/theme.dart index adee91d..46ab95d 100644 --- a/lib/theme.dart +++ b/lib/theme.dart @@ -43,7 +43,7 @@ ThemeData buildTheme({ inputDecorationTheme: _defaultInputDecorationTheme, listTileTheme: _defaultListTileTheme, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - appBarTheme: AppBarTheme( + appBarTheme: const AppBarTheme( scrolledUnderElevation: 0, ), chipTheme: ChipThemeData( diff --git a/packages/paperless_api/lib/config/hive/hive_type_ids.dart b/packages/paperless_api/lib/config/hive/hive_type_ids.dart index c2a3887..d08224a 100644 --- a/packages/paperless_api/lib/config/hive/hive_type_ids.dart +++ b/packages/paperless_api/lib/config/hive/hive_type_ids.dart @@ -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()); diff --git a/packages/paperless_api/lib/src/converters/date_range_query_json_converter.dart b/packages/paperless_api/lib/src/converters/date_range_query_json_converter.dart index 52a45ee..7115fe2 100644 --- a/packages/paperless_api/lib/src/converters/date_range_query_json_converter.dart +++ b/packages/paperless_api/lib/src/converters/date_range_query_json_converter.dart @@ -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> { diff --git a/packages/paperless_api/lib/src/converters/local_date_time_json_converter.dart b/packages/paperless_api/lib/src/converters/local_date_time_json_converter.dart index 5f95bc2..71b9060 100644 --- a/packages/paperless_api/lib/src/converters/local_date_time_json_converter.dart +++ b/packages/paperless_api/lib/src/converters/local_date_time_json_converter.dart @@ -1,4 +1,3 @@ -import 'dart:developer'; import 'package:json_annotation/json_annotation.dart'; diff --git a/packages/paperless_api/lib/src/models/filter_rule_model.dart b/packages/paperless_api/lib/src/models/filter_rule_model.dart index b8feec3..21268e0 100644 --- a/packages/paperless_api/lib/src/models/filter_rule_model.dart +++ b/packages/paperless_api/lib/src/models/filter_rule_model.dart @@ -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'; diff --git a/packages/paperless_api/lib/src/models/group_model.dart b/packages/paperless_api/lib/src/models/group_model.dart index f932e4a..ac952d5 100644 --- a/packages/paperless_api/lib/src/models/group_model.dart +++ b/packages/paperless_api/lib/src/models/group_model.dart @@ -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 permissions, + @HiveField(2) required List permissions, }) = _GroupModel; factory GroupModel.fromJson(Map json) => _$GroupModelFromJson(json); diff --git a/packages/paperless_api/lib/src/models/group_model.freezed.dart b/packages/paperless_api/lib/src/models/group_model.freezed.dart index d91b6bc..d3bc9cb 100644 --- a/packages/paperless_api/lib/src/models/group_model.freezed.dart +++ b/packages/paperless_api/lib/src/models/group_model.freezed.dart @@ -25,7 +25,7 @@ mixin _$GroupModel { @HiveField(1) String get name => throw _privateConstructorUsedError; @HiveField(2) - List get permissions => throw _privateConstructorUsedError; + List get permissions => throw _privateConstructorUsedError; Map 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 permissions}); + @HiveField(2) List 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, + as List, ) as $Val); } } @@ -90,7 +90,7 @@ abstract class _$$_GroupModelCopyWith<$Res> $Res call( {@HiveField(0) int id, @HiveField(1) String name, - @HiveField(2) List permissions}); + @HiveField(2) List permissions}); } /// @nodoc @@ -120,7 +120,7 @@ class __$$_GroupModelCopyWithImpl<$Res> permissions: null == permissions ? _value._permissions : permissions // ignore: cast_nullable_to_non_nullable - as List, + as List, )); } } @@ -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 permissions}) + @HiveField(2) required final List permissions}) : _permissions = permissions; factory _$_GroupModel.fromJson(Map json) => @@ -143,10 +143,10 @@ class _$_GroupModel implements _GroupModel { @override @HiveField(1) final String name; - final List _permissions; + final List _permissions; @override @HiveField(2) - List get permissions { + List 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 permissions}) = - _$_GroupModel; + {@HiveField(0) required final int id, + @HiveField(1) required final String name, + @HiveField(2) required final List permissions}) = _$_GroupModel; factory _GroupModel.fromJson(Map json) = _$_GroupModel.fromJson; @@ -205,7 +204,7 @@ abstract class _GroupModel implements GroupModel { String get name; @override @HiveField(2) - List get permissions; + List get permissions; @override @JsonKey(ignore: true) _$$_GroupModelCopyWith<_$_GroupModel> get copyWith => diff --git a/packages/paperless_api/lib/src/models/labels/tag_model.dart b/packages/paperless_api/lib/src/models/labels/tag_model.dart index f317d32..6052809 100644 --- a/packages/paperless_api/lib/src/models/labels/tag_model.dart +++ b/packages/paperless_api/lib/src/models/labels/tag_model.dart @@ -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, diff --git a/packages/paperless_api/lib/src/models/paperless_server_exception.dart b/packages/paperless_api/lib/src/models/paperless_server_exception.dart index 3d180de..10ada4d 100644 --- a/packages/paperless_api/lib/src/models/paperless_server_exception.dart +++ b/packages/paperless_api/lib/src/models/paperless_server_exception.dart @@ -53,6 +53,5 @@ enum ErrorCode { requestTimedOut, unsupportedFileFormat, missingClientCertificate, - acknowledgeTasksError, - notAuthorized; + acknowledgeTasksError; } diff --git a/packages/paperless_api/lib/src/models/permissions/user_permissions.dart b/packages/paperless_api/lib/src/models/permissions/user_permissions.dart index 14d0a54..dda513e 100644 --- a/packages/paperless_api/lib/src/models/permissions/user_permissions.dart +++ b/packages/paperless_api/lib/src/models/permissions/user_permissions.dart @@ -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); } diff --git a/packages/paperless_api/lib/src/models/query_parameters/date_range_queries/unset_date_range_query.dart b/packages/paperless_api/lib/src/models/query_parameters/date_range_queries/unset_date_range_query.dart index 94e494d..438c9d7 100644 --- a/packages/paperless_api/lib/src/models/query_parameters/date_range_queries/unset_date_range_query.dart +++ b/packages/paperless_api/lib/src/models/query_parameters/date_range_queries/unset_date_range_query.dart @@ -1,5 +1,4 @@ import 'package:hive/hive.dart'; -import 'package:paperless_api/config/hive/hive_type_ids.dart'; import 'package:paperless_api/src/models/query_parameters/date_range_queries/date_range_query_field.dart'; import 'date_range_query.dart'; diff --git a/packages/paperless_api/lib/src/models/query_parameters/tags_query/tags_query.dart b/packages/paperless_api/lib/src/models/query_parameters/tags_query/tags_query.dart index 6d66da0..f099539 100644 --- a/packages/paperless_api/lib/src/models/query_parameters/tags_query/tags_query.dart +++ b/packages/paperless_api/lib/src/models/query_parameters/tags_query/tags_query.dart @@ -1,4 +1,3 @@ -import 'package:equatable/equatable.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hive/hive.dart'; import 'package:paperless_api/config/hive/hive_type_ids.dart'; diff --git a/packages/paperless_api/lib/src/models/user_model.dart b/packages/paperless_api/lib/src/models/user_model.dart index acfba27..509b6a1 100644 --- a/packages/paperless_api/lib/src/models/user_model.dart +++ b/packages/paperless_api/lib/src/models/user_model.dart @@ -16,17 +16,33 @@ class UserModel with _$UserModel { @JsonSerializable(fieldRename: FieldRename.snake) @HiveType(typeId: PaperlessApiHiveTypeIds.userModelv3) const factory UserModel.v3({ - @HiveField(0) required int id, - @HiveField(1) required String username, - @HiveField(2) required String email, - @HiveField(3) String? firstName, - @HiveField(4) String? lastName, - @HiveField(5) DateTime? dateJoined, - @HiveField(6) @Default(true) bool isStaff, - @HiveField(7) @Default(true) bool isActive, - @HiveField(8) @Default(true) bool isSuperuser, - @HiveField(9) @Default([]) List groups, - @HiveField(10) @Default(UserPermissions.values) List userPermissions, + @HiveField(0) + required int id, + @HiveField(1) + required String username, + @HiveField(2) + required String email, + @HiveField(3) + String? firstName, + @HiveField(4) + String? lastName, + @HiveField(5) + DateTime? dateJoined, + @HiveField(6) + @Default(true) + bool isStaff, + @HiveField(7) + @Default(true) + bool isActive, + @HiveField(8) + @Default(true) + bool isSuperuser, + @HiveField(9) + @Default([]) + List groups, + @HiveField(10) + @Default([]) + List userPermissions, @HiveField(11) @Default(InheritedPermissions.values) List inheritedPermissions, @@ -55,13 +71,14 @@ class UserModel with _$UserModel { }, ); - bool hasPermission(UserPermissions permission) { + bool hasPermission(PermissionAction action, PermissionTarget target) { return map( v3: (value) { if (value.isSuperuser) { return true; } - return value.userPermissions.contains(permission); + final permissionIdentifier = "${action.value}_${target.value}"; + return value.userPermissions.contains(permissionIdentifier); }, v2: (value) { // In previous versions, all users have all permissions. diff --git a/packages/paperless_api/lib/src/models/user_model.freezed.dart b/packages/paperless_api/lib/src/models/user_model.freezed.dart index c675095..8d17d63 100644 --- a/packages/paperless_api/lib/src/models/user_model.freezed.dart +++ b/packages/paperless_api/lib/src/models/user_model.freezed.dart @@ -46,7 +46,7 @@ mixin _$UserModel { @HiveField(7) bool isActive, @HiveField(8) bool isSuperuser, @HiveField(9) List groups, - @HiveField(10) List userPermissions, + @HiveField(10) List userPermissions, @HiveField(11) List inheritedPermissions) v3, required TResult Function(@HiveField(0) @JsonKey(name: "user_id") int id, @@ -67,7 +67,7 @@ mixin _$UserModel { @HiveField(7) bool isActive, @HiveField(8) bool isSuperuser, @HiveField(9) List groups, - @HiveField(10) List userPermissions, + @HiveField(10) List userPermissions, @HiveField(11) List inheritedPermissions)? v3, TResult? Function(@HiveField(0) @JsonKey(name: "user_id") int id, @@ -88,7 +88,7 @@ mixin _$UserModel { @HiveField(7) bool isActive, @HiveField(8) bool isSuperuser, @HiveField(9) List groups, - @HiveField(10) List userPermissions, + @HiveField(10) List userPermissions, @HiveField(11) List inheritedPermissions)? v3, TResult Function(@HiveField(0) @JsonKey(name: "user_id") int id, @@ -178,7 +178,7 @@ abstract class _$$UserModelV3CopyWith<$Res> @HiveField(7) bool isActive, @HiveField(8) bool isSuperuser, @HiveField(9) List groups, - @HiveField(10) List userPermissions, + @HiveField(10) List userPermissions, @HiveField(11) List inheritedPermissions}); } @@ -250,7 +250,7 @@ class __$$UserModelV3CopyWithImpl<$Res> userPermissions: null == userPermissions ? _value._userPermissions : userPermissions // ignore: cast_nullable_to_non_nullable - as List, + as List, inheritedPermissions: null == inheritedPermissions ? _value._inheritedPermissions : inheritedPermissions // ignore: cast_nullable_to_non_nullable @@ -275,8 +275,7 @@ class _$UserModelV3 extends UserModelV3 { @HiveField(7) this.isActive = true, @HiveField(8) this.isSuperuser = true, @HiveField(9) final List groups = const [], - @HiveField(10) final List userPermissions = - UserPermissions.values, + @HiveField(10) final List userPermissions = const [], @HiveField(11) final List inheritedPermissions = InheritedPermissions.values, final String? $type}) @@ -329,11 +328,11 @@ class _$UserModelV3 extends UserModelV3 { return EqualUnmodifiableListView(_groups); } - final List _userPermissions; + final List _userPermissions; @override @JsonKey() @HiveField(10) - List get userPermissions { + List get userPermissions { if (_userPermissions is EqualUnmodifiableListView) return _userPermissions; // ignore: implicit_dynamic_type return EqualUnmodifiableListView(_userPermissions); @@ -422,7 +421,7 @@ class _$UserModelV3 extends UserModelV3 { @HiveField(7) bool isActive, @HiveField(8) bool isSuperuser, @HiveField(9) List groups, - @HiveField(10) List userPermissions, + @HiveField(10) List userPermissions, @HiveField(11) List inheritedPermissions) v3, required TResult Function(@HiveField(0) @JsonKey(name: "user_id") int id, @@ -447,7 +446,7 @@ class _$UserModelV3 extends UserModelV3 { @HiveField(7) bool isActive, @HiveField(8) bool isSuperuser, @HiveField(9) List groups, - @HiveField(10) List userPermissions, + @HiveField(10) List userPermissions, @HiveField(11) List inheritedPermissions)? v3, TResult? Function(@HiveField(0) @JsonKey(name: "user_id") int id, @@ -483,7 +482,7 @@ class _$UserModelV3 extends UserModelV3 { @HiveField(7) bool isActive, @HiveField(8) bool isSuperuser, @HiveField(9) List groups, - @HiveField(10) List userPermissions, + @HiveField(10) List userPermissions, @HiveField(11) List inheritedPermissions)? v3, TResult Function(@HiveField(0) @JsonKey(name: "user_id") int id, @@ -560,7 +559,7 @@ abstract class UserModelV3 extends UserModel { @HiveField(9) final List groups, @HiveField(10) - final List userPermissions, + final List userPermissions, @HiveField(11) final List inheritedPermissions}) = _$UserModelV3; @@ -592,7 +591,7 @@ abstract class UserModelV3 extends UserModel { @HiveField(9) List get groups; @HiveField(10) - List get userPermissions; + List get userPermissions; @HiveField(11) List get inheritedPermissions; @override @@ -718,7 +717,7 @@ class _$UserModelV2 extends UserModelV2 { @HiveField(7) bool isActive, @HiveField(8) bool isSuperuser, @HiveField(9) List groups, - @HiveField(10) List userPermissions, + @HiveField(10) List userPermissions, @HiveField(11) List inheritedPermissions) v3, required TResult Function(@HiveField(0) @JsonKey(name: "user_id") int id, @@ -742,7 +741,7 @@ class _$UserModelV2 extends UserModelV2 { @HiveField(7) bool isActive, @HiveField(8) bool isSuperuser, @HiveField(9) List groups, - @HiveField(10) List userPermissions, + @HiveField(10) List userPermissions, @HiveField(11) List inheritedPermissions)? v3, TResult? Function(@HiveField(0) @JsonKey(name: "user_id") int id, @@ -766,7 +765,7 @@ class _$UserModelV2 extends UserModelV2 { @HiveField(7) bool isActive, @HiveField(8) bool isSuperuser, @HiveField(9) List groups, - @HiveField(10) List userPermissions, + @HiveField(10) List userPermissions, @HiveField(11) List inheritedPermissions)? v3, TResult Function(@HiveField(0) @JsonKey(name: "user_id") int id, diff --git a/packages/paperless_api/lib/src/modules/documents_api/paperless_documents_api_impl.dart b/packages/paperless_api/lib/src/modules/documents_api/paperless_documents_api_impl.dart index 0f6d251..d444909 100644 --- a/packages/paperless_api/lib/src/modules/documents_api/paperless_documents_api_impl.dart +++ b/packages/paperless_api/lib/src/modules/documents_api/paperless_documents_api_impl.dart @@ -154,10 +154,10 @@ class PaperlessDocumentsApiImpl implements PaperlessDocumentsApi { @override Future findNextAsn() async { - final DocumentFilter asnQueryFilter = DocumentFilter( + const DocumentFilter asnQueryFilter = DocumentFilter( sortField: SortField.archiveSerialNumber, sortOrder: SortOrder.descending, - asnQuery: const IdQueryParameter.anyAssigned(), + asnQuery: IdQueryParameter.anyAssigned(), page: 1, pageSize: 1, ); diff --git a/packages/paperless_api/lib/src/modules/tasks_api/paperless_tasks_api_impl.dart b/packages/paperless_api/lib/src/modules/tasks_api/paperless_tasks_api_impl.dart index e38ba81..c40be65 100644 --- a/packages/paperless_api/lib/src/modules/tasks_api/paperless_tasks_api_impl.dart +++ b/packages/paperless_api/lib/src/modules/tasks_api/paperless_tasks_api_impl.dart @@ -2,7 +2,6 @@ import 'dart:developer'; import 'package:dio/dio.dart'; import 'package:paperless_api/paperless_api.dart'; -import 'package:paperless_api/src/request_utils.dart'; class PaperlessTasksApiImpl implements PaperlessTasksApi { final Dio _client; diff --git a/packages/paperless_api/test/saved_view_test.dart b/packages/paperless_api/test/saved_view_test.dart index e87d979..adabfe3 100644 --- a/packages/paperless_api/test/saved_view_test.dart +++ b/packages/paperless_api/test/saved_view_test.dart @@ -1,6 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:paperless_api/paperless_api.dart'; -import 'package:paperless_api/src/models/query_parameters/text_query.dart'; void main() { group('Validate parsing logic from [SavedView] to [DocumentFilter]:', () { @@ -202,16 +201,16 @@ void main() { test('Values are correctly parsed if unset.', () { expect( SavedView.fromDocumentFilter( - DocumentFilter( - correspondent: const IdQueryParameter.unset(), - documentType: const IdQueryParameter.unset(), - storagePath: const IdQueryParameter.unset(), - tags: const IdsTagsQuery(), + const DocumentFilter( + correspondent: IdQueryParameter.unset(), + documentType: IdQueryParameter.unset(), + storagePath: IdQueryParameter.unset(), + tags: IdsTagsQuery(), sortField: SortField.created, sortOrder: SortOrder.descending, - added: const UnsetDateRangeQuery(), - created: const UnsetDateRangeQuery(), - query: const TextQuery(), + added: UnsetDateRangeQuery(), + created: UnsetDateRangeQuery(), + query: TextQuery(), ), name: "test_name", showInSidebar: false, diff --git a/packages/paperless_document_scanner/example/lib/edge_detection_shape/animated_touch_bubble_part.dart b/packages/paperless_document_scanner/example/lib/edge_detection_shape/animated_touch_bubble_part.dart index 138a78a..d80aabc 100644 --- a/packages/paperless_document_scanner/example/lib/edge_detection_shape/animated_touch_bubble_part.dart +++ b/packages/paperless_document_scanner/example/lib/edge_detection_shape/animated_touch_bubble_part.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; class AnimatedTouchBubblePart extends StatefulWidget { - AnimatedTouchBubblePart({ + const AnimatedTouchBubblePart({super.key, required this.dragging, required this.size, }); @@ -35,7 +35,7 @@ class _AnimatedTouchBubblePartState extends State ).animate( CurvedAnimation( parent: _controller, - curve: Interval(0.5, 1.0), + curve: const Interval(0.5, 1.0), ), ); diff --git a/packages/paperless_document_scanner/example/lib/edge_detection_shape/edge_detection_shape.dart b/packages/paperless_document_scanner/example/lib/edge_detection_shape/edge_detection_shape.dart index 2c63ede..f171026 100644 --- a/packages/paperless_document_scanner/example/lib/edge_detection_shape/edge_detection_shape.dart +++ b/packages/paperless_document_scanner/example/lib/edge_detection_shape/edge_detection_shape.dart @@ -1,8 +1,6 @@ import 'dart:math'; -import 'dart:ui'; import 'package:flutter/material.dart'; -import 'package:paperless_document_scanner/paperless_document_scanner.dart'; import 'package:paperless_document_scanner/types/edge_detection_result.dart'; import 'edge_painter.dart'; diff --git a/packages/paperless_document_scanner/example/lib/edge_detection_shape/magnifier.dart b/packages/paperless_document_scanner/example/lib/edge_detection_shape/magnifier.dart index a7cc2a2..3550786 100644 --- a/packages/paperless_document_scanner/example/lib/edge_detection_shape/magnifier.dart +++ b/packages/paperless_document_scanner/example/lib/edge_detection_shape/magnifier.dart @@ -47,18 +47,11 @@ class _MagnifierState extends State { @override Widget build(BuildContext context) { return Stack( - children: [ - widget.child, - if (widget.visible && widget.position != null) _getMagnifier(context) - ], + children: [widget.child, if (widget.visible) _getMagnifier(context)], ); } void _calculateMatrix() { - if (widget.position == null) { - return; - } - setState(() { double newX = widget.position.dx - (_magnifierSize.width / 2 / _scale); double newY = widget.position.dy - (_magnifierSize.height / 2 / _scale); @@ -78,8 +71,7 @@ class _MagnifierState extends State { child: BackdropFilter( filter: ImageFilter.matrix(_matrix.storage), child: CustomPaint( - painter: MagnifierPainter( - color: Theme.of(context).colorScheme.secondary), + painter: MagnifierPainter(color: Theme.of(context).colorScheme.secondary), size: _magnifierSize, ), ), @@ -96,6 +88,5 @@ class _MagnifierState extends State { } bool _bubbleCrossesMagnifier() => - widget.position.dx < widget.size.width && - widget.position.dy < widget.size.height; + widget.position.dx < widget.size.width && widget.position.dy < widget.size.height; } diff --git a/packages/paperless_document_scanner/example/lib/edge_detection_shape/magnifier_painter.dart b/packages/paperless_document_scanner/example/lib/edge_detection_shape/magnifier_painter.dart index a0a8075..1c17492 100644 --- a/packages/paperless_document_scanner/example/lib/edge_detection_shape/magnifier_painter.dart +++ b/packages/paperless_document_scanner/example/lib/edge_detection_shape/magnifier_painter.dart @@ -19,7 +19,7 @@ class MagnifierPainter extends CustomPainter { ..color = color; canvas.drawCircle( - size.center(Offset(0, 0)), size.longestSide / 2, paintObject); + size.center(const Offset(0, 0)), size.longestSide / 2, paintObject); } void _drawCrosshair(Canvas canvas, Size size) { diff --git a/packages/paperless_document_scanner/example/lib/main.dart b/packages/paperless_document_scanner/example/lib/main.dart index 19f8648..1f514bc 100644 --- a/packages/paperless_document_scanner/example/lib/main.dart +++ b/packages/paperless_document_scanner/example/lib/main.dart @@ -1,10 +1,8 @@ -import 'dart:typed_data'; import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:image/image.dart' as imglib; -import 'scan.dart'; late final List cameras; void main() async { @@ -58,8 +56,8 @@ class _EdgeDetectionAppState extends State { final int uvRowStride = image.planes[1].bytesPerRow; final int uvPixelStride = image.planes[1].bytesPerPixel!; - print("uvRowStride: " + uvRowStride.toString()); - print("uvPixelStride: " + uvPixelStride.toString()); + print("uvRowStride: $uvRowStride"); + print("uvPixelStride: $uvPixelStride"); // imgLib -> Image package from https://pub.dartlang.org/packages/image var img = imglib.Image( @@ -89,7 +87,7 @@ class _EdgeDetectionAppState extends State { } } - imglib.PngEncoder pngEncoder = new imglib.PngEncoder(level: 0); + imglib.PngEncoder pngEncoder = imglib.PngEncoder(level: 0); final png = pngEncoder.encode(img); return Image.memory(png); } @@ -105,7 +103,7 @@ class _EdgeDetectionAppState extends State { body: Center( child: _image != null ? convertYUV420toImageColor(_image!) - : Placeholder(), + : const Placeholder(), ), ), ); diff --git a/packages/paperless_document_scanner/example/lib/scan.dart b/packages/paperless_document_scanner/example/lib/scan.dart index 0b2a419..cfacfb8 100644 --- a/packages/paperless_document_scanner/example/lib/scan.dart +++ b/packages/paperless_document_scanner/example/lib/scan.dart @@ -80,7 +80,7 @@ class _ScanState extends State { return Align( alignment: Alignment.bottomCenter, child: FloatingActionButton( - child: Icon(Icons.check), + child: const Icon(Icons.check), onPressed: () async { if (croppedImagePath == null) { return _processImage(imagePath!, edgeDetectionResult!); @@ -101,8 +101,8 @@ class _ScanState extends State { children: [ FloatingActionButton( foregroundColor: Colors.white, - child: Icon(Icons.camera_alt), onPressed: onTakePictureButtonPressed, + child: const Icon(Icons.camera_alt), ), ], ); @@ -167,7 +167,7 @@ class _ScanState extends State { Padding _getBottomBar() { return Padding( - padding: EdgeInsets.only(bottom: 32), + padding: const EdgeInsets.only(bottom: 32), child: Align(alignment: Alignment.bottomCenter, child: _getButtonRow()), ); } diff --git a/packages/paperless_document_scanner/example/pubspec.lock b/packages/paperless_document_scanner/example/pubspec.lock index 3f47cac..37fac4d 100644 --- a/packages/paperless_document_scanner/example/pubspec.lock +++ b/packages/paperless_document_scanner/example/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: archive - sha256: d6347d54a2d8028e0437e3c099f66fdb8ae02c4720c1e7534c9f24c10351f85d + sha256: "0c8368c9b3f0abbc193b9d6133649a614204b528982bebc7026372d61677ce3a" url: "https://pub.dev" source: hosted - version: "3.3.6" + version: "3.3.7" async: dependency: transitive description: name: async - sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.10.0" + version: "2.11.0" boolean_selector: dependency: transitive description: @@ -29,50 +29,50 @@ packages: dependency: "direct main" description: name: camera - sha256: e7ac55af24a890d20276821eb3c95857074d03b7de6f9892b99a205ee30bd179 + sha256: "309b823e61f15ff6b5b2e4c0ff2e1512ea661cad5355f71fc581e510ae5b26bb" url: "https://pub.dev" source: hosted - version: "0.10.3" + version: "0.10.5" camera_android: dependency: transitive description: name: camera_android - sha256: e491c836147f60dd8a54cf3895fd2960e13b21b78a9d15b563a1b6c70894f142 + sha256: e0f9b7eea2d1f4d4f5460f178522f0d02c095d2ae00b01a77419ce61c4184bfe url: "https://pub.dev" source: hosted - version: "0.10.4" + version: "0.10.7" camera_avfoundation: dependency: transitive description: name: camera_avfoundation - sha256: "6a68c20593d4cd58974d555f74a48b244f9db28cc9156de57781122d11b8754b" + sha256: "7ac8b950672716722af235eed7a7c37896853669800b7da706bb0a9fd41d3737" url: "https://pub.dev" source: hosted - version: "0.9.11" + version: "0.9.13+1" camera_platform_interface: dependency: transitive description: name: camera_platform_interface - sha256: b632be28e61d00a233f67d98ea90fd7041956f27a1c65500188ee459be60e15f + sha256: "525017018d116c5db8c4c43ec2d9b1663216b369c9f75149158280168a7ce472" url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.5.0" camera_web: dependency: transitive description: name: camera_web - sha256: "496de93c5d462738ce991dbfe91fb19026f115ed36406700a20a380fb0018299" + sha256: d77965f32479ee6d8f48205dcf10f845d7210595c6c00faa51eab265d1cae993 url: "https://pub.dev" source: hosted - version: "0.3.1+1" + version: "0.3.1+3" characters: dependency: transitive description: name: characters - sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" clock: dependency: transitive description: @@ -85,10 +85,10 @@ packages: dependency: transitive description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.17.1" convert: dependency: transitive description: @@ -109,10 +109,10 @@ packages: dependency: transitive description: name: crypto - sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67 + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.3" cupertino_icons: dependency: "direct main" description: @@ -133,10 +133,10 @@ packages: dependency: transitive description: name: ffi - sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978 + sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99 url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" file: dependency: transitive description: @@ -162,10 +162,10 @@ packages: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: "60fc7b78455b94e6de2333d2f95196d32cf5c22f4b0b0520a628804cb463503b" + sha256: "96af49aa6b57c10a312106ad6f71deed5a754029c24789bbf620ba784f0bd0b0" url: "https://pub.dev" source: hosted - version: "2.0.7" + version: "2.0.14" flutter_test: dependency: "direct dev" description: flutter @@ -180,34 +180,34 @@ packages: dependency: "direct main" description: name: image - sha256: "483a389d6ccb292b570c31b3a193779b1b0178e7eb571986d9a49904b6861227" + sha256: a72242c9a0ffb65d03de1b7113bc4e189686fc07c7147b8b41811d0dd0e0d9bf url: "https://pub.dev" source: hosted - version: "4.0.15" + version: "4.0.17" js: dependency: transitive description: name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.6.5" + version: "0.6.7" lints: dependency: transitive description: name: lints - sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + sha256: "6b0206b0bf4f04961fc5438198ccb3a885685cd67d4d4a32cc20ad7f8adbe015" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.0" matcher: dependency: transitive description: name: matcher - sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" + sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" url: "https://pub.dev" source: hosted - version: "0.12.13" + version: "0.12.15" material_color_utilities: dependency: transitive description: @@ -220,10 +220,10 @@ packages: dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.1" paperless_document_scanner: dependency: "direct main" description: @@ -235,66 +235,66 @@ packages: dependency: transitive description: name: path - sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" url: "https://pub.dev" source: hosted - version: "1.8.2" + version: "1.8.3" path_provider: dependency: "direct main" description: name: path_provider - sha256: dcea5feb97d8abf90cab9e9030b497fb7c3cbf26b7a1fe9e3ef7dcb0a1ddec95 + sha256: "3087813781ab814e4157b172f1a11c46be20179fcc9bea043e0fba36bc0acaa2" url: "https://pub.dev" source: hosted - version: "2.0.12" + version: "2.0.15" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: a776c088d671b27f6e3aa8881d64b87b3e80201c64e8869b811325de7a76c15e + sha256: "2cec049d282c7f13c594b4a73976b0b4f2d7a1838a6dd5aaf7bd9719196bee86" url: "https://pub.dev" source: hosted - version: "2.0.22" + version: "2.0.27" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "62a68e7e1c6c459f9289859e2fae58290c981ce21d1697faf54910fe1faa4c74" + sha256: "1995d88ec2948dac43edf8fe58eb434d35d22a2940ecee1a9fefcd62beee6eb3" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.2.3" path_provider_linux: dependency: transitive description: name: path_provider_linux - sha256: "2e32f1640f07caef0d3cb993680f181c79e54a3827b997d5ee221490d131fbd9" + sha256: "2ae08f2216225427e64ad224a24354221c2c7907e448e6e0e8b57b1eb9f10ad1" url: "https://pub.dev" source: hosted - version: "2.1.8" + version: "2.1.10" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - sha256: f0abc8ebd7253741f05488b4813d936b4d07c6bae3e86148a09e342ee4b08e76 + sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec" url: "https://pub.dev" source: hosted - version: "2.0.5" + version: "2.0.6" path_provider_windows: dependency: transitive description: name: path_provider_windows - sha256: bcabbe399d4042b8ee687e17548d5d3f527255253b4a639f5f8d2094a9c2b45c + sha256: d3f80b32e83ec208ac95253e0cd4d298e104fbc63cb29c5c69edaed43b0c69d6 url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.6" petitparser: dependency: transitive description: name: petitparser - sha256: "49392a45ced973e8d94a85fdb21293fbb40ba805fc49f2965101ae748a3683b4" + sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 url: "https://pub.dev" source: hosted - version: "5.1.0" + version: "5.4.0" platform: dependency: transitive description: @@ -307,18 +307,18 @@ packages: dependency: transitive description: name: plugin_platform_interface - sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a + sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.4" pointycastle: dependency: transitive description: name: pointycastle - sha256: db7306cf0249f838d1a24af52b5a5887c5bf7f31d8bb4e827d071dc0939ad346 + sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c" url: "https://pub.dev" source: hosted - version: "3.6.2" + version: "3.7.3" process: dependency: transitive description: @@ -392,18 +392,18 @@ packages: dependency: transitive description: name: test_api - sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 + sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb url: "https://pub.dev" source: hosted - version: "0.4.16" + version: "0.5.1" typed_data: dependency: transitive description: name: typed_data - sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.2" vector_math: dependency: transitive description: @@ -416,10 +416,10 @@ packages: dependency: transitive description: name: win32 - sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46 + sha256: "5a751eddf9db89b3e5f9d50c20ab8612296e4e8db69009788d6c8b060a84191c" url: "https://pub.dev" source: hosted - version: "3.1.3" + version: "4.1.4" xdg_directories: dependency: transitive description: @@ -432,10 +432,10 @@ packages: dependency: transitive description: name: xml - sha256: "979ee37d622dec6365e2efa4d906c37470995871fe9ae080d967e192d88286b5" + sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" url: "https://pub.dev" source: hosted - version: "6.2.2" + version: "6.3.0" sdks: - dart: ">=2.19.2 <3.0.0" - flutter: ">=3.0.0" + dart: ">=3.0.0-417 <4.0.0" + flutter: ">=3.3.0" From 891ab8985ba1b87016fce86f70500bfe8c6727f0 Mon Sep 17 00:00:00 2001 From: Anton Stubenbord Date: Fri, 12 May 2023 00:37:07 +0200 Subject: [PATCH 09/26] feat: Make account switching work again --- .../factory/paperless_api_factory_impl.dart | 2 +- .../view/pages/document_details_page.dart | 19 +-- lib/features/inbox/view/pages/inbox_page.dart | 5 +- .../login/cubit/authentication_cubit.dart | 4 +- .../cubit/authentication_cubit.freezed.dart | 141 ++++++++++++++++++ .../login/cubit/authentication_state.dart | 1 + lib/main.dart | 2 + 7 files changed, 162 insertions(+), 12 deletions(-) diff --git a/lib/core/factory/paperless_api_factory_impl.dart b/lib/core/factory/paperless_api_factory_impl.dart index 40b718f..fb66df5 100644 --- a/lib/core/factory/paperless_api_factory_impl.dart +++ b/lib/core/factory/paperless_api_factory_impl.dart @@ -42,7 +42,7 @@ class PaperlessApiFactoryImpl implements PaperlessApiFactory { PaperlessUserApi createUserApi(Dio dio, {required int apiVersion}) { if (apiVersion == 3) { return PaperlessUserApiV3Impl(dio); - } else if (apiVersion == 1 || apiVersion == 2) { + } else if (apiVersion < 3) { return PaperlessUserApiV2Impl(dio); } throw Exception("API $apiVersion not supported."); diff --git a/lib/features/document_details/view/pages/document_details_page.dart b/lib/features/document_details/view/pages/document_details_page.dart index e05af25..c9e4d04 100644 --- a/lib/features/document_details/view/pages/document_details_page.dart +++ b/lib/features/document_details/view/pages/document_details_page.dart @@ -233,15 +233,16 @@ class _DocumentDetailsPageState extends State { ), ], ), - CustomScrollView( - controller: _pagingScrollController, - slivers: [ - SliverOverlapInjector( - handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context), - ), - const DocumentPermissionsWidget(), - ], - ), + if (apiVersion.hasMultiUserSupport) + CustomScrollView( + controller: _pagingScrollController, + slivers: [ + SliverOverlapInjector( + handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context), + ), + const DocumentPermissionsWidget(), + ], + ), ], ), ), diff --git a/lib/features/inbox/view/pages/inbox_page.dart b/lib/features/inbox/view/pages/inbox_page.dart index aa6192a..6d12ec8 100644 --- a/lib/features/inbox/view/pages/inbox_page.dart +++ b/lib/features/inbox/view/pages/inbox_page.dart @@ -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/database/tables/local_user_account.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'; @@ -41,11 +42,13 @@ class _InboxPageState extends State with DocumentPagingViewMixin( builder: (context, state) { - if (!state.hasLoaded || state.documents.isEmpty) { + if (!state.hasLoaded || state.documents.isEmpty || !canEditDocument) { return const SizedBox.shrink(); } return FloatingActionButton.extended( diff --git a/lib/features/login/cubit/authentication_cubit.dart b/lib/features/login/cubit/authentication_cubit.dart index cdfb5d3..9fb94ed 100644 --- a/lib/features/login/cubit/authentication_cubit.dart +++ b/lib/features/login/cubit/authentication_cubit.dart @@ -66,6 +66,7 @@ class AuthenticationCubit extends Cubit { /// Switches to another account if it exists. Future switchAccount(String localUserId) async { + emit(const AuthenticationState.switchingAccounts()); final globalSettings = Hive.box(HiveBoxes.globalSettings).getValue()!; if (globalSettings.currentLoggedInUser == localUserId) { return; @@ -130,12 +131,13 @@ class AuthenticationCubit extends Cubit { }) async { assert(credentials.password != null && credentials.username != null); final localUserId = "${credentials.username}@$serverUrl"; + final sessionManager = SessionManager(); await _addUser( localUserId, serverUrl, credentials, clientCertificate, - _sessionManager, + sessionManager, ); return localUserId; diff --git a/lib/features/login/cubit/authentication_cubit.freezed.dart b/lib/features/login/cubit/authentication_cubit.freezed.dart index 629ef40..6dbc119 100644 --- a/lib/features/login/cubit/authentication_cubit.freezed.dart +++ b/lib/features/login/cubit/authentication_cubit.freezed.dart @@ -21,6 +21,7 @@ mixin _$AuthenticationState { required TResult Function() unauthenticated, required TResult Function() requriresLocalAuthentication, required TResult Function(String localUserId, int apiVersion) authenticated, + required TResult Function() switchingAccounts, }) => throw _privateConstructorUsedError; @optionalTypeArgs @@ -28,6 +29,7 @@ mixin _$AuthenticationState { TResult? Function()? unauthenticated, TResult? Function()? requriresLocalAuthentication, TResult? Function(String localUserId, int apiVersion)? authenticated, + TResult? Function()? switchingAccounts, }) => throw _privateConstructorUsedError; @optionalTypeArgs @@ -35,6 +37,7 @@ mixin _$AuthenticationState { TResult Function()? unauthenticated, TResult Function()? requriresLocalAuthentication, TResult Function(String localUserId, int apiVersion)? authenticated, + TResult Function()? switchingAccounts, required TResult orElse(), }) => throw _privateConstructorUsedError; @@ -44,6 +47,7 @@ mixin _$AuthenticationState { required TResult Function(_RequiresLocalAuthentication value) requriresLocalAuthentication, required TResult Function(_Authenticated value) authenticated, + required TResult Function(_SwitchingAccounts value) switchingAccounts, }) => throw _privateConstructorUsedError; @optionalTypeArgs @@ -52,6 +56,7 @@ mixin _$AuthenticationState { TResult? Function(_RequiresLocalAuthentication value)? requriresLocalAuthentication, TResult? Function(_Authenticated value)? authenticated, + TResult? Function(_SwitchingAccounts value)? switchingAccounts, }) => throw _privateConstructorUsedError; @optionalTypeArgs @@ -60,6 +65,7 @@ mixin _$AuthenticationState { TResult Function(_RequiresLocalAuthentication value)? requriresLocalAuthentication, TResult Function(_Authenticated value)? authenticated, + TResult Function(_SwitchingAccounts value)? switchingAccounts, required TResult orElse(), }) => throw _privateConstructorUsedError; @@ -124,6 +130,7 @@ class _$_Unauthenticated implements _Unauthenticated { required TResult Function() unauthenticated, required TResult Function() requriresLocalAuthentication, required TResult Function(String localUserId, int apiVersion) authenticated, + required TResult Function() switchingAccounts, }) { return unauthenticated(); } @@ -134,6 +141,7 @@ class _$_Unauthenticated implements _Unauthenticated { TResult? Function()? unauthenticated, TResult? Function()? requriresLocalAuthentication, TResult? Function(String localUserId, int apiVersion)? authenticated, + TResult? Function()? switchingAccounts, }) { return unauthenticated?.call(); } @@ -144,6 +152,7 @@ class _$_Unauthenticated implements _Unauthenticated { TResult Function()? unauthenticated, TResult Function()? requriresLocalAuthentication, TResult Function(String localUserId, int apiVersion)? authenticated, + TResult Function()? switchingAccounts, required TResult orElse(), }) { if (unauthenticated != null) { @@ -159,6 +168,7 @@ class _$_Unauthenticated implements _Unauthenticated { required TResult Function(_RequiresLocalAuthentication value) requriresLocalAuthentication, required TResult Function(_Authenticated value) authenticated, + required TResult Function(_SwitchingAccounts value) switchingAccounts, }) { return unauthenticated(this); } @@ -170,6 +180,7 @@ class _$_Unauthenticated implements _Unauthenticated { TResult? Function(_RequiresLocalAuthentication value)? requriresLocalAuthentication, TResult? Function(_Authenticated value)? authenticated, + TResult? Function(_SwitchingAccounts value)? switchingAccounts, }) { return unauthenticated?.call(this); } @@ -181,6 +192,7 @@ class _$_Unauthenticated implements _Unauthenticated { TResult Function(_RequiresLocalAuthentication value)? requriresLocalAuthentication, TResult Function(_Authenticated value)? authenticated, + TResult Function(_SwitchingAccounts value)? switchingAccounts, required TResult orElse(), }) { if (unauthenticated != null) { @@ -239,6 +251,7 @@ class _$_RequiresLocalAuthentication implements _RequiresLocalAuthentication { required TResult Function() unauthenticated, required TResult Function() requriresLocalAuthentication, required TResult Function(String localUserId, int apiVersion) authenticated, + required TResult Function() switchingAccounts, }) { return requriresLocalAuthentication(); } @@ -249,6 +262,7 @@ class _$_RequiresLocalAuthentication implements _RequiresLocalAuthentication { TResult? Function()? unauthenticated, TResult? Function()? requriresLocalAuthentication, TResult? Function(String localUserId, int apiVersion)? authenticated, + TResult? Function()? switchingAccounts, }) { return requriresLocalAuthentication?.call(); } @@ -259,6 +273,7 @@ class _$_RequiresLocalAuthentication implements _RequiresLocalAuthentication { TResult Function()? unauthenticated, TResult Function()? requriresLocalAuthentication, TResult Function(String localUserId, int apiVersion)? authenticated, + TResult Function()? switchingAccounts, required TResult orElse(), }) { if (requriresLocalAuthentication != null) { @@ -274,6 +289,7 @@ class _$_RequiresLocalAuthentication implements _RequiresLocalAuthentication { required TResult Function(_RequiresLocalAuthentication value) requriresLocalAuthentication, required TResult Function(_Authenticated value) authenticated, + required TResult Function(_SwitchingAccounts value) switchingAccounts, }) { return requriresLocalAuthentication(this); } @@ -285,6 +301,7 @@ class _$_RequiresLocalAuthentication implements _RequiresLocalAuthentication { TResult? Function(_RequiresLocalAuthentication value)? requriresLocalAuthentication, TResult? Function(_Authenticated value)? authenticated, + TResult? Function(_SwitchingAccounts value)? switchingAccounts, }) { return requriresLocalAuthentication?.call(this); } @@ -296,6 +313,7 @@ class _$_RequiresLocalAuthentication implements _RequiresLocalAuthentication { TResult Function(_RequiresLocalAuthentication value)? requriresLocalAuthentication, TResult Function(_Authenticated value)? authenticated, + TResult Function(_SwitchingAccounts value)? switchingAccounts, required TResult orElse(), }) { if (requriresLocalAuthentication != null) { @@ -386,6 +404,7 @@ class _$_Authenticated implements _Authenticated { required TResult Function() unauthenticated, required TResult Function() requriresLocalAuthentication, required TResult Function(String localUserId, int apiVersion) authenticated, + required TResult Function() switchingAccounts, }) { return authenticated(localUserId, apiVersion); } @@ -396,6 +415,7 @@ class _$_Authenticated implements _Authenticated { TResult? Function()? unauthenticated, TResult? Function()? requriresLocalAuthentication, TResult? Function(String localUserId, int apiVersion)? authenticated, + TResult? Function()? switchingAccounts, }) { return authenticated?.call(localUserId, apiVersion); } @@ -406,6 +426,7 @@ class _$_Authenticated implements _Authenticated { TResult Function()? unauthenticated, TResult Function()? requriresLocalAuthentication, TResult Function(String localUserId, int apiVersion)? authenticated, + TResult Function()? switchingAccounts, required TResult orElse(), }) { if (authenticated != null) { @@ -421,6 +442,7 @@ class _$_Authenticated implements _Authenticated { required TResult Function(_RequiresLocalAuthentication value) requriresLocalAuthentication, required TResult Function(_Authenticated value) authenticated, + required TResult Function(_SwitchingAccounts value) switchingAccounts, }) { return authenticated(this); } @@ -432,6 +454,7 @@ class _$_Authenticated implements _Authenticated { TResult? Function(_RequiresLocalAuthentication value)? requriresLocalAuthentication, TResult? Function(_Authenticated value)? authenticated, + TResult? Function(_SwitchingAccounts value)? switchingAccounts, }) { return authenticated?.call(this); } @@ -443,6 +466,7 @@ class _$_Authenticated implements _Authenticated { TResult Function(_RequiresLocalAuthentication value)? requriresLocalAuthentication, TResult Function(_Authenticated value)? authenticated, + TResult Function(_SwitchingAccounts value)? switchingAccounts, required TResult orElse(), }) { if (authenticated != null) { @@ -463,3 +487,120 @@ abstract class _Authenticated implements AuthenticationState { _$$_AuthenticatedCopyWith<_$_Authenticated> get copyWith => throw _privateConstructorUsedError; } + +/// @nodoc +abstract class _$$_SwitchingAccountsCopyWith<$Res> { + factory _$$_SwitchingAccountsCopyWith(_$_SwitchingAccounts value, + $Res Function(_$_SwitchingAccounts) then) = + __$$_SwitchingAccountsCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$_SwitchingAccountsCopyWithImpl<$Res> + extends _$AuthenticationStateCopyWithImpl<$Res, _$_SwitchingAccounts> + implements _$$_SwitchingAccountsCopyWith<$Res> { + __$$_SwitchingAccountsCopyWithImpl( + _$_SwitchingAccounts _value, $Res Function(_$_SwitchingAccounts) _then) + : super(_value, _then); +} + +/// @nodoc + +class _$_SwitchingAccounts implements _SwitchingAccounts { + const _$_SwitchingAccounts(); + + @override + String toString() { + return 'AuthenticationState.switchingAccounts()'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$_SwitchingAccounts); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() unauthenticated, + required TResult Function() requriresLocalAuthentication, + required TResult Function(String localUserId, int apiVersion) authenticated, + required TResult Function() switchingAccounts, + }) { + return switchingAccounts(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? unauthenticated, + TResult? Function()? requriresLocalAuthentication, + TResult? Function(String localUserId, int apiVersion)? authenticated, + TResult? Function()? switchingAccounts, + }) { + return switchingAccounts?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? unauthenticated, + TResult Function()? requriresLocalAuthentication, + TResult Function(String localUserId, int apiVersion)? authenticated, + TResult Function()? switchingAccounts, + required TResult orElse(), + }) { + if (switchingAccounts != null) { + return switchingAccounts(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_Unauthenticated value) unauthenticated, + required TResult Function(_RequiresLocalAuthentication value) + requriresLocalAuthentication, + required TResult Function(_Authenticated value) authenticated, + required TResult Function(_SwitchingAccounts value) switchingAccounts, + }) { + return switchingAccounts(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_Unauthenticated value)? unauthenticated, + TResult? Function(_RequiresLocalAuthentication value)? + requriresLocalAuthentication, + TResult? Function(_Authenticated value)? authenticated, + TResult? Function(_SwitchingAccounts value)? switchingAccounts, + }) { + return switchingAccounts?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_Unauthenticated value)? unauthenticated, + TResult Function(_RequiresLocalAuthentication value)? + requriresLocalAuthentication, + TResult Function(_Authenticated value)? authenticated, + TResult Function(_SwitchingAccounts value)? switchingAccounts, + required TResult orElse(), + }) { + if (switchingAccounts != null) { + return switchingAccounts(this); + } + return orElse(); + } +} + +abstract class _SwitchingAccounts implements AuthenticationState { + const factory _SwitchingAccounts() = _$_SwitchingAccounts; +} diff --git a/lib/features/login/cubit/authentication_state.dart b/lib/features/login/cubit/authentication_state.dart index 21c886f..2d91649 100644 --- a/lib/features/login/cubit/authentication_state.dart +++ b/lib/features/login/cubit/authentication_state.dart @@ -8,4 +8,5 @@ class AuthenticationState with _$AuthenticationState { required String localUserId, required int apiVersion, }) = _Authenticated; + const factory AuthenticationState.switchingAccounts() = _SwitchingAccounts; } diff --git a/lib/main.dart b/lib/main.dart index 69b10a2..f20fbd5 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -38,6 +38,7 @@ import 'package:paperless_mobile/features/login/model/login_form_credentials.dar import 'package:paperless_mobile/features/login/services/authentication_service.dart'; import 'package:paperless_mobile/features/login/view/login_page.dart'; import 'package:paperless_mobile/features/notifications/services/local_notification_service.dart'; +import 'package:paperless_mobile/features/settings/view/pages/switching_accounts_page.dart'; import 'package:paperless_mobile/features/settings/view/widgets/global_settings_builder.dart'; import 'package:paperless_mobile/features/sharing/share_intent_queue.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; @@ -270,6 +271,7 @@ class _AuthenticationWrapperState extends State { paperlessProviderFactory: widget.paperlessProviderFactory, localUserId: localUserId, ), + switchingAccounts: () => const SwitchingAccountsPage(), ); }, ); From 96c4b37f5fc7918b3b5fa71f5cd7b0cd0ab5650e Mon Sep 17 00:00:00 2001 From: Anton Stubenbord Date: Fri, 12 May 2023 00:40:10 +0200 Subject: [PATCH 10/26] fix: Small fix related to account switching --- lib/features/settings/view/manage_accounts_page.dart | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/lib/features/settings/view/manage_accounts_page.dart b/lib/features/settings/view/manage_accounts_page.dart index 2ab1f96..d4fdac0 100644 --- a/lib/features/settings/view/manage_accounts_page.dart +++ b/lib/features/settings/view/manage_accounts_page.dart @@ -203,15 +203,8 @@ class ManageAccountsPage extends StatelessWidget { } _onSwitchAccount(BuildContext context, String currentUser, String newUser) async { - final navigator = Navigator.of(context); if (currentUser == newUser) return; - Navigator.pushReplacement( - context, - MaterialPageRoute( - builder: (context) => const SwitchingAccountsPage(), - ), - ); - await context.read().switchAccount(newUser); - navigator.popUntil((route) => route.isFirst); + Navigator.of(context).pop(); + context.read().switchAccount(newUser); } } From 0231f28f3f8be9383517fb88ab2a50e2f3d56638 Mon Sep 17 00:00:00 2001 From: Anton Stubenbord Date: Fri, 12 May 2023 00:42:31 +0200 Subject: [PATCH 11/26] feat: bump version number, temporarily disable in-app viewer --- .../document_details/view/pages/document_details_page.dart | 3 ++- pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/features/document_details/view/pages/document_details_page.dart b/lib/features/document_details/view/pages/document_details_page.dart index c9e4d04..826a08e 100644 --- a/lib/features/document_details/view/pages/document_details_page.dart +++ b/lib/features/document_details/view/pages/document_details_page.dart @@ -304,10 +304,11 @@ class _DocumentDetailsPageState extends State { document: state.document, enabled: isConnected, ), + //TODO: Enable again, need new pdf viewer package... IconButton( tooltip: S.of(context)!.previewTooltip, icon: const Icon(Icons.visibility), - onPressed: isConnected ? () => _onOpen(state.document) : null, + onPressed: (isConnected && false) ? () => _onOpen(state.document) : null, ).paddedOnly(right: 4.0), IconButton( tooltip: S.of(context)!.openInSystemViewer, diff --git a/pubspec.yaml b/pubspec.yaml index cba1680..815d542 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,7 +17,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 2.2.4+32 +version: 2.2.5+33 environment: sdk: ">=2.19.0 <4.0.0" From 314fc51e5c336ed94da2cf3234bff0f0e9e9fac1 Mon Sep 17 00:00:00 2001 From: Anton Stubenbord Date: Fri, 12 May 2023 00:53:10 +0200 Subject: [PATCH 12/26] feat: Add changelogs --- android/fastlane/metadata/android/de-DE/changelogs/33.txt | 4 ++++ android/fastlane/metadata/android/en-US/changelogs/33.txt | 4 ++++ 2 files changed, 8 insertions(+) create mode 100644 android/fastlane/metadata/android/de-DE/changelogs/33.txt create mode 100644 android/fastlane/metadata/android/en-US/changelogs/33.txt diff --git a/android/fastlane/metadata/android/de-DE/changelogs/33.txt b/android/fastlane/metadata/android/de-DE/changelogs/33.txt new file mode 100644 index 0000000..0ecf050 --- /dev/null +++ b/android/fastlane/metadata/android/de-DE/changelogs/33.txt @@ -0,0 +1,4 @@ +Dies ist eine Beta Version und noch work in progress! Diese Version wurde mit paperless-ngx 1.13.0 sowie 1.14.2 getestet. +* Beheben einiger Fehler, die durch API-Update zustande kamen +* Berechtigungs-Checks bei wichtigen Funktionen (noch nicht vollständig) +* Neue Einstellungen für den Standard Dateityp beim Downloaden/Teilen \ No newline at end of file diff --git a/android/fastlane/metadata/android/en-US/changelogs/33.txt b/android/fastlane/metadata/android/en-US/changelogs/33.txt new file mode 100644 index 0000000..e0b2e47 --- /dev/null +++ b/android/fastlane/metadata/android/en-US/changelogs/33.txt @@ -0,0 +1,4 @@ +This is a beta version and still work in progress! This version was tested with paperless-ngx 1.3.0 as well as 1.14.2. +* Fixes some bugs which were caused by breaking changes to the Paperless API. +* Permission checks for major features (upload, edit, view documents etc., not complete yet) +* Add setting to set default download/share file type \ No newline at end of file From e7b095e6901155fc85d4d939568ee015854e5b63 Mon Sep 17 00:00:00 2001 From: Anton Stubenbord Date: Fri, 12 May 2023 10:37:47 +0200 Subject: [PATCH 13/26] chore: Update versions, add freezed files to gitignore --- .gitignore | 4 ++++ packages/paperless_api/pubspec.yaml | 8 ++++---- pubspec.lock | 14 +++++++------- pubspec.yaml | 4 ++-- scripts/install_dependencies.sh | 4 ++-- 5 files changed, 19 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index baefb01..e3f4e8f 100644 --- a/.gitignore +++ b/.gitignore @@ -54,7 +54,11 @@ app.*.map.json # build_runner generated files lib/**/*.g.dart +lib/**/*.freezed.dart packages/**/*.g.dart +packages/**/*.freezed.dart + + # mockito generated files *.mocks.dart diff --git a/packages/paperless_api/pubspec.yaml b/packages/paperless_api/pubspec.yaml index d1ba2c6..99afca5 100644 --- a/packages/paperless_api/pubspec.yaml +++ b/packages/paperless_api/pubspec.yaml @@ -7,7 +7,7 @@ version: 1.0.0+1 publish_to: "none" # Remove this line if you wish to publish to pub.dev environment: - sdk: ">=2.18.5 <3.0.0" + sdk: ">=3.0.0 <4.0.0" flutter: ">=1.17.0" dependencies: @@ -15,7 +15,7 @@ dependencies: sdk: flutter equatable: ^2.0.5 http: ^0.13.5 - json_annotation: ^4.7.0 + json_annotation: ^4.8.1 intl: any #^0.18.0 dio: ^5.0.0 collection: ^1.17.0 @@ -28,8 +28,8 @@ dev_dependencies: sdk: flutter flutter_lints: ^2.0.0 json_serializable: ^6.5.4 - build_runner: ^2.3.2 - freezed: ^2.3.2 + build_runner: ^2.4.2 + freezed: ^2.3.3 hive_generator: ^2.0.0 # For information on the generic Dart part of this file, see the diff --git a/pubspec.lock b/pubspec.lock index 3243aad..61ed198 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -149,10 +149,10 @@ packages: dependency: transitive description: name: build_daemon - sha256: "6bc5544ea6ce4428266e7ea680e945c68806c4aae2da0eb5e9ccf38df8d6acbf" + sha256: "5f02d73eb2ba16483e693f80bee4f088563a820e47d1027d4cdfe62b5bb43e65" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "4.0.0" build_resolvers: dependency: transitive description: @@ -165,10 +165,10 @@ packages: dependency: "direct dev" description: name: build_runner - sha256: b0a8a7b8a76c493e85f1b84bffa0588859a06197863dba8c9036b15581fd9727 + sha256: "87e06c939450b9b94e3e1bb2d46e0e9780adbff5500d3969f2ba2de6bbb860cb" url: "https://pub.dev" source: hosted - version: "2.3.3" + version: "2.4.2" build_runner_core: dependency: transitive description: @@ -719,10 +719,10 @@ packages: dependency: "direct dev" description: name: freezed - sha256: e819441678f1679b719008ff2ff0ef045d66eed9f9ec81166ca0d9b02a187454 + sha256: "73b58fe836dc05594451d8f740d97d5167886962d628b9f60a1fe945aa0a891f" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.3.3" freezed_annotation: dependency: "direct main" description: @@ -1738,5 +1738,5 @@ packages: source: hosted version: "3.1.1" sdks: - dart: ">=3.0.0-0 <4.0.0" + dart: ">=3.0.0-134.0.dev <4.0.0" flutter: ">=3.7.0" diff --git a/pubspec.yaml b/pubspec.yaml index 815d542..8be6a2e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -101,7 +101,7 @@ dev_dependencies: sdk: flutter flutter_test: sdk: flutter - build_runner: ^2.1.11 + build_runner: ^2.4.2 mockito: ^5.3.2 bloc_test: ^9.1.0 dependency_validator: ^3.0.0 @@ -109,7 +109,7 @@ dev_dependencies: json_serializable: ^6.5.4 dart_code_metrics: ^5.4.0 auto_route_generator: ^5.0.3 - freezed: ^2.3.2 + freezed: ^2.3.3 hive_generator: ^2.0.0 # For information on the generic Dart part of this file, see the diff --git a/scripts/install_dependencies.sh b/scripts/install_dependencies.sh index 1558a61..a01ac8c 100755 --- a/scripts/install_dependencies.sh +++ b/scripts/install_dependencies.sh @@ -2,8 +2,8 @@ pushd ../ pushd packages/paperless_api flutter pub get -flutter pub run build_runner build --delete-conflicting-outputs +dart run build_runner build --delete-conflicting-outputs popd flutter packages pub get flutter gen-l10n -flutter pub run build_runner build --delete-conflicting-outputs +dart run build_runner build --delete-conflicting-outputs From 71447b4dcc4a0a213638c476d3c085c7e36311f0 Mon Sep 17 00:00:00 2001 From: Anton Stubenbord Date: Fri, 12 May 2023 10:50:49 +0200 Subject: [PATCH 14/26] chore: update version constraints --- pubspec.lock | 6 +++--- pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 61ed198..149c0a6 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -881,10 +881,10 @@ packages: dependency: "direct main" description: name: json_annotation - sha256: c33da08e136c3df0190bd5bbe51ae1df4a7d96e7954d1d7249fea2968a72d317 + sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 url: "https://pub.dev" source: hosted - version: "4.8.0" + version: "4.8.1" json_serializable: dependency: "direct dev" description: @@ -1738,5 +1738,5 @@ packages: source: hosted version: "3.1.1" sdks: - dart: ">=3.0.0-134.0.dev <4.0.0" + dart: ">=3.0.0 <4.0.0" flutter: ">=3.7.0" diff --git a/pubspec.yaml b/pubspec.yaml index 8be6a2e..f4b76ab 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -78,7 +78,7 @@ dependencies: provider: ^6.0.5 dio: ^5.0.0 hydrated_bloc: ^9.0.0 - json_annotation: ^4.7.0 + json_annotation: ^4.8.1 pretty_dio_logger: ^1.2.0-beta-1 collection: ^1.17.0 device_info_plus: ^8.0.0 From a4a7838810a9413a1e1800b91ada0e7f0512c60b Mon Sep 17 00:00:00 2001 From: Anton Stubenbord Date: Fri, 12 May 2023 10:53:18 +0200 Subject: [PATCH 15/26] feat: Update tags query file --- .../lib/src/models/query_parameters/tags_query/tags_query.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/paperless_api/lib/src/models/query_parameters/tags_query/tags_query.dart b/packages/paperless_api/lib/src/models/query_parameters/tags_query/tags_query.dart index f099539..4602204 100644 --- a/packages/paperless_api/lib/src/models/query_parameters/tags_query/tags_query.dart +++ b/packages/paperless_api/lib/src/models/query_parameters/tags_query/tags_query.dart @@ -9,12 +9,10 @@ class TagsQuery with _$TagsQuery { const TagsQuery._(); @HiveType(typeId: PaperlessApiHiveTypeIds.notAssignedTagsQuery) const factory TagsQuery.notAssigned() = NotAssignedTagsQuery; - @HiveType(typeId: PaperlessApiHiveTypeIds.anyAssignedTagsQuery) const factory TagsQuery.anyAssigned({ @Default([]) List tagIds, }) = AnyAssignedTagsQuery; - @HiveType(typeId: PaperlessApiHiveTypeIds.idsTagsQuery) const factory TagsQuery.ids({ @Default([]) List include, From f913ab318fb471a6c066bd3c1340a88dab98a70c Mon Sep 17 00:00:00 2001 From: Anton Stubenbord Date: Fri, 12 May 2023 10:54:24 +0200 Subject: [PATCH 16/26] chore: Remove generated files (freezed) --- .../label_repository_state.freezed.dart | 258 ------ .../saved_view_repository_state.freezed.dart | 167 ---- .../repository/user_repository.freezed.dart | 161 ---- .../document_bulk_action_cubit.freezed.dart | 269 ------ .../cubit/document_details_cubit.freezed.dart | 350 -------- .../cubit/document_edit_cubit.freezed.dart | 260 ------ .../cubit/edit_label_cubit.freezed.dart | 237 ----- .../labels/cubit/label_cubit.freezed.dart | 237 ----- .../cubit/authentication_cubit.freezed.dart | 606 ------------- .../cubit/saved_view_cubit.freezed.dart | 585 ------------ .../lib/src/models/group_model.freezed.dart | 212 ----- .../permissions/permissions.freezed.dart | 195 ---- .../id_query_parameter.freezed.dart | 686 -------------- .../tags_query/tags_query.freezed.dart | 587 ------------ .../lib/src/models/user_model.freezed.dart | 844 ------------------ 15 files changed, 5654 deletions(-) delete mode 100644 lib/core/repository/label_repository_state.freezed.dart delete mode 100644 lib/core/repository/saved_view_repository_state.freezed.dart delete mode 100644 lib/core/repository/user_repository.freezed.dart delete mode 100644 lib/features/document_bulk_action/cubit/document_bulk_action_cubit.freezed.dart delete mode 100644 lib/features/document_details/cubit/document_details_cubit.freezed.dart delete mode 100644 lib/features/document_edit/cubit/document_edit_cubit.freezed.dart delete mode 100644 lib/features/edit_label/cubit/edit_label_cubit.freezed.dart delete mode 100644 lib/features/labels/cubit/label_cubit.freezed.dart delete mode 100644 lib/features/login/cubit/authentication_cubit.freezed.dart delete mode 100644 lib/features/saved_view/cubit/saved_view_cubit.freezed.dart delete mode 100644 packages/paperless_api/lib/src/models/group_model.freezed.dart delete mode 100644 packages/paperless_api/lib/src/models/permissions/permissions.freezed.dart delete mode 100644 packages/paperless_api/lib/src/models/query_parameters/id_query_parameter.freezed.dart delete mode 100644 packages/paperless_api/lib/src/models/query_parameters/tags_query/tags_query.freezed.dart delete mode 100644 packages/paperless_api/lib/src/models/user_model.freezed.dart diff --git a/lib/core/repository/label_repository_state.freezed.dart b/lib/core/repository/label_repository_state.freezed.dart deleted file mode 100644 index faf36ef..0000000 --- a/lib/core/repository/label_repository_state.freezed.dart +++ /dev/null @@ -1,258 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'label_repository_state.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); - -LabelRepositoryState _$LabelRepositoryStateFromJson(Map json) { - return _LabelRepositoryState.fromJson(json); -} - -/// @nodoc -mixin _$LabelRepositoryState { - Map get correspondents => - throw _privateConstructorUsedError; - Map get documentTypes => - throw _privateConstructorUsedError; - Map get tags => throw _privateConstructorUsedError; - Map get storagePaths => throw _privateConstructorUsedError; - - Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) - $LabelRepositoryStateCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $LabelRepositoryStateCopyWith<$Res> { - factory $LabelRepositoryStateCopyWith(LabelRepositoryState value, - $Res Function(LabelRepositoryState) then) = - _$LabelRepositoryStateCopyWithImpl<$Res, LabelRepositoryState>; - @useResult - $Res call( - {Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths}); -} - -/// @nodoc -class _$LabelRepositoryStateCopyWithImpl<$Res, - $Val extends LabelRepositoryState> - implements $LabelRepositoryStateCopyWith<$Res> { - _$LabelRepositoryStateCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? correspondents = null, - Object? documentTypes = null, - Object? tags = null, - Object? storagePaths = null, - }) { - return _then(_value.copyWith( - correspondents: null == correspondents - ? _value.correspondents - : correspondents // ignore: cast_nullable_to_non_nullable - as Map, - documentTypes: null == documentTypes - ? _value.documentTypes - : documentTypes // ignore: cast_nullable_to_non_nullable - as Map, - tags: null == tags - ? _value.tags - : tags // ignore: cast_nullable_to_non_nullable - as Map, - storagePaths: null == storagePaths - ? _value.storagePaths - : storagePaths // ignore: cast_nullable_to_non_nullable - as Map, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$_LabelRepositoryStateCopyWith<$Res> - implements $LabelRepositoryStateCopyWith<$Res> { - factory _$$_LabelRepositoryStateCopyWith(_$_LabelRepositoryState value, - $Res Function(_$_LabelRepositoryState) then) = - __$$_LabelRepositoryStateCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths}); -} - -/// @nodoc -class __$$_LabelRepositoryStateCopyWithImpl<$Res> - extends _$LabelRepositoryStateCopyWithImpl<$Res, _$_LabelRepositoryState> - implements _$$_LabelRepositoryStateCopyWith<$Res> { - __$$_LabelRepositoryStateCopyWithImpl(_$_LabelRepositoryState _value, - $Res Function(_$_LabelRepositoryState) _then) - : super(_value, _then); - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? correspondents = null, - Object? documentTypes = null, - Object? tags = null, - Object? storagePaths = null, - }) { - return _then(_$_LabelRepositoryState( - correspondents: null == correspondents - ? _value._correspondents - : correspondents // ignore: cast_nullable_to_non_nullable - as Map, - documentTypes: null == documentTypes - ? _value._documentTypes - : documentTypes // ignore: cast_nullable_to_non_nullable - as Map, - tags: null == tags - ? _value._tags - : tags // ignore: cast_nullable_to_non_nullable - as Map, - storagePaths: null == storagePaths - ? _value._storagePaths - : storagePaths // ignore: cast_nullable_to_non_nullable - as Map, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$_LabelRepositoryState implements _LabelRepositoryState { - const _$_LabelRepositoryState( - {final Map correspondents = const {}, - final Map documentTypes = const {}, - final Map tags = const {}, - final Map storagePaths = const {}}) - : _correspondents = correspondents, - _documentTypes = documentTypes, - _tags = tags, - _storagePaths = storagePaths; - - factory _$_LabelRepositoryState.fromJson(Map json) => - _$$_LabelRepositoryStateFromJson(json); - - final Map _correspondents; - @override - @JsonKey() - Map get correspondents { - if (_correspondents is EqualUnmodifiableMapView) return _correspondents; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_correspondents); - } - - final Map _documentTypes; - @override - @JsonKey() - Map get documentTypes { - if (_documentTypes is EqualUnmodifiableMapView) return _documentTypes; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_documentTypes); - } - - final Map _tags; - @override - @JsonKey() - Map get tags { - if (_tags is EqualUnmodifiableMapView) return _tags; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_tags); - } - - final Map _storagePaths; - @override - @JsonKey() - Map get storagePaths { - if (_storagePaths is EqualUnmodifiableMapView) return _storagePaths; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_storagePaths); - } - - @override - String toString() { - return 'LabelRepositoryState(correspondents: $correspondents, documentTypes: $documentTypes, tags: $tags, storagePaths: $storagePaths)'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$_LabelRepositoryState && - const DeepCollectionEquality() - .equals(other._correspondents, _correspondents) && - const DeepCollectionEquality() - .equals(other._documentTypes, _documentTypes) && - const DeepCollectionEquality().equals(other._tags, _tags) && - const DeepCollectionEquality() - .equals(other._storagePaths, _storagePaths)); - } - - @JsonKey(ignore: true) - @override - int get hashCode => Object.hash( - runtimeType, - const DeepCollectionEquality().hash(_correspondents), - const DeepCollectionEquality().hash(_documentTypes), - const DeepCollectionEquality().hash(_tags), - const DeepCollectionEquality().hash(_storagePaths)); - - @JsonKey(ignore: true) - @override - @pragma('vm:prefer-inline') - _$$_LabelRepositoryStateCopyWith<_$_LabelRepositoryState> get copyWith => - __$$_LabelRepositoryStateCopyWithImpl<_$_LabelRepositoryState>( - this, _$identity); - - @override - Map toJson() { - return _$$_LabelRepositoryStateToJson( - this, - ); - } -} - -abstract class _LabelRepositoryState implements LabelRepositoryState { - const factory _LabelRepositoryState( - {final Map correspondents, - final Map documentTypes, - final Map tags, - final Map storagePaths}) = _$_LabelRepositoryState; - - factory _LabelRepositoryState.fromJson(Map json) = - _$_LabelRepositoryState.fromJson; - - @override - Map get correspondents; - @override - Map get documentTypes; - @override - Map get tags; - @override - Map get storagePaths; - @override - @JsonKey(ignore: true) - _$$_LabelRepositoryStateCopyWith<_$_LabelRepositoryState> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/lib/core/repository/saved_view_repository_state.freezed.dart b/lib/core/repository/saved_view_repository_state.freezed.dart deleted file mode 100644 index cec7882..0000000 --- a/lib/core/repository/saved_view_repository_state.freezed.dart +++ /dev/null @@ -1,167 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'saved_view_repository_state.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); - -SavedViewRepositoryState _$SavedViewRepositoryStateFromJson( - Map json) { - return _SavedViewRepositoryState.fromJson(json); -} - -/// @nodoc -mixin _$SavedViewRepositoryState { - Map get savedViews => throw _privateConstructorUsedError; - - Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) - $SavedViewRepositoryStateCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $SavedViewRepositoryStateCopyWith<$Res> { - factory $SavedViewRepositoryStateCopyWith(SavedViewRepositoryState value, - $Res Function(SavedViewRepositoryState) then) = - _$SavedViewRepositoryStateCopyWithImpl<$Res, SavedViewRepositoryState>; - @useResult - $Res call({Map savedViews}); -} - -/// @nodoc -class _$SavedViewRepositoryStateCopyWithImpl<$Res, - $Val extends SavedViewRepositoryState> - implements $SavedViewRepositoryStateCopyWith<$Res> { - _$SavedViewRepositoryStateCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? savedViews = null, - }) { - return _then(_value.copyWith( - savedViews: null == savedViews - ? _value.savedViews - : savedViews // ignore: cast_nullable_to_non_nullable - as Map, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$_SavedViewRepositoryStateCopyWith<$Res> - implements $SavedViewRepositoryStateCopyWith<$Res> { - factory _$$_SavedViewRepositoryStateCopyWith( - _$_SavedViewRepositoryState value, - $Res Function(_$_SavedViewRepositoryState) then) = - __$$_SavedViewRepositoryStateCopyWithImpl<$Res>; - @override - @useResult - $Res call({Map savedViews}); -} - -/// @nodoc -class __$$_SavedViewRepositoryStateCopyWithImpl<$Res> - extends _$SavedViewRepositoryStateCopyWithImpl<$Res, - _$_SavedViewRepositoryState> - implements _$$_SavedViewRepositoryStateCopyWith<$Res> { - __$$_SavedViewRepositoryStateCopyWithImpl(_$_SavedViewRepositoryState _value, - $Res Function(_$_SavedViewRepositoryState) _then) - : super(_value, _then); - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? savedViews = null, - }) { - return _then(_$_SavedViewRepositoryState( - savedViews: null == savedViews - ? _value._savedViews - : savedViews // ignore: cast_nullable_to_non_nullable - as Map, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$_SavedViewRepositoryState implements _SavedViewRepositoryState { - const _$_SavedViewRepositoryState( - {final Map savedViews = const {}}) - : _savedViews = savedViews; - - factory _$_SavedViewRepositoryState.fromJson(Map json) => - _$$_SavedViewRepositoryStateFromJson(json); - - final Map _savedViews; - @override - @JsonKey() - Map get savedViews { - if (_savedViews is EqualUnmodifiableMapView) return _savedViews; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_savedViews); - } - - @override - String toString() { - return 'SavedViewRepositoryState(savedViews: $savedViews)'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$_SavedViewRepositoryState && - const DeepCollectionEquality() - .equals(other._savedViews, _savedViews)); - } - - @JsonKey(ignore: true) - @override - int get hashCode => Object.hash( - runtimeType, const DeepCollectionEquality().hash(_savedViews)); - - @JsonKey(ignore: true) - @override - @pragma('vm:prefer-inline') - _$$_SavedViewRepositoryStateCopyWith<_$_SavedViewRepositoryState> - get copyWith => __$$_SavedViewRepositoryStateCopyWithImpl< - _$_SavedViewRepositoryState>(this, _$identity); - - @override - Map toJson() { - return _$$_SavedViewRepositoryStateToJson( - this, - ); - } -} - -abstract class _SavedViewRepositoryState implements SavedViewRepositoryState { - const factory _SavedViewRepositoryState( - {final Map savedViews}) = _$_SavedViewRepositoryState; - - factory _SavedViewRepositoryState.fromJson(Map json) = - _$_SavedViewRepositoryState.fromJson; - - @override - Map get savedViews; - @override - @JsonKey(ignore: true) - _$$_SavedViewRepositoryStateCopyWith<_$_SavedViewRepositoryState> - get copyWith => throw _privateConstructorUsedError; -} diff --git a/lib/core/repository/user_repository.freezed.dart b/lib/core/repository/user_repository.freezed.dart deleted file mode 100644 index e8b8b71..0000000 --- a/lib/core/repository/user_repository.freezed.dart +++ /dev/null @@ -1,161 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'user_repository.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); - -UserRepositoryState _$UserRepositoryStateFromJson(Map json) { - return _UserRepositoryState.fromJson(json); -} - -/// @nodoc -mixin _$UserRepositoryState { - Map get users => throw _privateConstructorUsedError; - - Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) - $UserRepositoryStateCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $UserRepositoryStateCopyWith<$Res> { - factory $UserRepositoryStateCopyWith( - UserRepositoryState value, $Res Function(UserRepositoryState) then) = - _$UserRepositoryStateCopyWithImpl<$Res, UserRepositoryState>; - @useResult - $Res call({Map users}); -} - -/// @nodoc -class _$UserRepositoryStateCopyWithImpl<$Res, $Val extends UserRepositoryState> - implements $UserRepositoryStateCopyWith<$Res> { - _$UserRepositoryStateCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? users = null, - }) { - return _then(_value.copyWith( - users: null == users - ? _value.users - : users // ignore: cast_nullable_to_non_nullable - as Map, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$_UserRepositoryStateCopyWith<$Res> - implements $UserRepositoryStateCopyWith<$Res> { - factory _$$_UserRepositoryStateCopyWith(_$_UserRepositoryState value, - $Res Function(_$_UserRepositoryState) then) = - __$$_UserRepositoryStateCopyWithImpl<$Res>; - @override - @useResult - $Res call({Map users}); -} - -/// @nodoc -class __$$_UserRepositoryStateCopyWithImpl<$Res> - extends _$UserRepositoryStateCopyWithImpl<$Res, _$_UserRepositoryState> - implements _$$_UserRepositoryStateCopyWith<$Res> { - __$$_UserRepositoryStateCopyWithImpl(_$_UserRepositoryState _value, - $Res Function(_$_UserRepositoryState) _then) - : super(_value, _then); - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? users = null, - }) { - return _then(_$_UserRepositoryState( - users: null == users - ? _value._users - : users // ignore: cast_nullable_to_non_nullable - as Map, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$_UserRepositoryState implements _UserRepositoryState { - const _$_UserRepositoryState({final Map users = const {}}) - : _users = users; - - factory _$_UserRepositoryState.fromJson(Map json) => - _$$_UserRepositoryStateFromJson(json); - - final Map _users; - @override - @JsonKey() - Map get users { - if (_users is EqualUnmodifiableMapView) return _users; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_users); - } - - @override - String toString() { - return 'UserRepositoryState(users: $users)'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$_UserRepositoryState && - const DeepCollectionEquality().equals(other._users, _users)); - } - - @JsonKey(ignore: true) - @override - int get hashCode => - Object.hash(runtimeType, const DeepCollectionEquality().hash(_users)); - - @JsonKey(ignore: true) - @override - @pragma('vm:prefer-inline') - _$$_UserRepositoryStateCopyWith<_$_UserRepositoryState> get copyWith => - __$$_UserRepositoryStateCopyWithImpl<_$_UserRepositoryState>( - this, _$identity); - - @override - Map toJson() { - return _$$_UserRepositoryStateToJson( - this, - ); - } -} - -abstract class _UserRepositoryState implements UserRepositoryState { - const factory _UserRepositoryState({final Map users}) = - _$_UserRepositoryState; - - factory _UserRepositoryState.fromJson(Map json) = - _$_UserRepositoryState.fromJson; - - @override - Map get users; - @override - @JsonKey(ignore: true) - _$$_UserRepositoryStateCopyWith<_$_UserRepositoryState> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/lib/features/document_bulk_action/cubit/document_bulk_action_cubit.freezed.dart b/lib/features/document_bulk_action/cubit/document_bulk_action_cubit.freezed.dart deleted file mode 100644 index 77db26b..0000000 --- a/lib/features/document_bulk_action/cubit/document_bulk_action_cubit.freezed.dart +++ /dev/null @@ -1,269 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'document_bulk_action_cubit.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); - -/// @nodoc -mixin _$DocumentBulkActionState { - List get selection => throw _privateConstructorUsedError; - Map get correspondents => - throw _privateConstructorUsedError; - Map get documentTypes => - throw _privateConstructorUsedError; - Map get tags => throw _privateConstructorUsedError; - Map get storagePaths => throw _privateConstructorUsedError; - - @JsonKey(ignore: true) - $DocumentBulkActionStateCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $DocumentBulkActionStateCopyWith<$Res> { - factory $DocumentBulkActionStateCopyWith(DocumentBulkActionState value, - $Res Function(DocumentBulkActionState) then) = - _$DocumentBulkActionStateCopyWithImpl<$Res, DocumentBulkActionState>; - @useResult - $Res call( - {List selection, - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths}); -} - -/// @nodoc -class _$DocumentBulkActionStateCopyWithImpl<$Res, - $Val extends DocumentBulkActionState> - implements $DocumentBulkActionStateCopyWith<$Res> { - _$DocumentBulkActionStateCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? selection = null, - Object? correspondents = null, - Object? documentTypes = null, - Object? tags = null, - Object? storagePaths = null, - }) { - return _then(_value.copyWith( - selection: null == selection - ? _value.selection - : selection // ignore: cast_nullable_to_non_nullable - as List, - correspondents: null == correspondents - ? _value.correspondents - : correspondents // ignore: cast_nullable_to_non_nullable - as Map, - documentTypes: null == documentTypes - ? _value.documentTypes - : documentTypes // ignore: cast_nullable_to_non_nullable - as Map, - tags: null == tags - ? _value.tags - : tags // ignore: cast_nullable_to_non_nullable - as Map, - storagePaths: null == storagePaths - ? _value.storagePaths - : storagePaths // ignore: cast_nullable_to_non_nullable - as Map, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$_DocumentBulkActionStateCopyWith<$Res> - implements $DocumentBulkActionStateCopyWith<$Res> { - factory _$$_DocumentBulkActionStateCopyWith(_$_DocumentBulkActionState value, - $Res Function(_$_DocumentBulkActionState) then) = - __$$_DocumentBulkActionStateCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {List selection, - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths}); -} - -/// @nodoc -class __$$_DocumentBulkActionStateCopyWithImpl<$Res> - extends _$DocumentBulkActionStateCopyWithImpl<$Res, - _$_DocumentBulkActionState> - implements _$$_DocumentBulkActionStateCopyWith<$Res> { - __$$_DocumentBulkActionStateCopyWithImpl(_$_DocumentBulkActionState _value, - $Res Function(_$_DocumentBulkActionState) _then) - : super(_value, _then); - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? selection = null, - Object? correspondents = null, - Object? documentTypes = null, - Object? tags = null, - Object? storagePaths = null, - }) { - return _then(_$_DocumentBulkActionState( - selection: null == selection - ? _value._selection - : selection // ignore: cast_nullable_to_non_nullable - as List, - correspondents: null == correspondents - ? _value._correspondents - : correspondents // ignore: cast_nullable_to_non_nullable - as Map, - documentTypes: null == documentTypes - ? _value._documentTypes - : documentTypes // ignore: cast_nullable_to_non_nullable - as Map, - tags: null == tags - ? _value._tags - : tags // ignore: cast_nullable_to_non_nullable - as Map, - storagePaths: null == storagePaths - ? _value._storagePaths - : storagePaths // ignore: cast_nullable_to_non_nullable - as Map, - )); - } -} - -/// @nodoc - -class _$_DocumentBulkActionState extends _DocumentBulkActionState { - const _$_DocumentBulkActionState( - {required final List selection, - required final Map correspondents, - required final Map documentTypes, - required final Map tags, - required final Map storagePaths}) - : _selection = selection, - _correspondents = correspondents, - _documentTypes = documentTypes, - _tags = tags, - _storagePaths = storagePaths, - super._(); - - final List _selection; - @override - List get selection { - if (_selection is EqualUnmodifiableListView) return _selection; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_selection); - } - - final Map _correspondents; - @override - Map get correspondents { - if (_correspondents is EqualUnmodifiableMapView) return _correspondents; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_correspondents); - } - - final Map _documentTypes; - @override - Map get documentTypes { - if (_documentTypes is EqualUnmodifiableMapView) return _documentTypes; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_documentTypes); - } - - final Map _tags; - @override - Map get tags { - if (_tags is EqualUnmodifiableMapView) return _tags; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_tags); - } - - final Map _storagePaths; - @override - Map get storagePaths { - if (_storagePaths is EqualUnmodifiableMapView) return _storagePaths; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_storagePaths); - } - - @override - String toString() { - return 'DocumentBulkActionState(selection: $selection, correspondents: $correspondents, documentTypes: $documentTypes, tags: $tags, storagePaths: $storagePaths)'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$_DocumentBulkActionState && - const DeepCollectionEquality() - .equals(other._selection, _selection) && - const DeepCollectionEquality() - .equals(other._correspondents, _correspondents) && - const DeepCollectionEquality() - .equals(other._documentTypes, _documentTypes) && - const DeepCollectionEquality().equals(other._tags, _tags) && - const DeepCollectionEquality() - .equals(other._storagePaths, _storagePaths)); - } - - @override - int get hashCode => Object.hash( - runtimeType, - const DeepCollectionEquality().hash(_selection), - const DeepCollectionEquality().hash(_correspondents), - const DeepCollectionEquality().hash(_documentTypes), - const DeepCollectionEquality().hash(_tags), - const DeepCollectionEquality().hash(_storagePaths)); - - @JsonKey(ignore: true) - @override - @pragma('vm:prefer-inline') - _$$_DocumentBulkActionStateCopyWith<_$_DocumentBulkActionState> - get copyWith => - __$$_DocumentBulkActionStateCopyWithImpl<_$_DocumentBulkActionState>( - this, _$identity); -} - -abstract class _DocumentBulkActionState extends DocumentBulkActionState { - const factory _DocumentBulkActionState( - {required final List selection, - required final Map correspondents, - required final Map documentTypes, - required final Map tags, - required final Map storagePaths}) = - _$_DocumentBulkActionState; - const _DocumentBulkActionState._() : super._(); - - @override - List get selection; - @override - Map get correspondents; - @override - Map get documentTypes; - @override - Map get tags; - @override - Map get storagePaths; - @override - @JsonKey(ignore: true) - _$$_DocumentBulkActionStateCopyWith<_$_DocumentBulkActionState> - get copyWith => throw _privateConstructorUsedError; -} diff --git a/lib/features/document_details/cubit/document_details_cubit.freezed.dart b/lib/features/document_details/cubit/document_details_cubit.freezed.dart deleted file mode 100644 index 4d4f483..0000000 --- a/lib/features/document_details/cubit/document_details_cubit.freezed.dart +++ /dev/null @@ -1,350 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'document_details_cubit.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); - -/// @nodoc -mixin _$DocumentDetailsState { - DocumentModel get document => throw _privateConstructorUsedError; - DocumentMetaData? get metaData => throw _privateConstructorUsedError; - bool get isFullContentLoaded => throw _privateConstructorUsedError; - String? get fullContent => throw _privateConstructorUsedError; - FieldSuggestions? get suggestions => throw _privateConstructorUsedError; - Map get correspondents => - throw _privateConstructorUsedError; - Map get documentTypes => - throw _privateConstructorUsedError; - Map get tags => throw _privateConstructorUsedError; - Map get storagePaths => throw _privateConstructorUsedError; - - @JsonKey(ignore: true) - $DocumentDetailsStateCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $DocumentDetailsStateCopyWith<$Res> { - factory $DocumentDetailsStateCopyWith(DocumentDetailsState value, - $Res Function(DocumentDetailsState) then) = - _$DocumentDetailsStateCopyWithImpl<$Res, DocumentDetailsState>; - @useResult - $Res call( - {DocumentModel document, - DocumentMetaData? metaData, - bool isFullContentLoaded, - String? fullContent, - FieldSuggestions? suggestions, - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths}); -} - -/// @nodoc -class _$DocumentDetailsStateCopyWithImpl<$Res, - $Val extends DocumentDetailsState> - implements $DocumentDetailsStateCopyWith<$Res> { - _$DocumentDetailsStateCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? document = null, - Object? metaData = freezed, - Object? isFullContentLoaded = null, - Object? fullContent = freezed, - Object? suggestions = freezed, - Object? correspondents = null, - Object? documentTypes = null, - Object? tags = null, - Object? storagePaths = null, - }) { - return _then(_value.copyWith( - document: null == document - ? _value.document - : document // ignore: cast_nullable_to_non_nullable - as DocumentModel, - metaData: freezed == metaData - ? _value.metaData - : metaData // ignore: cast_nullable_to_non_nullable - as DocumentMetaData?, - isFullContentLoaded: null == isFullContentLoaded - ? _value.isFullContentLoaded - : isFullContentLoaded // ignore: cast_nullable_to_non_nullable - as bool, - fullContent: freezed == fullContent - ? _value.fullContent - : fullContent // ignore: cast_nullable_to_non_nullable - as String?, - suggestions: freezed == suggestions - ? _value.suggestions - : suggestions // ignore: cast_nullable_to_non_nullable - as FieldSuggestions?, - correspondents: null == correspondents - ? _value.correspondents - : correspondents // ignore: cast_nullable_to_non_nullable - as Map, - documentTypes: null == documentTypes - ? _value.documentTypes - : documentTypes // ignore: cast_nullable_to_non_nullable - as Map, - tags: null == tags - ? _value.tags - : tags // ignore: cast_nullable_to_non_nullable - as Map, - storagePaths: null == storagePaths - ? _value.storagePaths - : storagePaths // ignore: cast_nullable_to_non_nullable - as Map, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$_DocumentDetailsStateCopyWith<$Res> - implements $DocumentDetailsStateCopyWith<$Res> { - factory _$$_DocumentDetailsStateCopyWith(_$_DocumentDetailsState value, - $Res Function(_$_DocumentDetailsState) then) = - __$$_DocumentDetailsStateCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {DocumentModel document, - DocumentMetaData? metaData, - bool isFullContentLoaded, - String? fullContent, - FieldSuggestions? suggestions, - Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths}); -} - -/// @nodoc -class __$$_DocumentDetailsStateCopyWithImpl<$Res> - extends _$DocumentDetailsStateCopyWithImpl<$Res, _$_DocumentDetailsState> - implements _$$_DocumentDetailsStateCopyWith<$Res> { - __$$_DocumentDetailsStateCopyWithImpl(_$_DocumentDetailsState _value, - $Res Function(_$_DocumentDetailsState) _then) - : super(_value, _then); - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? document = null, - Object? metaData = freezed, - Object? isFullContentLoaded = null, - Object? fullContent = freezed, - Object? suggestions = freezed, - Object? correspondents = null, - Object? documentTypes = null, - Object? tags = null, - Object? storagePaths = null, - }) { - return _then(_$_DocumentDetailsState( - document: null == document - ? _value.document - : document // ignore: cast_nullable_to_non_nullable - as DocumentModel, - metaData: freezed == metaData - ? _value.metaData - : metaData // ignore: cast_nullable_to_non_nullable - as DocumentMetaData?, - isFullContentLoaded: null == isFullContentLoaded - ? _value.isFullContentLoaded - : isFullContentLoaded // ignore: cast_nullable_to_non_nullable - as bool, - fullContent: freezed == fullContent - ? _value.fullContent - : fullContent // ignore: cast_nullable_to_non_nullable - as String?, - suggestions: freezed == suggestions - ? _value.suggestions - : suggestions // ignore: cast_nullable_to_non_nullable - as FieldSuggestions?, - correspondents: null == correspondents - ? _value._correspondents - : correspondents // ignore: cast_nullable_to_non_nullable - as Map, - documentTypes: null == documentTypes - ? _value._documentTypes - : documentTypes // ignore: cast_nullable_to_non_nullable - as Map, - tags: null == tags - ? _value._tags - : tags // ignore: cast_nullable_to_non_nullable - as Map, - storagePaths: null == storagePaths - ? _value._storagePaths - : storagePaths // ignore: cast_nullable_to_non_nullable - as Map, - )); - } -} - -/// @nodoc - -class _$_DocumentDetailsState implements _DocumentDetailsState { - const _$_DocumentDetailsState( - {required this.document, - this.metaData, - this.isFullContentLoaded = false, - this.fullContent, - this.suggestions, - final Map correspondents = const {}, - final Map documentTypes = const {}, - final Map tags = const {}, - final Map storagePaths = const {}}) - : _correspondents = correspondents, - _documentTypes = documentTypes, - _tags = tags, - _storagePaths = storagePaths; - - @override - final DocumentModel document; - @override - final DocumentMetaData? metaData; - @override - @JsonKey() - final bool isFullContentLoaded; - @override - final String? fullContent; - @override - final FieldSuggestions? suggestions; - final Map _correspondents; - @override - @JsonKey() - Map get correspondents { - if (_correspondents is EqualUnmodifiableMapView) return _correspondents; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_correspondents); - } - - final Map _documentTypes; - @override - @JsonKey() - Map get documentTypes { - if (_documentTypes is EqualUnmodifiableMapView) return _documentTypes; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_documentTypes); - } - - final Map _tags; - @override - @JsonKey() - Map get tags { - if (_tags is EqualUnmodifiableMapView) return _tags; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_tags); - } - - final Map _storagePaths; - @override - @JsonKey() - Map get storagePaths { - if (_storagePaths is EqualUnmodifiableMapView) return _storagePaths; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_storagePaths); - } - - @override - String toString() { - return 'DocumentDetailsState(document: $document, metaData: $metaData, isFullContentLoaded: $isFullContentLoaded, fullContent: $fullContent, suggestions: $suggestions, correspondents: $correspondents, documentTypes: $documentTypes, tags: $tags, storagePaths: $storagePaths)'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$_DocumentDetailsState && - (identical(other.document, document) || - other.document == document) && - (identical(other.metaData, metaData) || - other.metaData == metaData) && - (identical(other.isFullContentLoaded, isFullContentLoaded) || - other.isFullContentLoaded == isFullContentLoaded) && - (identical(other.fullContent, fullContent) || - other.fullContent == fullContent) && - (identical(other.suggestions, suggestions) || - other.suggestions == suggestions) && - const DeepCollectionEquality() - .equals(other._correspondents, _correspondents) && - const DeepCollectionEquality() - .equals(other._documentTypes, _documentTypes) && - const DeepCollectionEquality().equals(other._tags, _tags) && - const DeepCollectionEquality() - .equals(other._storagePaths, _storagePaths)); - } - - @override - int get hashCode => Object.hash( - runtimeType, - document, - metaData, - isFullContentLoaded, - fullContent, - suggestions, - const DeepCollectionEquality().hash(_correspondents), - const DeepCollectionEquality().hash(_documentTypes), - const DeepCollectionEquality().hash(_tags), - const DeepCollectionEquality().hash(_storagePaths)); - - @JsonKey(ignore: true) - @override - @pragma('vm:prefer-inline') - _$$_DocumentDetailsStateCopyWith<_$_DocumentDetailsState> get copyWith => - __$$_DocumentDetailsStateCopyWithImpl<_$_DocumentDetailsState>( - this, _$identity); -} - -abstract class _DocumentDetailsState implements DocumentDetailsState { - const factory _DocumentDetailsState( - {required final DocumentModel document, - final DocumentMetaData? metaData, - final bool isFullContentLoaded, - final String? fullContent, - final FieldSuggestions? suggestions, - final Map correspondents, - final Map documentTypes, - final Map tags, - final Map storagePaths}) = _$_DocumentDetailsState; - - @override - DocumentModel get document; - @override - DocumentMetaData? get metaData; - @override - bool get isFullContentLoaded; - @override - String? get fullContent; - @override - FieldSuggestions? get suggestions; - @override - Map get correspondents; - @override - Map get documentTypes; - @override - Map get tags; - @override - Map get storagePaths; - @override - @JsonKey(ignore: true) - _$$_DocumentDetailsStateCopyWith<_$_DocumentDetailsState> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/lib/features/document_edit/cubit/document_edit_cubit.freezed.dart b/lib/features/document_edit/cubit/document_edit_cubit.freezed.dart deleted file mode 100644 index 862472a..0000000 --- a/lib/features/document_edit/cubit/document_edit_cubit.freezed.dart +++ /dev/null @@ -1,260 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'document_edit_cubit.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); - -/// @nodoc -mixin _$DocumentEditState { - DocumentModel get document => throw _privateConstructorUsedError; - Map get correspondents => - throw _privateConstructorUsedError; - Map get documentTypes => - throw _privateConstructorUsedError; - Map get storagePaths => throw _privateConstructorUsedError; - Map get tags => throw _privateConstructorUsedError; - - @JsonKey(ignore: true) - $DocumentEditStateCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $DocumentEditStateCopyWith<$Res> { - factory $DocumentEditStateCopyWith( - DocumentEditState value, $Res Function(DocumentEditState) then) = - _$DocumentEditStateCopyWithImpl<$Res, DocumentEditState>; - @useResult - $Res call( - {DocumentModel document, - Map correspondents, - Map documentTypes, - Map storagePaths, - Map tags}); -} - -/// @nodoc -class _$DocumentEditStateCopyWithImpl<$Res, $Val extends DocumentEditState> - implements $DocumentEditStateCopyWith<$Res> { - _$DocumentEditStateCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? document = null, - Object? correspondents = null, - Object? documentTypes = null, - Object? storagePaths = null, - Object? tags = null, - }) { - return _then(_value.copyWith( - document: null == document - ? _value.document - : document // ignore: cast_nullable_to_non_nullable - as DocumentModel, - correspondents: null == correspondents - ? _value.correspondents - : correspondents // ignore: cast_nullable_to_non_nullable - as Map, - documentTypes: null == documentTypes - ? _value.documentTypes - : documentTypes // ignore: cast_nullable_to_non_nullable - as Map, - storagePaths: null == storagePaths - ? _value.storagePaths - : storagePaths // ignore: cast_nullable_to_non_nullable - as Map, - tags: null == tags - ? _value.tags - : tags // ignore: cast_nullable_to_non_nullable - as Map, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$_DocumentEditStateCopyWith<$Res> - implements $DocumentEditStateCopyWith<$Res> { - factory _$$_DocumentEditStateCopyWith(_$_DocumentEditState value, - $Res Function(_$_DocumentEditState) then) = - __$$_DocumentEditStateCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {DocumentModel document, - Map correspondents, - Map documentTypes, - Map storagePaths, - Map tags}); -} - -/// @nodoc -class __$$_DocumentEditStateCopyWithImpl<$Res> - extends _$DocumentEditStateCopyWithImpl<$Res, _$_DocumentEditState> - implements _$$_DocumentEditStateCopyWith<$Res> { - __$$_DocumentEditStateCopyWithImpl( - _$_DocumentEditState _value, $Res Function(_$_DocumentEditState) _then) - : super(_value, _then); - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? document = null, - Object? correspondents = null, - Object? documentTypes = null, - Object? storagePaths = null, - Object? tags = null, - }) { - return _then(_$_DocumentEditState( - document: null == document - ? _value.document - : document // ignore: cast_nullable_to_non_nullable - as DocumentModel, - correspondents: null == correspondents - ? _value._correspondents - : correspondents // ignore: cast_nullable_to_non_nullable - as Map, - documentTypes: null == documentTypes - ? _value._documentTypes - : documentTypes // ignore: cast_nullable_to_non_nullable - as Map, - storagePaths: null == storagePaths - ? _value._storagePaths - : storagePaths // ignore: cast_nullable_to_non_nullable - as Map, - tags: null == tags - ? _value._tags - : tags // ignore: cast_nullable_to_non_nullable - as Map, - )); - } -} - -/// @nodoc - -class _$_DocumentEditState implements _DocumentEditState { - const _$_DocumentEditState( - {required this.document, - final Map correspondents = const {}, - final Map documentTypes = const {}, - final Map storagePaths = const {}, - final Map tags = const {}}) - : _correspondents = correspondents, - _documentTypes = documentTypes, - _storagePaths = storagePaths, - _tags = tags; - - @override - final DocumentModel document; - final Map _correspondents; - @override - @JsonKey() - Map get correspondents { - if (_correspondents is EqualUnmodifiableMapView) return _correspondents; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_correspondents); - } - - final Map _documentTypes; - @override - @JsonKey() - Map get documentTypes { - if (_documentTypes is EqualUnmodifiableMapView) return _documentTypes; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_documentTypes); - } - - final Map _storagePaths; - @override - @JsonKey() - Map get storagePaths { - if (_storagePaths is EqualUnmodifiableMapView) return _storagePaths; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_storagePaths); - } - - final Map _tags; - @override - @JsonKey() - Map get tags { - if (_tags is EqualUnmodifiableMapView) return _tags; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_tags); - } - - @override - String toString() { - return 'DocumentEditState(document: $document, correspondents: $correspondents, documentTypes: $documentTypes, storagePaths: $storagePaths, tags: $tags)'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$_DocumentEditState && - (identical(other.document, document) || - other.document == document) && - const DeepCollectionEquality() - .equals(other._correspondents, _correspondents) && - const DeepCollectionEquality() - .equals(other._documentTypes, _documentTypes) && - const DeepCollectionEquality() - .equals(other._storagePaths, _storagePaths) && - const DeepCollectionEquality().equals(other._tags, _tags)); - } - - @override - int get hashCode => Object.hash( - runtimeType, - document, - const DeepCollectionEquality().hash(_correspondents), - const DeepCollectionEquality().hash(_documentTypes), - const DeepCollectionEquality().hash(_storagePaths), - const DeepCollectionEquality().hash(_tags)); - - @JsonKey(ignore: true) - @override - @pragma('vm:prefer-inline') - _$$_DocumentEditStateCopyWith<_$_DocumentEditState> get copyWith => - __$$_DocumentEditStateCopyWithImpl<_$_DocumentEditState>( - this, _$identity); -} - -abstract class _DocumentEditState implements DocumentEditState { - const factory _DocumentEditState( - {required final DocumentModel document, - final Map correspondents, - final Map documentTypes, - final Map storagePaths, - final Map tags}) = _$_DocumentEditState; - - @override - DocumentModel get document; - @override - Map get correspondents; - @override - Map get documentTypes; - @override - Map get storagePaths; - @override - Map get tags; - @override - @JsonKey(ignore: true) - _$$_DocumentEditStateCopyWith<_$_DocumentEditState> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/lib/features/edit_label/cubit/edit_label_cubit.freezed.dart b/lib/features/edit_label/cubit/edit_label_cubit.freezed.dart deleted file mode 100644 index e085d11..0000000 --- a/lib/features/edit_label/cubit/edit_label_cubit.freezed.dart +++ /dev/null @@ -1,237 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'edit_label_cubit.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); - -/// @nodoc -mixin _$EditLabelState { - Map get correspondents => - throw _privateConstructorUsedError; - Map get documentTypes => - throw _privateConstructorUsedError; - Map get tags => throw _privateConstructorUsedError; - Map get storagePaths => throw _privateConstructorUsedError; - - @JsonKey(ignore: true) - $EditLabelStateCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $EditLabelStateCopyWith<$Res> { - factory $EditLabelStateCopyWith( - EditLabelState value, $Res Function(EditLabelState) then) = - _$EditLabelStateCopyWithImpl<$Res, EditLabelState>; - @useResult - $Res call( - {Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths}); -} - -/// @nodoc -class _$EditLabelStateCopyWithImpl<$Res, $Val extends EditLabelState> - implements $EditLabelStateCopyWith<$Res> { - _$EditLabelStateCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? correspondents = null, - Object? documentTypes = null, - Object? tags = null, - Object? storagePaths = null, - }) { - return _then(_value.copyWith( - correspondents: null == correspondents - ? _value.correspondents - : correspondents // ignore: cast_nullable_to_non_nullable - as Map, - documentTypes: null == documentTypes - ? _value.documentTypes - : documentTypes // ignore: cast_nullable_to_non_nullable - as Map, - tags: null == tags - ? _value.tags - : tags // ignore: cast_nullable_to_non_nullable - as Map, - storagePaths: null == storagePaths - ? _value.storagePaths - : storagePaths // ignore: cast_nullable_to_non_nullable - as Map, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$_EditLabelStateCopyWith<$Res> - implements $EditLabelStateCopyWith<$Res> { - factory _$$_EditLabelStateCopyWith( - _$_EditLabelState value, $Res Function(_$_EditLabelState) then) = - __$$_EditLabelStateCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths}); -} - -/// @nodoc -class __$$_EditLabelStateCopyWithImpl<$Res> - extends _$EditLabelStateCopyWithImpl<$Res, _$_EditLabelState> - implements _$$_EditLabelStateCopyWith<$Res> { - __$$_EditLabelStateCopyWithImpl( - _$_EditLabelState _value, $Res Function(_$_EditLabelState) _then) - : super(_value, _then); - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? correspondents = null, - Object? documentTypes = null, - Object? tags = null, - Object? storagePaths = null, - }) { - return _then(_$_EditLabelState( - correspondents: null == correspondents - ? _value._correspondents - : correspondents // ignore: cast_nullable_to_non_nullable - as Map, - documentTypes: null == documentTypes - ? _value._documentTypes - : documentTypes // ignore: cast_nullable_to_non_nullable - as Map, - tags: null == tags - ? _value._tags - : tags // ignore: cast_nullable_to_non_nullable - as Map, - storagePaths: null == storagePaths - ? _value._storagePaths - : storagePaths // ignore: cast_nullable_to_non_nullable - as Map, - )); - } -} - -/// @nodoc - -class _$_EditLabelState implements _EditLabelState { - const _$_EditLabelState( - {final Map correspondents = const {}, - final Map documentTypes = const {}, - final Map tags = const {}, - final Map storagePaths = const {}}) - : _correspondents = correspondents, - _documentTypes = documentTypes, - _tags = tags, - _storagePaths = storagePaths; - - final Map _correspondents; - @override - @JsonKey() - Map get correspondents { - if (_correspondents is EqualUnmodifiableMapView) return _correspondents; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_correspondents); - } - - final Map _documentTypes; - @override - @JsonKey() - Map get documentTypes { - if (_documentTypes is EqualUnmodifiableMapView) return _documentTypes; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_documentTypes); - } - - final Map _tags; - @override - @JsonKey() - Map get tags { - if (_tags is EqualUnmodifiableMapView) return _tags; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_tags); - } - - final Map _storagePaths; - @override - @JsonKey() - Map get storagePaths { - if (_storagePaths is EqualUnmodifiableMapView) return _storagePaths; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_storagePaths); - } - - @override - String toString() { - return 'EditLabelState(correspondents: $correspondents, documentTypes: $documentTypes, tags: $tags, storagePaths: $storagePaths)'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$_EditLabelState && - const DeepCollectionEquality() - .equals(other._correspondents, _correspondents) && - const DeepCollectionEquality() - .equals(other._documentTypes, _documentTypes) && - const DeepCollectionEquality().equals(other._tags, _tags) && - const DeepCollectionEquality() - .equals(other._storagePaths, _storagePaths)); - } - - @override - int get hashCode => Object.hash( - runtimeType, - const DeepCollectionEquality().hash(_correspondents), - const DeepCollectionEquality().hash(_documentTypes), - const DeepCollectionEquality().hash(_tags), - const DeepCollectionEquality().hash(_storagePaths)); - - @JsonKey(ignore: true) - @override - @pragma('vm:prefer-inline') - _$$_EditLabelStateCopyWith<_$_EditLabelState> get copyWith => - __$$_EditLabelStateCopyWithImpl<_$_EditLabelState>(this, _$identity); -} - -abstract class _EditLabelState implements EditLabelState { - const factory _EditLabelState( - {final Map correspondents, - final Map documentTypes, - final Map tags, - final Map storagePaths}) = _$_EditLabelState; - - @override - Map get correspondents; - @override - Map get documentTypes; - @override - Map get tags; - @override - Map get storagePaths; - @override - @JsonKey(ignore: true) - _$$_EditLabelStateCopyWith<_$_EditLabelState> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/lib/features/labels/cubit/label_cubit.freezed.dart b/lib/features/labels/cubit/label_cubit.freezed.dart deleted file mode 100644 index 725f412..0000000 --- a/lib/features/labels/cubit/label_cubit.freezed.dart +++ /dev/null @@ -1,237 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'label_cubit.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); - -/// @nodoc -mixin _$LabelState { - Map get correspondents => - throw _privateConstructorUsedError; - Map get documentTypes => - throw _privateConstructorUsedError; - Map get tags => throw _privateConstructorUsedError; - Map get storagePaths => throw _privateConstructorUsedError; - - @JsonKey(ignore: true) - $LabelStateCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $LabelStateCopyWith<$Res> { - factory $LabelStateCopyWith( - LabelState value, $Res Function(LabelState) then) = - _$LabelStateCopyWithImpl<$Res, LabelState>; - @useResult - $Res call( - {Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths}); -} - -/// @nodoc -class _$LabelStateCopyWithImpl<$Res, $Val extends LabelState> - implements $LabelStateCopyWith<$Res> { - _$LabelStateCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? correspondents = null, - Object? documentTypes = null, - Object? tags = null, - Object? storagePaths = null, - }) { - return _then(_value.copyWith( - correspondents: null == correspondents - ? _value.correspondents - : correspondents // ignore: cast_nullable_to_non_nullable - as Map, - documentTypes: null == documentTypes - ? _value.documentTypes - : documentTypes // ignore: cast_nullable_to_non_nullable - as Map, - tags: null == tags - ? _value.tags - : tags // ignore: cast_nullable_to_non_nullable - as Map, - storagePaths: null == storagePaths - ? _value.storagePaths - : storagePaths // ignore: cast_nullable_to_non_nullable - as Map, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$_LabelStateCopyWith<$Res> - implements $LabelStateCopyWith<$Res> { - factory _$$_LabelStateCopyWith( - _$_LabelState value, $Res Function(_$_LabelState) then) = - __$$_LabelStateCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {Map correspondents, - Map documentTypes, - Map tags, - Map storagePaths}); -} - -/// @nodoc -class __$$_LabelStateCopyWithImpl<$Res> - extends _$LabelStateCopyWithImpl<$Res, _$_LabelState> - implements _$$_LabelStateCopyWith<$Res> { - __$$_LabelStateCopyWithImpl( - _$_LabelState _value, $Res Function(_$_LabelState) _then) - : super(_value, _then); - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? correspondents = null, - Object? documentTypes = null, - Object? tags = null, - Object? storagePaths = null, - }) { - return _then(_$_LabelState( - correspondents: null == correspondents - ? _value._correspondents - : correspondents // ignore: cast_nullable_to_non_nullable - as Map, - documentTypes: null == documentTypes - ? _value._documentTypes - : documentTypes // ignore: cast_nullable_to_non_nullable - as Map, - tags: null == tags - ? _value._tags - : tags // ignore: cast_nullable_to_non_nullable - as Map, - storagePaths: null == storagePaths - ? _value._storagePaths - : storagePaths // ignore: cast_nullable_to_non_nullable - as Map, - )); - } -} - -/// @nodoc - -class _$_LabelState implements _LabelState { - const _$_LabelState( - {final Map correspondents = const {}, - final Map documentTypes = const {}, - final Map tags = const {}, - final Map storagePaths = const {}}) - : _correspondents = correspondents, - _documentTypes = documentTypes, - _tags = tags, - _storagePaths = storagePaths; - - final Map _correspondents; - @override - @JsonKey() - Map get correspondents { - if (_correspondents is EqualUnmodifiableMapView) return _correspondents; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_correspondents); - } - - final Map _documentTypes; - @override - @JsonKey() - Map get documentTypes { - if (_documentTypes is EqualUnmodifiableMapView) return _documentTypes; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_documentTypes); - } - - final Map _tags; - @override - @JsonKey() - Map get tags { - if (_tags is EqualUnmodifiableMapView) return _tags; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_tags); - } - - final Map _storagePaths; - @override - @JsonKey() - Map get storagePaths { - if (_storagePaths is EqualUnmodifiableMapView) return _storagePaths; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_storagePaths); - } - - @override - String toString() { - return 'LabelState(correspondents: $correspondents, documentTypes: $documentTypes, tags: $tags, storagePaths: $storagePaths)'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$_LabelState && - const DeepCollectionEquality() - .equals(other._correspondents, _correspondents) && - const DeepCollectionEquality() - .equals(other._documentTypes, _documentTypes) && - const DeepCollectionEquality().equals(other._tags, _tags) && - const DeepCollectionEquality() - .equals(other._storagePaths, _storagePaths)); - } - - @override - int get hashCode => Object.hash( - runtimeType, - const DeepCollectionEquality().hash(_correspondents), - const DeepCollectionEquality().hash(_documentTypes), - const DeepCollectionEquality().hash(_tags), - const DeepCollectionEquality().hash(_storagePaths)); - - @JsonKey(ignore: true) - @override - @pragma('vm:prefer-inline') - _$$_LabelStateCopyWith<_$_LabelState> get copyWith => - __$$_LabelStateCopyWithImpl<_$_LabelState>(this, _$identity); -} - -abstract class _LabelState implements LabelState { - const factory _LabelState( - {final Map correspondents, - final Map documentTypes, - final Map tags, - final Map storagePaths}) = _$_LabelState; - - @override - Map get correspondents; - @override - Map get documentTypes; - @override - Map get tags; - @override - Map get storagePaths; - @override - @JsonKey(ignore: true) - _$$_LabelStateCopyWith<_$_LabelState> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/lib/features/login/cubit/authentication_cubit.freezed.dart b/lib/features/login/cubit/authentication_cubit.freezed.dart deleted file mode 100644 index 6dbc119..0000000 --- a/lib/features/login/cubit/authentication_cubit.freezed.dart +++ /dev/null @@ -1,606 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'authentication_cubit.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); - -/// @nodoc -mixin _$AuthenticationState { - @optionalTypeArgs - TResult when({ - required TResult Function() unauthenticated, - required TResult Function() requriresLocalAuthentication, - required TResult Function(String localUserId, int apiVersion) authenticated, - required TResult Function() switchingAccounts, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult? whenOrNull({ - TResult? Function()? unauthenticated, - TResult? Function()? requriresLocalAuthentication, - TResult? Function(String localUserId, int apiVersion)? authenticated, - TResult? Function()? switchingAccounts, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult maybeWhen({ - TResult Function()? unauthenticated, - TResult Function()? requriresLocalAuthentication, - TResult Function(String localUserId, int apiVersion)? authenticated, - TResult Function()? switchingAccounts, - required TResult orElse(), - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult map({ - required TResult Function(_Unauthenticated value) unauthenticated, - required TResult Function(_RequiresLocalAuthentication value) - requriresLocalAuthentication, - required TResult Function(_Authenticated value) authenticated, - required TResult Function(_SwitchingAccounts value) switchingAccounts, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult? mapOrNull({ - TResult? Function(_Unauthenticated value)? unauthenticated, - TResult? Function(_RequiresLocalAuthentication value)? - requriresLocalAuthentication, - TResult? Function(_Authenticated value)? authenticated, - TResult? Function(_SwitchingAccounts value)? switchingAccounts, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult maybeMap({ - TResult Function(_Unauthenticated value)? unauthenticated, - TResult Function(_RequiresLocalAuthentication value)? - requriresLocalAuthentication, - TResult Function(_Authenticated value)? authenticated, - TResult Function(_SwitchingAccounts value)? switchingAccounts, - required TResult orElse(), - }) => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $AuthenticationStateCopyWith<$Res> { - factory $AuthenticationStateCopyWith( - AuthenticationState value, $Res Function(AuthenticationState) then) = - _$AuthenticationStateCopyWithImpl<$Res, AuthenticationState>; -} - -/// @nodoc -class _$AuthenticationStateCopyWithImpl<$Res, $Val extends AuthenticationState> - implements $AuthenticationStateCopyWith<$Res> { - _$AuthenticationStateCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; -} - -/// @nodoc -abstract class _$$_UnauthenticatedCopyWith<$Res> { - factory _$$_UnauthenticatedCopyWith( - _$_Unauthenticated value, $Res Function(_$_Unauthenticated) then) = - __$$_UnauthenticatedCopyWithImpl<$Res>; -} - -/// @nodoc -class __$$_UnauthenticatedCopyWithImpl<$Res> - extends _$AuthenticationStateCopyWithImpl<$Res, _$_Unauthenticated> - implements _$$_UnauthenticatedCopyWith<$Res> { - __$$_UnauthenticatedCopyWithImpl( - _$_Unauthenticated _value, $Res Function(_$_Unauthenticated) _then) - : super(_value, _then); -} - -/// @nodoc - -class _$_Unauthenticated implements _Unauthenticated { - const _$_Unauthenticated(); - - @override - String toString() { - return 'AuthenticationState.unauthenticated()'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && other is _$_Unauthenticated); - } - - @override - int get hashCode => runtimeType.hashCode; - - @override - @optionalTypeArgs - TResult when({ - required TResult Function() unauthenticated, - required TResult Function() requriresLocalAuthentication, - required TResult Function(String localUserId, int apiVersion) authenticated, - required TResult Function() switchingAccounts, - }) { - return unauthenticated(); - } - - @override - @optionalTypeArgs - TResult? whenOrNull({ - TResult? Function()? unauthenticated, - TResult? Function()? requriresLocalAuthentication, - TResult? Function(String localUserId, int apiVersion)? authenticated, - TResult? Function()? switchingAccounts, - }) { - return unauthenticated?.call(); - } - - @override - @optionalTypeArgs - TResult maybeWhen({ - TResult Function()? unauthenticated, - TResult Function()? requriresLocalAuthentication, - TResult Function(String localUserId, int apiVersion)? authenticated, - TResult Function()? switchingAccounts, - required TResult orElse(), - }) { - if (unauthenticated != null) { - return unauthenticated(); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map({ - required TResult Function(_Unauthenticated value) unauthenticated, - required TResult Function(_RequiresLocalAuthentication value) - requriresLocalAuthentication, - required TResult Function(_Authenticated value) authenticated, - required TResult Function(_SwitchingAccounts value) switchingAccounts, - }) { - return unauthenticated(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull({ - TResult? Function(_Unauthenticated value)? unauthenticated, - TResult? Function(_RequiresLocalAuthentication value)? - requriresLocalAuthentication, - TResult? Function(_Authenticated value)? authenticated, - TResult? Function(_SwitchingAccounts value)? switchingAccounts, - }) { - return unauthenticated?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap({ - TResult Function(_Unauthenticated value)? unauthenticated, - TResult Function(_RequiresLocalAuthentication value)? - requriresLocalAuthentication, - TResult Function(_Authenticated value)? authenticated, - TResult Function(_SwitchingAccounts value)? switchingAccounts, - required TResult orElse(), - }) { - if (unauthenticated != null) { - return unauthenticated(this); - } - return orElse(); - } -} - -abstract class _Unauthenticated implements AuthenticationState { - const factory _Unauthenticated() = _$_Unauthenticated; -} - -/// @nodoc -abstract class _$$_RequiresLocalAuthenticationCopyWith<$Res> { - factory _$$_RequiresLocalAuthenticationCopyWith( - _$_RequiresLocalAuthentication value, - $Res Function(_$_RequiresLocalAuthentication) then) = - __$$_RequiresLocalAuthenticationCopyWithImpl<$Res>; -} - -/// @nodoc -class __$$_RequiresLocalAuthenticationCopyWithImpl<$Res> - extends _$AuthenticationStateCopyWithImpl<$Res, - _$_RequiresLocalAuthentication> - implements _$$_RequiresLocalAuthenticationCopyWith<$Res> { - __$$_RequiresLocalAuthenticationCopyWithImpl( - _$_RequiresLocalAuthentication _value, - $Res Function(_$_RequiresLocalAuthentication) _then) - : super(_value, _then); -} - -/// @nodoc - -class _$_RequiresLocalAuthentication implements _RequiresLocalAuthentication { - const _$_RequiresLocalAuthentication(); - - @override - String toString() { - return 'AuthenticationState.requriresLocalAuthentication()'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$_RequiresLocalAuthentication); - } - - @override - int get hashCode => runtimeType.hashCode; - - @override - @optionalTypeArgs - TResult when({ - required TResult Function() unauthenticated, - required TResult Function() requriresLocalAuthentication, - required TResult Function(String localUserId, int apiVersion) authenticated, - required TResult Function() switchingAccounts, - }) { - return requriresLocalAuthentication(); - } - - @override - @optionalTypeArgs - TResult? whenOrNull({ - TResult? Function()? unauthenticated, - TResult? Function()? requriresLocalAuthentication, - TResult? Function(String localUserId, int apiVersion)? authenticated, - TResult? Function()? switchingAccounts, - }) { - return requriresLocalAuthentication?.call(); - } - - @override - @optionalTypeArgs - TResult maybeWhen({ - TResult Function()? unauthenticated, - TResult Function()? requriresLocalAuthentication, - TResult Function(String localUserId, int apiVersion)? authenticated, - TResult Function()? switchingAccounts, - required TResult orElse(), - }) { - if (requriresLocalAuthentication != null) { - return requriresLocalAuthentication(); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map({ - required TResult Function(_Unauthenticated value) unauthenticated, - required TResult Function(_RequiresLocalAuthentication value) - requriresLocalAuthentication, - required TResult Function(_Authenticated value) authenticated, - required TResult Function(_SwitchingAccounts value) switchingAccounts, - }) { - return requriresLocalAuthentication(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull({ - TResult? Function(_Unauthenticated value)? unauthenticated, - TResult? Function(_RequiresLocalAuthentication value)? - requriresLocalAuthentication, - TResult? Function(_Authenticated value)? authenticated, - TResult? Function(_SwitchingAccounts value)? switchingAccounts, - }) { - return requriresLocalAuthentication?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap({ - TResult Function(_Unauthenticated value)? unauthenticated, - TResult Function(_RequiresLocalAuthentication value)? - requriresLocalAuthentication, - TResult Function(_Authenticated value)? authenticated, - TResult Function(_SwitchingAccounts value)? switchingAccounts, - required TResult orElse(), - }) { - if (requriresLocalAuthentication != null) { - return requriresLocalAuthentication(this); - } - return orElse(); - } -} - -abstract class _RequiresLocalAuthentication implements AuthenticationState { - const factory _RequiresLocalAuthentication() = _$_RequiresLocalAuthentication; -} - -/// @nodoc -abstract class _$$_AuthenticatedCopyWith<$Res> { - factory _$$_AuthenticatedCopyWith( - _$_Authenticated value, $Res Function(_$_Authenticated) then) = - __$$_AuthenticatedCopyWithImpl<$Res>; - @useResult - $Res call({String localUserId, int apiVersion}); -} - -/// @nodoc -class __$$_AuthenticatedCopyWithImpl<$Res> - extends _$AuthenticationStateCopyWithImpl<$Res, _$_Authenticated> - implements _$$_AuthenticatedCopyWith<$Res> { - __$$_AuthenticatedCopyWithImpl( - _$_Authenticated _value, $Res Function(_$_Authenticated) _then) - : super(_value, _then); - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? localUserId = null, - Object? apiVersion = null, - }) { - return _then(_$_Authenticated( - localUserId: null == localUserId - ? _value.localUserId - : localUserId // ignore: cast_nullable_to_non_nullable - as String, - apiVersion: null == apiVersion - ? _value.apiVersion - : apiVersion // ignore: cast_nullable_to_non_nullable - as int, - )); - } -} - -/// @nodoc - -class _$_Authenticated implements _Authenticated { - const _$_Authenticated({required this.localUserId, required this.apiVersion}); - - @override - final String localUserId; - @override - final int apiVersion; - - @override - String toString() { - return 'AuthenticationState.authenticated(localUserId: $localUserId, apiVersion: $apiVersion)'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$_Authenticated && - (identical(other.localUserId, localUserId) || - other.localUserId == localUserId) && - (identical(other.apiVersion, apiVersion) || - other.apiVersion == apiVersion)); - } - - @override - int get hashCode => Object.hash(runtimeType, localUserId, apiVersion); - - @JsonKey(ignore: true) - @override - @pragma('vm:prefer-inline') - _$$_AuthenticatedCopyWith<_$_Authenticated> get copyWith => - __$$_AuthenticatedCopyWithImpl<_$_Authenticated>(this, _$identity); - - @override - @optionalTypeArgs - TResult when({ - required TResult Function() unauthenticated, - required TResult Function() requriresLocalAuthentication, - required TResult Function(String localUserId, int apiVersion) authenticated, - required TResult Function() switchingAccounts, - }) { - return authenticated(localUserId, apiVersion); - } - - @override - @optionalTypeArgs - TResult? whenOrNull({ - TResult? Function()? unauthenticated, - TResult? Function()? requriresLocalAuthentication, - TResult? Function(String localUserId, int apiVersion)? authenticated, - TResult? Function()? switchingAccounts, - }) { - return authenticated?.call(localUserId, apiVersion); - } - - @override - @optionalTypeArgs - TResult maybeWhen({ - TResult Function()? unauthenticated, - TResult Function()? requriresLocalAuthentication, - TResult Function(String localUserId, int apiVersion)? authenticated, - TResult Function()? switchingAccounts, - required TResult orElse(), - }) { - if (authenticated != null) { - return authenticated(localUserId, apiVersion); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map({ - required TResult Function(_Unauthenticated value) unauthenticated, - required TResult Function(_RequiresLocalAuthentication value) - requriresLocalAuthentication, - required TResult Function(_Authenticated value) authenticated, - required TResult Function(_SwitchingAccounts value) switchingAccounts, - }) { - return authenticated(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull({ - TResult? Function(_Unauthenticated value)? unauthenticated, - TResult? Function(_RequiresLocalAuthentication value)? - requriresLocalAuthentication, - TResult? Function(_Authenticated value)? authenticated, - TResult? Function(_SwitchingAccounts value)? switchingAccounts, - }) { - return authenticated?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap({ - TResult Function(_Unauthenticated value)? unauthenticated, - TResult Function(_RequiresLocalAuthentication value)? - requriresLocalAuthentication, - TResult Function(_Authenticated value)? authenticated, - TResult Function(_SwitchingAccounts value)? switchingAccounts, - required TResult orElse(), - }) { - if (authenticated != null) { - return authenticated(this); - } - return orElse(); - } -} - -abstract class _Authenticated implements AuthenticationState { - const factory _Authenticated( - {required final String localUserId, - required final int apiVersion}) = _$_Authenticated; - - String get localUserId; - int get apiVersion; - @JsonKey(ignore: true) - _$$_AuthenticatedCopyWith<_$_Authenticated> get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class _$$_SwitchingAccountsCopyWith<$Res> { - factory _$$_SwitchingAccountsCopyWith(_$_SwitchingAccounts value, - $Res Function(_$_SwitchingAccounts) then) = - __$$_SwitchingAccountsCopyWithImpl<$Res>; -} - -/// @nodoc -class __$$_SwitchingAccountsCopyWithImpl<$Res> - extends _$AuthenticationStateCopyWithImpl<$Res, _$_SwitchingAccounts> - implements _$$_SwitchingAccountsCopyWith<$Res> { - __$$_SwitchingAccountsCopyWithImpl( - _$_SwitchingAccounts _value, $Res Function(_$_SwitchingAccounts) _then) - : super(_value, _then); -} - -/// @nodoc - -class _$_SwitchingAccounts implements _SwitchingAccounts { - const _$_SwitchingAccounts(); - - @override - String toString() { - return 'AuthenticationState.switchingAccounts()'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && other is _$_SwitchingAccounts); - } - - @override - int get hashCode => runtimeType.hashCode; - - @override - @optionalTypeArgs - TResult when({ - required TResult Function() unauthenticated, - required TResult Function() requriresLocalAuthentication, - required TResult Function(String localUserId, int apiVersion) authenticated, - required TResult Function() switchingAccounts, - }) { - return switchingAccounts(); - } - - @override - @optionalTypeArgs - TResult? whenOrNull({ - TResult? Function()? unauthenticated, - TResult? Function()? requriresLocalAuthentication, - TResult? Function(String localUserId, int apiVersion)? authenticated, - TResult? Function()? switchingAccounts, - }) { - return switchingAccounts?.call(); - } - - @override - @optionalTypeArgs - TResult maybeWhen({ - TResult Function()? unauthenticated, - TResult Function()? requriresLocalAuthentication, - TResult Function(String localUserId, int apiVersion)? authenticated, - TResult Function()? switchingAccounts, - required TResult orElse(), - }) { - if (switchingAccounts != null) { - return switchingAccounts(); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map({ - required TResult Function(_Unauthenticated value) unauthenticated, - required TResult Function(_RequiresLocalAuthentication value) - requriresLocalAuthentication, - required TResult Function(_Authenticated value) authenticated, - required TResult Function(_SwitchingAccounts value) switchingAccounts, - }) { - return switchingAccounts(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull({ - TResult? Function(_Unauthenticated value)? unauthenticated, - TResult? Function(_RequiresLocalAuthentication value)? - requriresLocalAuthentication, - TResult? Function(_Authenticated value)? authenticated, - TResult? Function(_SwitchingAccounts value)? switchingAccounts, - }) { - return switchingAccounts?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap({ - TResult Function(_Unauthenticated value)? unauthenticated, - TResult Function(_RequiresLocalAuthentication value)? - requriresLocalAuthentication, - TResult Function(_Authenticated value)? authenticated, - TResult Function(_SwitchingAccounts value)? switchingAccounts, - required TResult orElse(), - }) { - if (switchingAccounts != null) { - return switchingAccounts(this); - } - return orElse(); - } -} - -abstract class _SwitchingAccounts implements AuthenticationState { - const factory _SwitchingAccounts() = _$_SwitchingAccounts; -} diff --git a/lib/features/saved_view/cubit/saved_view_cubit.freezed.dart b/lib/features/saved_view/cubit/saved_view_cubit.freezed.dart deleted file mode 100644 index ab53977..0000000 --- a/lib/features/saved_view/cubit/saved_view_cubit.freezed.dart +++ /dev/null @@ -1,585 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'saved_view_cubit.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); - -/// @nodoc -mixin _$SavedViewState { - @optionalTypeArgs - TResult when({ - required TResult Function() initial, - required TResult Function() loading, - required TResult Function(Map savedViews) loaded, - required TResult Function() error, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult? whenOrNull({ - TResult? Function()? initial, - TResult? Function()? loading, - TResult? Function(Map savedViews)? loaded, - TResult? Function()? error, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult maybeWhen({ - TResult Function()? initial, - TResult Function()? loading, - TResult Function(Map savedViews)? loaded, - TResult Function()? error, - required TResult orElse(), - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult map({ - required TResult Function(_SavedViewIntialState value) initial, - required TResult Function(_SavedViewLoadingState value) loading, - required TResult Function(_SavedViewLoadedState value) loaded, - required TResult Function(_SavedViewErrorState value) error, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult? mapOrNull({ - TResult? Function(_SavedViewIntialState value)? initial, - TResult? Function(_SavedViewLoadingState value)? loading, - TResult? Function(_SavedViewLoadedState value)? loaded, - TResult? Function(_SavedViewErrorState value)? error, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult maybeMap({ - TResult Function(_SavedViewIntialState value)? initial, - TResult Function(_SavedViewLoadingState value)? loading, - TResult Function(_SavedViewLoadedState value)? loaded, - TResult Function(_SavedViewErrorState value)? error, - required TResult orElse(), - }) => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $SavedViewStateCopyWith<$Res> { - factory $SavedViewStateCopyWith( - SavedViewState value, $Res Function(SavedViewState) then) = - _$SavedViewStateCopyWithImpl<$Res, SavedViewState>; -} - -/// @nodoc -class _$SavedViewStateCopyWithImpl<$Res, $Val extends SavedViewState> - implements $SavedViewStateCopyWith<$Res> { - _$SavedViewStateCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; -} - -/// @nodoc -abstract class _$$_SavedViewIntialStateCopyWith<$Res> { - factory _$$_SavedViewIntialStateCopyWith(_$_SavedViewIntialState value, - $Res Function(_$_SavedViewIntialState) then) = - __$$_SavedViewIntialStateCopyWithImpl<$Res>; -} - -/// @nodoc -class __$$_SavedViewIntialStateCopyWithImpl<$Res> - extends _$SavedViewStateCopyWithImpl<$Res, _$_SavedViewIntialState> - implements _$$_SavedViewIntialStateCopyWith<$Res> { - __$$_SavedViewIntialStateCopyWithImpl(_$_SavedViewIntialState _value, - $Res Function(_$_SavedViewIntialState) _then) - : super(_value, _then); -} - -/// @nodoc - -class _$_SavedViewIntialState implements _SavedViewIntialState { - const _$_SavedViewIntialState(); - - @override - String toString() { - return 'SavedViewState.initial()'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && other is _$_SavedViewIntialState); - } - - @override - int get hashCode => runtimeType.hashCode; - - @override - @optionalTypeArgs - TResult when({ - required TResult Function() initial, - required TResult Function() loading, - required TResult Function(Map savedViews) loaded, - required TResult Function() error, - }) { - return initial(); - } - - @override - @optionalTypeArgs - TResult? whenOrNull({ - TResult? Function()? initial, - TResult? Function()? loading, - TResult? Function(Map savedViews)? loaded, - TResult? Function()? error, - }) { - return initial?.call(); - } - - @override - @optionalTypeArgs - TResult maybeWhen({ - TResult Function()? initial, - TResult Function()? loading, - TResult Function(Map savedViews)? loaded, - TResult Function()? error, - required TResult orElse(), - }) { - if (initial != null) { - return initial(); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map({ - required TResult Function(_SavedViewIntialState value) initial, - required TResult Function(_SavedViewLoadingState value) loading, - required TResult Function(_SavedViewLoadedState value) loaded, - required TResult Function(_SavedViewErrorState value) error, - }) { - return initial(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull({ - TResult? Function(_SavedViewIntialState value)? initial, - TResult? Function(_SavedViewLoadingState value)? loading, - TResult? Function(_SavedViewLoadedState value)? loaded, - TResult? Function(_SavedViewErrorState value)? error, - }) { - return initial?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap({ - TResult Function(_SavedViewIntialState value)? initial, - TResult Function(_SavedViewLoadingState value)? loading, - TResult Function(_SavedViewLoadedState value)? loaded, - TResult Function(_SavedViewErrorState value)? error, - required TResult orElse(), - }) { - if (initial != null) { - return initial(this); - } - return orElse(); - } -} - -abstract class _SavedViewIntialState implements SavedViewState { - const factory _SavedViewIntialState() = _$_SavedViewIntialState; -} - -/// @nodoc -abstract class _$$_SavedViewLoadingStateCopyWith<$Res> { - factory _$$_SavedViewLoadingStateCopyWith(_$_SavedViewLoadingState value, - $Res Function(_$_SavedViewLoadingState) then) = - __$$_SavedViewLoadingStateCopyWithImpl<$Res>; -} - -/// @nodoc -class __$$_SavedViewLoadingStateCopyWithImpl<$Res> - extends _$SavedViewStateCopyWithImpl<$Res, _$_SavedViewLoadingState> - implements _$$_SavedViewLoadingStateCopyWith<$Res> { - __$$_SavedViewLoadingStateCopyWithImpl(_$_SavedViewLoadingState _value, - $Res Function(_$_SavedViewLoadingState) _then) - : super(_value, _then); -} - -/// @nodoc - -class _$_SavedViewLoadingState implements _SavedViewLoadingState { - const _$_SavedViewLoadingState(); - - @override - String toString() { - return 'SavedViewState.loading()'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && other is _$_SavedViewLoadingState); - } - - @override - int get hashCode => runtimeType.hashCode; - - @override - @optionalTypeArgs - TResult when({ - required TResult Function() initial, - required TResult Function() loading, - required TResult Function(Map savedViews) loaded, - required TResult Function() error, - }) { - return loading(); - } - - @override - @optionalTypeArgs - TResult? whenOrNull({ - TResult? Function()? initial, - TResult? Function()? loading, - TResult? Function(Map savedViews)? loaded, - TResult? Function()? error, - }) { - return loading?.call(); - } - - @override - @optionalTypeArgs - TResult maybeWhen({ - TResult Function()? initial, - TResult Function()? loading, - TResult Function(Map savedViews)? loaded, - TResult Function()? error, - required TResult orElse(), - }) { - if (loading != null) { - return loading(); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map({ - required TResult Function(_SavedViewIntialState value) initial, - required TResult Function(_SavedViewLoadingState value) loading, - required TResult Function(_SavedViewLoadedState value) loaded, - required TResult Function(_SavedViewErrorState value) error, - }) { - return loading(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull({ - TResult? Function(_SavedViewIntialState value)? initial, - TResult? Function(_SavedViewLoadingState value)? loading, - TResult? Function(_SavedViewLoadedState value)? loaded, - TResult? Function(_SavedViewErrorState value)? error, - }) { - return loading?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap({ - TResult Function(_SavedViewIntialState value)? initial, - TResult Function(_SavedViewLoadingState value)? loading, - TResult Function(_SavedViewLoadedState value)? loaded, - TResult Function(_SavedViewErrorState value)? error, - required TResult orElse(), - }) { - if (loading != null) { - return loading(this); - } - return orElse(); - } -} - -abstract class _SavedViewLoadingState implements SavedViewState { - const factory _SavedViewLoadingState() = _$_SavedViewLoadingState; -} - -/// @nodoc -abstract class _$$_SavedViewLoadedStateCopyWith<$Res> { - factory _$$_SavedViewLoadedStateCopyWith(_$_SavedViewLoadedState value, - $Res Function(_$_SavedViewLoadedState) then) = - __$$_SavedViewLoadedStateCopyWithImpl<$Res>; - @useResult - $Res call({Map savedViews}); -} - -/// @nodoc -class __$$_SavedViewLoadedStateCopyWithImpl<$Res> - extends _$SavedViewStateCopyWithImpl<$Res, _$_SavedViewLoadedState> - implements _$$_SavedViewLoadedStateCopyWith<$Res> { - __$$_SavedViewLoadedStateCopyWithImpl(_$_SavedViewLoadedState _value, - $Res Function(_$_SavedViewLoadedState) _then) - : super(_value, _then); - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? savedViews = null, - }) { - return _then(_$_SavedViewLoadedState( - savedViews: null == savedViews - ? _value._savedViews - : savedViews // ignore: cast_nullable_to_non_nullable - as Map, - )); - } -} - -/// @nodoc - -class _$_SavedViewLoadedState implements _SavedViewLoadedState { - const _$_SavedViewLoadedState({required final Map savedViews}) - : _savedViews = savedViews; - - final Map _savedViews; - @override - Map get savedViews { - if (_savedViews is EqualUnmodifiableMapView) return _savedViews; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_savedViews); - } - - @override - String toString() { - return 'SavedViewState.loaded(savedViews: $savedViews)'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$_SavedViewLoadedState && - const DeepCollectionEquality() - .equals(other._savedViews, _savedViews)); - } - - @override - int get hashCode => Object.hash( - runtimeType, const DeepCollectionEquality().hash(_savedViews)); - - @JsonKey(ignore: true) - @override - @pragma('vm:prefer-inline') - _$$_SavedViewLoadedStateCopyWith<_$_SavedViewLoadedState> get copyWith => - __$$_SavedViewLoadedStateCopyWithImpl<_$_SavedViewLoadedState>( - this, _$identity); - - @override - @optionalTypeArgs - TResult when({ - required TResult Function() initial, - required TResult Function() loading, - required TResult Function(Map savedViews) loaded, - required TResult Function() error, - }) { - return loaded(savedViews); - } - - @override - @optionalTypeArgs - TResult? whenOrNull({ - TResult? Function()? initial, - TResult? Function()? loading, - TResult? Function(Map savedViews)? loaded, - TResult? Function()? error, - }) { - return loaded?.call(savedViews); - } - - @override - @optionalTypeArgs - TResult maybeWhen({ - TResult Function()? initial, - TResult Function()? loading, - TResult Function(Map savedViews)? loaded, - TResult Function()? error, - required TResult orElse(), - }) { - if (loaded != null) { - return loaded(savedViews); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map({ - required TResult Function(_SavedViewIntialState value) initial, - required TResult Function(_SavedViewLoadingState value) loading, - required TResult Function(_SavedViewLoadedState value) loaded, - required TResult Function(_SavedViewErrorState value) error, - }) { - return loaded(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull({ - TResult? Function(_SavedViewIntialState value)? initial, - TResult? Function(_SavedViewLoadingState value)? loading, - TResult? Function(_SavedViewLoadedState value)? loaded, - TResult? Function(_SavedViewErrorState value)? error, - }) { - return loaded?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap({ - TResult Function(_SavedViewIntialState value)? initial, - TResult Function(_SavedViewLoadingState value)? loading, - TResult Function(_SavedViewLoadedState value)? loaded, - TResult Function(_SavedViewErrorState value)? error, - required TResult orElse(), - }) { - if (loaded != null) { - return loaded(this); - } - return orElse(); - } -} - -abstract class _SavedViewLoadedState implements SavedViewState { - const factory _SavedViewLoadedState( - {required final Map savedViews}) = - _$_SavedViewLoadedState; - - Map get savedViews; - @JsonKey(ignore: true) - _$$_SavedViewLoadedStateCopyWith<_$_SavedViewLoadedState> get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class _$$_SavedViewErrorStateCopyWith<$Res> { - factory _$$_SavedViewErrorStateCopyWith(_$_SavedViewErrorState value, - $Res Function(_$_SavedViewErrorState) then) = - __$$_SavedViewErrorStateCopyWithImpl<$Res>; -} - -/// @nodoc -class __$$_SavedViewErrorStateCopyWithImpl<$Res> - extends _$SavedViewStateCopyWithImpl<$Res, _$_SavedViewErrorState> - implements _$$_SavedViewErrorStateCopyWith<$Res> { - __$$_SavedViewErrorStateCopyWithImpl(_$_SavedViewErrorState _value, - $Res Function(_$_SavedViewErrorState) _then) - : super(_value, _then); -} - -/// @nodoc - -class _$_SavedViewErrorState implements _SavedViewErrorState { - const _$_SavedViewErrorState(); - - @override - String toString() { - return 'SavedViewState.error()'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && other is _$_SavedViewErrorState); - } - - @override - int get hashCode => runtimeType.hashCode; - - @override - @optionalTypeArgs - TResult when({ - required TResult Function() initial, - required TResult Function() loading, - required TResult Function(Map savedViews) loaded, - required TResult Function() error, - }) { - return error(); - } - - @override - @optionalTypeArgs - TResult? whenOrNull({ - TResult? Function()? initial, - TResult? Function()? loading, - TResult? Function(Map savedViews)? loaded, - TResult? Function()? error, - }) { - return error?.call(); - } - - @override - @optionalTypeArgs - TResult maybeWhen({ - TResult Function()? initial, - TResult Function()? loading, - TResult Function(Map savedViews)? loaded, - TResult Function()? error, - required TResult orElse(), - }) { - if (error != null) { - return error(); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map({ - required TResult Function(_SavedViewIntialState value) initial, - required TResult Function(_SavedViewLoadingState value) loading, - required TResult Function(_SavedViewLoadedState value) loaded, - required TResult Function(_SavedViewErrorState value) error, - }) { - return error(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull({ - TResult? Function(_SavedViewIntialState value)? initial, - TResult? Function(_SavedViewLoadingState value)? loading, - TResult? Function(_SavedViewLoadedState value)? loaded, - TResult? Function(_SavedViewErrorState value)? error, - }) { - return error?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap({ - TResult Function(_SavedViewIntialState value)? initial, - TResult Function(_SavedViewLoadingState value)? loading, - TResult Function(_SavedViewLoadedState value)? loaded, - TResult Function(_SavedViewErrorState value)? error, - required TResult orElse(), - }) { - if (error != null) { - return error(this); - } - return orElse(); - } -} - -abstract class _SavedViewErrorState implements SavedViewState { - const factory _SavedViewErrorState() = _$_SavedViewErrorState; -} diff --git a/packages/paperless_api/lib/src/models/group_model.freezed.dart b/packages/paperless_api/lib/src/models/group_model.freezed.dart deleted file mode 100644 index d3bc9cb..0000000 --- a/packages/paperless_api/lib/src/models/group_model.freezed.dart +++ /dev/null @@ -1,212 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'group_model.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); - -GroupModel _$GroupModelFromJson(Map json) { - return _GroupModel.fromJson(json); -} - -/// @nodoc -mixin _$GroupModel { - @HiveField(0) - int get id => throw _privateConstructorUsedError; - @HiveField(1) - String get name => throw _privateConstructorUsedError; - @HiveField(2) - List get permissions => throw _privateConstructorUsedError; - - Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) - $GroupModelCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $GroupModelCopyWith<$Res> { - factory $GroupModelCopyWith( - GroupModel value, $Res Function(GroupModel) then) = - _$GroupModelCopyWithImpl<$Res, GroupModel>; - @useResult - $Res call( - {@HiveField(0) int id, - @HiveField(1) String name, - @HiveField(2) List permissions}); -} - -/// @nodoc -class _$GroupModelCopyWithImpl<$Res, $Val extends GroupModel> - implements $GroupModelCopyWith<$Res> { - _$GroupModelCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? id = null, - Object? name = null, - Object? permissions = null, - }) { - return _then(_value.copyWith( - id: null == id - ? _value.id - : id // ignore: cast_nullable_to_non_nullable - as int, - name: null == name - ? _value.name - : name // ignore: cast_nullable_to_non_nullable - as String, - permissions: null == permissions - ? _value.permissions - : permissions // ignore: cast_nullable_to_non_nullable - as List, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$_GroupModelCopyWith<$Res> - implements $GroupModelCopyWith<$Res> { - factory _$$_GroupModelCopyWith( - _$_GroupModel value, $Res Function(_$_GroupModel) then) = - __$$_GroupModelCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {@HiveField(0) int id, - @HiveField(1) String name, - @HiveField(2) List permissions}); -} - -/// @nodoc -class __$$_GroupModelCopyWithImpl<$Res> - extends _$GroupModelCopyWithImpl<$Res, _$_GroupModel> - implements _$$_GroupModelCopyWith<$Res> { - __$$_GroupModelCopyWithImpl( - _$_GroupModel _value, $Res Function(_$_GroupModel) _then) - : super(_value, _then); - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? id = null, - Object? name = null, - Object? permissions = null, - }) { - return _then(_$_GroupModel( - id: null == id - ? _value.id - : id // ignore: cast_nullable_to_non_nullable - as int, - name: null == name - ? _value.name - : name // ignore: cast_nullable_to_non_nullable - as String, - permissions: null == permissions - ? _value._permissions - : permissions // ignore: cast_nullable_to_non_nullable - as List, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$_GroupModel implements _GroupModel { - const _$_GroupModel( - {@HiveField(0) required this.id, - @HiveField(1) required this.name, - @HiveField(2) required final List permissions}) - : _permissions = permissions; - - factory _$_GroupModel.fromJson(Map json) => - _$$_GroupModelFromJson(json); - - @override - @HiveField(0) - final int id; - @override - @HiveField(1) - final String name; - final List _permissions; - @override - @HiveField(2) - List get permissions { - if (_permissions is EqualUnmodifiableListView) return _permissions; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_permissions); - } - - @override - String toString() { - return 'GroupModel(id: $id, name: $name, permissions: $permissions)'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$_GroupModel && - (identical(other.id, id) || other.id == id) && - (identical(other.name, name) || other.name == name) && - const DeepCollectionEquality() - .equals(other._permissions, _permissions)); - } - - @JsonKey(ignore: true) - @override - int get hashCode => Object.hash( - runtimeType, id, name, const DeepCollectionEquality().hash(_permissions)); - - @JsonKey(ignore: true) - @override - @pragma('vm:prefer-inline') - _$$_GroupModelCopyWith<_$_GroupModel> get copyWith => - __$$_GroupModelCopyWithImpl<_$_GroupModel>(this, _$identity); - - @override - Map toJson() { - return _$$_GroupModelToJson( - this, - ); - } -} - -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 permissions}) = _$_GroupModel; - - factory _GroupModel.fromJson(Map json) = - _$_GroupModel.fromJson; - - @override - @HiveField(0) - int get id; - @override - @HiveField(1) - String get name; - @override - @HiveField(2) - List get permissions; - @override - @JsonKey(ignore: true) - _$$_GroupModelCopyWith<_$_GroupModel> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/packages/paperless_api/lib/src/models/permissions/permissions.freezed.dart b/packages/paperless_api/lib/src/models/permissions/permissions.freezed.dart deleted file mode 100644 index 6b324a5..0000000 --- a/packages/paperless_api/lib/src/models/permissions/permissions.freezed.dart +++ /dev/null @@ -1,195 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'permissions.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); - -Permissions _$PermissionsFromJson(Map json) { - return _Permissions.fromJson(json); -} - -/// @nodoc -mixin _$Permissions { - @HiveField(0) - List get view => throw _privateConstructorUsedError; - @HiveField(1) - List get change => throw _privateConstructorUsedError; - - Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) - $PermissionsCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $PermissionsCopyWith<$Res> { - factory $PermissionsCopyWith( - Permissions value, $Res Function(Permissions) then) = - _$PermissionsCopyWithImpl<$Res, Permissions>; - @useResult - $Res call({@HiveField(0) List view, @HiveField(1) List change}); -} - -/// @nodoc -class _$PermissionsCopyWithImpl<$Res, $Val extends Permissions> - implements $PermissionsCopyWith<$Res> { - _$PermissionsCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? view = null, - Object? change = null, - }) { - return _then(_value.copyWith( - view: null == view - ? _value.view - : view // ignore: cast_nullable_to_non_nullable - as List, - change: null == change - ? _value.change - : change // ignore: cast_nullable_to_non_nullable - as List, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$_PermissionsCopyWith<$Res> - implements $PermissionsCopyWith<$Res> { - factory _$$_PermissionsCopyWith( - _$_Permissions value, $Res Function(_$_Permissions) then) = - __$$_PermissionsCopyWithImpl<$Res>; - @override - @useResult - $Res call({@HiveField(0) List view, @HiveField(1) List change}); -} - -/// @nodoc -class __$$_PermissionsCopyWithImpl<$Res> - extends _$PermissionsCopyWithImpl<$Res, _$_Permissions> - implements _$$_PermissionsCopyWith<$Res> { - __$$_PermissionsCopyWithImpl( - _$_Permissions _value, $Res Function(_$_Permissions) _then) - : super(_value, _then); - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? view = null, - Object? change = null, - }) { - return _then(_$_Permissions( - view: null == view - ? _value._view - : view // ignore: cast_nullable_to_non_nullable - as List, - change: null == change - ? _value._change - : change // ignore: cast_nullable_to_non_nullable - as List, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$_Permissions implements _Permissions { - const _$_Permissions( - {@HiveField(0) final List view = const [], - @HiveField(1) final List change = const []}) - : _view = view, - _change = change; - - factory _$_Permissions.fromJson(Map json) => - _$$_PermissionsFromJson(json); - - final List _view; - @override - @JsonKey() - @HiveField(0) - List get view { - if (_view is EqualUnmodifiableListView) return _view; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_view); - } - - final List _change; - @override - @JsonKey() - @HiveField(1) - List get change { - if (_change is EqualUnmodifiableListView) return _change; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_change); - } - - @override - String toString() { - return 'Permissions(view: $view, change: $change)'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$_Permissions && - const DeepCollectionEquality().equals(other._view, _view) && - const DeepCollectionEquality().equals(other._change, _change)); - } - - @JsonKey(ignore: true) - @override - int get hashCode => Object.hash( - runtimeType, - const DeepCollectionEquality().hash(_view), - const DeepCollectionEquality().hash(_change)); - - @JsonKey(ignore: true) - @override - @pragma('vm:prefer-inline') - _$$_PermissionsCopyWith<_$_Permissions> get copyWith => - __$$_PermissionsCopyWithImpl<_$_Permissions>(this, _$identity); - - @override - Map toJson() { - return _$$_PermissionsToJson( - this, - ); - } -} - -abstract class _Permissions implements Permissions { - const factory _Permissions( - {@HiveField(0) final List view, - @HiveField(1) final List change}) = _$_Permissions; - - factory _Permissions.fromJson(Map json) = - _$_Permissions.fromJson; - - @override - @HiveField(0) - List get view; - @override - @HiveField(1) - List get change; - @override - @JsonKey(ignore: true) - _$$_PermissionsCopyWith<_$_Permissions> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/packages/paperless_api/lib/src/models/query_parameters/id_query_parameter.freezed.dart b/packages/paperless_api/lib/src/models/query_parameters/id_query_parameter.freezed.dart deleted file mode 100644 index 0509d0b..0000000 --- a/packages/paperless_api/lib/src/models/query_parameters/id_query_parameter.freezed.dart +++ /dev/null @@ -1,686 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'id_query_parameter.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); - -IdQueryParameter _$IdQueryParameterFromJson(Map json) { - switch (json['runtimeType']) { - case 'unset': - return UnsetIdQueryParameter.fromJson(json); - case 'notAssigned': - return NotAssignedIdQueryParameter.fromJson(json); - case 'anyAssigned': - return AnyAssignedIdQueryParameter.fromJson(json); - case 'fromId': - return SetIdQueryParameter.fromJson(json); - - default: - throw CheckedFromJsonException(json, 'runtimeType', 'IdQueryParameter', - 'Invalid union type "${json['runtimeType']}"!'); - } -} - -/// @nodoc -mixin _$IdQueryParameter { - @optionalTypeArgs - TResult when({ - required TResult Function() unset, - required TResult Function() notAssigned, - required TResult Function() anyAssigned, - required TResult Function(@HiveField(0) int id) fromId, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult? whenOrNull({ - TResult? Function()? unset, - TResult? Function()? notAssigned, - TResult? Function()? anyAssigned, - TResult? Function(@HiveField(0) int id)? fromId, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult maybeWhen({ - TResult Function()? unset, - TResult Function()? notAssigned, - TResult Function()? anyAssigned, - TResult Function(@HiveField(0) int id)? fromId, - required TResult orElse(), - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult map({ - required TResult Function(UnsetIdQueryParameter value) unset, - required TResult Function(NotAssignedIdQueryParameter value) notAssigned, - required TResult Function(AnyAssignedIdQueryParameter value) anyAssigned, - required TResult Function(SetIdQueryParameter value) fromId, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult? mapOrNull({ - TResult? Function(UnsetIdQueryParameter value)? unset, - TResult? Function(NotAssignedIdQueryParameter value)? notAssigned, - TResult? Function(AnyAssignedIdQueryParameter value)? anyAssigned, - TResult? Function(SetIdQueryParameter value)? fromId, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult maybeMap({ - TResult Function(UnsetIdQueryParameter value)? unset, - TResult Function(NotAssignedIdQueryParameter value)? notAssigned, - TResult Function(AnyAssignedIdQueryParameter value)? anyAssigned, - TResult Function(SetIdQueryParameter value)? fromId, - required TResult orElse(), - }) => - throw _privateConstructorUsedError; - Map toJson() => throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $IdQueryParameterCopyWith<$Res> { - factory $IdQueryParameterCopyWith( - IdQueryParameter value, $Res Function(IdQueryParameter) then) = - _$IdQueryParameterCopyWithImpl<$Res, IdQueryParameter>; -} - -/// @nodoc -class _$IdQueryParameterCopyWithImpl<$Res, $Val extends IdQueryParameter> - implements $IdQueryParameterCopyWith<$Res> { - _$IdQueryParameterCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; -} - -/// @nodoc -abstract class _$$UnsetIdQueryParameterCopyWith<$Res> { - factory _$$UnsetIdQueryParameterCopyWith(_$UnsetIdQueryParameter value, - $Res Function(_$UnsetIdQueryParameter) then) = - __$$UnsetIdQueryParameterCopyWithImpl<$Res>; -} - -/// @nodoc -class __$$UnsetIdQueryParameterCopyWithImpl<$Res> - extends _$IdQueryParameterCopyWithImpl<$Res, _$UnsetIdQueryParameter> - implements _$$UnsetIdQueryParameterCopyWith<$Res> { - __$$UnsetIdQueryParameterCopyWithImpl(_$UnsetIdQueryParameter _value, - $Res Function(_$UnsetIdQueryParameter) _then) - : super(_value, _then); -} - -/// @nodoc -@JsonSerializable() -@HiveType(typeId: PaperlessApiHiveTypeIds.unsetIdQueryParameter) -class _$UnsetIdQueryParameter extends UnsetIdQueryParameter { - const _$UnsetIdQueryParameter({final String? $type}) - : $type = $type ?? 'unset', - super._(); - - factory _$UnsetIdQueryParameter.fromJson(Map json) => - _$$UnsetIdQueryParameterFromJson(json); - - @JsonKey(name: 'runtimeType') - final String $type; - - @override - String toString() { - return 'IdQueryParameter.unset()'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && other is _$UnsetIdQueryParameter); - } - - @JsonKey(ignore: true) - @override - int get hashCode => runtimeType.hashCode; - - @override - @optionalTypeArgs - TResult when({ - required TResult Function() unset, - required TResult Function() notAssigned, - required TResult Function() anyAssigned, - required TResult Function(@HiveField(0) int id) fromId, - }) { - return unset(); - } - - @override - @optionalTypeArgs - TResult? whenOrNull({ - TResult? Function()? unset, - TResult? Function()? notAssigned, - TResult? Function()? anyAssigned, - TResult? Function(@HiveField(0) int id)? fromId, - }) { - return unset?.call(); - } - - @override - @optionalTypeArgs - TResult maybeWhen({ - TResult Function()? unset, - TResult Function()? notAssigned, - TResult Function()? anyAssigned, - TResult Function(@HiveField(0) int id)? fromId, - required TResult orElse(), - }) { - if (unset != null) { - return unset(); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map({ - required TResult Function(UnsetIdQueryParameter value) unset, - required TResult Function(NotAssignedIdQueryParameter value) notAssigned, - required TResult Function(AnyAssignedIdQueryParameter value) anyAssigned, - required TResult Function(SetIdQueryParameter value) fromId, - }) { - return unset(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull({ - TResult? Function(UnsetIdQueryParameter value)? unset, - TResult? Function(NotAssignedIdQueryParameter value)? notAssigned, - TResult? Function(AnyAssignedIdQueryParameter value)? anyAssigned, - TResult? Function(SetIdQueryParameter value)? fromId, - }) { - return unset?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap({ - TResult Function(UnsetIdQueryParameter value)? unset, - TResult Function(NotAssignedIdQueryParameter value)? notAssigned, - TResult Function(AnyAssignedIdQueryParameter value)? anyAssigned, - TResult Function(SetIdQueryParameter value)? fromId, - required TResult orElse(), - }) { - if (unset != null) { - return unset(this); - } - return orElse(); - } - - @override - Map toJson() { - return _$$UnsetIdQueryParameterToJson( - this, - ); - } -} - -abstract class UnsetIdQueryParameter extends IdQueryParameter { - const factory UnsetIdQueryParameter() = _$UnsetIdQueryParameter; - const UnsetIdQueryParameter._() : super._(); - - factory UnsetIdQueryParameter.fromJson(Map json) = - _$UnsetIdQueryParameter.fromJson; -} - -/// @nodoc -abstract class _$$NotAssignedIdQueryParameterCopyWith<$Res> { - factory _$$NotAssignedIdQueryParameterCopyWith( - _$NotAssignedIdQueryParameter value, - $Res Function(_$NotAssignedIdQueryParameter) then) = - __$$NotAssignedIdQueryParameterCopyWithImpl<$Res>; -} - -/// @nodoc -class __$$NotAssignedIdQueryParameterCopyWithImpl<$Res> - extends _$IdQueryParameterCopyWithImpl<$Res, _$NotAssignedIdQueryParameter> - implements _$$NotAssignedIdQueryParameterCopyWith<$Res> { - __$$NotAssignedIdQueryParameterCopyWithImpl( - _$NotAssignedIdQueryParameter _value, - $Res Function(_$NotAssignedIdQueryParameter) _then) - : super(_value, _then); -} - -/// @nodoc -@JsonSerializable() -@HiveType(typeId: PaperlessApiHiveTypeIds.notAssignedIdQueryParameter) -class _$NotAssignedIdQueryParameter extends NotAssignedIdQueryParameter { - const _$NotAssignedIdQueryParameter({final String? $type}) - : $type = $type ?? 'notAssigned', - super._(); - - factory _$NotAssignedIdQueryParameter.fromJson(Map json) => - _$$NotAssignedIdQueryParameterFromJson(json); - - @JsonKey(name: 'runtimeType') - final String $type; - - @override - String toString() { - return 'IdQueryParameter.notAssigned()'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$NotAssignedIdQueryParameter); - } - - @JsonKey(ignore: true) - @override - int get hashCode => runtimeType.hashCode; - - @override - @optionalTypeArgs - TResult when({ - required TResult Function() unset, - required TResult Function() notAssigned, - required TResult Function() anyAssigned, - required TResult Function(@HiveField(0) int id) fromId, - }) { - return notAssigned(); - } - - @override - @optionalTypeArgs - TResult? whenOrNull({ - TResult? Function()? unset, - TResult? Function()? notAssigned, - TResult? Function()? anyAssigned, - TResult? Function(@HiveField(0) int id)? fromId, - }) { - return notAssigned?.call(); - } - - @override - @optionalTypeArgs - TResult maybeWhen({ - TResult Function()? unset, - TResult Function()? notAssigned, - TResult Function()? anyAssigned, - TResult Function(@HiveField(0) int id)? fromId, - required TResult orElse(), - }) { - if (notAssigned != null) { - return notAssigned(); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map({ - required TResult Function(UnsetIdQueryParameter value) unset, - required TResult Function(NotAssignedIdQueryParameter value) notAssigned, - required TResult Function(AnyAssignedIdQueryParameter value) anyAssigned, - required TResult Function(SetIdQueryParameter value) fromId, - }) { - return notAssigned(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull({ - TResult? Function(UnsetIdQueryParameter value)? unset, - TResult? Function(NotAssignedIdQueryParameter value)? notAssigned, - TResult? Function(AnyAssignedIdQueryParameter value)? anyAssigned, - TResult? Function(SetIdQueryParameter value)? fromId, - }) { - return notAssigned?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap({ - TResult Function(UnsetIdQueryParameter value)? unset, - TResult Function(NotAssignedIdQueryParameter value)? notAssigned, - TResult Function(AnyAssignedIdQueryParameter value)? anyAssigned, - TResult Function(SetIdQueryParameter value)? fromId, - required TResult orElse(), - }) { - if (notAssigned != null) { - return notAssigned(this); - } - return orElse(); - } - - @override - Map toJson() { - return _$$NotAssignedIdQueryParameterToJson( - this, - ); - } -} - -abstract class NotAssignedIdQueryParameter extends IdQueryParameter { - const factory NotAssignedIdQueryParameter() = _$NotAssignedIdQueryParameter; - const NotAssignedIdQueryParameter._() : super._(); - - factory NotAssignedIdQueryParameter.fromJson(Map json) = - _$NotAssignedIdQueryParameter.fromJson; -} - -/// @nodoc -abstract class _$$AnyAssignedIdQueryParameterCopyWith<$Res> { - factory _$$AnyAssignedIdQueryParameterCopyWith( - _$AnyAssignedIdQueryParameter value, - $Res Function(_$AnyAssignedIdQueryParameter) then) = - __$$AnyAssignedIdQueryParameterCopyWithImpl<$Res>; -} - -/// @nodoc -class __$$AnyAssignedIdQueryParameterCopyWithImpl<$Res> - extends _$IdQueryParameterCopyWithImpl<$Res, _$AnyAssignedIdQueryParameter> - implements _$$AnyAssignedIdQueryParameterCopyWith<$Res> { - __$$AnyAssignedIdQueryParameterCopyWithImpl( - _$AnyAssignedIdQueryParameter _value, - $Res Function(_$AnyAssignedIdQueryParameter) _then) - : super(_value, _then); -} - -/// @nodoc -@JsonSerializable() -@HiveType(typeId: PaperlessApiHiveTypeIds.anyAssignedIdQueryParameter) -class _$AnyAssignedIdQueryParameter extends AnyAssignedIdQueryParameter { - const _$AnyAssignedIdQueryParameter({final String? $type}) - : $type = $type ?? 'anyAssigned', - super._(); - - factory _$AnyAssignedIdQueryParameter.fromJson(Map json) => - _$$AnyAssignedIdQueryParameterFromJson(json); - - @JsonKey(name: 'runtimeType') - final String $type; - - @override - String toString() { - return 'IdQueryParameter.anyAssigned()'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$AnyAssignedIdQueryParameter); - } - - @JsonKey(ignore: true) - @override - int get hashCode => runtimeType.hashCode; - - @override - @optionalTypeArgs - TResult when({ - required TResult Function() unset, - required TResult Function() notAssigned, - required TResult Function() anyAssigned, - required TResult Function(@HiveField(0) int id) fromId, - }) { - return anyAssigned(); - } - - @override - @optionalTypeArgs - TResult? whenOrNull({ - TResult? Function()? unset, - TResult? Function()? notAssigned, - TResult? Function()? anyAssigned, - TResult? Function(@HiveField(0) int id)? fromId, - }) { - return anyAssigned?.call(); - } - - @override - @optionalTypeArgs - TResult maybeWhen({ - TResult Function()? unset, - TResult Function()? notAssigned, - TResult Function()? anyAssigned, - TResult Function(@HiveField(0) int id)? fromId, - required TResult orElse(), - }) { - if (anyAssigned != null) { - return anyAssigned(); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map({ - required TResult Function(UnsetIdQueryParameter value) unset, - required TResult Function(NotAssignedIdQueryParameter value) notAssigned, - required TResult Function(AnyAssignedIdQueryParameter value) anyAssigned, - required TResult Function(SetIdQueryParameter value) fromId, - }) { - return anyAssigned(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull({ - TResult? Function(UnsetIdQueryParameter value)? unset, - TResult? Function(NotAssignedIdQueryParameter value)? notAssigned, - TResult? Function(AnyAssignedIdQueryParameter value)? anyAssigned, - TResult? Function(SetIdQueryParameter value)? fromId, - }) { - return anyAssigned?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap({ - TResult Function(UnsetIdQueryParameter value)? unset, - TResult Function(NotAssignedIdQueryParameter value)? notAssigned, - TResult Function(AnyAssignedIdQueryParameter value)? anyAssigned, - TResult Function(SetIdQueryParameter value)? fromId, - required TResult orElse(), - }) { - if (anyAssigned != null) { - return anyAssigned(this); - } - return orElse(); - } - - @override - Map toJson() { - return _$$AnyAssignedIdQueryParameterToJson( - this, - ); - } -} - -abstract class AnyAssignedIdQueryParameter extends IdQueryParameter { - const factory AnyAssignedIdQueryParameter() = _$AnyAssignedIdQueryParameter; - const AnyAssignedIdQueryParameter._() : super._(); - - factory AnyAssignedIdQueryParameter.fromJson(Map json) = - _$AnyAssignedIdQueryParameter.fromJson; -} - -/// @nodoc -abstract class _$$SetIdQueryParameterCopyWith<$Res> { - factory _$$SetIdQueryParameterCopyWith(_$SetIdQueryParameter value, - $Res Function(_$SetIdQueryParameter) then) = - __$$SetIdQueryParameterCopyWithImpl<$Res>; - @useResult - $Res call({@HiveField(0) int id}); -} - -/// @nodoc -class __$$SetIdQueryParameterCopyWithImpl<$Res> - extends _$IdQueryParameterCopyWithImpl<$Res, _$SetIdQueryParameter> - implements _$$SetIdQueryParameterCopyWith<$Res> { - __$$SetIdQueryParameterCopyWithImpl( - _$SetIdQueryParameter _value, $Res Function(_$SetIdQueryParameter) _then) - : super(_value, _then); - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? id = null, - }) { - return _then(_$SetIdQueryParameter( - null == id - ? _value.id - : id // ignore: cast_nullable_to_non_nullable - as int, - )); - } -} - -/// @nodoc -@JsonSerializable() -@HiveType(typeId: PaperlessApiHiveTypeIds.setIdQueryParameter) -class _$SetIdQueryParameter extends SetIdQueryParameter { - const _$SetIdQueryParameter(@HiveField(0) this.id, {final String? $type}) - : $type = $type ?? 'fromId', - super._(); - - factory _$SetIdQueryParameter.fromJson(Map json) => - _$$SetIdQueryParameterFromJson(json); - - @override - @HiveField(0) - final int id; - - @JsonKey(name: 'runtimeType') - final String $type; - - @override - String toString() { - return 'IdQueryParameter.fromId(id: $id)'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$SetIdQueryParameter && - (identical(other.id, id) || other.id == id)); - } - - @JsonKey(ignore: true) - @override - int get hashCode => Object.hash(runtimeType, id); - - @JsonKey(ignore: true) - @override - @pragma('vm:prefer-inline') - _$$SetIdQueryParameterCopyWith<_$SetIdQueryParameter> get copyWith => - __$$SetIdQueryParameterCopyWithImpl<_$SetIdQueryParameter>( - this, _$identity); - - @override - @optionalTypeArgs - TResult when({ - required TResult Function() unset, - required TResult Function() notAssigned, - required TResult Function() anyAssigned, - required TResult Function(@HiveField(0) int id) fromId, - }) { - return fromId(id); - } - - @override - @optionalTypeArgs - TResult? whenOrNull({ - TResult? Function()? unset, - TResult? Function()? notAssigned, - TResult? Function()? anyAssigned, - TResult? Function(@HiveField(0) int id)? fromId, - }) { - return fromId?.call(id); - } - - @override - @optionalTypeArgs - TResult maybeWhen({ - TResult Function()? unset, - TResult Function()? notAssigned, - TResult Function()? anyAssigned, - TResult Function(@HiveField(0) int id)? fromId, - required TResult orElse(), - }) { - if (fromId != null) { - return fromId(id); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map({ - required TResult Function(UnsetIdQueryParameter value) unset, - required TResult Function(NotAssignedIdQueryParameter value) notAssigned, - required TResult Function(AnyAssignedIdQueryParameter value) anyAssigned, - required TResult Function(SetIdQueryParameter value) fromId, - }) { - return fromId(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull({ - TResult? Function(UnsetIdQueryParameter value)? unset, - TResult? Function(NotAssignedIdQueryParameter value)? notAssigned, - TResult? Function(AnyAssignedIdQueryParameter value)? anyAssigned, - TResult? Function(SetIdQueryParameter value)? fromId, - }) { - return fromId?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap({ - TResult Function(UnsetIdQueryParameter value)? unset, - TResult Function(NotAssignedIdQueryParameter value)? notAssigned, - TResult Function(AnyAssignedIdQueryParameter value)? anyAssigned, - TResult Function(SetIdQueryParameter value)? fromId, - required TResult orElse(), - }) { - if (fromId != null) { - return fromId(this); - } - return orElse(); - } - - @override - Map toJson() { - return _$$SetIdQueryParameterToJson( - this, - ); - } -} - -abstract class SetIdQueryParameter extends IdQueryParameter { - const factory SetIdQueryParameter(@HiveField(0) final int id) = - _$SetIdQueryParameter; - const SetIdQueryParameter._() : super._(); - - factory SetIdQueryParameter.fromJson(Map json) = - _$SetIdQueryParameter.fromJson; - - @HiveField(0) - int get id; - @JsonKey(ignore: true) - _$$SetIdQueryParameterCopyWith<_$SetIdQueryParameter> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/packages/paperless_api/lib/src/models/query_parameters/tags_query/tags_query.freezed.dart b/packages/paperless_api/lib/src/models/query_parameters/tags_query/tags_query.freezed.dart deleted file mode 100644 index 6102564..0000000 --- a/packages/paperless_api/lib/src/models/query_parameters/tags_query/tags_query.freezed.dart +++ /dev/null @@ -1,587 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'tags_query.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); - -TagsQuery _$TagsQueryFromJson(Map json) { - switch (json['runtimeType']) { - case 'notAssigned': - return NotAssignedTagsQuery.fromJson(json); - case 'anyAssigned': - return AnyAssignedTagsQuery.fromJson(json); - case 'ids': - return IdsTagsQuery.fromJson(json); - - default: - throw CheckedFromJsonException(json, 'runtimeType', 'TagsQuery', - 'Invalid union type "${json['runtimeType']}"!'); - } -} - -/// @nodoc -mixin _$TagsQuery { - @optionalTypeArgs - TResult when({ - required TResult Function() notAssigned, - required TResult Function(List tagIds) anyAssigned, - required TResult Function(List include, List exclude) ids, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult? whenOrNull({ - TResult? Function()? notAssigned, - TResult? Function(List tagIds)? anyAssigned, - TResult? Function(List include, List exclude)? ids, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult maybeWhen({ - TResult Function()? notAssigned, - TResult Function(List tagIds)? anyAssigned, - TResult Function(List include, List exclude)? ids, - required TResult orElse(), - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult map({ - required TResult Function(NotAssignedTagsQuery value) notAssigned, - required TResult Function(AnyAssignedTagsQuery value) anyAssigned, - required TResult Function(IdsTagsQuery value) ids, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult? mapOrNull({ - TResult? Function(NotAssignedTagsQuery value)? notAssigned, - TResult? Function(AnyAssignedTagsQuery value)? anyAssigned, - TResult? Function(IdsTagsQuery value)? ids, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult maybeMap({ - TResult Function(NotAssignedTagsQuery value)? notAssigned, - TResult Function(AnyAssignedTagsQuery value)? anyAssigned, - TResult Function(IdsTagsQuery value)? ids, - required TResult orElse(), - }) => - throw _privateConstructorUsedError; - Map toJson() => throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $TagsQueryCopyWith<$Res> { - factory $TagsQueryCopyWith(TagsQuery value, $Res Function(TagsQuery) then) = - _$TagsQueryCopyWithImpl<$Res, TagsQuery>; -} - -/// @nodoc -class _$TagsQueryCopyWithImpl<$Res, $Val extends TagsQuery> - implements $TagsQueryCopyWith<$Res> { - _$TagsQueryCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; -} - -/// @nodoc -abstract class _$$NotAssignedTagsQueryCopyWith<$Res> { - factory _$$NotAssignedTagsQueryCopyWith(_$NotAssignedTagsQuery value, - $Res Function(_$NotAssignedTagsQuery) then) = - __$$NotAssignedTagsQueryCopyWithImpl<$Res>; -} - -/// @nodoc -class __$$NotAssignedTagsQueryCopyWithImpl<$Res> - extends _$TagsQueryCopyWithImpl<$Res, _$NotAssignedTagsQuery> - implements _$$NotAssignedTagsQueryCopyWith<$Res> { - __$$NotAssignedTagsQueryCopyWithImpl(_$NotAssignedTagsQuery _value, - $Res Function(_$NotAssignedTagsQuery) _then) - : super(_value, _then); -} - -/// @nodoc -@JsonSerializable() -@HiveType(typeId: PaperlessApiHiveTypeIds.notAssignedTagsQuery) -class _$NotAssignedTagsQuery extends NotAssignedTagsQuery { - const _$NotAssignedTagsQuery({final String? $type}) - : $type = $type ?? 'notAssigned', - super._(); - - factory _$NotAssignedTagsQuery.fromJson(Map json) => - _$$NotAssignedTagsQueryFromJson(json); - - @JsonKey(name: 'runtimeType') - final String $type; - - @override - String toString() { - return 'TagsQuery.notAssigned()'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && other is _$NotAssignedTagsQuery); - } - - @JsonKey(ignore: true) - @override - int get hashCode => runtimeType.hashCode; - - @override - @optionalTypeArgs - TResult when({ - required TResult Function() notAssigned, - required TResult Function(List tagIds) anyAssigned, - required TResult Function(List include, List exclude) ids, - }) { - return notAssigned(); - } - - @override - @optionalTypeArgs - TResult? whenOrNull({ - TResult? Function()? notAssigned, - TResult? Function(List tagIds)? anyAssigned, - TResult? Function(List include, List exclude)? ids, - }) { - return notAssigned?.call(); - } - - @override - @optionalTypeArgs - TResult maybeWhen({ - TResult Function()? notAssigned, - TResult Function(List tagIds)? anyAssigned, - TResult Function(List include, List exclude)? ids, - required TResult orElse(), - }) { - if (notAssigned != null) { - return notAssigned(); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map({ - required TResult Function(NotAssignedTagsQuery value) notAssigned, - required TResult Function(AnyAssignedTagsQuery value) anyAssigned, - required TResult Function(IdsTagsQuery value) ids, - }) { - return notAssigned(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull({ - TResult? Function(NotAssignedTagsQuery value)? notAssigned, - TResult? Function(AnyAssignedTagsQuery value)? anyAssigned, - TResult? Function(IdsTagsQuery value)? ids, - }) { - return notAssigned?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap({ - TResult Function(NotAssignedTagsQuery value)? notAssigned, - TResult Function(AnyAssignedTagsQuery value)? anyAssigned, - TResult Function(IdsTagsQuery value)? ids, - required TResult orElse(), - }) { - if (notAssigned != null) { - return notAssigned(this); - } - return orElse(); - } - - @override - Map toJson() { - return _$$NotAssignedTagsQueryToJson( - this, - ); - } -} - -abstract class NotAssignedTagsQuery extends TagsQuery { - const factory NotAssignedTagsQuery() = _$NotAssignedTagsQuery; - const NotAssignedTagsQuery._() : super._(); - - factory NotAssignedTagsQuery.fromJson(Map json) = - _$NotAssignedTagsQuery.fromJson; -} - -/// @nodoc -abstract class _$$AnyAssignedTagsQueryCopyWith<$Res> { - factory _$$AnyAssignedTagsQueryCopyWith(_$AnyAssignedTagsQuery value, - $Res Function(_$AnyAssignedTagsQuery) then) = - __$$AnyAssignedTagsQueryCopyWithImpl<$Res>; - @useResult - $Res call({List tagIds}); -} - -/// @nodoc -class __$$AnyAssignedTagsQueryCopyWithImpl<$Res> - extends _$TagsQueryCopyWithImpl<$Res, _$AnyAssignedTagsQuery> - implements _$$AnyAssignedTagsQueryCopyWith<$Res> { - __$$AnyAssignedTagsQueryCopyWithImpl(_$AnyAssignedTagsQuery _value, - $Res Function(_$AnyAssignedTagsQuery) _then) - : super(_value, _then); - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? tagIds = null, - }) { - return _then(_$AnyAssignedTagsQuery( - tagIds: null == tagIds - ? _value._tagIds - : tagIds // ignore: cast_nullable_to_non_nullable - as List, - )); - } -} - -/// @nodoc -@JsonSerializable() -@HiveType(typeId: PaperlessApiHiveTypeIds.anyAssignedTagsQuery) -class _$AnyAssignedTagsQuery extends AnyAssignedTagsQuery { - const _$AnyAssignedTagsQuery( - {final List tagIds = const [], final String? $type}) - : _tagIds = tagIds, - $type = $type ?? 'anyAssigned', - super._(); - - factory _$AnyAssignedTagsQuery.fromJson(Map json) => - _$$AnyAssignedTagsQueryFromJson(json); - - final List _tagIds; - @override - @JsonKey() - List get tagIds { - if (_tagIds is EqualUnmodifiableListView) return _tagIds; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_tagIds); - } - - @JsonKey(name: 'runtimeType') - final String $type; - - @override - String toString() { - return 'TagsQuery.anyAssigned(tagIds: $tagIds)'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$AnyAssignedTagsQuery && - const DeepCollectionEquality().equals(other._tagIds, _tagIds)); - } - - @JsonKey(ignore: true) - @override - int get hashCode => - Object.hash(runtimeType, const DeepCollectionEquality().hash(_tagIds)); - - @JsonKey(ignore: true) - @override - @pragma('vm:prefer-inline') - _$$AnyAssignedTagsQueryCopyWith<_$AnyAssignedTagsQuery> get copyWith => - __$$AnyAssignedTagsQueryCopyWithImpl<_$AnyAssignedTagsQuery>( - this, _$identity); - - @override - @optionalTypeArgs - TResult when({ - required TResult Function() notAssigned, - required TResult Function(List tagIds) anyAssigned, - required TResult Function(List include, List exclude) ids, - }) { - return anyAssigned(tagIds); - } - - @override - @optionalTypeArgs - TResult? whenOrNull({ - TResult? Function()? notAssigned, - TResult? Function(List tagIds)? anyAssigned, - TResult? Function(List include, List exclude)? ids, - }) { - return anyAssigned?.call(tagIds); - } - - @override - @optionalTypeArgs - TResult maybeWhen({ - TResult Function()? notAssigned, - TResult Function(List tagIds)? anyAssigned, - TResult Function(List include, List exclude)? ids, - required TResult orElse(), - }) { - if (anyAssigned != null) { - return anyAssigned(tagIds); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map({ - required TResult Function(NotAssignedTagsQuery value) notAssigned, - required TResult Function(AnyAssignedTagsQuery value) anyAssigned, - required TResult Function(IdsTagsQuery value) ids, - }) { - return anyAssigned(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull({ - TResult? Function(NotAssignedTagsQuery value)? notAssigned, - TResult? Function(AnyAssignedTagsQuery value)? anyAssigned, - TResult? Function(IdsTagsQuery value)? ids, - }) { - return anyAssigned?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap({ - TResult Function(NotAssignedTagsQuery value)? notAssigned, - TResult Function(AnyAssignedTagsQuery value)? anyAssigned, - TResult Function(IdsTagsQuery value)? ids, - required TResult orElse(), - }) { - if (anyAssigned != null) { - return anyAssigned(this); - } - return orElse(); - } - - @override - Map toJson() { - return _$$AnyAssignedTagsQueryToJson( - this, - ); - } -} - -abstract class AnyAssignedTagsQuery extends TagsQuery { - const factory AnyAssignedTagsQuery({final List tagIds}) = - _$AnyAssignedTagsQuery; - const AnyAssignedTagsQuery._() : super._(); - - factory AnyAssignedTagsQuery.fromJson(Map json) = - _$AnyAssignedTagsQuery.fromJson; - - List get tagIds; - @JsonKey(ignore: true) - _$$AnyAssignedTagsQueryCopyWith<_$AnyAssignedTagsQuery> get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class _$$IdsTagsQueryCopyWith<$Res> { - factory _$$IdsTagsQueryCopyWith( - _$IdsTagsQuery value, $Res Function(_$IdsTagsQuery) then) = - __$$IdsTagsQueryCopyWithImpl<$Res>; - @useResult - $Res call({List include, List exclude}); -} - -/// @nodoc -class __$$IdsTagsQueryCopyWithImpl<$Res> - extends _$TagsQueryCopyWithImpl<$Res, _$IdsTagsQuery> - implements _$$IdsTagsQueryCopyWith<$Res> { - __$$IdsTagsQueryCopyWithImpl( - _$IdsTagsQuery _value, $Res Function(_$IdsTagsQuery) _then) - : super(_value, _then); - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? include = null, - Object? exclude = null, - }) { - return _then(_$IdsTagsQuery( - include: null == include - ? _value._include - : include // ignore: cast_nullable_to_non_nullable - as List, - exclude: null == exclude - ? _value._exclude - : exclude // ignore: cast_nullable_to_non_nullable - as List, - )); - } -} - -/// @nodoc -@JsonSerializable() -@HiveType(typeId: PaperlessApiHiveTypeIds.idsTagsQuery) -class _$IdsTagsQuery extends IdsTagsQuery { - const _$IdsTagsQuery( - {final List include = const [], - final List exclude = const [], - final String? $type}) - : _include = include, - _exclude = exclude, - $type = $type ?? 'ids', - super._(); - - factory _$IdsTagsQuery.fromJson(Map json) => - _$$IdsTagsQueryFromJson(json); - - final List _include; - @override - @JsonKey() - List get include { - if (_include is EqualUnmodifiableListView) return _include; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_include); - } - - final List _exclude; - @override - @JsonKey() - List get exclude { - if (_exclude is EqualUnmodifiableListView) return _exclude; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_exclude); - } - - @JsonKey(name: 'runtimeType') - final String $type; - - @override - String toString() { - return 'TagsQuery.ids(include: $include, exclude: $exclude)'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$IdsTagsQuery && - const DeepCollectionEquality().equals(other._include, _include) && - const DeepCollectionEquality().equals(other._exclude, _exclude)); - } - - @JsonKey(ignore: true) - @override - int get hashCode => Object.hash( - runtimeType, - const DeepCollectionEquality().hash(_include), - const DeepCollectionEquality().hash(_exclude)); - - @JsonKey(ignore: true) - @override - @pragma('vm:prefer-inline') - _$$IdsTagsQueryCopyWith<_$IdsTagsQuery> get copyWith => - __$$IdsTagsQueryCopyWithImpl<_$IdsTagsQuery>(this, _$identity); - - @override - @optionalTypeArgs - TResult when({ - required TResult Function() notAssigned, - required TResult Function(List tagIds) anyAssigned, - required TResult Function(List include, List exclude) ids, - }) { - return ids(include, exclude); - } - - @override - @optionalTypeArgs - TResult? whenOrNull({ - TResult? Function()? notAssigned, - TResult? Function(List tagIds)? anyAssigned, - TResult? Function(List include, List exclude)? ids, - }) { - return ids?.call(include, exclude); - } - - @override - @optionalTypeArgs - TResult maybeWhen({ - TResult Function()? notAssigned, - TResult Function(List tagIds)? anyAssigned, - TResult Function(List include, List exclude)? ids, - required TResult orElse(), - }) { - if (ids != null) { - return ids(include, exclude); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map({ - required TResult Function(NotAssignedTagsQuery value) notAssigned, - required TResult Function(AnyAssignedTagsQuery value) anyAssigned, - required TResult Function(IdsTagsQuery value) ids, - }) { - return ids(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull({ - TResult? Function(NotAssignedTagsQuery value)? notAssigned, - TResult? Function(AnyAssignedTagsQuery value)? anyAssigned, - TResult? Function(IdsTagsQuery value)? ids, - }) { - return ids?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap({ - TResult Function(NotAssignedTagsQuery value)? notAssigned, - TResult Function(AnyAssignedTagsQuery value)? anyAssigned, - TResult Function(IdsTagsQuery value)? ids, - required TResult orElse(), - }) { - if (ids != null) { - return ids(this); - } - return orElse(); - } - - @override - Map toJson() { - return _$$IdsTagsQueryToJson( - this, - ); - } -} - -abstract class IdsTagsQuery extends TagsQuery { - const factory IdsTagsQuery( - {final List include, final List exclude}) = _$IdsTagsQuery; - const IdsTagsQuery._() : super._(); - - factory IdsTagsQuery.fromJson(Map json) = - _$IdsTagsQuery.fromJson; - - List get include; - List get exclude; - @JsonKey(ignore: true) - _$$IdsTagsQueryCopyWith<_$IdsTagsQuery> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/packages/paperless_api/lib/src/models/user_model.freezed.dart b/packages/paperless_api/lib/src/models/user_model.freezed.dart deleted file mode 100644 index 8d17d63..0000000 --- a/packages/paperless_api/lib/src/models/user_model.freezed.dart +++ /dev/null @@ -1,844 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'user_model.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); - -UserModel _$UserModelFromJson(Map json) { - switch (json['runtimeType']) { - case 'v3': - return UserModelV3.fromJson(json); - case 'v2': - return UserModelV2.fromJson(json); - - default: - throw CheckedFromJsonException(json, 'runtimeType', 'UserModel', - 'Invalid union type "${json['runtimeType']}"!'); - } -} - -/// @nodoc -mixin _$UserModel { - @HiveField(0) - int get id => throw _privateConstructorUsedError; - @HiveField(1) - String get username => throw _privateConstructorUsedError; - @optionalTypeArgs - TResult when({ - required TResult Function( - @HiveField(0) int id, - @HiveField(1) String username, - @HiveField(2) String email, - @HiveField(3) String? firstName, - @HiveField(4) String? lastName, - @HiveField(5) DateTime? dateJoined, - @HiveField(6) bool isStaff, - @HiveField(7) bool isActive, - @HiveField(8) bool isSuperuser, - @HiveField(9) List groups, - @HiveField(10) List userPermissions, - @HiveField(11) List inheritedPermissions) - v3, - required TResult Function(@HiveField(0) @JsonKey(name: "user_id") int id, - @HiveField(1) String username, @HiveField(2) String? displayName) - v2, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult? whenOrNull({ - TResult? Function( - @HiveField(0) int id, - @HiveField(1) String username, - @HiveField(2) String email, - @HiveField(3) String? firstName, - @HiveField(4) String? lastName, - @HiveField(5) DateTime? dateJoined, - @HiveField(6) bool isStaff, - @HiveField(7) bool isActive, - @HiveField(8) bool isSuperuser, - @HiveField(9) List groups, - @HiveField(10) List userPermissions, - @HiveField(11) List inheritedPermissions)? - v3, - TResult? Function(@HiveField(0) @JsonKey(name: "user_id") int id, - @HiveField(1) String username, @HiveField(2) String? displayName)? - v2, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult maybeWhen({ - TResult Function( - @HiveField(0) int id, - @HiveField(1) String username, - @HiveField(2) String email, - @HiveField(3) String? firstName, - @HiveField(4) String? lastName, - @HiveField(5) DateTime? dateJoined, - @HiveField(6) bool isStaff, - @HiveField(7) bool isActive, - @HiveField(8) bool isSuperuser, - @HiveField(9) List groups, - @HiveField(10) List userPermissions, - @HiveField(11) List inheritedPermissions)? - v3, - TResult Function(@HiveField(0) @JsonKey(name: "user_id") int id, - @HiveField(1) String username, @HiveField(2) String? displayName)? - v2, - required TResult orElse(), - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult map({ - required TResult Function(UserModelV3 value) v3, - required TResult Function(UserModelV2 value) v2, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult? mapOrNull({ - TResult? Function(UserModelV3 value)? v3, - TResult? Function(UserModelV2 value)? v2, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult maybeMap({ - TResult Function(UserModelV3 value)? v3, - TResult Function(UserModelV2 value)? v2, - required TResult orElse(), - }) => - throw _privateConstructorUsedError; - Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) - $UserModelCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $UserModelCopyWith<$Res> { - factory $UserModelCopyWith(UserModel value, $Res Function(UserModel) then) = - _$UserModelCopyWithImpl<$Res, UserModel>; - @useResult - $Res call({@HiveField(0) int id, @HiveField(1) String username}); -} - -/// @nodoc -class _$UserModelCopyWithImpl<$Res, $Val extends UserModel> - implements $UserModelCopyWith<$Res> { - _$UserModelCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? id = null, - Object? username = null, - }) { - return _then(_value.copyWith( - id: null == id - ? _value.id - : id // ignore: cast_nullable_to_non_nullable - as int, - username: null == username - ? _value.username - : username // ignore: cast_nullable_to_non_nullable - as String, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$UserModelV3CopyWith<$Res> - implements $UserModelCopyWith<$Res> { - factory _$$UserModelV3CopyWith( - _$UserModelV3 value, $Res Function(_$UserModelV3) then) = - __$$UserModelV3CopyWithImpl<$Res>; - @override - @useResult - $Res call( - {@HiveField(0) int id, - @HiveField(1) String username, - @HiveField(2) String email, - @HiveField(3) String? firstName, - @HiveField(4) String? lastName, - @HiveField(5) DateTime? dateJoined, - @HiveField(6) bool isStaff, - @HiveField(7) bool isActive, - @HiveField(8) bool isSuperuser, - @HiveField(9) List groups, - @HiveField(10) List userPermissions, - @HiveField(11) List inheritedPermissions}); -} - -/// @nodoc -class __$$UserModelV3CopyWithImpl<$Res> - extends _$UserModelCopyWithImpl<$Res, _$UserModelV3> - implements _$$UserModelV3CopyWith<$Res> { - __$$UserModelV3CopyWithImpl( - _$UserModelV3 _value, $Res Function(_$UserModelV3) _then) - : super(_value, _then); - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? id = null, - Object? username = null, - Object? email = null, - Object? firstName = freezed, - Object? lastName = freezed, - Object? dateJoined = freezed, - Object? isStaff = null, - Object? isActive = null, - Object? isSuperuser = null, - Object? groups = null, - Object? userPermissions = null, - Object? inheritedPermissions = null, - }) { - return _then(_$UserModelV3( - id: null == id - ? _value.id - : id // ignore: cast_nullable_to_non_nullable - as int, - username: null == username - ? _value.username - : username // ignore: cast_nullable_to_non_nullable - as String, - email: null == email - ? _value.email - : email // ignore: cast_nullable_to_non_nullable - as String, - firstName: freezed == firstName - ? _value.firstName - : firstName // ignore: cast_nullable_to_non_nullable - as String?, - lastName: freezed == lastName - ? _value.lastName - : lastName // ignore: cast_nullable_to_non_nullable - as String?, - dateJoined: freezed == dateJoined - ? _value.dateJoined - : dateJoined // ignore: cast_nullable_to_non_nullable - as DateTime?, - isStaff: null == isStaff - ? _value.isStaff - : isStaff // ignore: cast_nullable_to_non_nullable - as bool, - isActive: null == isActive - ? _value.isActive - : isActive // ignore: cast_nullable_to_non_nullable - as bool, - isSuperuser: null == isSuperuser - ? _value.isSuperuser - : isSuperuser // ignore: cast_nullable_to_non_nullable - as bool, - groups: null == groups - ? _value._groups - : groups // ignore: cast_nullable_to_non_nullable - as List, - userPermissions: null == userPermissions - ? _value._userPermissions - : userPermissions // ignore: cast_nullable_to_non_nullable - as List, - inheritedPermissions: null == inheritedPermissions - ? _value._inheritedPermissions - : inheritedPermissions // ignore: cast_nullable_to_non_nullable - as List, - )); - } -} - -/// @nodoc - -@JsonSerializable(fieldRename: FieldRename.snake) -@HiveType(typeId: PaperlessApiHiveTypeIds.userModelv3) -class _$UserModelV3 extends UserModelV3 { - const _$UserModelV3( - {@HiveField(0) required this.id, - @HiveField(1) required this.username, - @HiveField(2) required this.email, - @HiveField(3) this.firstName, - @HiveField(4) this.lastName, - @HiveField(5) this.dateJoined, - @HiveField(6) this.isStaff = true, - @HiveField(7) this.isActive = true, - @HiveField(8) this.isSuperuser = true, - @HiveField(9) final List groups = const [], - @HiveField(10) final List userPermissions = const [], - @HiveField(11) final List inheritedPermissions = - InheritedPermissions.values, - final String? $type}) - : _groups = groups, - _userPermissions = userPermissions, - _inheritedPermissions = inheritedPermissions, - $type = $type ?? 'v3', - super._(); - - factory _$UserModelV3.fromJson(Map json) => - _$$UserModelV3FromJson(json); - - @override - @HiveField(0) - final int id; - @override - @HiveField(1) - final String username; - @override - @HiveField(2) - final String email; - @override - @HiveField(3) - final String? firstName; - @override - @HiveField(4) - final String? lastName; - @override - @HiveField(5) - final DateTime? dateJoined; - @override - @JsonKey() - @HiveField(6) - final bool isStaff; - @override - @JsonKey() - @HiveField(7) - final bool isActive; - @override - @JsonKey() - @HiveField(8) - final bool isSuperuser; - final List _groups; - @override - @JsonKey() - @HiveField(9) - List get groups { - if (_groups is EqualUnmodifiableListView) return _groups; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_groups); - } - - final List _userPermissions; - @override - @JsonKey() - @HiveField(10) - List get userPermissions { - if (_userPermissions is EqualUnmodifiableListView) return _userPermissions; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_userPermissions); - } - - final List _inheritedPermissions; - @override - @JsonKey() - @HiveField(11) - List get inheritedPermissions { - if (_inheritedPermissions is EqualUnmodifiableListView) - return _inheritedPermissions; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_inheritedPermissions); - } - - @JsonKey(name: 'runtimeType') - final String $type; - - @override - String toString() { - return 'UserModel.v3(id: $id, username: $username, email: $email, firstName: $firstName, lastName: $lastName, dateJoined: $dateJoined, isStaff: $isStaff, isActive: $isActive, isSuperuser: $isSuperuser, groups: $groups, userPermissions: $userPermissions, inheritedPermissions: $inheritedPermissions)'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$UserModelV3 && - (identical(other.id, id) || other.id == id) && - (identical(other.username, username) || - other.username == username) && - (identical(other.email, email) || other.email == email) && - (identical(other.firstName, firstName) || - other.firstName == firstName) && - (identical(other.lastName, lastName) || - other.lastName == lastName) && - (identical(other.dateJoined, dateJoined) || - other.dateJoined == dateJoined) && - (identical(other.isStaff, isStaff) || other.isStaff == isStaff) && - (identical(other.isActive, isActive) || - other.isActive == isActive) && - (identical(other.isSuperuser, isSuperuser) || - other.isSuperuser == isSuperuser) && - const DeepCollectionEquality().equals(other._groups, _groups) && - const DeepCollectionEquality() - .equals(other._userPermissions, _userPermissions) && - const DeepCollectionEquality() - .equals(other._inheritedPermissions, _inheritedPermissions)); - } - - @JsonKey(ignore: true) - @override - int get hashCode => Object.hash( - runtimeType, - id, - username, - email, - firstName, - lastName, - dateJoined, - isStaff, - isActive, - isSuperuser, - const DeepCollectionEquality().hash(_groups), - const DeepCollectionEquality().hash(_userPermissions), - const DeepCollectionEquality().hash(_inheritedPermissions)); - - @JsonKey(ignore: true) - @override - @pragma('vm:prefer-inline') - _$$UserModelV3CopyWith<_$UserModelV3> get copyWith => - __$$UserModelV3CopyWithImpl<_$UserModelV3>(this, _$identity); - - @override - @optionalTypeArgs - TResult when({ - required TResult Function( - @HiveField(0) int id, - @HiveField(1) String username, - @HiveField(2) String email, - @HiveField(3) String? firstName, - @HiveField(4) String? lastName, - @HiveField(5) DateTime? dateJoined, - @HiveField(6) bool isStaff, - @HiveField(7) bool isActive, - @HiveField(8) bool isSuperuser, - @HiveField(9) List groups, - @HiveField(10) List userPermissions, - @HiveField(11) List inheritedPermissions) - v3, - required TResult Function(@HiveField(0) @JsonKey(name: "user_id") int id, - @HiveField(1) String username, @HiveField(2) String? displayName) - v2, - }) { - return v3(id, username, email, firstName, lastName, dateJoined, isStaff, - isActive, isSuperuser, groups, userPermissions, inheritedPermissions); - } - - @override - @optionalTypeArgs - TResult? whenOrNull({ - TResult? Function( - @HiveField(0) int id, - @HiveField(1) String username, - @HiveField(2) String email, - @HiveField(3) String? firstName, - @HiveField(4) String? lastName, - @HiveField(5) DateTime? dateJoined, - @HiveField(6) bool isStaff, - @HiveField(7) bool isActive, - @HiveField(8) bool isSuperuser, - @HiveField(9) List groups, - @HiveField(10) List userPermissions, - @HiveField(11) List inheritedPermissions)? - v3, - TResult? Function(@HiveField(0) @JsonKey(name: "user_id") int id, - @HiveField(1) String username, @HiveField(2) String? displayName)? - v2, - }) { - return v3?.call( - id, - username, - email, - firstName, - lastName, - dateJoined, - isStaff, - isActive, - isSuperuser, - groups, - userPermissions, - inheritedPermissions); - } - - @override - @optionalTypeArgs - TResult maybeWhen({ - TResult Function( - @HiveField(0) int id, - @HiveField(1) String username, - @HiveField(2) String email, - @HiveField(3) String? firstName, - @HiveField(4) String? lastName, - @HiveField(5) DateTime? dateJoined, - @HiveField(6) bool isStaff, - @HiveField(7) bool isActive, - @HiveField(8) bool isSuperuser, - @HiveField(9) List groups, - @HiveField(10) List userPermissions, - @HiveField(11) List inheritedPermissions)? - v3, - TResult Function(@HiveField(0) @JsonKey(name: "user_id") int id, - @HiveField(1) String username, @HiveField(2) String? displayName)? - v2, - required TResult orElse(), - }) { - if (v3 != null) { - return v3(id, username, email, firstName, lastName, dateJoined, isStaff, - isActive, isSuperuser, groups, userPermissions, inheritedPermissions); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map({ - required TResult Function(UserModelV3 value) v3, - required TResult Function(UserModelV2 value) v2, - }) { - return v3(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull({ - TResult? Function(UserModelV3 value)? v3, - TResult? Function(UserModelV2 value)? v2, - }) { - return v3?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap({ - TResult Function(UserModelV3 value)? v3, - TResult Function(UserModelV2 value)? v2, - required TResult orElse(), - }) { - if (v3 != null) { - return v3(this); - } - return orElse(); - } - - @override - Map toJson() { - return _$$UserModelV3ToJson( - this, - ); - } -} - -abstract class UserModelV3 extends UserModel { - const factory UserModelV3( - {@HiveField(0) - required final int id, - @HiveField(1) - required final String username, - @HiveField(2) - required final String email, - @HiveField(3) - final String? firstName, - @HiveField(4) - final String? lastName, - @HiveField(5) - final DateTime? dateJoined, - @HiveField(6) - final bool isStaff, - @HiveField(7) - final bool isActive, - @HiveField(8) - final bool isSuperuser, - @HiveField(9) - final List groups, - @HiveField(10) - final List userPermissions, - @HiveField(11) - final List inheritedPermissions}) = - _$UserModelV3; - const UserModelV3._() : super._(); - - factory UserModelV3.fromJson(Map json) = - _$UserModelV3.fromJson; - - @override - @HiveField(0) - int get id; - @override - @HiveField(1) - String get username; - @HiveField(2) - String get email; - @HiveField(3) - String? get firstName; - @HiveField(4) - String? get lastName; - @HiveField(5) - DateTime? get dateJoined; - @HiveField(6) - bool get isStaff; - @HiveField(7) - bool get isActive; - @HiveField(8) - bool get isSuperuser; - @HiveField(9) - List get groups; - @HiveField(10) - List get userPermissions; - @HiveField(11) - List get inheritedPermissions; - @override - @JsonKey(ignore: true) - _$$UserModelV3CopyWith<_$UserModelV3> get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class _$$UserModelV2CopyWith<$Res> - implements $UserModelCopyWith<$Res> { - factory _$$UserModelV2CopyWith( - _$UserModelV2 value, $Res Function(_$UserModelV2) then) = - __$$UserModelV2CopyWithImpl<$Res>; - @override - @useResult - $Res call( - {@HiveField(0) @JsonKey(name: "user_id") int id, - @HiveField(1) String username, - @HiveField(2) String? displayName}); -} - -/// @nodoc -class __$$UserModelV2CopyWithImpl<$Res> - extends _$UserModelCopyWithImpl<$Res, _$UserModelV2> - implements _$$UserModelV2CopyWith<$Res> { - __$$UserModelV2CopyWithImpl( - _$UserModelV2 _value, $Res Function(_$UserModelV2) _then) - : super(_value, _then); - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? id = null, - Object? username = null, - Object? displayName = freezed, - }) { - return _then(_$UserModelV2( - id: null == id - ? _value.id - : id // ignore: cast_nullable_to_non_nullable - as int, - username: null == username - ? _value.username - : username // ignore: cast_nullable_to_non_nullable - as String, - displayName: freezed == displayName - ? _value.displayName - : displayName // ignore: cast_nullable_to_non_nullable - as String?, - )); - } -} - -/// @nodoc - -@JsonSerializable(fieldRename: FieldRename.snake) -@HiveType(typeId: PaperlessApiHiveTypeIds.userModelv2) -class _$UserModelV2 extends UserModelV2 { - const _$UserModelV2( - {@HiveField(0) @JsonKey(name: "user_id") required this.id, - @HiveField(1) required this.username, - @HiveField(2) this.displayName, - final String? $type}) - : $type = $type ?? 'v2', - super._(); - - factory _$UserModelV2.fromJson(Map json) => - _$$UserModelV2FromJson(json); - - @override - @HiveField(0) - @JsonKey(name: "user_id") - final int id; - @override - @HiveField(1) - final String username; - @override - @HiveField(2) - final String? displayName; - - @JsonKey(name: 'runtimeType') - final String $type; - - @override - String toString() { - return 'UserModel.v2(id: $id, username: $username, displayName: $displayName)'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$UserModelV2 && - (identical(other.id, id) || other.id == id) && - (identical(other.username, username) || - other.username == username) && - (identical(other.displayName, displayName) || - other.displayName == displayName)); - } - - @JsonKey(ignore: true) - @override - int get hashCode => Object.hash(runtimeType, id, username, displayName); - - @JsonKey(ignore: true) - @override - @pragma('vm:prefer-inline') - _$$UserModelV2CopyWith<_$UserModelV2> get copyWith => - __$$UserModelV2CopyWithImpl<_$UserModelV2>(this, _$identity); - - @override - @optionalTypeArgs - TResult when({ - required TResult Function( - @HiveField(0) int id, - @HiveField(1) String username, - @HiveField(2) String email, - @HiveField(3) String? firstName, - @HiveField(4) String? lastName, - @HiveField(5) DateTime? dateJoined, - @HiveField(6) bool isStaff, - @HiveField(7) bool isActive, - @HiveField(8) bool isSuperuser, - @HiveField(9) List groups, - @HiveField(10) List userPermissions, - @HiveField(11) List inheritedPermissions) - v3, - required TResult Function(@HiveField(0) @JsonKey(name: "user_id") int id, - @HiveField(1) String username, @HiveField(2) String? displayName) - v2, - }) { - return v2(id, username, displayName); - } - - @override - @optionalTypeArgs - TResult? whenOrNull({ - TResult? Function( - @HiveField(0) int id, - @HiveField(1) String username, - @HiveField(2) String email, - @HiveField(3) String? firstName, - @HiveField(4) String? lastName, - @HiveField(5) DateTime? dateJoined, - @HiveField(6) bool isStaff, - @HiveField(7) bool isActive, - @HiveField(8) bool isSuperuser, - @HiveField(9) List groups, - @HiveField(10) List userPermissions, - @HiveField(11) List inheritedPermissions)? - v3, - TResult? Function(@HiveField(0) @JsonKey(name: "user_id") int id, - @HiveField(1) String username, @HiveField(2) String? displayName)? - v2, - }) { - return v2?.call(id, username, displayName); - } - - @override - @optionalTypeArgs - TResult maybeWhen({ - TResult Function( - @HiveField(0) int id, - @HiveField(1) String username, - @HiveField(2) String email, - @HiveField(3) String? firstName, - @HiveField(4) String? lastName, - @HiveField(5) DateTime? dateJoined, - @HiveField(6) bool isStaff, - @HiveField(7) bool isActive, - @HiveField(8) bool isSuperuser, - @HiveField(9) List groups, - @HiveField(10) List userPermissions, - @HiveField(11) List inheritedPermissions)? - v3, - TResult Function(@HiveField(0) @JsonKey(name: "user_id") int id, - @HiveField(1) String username, @HiveField(2) String? displayName)? - v2, - required TResult orElse(), - }) { - if (v2 != null) { - return v2(id, username, displayName); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map({ - required TResult Function(UserModelV3 value) v3, - required TResult Function(UserModelV2 value) v2, - }) { - return v2(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull({ - TResult? Function(UserModelV3 value)? v3, - TResult? Function(UserModelV2 value)? v2, - }) { - return v2?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap({ - TResult Function(UserModelV3 value)? v3, - TResult Function(UserModelV2 value)? v2, - required TResult orElse(), - }) { - if (v2 != null) { - return v2(this); - } - return orElse(); - } - - @override - Map toJson() { - return _$$UserModelV2ToJson( - this, - ); - } -} - -abstract class UserModelV2 extends UserModel { - const factory UserModelV2( - {@HiveField(0) @JsonKey(name: "user_id") required final int id, - @HiveField(1) required final String username, - @HiveField(2) final String? displayName}) = _$UserModelV2; - const UserModelV2._() : super._(); - - factory UserModelV2.fromJson(Map json) = - _$UserModelV2.fromJson; - - @override - @HiveField(0) - @JsonKey(name: "user_id") - int get id; - @override - @HiveField(1) - String get username; - @HiveField(2) - String? get displayName; - @override - @JsonKey(ignore: true) - _$$UserModelV2CopyWith<_$UserModelV2> get copyWith => - throw _privateConstructorUsedError; -} From 72f61c7866b38ebe47b69ebaee9b715889a03981 Mon Sep 17 00:00:00 2001 From: Anton Stubenbord Date: Fri, 12 May 2023 12:01:09 +0200 Subject: [PATCH 17/26] fix: Override dependency constraint for graph to make codegen work again --- packages/paperless_api/pubspec.yaml | 5 ++++- pubspec.lock | 2 +- pubspec.yaml | 2 ++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/paperless_api/pubspec.yaml b/packages/paperless_api/pubspec.yaml index 99afca5..09a6990 100644 --- a/packages/paperless_api/pubspec.yaml +++ b/packages/paperless_api/pubspec.yaml @@ -22,7 +22,10 @@ dependencies: jiffy: ^5.0.0 freezed_annotation: ^2.2.0 hive: ^2.2.3 - + +dependency_overrides: + graphs: 2.2.0 + dev_dependencies: flutter_test: sdk: flutter diff --git a/pubspec.lock b/pubspec.lock index 149c0a6..b0a0506 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -753,7 +753,7 @@ packages: source: hosted version: "2.1.1" graphs: - dependency: transitive + dependency: "direct overridden" description: name: graphs sha256: f9e130f3259f52d26f0cfc0e964513796dafed572fa52e45d2f8d6ca14db39b2 diff --git a/pubspec.yaml b/pubspec.yaml index f4b76ab..8707f17 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -95,6 +95,8 @@ dependencies: dependency_overrides: intl: ^0.18.0 + graphs: 2.2.0 + dev_dependencies: integration_test: From 2fad76c697fb5bb25d7e5ccce66bb700e053a045 Mon Sep 17 00:00:00 2001 From: Anton Stubenbord Date: Fri, 12 May 2023 13:00:56 +0200 Subject: [PATCH 18/26] fix: Add providers to bulk edit routes --- lib/core/navigation/push_routes.dart | 168 ++++++++++++++++++ lib/features/app_drawer/view/app_drawer.dart | 1 - .../documents/cubit/documents_state.dart | 3 +- .../documents/view/pages/documents_page.dart | 2 +- .../document_selection_sliver_app_bar.dart | 153 +--------------- lib/features/home/view/home_route.dart | 17 +- 6 files changed, 188 insertions(+), 156 deletions(-) diff --git a/lib/core/navigation/push_routes.dart b/lib/core/navigation/push_routes.dart index fc3e9d6..fbae8a3 100644 --- a/lib/core/navigation/push_routes.dart +++ b/lib/core/navigation/push_routes.dart @@ -10,6 +10,9 @@ import 'package:paperless_mobile/core/database/tables/local_user_app_state.dart' import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart'; import 'package:paperless_mobile/core/repository/label_repository.dart'; import 'package:paperless_mobile/core/repository/user_repository.dart'; +import 'package:paperless_mobile/features/document_bulk_action/cubit/document_bulk_action_cubit.dart'; +import 'package:paperless_mobile/features/document_bulk_action/view/widgets/fullscreen_bulk_edit_label_page.dart'; +import 'package:paperless_mobile/features/document_bulk_action/view/widgets/fullscreen_bulk_edit_tags_widget.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'; @@ -20,6 +23,7 @@ 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/generated/l10n/app_localizations.dart'; import 'package:paperless_mobile/routes/document_details_route.dart'; import 'package:provider/provider.dart'; @@ -161,3 +165,167 @@ Future pushLinkedDocumentsView(BuildContext context, {required DocumentFil ), ); } + +Future pushBulkEditCorrespondentRoute( + BuildContext context, { + required List selection, +}) { + return Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => MultiProvider( + providers: [ + ..._getRequiredBulkEditProviders(context), + ], + builder: (_, __) => BlocProvider( + create: (_) => DocumentBulkActionCubit( + context.read(), + context.read(), + context.read(), + selection: selection, + ), + child: BlocBuilder( + builder: (context, state) { + return FullscreenBulkEditLabelPage( + options: state.correspondents, + selection: state.selection, + labelMapper: (document) => document.correspondent, + leadingIcon: const Icon(Icons.person_outline), + hintText: S.of(context)!.startTyping, + onSubmit: context.read().bulkModifyCorrespondent, + assignMessageBuilder: (int count, String name) { + return S.of(context)!.bulkEditCorrespondentAssignMessage( + name, + count, + ); + }, + removeMessageBuilder: (int count) { + return S.of(context)!.bulkEditCorrespondentRemoveMessage(count); + }, + ); + }, + ), + ), + ), + ), + ); +} + +Future pushBulkEditStoragePathRoute( + BuildContext context, { + required List selection, +}) { + return Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => MultiProvider( + providers: [ + ..._getRequiredBulkEditProviders(context), + ], + builder: (_, __) => BlocProvider( + create: (_) => DocumentBulkActionCubit( + context.read(), + context.read(), + context.read(), + selection: selection, + ), + child: BlocBuilder( + builder: (context, state) { + return FullscreenBulkEditLabelPage( + options: state.storagePaths, + selection: state.selection, + labelMapper: (document) => document.storagePath, + leadingIcon: const Icon(Icons.folder_outlined), + hintText: S.of(context)!.startTyping, + onSubmit: context.read().bulkModifyStoragePath, + assignMessageBuilder: (int count, String name) { + return S.of(context)!.bulkEditStoragePathAssignMessage( + count, + name, + ); + }, + removeMessageBuilder: (int count) { + return S.of(context)!.bulkEditStoragePathRemoveMessage(count); + }, + ); + }, + ), + ), + ), + ), + ); +} + +Future pushBulkEditTagsRoute( + BuildContext context, { + required List selection, +}) { + return Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => MultiProvider( + providers: [ + ..._getRequiredBulkEditProviders(context), + ], + builder: (_, __) => BlocProvider( + create: (_) => DocumentBulkActionCubit( + context.read(), + context.read(), + context.read(), + selection: selection, + ), + child: Builder(builder: (context) { + return const FullscreenBulkEditTagsWidget(); + }), + ), + ), + ), + ); +} + +Future pushBulkEditDocumentTypeRoute(BuildContext context, + {required List selection}) { + return Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => MultiProvider( + providers: [ + ..._getRequiredBulkEditProviders(context), + ], + builder: (_, __) => BlocProvider( + create: (_) => DocumentBulkActionCubit( + context.read(), + context.read(), + context.read(), + selection: selection, + ), + child: BlocBuilder( + builder: (context, state) { + return FullscreenBulkEditLabelPage( + options: state.documentTypes, + selection: state.selection, + labelMapper: (document) => document.documentType, + leadingIcon: const Icon(Icons.description_outlined), + hintText: S.of(context)!.startTyping, + onSubmit: context.read().bulkModifyDocumentType, + assignMessageBuilder: (int count, String name) { + return S.of(context)!.bulkEditDocumentTypeAssignMessage( + count, + name, + ); + }, + removeMessageBuilder: (int count) { + return S.of(context)!.bulkEditDocumentTypeRemoveMessage(count); + }, + ); + }, + ), + ), + ), + ), + ); +} + +List _getRequiredBulkEditProviders(BuildContext context) { + return [ + Provider.value(value: context.read()), + Provider.value(value: context.read()), + Provider.value(value: context.read()), + ]; +} diff --git a/lib/features/app_drawer/view/app_drawer.dart b/lib/features/app_drawer/view/app_drawer.dart index 103e314..fb58374 100644 --- a/lib/features/app_drawer/view/app_drawer.dart +++ b/lib/features/app_drawer/view/app_drawer.dart @@ -17,7 +17,6 @@ class AppDrawer extends StatelessWidget { @override Widget build(BuildContext context) { return SafeArea( - top: true, child: Drawer( child: Column( children: [ diff --git a/lib/features/documents/cubit/documents_state.dart b/lib/features/documents/cubit/documents_state.dart index 516cc02..527d4d3 100644 --- a/lib/features/documents/cubit/documents_state.dart +++ b/lib/features/documents/cubit/documents_state.dart @@ -83,8 +83,7 @@ class DocumentsState extends DocumentPagingState { ); } - factory DocumentsState.fromJson(Map json) => - _$DocumentsStateFromJson(json); + factory DocumentsState.fromJson(Map json) => _$DocumentsStateFromJson(json); Map toJson() => _$DocumentsStateToJson(this); } diff --git a/lib/features/documents/view/pages/documents_page.dart b/lib/features/documents/view/pages/documents_page.dart index 75452b2..3e2199a 100644 --- a/lib/features/documents/view/pages/documents_page.dart +++ b/lib/features/documents/view/pages/documents_page.dart @@ -107,7 +107,7 @@ class _DocumentsPageState extends State with SingleTickerProvider }, builder: (context, connectivityState) { return SafeArea( - top: context.read().state.selection.isEmpty, + top: context.watch().state.selection.isEmpty, child: Scaffold( drawer: const AppDrawer(), floatingActionButton: BlocBuilder( diff --git a/lib/features/documents/view/widgets/selection/document_selection_sliver_app_bar.dart b/lib/features/documents/view/widgets/selection/document_selection_sliver_app_bar.dart index b3513de..30f67a5 100644 --- a/lib/features/documents/view/widgets/selection/document_selection_sliver_app_bar.dart +++ b/lib/features/documents/view/widgets/selection/document_selection_sliver_app_bar.dart @@ -1,10 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:paperless_api/paperless_api.dart'; +import 'package:paperless_mobile/core/navigation/push_routes.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.dart'; -import 'package:paperless_mobile/features/document_bulk_action/cubit/document_bulk_action_cubit.dart'; -import 'package:paperless_mobile/features/document_bulk_action/view/widgets/fullscreen_bulk_edit_label_page.dart'; -import 'package:paperless_mobile/features/document_bulk_action/view/widgets/fullscreen_bulk_edit_tags_widget.dart'; import 'package:paperless_mobile/features/documents/cubit/documents_cubit.dart'; import 'package:paperless_mobile/features/documents/view/widgets/selection/bulk_delete_confirmation_dialog.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; @@ -35,15 +33,12 @@ class DocumentSelectionSliverAppBar extends StatelessWidget { onPressed: () async { final shouldDelete = await showDialog( context: context, - builder: (context) => - BulkDeleteConfirmationDialog(state: state), + builder: (context) => BulkDeleteConfirmationDialog(state: state), ) ?? false; if (shouldDelete) { try { - await context - .read() - .bulkDelete(state.selection); + await context.read().bulkDelete(state.selection); showSnackBar( context, S.of(context)!.documentsSuccessfullyDeleted, @@ -66,140 +61,22 @@ class DocumentSelectionSliverAppBar extends StatelessWidget { ActionChip( label: Text(S.of(context)!.correspondent), avatar: const Icon(Icons.edit), - onPressed: () async { - await Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => BlocProvider( - create: (context) => DocumentBulkActionCubit( - context.read(), - context.read(), - context.read(), - selection: state.selection, - ), - child: BlocBuilder( - builder: (context, state) { - return FullscreenBulkEditLabelPage( - options: state.correspondents, - selection: state.selection, - labelMapper: (document) => document.correspondent, - leadingIcon: const Icon(Icons.person_outline), - hintText: S.of(context)!.startTyping, - onSubmit: context - .read() - .bulkModifyCorrespondent, - assignMessageBuilder: (int count, String name) { - return S - .of(context)! - .bulkEditCorrespondentAssignMessage( - name, - count, - ); - }, - removeMessageBuilder: (int count) { - return S - .of(context)! - .bulkEditCorrespondentRemoveMessage(count); - }, - ); - }, - ), - ), - ), - ); + onPressed: () { + pushBulkEditCorrespondentRoute(context, selection: state.selection); }, ).paddedOnly(left: 8, right: 4), ActionChip( label: Text(S.of(context)!.documentType), avatar: const Icon(Icons.edit), onPressed: () async { - await Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => BlocProvider( - create: (context) => DocumentBulkActionCubit( - context.read(), - context.read(), - context.read(), - selection: state.selection, - ), - child: BlocBuilder( - builder: (context, state) { - return FullscreenBulkEditLabelPage( - options: state.documentTypes, - selection: state.selection, - labelMapper: (document) => document.documentType, - leadingIcon: - const Icon(Icons.description_outlined), - hintText: S.of(context)!.startTyping, - onSubmit: context - .read() - .bulkModifyDocumentType, - assignMessageBuilder: (int count, String name) { - return S - .of(context)! - .bulkEditDocumentTypeAssignMessage( - count, - name, - ); - }, - removeMessageBuilder: (int count) { - return S - .of(context)! - .bulkEditDocumentTypeRemoveMessage(count); - }, - ); - }, - ), - ), - ), - ); + pushBulkEditDocumentTypeRoute(context, selection: state.selection); }, ).paddedOnly(left: 8, right: 4), ActionChip( label: Text(S.of(context)!.storagePath), avatar: const Icon(Icons.edit), onPressed: () async { - await Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => BlocProvider( - create: (context) => DocumentBulkActionCubit( - context.read(), - context.read(), - context.read(), - selection: state.selection, - ), - child: BlocBuilder( - builder: (context, state) { - return FullscreenBulkEditLabelPage( - options: state.storagePaths, - selection: state.selection, - labelMapper: (document) => document.storagePath, - leadingIcon: const Icon(Icons.folder_outlined), - hintText: S.of(context)!.startTyping, - onSubmit: context - .read() - .bulkModifyStoragePath, - assignMessageBuilder: (int count, String name) { - return S - .of(context)! - .bulkEditStoragePathAssignMessage( - count, - name, - ); - }, - removeMessageBuilder: (int count) { - return S - .of(context)! - .bulkEditStoragePathRemoveMessage(count); - }, - ); - }, - ), - ), - ), - ); + pushBulkEditStoragePathRoute(context, selection: state.selection); }, ).paddedOnly(left: 8, right: 4), _buildBulkEditTagsChip(context).paddedOnly(left: 4, right: 4), @@ -215,21 +92,7 @@ class DocumentSelectionSliverAppBar extends StatelessWidget { label: Text(S.of(context)!.tags), avatar: const Icon(Icons.edit), onPressed: () { - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => BlocProvider( - create: (context) => DocumentBulkActionCubit( - context.read(), - context.read(), - context.read(), - selection: state.selection, - ), - child: Builder(builder: (context) { - return const FullscreenBulkEditTagsWidget(); - }), - ), - ), - ); + pushBulkEditTagsRoute(context, selection: state.selection); }, ); } diff --git a/lib/features/home/view/home_route.dart b/lib/features/home/view/home_route.dart index 0324c3c..03be9a3 100644 --- a/lib/features/home/view/home_route.dart +++ b/lib/features/home/view/home_route.dart @@ -109,13 +109,16 @@ class HomeRoute extends StatelessWidget { providers: [ ProxyProvider3( - update: (context, docApi, notifier, labelRepo, previous) => DocumentsCubit( - docApi, - notifier, - labelRepo, - Hive.box(HiveBoxes.localUserAppState) - .get(currentLocalUserId)!, - )..reload(), + update: (context, docApi, notifier, labelRepo, previous) { + print("UPDATED DOCUMENT CUBIT"); //TODO: REMOVE + return DocumentsCubit( + docApi, + notifier, + labelRepo, + Hive.box(HiveBoxes.localUserAppState) + .get(currentLocalUserId)!, + )..reload(); + }, ), Provider(create: (context) => DocumentScannerCubit()), ProxyProvider4 Date: Fri, 12 May 2023 13:18:49 +0200 Subject: [PATCH 19/26] fix: Fix dependents error when switching accounts --- .../document_search/view/sliver_search_bar.dart | 5 +++-- lib/features/home/view/home_route.dart | 17 +++++++---------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/lib/features/document_search/view/sliver_search_bar.dart b/lib/features/document_search/view/sliver_search_bar.dart index ae29581..044dbdc 100644 --- a/lib/features/document_search/view/sliver_search_bar.dart +++ b/lib/features/document_search/view/sliver_search_bar.dart @@ -56,10 +56,11 @@ class SliverSearchBar extends StatelessWidget { }, ), onPressed: () { + final apiVersion = context.read(); showDialog( context: context, - builder: (_) => Provider.value( - value: context.read(), + builder: (context) => Provider.value( + value: apiVersion, child: const ManageAccountsPage(), ), ); diff --git a/lib/features/home/view/home_route.dart b/lib/features/home/view/home_route.dart index 03be9a3..0324c3c 100644 --- a/lib/features/home/view/home_route.dart +++ b/lib/features/home/view/home_route.dart @@ -109,16 +109,13 @@ class HomeRoute extends StatelessWidget { providers: [ ProxyProvider3( - update: (context, docApi, notifier, labelRepo, previous) { - print("UPDATED DOCUMENT CUBIT"); //TODO: REMOVE - return DocumentsCubit( - docApi, - notifier, - labelRepo, - Hive.box(HiveBoxes.localUserAppState) - .get(currentLocalUserId)!, - )..reload(); - }, + update: (context, docApi, notifier, labelRepo, previous) => DocumentsCubit( + docApi, + notifier, + labelRepo, + Hive.box(HiveBoxes.localUserAppState) + .get(currentLocalUserId)!, + )..reload(), ), Provider(create: (context) => DocumentScannerCubit()), ProxyProvider4 Date: Fri, 12 May 2023 15:15:44 +0200 Subject: [PATCH 20/26] fix: Remove type from inherited permissions --- .../lib/config/hive/hive_type_ids.dart | 1 - .../permissions/inherited_permissions.dart | 478 +++++++++--------- .../lib/src/models/user_model.dart | 43 +- 3 files changed, 254 insertions(+), 268 deletions(-) diff --git a/packages/paperless_api/lib/config/hive/hive_type_ids.dart b/packages/paperless_api/lib/config/hive/hive_type_ids.dart index d08224a..3568647 100644 --- a/packages/paperless_api/lib/config/hive/hive_type_ids.dart +++ b/packages/paperless_api/lib/config/hive/hive_type_ids.dart @@ -55,7 +55,6 @@ void registerPaperlessApiHiveTypeAdapters() { // Users and permissions Hive.registerAdapter(UserModelV3Adapter()); Hive.registerAdapter(UserModelV2Adapter()); - Hive.registerAdapter(InheritedPermissionsAdapter()); Hive.registerAdapter(GroupModelAdapter()); Hive.registerAdapter(PermissionsAdapter()); } diff --git a/packages/paperless_api/lib/src/models/permissions/inherited_permissions.dart b/packages/paperless_api/lib/src/models/permissions/inherited_permissions.dart index dc8a272..4bb13ae 100644 --- a/packages/paperless_api/lib/src/models/permissions/inherited_permissions.dart +++ b/packages/paperless_api/lib/src/models/permissions/inherited_permissions.dart @@ -1,237 +1,243 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; -import 'package:hive/hive.dart'; -import 'package:paperless_api/config/hive/hive_type_ids.dart'; - -part 'inherited_permissions.g.dart'; - -@HiveType(typeId: PaperlessApiHiveTypeIds.inheritedPermissions) -@JsonEnum(valueField: "value") -enum InheritedPermissions { - @HiveField(0) - adminAddLogentry("admin.add_logentry"), - @HiveField(1) - adminChangeLogentry("admin.change_logentry"), - @HiveField(2) - adminDeleteLogentry("admin.delete_logentry"), - @HiveField(3) - adminViewLogentry("admin.view_logentry"), - @HiveField(4) - authAddGroup("auth.add_group"), - @HiveField(5) - authAddPermission("auth.add_permission"), - @HiveField(6) - authAddUser("auth.add_user"), - @HiveField(7) - authChangeGroup("auth.change_group"), - @HiveField(8) - authChangePermission("auth.change_permission"), - @HiveField(9) - authChangeUser("auth.change_user"), - @HiveField(10) - authDeleteGroup("auth.delete_group"), - @HiveField(11) - authDeletePermission("auth.delete_permission"), - @HiveField(12) - authDeleteUser("auth.delete_user"), - @HiveField(13) - authViewGroup("auth.view_group"), - @HiveField(14) - authViewPermission("auth.view_permission"), - @HiveField(15) - authViewUser("auth.view_user"), - @HiveField(16) - authtokenAddToken("authtoken.add_token"), - @HiveField(17) - authtokenAddTokenproxy("authtoken.add_tokenproxy"), - @HiveField(18) - authtokenChangeToken("authtoken.change_token"), - @HiveField(19) - authtokenChangeTokenproxy("authtoken.change_tokenproxy"), - @HiveField(20) - authtokenDeleteToken("authtoken.delete_token"), - @HiveField(21) - authtokenDeleteTokenproxy("authtoken.delete_tokenproxy"), - @HiveField(22) - authtokenViewToken("authtoken.view_token"), - @HiveField(23) - authtokenViewTokenproxy("authtoken.view_tokenproxy"), - @HiveField(24) - contenttypesAddContenttype("contenttypes.add_contenttype"), - @HiveField(25) - contenttypesChangeContenttype("contenttypes.change_contenttype"), - @HiveField(26) - contenttypesDeleteContenttype("contenttypes.delete_contenttype"), - @HiveField(27) - contenttypesViewContenttype("contenttypes.view_contenttype"), - @HiveField(28) - djangoCeleryResultsAddChordcounter("django_celery_results.add_chordcounter"), - @HiveField(29) - djangoCeleryResultsAddGroupresult("django_celery_results.add_groupresult"), - @HiveField(30) - djangoCeleryResultsAddTaskresult("django_celery_results.add_taskresult"), - @HiveField(31) - djangoCeleryResultsChangeChordcounter("django_celery_results.change_chordcounter"), - @HiveField(32) - djangoCeleryResultsChangeGroupresult("django_celery_results.change_groupresult"), - @HiveField(33) - djangoCeleryResultsChangeTaskresult("django_celery_results.change_taskresult"), - @HiveField(34) - djangoCeleryResultsDeleteChordcounter("django_celery_results.delete_chordcounter"), - @HiveField(35) - djangoCeleryResultsDeleteGroupresult("django_celery_results.delete_groupresult"), - @HiveField(36) - djangoCeleryResultsDeleteTaskresult("django_celery_results.delete_taskresult"), - @HiveField(37) - djangoCeleryResultsViewChordcounter("django_celery_results.view_chordcounter"), - @HiveField(38) - djangoCeleryResultsViewGroupresult("django_celery_results.view_groupresult"), - @HiveField(39) - djangoCeleryResultsViewTaskresult("django_celery_results.view_taskresult"), - @HiveField(40) - documentsAddComment("documents.add_comment"), - @HiveField(41) - documentsAddCorrespondent("documents.add_correspondent"), - @HiveField(42) - documentsAddDocument("documents.add_document"), - @HiveField(43) - documentsAddDocumenttype("documents.add_documenttype"), - @HiveField(44) - documentsAddLog("documents.add_log"), - @HiveField(45) - documentsAddNote("documents.add_note"), - @HiveField(46) - documentsAddPaperlesstask("documents.add_paperlesstask"), - @HiveField(47) - documentsAddSavedview("documents.add_savedview"), - @HiveField(48) - documentsAddSavedviewfilterrule("documents.add_savedviewfilterrule"), - @HiveField(49) - documentsAddStoragepath("documents.add_storagepath"), - @HiveField(50) - documentsAddTag("documents.add_tag"), - @HiveField(51) - documentsAddUisettings("documents.add_uisettings"), - @HiveField(52) - documentsChangeComment("documents.change_comment"), - @HiveField(53) - documentsChangeCorrespondent("documents.change_correspondent"), - @HiveField(54) - documentsChangeDocument("documents.change_document"), - @HiveField(55) - documentsChangeDocumenttype("documents.change_documenttype"), - @HiveField(56) - documentsChangeLog("documents.change_log"), - @HiveField(57) - documentsChangeNote("documents.change_note"), - @HiveField(58) - documentsChangePaperlesstask("documents.change_paperlesstask"), - @HiveField(59) - documentsChangeSavedview("documents.change_savedview"), - @HiveField(60) - documentsChangeSavedviewfilterrule("documents.change_savedviewfilterrule"), - @HiveField(61) - documentsChangeStoragepath("documents.change_storagepath"), - @HiveField(111) - documentsChangeTag("documents.change_tag"), - @HiveField(62) - documentsChangeUisettings("documents.change_uisettings"), - @HiveField(63) - documentsDeleteComment("documents.delete_comment"), - @HiveField(64) - documentsDeleteCorrespondent("documents.delete_correspondent"), - @HiveField(65) - documentsDeleteDocument("documents.delete_document"), - @HiveField(66) - documentsDeleteDocumenttype("documents.delete_documenttype"), - @HiveField(67) - documentsDeleteLog("documents.delete_log"), - @HiveField(68) - documentsDeleteNote("documents.delete_note"), - @HiveField(69) - documentsDeletePaperlesstask("documents.delete_paperlesstask"), - @HiveField(70) - documentsDeleteSavedview("documents.delete_savedview"), - @HiveField(71) - documentsDeleteSavedviewfilterrule("documents.delete_savedviewfilterrule"), - @HiveField(72) - documentsDeleteStoragepath("documents.delete_storagepath"), - @HiveField(73) - documentsDeleteTag("documents.delete_tag"), - @HiveField(74) - documentsDeleteUisettings("documents.delete_uisettings"), - @HiveField(75) - documentsViewComment("documents.view_comment"), - @HiveField(76) - documentsViewCorrespondent("documents.view_correspondent"), - @HiveField(77) - documentsViewDocument("documents.view_document"), - @HiveField(78) - documentsViewDocumenttype("documents.view_documenttype"), - @HiveField(79) - documentsViewLog("documents.view_log"), - @HiveField(80) - documentsViewNote("documents.view_note"), - @HiveField(81) - documentsViewPaperlesstask("documents.view_paperlesstask"), - @HiveField(82) - documentsViewSavedview("documents.view_savedview"), - @HiveField(83) - documentsViewSavedviewfilterrule("documents.view_savedviewfilterrule"), - @HiveField(84) - documentsViewStoragepath("documents.view_storagepath"), - @HiveField(85) - documentsViewTag("documents.view_tag"), - @HiveField(86) - documentsViewUisettings("documents.view_uisettings"), - @HiveField(87) - guardianAddGroupobjectpermission("guardian.add_groupobjectpermission"), - @HiveField(88) - guardianAddUserobjectpermission("guardian.add_userobjectpermission"), - @HiveField(89) - guardianChangeGroupobjectpermission("guardian.change_groupobjectpermission"), - @HiveField(90) - guardianChangeUserobjectpermission("guardian.change_userobjectpermission"), - @HiveField(91) - guardianDeleteGroupobjectpermission("guardian.delete_groupobjectpermission"), - @HiveField(92) - guardianDeleteUserobjectpermission("guardian.delete_userobjectpermission"), - @HiveField(93) - guardianViewGroupobjectpermission("guardian.view_groupobjectpermission"), - @HiveField(94) - guardianViewUserobjectpermission("guardian.view_userobjectpermission"), - @HiveField(95) - paperlessMailAddMailaccount("paperless_mail.add_mailaccount"), - @HiveField(96) - paperlessMailAddMailrule("paperless_mail.add_mailrule"), - @HiveField(97) - paperlessMailAddProcessedmail("paperless_mail.add_processedmail"), - @HiveField(98) - paperlessMailChangeMailaccount("paperless_mail.change_mailaccount"), - @HiveField(99) - paperlessMailChangeMailrule("paperless_mail.change_mailrule"), - @HiveField(100) - paperlessMailChangeProcessedmail("paperless_mail.change_processedmail"), - @HiveField(101) - paperlessMailDeleteMailaccount("paperless_mail.delete_mailaccount"), - @HiveField(102) - paperlessMailDeleteMailrule("paperless_mail.delete_mailrule"), - @HiveField(103) - paperlessMailDeleteProcessedmail("paperless_mail.delete_processedmail"), - @HiveField(104) - paperlessMailViewMailaccount("paperless_mail.view_mailaccount"), - @HiveField(105) - paperlessMailViewMailrule("paperless_mail.view_mailrule"), - @HiveField(106) - paperlessMailViewProcessedmail("paperless_mail.view_processedmail"), - @HiveField(107) - sessionsAddSession("sessions.add_session"), - @HiveField(108) - sessionsChangeSession("sessions.change_session"), - @HiveField(109) - sessionsDeleteSession("sessions.delete_session"), - @HiveField(110) - sessionsViewSession("sessions.view_session"); - - const InheritedPermissions(this.value); - final String value; +enum InheritedPermissionGroup { + admin, + auth, + authtoken, + contenttypes, + djangoCeleryResults, + documents, + guardian, + paperlessMail, + sessions; } + +// @HiveType(typeId: PaperlessApiHiveTypeIds.inheritedPermissions) +// @JsonEnum(valueField: "value") +// enum InheritedPermissions { +// @HiveField(0) +// adminAddLogentry("admin.add_logentry"), +// @HiveField(1) +// adminChangeLogentry("admin.change_logentry"), +// @HiveField(2) +// adminDeleteLogentry("admin.delete_logentry"), +// @HiveField(3) +// adminViewLogentry("admin.view_logentry"), +// @HiveField(4) +// authAddGroup("auth.add_group"), +// @HiveField(5) +// authAddPermission("auth.add_permission"), +// @HiveField(6) +// authAddUser("auth.add_user"), +// @HiveField(7) +// authChangeGroup("auth.change_group"), +// @HiveField(8) +// authChangePermission("auth.change_permission"), +// @HiveField(9) +// authChangeUser("auth.change_user"), +// @HiveField(10) +// authDeleteGroup("auth.delete_group"), +// @HiveField(11) +// authDeletePermission("auth.delete_permission"), +// @HiveField(12) +// authDeleteUser("auth.delete_user"), +// @HiveField(13) +// authViewGroup("auth.view_group"), +// @HiveField(14) +// authViewPermission("auth.view_permission"), +// @HiveField(15) +// authViewUser("auth.view_user"), +// @HiveField(16) +// authtokenAddToken("authtoken.add_token"), +// @HiveField(17) +// authtokenAddTokenproxy("authtoken.add_tokenproxy"), +// @HiveField(18) +// authtokenChangeToken("authtoken.change_token"), +// @HiveField(19) +// authtokenChangeTokenproxy("authtoken.change_tokenproxy"), +// @HiveField(20) +// authtokenDeleteToken("authtoken.delete_token"), +// @HiveField(21) +// authtokenDeleteTokenproxy("authtoken.delete_tokenproxy"), +// @HiveField(22) +// authtokenViewToken("authtoken.view_token"), +// @HiveField(23) +// authtokenViewTokenproxy("authtoken.view_tokenproxy"), +// @HiveField(24) +// contenttypesAddContenttype("contenttypes.add_contenttype"), +// @HiveField(25) +// contenttypesChangeContenttype("contenttypes.change_contenttype"), +// @HiveField(26) +// contenttypesDeleteContenttype("contenttypes.delete_contenttype"), +// @HiveField(27) +// contenttypesViewContenttype("contenttypes.view_contenttype"), +// @HiveField(28) +// djangoCeleryResultsAddChordcounter("django_celery_results.add_chordcounter"), +// @HiveField(29) +// djangoCeleryResultsAddGroupresult("django_celery_results.add_groupresult"), +// @HiveField(30) +// djangoCeleryResultsAddTaskresult("django_celery_results.add_taskresult"), +// @HiveField(31) +// djangoCeleryResultsChangeChordcounter("django_celery_results.change_chordcounter"), +// @HiveField(32) +// djangoCeleryResultsChangeGroupresult("django_celery_results.change_groupresult"), +// @HiveField(33) +// djangoCeleryResultsChangeTaskresult("django_celery_results.change_taskresult"), +// @HiveField(34) +// djangoCeleryResultsDeleteChordcounter("django_celery_results.delete_chordcounter"), +// @HiveField(35) +// djangoCeleryResultsDeleteGroupresult("django_celery_results.delete_groupresult"), +// @HiveField(36) +// djangoCeleryResultsDeleteTaskresult("django_celery_results.delete_taskresult"), +// @HiveField(37) +// djangoCeleryResultsViewChordcounter("django_celery_results.view_chordcounter"), +// @HiveField(38) +// djangoCeleryResultsViewGroupresult("django_celery_results.view_groupresult"), +// @HiveField(39) +// djangoCeleryResultsViewTaskresult("django_celery_results.view_taskresult"), +// @HiveField(40) +// documentsAddComment("documents.add_comment"), +// @HiveField(41) +// documentsAddCorrespondent("documents.add_correspondent"), +// @HiveField(42) +// documentsAddDocument("documents.add_document"), +// @HiveField(43) +// documentsAddDocumenttype("documents.add_documenttype"), +// @HiveField(44) +// documentsAddLog("documents.add_log"), +// @HiveField(45) +// documentsAddNote("documents.add_note"), +// @HiveField(46) +// documentsAddPaperlesstask("documents.add_paperlesstask"), +// @HiveField(47) +// documentsAddSavedview("documents.add_savedview"), +// @HiveField(48) +// documentsAddSavedviewfilterrule("documents.add_savedviewfilterrule"), +// @HiveField(49) +// documentsAddStoragepath("documents.add_storagepath"), +// @HiveField(50) +// documentsAddTag("documents.add_tag"), +// @HiveField(51) +// documentsAddUisettings("documents.add_uisettings"), +// @HiveField(52) +// documentsChangeComment("documents.change_comment"), +// @HiveField(53) +// documentsChangeCorrespondent("documents.change_correspondent"), +// @HiveField(54) +// documentsChangeDocument("documents.change_document"), +// @HiveField(55) +// documentsChangeDocumenttype("documents.change_documenttype"), +// @HiveField(56) +// documentsChangeLog("documents.change_log"), +// @HiveField(57) +// documentsChangeNote("documents.change_note"), +// @HiveField(58) +// documentsChangePaperlesstask("documents.change_paperlesstask"), +// @HiveField(59) +// documentsChangeSavedview("documents.change_savedview"), +// @HiveField(60) +// documentsChangeSavedviewfilterrule("documents.change_savedviewfilterrule"), +// @HiveField(61) +// documentsChangeStoragepath("documents.change_storagepath"), +// @HiveField(111) +// documentsChangeTag("documents.change_tag"), +// @HiveField(62) +// documentsChangeUisettings("documents.change_uisettings"), +// @HiveField(63) +// documentsDeleteComment("documents.delete_comment"), +// @HiveField(64) +// documentsDeleteCorrespondent("documents.delete_correspondent"), +// @HiveField(65) +// documentsDeleteDocument("documents.delete_document"), +// @HiveField(66) +// documentsDeleteDocumenttype("documents.delete_documenttype"), +// @HiveField(67) +// documentsDeleteLog("documents.delete_log"), +// @HiveField(68) +// documentsDeleteNote("documents.delete_note"), +// @HiveField(69) +// documentsDeletePaperlesstask("documents.delete_paperlesstask"), +// @HiveField(70) +// documentsDeleteSavedview("documents.delete_savedview"), +// @HiveField(71) +// documentsDeleteSavedviewfilterrule("documents.delete_savedviewfilterrule"), +// @HiveField(72) +// documentsDeleteStoragepath("documents.delete_storagepath"), +// @HiveField(73) +// documentsDeleteTag("documents.delete_tag"), +// @HiveField(74) +// documentsDeleteUisettings("documents.delete_uisettings"), +// @HiveField(75) +// documentsViewComment("documents.view_comment"), +// @HiveField(76) +// documentsViewCorrespondent("documents.view_correspondent"), +// @HiveField(77) +// documentsViewDocument("documents.view_document"), +// @HiveField(78) +// documentsViewDocumenttype("documents.view_documenttype"), +// @HiveField(79) +// documentsViewLog("documents.view_log"), +// @HiveField(80) +// documentsViewNote("documents.view_note"), +// @HiveField(81) +// documentsViewPaperlesstask("documents.view_paperlesstask"), +// @HiveField(82) +// documentsViewSavedview("documents.view_savedview"), +// @HiveField(83) +// documentsViewSavedviewfilterrule("documents.view_savedviewfilterrule"), +// @HiveField(84) +// documentsViewStoragepath("documents.view_storagepath"), +// @HiveField(85) +// documentsViewTag("documents.view_tag"), +// @HiveField(86) +// documentsViewUisettings("documents.view_uisettings"), +// @HiveField(87) +// guardianAddGroupobjectpermission("guardian.add_groupobjectpermission"), +// @HiveField(88) +// guardianAddUserobjectpermission("guardian.add_userobjectpermission"), +// @HiveField(89) +// guardianChangeGroupobjectpermission("guardian.change_groupobjectpermission"), +// @HiveField(90) +// guardianChangeUserobjectpermission("guardian.change_userobjectpermission"), +// @HiveField(91) +// guardianDeleteGroupobjectpermission("guardian.delete_groupobjectpermission"), +// @HiveField(92) +// guardianDeleteUserobjectpermission("guardian.delete_userobjectpermission"), +// @HiveField(93) +// guardianViewGroupobjectpermission("guardian.view_groupobjectpermission"), +// @HiveField(94) +// guardianViewUserobjectpermission("guardian.view_userobjectpermission"), +// @HiveField(95) +// paperlessMailAddMailaccount("paperless_mail.add_mailaccount"), +// @HiveField(96) +// paperlessMailAddMailrule("paperless_mail.add_mailrule"), +// @HiveField(97) +// paperlessMailAddProcessedmail("paperless_mail.add_processedmail"), +// @HiveField(98) +// paperlessMailChangeMailaccount("paperless_mail.change_mailaccount"), +// @HiveField(99) +// paperlessMailChangeMailrule("paperless_mail.change_mailrule"), +// @HiveField(100) +// paperlessMailChangeProcessedmail("paperless_mail.change_processedmail"), +// @HiveField(101) +// paperlessMailDeleteMailaccount("paperless_mail.delete_mailaccount"), +// @HiveField(102) +// paperlessMailDeleteMailrule("paperless_mail.delete_mailrule"), +// @HiveField(103) +// paperlessMailDeleteProcessedmail("paperless_mail.delete_processedmail"), +// @HiveField(104) +// paperlessMailViewMailaccount("paperless_mail.view_mailaccount"), +// @HiveField(105) +// paperlessMailViewMailrule("paperless_mail.view_mailrule"), +// @HiveField(106) +// paperlessMailViewProcessedmail("paperless_mail.view_processedmail"), +// @HiveField(107) +// sessionsAddSession("sessions.add_session"), +// @HiveField(108) +// sessionsChangeSession("sessions.change_session"), +// @HiveField(109) +// sessionsDeleteSession("sessions.delete_session"), +// @HiveField(110) +// sessionsViewSession("sessions.view_session"); + +// const InheritedPermissions(this.value); +// final String value; +// } diff --git a/packages/paperless_api/lib/src/models/user_model.dart b/packages/paperless_api/lib/src/models/user_model.dart index 509b6a1..2d72ac0 100644 --- a/packages/paperless_api/lib/src/models/user_model.dart +++ b/packages/paperless_api/lib/src/models/user_model.dart @@ -3,7 +3,6 @@ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hive/hive.dart'; import 'package:paperless_api/config/hive/hive_type_ids.dart'; -import 'package:paperless_api/src/models/permissions/inherited_permissions.dart'; import 'package:paperless_api/src/models/permissions/user_permissions.dart'; part 'user_model.freezed.dart'; @@ -16,36 +15,18 @@ class UserModel with _$UserModel { @JsonSerializable(fieldRename: FieldRename.snake) @HiveType(typeId: PaperlessApiHiveTypeIds.userModelv3) const factory UserModel.v3({ - @HiveField(0) - required int id, - @HiveField(1) - required String username, - @HiveField(2) - required String email, - @HiveField(3) - String? firstName, - @HiveField(4) - String? lastName, - @HiveField(5) - DateTime? dateJoined, - @HiveField(6) - @Default(true) - bool isStaff, - @HiveField(7) - @Default(true) - bool isActive, - @HiveField(8) - @Default(true) - bool isSuperuser, - @HiveField(9) - @Default([]) - List groups, - @HiveField(10) - @Default([]) - List userPermissions, - @HiveField(11) - @Default(InheritedPermissions.values) - List inheritedPermissions, + @HiveField(0) required int id, + @HiveField(1) required String username, + @HiveField(2) required String email, + @HiveField(3) String? firstName, + @HiveField(4) String? lastName, + @HiveField(5) DateTime? dateJoined, + @HiveField(6) @Default(true) bool isStaff, + @HiveField(7) @Default(true) bool isActive, + @HiveField(8) @Default(true) bool isSuperuser, + @HiveField(9) @Default([]) List groups, + @HiveField(10) @Default([]) List userPermissions, + @HiveField(11) @Default([]) List inheritedPermissions, }) = UserModelV3; @JsonSerializable(fieldRename: FieldRename.snake) From 062c46ccd3ef812032bff974a1ef88a3b88686c1 Mon Sep 17 00:00:00 2001 From: Anton Stubenbord Date: Fri, 12 May 2023 15:16:06 +0200 Subject: [PATCH 21/26] feat: Bump version number --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 8707f17..f5d7080 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,7 +17,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 2.2.5+33 +version: 2.2.6+34 environment: sdk: ">=2.19.0 <4.0.0" From c9a6bc64b89f9f6f10c38066699519cb4cc4b0d8 Mon Sep 17 00:00:00 2001 From: Anton Stubenbord Date: Thu, 18 May 2023 18:23:47 +0200 Subject: [PATCH 22/26] feat:Update search --- .../cubit/document_search_state.dart | 18 -- .../view/document_search_bar.dart | 211 ++++++++++++++++++ .../view/document_search_page.dart | 5 - .../view/documents_search_app_bar.dart | 1 - .../view/sliver_search_bar.dart | 88 +++++--- .../documents/view/pages/documents_page.dart | 4 - .../view/widgets/adaptive_documents_view.dart | 45 ---- .../widgets/items/document_detailed_item.dart | 16 +- .../widgets/items/document_grid_item.dart | 25 ++- .../view/widgets/items/document_item.dart | 9 - .../widgets/items/document_list_item.dart | 23 +- .../view/linked_documents_page.dart | 5 - .../view/saved_view_details_page.dart | 4 - .../view/similar_documents_view.dart | 4 - .../paperless_api/lib/src/models/models.dart | 1 + .../user_permission_extension.dart | 14 ++ .../lib/src/models/user_model.dart | 31 +-- .../example/android/build.gradle | 2 +- .../example/lib/main.dart | 48 ++-- .../example/pubspec.lock | 40 ++++ .../paperless_document_scanner/pubspec.yaml | 1 + 21 files changed, 385 insertions(+), 210 deletions(-) create mode 100644 lib/features/document_search/view/document_search_bar.dart delete mode 100644 lib/features/document_search/view/documents_search_app_bar.dart create mode 100644 packages/paperless_api/lib/src/models/permissions/user_permission_extension.dart diff --git a/lib/features/document_search/cubit/document_search_state.dart b/lib/features/document_search/cubit/document_search_state.dart index 8855670..8a66296 100644 --- a/lib/features/document_search/cubit/document_search_state.dart +++ b/lib/features/document_search/cubit/document_search_state.dart @@ -7,18 +7,12 @@ enum SearchView { @JsonSerializable(ignoreUnannotated: true) class DocumentSearchState extends DocumentPagingState { - @JsonKey() final List searchHistory; final SearchView view; final List suggestions; @JsonKey() final ViewType viewType; - final Map correspondents; - final Map documentTypes; - final Map tags; - final Map storagePaths; - const DocumentSearchState({ this.view = SearchView.suggestions, this.searchHistory = const [], @@ -28,10 +22,6 @@ class DocumentSearchState extends DocumentPagingState { super.hasLoaded, super.isLoading, super.value, - this.correspondents = const {}, - this.documentTypes = const {}, - this.tags = const {}, - this.storagePaths = const {}, }); @override @@ -41,10 +31,6 @@ class DocumentSearchState extends DocumentPagingState { suggestions, view, viewType, - correspondents, - documentTypes, - tags, - storagePaths, ]; @override @@ -85,10 +71,6 @@ class DocumentSearchState extends DocumentPagingState { view: view ?? this.view, suggestions: suggestions ?? this.suggestions, viewType: viewType ?? this.viewType, - correspondents: correspondents ?? this.correspondents, - documentTypes: documentTypes ?? this.documentTypes, - tags: tags ?? this.tags, - storagePaths: storagePaths ?? this.storagePaths, ); } diff --git a/lib/features/document_search/view/document_search_bar.dart b/lib/features/document_search/view/document_search_bar.dart new file mode 100644 index 0000000..2d4f118 --- /dev/null +++ b/lib/features/document_search/view/document_search_bar.dart @@ -0,0 +1,211 @@ +import 'dart:async'; +import 'dart:math' as math; + +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/local_user_account.dart'; +import 'package:paperless_mobile/core/navigation/push_routes.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'; +import 'package:paperless_mobile/features/documents/view/widgets/adaptive_documents_view.dart'; +import 'package:paperless_mobile/features/documents/view/widgets/selection/view_type_selection_widget.dart'; +import 'package:paperless_mobile/features/home/view/model/api_version.dart'; +import 'package:paperless_mobile/features/settings/model/view_type.dart'; +import 'package:paperless_mobile/features/settings/view/manage_accounts_page.dart'; +import 'package:paperless_mobile/features/settings/view/widgets/global_settings_builder.dart'; +import 'package:paperless_mobile/features/settings/view/widgets/user_avatar.dart'; +import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; +import 'package:provider/provider.dart'; + +class DocumentSearchBar extends StatefulWidget { + const DocumentSearchBar({super.key}); + + @override + State createState() => _DocumentSearchBarState(); +} + +class _DocumentSearchBarState extends State { + Timer? _debounceTimer; + + final _controller = SearchController(); + + @override + void initState() { + super.initState(); + _controller.addListener(() { + _debounceTimer?.cancel(); + _debounceTimer = Timer(const Duration(milliseconds: 500), () { + context.read().suggest(query); + }); + }); + } + + late final DocumentSearchCubit _searchCubit; + String get query => _controller.text; + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + _searchCubit = context.watch(); + } + + @override + Widget build(BuildContext context) { + return SearchAnchor.bar( + searchController: _controller, + barLeading: IconButton( + icon: const Icon(Icons.menu), + onPressed: Scaffold.of(context).openDrawer, + ), + barHintText: S.of(context)!.searchDocuments, + barTrailing: [ + IconButton( + icon: GlobalSettingsBuilder( + builder: (context, settings) { + return ValueListenableBuilder( + valueListenable: + Hive.box(HiveBoxes.localUserAccount).listenable(), + builder: (context, box, _) { + final account = box.get(settings.currentLoggedInUser!)!; + return UserAvatar( + userId: settings.currentLoggedInUser!, + account: account, + ); + }, + ); + }, + ), + onPressed: () { + final apiVersion = context.read(); + showDialog( + context: context, + builder: (context) => Provider.value( + value: apiVersion, + child: const ManageAccountsPage(), + ), + ); + }, + ), + ], + suggestionsBuilder: (context, controller) { + switch (_searchCubit.state.view) { + case SearchView.suggestions: + return _buildSuggestionItems(_searchCubit.state); + case SearchView.results: + // TODO: Handle this case. + break; + } + }, + ); + } + + Iterable _buildSuggestionItems(DocumentSearchState state) sync* { + final suggestions = + state.suggestions.whereNot((element) => state.searchHistory.contains(element)); + final historyMatches = state.searchHistory.where((element) => element.startsWith(query)); + for (var match in historyMatches.take(5)) { + yield ListTile( + title: Text(match), + leading: const Icon(Icons.history), + onLongPress: () => _onDeleteHistoryEntry(match), + onTap: () => _selectSuggestion(match), + trailing: _buildInsertSuggestionButton(match), + ); + } + + for (var suggestion in suggestions) { + yield ListTile( + title: Text(suggestion), + leading: const Icon(Icons.search), + onTap: () => _selectSuggestion(suggestion), + trailing: _buildInsertSuggestionButton(suggestion), + ); + } + } + + void _onDeleteHistoryEntry(String entry) async { + final shouldRemove = await showDialog( + context: context, + builder: (context) => RemoveHistoryEntryDialog(entry: entry), + ) ?? + false; + if (shouldRemove) { + context.read().removeHistoryEntry(entry); + } + } + + Widget _buildInsertSuggestionButton(String suggestion) { + return Transform( + alignment: Alignment.center, + transform: Matrix4.rotationY(math.pi), + child: IconButton( + icon: const Icon(Icons.arrow_outward), + onPressed: () { + _controller.text = '$suggestion '; + _controller.selection = TextSelection.fromPosition( + TextPosition(offset: _controller.text.length), + ); + }, + ), + ); + } + + Widget _buildResultsView(DocumentSearchState state) { + final header = Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + S.of(context)!.results, + style: Theme.of(context).textTheme.bodySmall, + ), + BlocBuilder( + builder: (context, state) { + return ViewTypeSelectionWidget( + viewType: state.viewType, + onChanged: (type) => context.read().updateViewType(type), + ); + }, + ) + ], + ).padded(); + return CustomScrollView( + slivers: [ + SliverToBoxAdapter(child: header), + if (state.hasLoaded && !state.isLoading && state.documents.isEmpty) + SliverToBoxAdapter( + child: Center( + child: Text(S.of(context)!.noMatchesFound), + ), + ) + else + SliverAdaptiveDocumentsView( + viewType: state.viewType, + documents: state.documents, + hasInternetConnection: true, + isLabelClickable: false, + isLoading: state.isLoading, + hasLoaded: state.hasLoaded, + enableHeroAnimation: false, + onTap: (document) { + pushDocumentDetailsRoute( + context, + document: document, + isLabelClickable: false, + ); + }, + ) + ], + ); + } + + void _selectSuggestion(String suggestion) { + _controller.text = suggestion; + context.read().search(suggestion); + FocusScope.of(context).unfocus(); + } +} diff --git a/lib/features/document_search/view/document_search_page.dart b/lib/features/document_search/view/document_search_page.dart index 131393d..a0ec9ee 100644 --- a/lib/features/document_search/view/document_search_page.dart +++ b/lib/features/document_search/view/document_search_page.dart @@ -13,7 +13,6 @@ import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; import 'dart:math' as math; - class DocumentSearchPage extends StatefulWidget { const DocumentSearchPage({super.key}); @@ -211,10 +210,6 @@ class _DocumentSearchPageState extends State { isLabelClickable: false, ); }, - correspondents: state.correspondents, - documentTypes: state.documentTypes, - tags: state.tags, - storagePaths: state.storagePaths, ) ], ); diff --git a/lib/features/document_search/view/documents_search_app_bar.dart b/lib/features/document_search/view/documents_search_app_bar.dart deleted file mode 100644 index 8b13789..0000000 --- a/lib/features/document_search/view/documents_search_app_bar.dart +++ /dev/null @@ -1 +0,0 @@ - diff --git a/lib/features/document_search/view/sliver_search_bar.dart b/lib/features/document_search/view/sliver_search_bar.dart index 044dbdc..d39b41d 100644 --- a/lib/features/document_search/view/sliver_search_bar.dart +++ b/lib/features/document_search/view/sliver_search_bar.dart @@ -1,10 +1,15 @@ 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'; import 'package:paperless_mobile/core/database/tables/local_user_account.dart'; +import 'package:paperless_mobile/core/database/tables/local_user_app_state.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/widgets/material/search/m3_search_bar.dart' as s; +import 'package:paperless_mobile/features/document_search/cubit/document_search_cubit.dart'; +import 'package:paperless_mobile/features/document_search/view/document_search_bar.dart'; import 'package:paperless_mobile/features/home/view/model/api_version.dart'; import 'package:paperless_mobile/features/settings/view/manage_accounts_page.dart'; import 'package:paperless_mobile/features/settings/view/widgets/global_settings_builder.dart'; @@ -23,6 +28,8 @@ class SliverSearchBar extends StatelessWidget { @override Widget build(BuildContext context) { + final currentUser = + Hive.box(HiveBoxes.globalSettings).getValue()!.currentLoggedInUser; return SliverPersistentHeader( floating: floating, pinned: pinned, @@ -30,45 +37,56 @@ class SliverSearchBar extends StatelessWidget { minExtent: kToolbarHeight, maxExtent: kToolbarHeight, child: Container( - margin: const EdgeInsets.symmetric(horizontal: 8.0), - child: s.SearchBar( - height: kToolbarHeight, - supportingText: S.of(context)!.searchDocuments, - onTap: () => pushDocumentSearchPage(context), - leadingIcon: IconButton( - icon: const Icon(Icons.menu), - onPressed: Scaffold.of(context).openDrawer, - ), - trailingIcon: IconButton( - icon: GlobalSettingsBuilder( - builder: (context, settings) { - return ValueListenableBuilder( - valueListenable: - Hive.box(HiveBoxes.localUserAccount).listenable(), - builder: (context, box, _) { - final account = box.get(settings.currentLoggedInUser!)!; - return UserAvatar( - userId: settings.currentLoggedInUser!, - account: account, - ); - }, - ); - }, - ), - onPressed: () { - final apiVersion = context.read(); - showDialog( - context: context, - builder: (context) => Provider.value( - value: apiVersion, - child: const ManageAccountsPage(), - ), - ); - }, + margin: const EdgeInsets.symmetric(horizontal: 16.0), + child: BlocProvider( + create: (context) => DocumentSearchCubit( + context.read(), + context.read(), + context.read(), + Hive.box(HiveBoxes.localUserAppState).get(currentUser)!, ), + child: const DocumentSearchBar(), ), ), ), ); } + + s.SearchBar _buildOld(BuildContext context) { + return s.SearchBar( + height: kToolbarHeight, + supportingText: S.of(context)!.searchDocuments, + onTap: () => pushDocumentSearchPage(context), + leadingIcon: IconButton( + icon: const Icon(Icons.menu), + onPressed: Scaffold.of(context).openDrawer, + ), + trailingIcon: IconButton( + icon: GlobalSettingsBuilder( + builder: (context, settings) { + return ValueListenableBuilder( + valueListenable: Hive.box(HiveBoxes.localUserAccount).listenable(), + builder: (context, box, _) { + final account = box.get(settings.currentLoggedInUser!)!; + return UserAvatar( + userId: settings.currentLoggedInUser!, + account: account, + ); + }, + ); + }, + ), + onPressed: () { + final apiVersion = context.read(); + showDialog( + context: context, + builder: (context) => Provider.value( + value: apiVersion, + child: const ManageAccountsPage(), + ), + ); + }, + ), + ); + } } diff --git a/lib/features/documents/view/pages/documents_page.dart b/lib/features/documents/view/pages/documents_page.dart index 3e2199a..fc2c399 100644 --- a/lib/features/documents/view/pages/documents_page.dart +++ b/lib/features/documents/view/pages/documents_page.dart @@ -333,10 +333,6 @@ class _DocumentsPageState extends State with SingleTickerProvider isLabelClickable: true, isLoading: state.isLoading, selectedDocumentIds: state.selectedIds, - correspondents: state.correspondents, - documentTypes: state.documentTypes, - tags: state.tags, - storagePaths: state.storagePaths, ); }, ), diff --git a/lib/features/documents/view/widgets/adaptive_documents_view.dart b/lib/features/documents/view/widgets/adaptive_documents_view.dart index 6241a98..47f9110 100644 --- a/lib/features/documents/view/widgets/adaptive_documents_view.dart +++ b/lib/features/documents/view/widgets/adaptive_documents_view.dart @@ -24,11 +24,6 @@ abstract class AdaptiveDocumentsView extends StatelessWidget { final void Function(int? id)? onDocumentTypeSelected; final void Function(int? id)? onStoragePathSelected; - final Map correspondents; - final Map documentTypes; - final Map tags; - final Map storagePaths; - bool get showLoadingPlaceholder => !hasLoaded && isLoading; const AdaptiveDocumentsView({ @@ -47,10 +42,6 @@ abstract class AdaptiveDocumentsView extends StatelessWidget { required this.isLoading, required this.hasLoaded, this.enableHeroAnimation = true, - required this.correspondents, - required this.documentTypes, - required this.tags, - required this.storagePaths, }); AdaptiveDocumentsView.fromPagedState( @@ -67,10 +58,6 @@ abstract class AdaptiveDocumentsView extends StatelessWidget { required this.hasInternetConnection, this.viewType = ViewType.list, this.selectedDocumentIds = const [], - required this.correspondents, - required this.documentTypes, - required this.tags, - required this.storagePaths, }) : documents = state.documents, isLoading = state.isLoading, hasLoaded = state.hasLoaded; @@ -93,10 +80,6 @@ class SliverAdaptiveDocumentsView extends AdaptiveDocumentsView { super.enableHeroAnimation, required super.isLoading, required super.hasLoaded, - required super.correspondents, - required super.documentTypes, - required super.tags, - required super.storagePaths, }); @override @@ -132,10 +115,6 @@ class SliverAdaptiveDocumentsView extends AdaptiveDocumentsView { onDocumentTypeSelected: onDocumentTypeSelected, onStoragePathSelected: onStoragePathSelected, enableHeroAnimation: enableHeroAnimation, - correspondents: correspondents, - documentTypes: documentTypes, - storagePaths: storagePaths, - tags: tags, ); }, ), @@ -165,10 +144,6 @@ class SliverAdaptiveDocumentsView extends AdaptiveDocumentsView { onStoragePathSelected: onStoragePathSelected, enableHeroAnimation: enableHeroAnimation, highlights: document.searchHit?.highlights, - correspondents: correspondents, - documentTypes: documentTypes, - storagePaths: storagePaths, - tags: tags, ); }, ), @@ -201,10 +176,6 @@ class SliverAdaptiveDocumentsView extends AdaptiveDocumentsView { onDocumentTypeSelected: onDocumentTypeSelected, onStoragePathSelected: onStoragePathSelected, enableHeroAnimation: enableHeroAnimation, - correspondents: correspondents, - documentTypes: documentTypes, - storagePaths: storagePaths, - tags: tags, ); }, ); @@ -230,10 +201,6 @@ class DefaultAdaptiveDocumentsView extends AdaptiveDocumentsView { super.selectedDocumentIds, super.viewType, super.enableHeroAnimation = true, - required super.correspondents, - required super.documentTypes, - required super.tags, - required super.storagePaths, }); @override @@ -272,10 +239,6 @@ class DefaultAdaptiveDocumentsView extends AdaptiveDocumentsView { onDocumentTypeSelected: onDocumentTypeSelected, onStoragePathSelected: onStoragePathSelected, enableHeroAnimation: enableHeroAnimation, - correspondents: correspondents, - documentTypes: documentTypes, - storagePaths: storagePaths, - tags: tags, ); }, ); @@ -306,10 +269,6 @@ class DefaultAdaptiveDocumentsView extends AdaptiveDocumentsView { onDocumentTypeSelected: onDocumentTypeSelected, onStoragePathSelected: onStoragePathSelected, enableHeroAnimation: enableHeroAnimation, - correspondents: correspondents, - documentTypes: documentTypes, - storagePaths: storagePaths, - tags: tags, ); }, ); @@ -344,10 +303,6 @@ class DefaultAdaptiveDocumentsView extends AdaptiveDocumentsView { onDocumentTypeSelected: onDocumentTypeSelected, onStoragePathSelected: onStoragePathSelected, enableHeroAnimation: enableHeroAnimation, - correspondents: correspondents, - documentTypes: documentTypes, - storagePaths: storagePaths, - tags: tags, ); }, ); diff --git a/lib/features/documents/view/widgets/items/document_detailed_item.dart b/lib/features/documents/view/widgets/items/document_detailed_item.dart index bda4953..5fd6ae9 100644 --- a/lib/features/documents/view/widgets/items/document_detailed_item.dart +++ b/lib/features/documents/view/widgets/items/document_detailed_item.dart @@ -3,12 +3,14 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter_html/flutter_html.dart'; import 'package:intl/intl.dart'; +import 'package:paperless_mobile/core/repository/label_repository.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.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'; import 'package:paperless_mobile/features/labels/document_type/view/widgets/document_type_widget.dart'; import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_widget.dart'; +import 'package:provider/provider.dart'; class DocumentDetailedItem extends DocumentItem { final String? highlights; @@ -26,10 +28,6 @@ class DocumentDetailedItem extends DocumentItem { super.onStoragePathSelected, super.onTagSelected, super.onTap, - required super.tags, - required super.correspondents, - required super.documentTypes, - required super.storagePaths, }); @override @@ -116,7 +114,8 @@ class DocumentDetailedItem extends DocumentItem { textStyle: Theme.of(context).textTheme.titleSmall?.apply( color: Theme.of(context).colorScheme.onSurfaceVariant, ), - correspondent: correspondents[document.correspondent], + correspondent: + context.watch().state.correspondents[document.correspondent], ), ], ).paddedLTRB(8, 0, 8, 4), @@ -131,13 +130,16 @@ class DocumentDetailedItem extends DocumentItem { textStyle: Theme.of(context).textTheme.titleSmall?.apply( color: Theme.of(context).colorScheme.onSurfaceVariant, ), - documentType: documentTypes[document.documentType], + documentType: + context.watch().state.documentTypes[document.documentType], ), ], ).paddedLTRB(8, 0, 8, 4), TagsWidget( isMultiLine: false, - tags: document.tags.map((e) => tags[e]!).toList(), + tags: document.tags + .map((e) => context.watch().state.tags[e]!) + .toList(), ).padded(), if (highlights != null) Html( diff --git a/lib/features/documents/view/widgets/items/document_grid_item.dart b/lib/features/documents/view/widgets/items/document_grid_item.dart index 506acbe..e7a954c 100644 --- a/lib/features/documents/view/widgets/items/document_grid_item.dart +++ b/lib/features/documents/view/widgets/items/document_grid_item.dart @@ -1,10 +1,12 @@ import 'package:flutter/material.dart'; +import 'package:paperless_mobile/core/repository/label_repository.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'; import 'package:paperless_mobile/features/labels/document_type/view/widgets/document_type_widget.dart'; import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_widget.dart'; import 'package:intl/intl.dart'; +import 'package:provider/provider.dart'; class DocumentGridItem extends DocumentItem { const DocumentGridItem({ @@ -20,10 +22,6 @@ class DocumentGridItem extends DocumentItem { super.onTagSelected, super.onTap, required super.enableHeroAnimation, - required super.tags, - required super.correspondents, - required super.documentTypes, - required super.storagePaths, }); @override @@ -32,9 +30,8 @@ class DocumentGridItem extends DocumentItem { padding: const EdgeInsets.all(8.0), child: Card( elevation: 1.0, - color: isSelected - ? Theme.of(context).colorScheme.inversePrimary - : Theme.of(context).cardColor, + color: + isSelected ? Theme.of(context).colorScheme.inversePrimary : Theme.of(context).cardColor, child: InkWell( borderRadius: BorderRadius.circular(12), onTap: _onTap, @@ -57,10 +54,16 @@ class DocumentGridItem extends DocumentItem { crossAxisAlignment: CrossAxisAlignment.start, children: [ CorrespondentWidget( - correspondent: correspondents[document.correspondent], + correspondent: context + .watch() + .state + .correspondents[document.correspondent], ), DocumentTypeWidget( - documentType: documentTypes[document.documentType], + documentType: context + .watch() + .state + .documentTypes[document.documentType], ), Text( document.title, @@ -70,7 +73,9 @@ class DocumentGridItem extends DocumentItem { ), const Spacer(), TagsWidget( - tags: document.tags.map((e) => tags[e]!).toList(), + tags: document.tags + .map((e) => context.watch().state.tags[e]!) + .toList(), isMultiLine: false, onTagSelected: onTagSelected, ), diff --git a/lib/features/documents/view/widgets/items/document_item.dart b/lib/features/documents/view/widgets/items/document_item.dart index bf29f45..a19fef3 100644 --- a/lib/features/documents/view/widgets/items/document_item.dart +++ b/lib/features/documents/view/widgets/items/document_item.dart @@ -10,11 +10,6 @@ abstract class DocumentItem extends StatelessWidget { final bool isLabelClickable; final bool enableHeroAnimation; - final Map tags; - final Map correspondents; - final Map documentTypes; - final Map storagePaths; - final void Function(int tagId)? onTagSelected; final void Function(int? correspondentId)? onCorrespondentSelected; final void Function(int? documentTypeId)? onDocumentTypeSelected; @@ -33,9 +28,5 @@ abstract class DocumentItem extends StatelessWidget { this.onDocumentTypeSelected, this.onStoragePathSelected, required this.enableHeroAnimation, - required this.tags, - required this.correspondents, - required this.documentTypes, - required this.storagePaths, }); } diff --git a/lib/features/documents/view/widgets/items/document_list_item.dart b/lib/features/documents/view/widgets/items/document_list_item.dart index bf16352..b08df94 100644 --- a/lib/features/documents/view/widgets/items/document_list_item.dart +++ b/lib/features/documents/view/widgets/items/document_list_item.dart @@ -1,9 +1,11 @@ import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; +import 'package:paperless_mobile/core/repository/label_repository.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'; import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_widget.dart'; +import 'package:provider/provider.dart'; class DocumentListItem extends DocumentItem { static const _a4AspectRatio = 1 / 1.4142; @@ -21,14 +23,11 @@ class DocumentListItem extends DocumentItem { super.onTagSelected, super.onTap, super.enableHeroAnimation = true, - required super.tags, - required super.correspondents, - required super.documentTypes, - required super.storagePaths, }); @override Widget build(BuildContext context) { + final labels = context.watch().state; return Material( child: ListTile( dense: true, @@ -46,7 +45,10 @@ class DocumentListItem extends DocumentItem { absorbing: isSelectionActive, child: CorrespondentWidget( isClickable: isLabelClickable, - correspondent: correspondents[document.correspondent], + correspondent: context + .watch() + .state + .correspondents[document.correspondent], onSelected: onCorrespondentSelected, ), ), @@ -62,8 +64,8 @@ class DocumentListItem extends DocumentItem { child: TagsWidget( isClickable: isLabelClickable, tags: document.tags - .where((e) => tags.containsKey(e)) - .map((e) => tags[e]!) + .where((e) => labels.tags.containsKey(e)) + .map((e) => labels.tags[e]!) .toList(), isMultiLine: false, onTagSelected: (id) => onTagSelected?.call(id), @@ -78,15 +80,12 @@ class DocumentListItem extends DocumentItem { overflow: TextOverflow.ellipsis, text: TextSpan( text: DateFormat.yMMMd().format(document.created), - style: Theme.of(context) - .textTheme - .labelSmall - ?.apply(color: Colors.grey), + style: Theme.of(context).textTheme.labelSmall?.apply(color: Colors.grey), children: document.documentType != null ? [ const TextSpan(text: '\u30FB'), TextSpan( - text: documentTypes[document.documentType]?.name, + text: labels.documentTypes[document.documentType]?.name, ), ] : null, diff --git a/lib/features/linked_documents/view/linked_documents_page.dart b/lib/features/linked_documents/view/linked_documents_page.dart index 70c1bf2..889da31 100644 --- a/lib/features/linked_documents/view/linked_documents_page.dart +++ b/lib/features/linked_documents/view/linked_documents_page.dart @@ -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'; - class LinkedDocumentsPage extends StatefulWidget { const LinkedDocumentsPage({super.key}); @@ -58,10 +57,6 @@ class _LinkedDocumentsPageState extends State isLabelClickable: false, ); }, - correspondents: state.correspondents, - documentTypes: state.documentTypes, - storagePaths: state.storagePaths, - tags: state.tags, ), ], ); diff --git a/lib/features/saved_view_details/view/saved_view_details_page.dart b/lib/features/saved_view_details/view/saved_view_details_page.dart index d19e0e1..d82bd21 100644 --- a/lib/features/saved_view_details/view/saved_view_details_page.dart +++ b/lib/features/saved_view_details/view/saved_view_details_page.dart @@ -83,10 +83,6 @@ class _SavedViewDetailsPageState extends State ); }, viewType: state.viewType, - correspondents: state.correspondents, - documentTypes: state.documentTypes, - tags: state.tags, - storagePaths: state.storagePaths, ), if (state.hasLoaded && state.isLoading) const SliverToBoxAdapter( diff --git a/lib/features/similar_documents/view/similar_documents_view.dart b/lib/features/similar_documents/view/similar_documents_view.dart index a12a466..9cf7a65 100644 --- a/lib/features/similar_documents/view/similar_documents_view.dart +++ b/lib/features/similar_documents/view/similar_documents_view.dart @@ -66,10 +66,6 @@ class _SimilarDocumentsViewState extends State isLabelClickable: false, ); }, - correspondents: state.correspondents, - documentTypes: state.documentTypes, - tags: state.tags, - storagePaths: state.storagePaths, ); }, ); diff --git a/packages/paperless_api/lib/src/models/models.dart b/packages/paperless_api/lib/src/models/models.dart index 7421f2d..73637bd 100644 --- a/packages/paperless_api/lib/src/models/models.dart +++ b/packages/paperless_api/lib/src/models/models.dart @@ -29,3 +29,4 @@ export 'permissions/user_permissions.dart'; export 'permissions/inherited_permissions.dart'; export 'group_model.dart'; export 'user_model.dart'; +export 'permissions/user_permission_extension.dart'; diff --git a/packages/paperless_api/lib/src/models/permissions/user_permission_extension.dart b/packages/paperless_api/lib/src/models/permissions/user_permission_extension.dart new file mode 100644 index 0000000..e1c8d8f --- /dev/null +++ b/packages/paperless_api/lib/src/models/permissions/user_permission_extension.dart @@ -0,0 +1,14 @@ +import 'package:paperless_api/paperless_api.dart'; + +extension UserPermissionExtension on UserModel { + bool hasPermission(PermissionAction action, PermissionTarget target) { + return map( + v3: (user) { + final permission = [action.value, target.value].join("_"); + return user.userPermissions.any((element) => element == permission) || + user.inheritedPermissions.any((element) => element.split(".").last == permission); + }, + v2: (_) => true, + ); + } +} diff --git a/packages/paperless_api/lib/src/models/user_model.dart b/packages/paperless_api/lib/src/models/user_model.dart index 2d72ac0..340f5f4 100644 --- a/packages/paperless_api/lib/src/models/user_model.dart +++ b/packages/paperless_api/lib/src/models/user_model.dart @@ -3,7 +3,6 @@ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hive/hive.dart'; import 'package:paperless_api/config/hive/hive_type_ids.dart'; -import 'package:paperless_api/src/models/permissions/user_permissions.dart'; part 'user_model.freezed.dart'; part 'user_model.g.dart'; @@ -42,29 +41,17 @@ class UserModel with _$UserModel { String? get fullName => map( v2: (value) => value.displayName, v3: (value) { - if (value.firstName == null && value.lastName == null) { + bool hasFirstName = value.firstName?.trim().isNotEmpty ?? false; + bool hasLastName = value.lastName?.trim().isNotEmpty ?? false; + if (hasFirstName && hasLastName) { + return "${value.firstName!} ${value.lastName!}"; + } else if (hasFirstName) { + return value.firstName!; + } else if (hasLastName) { + return value.lastName; + } else { return null; } - if (value.firstName == null) { - return value.lastName; - } - return "${value.firstName!} ${value.lastName ?? ''}"; }, ); - - bool hasPermission(PermissionAction action, PermissionTarget target) { - return map( - v3: (value) { - if (value.isSuperuser) { - return true; - } - final permissionIdentifier = "${action.value}_${target.value}"; - return value.userPermissions.contains(permissionIdentifier); - }, - v2: (value) { - // In previous versions, all users have all permissions. - return true; - }, - ); - } } diff --git a/packages/paperless_document_scanner/example/android/build.gradle b/packages/paperless_document_scanner/example/android/build.gradle index 58a8c74..713d7f6 100644 --- a/packages/paperless_document_scanner/example/android/build.gradle +++ b/packages/paperless_document_scanner/example/android/build.gradle @@ -26,6 +26,6 @@ subprojects { project.evaluationDependsOn(':app') } -task clean(type: Delete) { +tasks.register("clean", Delete) { delete rootProject.buildDir } diff --git a/packages/paperless_document_scanner/example/lib/main.dart b/packages/paperless_document_scanner/example/lib/main.dart index 1f514bc..ba0240a 100644 --- a/packages/paperless_document_scanner/example/lib/main.dart +++ b/packages/paperless_document_scanner/example/lib/main.dart @@ -1,8 +1,9 @@ - import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:image/image.dart' as imglib; +import 'package:camerawesome/camerawesome_plugin.dart'; +import 'package:path_provider/path_provider.dart'; late final List cameras; void main() async { @@ -19,27 +20,9 @@ class EdgeDetectionApp extends StatefulWidget { } class _EdgeDetectionAppState extends State { - CameraImage? _image; - late final CameraController _controller; - @override void initState() { super.initState(); - - () async { - _controller = CameraController( - cameras - .where( - (element) => element.lensDirection == CameraLensDirection.back) - .first, - ResolutionPreset.low, - enableAudio: false, - ); - await _controller.initialize(); - _controller.startImageStream((image) { - setState(() => _image = image); - }); - }(); } Uint8List concatenatePlanes(List planes) { @@ -68,8 +51,7 @@ class _EdgeDetectionAppState extends State { // Fill image buffer with plane[0] from YUV420_888 for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { - final int uvIndex = - uvPixelStride * (x / 2).floor() + uvRowStride * (y / 2).floor(); + final int uvIndex = uvPixelStride * (x / 2).floor() + uvRowStride * (y / 2).floor(); final int index = y * width + x; final yp = image.planes[0].bytes[index]; @@ -77,9 +59,7 @@ class _EdgeDetectionAppState extends State { final vp = image.planes[2].bytes[uvIndex]; // Calculate pixel color int r = (yp + vp * 1436 / 1024 - 179).round().clamp(0, 255); - int g = (yp - up * 46549 / 131072 + 44 - vp * 93604 / 131072 + 91) - .round() - .clamp(0, 255); + int g = (yp - up * 46549 / 131072 + 44 - vp * 93604 / 131072 + 91).round().clamp(0, 255); int b = (yp + up * 1814 / 1024 - 227).round().clamp(0, 255); // color: 0x FF FF FF FF // A B G R @@ -100,10 +80,22 @@ class _EdgeDetectionAppState extends State { visualDensity: VisualDensity.adaptivePlatformDensity, ), home: Scaffold( - body: Center( - child: _image != null - ? convertYUV420toImageColor(_image!) - : const Placeholder(), + body: CameraAwesomeBuilder.awesome( + saveConfig: SaveConfig.photo( + pathBuilder: () => + getApplicationDocumentsDirectory().then((value) => "${value.path}/test.jpg"), + ), + onImageForAnalysis: (image) async {}, + imageAnalysisConfig: AnalysisConfig( + // Android specific options + androidOptions: const AndroidAnalysisOptions.yuv420( + // Target width (CameraX will chose the closest resolution to this width) + width: 250, + ), + // Wether to start automatically the analysis (true by default) + autoStart: true, + // Max frames per second, null for no limit (default) + ), ), ), ); diff --git a/packages/paperless_document_scanner/example/pubspec.lock b/packages/paperless_document_scanner/example/pubspec.lock index 37fac4d..6b2cc09 100644 --- a/packages/paperless_document_scanner/example/pubspec.lock +++ b/packages/paperless_document_scanner/example/pubspec.lock @@ -65,6 +65,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.1+3" + camerawesome: + dependency: transitive + description: + name: camerawesome + sha256: "0fd4ad7cf85ced7c3018edbe5a66d8c4b630ada7120b02c01961db3ea36f52ce" + url: "https://pub.dev" + source: hosted + version: "1.4.0" + carousel_slider: + dependency: transitive + description: + name: carousel_slider + sha256: "9c695cc963bf1d04a47bd6021f68befce8970bcd61d24938e1fb0918cf5d9c42" + url: "https://pub.dev" + source: hosted + version: "4.2.1" characters: dependency: transitive description: @@ -89,6 +105,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.17.1" + colorfilter_generator: + dependency: transitive + description: + name: colorfilter_generator + sha256: ccc2995e440b1d828d55d99150e7cad64624f3cb4a1e235000de3f93cf10d35c + url: "https://pub.dev" + source: hosted + version: "0.0.8" convert: dependency: transitive description: @@ -216,6 +240,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.2.0" + matrix2d: + dependency: transitive + description: + name: matrix2d + sha256: "188718dd3bc2a31e372cfd0791b0f77f4f13ea76164147342cc378d9132949e7" + url: "https://pub.dev" + source: hosted + version: "1.0.4" meta: dependency: transitive description: @@ -335,6 +367,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.2.1" + rxdart: + dependency: transitive + description: + name: rxdart + sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" + url: "https://pub.dev" + source: hosted + version: "0.27.7" sky_engine: dependency: transitive description: flutter diff --git a/packages/paperless_document_scanner/pubspec.yaml b/packages/paperless_document_scanner/pubspec.yaml index 2debf35..8c3a641 100644 --- a/packages/paperless_document_scanner/pubspec.yaml +++ b/packages/paperless_document_scanner/pubspec.yaml @@ -8,6 +8,7 @@ environment: flutter: ">=2.5.0" dependencies: + camerawesome: ^1.4.0 ffi: ^2.0.1 flutter: sdk: flutter From 06611e03551f73ff8525ebef5e381a50697823ef Mon Sep 17 00:00:00 2001 From: Anton Stubenbord Date: Thu, 18 May 2023 21:33:13 +0200 Subject: [PATCH 23/26] fix: Improve search (WIP) --- .../widgets/material/search/m3_search.dart | 602 ------ .../material/search/m3_search_bar.dart | 79 - .../material/search/search_anchor.dart | 1883 ----------------- .../cubit/document_search_cubit.dart | 2 +- .../view/document_search_bar.dart | 196 +- .../view/sliver_search_bar.dart | 47 - lib/theme.dart | 2 +- 7 files changed, 148 insertions(+), 2663 deletions(-) delete mode 100644 lib/core/widgets/material/search/m3_search.dart delete mode 100644 lib/core/widgets/material/search/m3_search_bar.dart delete mode 100644 lib/core/widgets/material/search/search_anchor.dart diff --git a/lib/core/widgets/material/search/m3_search.dart b/lib/core/widgets/material/search/m3_search.dart deleted file mode 100644 index 9e7b78d..0000000 --- a/lib/core/widgets/material/search/m3_search.dart +++ /dev/null @@ -1,602 +0,0 @@ -//TODO: REMOVE THIS WHEN NATIVE MATERIAL FLUTTER SEARCH IS RELEASED -// Copyright 2014 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; - -/// Shows a full screen search page and returns the search result selected by -/// the user when the page is closed. -/// -/// The search page consists of an app bar with a search field and a body which -/// can either show suggested search queries or the search results. -/// -/// The appearance of the search page is determined by the provided -/// `delegate`. The initial query string is given by `query`, which defaults -/// to the empty string. When `query` is set to null, `delegate.query` will -/// be used as the initial query. -/// -/// This method returns the selected search result, which can be set in the -/// [SearchDelegate.close] call. If the search page is closed with the system -/// back button, it returns null. -/// -/// A given [SearchDelegate] can only be associated with one active [showMaterial3Search] -/// call. Call [SearchDelegate.close] before re-using the same delegate instance -/// for another [showMaterial3Search] call. -/// -/// The `useRootNavigator` argument is used to determine whether to push the -/// search page to the [Navigator] furthest from or nearest to the given -/// `context`. By default, `useRootNavigator` is `false` and the search page -/// route created by this method is pushed to the nearest navigator to the -/// given `context`. It can not be `null`. -/// -/// The transition to the search page triggered by this method looks best if the -/// screen triggering the transition contains an [AppBar] at the top and the -/// transition is called from an [IconButton] that's part of [AppBar.actions]. -/// The animation provided by [SearchDelegate.transitionAnimation] can be used -/// to trigger additional animations in the underlying page while the search -/// page fades in or out. This is commonly used to animate an [AnimatedIcon] in -/// the [AppBar.leading] position e.g. from the hamburger menu to the back arrow -/// used to exit the search page. -/// -/// ## Handling emojis and other complex characters -/// {@macro flutter.widgets.EditableText.onChanged} -/// -/// See also: -/// -/// * [SearchDelegate] to define the content of the search page. -Future showMaterial3Search({ - required BuildContext context, - required SearchDelegate delegate, - String? query = '', - bool useRootNavigator = false, -}) { - delegate.query = query ?? delegate.query; - delegate._currentBody = _SearchBody.suggestions; - return Navigator.of(context, rootNavigator: useRootNavigator) - .push(_SearchPageRoute( - delegate: delegate, - )); -} - -/// Delegate for [showMaterial3Search] to define the content of the search page. -/// -/// The search page always shows an [AppBar] at the top where users can -/// enter their search queries. The buttons shown before and after the search -/// query text field can be customized via [SearchDelegate.buildLeading] -/// and [SearchDelegate.buildActions]. Additionally, a widget can be placed -/// across the bottom of the [AppBar] via [SearchDelegate.buildBottom]. -/// -/// The body below the [AppBar] can either show suggested queries (returned by -/// [SearchDelegate.buildSuggestions]) or - once the user submits a search - the -/// results of the search as returned by [SearchDelegate.buildResults]. -/// -/// [SearchDelegate.query] always contains the current query entered by the user -/// and should be used to build the suggestions and results. -/// -/// The results can be brought on screen by calling [SearchDelegate.showResults] -/// and you can go back to showing the suggestions by calling -/// [SearchDelegate.showSuggestions]. -/// -/// Once the user has selected a search result, [SearchDelegate.close] should be -/// called to remove the search page from the top of the navigation stack and -/// to notify the caller of [showMaterial3Search] about the selected search result. -/// -/// A given [SearchDelegate] can only be associated with one active [showMaterial3Search] -/// call. Call [SearchDelegate.close] before re-using the same delegate instance -/// for another [showMaterial3Search] call. -/// -/// ## Handling emojis and other complex characters -/// {@macro flutter.widgets.EditableText.onChanged} -abstract class SearchDelegate { - /// Constructor to be called by subclasses which may specify - /// [searchFieldLabel], either [searchFieldStyle] or [searchFieldDecorationTheme], - /// [keyboardType] and/or [textInputAction]. Only one of [searchFieldLabel] - /// and [searchFieldDecorationTheme] may be non-null. - /// - /// {@tool snippet} - /// ```dart - /// class CustomSearchHintDelegate extends SearchDelegate { - /// CustomSearchHintDelegate({ - /// required String hintText, - /// }) : super( - /// searchFieldLabel: hintText, - /// keyboardType: TextInputType.text, - /// textInputAction: TextInputAction.search, - /// ); - /// - /// @override - /// Widget buildLeading(BuildContext context) => const Text('leading'); - /// - /// @override - /// PreferredSizeWidget buildBottom(BuildContext context) { - /// return const PreferredSize( - /// preferredSize: Size.fromHeight(56.0), - /// child: Text('bottom')); - /// } - /// - /// @override - /// Widget buildSuggestions(BuildContext context) => const Text('suggestions'); - /// - /// @override - /// Widget buildResults(BuildContext context) => const Text('results'); - /// - /// @override - /// List buildActions(BuildContext context) => []; - /// } - /// ``` - /// {@end-tool} - SearchDelegate({ - this.searchFieldLabel, - this.searchFieldStyle, - this.searchFieldDecorationTheme, - this.keyboardType, - this.textInputAction = TextInputAction.search, - }) : assert(searchFieldStyle == null || searchFieldDecorationTheme == null); - - /// Suggestions shown in the body of the search page while the user types a - /// query into the search field. - /// - /// The delegate method is called whenever the content of [query] changes. - /// The suggestions should be based on the current [query] string. If the query - /// string is empty, it is good practice to show suggested queries based on - /// past queries or the current context. - /// - /// Usually, this method will return a [ListView] with one [ListTile] per - /// suggestion. When [ListTile.onTap] is called, [query] should be updated - /// with the corresponding suggestion and the results page should be shown - /// by calling [showResults]. - Widget buildSuggestions(BuildContext context); - - /// The results shown after the user submits a search from the search page. - /// - /// The current value of [query] can be used to determine what the user - /// searched for. - /// - /// This method might be applied more than once to the same query. - /// If your [buildResults] method is computationally expensive, you may want - /// to cache the search results for one or more queries. - /// - /// Typically, this method returns a [ListView] with the search results. - /// When the user taps on a particular search result, [close] should be called - /// with the selected result as argument. This will close the search page and - /// communicate the result back to the initial caller of [showMaterial3Search]. - Widget buildResults(BuildContext context); - - /// A widget to display before the current query in the [AppBar]. - /// - /// Typically an [IconButton] configured with a [BackButtonIcon] that exits - /// the search with [close]. One can also use an [AnimatedIcon] driven by - /// [transitionAnimation], which animates from e.g. a hamburger menu to the - /// back button as the search overlay fades in. - /// - /// Returns null if no widget should be shown. - /// - /// See also: - /// - /// * [AppBar.leading], the intended use for the return value of this method. - Widget? buildLeading(BuildContext context); - - /// Widgets to display after the search query in the [AppBar]. - /// - /// If the [query] is not empty, this should typically contain a button to - /// clear the query and show the suggestions again (via [showSuggestions]) if - /// the results are currently shown. - /// - /// Returns null if no widget should be shown. - /// - /// See also: - /// - /// * [AppBar.actions], the intended use for the return value of this method. - List? buildActions(BuildContext context); - - /// Widget to display across the bottom of the [AppBar]. - /// - /// Returns null by default, i.e. a bottom widget is not included. - /// - /// See also: - /// - /// * [AppBar.bottom], the intended use for the return value of this method. - /// - PreferredSizeWidget? buildBottom(BuildContext context) => null; - - /// The theme used to configure the search page. - /// - /// The returned [ThemeData] will be used to wrap the entire search page, - /// so it can be used to configure any of its components with the appropriate - /// theme properties. - /// - /// Unless overridden, the default theme will configure the AppBar containing - /// the search input text field with a white background and black text on light - /// themes. For dark themes the default is a dark grey background with light - /// color text. - /// - /// See also: - /// - /// * [AppBarTheme], which configures the AppBar's appearance. - /// * [InputDecorationTheme], which configures the appearance of the search - /// text field. - ThemeData appBarTheme(BuildContext context) { - final ThemeData theme = Theme.of(context); - final ColorScheme colorScheme = theme.colorScheme; - return theme.copyWith( - appBarTheme: AppBarTheme( - systemOverlayStyle: colorScheme.brightness == Brightness.light - ? SystemUiOverlayStyle.light - : SystemUiOverlayStyle.dark, - backgroundColor: colorScheme.brightness == Brightness.dark - ? Colors.grey[900] - : Colors.white, - iconTheme: theme.primaryIconTheme.copyWith(color: Colors.grey), - ), - inputDecorationTheme: searchFieldDecorationTheme ?? - InputDecorationTheme( - hintStyle: searchFieldStyle ?? theme.inputDecorationTheme.hintStyle, - border: InputBorder.none, - ), - ); - } - - /// The current query string shown in the [AppBar]. - /// - /// The user manipulates this string via the keyboard. - /// - /// If the user taps on a suggestion provided by [buildSuggestions] this - /// string should be updated to that suggestion via the setter. - String get query => _queryTextController.text; - - /// Changes the current query string. - /// - /// Setting the query string programmatically moves the cursor to the end of the text field. - set query(String value) { - _queryTextController.text = value; - if (_queryTextController.text.isNotEmpty) { - _queryTextController.selection = TextSelection.fromPosition( - TextPosition(offset: _queryTextController.text.length)); - } - } - - /// Transition from the suggestions returned by [buildSuggestions] to the - /// [query] results returned by [buildResults]. - /// - /// If the user taps on a suggestion provided by [buildSuggestions] the - /// screen should typically transition to the page showing the search - /// results for the suggested query. This transition can be triggered - /// by calling this method. - /// - /// See also: - /// - /// * [showSuggestions] to show the search suggestions again. - void showResults(BuildContext context) { - _focusNode?.unfocus(); - _currentBody = _SearchBody.results; - } - - /// Transition from showing the results returned by [buildResults] to showing - /// the suggestions returned by [buildSuggestions]. - /// - /// Calling this method will also put the input focus back into the search - /// field of the [AppBar]. - /// - /// If the results are currently shown this method can be used to go back - /// to showing the search suggestions. - /// - /// See also: - /// - /// * [showResults] to show the search results. - void showSuggestions(BuildContext context) { - assert(_focusNode != null, - '_focusNode must be set by route before showSuggestions is called.'); - _focusNode!.requestFocus(); - _currentBody = _SearchBody.suggestions; - } - - /// Closes the search page and returns to the underlying route. - /// - /// The value provided for `result` is used as the return value of the call - /// to [showMaterial3Search] that launched the search initially. - void close(BuildContext context, T result) { - _currentBody = null; - _focusNode?.unfocus(); - Navigator.of(context) - ..popUntil((Route route) => route == _route) - ..pop(result); - } - - /// The hint text that is shown in the search field when it is empty. - /// - /// If this value is set to null, the value of - /// `MaterialLocalizationS.of(context)!.searchFieldLabel` will be used instead. - final String? searchFieldLabel; - - /// The style of the [searchFieldLabel]. - /// - /// If this value is set to null, the value of the ambient [Theme]'s - /// [InputDecorationTheme.hintStyle] will be used instead. - /// - /// Only one of [searchFieldStyle] or [searchFieldDecorationTheme] can - /// be non-null. - final TextStyle? searchFieldStyle; - - /// The [InputDecorationTheme] used to configure the search field's visuals. - /// - /// Only one of [searchFieldStyle] or [searchFieldDecorationTheme] can - /// be non-null. - final InputDecorationTheme? searchFieldDecorationTheme; - - /// The type of action button to use for the keyboard. - /// - /// Defaults to the default value specified in [TextField]. - final TextInputType? keyboardType; - - /// The text input action configuring the soft keyboard to a particular action - /// button. - /// - /// Defaults to [TextInputAction.search]. - final TextInputAction textInputAction; - - /// [Animation] triggered when the search pages fades in or out. - /// - /// This animation is commonly used to animate [AnimatedIcon]s of - /// [IconButton]s returned by [buildLeading] or [buildActions]. It can also be - /// used to animate [IconButton]s contained within the route below the search - /// page. - Animation get transitionAnimation => _proxyAnimation; - - // The focus node to use for manipulating focus on the search page. This is - // managed, owned, and set by the _SearchPageRoute using this delegate. - FocusNode? _focusNode; - - final TextEditingController _queryTextController = TextEditingController(); - - final ProxyAnimation _proxyAnimation = - ProxyAnimation(kAlwaysDismissedAnimation); - - final ValueNotifier<_SearchBody?> _currentBodyNotifier = - ValueNotifier<_SearchBody?>(null); - - _SearchBody? get _currentBody => _currentBodyNotifier.value; - set _currentBody(_SearchBody? value) { - _currentBodyNotifier.value = value; - } - - _SearchPageRoute? _route; -} - -/// Describes the body that is currently shown under the [AppBar] in the -/// search page. -enum _SearchBody { - /// Suggested queries are shown in the body. - /// - /// The suggested queries are generated by [SearchDelegate.buildSuggestions]. - suggestions, - - /// Search results are currently shown in the body. - /// - /// The search results are generated by [SearchDelegate.buildResults]. - results, -} - -class _SearchPageRoute extends PageRoute { - _SearchPageRoute({ - required this.delegate, - }) { - assert( - delegate._route == null, - 'The ${delegate.runtimeType} instance is currently used by another active ' - 'search. Please close that search by calling close() on the SearchDelegate ' - 'before opening another search with the same delegate instance.', - ); - delegate._route = this; - } - - final SearchDelegate delegate; - - @override - Color? get barrierColor => null; - - @override - String? get barrierLabel => null; - - @override - Duration get transitionDuration => const Duration(milliseconds: 300); - - @override - bool get maintainState => false; - - @override - Widget buildTransitions( - BuildContext context, - Animation animation, - Animation secondaryAnimation, - Widget child, - ) { - return FadeTransition( - opacity: animation, - child: child, - ); - } - - @override - Animation createAnimation() { - final Animation animation = super.createAnimation(); - delegate._proxyAnimation.parent = animation; - return animation; - } - - @override - Widget buildPage( - BuildContext context, - Animation animation, - Animation secondaryAnimation, - ) { - return _SearchPage( - delegate: delegate, - animation: animation, - ); - } - - @override - void didComplete(T? result) { - super.didComplete(result); - assert(delegate._route == this); - delegate._route = null; - delegate._currentBody = null; - } -} - -class _SearchPage extends StatefulWidget { - const _SearchPage({ - required this.delegate, - required this.animation, - }); - - final SearchDelegate delegate; - final Animation animation; - - @override - State createState() => _SearchPageState(); -} - -class _SearchPageState extends State<_SearchPage> { - // This node is owned, but not hosted by, the search page. Hosting is done by - // the text field. - FocusNode focusNode = FocusNode(); - - @override - void initState() { - super.initState(); - widget.delegate._queryTextController.addListener(_onQueryChanged); - widget.animation.addStatusListener(_onAnimationStatusChanged); - widget.delegate._currentBodyNotifier.addListener(_onSearchBodyChanged); - focusNode.addListener(_onFocusChanged); - widget.delegate._focusNode = focusNode; - } - - @override - void dispose() { - super.dispose(); - widget.delegate._queryTextController.removeListener(_onQueryChanged); - widget.animation.removeStatusListener(_onAnimationStatusChanged); - widget.delegate._currentBodyNotifier.removeListener(_onSearchBodyChanged); - widget.delegate._focusNode = null; - focusNode.dispose(); - } - - void _onAnimationStatusChanged(AnimationStatus status) { - if (status != AnimationStatus.completed) { - return; - } - widget.animation.removeStatusListener(_onAnimationStatusChanged); - if (widget.delegate._currentBody == _SearchBody.suggestions) { - focusNode.requestFocus(); - } - } - - @override - void didUpdateWidget(_SearchPage oldWidget) { - super.didUpdateWidget(oldWidget); - if (widget.delegate != oldWidget.delegate) { - oldWidget.delegate._queryTextController.removeListener(_onQueryChanged); - widget.delegate._queryTextController.addListener(_onQueryChanged); - oldWidget.delegate._currentBodyNotifier - .removeListener(_onSearchBodyChanged); - widget.delegate._currentBodyNotifier.addListener(_onSearchBodyChanged); - oldWidget.delegate._focusNode = null; - widget.delegate._focusNode = focusNode; - } - } - - void _onFocusChanged() { - if (focusNode.hasFocus && - widget.delegate._currentBody != _SearchBody.suggestions) { - widget.delegate.showSuggestions(context); - } - } - - void _onQueryChanged() { - setState(() { - // rebuild ourselves because query changed. - }); - } - - void _onSearchBodyChanged() { - setState(() { - // rebuild ourselves because search body changed. - }); - } - - @override - Widget build(BuildContext context) { - assert(debugCheckHasMaterialLocalizations(context)); - final ThemeData theme = widget.delegate.appBarTheme(context); - final String searchFieldLabel = - widget.delegate.searchFieldLabel ?? S.of(context)!.search; - Widget? body; - switch (widget.delegate._currentBody) { - case _SearchBody.suggestions: - body = KeyedSubtree( - key: const ValueKey<_SearchBody>(_SearchBody.suggestions), - child: widget.delegate.buildSuggestions(context), - ); - break; - case _SearchBody.results: - body = KeyedSubtree( - key: const ValueKey<_SearchBody>(_SearchBody.results), - child: widget.delegate.buildResults(context), - ); - break; - case null: - break; - } - - late final String routeName; - switch (theme.platform) { - case TargetPlatform.iOS: - case TargetPlatform.macOS: - routeName = ''; - break; - case TargetPlatform.android: - case TargetPlatform.fuchsia: - case TargetPlatform.linux: - case TargetPlatform.windows: - routeName = searchFieldLabel; - } - - return Semantics( - explicitChildNodes: true, - scopesRoute: true, - namesRoute: true, - label: routeName, - child: Theme( - data: theme, - child: Scaffold( - appBar: AppBar( - toolbarHeight: 72, - leading: widget.delegate.buildLeading(context), - title: TextField( - controller: widget.delegate._queryTextController, - focusNode: focusNode, - style: widget.delegate.searchFieldStyle ?? - theme.textTheme.titleLarge, - textInputAction: widget.delegate.textInputAction, - keyboardType: widget.delegate.keyboardType, - onSubmitted: (String _) { - widget.delegate.showResults(context); - }, - decoration: InputDecoration(hintText: searchFieldLabel), - ), - actions: widget.delegate.buildActions(context), - bottom: widget.delegate.buildBottom(context), - ), - body: AnimatedSwitcher( - duration: const Duration(milliseconds: 300), - child: body, - ), - ), - ), - ); - } -} diff --git a/lib/core/widgets/material/search/m3_search_bar.dart b/lib/core/widgets/material/search/m3_search_bar.dart deleted file mode 100644 index dafcea1..0000000 --- a/lib/core/widgets/material/search/m3_search_bar.dart +++ /dev/null @@ -1,79 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; - -class SearchBar extends StatelessWidget { - const SearchBar({ - Key? key, - this.height = 56, - required this.leadingIcon, - this.trailingIcon, - required this.supportingText, - required this.onTap, - }) : super(key: key); - - final double height; - double get effectiveHeight { - return max(height, 48); - } - - final VoidCallback onTap; - final Widget leadingIcon; - final Widget? trailingIcon; - - final String supportingText; - - @override - Widget build(BuildContext context) { - final ColorScheme colorScheme = Theme.of(context).colorScheme; - final TextTheme textTheme = Theme.of(context).textTheme; - - return Container( - constraints: const BoxConstraints(minWidth: 360, maxWidth: 720), - width: double.infinity, - height: effectiveHeight, - child: Material( - elevation: 1, - color: colorScheme.surface, - shadowColor: colorScheme.shadow, - surfaceTintColor: colorScheme.surfaceTint, - borderRadius: BorderRadius.circular(effectiveHeight / 2), - child: InkWell( - onTap: onTap, - borderRadius: BorderRadius.circular(effectiveHeight / 2), - highlightColor: Colors.transparent, - splashFactory: InkRipple.splashFactory, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 8), - child: Row(children: [ - leadingIcon, - Expanded( - child: Padding( - padding: const EdgeInsets.only(right: 8), - child: TextField( - onTap: onTap, - readOnly: true, - enabled: false, - cursorColor: colorScheme.primary, - style: textTheme.bodyLarge, - textAlignVertical: TextAlignVertical.center, - decoration: InputDecoration( - isCollapsed: true, - border: InputBorder.none, - contentPadding: const EdgeInsets.symmetric(horizontal: 8), - hintText: supportingText, - hintStyle: textTheme.bodyLarge?.apply( - color: colorScheme.onSurfaceVariant, - ), - ), - ), - ), - ), - if (trailingIcon != null) trailingIcon!, - ]), - ), - ), - ), - ); - } -} diff --git a/lib/core/widgets/material/search/search_anchor.dart b/lib/core/widgets/material/search/search_anchor.dart deleted file mode 100644 index 6acadaa..0000000 --- a/lib/core/widgets/material/search/search_anchor.dart +++ /dev/null @@ -1,1883 +0,0 @@ -// Copyright 2014 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -// TODO: Remove once these changes were merged into stable release. -import 'dart:math' as math; -import 'dart:ui'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; - -const int _kOpenViewMilliseconds = 600; -const Duration _kOpenViewDuration = - Duration(milliseconds: _kOpenViewMilliseconds); -const Duration _kAnchorFadeDuration = Duration(milliseconds: 150); -const Curve _kViewFadeOnInterval = Interval(0.0, 1 / 2); -const Curve _kViewIconsFadeOnInterval = Interval(1 / 6, 2 / 6); -const Curve _kViewDividerFadeOnInterval = Interval(0.0, 1 / 6); -const Curve _kViewListFadeOnInterval = - Interval(133 / _kOpenViewMilliseconds, 233 / _kOpenViewMilliseconds); - -/// Signature for a function that creates a [Widget] which is used to open a search view. -/// -/// The `controller` callback provided to [SearchAnchor.builder] can be used -/// to open the search view and control the editable field on the view. -typedef SearchAnchorChildBuilder = Widget Function( - BuildContext context, SearchController controller); - -/// Signature for a function that creates a [Widget] to build the suggestion list -/// based on the input in the search bar. -/// -/// The `controller` callback provided to [SearchAnchor.suggestionsBuilder] can be used -/// to close the search view and control the editable field on the view. -typedef SuggestionsBuilder = Iterable Function( - BuildContext context, SearchController controller); - -/// Signature for a function that creates a [Widget] to layout the suggestion list. -/// -/// Parameter `suggestions` is the content list that this function wants to lay out. -typedef ViewBuilder = Widget Function(Iterable suggestions); - -/// Manages a "search view" route that allows the user to select one of the -/// suggested completions for a search query. -/// -/// The search view's route can either be shown by creating a [SearchController] -/// and then calling [SearchController.openView] or by tapping on an anchor. -/// When the anchor is tapped or [SearchController.openView] is called, the search view either -/// grows to a specific size, or grows to fill the entire screen. By default, -/// the search view only shows full screen on mobile platforms. Use [SearchAnchor.isFullScreen] -/// to override the default setting. -/// -/// The search view is usually opened by a [SearchBar], an [IconButton] or an [Icon]. -/// If [builder] returns an Icon, or any un-tappable widgets, we don't have -/// to explicitly call [SearchController.openView]. -/// -/// {@tool dartpad} -/// This example shows how to use an IconButton to open a search view in a [SearchAnchor]. -/// It also shows how to use [SearchController] to open or close the search view route. -/// -/// ** See code in examples/api/lib/material/search_anchor/search_anchor.2.dart ** -/// {@end-tool} -/// -/// {@tool dartpad} -/// This example shows how to set up a floating (or pinned) AppBar with a -/// [SearchAnchor] for a title. -/// -/// ** See code in examples/api/lib/material/search_anchor/search_anchor.1.dart ** -/// {@end-tool} -/// -/// See also: -/// -/// * [SearchBar], a widget that defines a search bar. -/// * [SearchBarTheme], a widget that overrides the default configuration of a search bar. -/// * [SearchViewTheme], a widget that overrides the default configuration of a search view. -class SearchAnchor extends StatefulWidget { - /// Creates a const [SearchAnchor]. - /// - /// The [builder] and [suggestionsBuilder] arguments are required. - const SearchAnchor({ - super.key, - this.isFullScreen, - this.searchController, - this.viewBuilder, - this.viewLeading, - this.viewTrailing, - this.viewHintText, - this.viewBackgroundColor, - this.viewElevation, - this.viewSurfaceTintColor, - this.viewSide, - this.viewShape, - this.headerTextStyle, - this.headerHintStyle, - this.dividerColor, - this.viewConstraints, - required this.builder, - required this.suggestionsBuilder, - }); - - /// Create a [SearchAnchor] that has a [SearchBar] which opens a search view. - /// - /// All the barX parameters are used to customize the anchor. Similarly, all the - /// viewX parameters are used to override the view's defaults. - /// - /// {@tool dartpad} - /// This example shows how to use a [SearchAnchor.bar] which uses a default search - /// bar to open a search view route. - /// - /// ** See code in examples/api/lib/material/search_anchor/search_anchor.0.dart ** - /// {@end-tool} - /// - /// The [suggestionsBuilder] argument must not be null. - factory SearchAnchor.bar( - {Widget? barLeading, - Iterable? barTrailing, - String? barHintText, - GestureTapCallback? onTap, - MaterialStateProperty? barElevation, - MaterialStateProperty? barBackgroundColor, - MaterialStateProperty? barOverlayColor, - MaterialStateProperty? barSide, - MaterialStateProperty? barShape, - MaterialStateProperty? barPadding, - MaterialStateProperty? barTextStyle, - MaterialStateProperty? barHintStyle, - Widget? viewLeading, - Iterable? viewTrailing, - String? viewHintText, - Color? viewBackgroundColor, - double? viewElevation, - BorderSide? viewSide, - OutlinedBorder? viewShape, - TextStyle? viewHeaderTextStyle, - TextStyle? viewHeaderHintStyle, - Color? dividerColor, - BoxConstraints? constraints, - bool? isFullScreen, - SearchController searchController, - required SuggestionsBuilder suggestionsBuilder}) = - _SearchAnchorWithSearchBar; - - /// Whether the search view grows to fill the entire screen when the - /// [SearchAnchor] is tapped. - /// - /// By default, the search view is full-screen on mobile devices. On other - /// platforms, the search view only grows to a specific size that is determined - /// by the anchor and the default size. - final bool? isFullScreen; - - /// An optional controller that allows opening and closing of the search view from - /// other widgets. - /// - /// If this is null, one internal search controller is created automatically - /// and it is used to open the search view when the user taps on the anchor. - final SearchController? searchController; - - /// Optional callback to obtain a widget to lay out the suggestion list of the - /// search view. - /// - /// Default view uses a [ListView] with a vertical scroll direction. - final ViewBuilder? viewBuilder; - - /// An optional widget to display before the text input field when the search - /// view is open. - /// - /// Typically the [viewLeading] widget is an [Icon] or an [IconButton]. - /// - /// Defaults to a back button which pops the view. - final Widget? viewLeading; - - /// An optional widget list to display after the text input field when the search - /// view is open. - /// - /// Typically the [viewTrailing] widget list only has one or two widgets. - /// - /// Defaults to an icon button which clears the text in the input field. - final Iterable? viewTrailing; - - /// Text that is displayed when the search bar's input field is empty. - final String? viewHintText; - - /// The search view's background fill color. - /// - /// If null, the value of [SearchViewThemeData.backgroundColor] will be used. - /// If this is also null, then the default value is [ColorScheme.surface]. - final Color? viewBackgroundColor; - - /// The elevation of the search view's [Material]. - /// - /// If null, the value of [SearchViewThemeData.elevation] will be used. If this - /// is also null, then default value is 6.0. - final double? viewElevation; - - /// The surface tint color of the search view's [Material]. - /// - /// See [Material.surfaceTintColor] for more details. - /// - /// If null, the value of [SearchViewThemeData.surfaceTintColor] will be used. - /// If this is also null, then the default value is [ColorScheme.surfaceTint]. - final Color? viewSurfaceTintColor; - - /// The color and weight of the search view's outline. - /// - /// This value is combined with [viewShape] to create a shape decorated - /// with an outline. This will be ignored if the view is full-screen. - /// - /// If null, the value of [SearchViewThemeData.side] will be used. If this is - /// also null, the search view doesn't have a side by default. - final BorderSide? viewSide; - - /// The shape of the search view's underlying [Material]. - /// - /// This shape is combined with [viewSide] to create a shape decorated - /// with an outline. - /// - /// If null, the value of [SearchViewThemeData.shape] will be used. - /// If this is also null, then the default value is a rectangle shape for full-screen - /// mode and a [RoundedRectangleBorder] shape with a 28.0 radius otherwise. - final OutlinedBorder? viewShape; - - /// The style to use for the text being edited on the search view. - /// - /// If null, defaults to the `bodyLarge` text style from the current [Theme]. - /// The default text color is [ColorScheme.onSurface]. - final TextStyle? headerTextStyle; - - /// The style to use for the [viewHintText] on the search view. - /// - /// If null, the value of [SearchViewThemeData.headerHintStyle] will be used. - /// If this is also null, the value of [headerTextStyle] will be used. If this is also null, - /// defaults to the `bodyLarge` text style from the current [Theme]. The default - /// text color is [ColorScheme.onSurfaceVariant]. - final TextStyle? headerHintStyle; - - /// The color of the divider on the search view. - /// - /// If this property is null, then [SearchViewThemeData.dividerColor] is used. - /// If that is also null, the default value is [ColorScheme.outline]. - final Color? dividerColor; - - /// Optional size constraints for the search view. - /// - /// If null, the value of [SearchViewThemeData.constraints] will be used. If - /// this is also null, then the constraints defaults to: - /// ```dart - /// const BoxConstraints(minWidth: 360.0, minHeight: 240.0) - /// ``` - final BoxConstraints? viewConstraints; - - /// Called to create a widget which can open a search view route when it is tapped. - /// - /// The widget returned by this builder is faded out when it is tapped. - /// At the same time a search view route is faded in. - /// - /// This must not be null. - final SearchAnchorChildBuilder builder; - - /// Called to get the suggestion list for the search view. - /// - /// By default, the list returned by this builder is laid out in a [ListView]. - /// To get a different layout, use [viewBuilder] to override. - final SuggestionsBuilder suggestionsBuilder; - - @override - State createState() => _SearchAnchorState(); -} - -class _SearchAnchorState extends State { - bool _anchorIsVisible = true; - final GlobalKey _anchorKey = GlobalKey(); - bool get _viewIsOpen => !_anchorIsVisible; - late SearchController? _internalSearchController; - SearchController get _searchController => - widget.searchController ?? _internalSearchController!; - - @override - void initState() { - super.initState(); - if (widget.searchController == null) { - _internalSearchController = SearchController(); - } - _searchController._attach(this); - } - - @override - void dispose() { - super.dispose(); - _searchController._detach(this); - _internalSearchController = null; - } - - void _openView() { - Navigator.of(context).push(_SearchViewRoute( - viewLeading: widget.viewLeading, - viewTrailing: widget.viewTrailing, - viewHintText: widget.viewHintText, - viewBackgroundColor: widget.viewBackgroundColor, - viewElevation: widget.viewElevation, - viewSurfaceTintColor: widget.viewSurfaceTintColor, - viewSide: widget.viewSide, - viewShape: widget.viewShape, - viewHeaderTextStyle: widget.headerTextStyle, - viewHeaderHintStyle: widget.headerHintStyle, - dividerColor: widget.dividerColor, - viewConstraints: widget.viewConstraints, - showFullScreenView: getShowFullScreenView(), - toggleVisibility: toggleVisibility, - textDirection: Directionality.of(context), - viewBuilder: widget.viewBuilder, - anchorKey: _anchorKey, - searchController: _searchController, - suggestionsBuilder: widget.suggestionsBuilder, - )); - } - - void _closeView(String? selectedText) { - if (selectedText != null) { - _searchController.text = selectedText; - } - Navigator.of(context).pop(); - } - - Rect? getRect(GlobalKey key) { - final BuildContext? context = key.currentContext; - if (context != null) { - final RenderBox searchBarBox = context.findRenderObject()! as RenderBox; - final Size boxSize = searchBarBox.size; - final Offset boxLocation = searchBarBox.localToGlobal(Offset.zero); - return boxLocation & boxSize; - } - return null; - } - - bool toggleVisibility() { - setState(() { - _anchorIsVisible = !_anchorIsVisible; - }); - return _anchorIsVisible; - } - - bool getShowFullScreenView() { - if (widget.isFullScreen != null) { - return widget.isFullScreen!; - } - - switch (Theme.of(context).platform) { - case TargetPlatform.iOS: - case TargetPlatform.android: - case TargetPlatform.fuchsia: - return true; - case TargetPlatform.macOS: - case TargetPlatform.linux: - case TargetPlatform.windows: - return false; - } - } - - @override - Widget build(BuildContext context) { - return AnimatedOpacity( - key: _anchorKey, - opacity: _anchorIsVisible ? 1.0 : 0.0, - duration: _kAnchorFadeDuration, - child: GestureDetector( - onTap: _openView, - child: widget.builder(context, _searchController), - ), - ); - } -} - -class _SearchViewRoute extends PopupRoute<_SearchViewRoute> { - _SearchViewRoute({ - this.toggleVisibility, - this.textDirection, - this.viewBuilder, - this.viewLeading, - this.viewTrailing, - this.viewHintText, - this.viewBackgroundColor, - this.viewElevation, - this.viewSurfaceTintColor, - this.viewSide, - this.viewShape, - this.viewHeaderTextStyle, - this.viewHeaderHintStyle, - this.dividerColor, - this.viewConstraints, - required this.showFullScreenView, - required this.anchorKey, - required this.searchController, - required this.suggestionsBuilder, - }); - - final ValueGetter? toggleVisibility; - final TextDirection? textDirection; - final ViewBuilder? viewBuilder; - final Widget? viewLeading; - final Iterable? viewTrailing; - final String? viewHintText; - final Color? viewBackgroundColor; - final double? viewElevation; - final Color? viewSurfaceTintColor; - final BorderSide? viewSide; - final OutlinedBorder? viewShape; - final TextStyle? viewHeaderTextStyle; - final TextStyle? viewHeaderHintStyle; - final Color? dividerColor; - final BoxConstraints? viewConstraints; - final bool showFullScreenView; - final GlobalKey anchorKey; - final SearchController searchController; - final SuggestionsBuilder suggestionsBuilder; - - @override - Color? get barrierColor => Colors.transparent; - - @override - bool get barrierDismissible => true; - - @override - String? get barrierLabel => 'Dismiss'; - - late final SearchViewThemeData viewDefaults; - late final SearchViewThemeData viewTheme; - late final DividerThemeData dividerTheme; - final RectTween _rectTween = RectTween(); - - Rect? getRect() { - final BuildContext? context = anchorKey.currentContext; - if (context != null) { - final RenderBox searchBarBox = context.findRenderObject()! as RenderBox; - final Size boxSize = searchBarBox.size; - final Offset boxLocation = searchBarBox.localToGlobal(Offset.zero); - return boxLocation & boxSize; - } - return null; - } - - @override - TickerFuture didPush() { - assert(anchorKey.currentContext != null); - updateViewConfig(anchorKey.currentContext!); - updateTweens(anchorKey.currentContext!); - toggleVisibility?.call(); - return super.didPush(); - } - - @override - bool didPop(_SearchViewRoute? result) { - assert(anchorKey.currentContext != null); - updateTweens(anchorKey.currentContext!); - toggleVisibility?.call(); - return super.didPop(result); - } - - void updateViewConfig(BuildContext context) { - viewDefaults = - _SearchViewDefaultsM3(context, isFullScreen: showFullScreenView); - viewTheme = SearchViewTheme.of(context); - dividerTheme = DividerTheme.of(context); - } - - void updateTweens(BuildContext context) { - final Size screenSize = MediaQuery.of(context).size; - final Rect anchorRect = getRect() ?? Rect.zero; - - // Check if the search view goes off the screen. - final BoxConstraints effectiveConstraints = - viewConstraints ?? viewTheme.constraints ?? viewDefaults.constraints!; - final double verticalDistanceToEdge = screenSize.height - anchorRect.top; - final double endHeight = math.max(effectiveConstraints.minHeight, - math.min(screenSize.height * 2 / 3, verticalDistanceToEdge)); - _rectTween.begin = anchorRect; - - switch (textDirection ?? TextDirection.ltr) { - case TextDirection.ltr: - final double viewEdgeToScreenEdge = screenSize.width - anchorRect.left; - final double endWidth = math.max(effectiveConstraints.minWidth, - math.min(anchorRect.width, viewEdgeToScreenEdge)); - final Size endSize = Size(endWidth, endHeight); - _rectTween.end = showFullScreenView - ? Offset.zero & screenSize - : (anchorRect.topLeft & endSize); - return; - case TextDirection.rtl: - final double viewEdgeToScreenEdge = anchorRect.right; - final double endWidth = math.max(effectiveConstraints.minWidth, - math.min(anchorRect.width, viewEdgeToScreenEdge)); - final Offset topLeft = - Offset(math.max(anchorRect.right - endWidth, 0.0), anchorRect.top); - final Size endSize = Size(endWidth, endHeight); - _rectTween.end = - showFullScreenView ? Offset.zero & screenSize : (topLeft & endSize); - } - } - - @override - Widget buildPage(BuildContext context, Animation animation, - Animation secondaryAnimation) { - return Directionality( - textDirection: textDirection ?? TextDirection.ltr, - child: AnimatedBuilder( - animation: animation, - builder: (BuildContext context, Widget? child) { - final Animation curvedAnimation = CurvedAnimation( - parent: animation, - curve: Curves.easeInOutCubicEmphasized, - reverseCurve: Curves.easeInOutCubicEmphasized.flipped, - ); - - final Rect viewRect = _rectTween.evaluate(curvedAnimation)!; - final double topPadding = showFullScreenView - ? lerpDouble(0.0, MediaQuery.of(context).padding.top, - curvedAnimation.value)! - : 0.0; - - return FadeTransition( - opacity: CurvedAnimation( - parent: animation, - curve: _kViewFadeOnInterval, - reverseCurve: _kViewFadeOnInterval.flipped, - ), - child: _ViewContent( - viewLeading: viewLeading, - viewTrailing: viewTrailing, - viewHintText: viewHintText, - viewBackgroundColor: viewBackgroundColor, - viewElevation: viewElevation, - viewSurfaceTintColor: viewSurfaceTintColor, - viewSide: viewSide, - viewShape: viewShape, - viewHeaderTextStyle: viewHeaderTextStyle, - viewHeaderHintStyle: viewHeaderHintStyle, - dividerColor: dividerColor, - viewConstraints: viewConstraints, - showFullScreenView: showFullScreenView, - animation: curvedAnimation, - getRect: getRect, - topPadding: topPadding, - viewRect: viewRect, - viewDefaults: viewDefaults, - viewTheme: viewTheme, - dividerTheme: dividerTheme, - viewBuilder: viewBuilder, - searchController: searchController, - suggestionsBuilder: suggestionsBuilder, - ), - ); - }), - ); - } - - @override - Duration get transitionDuration => _kOpenViewDuration; -} - -class _ViewContent extends StatefulWidget { - const _ViewContent({ - this.viewBuilder, - this.viewLeading, - this.viewTrailing, - this.viewHintText, - this.viewBackgroundColor, - this.viewElevation, - this.viewSurfaceTintColor, - this.viewSide, - this.viewShape, - this.viewHeaderTextStyle, - this.viewHeaderHintStyle, - this.dividerColor, - this.viewConstraints, - required this.showFullScreenView, - required this.getRect, - required this.topPadding, - required this.animation, - required this.viewRect, - required this.viewDefaults, - required this.viewTheme, - required this.dividerTheme, - required this.searchController, - required this.suggestionsBuilder, - }); - - final ViewBuilder? viewBuilder; - final Widget? viewLeading; - final Iterable? viewTrailing; - final String? viewHintText; - final Color? viewBackgroundColor; - final double? viewElevation; - final Color? viewSurfaceTintColor; - final BorderSide? viewSide; - final OutlinedBorder? viewShape; - final TextStyle? viewHeaderTextStyle; - final TextStyle? viewHeaderHintStyle; - final Color? dividerColor; - final BoxConstraints? viewConstraints; - final bool showFullScreenView; - final ValueGetter getRect; - final double topPadding; - final Animation animation; - final Rect viewRect; - final SearchViewThemeData viewDefaults; - final SearchViewThemeData viewTheme; - final DividerThemeData dividerTheme; - final SearchController searchController; - final SuggestionsBuilder suggestionsBuilder; - - @override - State<_ViewContent> createState() => _ViewContentState(); -} - -class _ViewContentState extends State<_ViewContent> { - Size? _screenSize; - late Rect _viewRect; - late final SearchController _controller; - late Iterable result; - final FocusNode _focusNode = FocusNode(); - - @override - void initState() { - super.initState(); - _viewRect = widget.viewRect; - _controller = widget.searchController; - result = widget.suggestionsBuilder(context, _controller); - if (!_focusNode.hasFocus) { - _focusNode.requestFocus(); - } - } - - @override - void didUpdateWidget(covariant _ViewContent oldWidget) { - super.didUpdateWidget(oldWidget); - if (widget.viewRect != oldWidget.viewRect) { - setState(() { - _viewRect = widget.viewRect; - }); - } - } - - @override - void didChangeDependencies() { - super.didChangeDependencies(); - final Size updatedScreenSize = MediaQuery.of(context).size; - if (_screenSize != updatedScreenSize) { - _screenSize = updatedScreenSize; - setState(() { - final Rect anchorRect = widget.getRect() ?? _viewRect; - final BoxConstraints constraints = widget.viewConstraints ?? - widget.viewTheme.constraints ?? - widget.viewDefaults.constraints!; - final Size updatedViewSize = Size( - math.max(constraints.minWidth, anchorRect.width), _viewRect.height); - switch (Directionality.of(context)) { - case TextDirection.ltr: - final Offset updatedPosition = anchorRect.topLeft; - _viewRect = updatedPosition & updatedViewSize; - return; - case TextDirection.rtl: - final Offset topLeft = Offset( - math.max(anchorRect.right - updatedViewSize.width, 0.0), - anchorRect.top); - _viewRect = topLeft & updatedViewSize; - } - }); - } - } - - Widget viewBuilder(Iterable suggestions) { - if (widget.viewBuilder == null) { - return MediaQuery.removePadding( - context: context, - removeTop: true, - child: ListView(children: suggestions.toList()), - ); - } - return widget.viewBuilder!(suggestions); - } - - void updateSuggestions() { - setState(() { - result = widget.suggestionsBuilder(context, _controller); - }); - } - - @override - Widget build(BuildContext context) { - final Widget defaultLeading = IconButton( - icon: const Icon(Icons.arrow_back), - onPressed: () { - Navigator.of(context).pop(); - }, - style: const ButtonStyle(tapTargetSize: MaterialTapTargetSize.shrinkWrap), - ); - - final List defaultTrailing = [ - IconButton( - icon: const Icon(Icons.close), - onPressed: () { - _controller.clear(); - updateSuggestions(); - }, - ), - ]; - - final Color effectiveBackgroundColor = widget.viewBackgroundColor ?? - widget.viewTheme.backgroundColor ?? - widget.viewDefaults.backgroundColor!; - final Color effectiveSurfaceTint = widget.viewSurfaceTintColor ?? - widget.viewTheme.surfaceTintColor ?? - widget.viewDefaults.surfaceTintColor!; - final double effectiveElevation = widget.viewElevation ?? - widget.viewTheme.elevation ?? - widget.viewDefaults.elevation!; - final BorderSide? effectiveSide = - widget.viewSide ?? widget.viewTheme.side ?? widget.viewDefaults.side; - OutlinedBorder effectiveShape = widget.viewShape ?? - widget.viewTheme.shape ?? - widget.viewDefaults.shape!; - if (effectiveSide != null) { - effectiveShape = effectiveShape.copyWith(side: effectiveSide); - } - final Color effectiveDividerColor = widget.dividerColor ?? - widget.viewTheme.dividerColor ?? - widget.dividerTheme.color ?? - widget.viewDefaults.dividerColor!; - final TextStyle? effectiveTextStyle = widget.viewHeaderTextStyle ?? - widget.viewTheme.headerTextStyle ?? - widget.viewDefaults.headerTextStyle; - final TextStyle? effectiveHintStyle = widget.viewHeaderHintStyle ?? - widget.viewTheme.headerHintStyle ?? - widget.viewHeaderTextStyle ?? - widget.viewTheme.headerTextStyle ?? - widget.viewDefaults.headerHintStyle; - - final Widget viewDivider = DividerTheme( - data: widget.dividerTheme.copyWith(color: effectiveDividerColor), - child: const Divider(height: 1), - ); - - return Align( - alignment: Alignment.topLeft, - child: Transform.translate( - offset: _viewRect.topLeft, - child: SizedBox( - width: _viewRect.width, - height: _viewRect.height, - child: Material( - shape: effectiveShape, - color: effectiveBackgroundColor, - surfaceTintColor: effectiveSurfaceTint, - elevation: effectiveElevation, - child: FadeTransition( - opacity: CurvedAnimation( - parent: widget.animation, - curve: _kViewIconsFadeOnInterval, - reverseCurve: _kViewIconsFadeOnInterval.flipped, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Padding( - padding: EdgeInsets.only(top: widget.topPadding), - child: SafeArea( - top: false, - bottom: false, - child: SearchBar( - constraints: widget.showFullScreenView - ? BoxConstraints( - minHeight: - _SearchViewDefaultsM3.fullScreenBarHeight) - : null, - focusNode: _focusNode, - leading: widget.viewLeading ?? defaultLeading, - trailing: widget.viewTrailing ?? defaultTrailing, - hintText: widget.viewHintText, - backgroundColor: const MaterialStatePropertyAll( - Colors.transparent), - overlayColor: const MaterialStatePropertyAll( - Colors.transparent), - elevation: const MaterialStatePropertyAll(0.0), - textStyle: MaterialStatePropertyAll( - effectiveTextStyle), - hintStyle: MaterialStatePropertyAll( - effectiveHintStyle), - controller: _controller, - onChanged: (_) { - updateSuggestions(); - }, - ), - ), - ), - FadeTransition( - opacity: CurvedAnimation( - parent: widget.animation, - curve: _kViewDividerFadeOnInterval, - reverseCurve: _kViewFadeOnInterval.flipped, - ), - child: viewDivider), - Expanded( - child: FadeTransition( - opacity: CurvedAnimation( - parent: widget.animation, - curve: _kViewListFadeOnInterval, - reverseCurve: _kViewListFadeOnInterval.flipped, - ), - child: viewBuilder(result), - ), - ), - ], - ), - ), - ), - ), - ), - ); - } -} - -class _SearchAnchorWithSearchBar extends SearchAnchor { - _SearchAnchorWithSearchBar( - {Widget? barLeading, - Iterable? barTrailing, - String? barHintText, - GestureTapCallback? onTap, - MaterialStateProperty? barElevation, - MaterialStateProperty? barBackgroundColor, - MaterialStateProperty? barOverlayColor, - MaterialStateProperty? barSide, - MaterialStateProperty? barShape, - MaterialStateProperty? barPadding, - MaterialStateProperty? barTextStyle, - MaterialStateProperty? barHintStyle, - super.viewLeading, - super.viewTrailing, - String? viewHintText, - super.viewBackgroundColor, - super.viewElevation, - super.viewSide, - super.viewShape, - TextStyle? viewHeaderTextStyle, - TextStyle? viewHeaderHintStyle, - super.dividerColor, - BoxConstraints? constraints, - super.isFullScreen, - super.searchController, - required super.suggestionsBuilder}) - : super( - viewHintText: viewHintText ?? barHintText, - headerTextStyle: viewHeaderTextStyle, - headerHintStyle: viewHeaderHintStyle, - builder: (BuildContext context, SearchController controller) { - return SearchBar( - constraints: constraints, - controller: controller, - onTap: () { - controller.openView(); - onTap?.call(); - }, - onChanged: (_) { - controller.openView(); - }, - hintText: barHintText, - hintStyle: barHintStyle, - textStyle: barTextStyle, - elevation: barElevation, - backgroundColor: barBackgroundColor, - overlayColor: barOverlayColor, - side: barSide, - shape: barShape, - padding: barPadding ?? - const MaterialStatePropertyAll( - EdgeInsets.symmetric(horizontal: 16.0)), - leading: barLeading ?? const Icon(Icons.search), - trailing: barTrailing, - ); - }); -} - -/// A controller to manage a search view created by [SearchAnchor]. -/// -/// A [SearchController] is used to control a menu after it has been created, -/// with methods such as [openView] and [closeView]. It can also control the text in the -/// input field. -/// -/// See also: -/// -/// * [SearchAnchor], a widget that defines a region that opens a search view. -/// * [TextEditingController], A controller for an editable text field. -class SearchController extends TextEditingController { - // The anchor that this controller controls. - // - // This is set automatically when a [SearchController] is given to the anchor - // it controls. - _SearchAnchorState? _anchor; - - /// Whether or not the associated search view is currently open. - bool get isOpen { - assert(_anchor != null); - return _anchor!._viewIsOpen; - } - - /// Opens the search view that this controller is associated with. - void openView() { - assert(_anchor != null); - _anchor!._openView(); - } - - /// Close the search view that this search controller is associated with. - /// - /// If `selectedText` is given, then the text value of the controller is set to - /// `selectedText`. - void closeView(String? selectedText) { - assert(_anchor != null); - _anchor!._closeView(selectedText); - } - - // ignore: use_setters_to_change_properties - void _attach(_SearchAnchorState anchor) { - _anchor = anchor; - } - - void _detach(_SearchAnchorState anchor) { - if (_anchor == anchor) { - _anchor = null; - } - } -} - -/// A Material Design search bar. -/// -/// Search bars include a [leading] Search icon, a text input field and optional -/// [trailing] icons. A search bar is typically used to open a search view. -/// It is the default trigger for a search view. -/// -/// For [TextDirection.ltr], the [leading] widget is on the left side of the bar. -/// It should contain either a navigational action (such as a menu or up-arrow) -/// or a non-functional search icon. -/// -/// The [trailing] is an optional list that appears at the other end of -/// the search bar. Typically only one or two action icons are included. -/// These actions can represent additional modes of searching (like voice search), -/// a separate high-level action (such as current location) or an overflow menu. -class SearchBar extends StatefulWidget { - /// Creates a Material Design search bar. - const SearchBar({ - super.key, - this.controller, - this.focusNode, - this.hintText, - this.leading, - this.trailing, - this.onTap, - this.onChanged, - this.constraints, - this.elevation, - this.backgroundColor, - this.shadowColor, - this.surfaceTintColor, - this.overlayColor, - this.side, - this.shape, - this.padding, - this.textStyle, - this.hintStyle, - }); - - /// Controls the text being edited in the search bar's text field. - /// - /// If null, this widget will create its own [TextEditingController]. - final TextEditingController? controller; - - /// {@macro flutter.widgets.Focus.focusNode} - final FocusNode? focusNode; - - /// Text that suggests what sort of input the field accepts. - /// - /// Displayed at the same location on the screen where text may be entered - /// when the input is empty. - /// - /// Defaults to null. - final String? hintText; - - /// A widget to display before the text input field. - /// - /// Typically the [leading] widget is an [Icon] or an [IconButton]. - final Widget? leading; - - /// A list of Widgets to display in a row after the text field. - /// - /// Typically these actions can represent additional modes of searching - /// (like voice search), an avatar, a separate high-level action (such as - /// current location) or an overflow menu. There should not be more than - /// two trailing actions. - final Iterable? trailing; - - /// Called when the user taps this search bar. - final GestureTapCallback? onTap; - - /// Invoked upon user input. - final ValueChanged? onChanged; - - /// Optional size constraints for the search bar. - /// - /// If null, the value of [SearchBarThemeData.constraints] will be used. If - /// this is also null, then the constraints defaults to: - /// ```dart - /// const BoxConstraints(minWidth: 360.0, maxWidth: 800.0, minHeight: 56.0) - /// ``` - final BoxConstraints? constraints; - - /// The elevation of the search bar's [Material]. - /// - /// If null, the value of [SearchBarThemeData.elevation] will be used. If this - /// is also null, then default value is 6.0. - final MaterialStateProperty? elevation; - - /// The search bar's background fill color. - /// - /// If null, the value of [SearchBarThemeData.backgroundColor] will be used. - /// If this is also null, then the default value is [ColorScheme.surface]. - final MaterialStateProperty? backgroundColor; - - /// The shadow color of the search bar's [Material]. - /// - /// If null, the value of [SearchBarThemeData.shadowColor] will be used. - /// If this is also null, then the default value is [ColorScheme.shadow]. - final MaterialStateProperty? shadowColor; - - /// The surface tint color of the search bar's [Material]. - /// - /// See [Material.surfaceTintColor] for more details. - /// - /// If null, the value of [SearchBarThemeData.surfaceTintColor] will be used. - /// If this is also null, then the default value is [ColorScheme.surfaceTint]. - final MaterialStateProperty? surfaceTintColor; - - /// The highlight color that's typically used to indicate that - /// the search bar is focused, hovered, or pressed. - final MaterialStateProperty? overlayColor; - - /// The color and weight of the search bar's outline. - /// - /// This value is combined with [shape] to create a shape decorated - /// with an outline. - /// - /// If null, the value of [SearchBarThemeData.side] will be used. If this is - /// also null, the search bar doesn't have a side by default. - final MaterialStateProperty? side; - - /// The shape of the search bar's underlying [Material]. - /// - /// This shape is combined with [side] to create a shape decorated - /// with an outline. - /// - /// If null, the value of [SearchBarThemeData.shape] will be used. - /// If this is also null, defaults to [StadiumBorder]. - final MaterialStateProperty? shape; - - /// The padding between the search bar's boundary and its contents. - /// - /// If null, the value of [SearchBarThemeData.padding] will be used. - /// If this is also null, then the default value is 16.0 horizontally. - final MaterialStateProperty? padding; - - /// The style to use for the text being edited. - /// - /// If null, defaults to the `bodyLarge` text style from the current [Theme]. - /// The default text color is [ColorScheme.onSurface]. - final MaterialStateProperty? textStyle; - - /// The style to use for the [hintText]. - /// - /// If null, the value of [SearchBarThemeData.hintStyle] will be used. If this - /// is also null, the value of [textStyle] will be used. If this is also null, - /// defaults to the `bodyLarge` text style from the current [Theme]. - /// The default text color is [ColorScheme.onSurfaceVariant]. - final MaterialStateProperty? hintStyle; - - @override - State createState() => _SearchBarState(); -} - -class _SearchBarState extends State { - late final MaterialStatesController _internalStatesController; - late final FocusNode _focusNode; - - @override - void initState() { - super.initState(); - _internalStatesController = MaterialStatesController(); - _internalStatesController.addListener(() { - setState(() {}); - }); - _focusNode = widget.focusNode ?? FocusNode(); - } - - @override - void dispose() { - _internalStatesController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final TextDirection textDirection = Directionality.of(context); - final ColorScheme colorScheme = Theme.of(context).colorScheme; - final IconThemeData iconTheme = IconTheme.of(context); - final SearchBarThemeData searchBarTheme = SearchBarTheme.of(context); - final SearchBarThemeData defaults = _SearchBarDefaultsM3(context); - - T? resolve( - MaterialStateProperty? widgetValue, - MaterialStateProperty? themeValue, - MaterialStateProperty? defaultValue, - ) { - final Set states = _internalStatesController.value; - return widgetValue?.resolve(states) ?? - themeValue?.resolve(states) ?? - defaultValue?.resolve(states); - } - - final TextStyle? effectiveTextStyle = resolve( - widget.textStyle, searchBarTheme.textStyle, defaults.textStyle); - final double? effectiveElevation = resolve( - widget.elevation, searchBarTheme.elevation, defaults.elevation); - final Color? effectiveShadowColor = resolve( - widget.shadowColor, searchBarTheme.shadowColor, defaults.shadowColor); - final Color? effectiveBackgroundColor = resolve( - widget.backgroundColor, - searchBarTheme.backgroundColor, - defaults.backgroundColor); - final Color? effectiveSurfaceTintColor = resolve( - widget.surfaceTintColor, - searchBarTheme.surfaceTintColor, - defaults.surfaceTintColor); - final OutlinedBorder? effectiveShape = resolve( - widget.shape, searchBarTheme.shape, defaults.shape); - final BorderSide? effectiveSide = - resolve(widget.side, searchBarTheme.side, defaults.side); - final EdgeInsetsGeometry? effectivePadding = resolve( - widget.padding, searchBarTheme.padding, defaults.padding); - final MaterialStateProperty? effectiveOverlayColor = - widget.overlayColor ?? - searchBarTheme.overlayColor ?? - defaults.overlayColor; - - final Set states = _internalStatesController.value; - final TextStyle? effectiveHintStyle = widget.hintStyle?.resolve(states) ?? - searchBarTheme.hintStyle?.resolve(states) ?? - widget.textStyle?.resolve(states) ?? - searchBarTheme.textStyle?.resolve(states) ?? - defaults.hintStyle?.resolve(states); - - final bool isDark = Theme.of(context).brightness == Brightness.dark; - bool isIconThemeColorDefault(Color? color) { - if (isDark) { - return color == kDefaultIconLightColor; - } - return color == kDefaultIconDarkColor; - } - - Widget? leading; - if (widget.leading != null) { - leading = IconTheme.merge( - data: isIconThemeColorDefault(iconTheme.color) - ? IconThemeData(color: colorScheme.onSurface) - : iconTheme, - child: widget.leading!, - ); - } - - List? trailing; - if (widget.trailing != null) { - trailing = widget.trailing - ?.map((Widget trailing) => IconTheme.merge( - data: isIconThemeColorDefault(iconTheme.color) - ? IconThemeData(color: colorScheme.onSurfaceVariant) - : iconTheme, - child: trailing, - )) - .toList(); - } - - return ConstrainedBox( - constraints: widget.constraints ?? - searchBarTheme.constraints ?? - defaults.constraints!, - child: Material( - elevation: effectiveElevation!, - shadowColor: effectiveShadowColor, - color: effectiveBackgroundColor, - surfaceTintColor: effectiveSurfaceTintColor, - shape: effectiveShape?.copyWith(side: effectiveSide), - child: InkWell( - onTap: () { - widget.onTap?.call(); - _focusNode.requestFocus(); - }, - overlayColor: effectiveOverlayColor, - customBorder: effectiveShape?.copyWith(side: effectiveSide), - statesController: _internalStatesController, - child: Padding( - padding: effectivePadding!, - child: Row( - textDirection: textDirection, - children: [ - if (leading != null) leading, - Expanded( - child: IgnorePointer( - child: Padding( - padding: effectivePadding, - child: TextField( - focusNode: _focusNode, - onChanged: widget.onChanged, - controller: widget.controller, - style: effectiveTextStyle, - decoration: InputDecoration( - border: InputBorder.none, - hintText: widget.hintText, - hintStyle: effectiveHintStyle, - ), - ), - ), - )), - if (trailing != null) ...trailing, - ], - ), - ), - ), - ), - ); - } -} - -// BEGIN GENERATED TOKEN PROPERTIES - SearchBar - -// Do not edit by hand. The code between the "BEGIN GENERATED" and -// "END GENERATED" comments are generated from data in the Material -// Design token database by the script: -// dev/tools/gen_defaults/bin/gen_defaults.dart. - -// Token database version: v0_162 - -class _SearchBarDefaultsM3 extends SearchBarThemeData { - _SearchBarDefaultsM3(this.context); - - final BuildContext context; - late final ColorScheme _colors = Theme.of(context).colorScheme; - late final TextTheme _textTheme = Theme.of(context).textTheme; - - @override - MaterialStateProperty? get backgroundColor => - MaterialStatePropertyAll(_colors.surface); - - @override - MaterialStateProperty? get elevation => - const MaterialStatePropertyAll(6.0); - - @override - MaterialStateProperty? get shadowColor => - MaterialStatePropertyAll(_colors.shadow); - - @override - MaterialStateProperty? get surfaceTintColor => - MaterialStatePropertyAll(_colors.surfaceTint); - - @override - MaterialStateProperty? get overlayColor => - MaterialStateProperty.resolveWith((Set states) { - if (states.contains(MaterialState.pressed)) { - return _colors.onSurface.withOpacity(0.12); - } - if (states.contains(MaterialState.hovered)) { - return _colors.onSurface.withOpacity(0.08); - } - if (states.contains(MaterialState.focused)) { - return Colors.transparent; - } - return Colors.transparent; - }); - - // No default side - - @override - MaterialStateProperty? get shape => - const MaterialStatePropertyAll(StadiumBorder()); - - @override - MaterialStateProperty? get padding => - const MaterialStatePropertyAll( - EdgeInsets.symmetric(horizontal: 8.0)); - - @override - MaterialStateProperty get textStyle => - MaterialStatePropertyAll( - _textTheme.bodyLarge?.copyWith(color: _colors.onSurface)); - - @override - MaterialStateProperty get hintStyle => - MaterialStatePropertyAll( - _textTheme.bodyLarge?.copyWith(color: _colors.onSurfaceVariant)); - - @override - BoxConstraints get constraints => - const BoxConstraints(minWidth: 360.0, maxWidth: 800.0, minHeight: 56.0); -} - -// END GENERATED TOKEN PROPERTIES - SearchBar - -// BEGIN GENERATED TOKEN PROPERTIES - SearchView - -// Do not edit by hand. The code between the "BEGIN GENERATED" and -// "END GENERATED" comments are generated from data in the Material -// Design token database by the script: -// dev/tools/gen_defaults/bin/gen_defaults.dart. - -// Token database version: v0_162 - -class _SearchViewDefaultsM3 extends SearchViewThemeData { - _SearchViewDefaultsM3(this.context, {required this.isFullScreen}); - - final BuildContext context; - final bool isFullScreen; - late final ColorScheme _colors = Theme.of(context).colorScheme; - late final TextTheme _textTheme = Theme.of(context).textTheme; - - static double fullScreenBarHeight = 72.0; - - @override - Color? get backgroundColor => _colors.surface; - - @override - double? get elevation => 6.0; - - @override - Color? get surfaceTintColor => _colors.surfaceTint; - - // No default side - - @override - OutlinedBorder? get shape => isFullScreen - ? const RoundedRectangleBorder() - : const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(28.0))); - - @override - TextStyle? get headerTextStyle => - _textTheme.bodyLarge?.copyWith(color: _colors.onSurface); - - @override - TextStyle? get headerHintStyle => - _textTheme.bodyLarge?.copyWith(color: _colors.onSurfaceVariant); - - @override - BoxConstraints get constraints => - const BoxConstraints(minWidth: 360.0, minHeight: 240.0); - - @override - Color? get dividerColor => _colors.outline; -} - -// Copyright 2014 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Examples can assume: -// late BuildContext context; - -/// Defines default property values for descendant [SearchBar] widgets. -/// -/// Descendant widgets obtain the current [SearchBarThemeData] object using -/// `SearchBarTheme.of(context)`. Instances of [SearchBarThemeData] can be customized -/// with [SearchBarThemeData.copyWith]. -/// -/// Typically a [SearchBarThemeData] is specified as part of the overall [Theme] -/// with [ThemeData.searchBarTheme]. -/// -/// All [SearchBarThemeData] properties are `null` by default. When null, the -/// [SearchBar] will use the values from [ThemeData] if they exist, otherwise it -/// will provide its own defaults based on the overall [Theme]'s colorScheme. -/// See the individual [SearchBar] properties for details. -/// -/// See also: -/// -/// * [ThemeData], which describes the overall theme information for the -/// application. -@immutable -class SearchBarThemeData with Diagnosticable { - /// Creates a theme that can be used for [ThemeData.searchBarTheme]. - const SearchBarThemeData({ - this.elevation, - this.backgroundColor, - this.shadowColor, - this.surfaceTintColor, - this.overlayColor, - this.side, - this.shape, - this.padding, - this.textStyle, - this.hintStyle, - this.constraints, - }); - - /// Overrides the default value of the [SearchBar.elevation]. - final MaterialStateProperty? elevation; - - /// Overrides the default value of the [SearchBar.backgroundColor]. - final MaterialStateProperty? backgroundColor; - - /// Overrides the default value of the [SearchBar.shadowColor]. - final MaterialStateProperty? shadowColor; - - /// Overrides the default value of the [SearchBar.surfaceTintColor]. - final MaterialStateProperty? surfaceTintColor; - - /// Overrides the default value of the [SearchBar.overlayColor]. - final MaterialStateProperty? overlayColor; - - /// Overrides the default value of the [SearchBar.side]. - final MaterialStateProperty? side; - - /// Overrides the default value of the [SearchBar.shape]. - final MaterialStateProperty? shape; - - /// Overrides the default value for [SearchBar.padding]. - final MaterialStateProperty? padding; - - /// Overrides the default value for [SearchBar.textStyle]. - final MaterialStateProperty? textStyle; - - /// Overrides the default value for [SearchBar.hintStyle]. - final MaterialStateProperty? hintStyle; - - /// Overrides the value of size constraints for [SearchBar]. - final BoxConstraints? constraints; - - /// Creates a copy of this object but with the given fields replaced with the - /// new values. - SearchBarThemeData copyWith({ - MaterialStateProperty? elevation, - MaterialStateProperty? backgroundColor, - MaterialStateProperty? shadowColor, - MaterialStateProperty? surfaceTintColor, - MaterialStateProperty? overlayColor, - MaterialStateProperty? side, - MaterialStateProperty? shape, - MaterialStateProperty? padding, - MaterialStateProperty? textStyle, - MaterialStateProperty? hintStyle, - BoxConstraints? constraints, - }) { - return SearchBarThemeData( - elevation: elevation ?? this.elevation, - backgroundColor: backgroundColor ?? this.backgroundColor, - shadowColor: shadowColor ?? this.shadowColor, - surfaceTintColor: surfaceTintColor ?? this.surfaceTintColor, - overlayColor: overlayColor ?? this.overlayColor, - side: side ?? this.side, - shape: shape ?? this.shape, - padding: padding ?? this.padding, - textStyle: textStyle ?? this.textStyle, - hintStyle: hintStyle ?? this.hintStyle, - constraints: constraints ?? this.constraints, - ); - } - - /// Linearly interpolate between two [SearchBarThemeData]s. - /// - /// {@macro dart.ui.shadow.lerp} - static SearchBarThemeData? lerp( - SearchBarThemeData? a, SearchBarThemeData? b, double t) { - if (identical(a, b)) { - return a; - } - return SearchBarThemeData( - elevation: MaterialStateProperty.lerp( - a?.elevation, b?.elevation, t, lerpDouble), - backgroundColor: MaterialStateProperty.lerp( - a?.backgroundColor, b?.backgroundColor, t, Color.lerp), - shadowColor: MaterialStateProperty.lerp( - a?.shadowColor, b?.shadowColor, t, Color.lerp), - surfaceTintColor: MaterialStateProperty.lerp( - a?.surfaceTintColor, b?.surfaceTintColor, t, Color.lerp), - overlayColor: MaterialStateProperty.lerp( - a?.overlayColor, b?.overlayColor, t, Color.lerp), - side: _lerpSides(a?.side, b?.side, t), - shape: MaterialStateProperty.lerp( - a?.shape, b?.shape, t, OutlinedBorder.lerp), - padding: MaterialStateProperty.lerp( - a?.padding, b?.padding, t, EdgeInsetsGeometry.lerp), - textStyle: MaterialStateProperty.lerp( - a?.textStyle, b?.textStyle, t, TextStyle.lerp), - hintStyle: MaterialStateProperty.lerp( - a?.hintStyle, b?.hintStyle, t, TextStyle.lerp), - constraints: BoxConstraints.lerp(a?.constraints, b?.constraints, t), - ); - } - - @override - int get hashCode => Object.hash( - elevation, - backgroundColor, - shadowColor, - surfaceTintColor, - overlayColor, - side, - shape, - padding, - textStyle, - hintStyle, - constraints, - ); - - @override - bool operator ==(Object other) { - if (identical(this, other)) { - return true; - } - if (other.runtimeType != runtimeType) { - return false; - } - return other is SearchBarThemeData && - other.elevation == elevation && - other.backgroundColor == backgroundColor && - other.shadowColor == shadowColor && - other.surfaceTintColor == surfaceTintColor && - other.overlayColor == overlayColor && - other.side == side && - other.shape == shape && - other.padding == padding && - other.textStyle == textStyle && - other.hintStyle == hintStyle && - other.constraints == constraints; - } - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties.add(DiagnosticsProperty>( - 'elevation', elevation, - defaultValue: null)); - properties.add(DiagnosticsProperty>( - 'backgroundColor', backgroundColor, - defaultValue: null)); - properties.add(DiagnosticsProperty>( - 'shadowColor', shadowColor, - defaultValue: null)); - properties.add(DiagnosticsProperty>( - 'surfaceTintColor', surfaceTintColor, - defaultValue: null)); - properties.add(DiagnosticsProperty>( - 'overlayColor', overlayColor, - defaultValue: null)); - properties.add(DiagnosticsProperty>( - 'side', side, - defaultValue: null)); - properties.add(DiagnosticsProperty>( - 'shape', shape, - defaultValue: null)); - properties.add( - DiagnosticsProperty>( - 'padding', padding, - defaultValue: null)); - properties.add(DiagnosticsProperty>( - 'textStyle', textStyle, - defaultValue: null)); - properties.add(DiagnosticsProperty>( - 'hintStyle', hintStyle, - defaultValue: null)); - properties.add(DiagnosticsProperty( - 'constraints', constraints, - defaultValue: null)); - } - - // Special case because BorderSide.lerp() doesn't support null arguments - static MaterialStateProperty? _lerpSides( - MaterialStateProperty? a, - MaterialStateProperty? b, - double t) { - if (identical(a, b)) { - return a; - } - return _LerpSides(a, b, t); - } -} - -class _LerpSides implements MaterialStateProperty { - const _LerpSides(this.a, this.b, this.t); - - final MaterialStateProperty? a; - final MaterialStateProperty? b; - final double t; - - @override - BorderSide? resolve(Set states) { - final BorderSide? resolvedA = a?.resolve(states); - final BorderSide? resolvedB = b?.resolve(states); - if (identical(resolvedA, resolvedB)) { - return resolvedA; - } - if (resolvedA == null) { - return BorderSide.lerp( - BorderSide(width: 0, color: resolvedB!.color.withAlpha(0)), - resolvedB, - t); - } - if (resolvedB == null) { - return BorderSide.lerp(resolvedA, - BorderSide(width: 0, color: resolvedA.color.withAlpha(0)), t); - } - return BorderSide.lerp(resolvedA, resolvedB, t); - } -} - -/// Applies a search bar theme to descendant [SearchBar] widgets. -/// -/// Descendant widgets obtain the current theme's [SearchBarTheme] object using -/// [SearchBarTheme.of]. When a widget uses [SearchBarTheme.of], it is automatically -/// rebuilt if the theme later changes. -/// -/// A search bar theme can be specified as part of the overall Material theme using -/// [ThemeData.searchBarTheme]. -/// -/// See also: -/// -/// * [SearchBarThemeData], which describes the actual configuration of a search bar -/// theme. -class SearchBarTheme extends InheritedWidget { - /// Constructs a search bar theme that configures all descendant [SearchBar] widgets. - const SearchBarTheme({ - super.key, - required this.data, - required super.child, - }); - - /// The properties used for all descendant [SearchBar] widgets. - final SearchBarThemeData data; - - /// Returns the configuration [data] from the closest [SearchBarTheme] ancestor. - /// If there is no ancestor, it returns [ThemeData.searchBarTheme]. - /// - /// Typical usage is as follows: - /// - /// ```dart - /// SearchBarThemeData theme = SearchBarTheme.of(context); - /// ``` - static SearchBarThemeData of(BuildContext context) { - final SearchBarTheme? searchBarTheme = - context.dependOnInheritedWidgetOfExactType(); - return searchBarTheme?.data ?? const SearchBarThemeData(); - } - - @override - bool updateShouldNotify(SearchBarTheme oldWidget) => data != oldWidget.data; -} - -// Copyright 2014 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Examples can assume: -// late BuildContext context; - -/// Defines the configuration of the search views created by the [SearchAnchor] -/// widget. -/// -/// Descendant widgets obtain the current [SearchViewThemeData] object using -/// `SearchViewTheme.of(context)`. -/// -/// Typically, a [SearchViewThemeData] is specified as part of the overall [Theme] -/// with [ThemeData.searchViewTheme]. Otherwise, [SearchViewTheme] can be used -/// to configure its own widget subtree. -/// -/// All [SearchViewThemeData] properties are `null` by default. If any of these -/// properties are null, the search view will provide its own defaults. -/// -/// See also: -/// -/// * [ThemeData], which describes the overall theme for the application. -/// * [SearchBarThemeData], which describes the theme for the search bar itself in a -/// [SearchBar] widget. -/// * [SearchAnchor], which is used to open a search view route. -@immutable -class SearchViewThemeData with Diagnosticable { - /// Creates a theme that can be used for [ThemeData.searchViewTheme]. - const SearchViewThemeData({ - this.backgroundColor, - this.elevation, - this.surfaceTintColor, - this.constraints, - this.side, - this.shape, - this.headerTextStyle, - this.headerHintStyle, - this.dividerColor, - }); - - /// Overrides the default value of the [SearchAnchor.viewBackgroundColor]. - final Color? backgroundColor; - - /// Overrides the default value of the [SearchAnchor.viewElevation]. - final double? elevation; - - /// Overrides the default value of the [SearchAnchor.viewSurfaceTintColor]. - final Color? surfaceTintColor; - - /// Overrides the default value of the [SearchAnchor.viewSide]. - final BorderSide? side; - - /// Overrides the default value of the [SearchAnchor.viewShape]. - final OutlinedBorder? shape; - - /// Overrides the default value for [SearchAnchor.headerTextStyle]. - final TextStyle? headerTextStyle; - - /// Overrides the default value for [SearchAnchor.headerHintStyle]. - final TextStyle? headerHintStyle; - - /// Overrides the value of size constraints for [SearchAnchor.viewConstraints]. - final BoxConstraints? constraints; - - /// Overrides the value of the divider color for [SearchAnchor.dividerColor]. - final Color? dividerColor; - - /// Creates a copy of this object but with the given fields replaced with the - /// new values. - SearchViewThemeData copyWith({ - Color? backgroundColor, - double? elevation, - Color? surfaceTintColor, - BorderSide? side, - OutlinedBorder? shape, - TextStyle? headerTextStyle, - TextStyle? headerHintStyle, - BoxConstraints? constraints, - Color? dividerColor, - }) { - return SearchViewThemeData( - backgroundColor: backgroundColor ?? this.backgroundColor, - elevation: elevation ?? this.elevation, - surfaceTintColor: surfaceTintColor ?? this.surfaceTintColor, - side: side ?? this.side, - shape: shape ?? this.shape, - headerTextStyle: headerTextStyle ?? this.headerTextStyle, - headerHintStyle: headerHintStyle ?? this.headerHintStyle, - constraints: constraints ?? this.constraints, - dividerColor: dividerColor ?? this.dividerColor, - ); - } - - /// Linearly interpolate between two [SearchViewThemeData]s. - static SearchViewThemeData? lerp( - SearchViewThemeData? a, SearchViewThemeData? b, double t) { - if (identical(a, b)) { - return a; - } - return SearchViewThemeData( - backgroundColor: Color.lerp(a?.backgroundColor, b?.backgroundColor, t), - elevation: lerpDouble(a?.elevation, b?.elevation, t), - surfaceTintColor: Color.lerp(a?.surfaceTintColor, b?.surfaceTintColor, t), - side: _lerpSides(a?.side, b?.side, t), - shape: OutlinedBorder.lerp(a?.shape, b?.shape, t), - headerTextStyle: - TextStyle.lerp(a?.headerTextStyle, b?.headerTextStyle, t), - headerHintStyle: - TextStyle.lerp(a?.headerTextStyle, b?.headerTextStyle, t), - constraints: BoxConstraints.lerp(a?.constraints, b?.constraints, t), - dividerColor: Color.lerp(a?.dividerColor, b?.dividerColor, t), - ); - } - - @override - int get hashCode => Object.hash( - backgroundColor, - elevation, - surfaceTintColor, - side, - shape, - headerTextStyle, - headerHintStyle, - constraints, - dividerColor, - ); - - @override - bool operator ==(Object other) { - if (identical(this, other)) { - return true; - } - if (other.runtimeType != runtimeType) { - return false; - } - return other is SearchViewThemeData && - other.backgroundColor == backgroundColor && - other.elevation == elevation && - other.surfaceTintColor == surfaceTintColor && - other.side == side && - other.shape == shape && - other.headerTextStyle == headerTextStyle && - other.headerHintStyle == headerHintStyle && - other.constraints == constraints && - other.dividerColor == dividerColor; - } - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties.add(DiagnosticsProperty( - 'backgroundColor', backgroundColor, - defaultValue: null)); - properties.add(DiagnosticsProperty('elevation', elevation, - defaultValue: null)); - properties.add(DiagnosticsProperty( - 'surfaceTintColor', surfaceTintColor, - defaultValue: null)); - properties.add( - DiagnosticsProperty('side', side, defaultValue: null)); - properties.add(DiagnosticsProperty('shape', shape, - defaultValue: null)); - properties.add(DiagnosticsProperty( - 'headerTextStyle', headerTextStyle, - defaultValue: null)); - properties.add(DiagnosticsProperty( - 'headerHintStyle', headerHintStyle, - defaultValue: null)); - properties.add(DiagnosticsProperty( - 'constraints', constraints, - defaultValue: null)); - properties.add(DiagnosticsProperty('dividerColor', dividerColor, - defaultValue: null)); - } - - // Special case because BorderSide.lerp() doesn't support null arguments - static BorderSide? _lerpSides(BorderSide? a, BorderSide? b, double t) { - if (a == null || b == null) { - return null; - } - if (identical(a, b)) { - return a; - } - return BorderSide.lerp(a, b, t); - } -} - -/// An inherited widget that defines the configuration in this widget's -/// descendants for search view created by the [SearchAnchor] widget. -/// -/// A search view theme can be specified as part of the overall Material theme using -/// [ThemeData.searchViewTheme]. -/// -/// See also: -/// -/// * [SearchViewThemeData], which describes the actual configuration of a search view -/// theme. -class SearchViewTheme extends InheritedWidget { - /// Creates a const theme that controls the configurations for the search view - /// created by the [SearchAnchor] widget. - const SearchViewTheme({ - super.key, - required this.data, - required super.child, - }); - - /// The properties used for all descendant [SearchAnchor] widgets. - final SearchViewThemeData data; - - /// Returns the configuration [data] from the closest [SearchViewTheme] ancestor. - /// If there is no ancestor, it returns [ThemeData.searchViewTheme]. - /// - /// Typical usage is as follows: - /// - /// ```dart - /// SearchViewThemeData theme = SearchViewTheme.of(context); - /// ``` - static SearchViewThemeData of(BuildContext context) { - final SearchViewTheme? searchViewTheme = - context.dependOnInheritedWidgetOfExactType(); - return searchViewTheme?.data ?? const SearchViewThemeData(); - } - - @override - bool updateShouldNotify(SearchViewTheme oldWidget) => data != oldWidget.data; -} diff --git a/lib/features/document_search/cubit/document_search_cubit.dart b/lib/features/document_search/cubit/document_search_cubit.dart index 9afa344..74dd3a1 100644 --- a/lib/features/document_search/cubit/document_search_cubit.dart +++ b/lib/features/document_search/cubit/document_search_cubit.dart @@ -56,7 +56,7 @@ class DocumentSearchCubit extends Cubit with DocumentPaging final searchFilter = DocumentFilter( query: TextQuery.extended(query), ); - + await updateFilter(filter: searchFilter); emit( state.copyWith( diff --git a/lib/features/document_search/view/document_search_bar.dart b/lib/features/document_search/view/document_search_bar.dart index 2d4f118..b13f0e5 100644 --- a/lib/features/document_search/view/document_search_bar.dart +++ b/lib/features/document_search/view/document_search_bar.dart @@ -8,14 +8,12 @@ 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/navigation/push_routes.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'; import 'package:paperless_mobile/features/documents/view/widgets/adaptive_documents_view.dart'; import 'package:paperless_mobile/features/documents/view/widgets/selection/view_type_selection_widget.dart'; import 'package:paperless_mobile/features/home/view/model/api_version.dart'; -import 'package:paperless_mobile/features/settings/model/view_type.dart'; import 'package:paperless_mobile/features/settings/view/manage_accounts_page.dart'; import 'package:paperless_mobile/features/settings/view/widgets/global_settings_builder.dart'; import 'package:paperless_mobile/features/settings/view/widgets/user_avatar.dart'; @@ -40,67 +38,140 @@ class _DocumentSearchBarState extends State { _controller.addListener(() { _debounceTimer?.cancel(); _debounceTimer = Timer(const Duration(milliseconds: 500), () { + print("Searching for $query"); context.read().suggest(query); }); }); } - late final DocumentSearchCubit _searchCubit; String get query => _controller.text; - @override - void didChangeDependencies() { - super.didChangeDependencies(); - _searchCubit = context.watch(); - } - @override Widget build(BuildContext context) { - return SearchAnchor.bar( - searchController: _controller, - barLeading: IconButton( - icon: const Icon(Icons.menu), - onPressed: Scaffold.of(context).openDrawer, + return Theme( + data: Theme.of(context).copyWith( + inputDecorationTheme: const InputDecorationTheme(), ), - barHintText: S.of(context)!.searchDocuments, - barTrailing: [ - IconButton( - icon: GlobalSettingsBuilder( - builder: (context, settings) { - return ValueListenableBuilder( - valueListenable: - Hive.box(HiveBoxes.localUserAccount).listenable(), - builder: (context, box, _) { - final account = box.get(settings.currentLoggedInUser!)!; - return UserAvatar( - userId: settings.currentLoggedInUser!, - account: account, + child: BlocBuilder( + builder: (context, state) { + return Padding( + padding: const EdgeInsets.only(top: 4), + child: SearchAnchor( + searchController: _controller, + viewHintText: S.of(context)!.searchDocuments, + builder: (context, controller) { + return SearchBar( + focusNode: FocusNode(), + controller: controller, + leading: IconButton( + icon: const Icon(Icons.menu), + onPressed: Scaffold.of(context).openDrawer, + ), + trailing: [ + IconButton( + icon: GlobalSettingsBuilder( + builder: (context, settings) { + return ValueListenableBuilder( + valueListenable: + Hive.box(HiveBoxes.localUserAccount).listenable(), + builder: (context, box, _) { + final account = box.get(settings.currentLoggedInUser!)!; + return UserAvatar( + userId: settings.currentLoggedInUser!, + account: account, + ); + }, + ); + }, + ), + onPressed: () { + final apiVersion = context.read(); + showDialog( + context: context, + builder: (context) => Provider.value( + value: apiVersion, + child: const ManageAccountsPage(), + ), + ); + }, + ), + ], + hintText: S.of(context)!.searchDocuments, + onTap: () { + controller.openView().then((value) => FocusScope.of(context).unfocus()); + }, + ); + }, + suggestionsBuilder: (context, controller) { + switch (state.view) { + case SearchView.suggestions: + return _buildSuggestionItems(state); + case SearchView.results: + return _buildResultsList(state); + } + }, + ), + ); + + return SearchAnchor.bar( + barPadding: MaterialStatePropertyAll(EdgeInsets.only(left: 8, right: 0)), + viewLeading: IconButton( + icon: const Icon(Icons.arrow_back), + onPressed: () { + // FocusManager.instance.primaryFocus?.unfocus(); + _controller.clear(); + _controller.closeView(null); + Future.delayed(const Duration(milliseconds: 100), () { + FocusManager.instance.primaryFocus?.unfocus(); + }); + }, + ), + searchController: _controller, + barLeading: IconButton( + icon: const Icon(Icons.menu), + onPressed: Scaffold.of(context).openDrawer, + ), + barHintText: S.of(context)!.searchDocuments, + barTrailing: [ + IconButton( + icon: GlobalSettingsBuilder( + builder: (context, settings) { + return ValueListenableBuilder( + valueListenable: + Hive.box(HiveBoxes.localUserAccount).listenable(), + builder: (context, box, _) { + final account = box.get(settings.currentLoggedInUser!)!; + return UserAvatar( + userId: settings.currentLoggedInUser!, + account: account, + ); + }, + ); + }, + ), + onPressed: () { + final apiVersion = context.read(); + showDialog( + context: context, + builder: (context) => Provider.value( + value: apiVersion, + child: const ManageAccountsPage(), + ), ); }, - ); - }, - ), - onPressed: () { - final apiVersion = context.read(); - showDialog( - context: context, - builder: (context) => Provider.value( - value: apiVersion, - child: const ManageAccountsPage(), ), - ); - }, - ), - ], - suggestionsBuilder: (context, controller) { - switch (_searchCubit.state.view) { - case SearchView.suggestions: - return _buildSuggestionItems(_searchCubit.state); - case SearchView.results: - // TODO: Handle this case. - break; - } - }, + ], + suggestionsBuilder: (context, controller) { + switch (state.view) { + case SearchView.suggestions: + return _buildSuggestionItems(state); + case SearchView.results: + return _buildResultsList(state); + } + }, + ); + }, + ), ); } @@ -155,6 +226,31 @@ class _DocumentSearchBarState extends State { ); } + Iterable _buildResultsList(DocumentSearchState state) sync* { + if (state.hasLoaded && !state.isLoading && state.documents.isEmpty) { + yield Center( + child: Text(S.of(context)!.noMatchesFound), + ); + return; + } + yield DefaultAdaptiveDocumentsView( + viewType: state.viewType, + documents: state.documents, + hasInternetConnection: true, + isLabelClickable: false, + isLoading: state.isLoading, + hasLoaded: state.hasLoaded, + enableHeroAnimation: false, + onTap: (document) { + pushDocumentDetailsRoute( + context, + document: document, + isLabelClickable: false, + ); + }, + ); + } + Widget _buildResultsView(DocumentSearchState state) { final header = Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, diff --git a/lib/features/document_search/view/sliver_search_bar.dart b/lib/features/document_search/view/sliver_search_bar.dart index d39b41d..64e3563 100644 --- a/lib/features/document_search/view/sliver_search_bar.dart +++ b/lib/features/document_search/view/sliver_search_bar.dart @@ -3,19 +3,10 @@ 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'; -import 'package:paperless_mobile/core/database/tables/local_user_account.dart'; import 'package:paperless_mobile/core/database/tables/local_user_app_state.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/widgets/material/search/m3_search_bar.dart' as s; import 'package:paperless_mobile/features/document_search/cubit/document_search_cubit.dart'; import 'package:paperless_mobile/features/document_search/view/document_search_bar.dart'; -import 'package:paperless_mobile/features/home/view/model/api_version.dart'; -import 'package:paperless_mobile/features/settings/view/manage_accounts_page.dart'; -import 'package:paperless_mobile/features/settings/view/widgets/global_settings_builder.dart'; -import 'package:paperless_mobile/features/settings/view/widgets/user_avatar.dart'; -import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; -import 'package:provider/provider.dart'; class SliverSearchBar extends StatelessWidget { final bool floating; @@ -51,42 +42,4 @@ class SliverSearchBar extends StatelessWidget { ), ); } - - s.SearchBar _buildOld(BuildContext context) { - return s.SearchBar( - height: kToolbarHeight, - supportingText: S.of(context)!.searchDocuments, - onTap: () => pushDocumentSearchPage(context), - leadingIcon: IconButton( - icon: const Icon(Icons.menu), - onPressed: Scaffold.of(context).openDrawer, - ), - trailingIcon: IconButton( - icon: GlobalSettingsBuilder( - builder: (context, settings) { - return ValueListenableBuilder( - valueListenable: Hive.box(HiveBoxes.localUserAccount).listenable(), - builder: (context, box, _) { - final account = box.get(settings.currentLoggedInUser!)!; - return UserAvatar( - userId: settings.currentLoggedInUser!, - account: account, - ); - }, - ); - }, - ), - onPressed: () { - final apiVersion = context.read(); - showDialog( - context: context, - builder: (context) => Provider.value( - value: apiVersion, - child: const ManageAccountsPage(), - ), - ); - }, - ), - ); - } } diff --git a/lib/theme.dart b/lib/theme.dart index 46ab95d..d1543ba 100644 --- a/lib/theme.dart +++ b/lib/theme.dart @@ -26,7 +26,7 @@ ThemeData buildTheme({ final classicScheme = ColorScheme.fromSeed( seedColor: _classicThemeColorSeed, brightness: brightness, - ).harmonized(); + ); late ColorScheme colorScheme; switch (preferredColorScheme) { case ColorSchemeOption.classic: From a36c96c7bb9815d87168fc3bfbf36e8af2043600 Mon Sep 17 00:00:00 2001 From: Anton Stubenbord Date: Thu, 25 May 2023 23:06:48 +0200 Subject: [PATCH 24/26] fix: Update search --- .../cubit/document_search_cubit.dart | 15 +- .../view/document_search_bar.dart | 372 +++++------------- .../view/document_search_page.dart | 86 ++-- .../view/widgets/document_preview.dart | 4 +- .../example/lib/camera_view.dart | 19 - .../example/lib/scan.dart | 14 +- 6 files changed, 159 insertions(+), 351 deletions(-) delete mode 100644 packages/paperless_document_scanner/example/lib/camera_view.dart diff --git a/lib/features/document_search/cubit/document_search_cubit.dart b/lib/features/document_search/cubit/document_search_cubit.dart index 74dd3a1..4f10822 100644 --- a/lib/features/document_search/cubit/document_search_cubit.dart +++ b/lib/features/document_search/cubit/document_search_cubit.dart @@ -87,6 +87,10 @@ class DocumentSearchCubit extends Cubit with DocumentPaging } Future suggest(String query) async { + final normalizedQuery = query.trim(); + if (normalizedQuery.isEmpty) { + return; + } emit( state.copyWith( isLoading: true, @@ -96,10 +100,13 @@ class DocumentSearchCubit extends Cubit with DocumentPaging ), ); final suggestions = await api.autocomplete(query); - emit(state.copyWith( - suggestions: suggestions, - isLoading: false, - )); + print("Suggestions found: $suggestions"); + emit( + state.copyWith( + suggestions: suggestions, + isLoading: false, + ), + ); } void reset() { diff --git a/lib/features/document_search/view/document_search_bar.dart b/lib/features/document_search/view/document_search_bar.dart index b13f0e5..85f591b 100644 --- a/lib/features/document_search/view/document_search_bar.dart +++ b/lib/features/document_search/view/document_search_bar.dart @@ -1,18 +1,15 @@ -import 'dart:async'; -import 'dart:math' as math; - -import 'package:collection/collection.dart'; +import 'package:animations/animations.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_flutter/adapters.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/local_user_account.dart'; -import 'package:paperless_mobile/core/navigation/push_routes.dart'; -import 'package:paperless_mobile/extensions/flutter_extensions.dart'; +import 'package:paperless_mobile/core/database/tables/local_user_app_state.dart'; +import 'package:paperless_mobile/core/repository/label_repository.dart'; +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/remove_history_entry_dialog.dart'; -import 'package:paperless_mobile/features/documents/view/widgets/adaptive_documents_view.dart'; -import 'package:paperless_mobile/features/documents/view/widgets/selection/view_type_selection_widget.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/settings/view/manage_accounts_page.dart'; import 'package:paperless_mobile/features/settings/view/widgets/global_settings_builder.dart'; @@ -28,280 +25,113 @@ class DocumentSearchBar extends StatefulWidget { } class _DocumentSearchBarState extends State { - Timer? _debounceTimer; - - final _controller = SearchController(); - - @override - void initState() { - super.initState(); - _controller.addListener(() { - _debounceTimer?.cancel(); - _debounceTimer = Timer(const Duration(milliseconds: 500), () { - print("Searching for $query"); - context.read().suggest(query); - }); - }); - } - - String get query => _controller.text; - @override Widget build(BuildContext context) { - return Theme( - data: Theme.of(context).copyWith( - inputDecorationTheme: const InputDecorationTheme(), + return OpenContainer( + transitionType: ContainerTransitionType.fadeThrough, + closedElevation: 1, + middleColor: Theme.of(context).colorScheme.surfaceVariant, + openColor: Theme.of(context).colorScheme.background, + closedColor: Theme.of(context).colorScheme.surfaceVariant, + closedShape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(56), ), - child: BlocBuilder( - builder: (context, state) { - return Padding( - padding: const EdgeInsets.only(top: 4), - child: SearchAnchor( - searchController: _controller, - viewHintText: S.of(context)!.searchDocuments, - builder: (context, controller) { - return SearchBar( - focusNode: FocusNode(), - controller: controller, - leading: IconButton( - icon: const Icon(Icons.menu), - onPressed: Scaffold.of(context).openDrawer, - ), - trailing: [ - IconButton( - icon: GlobalSettingsBuilder( - builder: (context, settings) { - return ValueListenableBuilder( - valueListenable: - Hive.box(HiveBoxes.localUserAccount).listenable(), - builder: (context, box, _) { - final account = box.get(settings.currentLoggedInUser!)!; - return UserAvatar( - userId: settings.currentLoggedInUser!, - account: account, - ); - }, - ); - }, - ), - onPressed: () { - final apiVersion = context.read(); - showDialog( - context: context, - builder: (context) => Provider.value( - value: apiVersion, - child: const ManageAccountsPage(), + closedBuilder: (_, action) { + return InkWell( + child: ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: 720, + minWidth: 360, + maxHeight: 56, + minHeight: 48, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 8), + child: Row( + children: [ + IconButton( + icon: Icon(Icons.menu), + onPressed: Scaffold.of(context).openDrawer, + ), + Expanded( + child: Hero( + tag: "search_hero_tag", + child: TextField( + enabled: false, + decoration: InputDecoration( + border: InputBorder.none, + hintText: S.of(context)!.searchDocuments, + hintStyle: TextStyle( + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + ), + ), ), - ); - }, + ), + ], ), - ], - hintText: S.of(context)!.searchDocuments, - onTap: () { - controller.openView().then((value) => FocusScope.of(context).unfocus()); - }, - ); - }, - suggestionsBuilder: (context, controller) { - switch (state.view) { - case SearchView.suggestions: - return _buildSuggestionItems(state); - case SearchView.results: - return _buildResultsList(state); - } - }, - ), - ); - - return SearchAnchor.bar( - barPadding: MaterialStatePropertyAll(EdgeInsets.only(left: 8, right: 0)), - viewLeading: IconButton( - icon: const Icon(Icons.arrow_back), - onPressed: () { - // FocusManager.instance.primaryFocus?.unfocus(); - _controller.clear(); - _controller.closeView(null); - Future.delayed(const Duration(milliseconds: 100), () { - FocusManager.instance.primaryFocus?.unfocus(); - }); - }, - ), - searchController: _controller, - barLeading: IconButton( - icon: const Icon(Icons.menu), - onPressed: Scaffold.of(context).openDrawer, - ), - barHintText: S.of(context)!.searchDocuments, - barTrailing: [ - IconButton( - icon: GlobalSettingsBuilder( - builder: (context, settings) { - return ValueListenableBuilder( - valueListenable: - Hive.box(HiveBoxes.localUserAccount).listenable(), - builder: (context, box, _) { - final account = box.get(settings.currentLoggedInUser!)!; - return UserAvatar( - userId: settings.currentLoggedInUser!, - account: account, - ); - }, - ); - }, + ), ), - onPressed: () { - final apiVersion = context.read(); - showDialog( - context: context, - builder: (context) => Provider.value( - value: apiVersion, - child: const ManageAccountsPage(), - ), - ); - }, - ), - ], - suggestionsBuilder: (context, controller) { - switch (state.view) { - case SearchView.suggestions: - return _buildSuggestionItems(state); - case SearchView.results: - return _buildResultsList(state); - } - }, - ); - }, - ), - ); - } - - Iterable _buildSuggestionItems(DocumentSearchState state) sync* { - final suggestions = - state.suggestions.whereNot((element) => state.searchHistory.contains(element)); - final historyMatches = state.searchHistory.where((element) => element.startsWith(query)); - for (var match in historyMatches.take(5)) { - yield ListTile( - title: Text(match), - leading: const Icon(Icons.history), - onLongPress: () => _onDeleteHistoryEntry(match), - onTap: () => _selectSuggestion(match), - trailing: _buildInsertSuggestionButton(match), - ); - } - - for (var suggestion in suggestions) { - yield ListTile( - title: Text(suggestion), - leading: const Icon(Icons.search), - onTap: () => _selectSuggestion(suggestion), - trailing: _buildInsertSuggestionButton(suggestion), - ); - } - } - - void _onDeleteHistoryEntry(String entry) async { - final shouldRemove = await showDialog( - context: context, - builder: (context) => RemoveHistoryEntryDialog(entry: entry), - ) ?? - false; - if (shouldRemove) { - context.read().removeHistoryEntry(entry); - } - } - - Widget _buildInsertSuggestionButton(String suggestion) { - return Transform( - alignment: Alignment.center, - transform: Matrix4.rotationY(math.pi), - child: IconButton( - icon: const Icon(Icons.arrow_outward), - onPressed: () { - _controller.text = '$suggestion '; - _controller.selection = TextSelection.fromPosition( - TextPosition(offset: _controller.text.length), - ); - }, - ), - ); - } - - Iterable _buildResultsList(DocumentSearchState state) sync* { - if (state.hasLoaded && !state.isLoading && state.documents.isEmpty) { - yield Center( - child: Text(S.of(context)!.noMatchesFound), - ); - return; - } - yield DefaultAdaptiveDocumentsView( - viewType: state.viewType, - documents: state.documents, - hasInternetConnection: true, - isLabelClickable: false, - isLoading: state.isLoading, - hasLoaded: state.hasLoaded, - enableHeroAnimation: false, - onTap: (document) { - pushDocumentDetailsRoute( - context, - document: document, - isLabelClickable: false, + _buildUserAvatar(context), + ], + ), + ), + ); + }, + openBuilder: (_, action) { + return MultiProvider( + providers: [ + Provider.value(value: context.read()), + Provider.value(value: context.read()), + Provider.value(value: context.read()), + Provider.value(value: context.read()), + Provider.value(value: context.read()), + ], + child: Provider( + create: (_) => DocumentSearchCubit( + context.read(), + context.read(), + context.read(), + Hive.box(HiveBoxes.localUserAppState) + .get(LocalUserAccount.current.id)!, + ), + builder: (_, __) => const DocumentSearchPage(), + ), ); }, ); } - Widget _buildResultsView(DocumentSearchState state) { - final header = Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - S.of(context)!.results, - style: Theme.of(context).textTheme.bodySmall, - ), - BlocBuilder( - builder: (context, state) { - return ViewTypeSelectionWidget( - viewType: state.viewType, - onChanged: (type) => context.read().updateViewType(type), - ); - }, - ) - ], - ).padded(); - return CustomScrollView( - slivers: [ - SliverToBoxAdapter(child: header), - if (state.hasLoaded && !state.isLoading && state.documents.isEmpty) - SliverToBoxAdapter( - child: Center( - child: Text(S.of(context)!.noMatchesFound), - ), - ) - else - SliverAdaptiveDocumentsView( - viewType: state.viewType, - documents: state.documents, - hasInternetConnection: true, - isLabelClickable: false, - isLoading: state.isLoading, - hasLoaded: state.hasLoaded, - enableHeroAnimation: false, - onTap: (document) { - pushDocumentDetailsRoute( - context, - document: document, - isLabelClickable: false, + IconButton _buildUserAvatar(BuildContext context) { + return IconButton( + icon: GlobalSettingsBuilder( + builder: (context, settings) { + return ValueListenableBuilder( + valueListenable: Hive.box(HiveBoxes.localUserAccount).listenable(), + builder: (context, box, _) { + final account = box.get(settings.currentLoggedInUser!)!; + return UserAvatar( + userId: settings.currentLoggedInUser!, + account: account, ); }, - ) - ], + ); + }, + ), + onPressed: () { + final apiVersion = context.read(); + showDialog( + context: context, + builder: (context) => Provider.value( + value: apiVersion, + child: const ManageAccountsPage(), + ), + ); + }, ); } - - void _selectSuggestion(String suggestion) { - _controller.text = suggestion; - context.read().search(suggestion); - FocusScope.of(context).unfocus(); - } } diff --git a/lib/features/document_search/view/document_search_page.dart b/lib/features/document_search/view/document_search_page.dart index a0ec9ee..5bd6c99 100644 --- a/lib/features/document_search/view/document_search_page.dart +++ b/lib/features/document_search/view/document_search_page.dart @@ -32,38 +32,38 @@ class _DocumentSearchPageState extends State { final theme = Theme.of(context); return Scaffold( appBar: AppBar( - backgroundColor: theme.colorScheme.surface, + backgroundColor: theme.colorScheme.surfaceVariant, toolbarHeight: 72, leading: BackButton( - color: theme.colorScheme.onSurface, + color: theme.colorScheme.onSurfaceVariant, ), - title: TextField( - autofocus: true, - style: theme.textTheme.bodyLarge?.apply( - color: theme.colorScheme.onSurface, - ), - focusNode: _queryFocusNode, - decoration: InputDecoration( - contentPadding: EdgeInsets.zero, - hintStyle: theme.textTheme.bodyLarge?.apply( - color: theme.colorScheme.onSurfaceVariant, + title: Hero( + tag: "search_hero_tag", + child: TextField( + autofocus: true, + // style: theme.textTheme.bodyLarge?.apply( + // color: theme.colorScheme.onSurface, + // ), + focusNode: _queryFocusNode, + decoration: InputDecoration( + contentPadding: EdgeInsets.zero, + hintText: S.of(context)!.searchDocuments, + border: InputBorder.none, ), - hintText: S.of(context)!.searchDocuments, - border: InputBorder.none, + controller: _queryController, + onChanged: (query) { + _debounceTimer?.cancel(); + _debounceTimer = Timer(const Duration(milliseconds: 500), () { + context.read().suggest(query); + }); + }, + textInputAction: TextInputAction.search, + onSubmitted: (query) { + FocusScope.of(context).unfocus(); + _debounceTimer?.cancel(); + context.read().search(query); + }, ), - controller: _queryController, - onChanged: (query) { - _debounceTimer?.cancel(); - _debounceTimer = Timer(const Duration(milliseconds: 500), () { - context.read().suggest(query); - }); - }, - textInputAction: TextInputAction.search, - onSubmitted: (query) { - FocusScope.of(context).unfocus(); - _debounceTimer?.cancel(); - context.read().search(query); - }, ), actions: [ IconButton( @@ -75,22 +75,22 @@ class _DocumentSearchPageState extends State { }, ).padded(), ], - bottom: PreferredSize( - preferredSize: const Size.fromHeight(1), - child: Divider( - color: theme.colorScheme.outline, - ), - ), ), - body: BlocBuilder( - builder: (context, state) { - switch (state.view) { - case SearchView.suggestions: - return _buildSuggestionsView(state); - case SearchView.results: - return _buildResultsView(state); - } - }, + body: Column( + children: [ + Expanded( + child: BlocBuilder( + builder: (context, state) { + switch (state.view) { + case SearchView.suggestions: + return _buildSuggestionsView(state); + case SearchView.results: + return _buildResultsView(state); + } + }, + ), + ), + ], ), ); } @@ -134,7 +134,7 @@ class _DocumentSearchPageState extends State { ), childCount: suggestions.length, ), - ) + ), ], ); } diff --git a/lib/features/documents/view/widgets/document_preview.dart b/lib/features/documents/view/widgets/document_preview.dart index 74da008..11d2e6d 100644 --- a/lib/features/documents/view/widgets/document_preview.dart +++ b/lib/features/documents/view/widgets/document_preview.dart @@ -43,9 +43,7 @@ class DocumentPreview extends StatelessWidget { fit: fit, alignment: alignment, cacheKey: "thumb_${document.id}", - imageUrl: context - .read() - .getThumbnailUrl(document.id), + imageUrl: context.read().getThumbnailUrl(document.id), errorWidget: (ctxt, msg, __) => Text(msg), placeholder: (context, value) => Shimmer.fromColors( baseColor: Colors.grey[300]!, diff --git a/packages/paperless_document_scanner/example/lib/camera_view.dart b/packages/paperless_document_scanner/example/lib/camera_view.dart deleted file mode 100644 index 8eae9b6..0000000 --- a/packages/paperless_document_scanner/example/lib/camera_view.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:camera/camera.dart'; -import 'package:flutter/material.dart'; - -class CameraView extends StatelessWidget { - const CameraView({super.key, required this.controller}); - - final CameraController controller; - - @override - Widget build(BuildContext context) { - if (!controller.value.isInitialized) { - return Container(); - } - - return Center( - child: CameraPreview(controller), - ); - } -} diff --git a/packages/paperless_document_scanner/example/lib/scan.dart b/packages/paperless_document_scanner/example/lib/scan.dart index cfacfb8..aed8a26 100644 --- a/packages/paperless_document_scanner/example/lib/scan.dart +++ b/packages/paperless_document_scanner/example/lib/scan.dart @@ -5,7 +5,6 @@ import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; import 'package:paperless_document_scanner/types/edge_detection_result.dart'; -import 'camera_view.dart'; import 'cropping_preview.dart'; import 'edge_detector.dart'; import 'image_view.dart'; @@ -65,10 +64,6 @@ class _ScanState extends State { return ImageView(imagePath: croppedImagePath!); } - if (imagePath == null && edgeDetectionResult == null) { - return CameraView(controller: controller); - } - return ImagePreview( imagePath: imagePath!, edgeDetectionResult: edgeDetectionResult, @@ -129,22 +124,19 @@ class _ScanState extends State { imagePath = filePath; }); - EdgeDetectionResult result = - await EdgeDetector().detectEdgesFromFile(filePath); + EdgeDetectionResult result = await EdgeDetector().detectEdgesFromFile(filePath); setState(() { edgeDetectionResult = result; }); } - Future _processImage( - String filePath, EdgeDetectionResult edgeDetectionResult) async { + Future _processImage(String filePath, EdgeDetectionResult edgeDetectionResult) async { if (!mounted) { return; } - bool result = await EdgeDetector() - .processImageFromFile(filePath, edgeDetectionResult); + bool result = await EdgeDetector().processImageFromFile(filePath, edgeDetectionResult); if (result == false) { return; From 4f13146dbc0d9ccb6ac2aeae654914f6e2156dc5 Mon Sep 17 00:00:00 2001 From: Anton Stubenbord Date: Sat, 27 May 2023 13:55:02 +0200 Subject: [PATCH 25/26] fix: Add providers to document upload, fix text color in about dialog --- lib/core/navigation/push_routes.dart | 38 ++++++++++++++++++ lib/features/app_drawer/view/app_drawer.dart | 31 +++++++++++++-- .../document_scan/view/scanner_page.dart | 39 ++++++------------- .../view/document_search_bar.dart | 1 + 4 files changed, 78 insertions(+), 31 deletions(-) diff --git a/lib/core/navigation/push_routes.dart b/lib/core/navigation/push_routes.dart index fbae8a3..02f296d 100644 --- a/lib/core/navigation/push_routes.dart +++ b/lib/core/navigation/push_routes.dart @@ -1,3 +1,5 @@ +import 'dart:typed_data'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart'; @@ -13,8 +15,11 @@ import 'package:paperless_mobile/core/repository/user_repository.dart'; import 'package:paperless_mobile/features/document_bulk_action/cubit/document_bulk_action_cubit.dart'; import 'package:paperless_mobile/features/document_bulk_action/view/widgets/fullscreen_bulk_edit_label_page.dart'; import 'package:paperless_mobile/features/document_bulk_action/view/widgets/fullscreen_bulk_edit_tags_widget.dart'; +import 'package:paperless_mobile/features/document_scan/view/scanner_page.dart'; import 'package:paperless_mobile/features/document_search/cubit/document_search_cubit.dart'; import 'package:paperless_mobile/features/document_search/view/document_search_page.dart'; +import 'package:paperless_mobile/features/document_upload/cubit/document_upload_cubit.dart'; +import 'package:paperless_mobile/features/document_upload/view/document_upload_preparation_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'; @@ -322,6 +327,39 @@ Future pushBulkEditDocumentTypeRoute(BuildContext context, ); } +Future pushDocumentUploadPreparationPage( + BuildContext context, { + required Uint8List bytes, + String? filename, + String? fileExtension, + String? title, +}) { + final labelRepo = context.read(); + final docsApi = context.read(); + return Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => MultiProvider( + providers: [ + Provider.value(value: labelRepo), + Provider.value(value: docsApi), + ], + builder: (_, child) => BlocProvider( + create: (_) => DocumentUploadCubit( + context.read(), + context.read(), + ), + child: DocumentUploadPreparationPage( + fileBytes: bytes, + fileExtension: fileExtension, + filename: filename, + title: title, + ), + ), + ), + ), + ); +} + List _getRequiredBulkEditProviders(BuildContext context) { return [ Provider.value(value: context.read()), diff --git a/lib/features/app_drawer/view/app_drawer.dart b/lib/features/app_drawer/view/app_drawer.dart index fb58374..16d8f36 100644 --- a/lib/features/app_drawer/view/app_drawer.dart +++ b/lib/features/app_drawer/view/app_drawer.dart @@ -1,3 +1,4 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -105,6 +106,8 @@ class AppDrawer extends StatelessWidget { } void _showAboutDialog(BuildContext context) { + final theme = Theme.of(context); + final colorScheme = theme.colorScheme; showAboutDialog( context: context, applicationIcon: const ImageIcon( @@ -117,10 +120,11 @@ class AppDrawer extends StatelessWidget { const SizedBox(height: 16), Text( "Source Code", - style: Theme.of(context).textTheme.titleMedium, + style: theme.textTheme.titleMedium, ), RichText( text: TextSpan( + style: theme.textTheme.bodyMedium?.copyWith(color: colorScheme.onSurface), children: [ TextSpan( text: S.of(context)!.findTheSourceCodeOn, @@ -143,9 +147,30 @@ class AppDrawer extends StatelessWidget { const SizedBox(height: 16), Text( 'Credits', - style: Theme.of(context).textTheme.titleMedium, + style: theme.textTheme.titleMedium?.copyWith(color: colorScheme.onSurface), + ), + RichText( + text: TextSpan( + style: theme.textTheme.bodyMedium?.copyWith(color: colorScheme.onSurface), + children: [ + const TextSpan( + text: 'Onboarding images by ', + ), + TextSpan( + text: 'pch.vector', + style: const TextStyle(color: Colors.blue), + recognizer: TapGestureRecognizer() + ..onTap = () { + launchUrlString( + 'https://www.freepik.com/free-vector/business-team-working-cogwheel-mechanism-together_8270974.htm#query=setting&position=4&from_view=author'); + }, + ), + const TextSpan( + text: ' on Freepik.', + ), + ], + ), ), - _buildOnboardingImageCredits(), ], ); } diff --git a/lib/features/document_scan/view/scanner_page.dart b/lib/features/document_scan/view/scanner_page.dart index d396903..c4426a2 100644 --- a/lib/features/document_scan/view/scanner_page.dart +++ b/lib/features/document_scan/view/scanner_page.dart @@ -11,6 +11,7 @@ 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/global/constants.dart'; +import 'package:paperless_mobile/core/navigation/push_routes.dart'; import 'package:paperless_mobile/core/service/file_description.dart'; import 'package:paperless_mobile/core/service/file_service.dart'; import 'package:paperless_mobile/features/app_drawer/view/app_drawer.dart'; @@ -189,19 +190,10 @@ class _ScannerPageState extends State with SingleTickerProviderStat final file = await _assembleFileBytes( context.read().state, ); - final uploadResult = await Navigator.of(context).push( - MaterialPageRoute( - builder: (_) => BlocProvider( - create: (_) => DocumentUploadCubit( - context.read(), - context.read(), - ), - child: DocumentUploadPreparationPage( - fileBytes: file.bytes, - fileExtension: file.extension, - ), - ), - ), + final uploadResult = await pushDocumentUploadPreparationPage( + context, + bytes: file.bytes, + fileExtension: file.extension, ); if ((uploadResult?.success ?? false) && uploadResult?.taskId != null) { // For paperless version older than 1.11.3, task id will always be null! @@ -299,21 +291,12 @@ class _ScannerPageState extends State with SingleTickerProviderStat ); return; } - Navigator.of(context).push( - MaterialPageRoute( - builder: (_) => BlocProvider( - create: (context) => DocumentUploadCubit( - context.read(), - context.read(), - ), - child: DocumentUploadPreparationPage( - fileBytes: file.readAsBytesSync(), - filename: fileDescription.filename, - title: fileDescription.filename, - fileExtension: fileDescription.extension, - ), - ), - ), + pushDocumentUploadPreparationPage( + context, + bytes: file.readAsBytesSync(), + filename: fileDescription.filename, + title: fileDescription.filename, + fileExtension: fileDescription.extension, ); } } diff --git a/lib/features/document_search/view/document_search_bar.dart b/lib/features/document_search/view/document_search_bar.dart index 85f591b..1150d1e 100644 --- a/lib/features/document_search/view/document_search_bar.dart +++ b/lib/features/document_search/view/document_search_bar.dart @@ -28,6 +28,7 @@ class _DocumentSearchBarState extends State { @override Widget build(BuildContext context) { return OpenContainer( + transitionDuration: const Duration(milliseconds: 200), transitionType: ContainerTransitionType.fadeThrough, closedElevation: 1, middleColor: Theme.of(context).colorScheme.surfaceVariant, From b30ede6661ab8a7e32b883f7074683fb59c98276 Mon Sep 17 00:00:00 2001 From: Anton Stubenbord Date: Sat, 27 May 2023 18:14:35 +0200 Subject: [PATCH 26/26] feat: Add permission checks, fix search, fix document upload, fix linked documents always being loaded all at once instead of paged --- lib/core/config/hive/hive_extensions.dart | 35 +++++ lib/core/navigation/push_routes.dart | 3 +- lib/features/app_drawer/view/app_drawer.dart | 8 +- .../view/pages/document_details_page.dart | 4 +- .../widgets/document_permissions_widget.dart | 11 +- .../document_scan/view/scanner_page.dart | 2 - .../view/document_search_bar.dart | 143 +++++++++--------- .../view/sliver_search_bar.dart | 1 + .../documents/cubit/documents_cubit.dart | 5 +- .../documents/view/pages/documents_page.dart | 22 ++- .../selection/view_type_selection_widget.dart | 2 +- lib/features/home/view/home_page.dart | 31 ++-- lib/features/home/view/route_description.dart | 2 + .../labels/view/pages/labels_page.dart | 4 - .../login/cubit/authentication_cubit.dart | 113 ++++++-------- lib/features/settings/view/settings_page.dart | 18 ++- .../user_permission_extension.dart | 16 ++ pubspec.lock | 40 +++++ pubspec.yaml | 2 + 19 files changed, 280 insertions(+), 182 deletions(-) create mode 100644 lib/core/config/hive/hive_extensions.dart diff --git a/lib/core/config/hive/hive_extensions.dart b/lib/core/config/hive/hive_extensions.dart new file mode 100644 index 0000000..09913ce --- /dev/null +++ b/lib/core/config/hive/hive_extensions.dart @@ -0,0 +1,35 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:hive_flutter/adapters.dart'; + +/// +/// Opens an encrypted box, calls [callback] with the now opened box, awaits +/// [callback] to return and returns the calculated value. Closes the box after. +/// +Future withEncryptedBox(String name, FutureOr Function(Box box) callback) async { + final key = await _getEncryptedBoxKey(); + final box = await Hive.openBox( + name, + encryptionCipher: HiveAesCipher(key), + ); + final result = await callback(box); + await box.close(); + return result; +} + +Future _getEncryptedBoxKey() async { + const secureStorage = FlutterSecureStorage(); + if (!await secureStorage.containsKey(key: 'key')) { + final key = Hive.generateSecureKey(); + + await secureStorage.write( + key: 'key', + value: base64UrlEncode(key), + ); + } + final key = (await secureStorage.read(key: 'key'))!; + return base64Decode(key); +} diff --git a/lib/core/navigation/push_routes.dart b/lib/core/navigation/push_routes.dart index 02f296d..4d7cb05 100644 --- a/lib/core/navigation/push_routes.dart +++ b/lib/core/navigation/push_routes.dart @@ -15,7 +15,6 @@ import 'package:paperless_mobile/core/repository/user_repository.dart'; import 'package:paperless_mobile/features/document_bulk_action/cubit/document_bulk_action_cubit.dart'; import 'package:paperless_mobile/features/document_bulk_action/view/widgets/fullscreen_bulk_edit_label_page.dart'; import 'package:paperless_mobile/features/document_bulk_action/view/widgets/fullscreen_bulk_edit_tags_widget.dart'; -import 'package:paperless_mobile/features/document_scan/view/scanner_page.dart'; import 'package:paperless_mobile/features/document_search/cubit/document_search_cubit.dart'; import 'package:paperless_mobile/features/document_search/view/document_search_page.dart'; import 'package:paperless_mobile/features/document_upload/cubit/document_upload_cubit.dart'; @@ -38,6 +37,7 @@ import 'package:provider/provider.dart'; Future pushDocumentSearchPage(BuildContext context) { final currentUser = Hive.box(HiveBoxes.globalSettings).getValue()!.currentLoggedInUser; + final userRepo = context.read(); return Navigator.of(context).push( MaterialPageRoute( builder: (_) => MultiProvider( @@ -46,6 +46,7 @@ Future pushDocumentSearchPage(BuildContext context) { Provider.value(value: context.read()), Provider.value(value: context.read()), Provider.value(value: context.read()), + Provider.value(value: userRepo), ], builder: (context, _) { return BlocProvider( diff --git a/lib/features/app_drawer/view/app_drawer.dart b/lib/features/app_drawer/view/app_drawer.dart index 16d8f36..19778a7 100644 --- a/lib/features/app_drawer/view/app_drawer.dart +++ b/lib/features/app_drawer/view/app_drawer.dart @@ -2,6 +2,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_mobile/constants.dart'; import 'package:paperless_mobile/core/widgets/paperless_logo.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.dart'; @@ -92,8 +93,11 @@ class AppDrawer extends StatelessWidget { ), onTap: () => Navigator.of(context).push( MaterialPageRoute( - builder: (_) => Provider.value( - value: context.read(), + builder: (_) => MultiProvider( + providers: [ + Provider.value(value: context.read()), + Provider.value(value: context.read()), + ], child: const SettingsPage(), ), ), diff --git a/lib/features/document_details/view/pages/document_details_page.dart b/lib/features/document_details/view/pages/document_details_page.dart index 826a08e..0d7396c 100644 --- a/lib/features/document_details/view/pages/document_details_page.dart +++ b/lib/features/document_details/view/pages/document_details_page.dart @@ -240,7 +240,9 @@ class _DocumentDetailsPageState extends State { SliverOverlapInjector( handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context), ), - const DocumentPermissionsWidget(), + DocumentPermissionsWidget( + document: state.document, + ), ], ), ], diff --git a/lib/features/document_details/view/widgets/document_permissions_widget.dart b/lib/features/document_details/view/widgets/document_permissions_widget.dart index 655fbf6..c2621c0 100644 --- a/lib/features/document_details/view/widgets/document_permissions_widget.dart +++ b/lib/features/document_details/view/widgets/document_permissions_widget.dart @@ -1,8 +1,15 @@ import 'package:flutter/material.dart'; +import 'package:paperless_api/paperless_api.dart'; -class DocumentPermissionsWidget extends StatelessWidget { - const DocumentPermissionsWidget({super.key}); +class DocumentPermissionsWidget extends StatefulWidget { + final DocumentModel document; + const DocumentPermissionsWidget({super.key, required this.document}); + @override + State createState() => _DocumentPermissionsWidgetState(); +} + +class _DocumentPermissionsWidgetState extends State { @override Widget build(BuildContext context) { return const SliverToBoxAdapter( diff --git a/lib/features/document_scan/view/scanner_page.dart b/lib/features/document_scan/view/scanner_page.dart index c4426a2..1f2b8c6 100644 --- a/lib/features/document_scan/view/scanner_page.dart +++ b/lib/features/document_scan/view/scanner_page.dart @@ -18,8 +18,6 @@ import 'package:paperless_mobile/features/app_drawer/view/app_drawer.dart'; import 'package:paperless_mobile/features/document_scan/cubit/document_scanner_cubit.dart'; import 'package:paperless_mobile/features/document_scan/view/widgets/scanned_image_item.dart'; import 'package:paperless_mobile/features/document_search/view/sliver_search_bar.dart'; -import 'package:paperless_mobile/features/document_upload/cubit/document_upload_cubit.dart'; -import 'package:paperless_mobile/features/document_upload/view/document_upload_preparation_page.dart'; import 'package:paperless_mobile/features/documents/view/pages/document_view.dart'; import 'package:paperless_mobile/features/tasks/cubit/task_status_cubit.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; diff --git a/lib/features/document_search/view/document_search_bar.dart b/lib/features/document_search/view/document_search_bar.dart index 1150d1e..95e8285 100644 --- a/lib/features/document_search/view/document_search_bar.dart +++ b/lib/features/document_search/view/document_search_bar.dart @@ -27,88 +27,89 @@ class DocumentSearchBar extends StatefulWidget { class _DocumentSearchBarState extends State { @override Widget build(BuildContext context) { - return OpenContainer( - transitionDuration: const Duration(milliseconds: 200), - transitionType: ContainerTransitionType.fadeThrough, - closedElevation: 1, - middleColor: Theme.of(context).colorScheme.surfaceVariant, - openColor: Theme.of(context).colorScheme.background, - closedColor: Theme.of(context).colorScheme.surfaceVariant, - closedShape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(56), - ), - closedBuilder: (_, action) { - return InkWell( - child: ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: 720, - minWidth: 360, - maxHeight: 56, - minHeight: 48, - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Flexible( - child: Padding( - padding: EdgeInsets.symmetric(horizontal: 8), - child: Row( - children: [ - IconButton( - icon: Icon(Icons.menu), - onPressed: Scaffold.of(context).openDrawer, - ), - Expanded( - child: Hero( - tag: "search_hero_tag", - child: TextField( - enabled: false, - decoration: InputDecoration( - border: InputBorder.none, - hintText: S.of(context)!.searchDocuments, - hintStyle: TextStyle( - color: Theme.of(context).colorScheme.onSurfaceVariant, - ), - ), + return Container( + margin: EdgeInsets.only(top: 8), + child: OpenContainer( + transitionDuration: const Duration(milliseconds: 200), + transitionType: ContainerTransitionType.fadeThrough, + closedElevation: 1, + middleColor: Theme.of(context).colorScheme.surfaceVariant, + openColor: Theme.of(context).colorScheme.background, + closedColor: Theme.of(context).colorScheme.surfaceVariant, + closedShape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(56), + ), + closedBuilder: (_, action) { + return InkWell( + onTap: action, + child: ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: 720, + minWidth: 360, + maxHeight: 56, + minHeight: 48, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + IconButton( + icon: const Icon(Icons.menu), + onPressed: Scaffold.of(context).openDrawer, + ), + Flexible( + child: Text( + S.of(context)!.searchDocuments, + style: Theme.of(context).textTheme.bodyLarge?.copyWith( + fontWeight: FontWeight.w500, + color: Theme.of(context).hintColor, + ), ), ), - ), - ], + ], + ), ), ), - ), - _buildUserAvatar(context), - ], + _buildUserAvatar(context), + ], + ), ), - ), - ); - }, - openBuilder: (_, action) { - return MultiProvider( - providers: [ - Provider.value(value: context.read()), - Provider.value(value: context.read()), - Provider.value(value: context.read()), - Provider.value(value: context.read()), - Provider.value(value: context.read()), - ], - child: Provider( - create: (_) => DocumentSearchCubit( - context.read(), - context.read(), - context.read(), - Hive.box(HiveBoxes.localUserAppState) - .get(LocalUserAccount.current.id)!, + ); + }, + openBuilder: (_, action) { + return MultiProvider( + providers: [ + Provider.value(value: context.read()), + Provider.value(value: context.read()), + Provider.value(value: context.read()), + Provider.value(value: context.read()), + if (context.read().hasMultiUserSupport) + Provider.value(value: context.read()), + ], + child: Provider( + create: (_) => DocumentSearchCubit( + context.read(), + context.read(), + context.read(), + Hive.box(HiveBoxes.localUserAppState) + .get(LocalUserAccount.current.id)!, + ), + builder: (_, __) => const DocumentSearchPage(), ), - builder: (_, __) => const DocumentSearchPage(), - ), - ); - }, + ); + }, + ), ); } IconButton _buildUserAvatar(BuildContext context) { return IconButton( + padding: const EdgeInsets.all(6), icon: GlobalSettingsBuilder( builder: (context, settings) { return ValueListenableBuilder( diff --git a/lib/features/document_search/view/sliver_search_bar.dart b/lib/features/document_search/view/sliver_search_bar.dart index 64e3563..39e7e35 100644 --- a/lib/features/document_search/view/sliver_search_bar.dart +++ b/lib/features/document_search/view/sliver_search_bar.dart @@ -21,6 +21,7 @@ class SliverSearchBar extends StatelessWidget { Widget build(BuildContext context) { final currentUser = Hive.box(HiveBoxes.globalSettings).getValue()!.currentLoggedInUser; + return SliverPersistentHeader( floating: floating, pinned: pinned, diff --git a/lib/features/documents/cubit/documents_cubit.dart b/lib/features/documents/cubit/documents_cubit.dart index 78226a8..edeb82b 100644 --- a/lib/features/documents/cubit/documents_cubit.dart +++ b/lib/features/documents/cubit/documents_cubit.dart @@ -114,8 +114,9 @@ class DocumentsCubit extends HydratedCubit with DocumentPagingBl void setViewType(ViewType viewType) { emit(state.copyWith(viewType: viewType)); - _userState.documentsPageViewType = viewType; - _userState.save(); + _userState + ..documentsPageViewType = viewType + ..save(); } @override diff --git a/lib/features/documents/view/pages/documents_page.dart b/lib/features/documents/view/pages/documents_page.dart index fc2c399..847b64e 100644 --- a/lib/features/documents/view/pages/documents_page.dart +++ b/lib/features/documents/view/pages/documents_page.dart @@ -22,6 +22,7 @@ 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:sliver_tools/sliver_tools.dart'; class DocumentFilterIntent { final DocumentFilter? filter; @@ -107,7 +108,7 @@ class _DocumentsPageState extends State with SingleTickerProvider }, builder: (context, connectivityState) { return SafeArea( - top: context.watch().state.selection.isEmpty, + top: true, child: Scaffold( drawer: const AppDrawer(), floatingActionButton: BlocBuilder( @@ -160,13 +161,18 @@ class _DocumentsPageState extends State with SingleTickerProvider handle: searchBarHandle, sliver: BlocBuilder( builder: (context, state) { - if (state.selection.isNotEmpty) { - // Show selection app bar when selection mode is active - return DocumentSelectionSliverAppBar( - state: state, - ); - } - return const SliverSearchBar(floating: true); + return AnimatedSwitcher( + layoutBuilder: SliverAnimatedSwitcher.defaultLayoutBuilder, + transitionBuilder: SliverAnimatedSwitcher.defaultTransitionBuilder, + child: state.selection.isEmpty + ? const SliverSearchBar(floating: true) + : DocumentSelectionSliverAppBar( + state: state, + ), + duration: const Duration( + milliseconds: 250, + ), + ); }, ), ), diff --git a/lib/features/documents/view/widgets/selection/view_type_selection_widget.dart b/lib/features/documents/view/widgets/selection/view_type_selection_widget.dart index 8289761..ba0d9a8 100644 --- a/lib/features/documents/view/widgets/selection/view_type_selection_widget.dart +++ b/lib/features/documents/view/widgets/selection/view_type_selection_widget.dart @@ -35,7 +35,7 @@ class ViewTypeSelectionWidget extends StatelessWidget { ), // Ensures text is not split into two lines position: PopupMenuPosition.under, initialValue: viewType, - icon: Icon(icon), + icon: Icon(icon, color: Theme.of(context).colorScheme.primary), itemBuilder: (context) => [ _buildViewTypeOption( context, diff --git a/lib/features/home/view/home_page.dart b/lib/features/home/view/home_page.dart index 6bac0f8..967c083 100644 --- a/lib/features/home/view/home_page.dart +++ b/lib/features/home/view/home_page.dart @@ -10,13 +10,12 @@ 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/global/constants.dart'; +import 'package:paperless_mobile/core/navigation/push_routes.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/service/file_description.dart'; import 'package:paperless_mobile/core/translation/error_code_localization_mapper.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/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'; @@ -139,25 +138,23 @@ class _HomePageState extends State with WidgetsBindingObserver { } return; } + + if (!LocalUserAccount.current.paperlessUser + .hasPermission(PermissionAction.add, PermissionTarget.document)) { + Fluttertoast.showToast( + msg: "You do not have the permissions to upload documents.", + ); + return; + } final fileDescription = FileDescription.fromPath(mediaFile.path); if (await File(mediaFile.path).exists()) { final bytes = File(mediaFile.path).readAsBytesSync(); - final result = await Navigator.push( + final result = await pushDocumentUploadPreparationPage( context, - MaterialPageRoute( - builder: (context) => BlocProvider.value( - value: DocumentUploadCubit( - context.read(), - context.read(), - ), - child: DocumentUploadPreparationPage( - fileBytes: bytes, - filename: fileDescription.filename, - title: fileDescription.filename, - fileExtension: fileDescription.extension, - ), - ), - ), + bytes: bytes, + filename: fileDescription.filename, + title: fileDescription.filename, + fileExtension: fileDescription.extension, ); if (result?.success ?? false) { await Fluttertoast.showToast( diff --git a/lib/features/home/view/route_description.dart b/lib/features/home/view/route_description.dart index 367c41a..8440e03 100644 --- a/lib/features/home/view/route_description.dart +++ b/lib/features/home/view/route_description.dart @@ -5,12 +5,14 @@ class RouteDescription { final Icon icon; final Icon selectedIcon; final Widget Function(Widget icon)? badgeBuilder; + final bool enabled; RouteDescription({ required this.label, required this.icon, required this.selectedIcon, this.badgeBuilder, + this.enabled = true, }); NavigationDestination toNavigationDestination() { diff --git a/lib/features/labels/view/pages/labels_page.dart b/lib/features/labels/view/pages/labels_page.dart index 6c93123..3d385cc 100644 --- a/lib/features/labels/view/pages/labels_page.dart +++ b/lib/features/labels/view/pages/labels_page.dart @@ -146,7 +146,6 @@ class _LabelsPageState extends State with SingleTickerProviderStateM labels: context.watch().state.correspondents, filterBuilder: (label) => DocumentFilter( correspondent: IdQueryParameter.fromId(label.id!), - pageSize: label.documentCount ?? 0, ), canEdit: LocalUserAccount.current.paperlessUser.hasPermission( PermissionAction.change, PermissionTarget.correspondent), @@ -171,7 +170,6 @@ class _LabelsPageState extends State with SingleTickerProviderStateM labels: context.watch().state.documentTypes, filterBuilder: (label) => DocumentFilter( documentType: IdQueryParameter.fromId(label.id!), - pageSize: label.documentCount ?? 0, ), canEdit: LocalUserAccount.current.paperlessUser.hasPermission( PermissionAction.change, PermissionTarget.documentType), @@ -196,7 +194,6 @@ class _LabelsPageState extends State with SingleTickerProviderStateM labels: context.watch().state.tags, filterBuilder: (label) => DocumentFilter( tags: TagsQuery.ids(include: [label.id!]), - pageSize: label.documentCount ?? 0, ), canEdit: LocalUserAccount.current.paperlessUser .hasPermission(PermissionAction.change, PermissionTarget.tag), @@ -231,7 +228,6 @@ class _LabelsPageState extends State with SingleTickerProviderStateM onEdit: _openEditStoragePathPage, filterBuilder: (label) => DocumentFilter( storagePath: IdQueryParameter.fromId(label.id!), - pageSize: label.documentCount ?? 0, ), canEdit: LocalUserAccount.current.paperlessUser.hasPermission( PermissionAction.change, PermissionTarget.storagePath), diff --git a/lib/features/login/cubit/authentication_cubit.dart b/lib/features/login/cubit/authentication_cubit.dart index 9fb94ed..2438689 100644 --- a/lib/features/login/cubit/authentication_cubit.dart +++ b/lib/features/login/cubit/authentication_cubit.dart @@ -1,13 +1,10 @@ -import 'dart:convert'; -import 'dart:typed_data'; - import 'package:dio/dio.dart'; import 'package:flutter/widgets.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hive_flutter/adapters.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart'; import 'package:paperless_mobile/core/config/hive/hive_config.dart'; +import 'package:paperless_mobile/core/config/hive/hive_extensions.dart'; import 'package:paperless_mobile/core/database/tables/global_settings.dart'; import 'package:paperless_mobile/core/database/tables/local_user_account.dart'; import 'package:paperless_mobile/core/database/tables/local_user_app_state.dart'; @@ -88,39 +85,38 @@ class AuthenticationCubit extends Cubit { return; } } + await withEncryptedBox(HiveBoxes.localUserCredentials, + (credentialsBox) async { + if (!credentialsBox.containsKey(localUserId)) { + await credentialsBox.close(); + debugPrint("Invalid authentication for $localUserId"); + return; + } + final credentials = credentialsBox.get(localUserId); + await _resetExternalState(); - final credentialsBox = await _getUserCredentialsBox(); - if (!credentialsBox.containsKey(localUserId)) { - await credentialsBox.close(); - debugPrint("Invalid authentication for $localUserId"); - return; - } - final credentials = credentialsBox.get(localUserId); - await credentialsBox.close(); + _sessionManager.updateSettings( + authToken: credentials!.token, + clientCertificate: credentials.clientCertificate, + baseUrl: account.serverUrl, + ); - await _resetExternalState(); + globalSettings.currentLoggedInUser = localUserId; + await globalSettings.save(); - _sessionManager.updateSettings( - authToken: credentials!.token, - clientCertificate: credentials.clientCertificate, - baseUrl: account.serverUrl, - ); + final apiVersion = await _getApiVersion(_sessionManager.client); - globalSettings.currentLoggedInUser = localUserId; - await globalSettings.save(); + await _updateRemoteUser( + _sessionManager, + Hive.box(HiveBoxes.localUserAccount).get(localUserId)!, + apiVersion, + ); - final apiVersion = await _getApiVersion(_sessionManager.client); - - await _updateRemoteUser( - _sessionManager, - Hive.box(HiveBoxes.localUserAccount).get(localUserId)!, - apiVersion, - ); - - emit(AuthenticationState.authenticated( - localUserId: localUserId, - apiVersion: apiVersion, - )); + emit(AuthenticationState.authenticated( + localUserId: localUserId, + apiVersion: apiVersion, + )); + }); } Future addAccount({ @@ -146,14 +142,14 @@ class AuthenticationCubit extends Cubit { Future removeAccount(String userId) async { final globalSettings = Hive.box(HiveBoxes.globalSettings).getValue()!; final userAccountBox = Hive.box(HiveBoxes.localUserAccount); - final userCredentialsBox = await _getUserCredentialsBox(); final userAppStateBox = Hive.box(HiveBoxes.localUserAppState); final currentUser = globalSettings.currentLoggedInUser; await userAccountBox.delete(userId); await userAppStateBox.delete(userId); - await userCredentialsBox.delete(userId); - await userCredentialsBox.close(); + await withEncryptedBox(HiveBoxes.localUserCredentials, (box) { + box.delete(userId); + }); if (currentUser == userId) { return logout(); @@ -182,9 +178,10 @@ class AuthenticationCubit extends Cubit { } } - final userCredentialsBox = await _getUserCredentialsBox(); - final authentication = userCredentialsBox.get(globalSettings.currentLoggedInUser!); - await userCredentialsBox.close(); + final authentication = await withEncryptedBox( + HiveBoxes.localUserCredentials, (box) { + return box.get(globalSettings.currentLoggedInUser!); + }); if (authentication == null) { throw Exception( @@ -218,28 +215,6 @@ class AuthenticationCubit extends Cubit { emit(const AuthenticationState.unauthenticated()); } - Future _getEncryptedBoxKey() async { - const secureStorage = FlutterSecureStorage(); - if (!await secureStorage.containsKey(key: 'key')) { - final key = Hive.generateSecureKey(); - - await secureStorage.write( - key: 'key', - value: base64UrlEncode(key), - ); - } - final key = (await secureStorage.read(key: 'key'))!; - return base64Decode(key); - } - - Future> _getUserCredentialsBox() async { - final keyBytes = await _getEncryptedBoxKey(); - return Hive.openBox( - HiveBoxes.localUserCredentials, - encryptionCipher: HiveAesCipher(keyBytes), - ); - } - Future _resetExternalState() async { _sessionManager.resetSettings(); await HydratedBloc.storage.clear(); @@ -305,15 +280,15 @@ class AuthenticationCubit extends Cubit { ); // Save credentials in encrypted box - final userCredentialsBox = await _getUserCredentialsBox(); - await userCredentialsBox.put( - localUserId, - UserCredentials( - token: token, - clientCertificate: clientCert, - ), - ); - userCredentialsBox.close(); + await withEncryptedBox(HiveBoxes.localUserCredentials, (box) async { + await box.put( + localUserId, + UserCredentials( + token: token, + clientCertificate: clientCert, + ), + ); + }); return serverUser.id; } diff --git a/lib/features/settings/view/settings_page.dart b/lib/features/settings/view/settings_page.dart index a7d9f9b..22a3cd3 100644 --- a/lib/features/settings/view/settings_page.dart +++ b/lib/features/settings/view/settings_page.dart @@ -4,6 +4,7 @@ import 'package:paperless_mobile/features/settings/view/pages/application_settin import 'package:paperless_mobile/features/settings/view/pages/security_settings_page.dart'; import 'package:paperless_mobile/features/settings/view/widgets/user_settings_builder.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; +import 'package:provider/provider.dart'; class SettingsPage extends StatelessWidget { const SettingsPage({super.key}); @@ -25,10 +26,21 @@ class SettingsPage extends StatelessWidget { textAlign: TextAlign.center, ), subtitle: FutureBuilder( + future: context.read().getServerInformation(), builder: (context, snapshot) { + if (snapshot.hasError) { + return Text( + "Something went wrong while retrieving server data.", //TODO: INTL + style: Theme.of(context) + .textTheme + .labelSmall + ?.copyWith(color: Theme.of(context).colorScheme.error), + textAlign: TextAlign.center, + ); + } if (!snapshot.hasData) { return Text( - "Loading server information...", + "Loading server information...", //TODO: INTL style: Theme.of(context).textTheme.labelSmall, textAlign: TextAlign.center, ); @@ -39,7 +51,9 @@ class SettingsPage extends StatelessWidget { ' ' + serverData.version.toString() + ' (API v${serverData.apiVersion})', - style: Theme.of(context).textTheme.labelSmall, + style: Theme.of(context).textTheme.labelSmall?.copyWith( + color: Theme.of(context).colorScheme.secondary, + ), textAlign: TextAlign.center, ); }, diff --git a/packages/paperless_api/lib/src/models/permissions/user_permission_extension.dart b/packages/paperless_api/lib/src/models/permissions/user_permission_extension.dart index e1c8d8f..7bf5185 100644 --- a/packages/paperless_api/lib/src/models/permissions/user_permission_extension.dart +++ b/packages/paperless_api/lib/src/models/permissions/user_permission_extension.dart @@ -11,4 +11,20 @@ extension UserPermissionExtension on UserModel { v2: (_) => true, ); } + + bool hasPermissions(List actions, List targets) { + return map( + v3: (user) { + final permissions = [ + for (var action in actions) + for (var target in targets) [action, target].join("_") + ]; + return permissions.every((requestedPermission) => + user.userPermissions.contains(requestedPermission) || + user.inheritedPermissions + .any((element) => element.split(".").last == requestedPermission)); + }, + v2: (_) => true, + ); + } } diff --git a/pubspec.lock b/pubspec.lock index b0a0506..d3d982b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1417,6 +1417,14 @@ packages: description: flutter source: sdk version: "0.0.99" + sliver_tools: + dependency: "direct main" + description: + name: sliver_tools + sha256: ccdc502098a8bfa07b3ec582c282620031481300035584e1bb3aca296a505e8c + url: "https://pub.dev" + source: hosted + version: "0.2.10" source_gen: dependency: transitive description: @@ -1705,6 +1713,38 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + webview_flutter: + dependency: "direct main" + description: + name: webview_flutter + sha256: "5604dac1178680a34fbe4a08c7b69ec42cca6601dc300009ec9ff69bef284cc2" + url: "https://pub.dev" + source: hosted + version: "4.2.1" + webview_flutter_android: + dependency: transitive + description: + name: webview_flutter_android + sha256: "57a22c86065375c1598b57224f92d6008141be0c877c64100de8bfb6f71083d8" + url: "https://pub.dev" + source: hosted + version: "3.7.1" + webview_flutter_platform_interface: + dependency: transitive + description: + name: webview_flutter_platform_interface + sha256: "656e2aeaef318900fffd21468b6ddc7958c7092a642f0e7220bac328b70d4a81" + url: "https://pub.dev" + source: hosted + version: "2.3.1" + webview_flutter_wkwebview: + dependency: transitive + description: + name: webview_flutter_wkwebview + sha256: "6bbc6ade302b842999b27cbaa7171241c273deea8a9c73f92ceb3d811c767de2" + url: "https://pub.dev" + source: hosted + version: "3.4.4" win32: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index f5d7080..8285c4e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -92,6 +92,8 @@ dependencies: animations: ^2.0.7 hive_flutter: ^1.1.0 flutter_secure_storage: ^8.0.0 + sliver_tools: ^0.2.10 + webview_flutter: ^4.2.1 dependency_overrides: intl: ^0.18.0