feat: More resturcturings, adapt code to previous changes

This commit is contained in:
Anton Stubenbord
2023-05-01 23:05:54 +02:00
parent 88085b5662
commit d5c68e023c
35 changed files with 475 additions and 200 deletions

View File

@@ -16,13 +16,13 @@ class LocalUserAccount extends HiveObject {
@HiveField(4) @HiveField(4)
final LocalUserSettings settings; final LocalUserSettings settings;
@HiveField(6) @HiveField(7)
final int paperlessUserId; final UserModel paperlessUser;
LocalUserAccount({ LocalUserAccount({
required this.id, required this.id,
required this.serverUrl, required this.serverUrl,
required this.settings, required this.settings,
required this.paperlessUserId, required this.paperlessUser,
}); });
} }

View File

@@ -7,4 +7,6 @@ abstract class PaperlessApiFactory {
PaperlessLabelsApi createLabelsApi(Dio dio, {required int apiVersion}); PaperlessLabelsApi createLabelsApi(Dio dio, {required int apiVersion});
PaperlessServerStatsApi createServerStatsApi(Dio dio, {required int apiVersion}); PaperlessServerStatsApi createServerStatsApi(Dio dio, {required int apiVersion});
PaperlessTasksApi createTasksApi(Dio dio, {required int apiVersion}); PaperlessTasksApi createTasksApi(Dio dio, {required int apiVersion});
PaperlessAuthenticationApi createAuthenticationApi(Dio dio);
PaperlessUserApi createUserApi(Dio dio, {required int apiVersion});
} }

View File

@@ -32,4 +32,19 @@ class PaperlessApiFactoryImpl implements PaperlessApiFactory {
PaperlessTasksApi createTasksApi(Dio dio, {required int apiVersion}) { PaperlessTasksApi createTasksApi(Dio dio, {required int apiVersion}) {
return PaperlessTasksApiImpl(dio); 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.");
}
} }

View File

@@ -4,28 +4,13 @@ import 'package:flutter/widgets.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/repository/label_repository_state.dart'; import 'package:paperless_mobile/core/repository/label_repository_state.dart';
import 'package:paperless_mobile/core/repository/persistent_repository.dart';
class LabelRepository extends HydratedCubit<LabelRepositoryState> { class LabelRepository extends PersistentRepository<LabelRepositoryState> {
final PaperlessLabelsApi _api; final PaperlessLabelsApi _api;
final Map<Object, StreamSubscription> _subscribers = {};
LabelRepository(this._api) : super(const LabelRepositoryState()); 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<void> initialize() { Future<void> initialize() {
debugPrint("Initializing labels..."); debugPrint("Initializing labels...");
return Future.wait([ return Future.wait([
@@ -195,14 +180,6 @@ class LabelRepository extends HydratedCubit<LabelRepositoryState> {
return updated; return updated;
} }
@override
Future<void> close() {
_subscribers.forEach((key, subscription) {
subscription.cancel();
});
return super.close();
}
@override @override
Future<void> clear() async { Future<void> clear() async {
await super.clear(); await super.clear();

View File

@@ -0,0 +1,32 @@
import 'dart:async';
import 'package:hydrated_bloc/hydrated_bloc.dart';
abstract class PersistentRepository<T> extends HydratedCubit<T> {
final Map<Object, StreamSubscription> _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<void> close() {
_subscribers.forEach((key, subscription) {
subscription.cancel();
});
return super.close();
}
}

View File

@@ -1,28 +1,11 @@
import 'dart:async'; 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_api/paperless_api.dart';
import 'package:paperless_mobile/core/repository/persistent_repository.dart';
import 'package:paperless_mobile/core/repository/saved_view_repository_state.dart'; import 'package:paperless_mobile/core/repository/saved_view_repository_state.dart';
class SavedViewRepository extends HydratedCubit<SavedViewRepositoryState> { class SavedViewRepository extends PersistentRepository<SavedViewRepositoryState> {
final PaperlessSavedViewsApi _api; final PaperlessSavedViewsApi _api;
final Map<Object, StreamSubscription> _subscribers = {};
void subscribe(
Object source,
void Function(Map<int, SavedView>) 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()); SavedViewRepository(this._api) : super(const SavedViewRepositoryState());
@@ -63,14 +46,6 @@ class SavedViewRepository extends HydratedCubit<SavedViewRepositoryState> {
return found; return found;
} }
@override
Future<void> close() {
_subscribers.forEach((key, subscription) {
subscription.cancel();
});
return super.close();
}
@override @override
Future<void> clear() async { Future<void> clear() async {
await super.clear(); await super.clear();

View File

@@ -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<UserRepositoryState> {
final PaperlessUserApiV3 _userApiV3;
UserRepository(this._userApiV3) : super(const UserRepositoryState());
Future<void> initialize() async {
await findAll();
}
Future<Iterable<UserModel>> findAll() async {
final users = await _userApiV3.findAll();
emit(state.copyWith(users: {for (var e in users) e.id: e}));
return users;
}
Future<UserModel?> find(int id) async {
final user = await _userApiV3.find(id);
emit(state.copyWith(users: state.users..[id] = user));
return user;
}
@override
UserRepositoryState? fromJson(Map<String, dynamic> json) {
return UserRepositoryState.fromJson(json);
}
@override
Map<String, dynamic>? toJson(UserRepositoryState state) {
return state.toJson();
}
}

View File

@@ -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>(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<String, dynamic> json) {
return _UserRepositoryState.fromJson(json);
}
/// @nodoc
mixin _$UserRepositoryState {
Map<int, UserModel> get users => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$UserRepositoryStateCopyWith<UserRepositoryState> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $UserRepositoryStateCopyWith<$Res> {
factory $UserRepositoryStateCopyWith(
UserRepositoryState value, $Res Function(UserRepositoryState) then) =
_$UserRepositoryStateCopyWithImpl<$Res, UserRepositoryState>;
@useResult
$Res call({Map<int, UserModel> 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<int, UserModel>,
) 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<int, UserModel> 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<int, UserModel>,
));
}
}
/// @nodoc
@JsonSerializable()
class _$_UserRepositoryState implements _UserRepositoryState {
const _$_UserRepositoryState({final Map<int, UserModel> users = const {}})
: _users = users;
factory _$_UserRepositoryState.fromJson(Map<String, dynamic> json) =>
_$$_UserRepositoryStateFromJson(json);
final Map<int, UserModel> _users;
@override
@JsonKey()
Map<int, UserModel> 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<String, dynamic> toJson() {
return _$$_UserRepositoryStateToJson(
this,
);
}
}
abstract class _UserRepositoryState implements UserRepositoryState {
const factory _UserRepositoryState({final Map<int, UserModel> users}) =
_$_UserRepositoryState;
factory _UserRepositoryState.fromJson(Map<String, dynamic> json) =
_$_UserRepositoryState.fromJson;
@override
Map<int, UserModel> get users;
@override
@JsonKey(ignore: true)
_$$_UserRepositoryStateCopyWith<_$_UserRepositoryState> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@@ -0,0 +1,11 @@
part of 'user_repository.dart';
@freezed
class UserRepositoryState with _$UserRepositoryState {
const factory UserRepositoryState({
@Default({}) Map<int, UserModel> users,
}) = _UserRepositoryState;
factory UserRepositoryState.fromJson(Map<String, dynamic> json) =>
_$UserRepositoryStateFromJson(json);
}

View File

@@ -39,20 +39,13 @@ class DocumentDetailsPage extends StatefulWidget {
} }
class _DocumentDetailsPageState extends State<DocumentDetailsPage> { class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
late Future<DocumentMetaData> _metaData;
static const double _itemSpacing = 24; static const double _itemSpacing = 24;
final _pagingScrollController = ScrollController(); final _pagingScrollController = ScrollController();
@override
void initState() {
super.initState();
_loadMetaData();
}
void _loadMetaData() { @override
_metaData = context void didChangeDependencies() {
.read<PaperlessDocumentsApi>() super.didChangeDependencies();
.getMetaData(context.read<DocumentDetailsCubit>().state.document);
} }
@override @override
@@ -67,8 +60,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
child: BlocListener<ConnectivityCubit, ConnectivityState>( child: BlocListener<ConnectivityCubit, ConnectivityState>(
listenWhen: (previous, current) => !previous.isConnected && current.isConnected, listenWhen: (previous, current) => !previous.isConnected && current.isConnected,
listener: (context, state) { listener: (context, state) {
_loadMetaData(); context.read<DocumentDetailsCubit>().loadMetaData();
setState(() {});
}, },
child: Scaffold( child: Scaffold(
extendBodyBehindAppBar: false, extendBodyBehindAppBar: false,
@@ -98,7 +90,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
), ),
), ),
), ),
Positioned.fill( Positioned.fill(
top: 0, top: 0,
child: Container( child: Container(
height: 100, height: 100,
@@ -285,7 +277,6 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
DocumentDownloadButton( DocumentDownloadButton(
document: state.document, document: state.document,
enabled: isConnected, enabled: isConnected,
metaData: _metaData,
), ),
IconButton( IconButton(
tooltip: S.of(context)!.previewTooltip, tooltip: S.of(context)!.previewTooltip,

View File

@@ -20,12 +20,10 @@ import 'package:permission_handler/permission_handler.dart';
class DocumentDownloadButton extends StatefulWidget { class DocumentDownloadButton extends StatefulWidget {
final DocumentModel? document; final DocumentModel? document;
final bool enabled; final bool enabled;
final Future<DocumentMetaData> metaData;
const DocumentDownloadButton({ const DocumentDownloadButton({
super.key, super.key,
required this.document, required this.document,
this.enabled = true, this.enabled = true,
required this.metaData,
}); });
@override @override

View File

@@ -38,12 +38,9 @@ class ScannerPage extends StatefulWidget {
State<ScannerPage> createState() => _ScannerPageState(); State<ScannerPage> createState() => _ScannerPageState();
} }
class _ScannerPageState extends State<ScannerPage> class _ScannerPageState extends State<ScannerPage> with SingleTickerProviderStateMixin {
with SingleTickerProviderStateMixin { final SliverOverlapAbsorberHandle searchBarHandle = SliverOverlapAbsorberHandle();
final SliverOverlapAbsorberHandle searchBarHandle = final SliverOverlapAbsorberHandle actionsHandle = SliverOverlapAbsorberHandle();
SliverOverlapAbsorberHandle();
final SliverOverlapAbsorberHandle actionsHandle =
SliverOverlapAbsorberHandle();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -180,8 +177,7 @@ class _ScannerPageState extends State<ScannerPage>
final success = await EdgeDetection.detectEdge(file.path); final success = await EdgeDetection.detectEdge(file.path);
if (!success) { if (!success) {
if (kDebugMode) { if (kDebugMode) {
dev.log( dev.log('[ScannerPage] Scan either not successful or canceled by user.');
'[ScannerPage] Scan either not successful or canceled by user.');
} }
return; return;
} }
@@ -198,7 +194,7 @@ class _ScannerPageState extends State<ScannerPage>
final uploadResult = await Navigator.of(context).push<DocumentUploadResult>( final uploadResult = await Navigator.of(context).push<DocumentUploadResult>(
MaterialPageRoute( MaterialPageRoute(
builder: (_) => BlocProvider( builder: (_) => BlocProvider(
create: (context) => DocumentUploadCubit( create: (_) => DocumentUploadCubit(
context.read(), context.read(),
context.read(), context.read(),
), ),
@@ -212,9 +208,7 @@ class _ScannerPageState extends State<ScannerPage>
if ((uploadResult?.success ?? false) && uploadResult?.taskId != null) { if ((uploadResult?.success ?? false) && uploadResult?.taskId != null) {
// For paperless version older than 1.11.3, task id will always be null! // For paperless version older than 1.11.3, task id will always be null!
context.read<DocumentScannerCubit>().reset(); context.read<DocumentScannerCubit>().reset();
context context.read<TaskStatusCubit>().listenToTaskChanges(uploadResult!.taskId!);
.read<TaskStatusCubit>()
.listenToTaskChanges(uploadResult!.taskId!);
} }
} }

View File

@@ -226,13 +226,10 @@ class _DocumentSearchPageState extends State<DocumentSearchPage> {
hasLoaded: state.hasLoaded, hasLoaded: state.hasLoaded,
enableHeroAnimation: false, enableHeroAnimation: false,
onTap: (document) { onTap: (document) {
Navigator.pushNamed( pushDocumentDetailsRoute(
context, context,
DocumentDetailsRoute.routeName, document: document,
arguments: DocumentDetailsRouteArguments( isLabelClickable: false,
document: document,
isLabelClickable: false,
),
); );
}, },
correspondents: state.correspondents, correspondents: state.correspondents,

View File

@@ -47,7 +47,10 @@ class SliverSearchBar extends StatelessWidget {
Hive.box<LocalUserAccount>(HiveBoxes.localUserAccount).listenable(), Hive.box<LocalUserAccount>(HiveBoxes.localUserAccount).listenable(),
builder: (context, box, _) { builder: (context, box, _) {
final account = box.get(settings.currentLoggedInUser!)!; final account = box.get(settings.currentLoggedInUser!)!;
return UserAvatar(userId: settings.currentLoggedInUser!, account: account); return UserAvatar(
userId: settings.currentLoggedInUser!,
account: account,
);
}, },
); );
}, },

View File

@@ -32,6 +32,7 @@ class DocumentUploadPreparationPage extends StatefulWidget {
final String? filename; final String? filename;
final String? fileExtension; final String? fileExtension;
const DocumentUploadPreparationPage({ const DocumentUploadPreparationPage({
Key? key, Key? key,
required this.fileBytes, required this.fileBytes,

View File

@@ -5,6 +5,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart'; import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
import 'package:paperless_mobile/core/delegate/customizable_sliver_persistent_header_delegate.dart'; import 'package:paperless_mobile/core/delegate/customizable_sliver_persistent_header_delegate.dart';
import 'package:paperless_mobile/core/repository/label_repository.dart';
import 'package:paperless_mobile/core/widgets/material/colored_tab_bar.dart'; import 'package:paperless_mobile/core/widgets/material/colored_tab_bar.dart';
import 'package:paperless_mobile/extensions/flutter_extensions.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.dart';
import 'package:paperless_mobile/features/app_drawer/view/app_drawer.dart'; import 'package:paperless_mobile/features/app_drawer/view/app_drawer.dart';
@@ -443,12 +444,9 @@ class _DocumentsPageState extends State<DocumentsPage> with SingleTickerProvider
} }
void _openDetails(DocumentModel document) { void _openDetails(DocumentModel document) {
Navigator.pushNamed( pushDocumentDetailsRoute(
context, context,
DocumentDetailsRoute.routeName, document: document,
arguments: DocumentDetailsRouteArguments(
document: document,
),
); );
} }

View File

@@ -2,7 +2,6 @@ import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:paperless_api/paperless_api.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:provider/provider.dart';
import 'package:shimmer/shimmer.dart'; import 'package:shimmer/shimmer.dart';

View File

@@ -23,8 +23,13 @@ import 'package:paperless_mobile/features/tasks/cubit/task_status_cubit.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class HomeRoute extends StatelessWidget { class HomeRoute extends StatelessWidget {
/// The id of the currently authenticated user (e.g. demo@paperless.example.com)
final String localUserId; final String localUserId;
/// The Paperless API version of the currently connected instance
final int paperlessApiVersion; final int paperlessApiVersion;
// A factory providing the API implementations given an API version
final PaperlessApiFactory paperlessProviderFactory; final PaperlessApiFactory paperlessProviderFactory;
const HomeRoute({ const HomeRoute({
@@ -44,6 +49,7 @@ class HomeRoute extends StatelessWidget {
Provider<CacheManager>( Provider<CacheManager>(
create: (context) => CacheManager( create: (context) => CacheManager(
Config( Config(
// Isolated cache per user.
localUserId, localUserId,
fileService: DioFileService(context.read<SessionManager>().client), fileService: DioFileService(context.read<SessionManager>().client),
), ),
@@ -121,11 +127,15 @@ class HomeRoute extends StatelessWidget {
)..initialize(), )..initialize(),
), ),
ProxyProvider<PaperlessServerStatsApi, ServerInformationCubit>( ProxyProvider<PaperlessServerStatsApi, ServerInformationCubit>(
update: (context, value, previous) => ServerInformationCubit(value), update: (context, value, previous) =>
ServerInformationCubit(value)..updateInformation(),
), ),
ProxyProvider<LabelRepository, LabelCubit>( ProxyProvider<LabelRepository, LabelCubit>(
update: (context, value, previous) => LabelCubit(value), update: (context, value, previous) => LabelCubit(value),
), ),
ProxyProvider<PaperlessTasksApi, TaskStatusCubit>(
update: (context, value, previous) => TaskStatusCubit(value),
),
], ],
child: const HomePage(), child: const HomePage(),
); );

View File

@@ -37,14 +37,11 @@ class _InboxItemState extends State<InboxItem> {
builder: (context, state) { builder: (context, state) {
return GestureDetector( return GestureDetector(
behavior: HitTestBehavior.translucent, behavior: HitTestBehavior.translucent,
onTap: () async { onTap: () {
Navigator.pushNamed( pushDocumentDetailsRoute(
context, context,
DocumentDetailsRoute.routeName, document: widget.document,
arguments: DocumentDetailsRouteArguments( isLabelClickable: false,
document: widget.document,
isLabelClickable: false,
),
); );
}, },
child: SizedBox( child: SizedBox(

View File

@@ -52,13 +52,10 @@ class _LinkedDocumentsPageState extends State<LinkedDocumentsPage>
isLoading: state.isLoading, isLoading: state.isLoading,
hasLoaded: state.hasLoaded, hasLoaded: state.hasLoaded,
onTap: (document) { onTap: (document) {
Navigator.pushNamed( pushDocumentDetailsRoute(
context, context,
DocumentDetailsRoute.routeName, document: document,
arguments: DocumentDetailsRouteArguments( isLabelClickable: false,
document: document,
isLabelClickable: false,
),
); );
}, },
correspondents: state.correspondents, correspondents: state.correspondents,

View File

@@ -1,7 +1,7 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:equatable/equatable.dart'; import 'package:dio/dio.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:hive_flutter/adapters.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_api/paperless_api.dart';
import 'package:paperless_mobile/core/config/hive/hive_config.dart'; import 'package:paperless_mobile/core/config/hive/hive_config.dart';
import 'package:paperless_mobile/core/database/tables/local_user_app_state.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/core/security/session_manager.dart';
import 'package:paperless_mobile/features/login/model/client_certificate.dart'; import 'package:paperless_mobile/features/login/model/client_certificate.dart';
import 'package:paperless_mobile/features/login/model/login_form_credentials.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'; part 'authentication_cubit.freezed.dart';
class AuthenticationCubit extends Cubit<AuthenticationState> { class AuthenticationCubit extends Cubit<AuthenticationState> {
final PaperlessUserApi _userApi;
final LocalAuthenticationService _localAuthService; final LocalAuthenticationService _localAuthService;
final PaperlessAuthenticationApi _authApi; final PaperlessApiFactory _apiFactory;
final SessionManager _sessionManager; final SessionManager _sessionManager;
AuthenticationCubit( AuthenticationCubit(
this._localAuthService, this._localAuthService,
this._authApi, this._apiFactory,
this._sessionManager, this._sessionManager,
this._userApi,
) : super(const AuthenticationState.unauthenticated()); ) : super(const AuthenticationState.unauthenticated());
Future<void> login({ Future<void> login({
@@ -43,7 +42,7 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
assert(credentials.username != null && credentials.password != null); assert(credentials.username != null && credentials.password != null);
final localUserId = "${credentials.username}@$serverUrl"; final localUserId = "${credentials.username}@$serverUrl";
final serverUser = await _addUser( await _addUser(
localUserId, localUserId,
serverUrl, serverUrl,
credentials, credentials,
@@ -51,13 +50,13 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
_sessionManager, _sessionManager,
); );
final response = await _sessionManager.client.get("/api/"); final apiVersion = await _getApiVersion(_sessionManager.client);
final apiVersion = response.headers["x-api-version"] as int;
// Mark logged in user as currently active user. // Mark logged in user as currently active user.
final globalSettings = Hive.box<GlobalSettings>(HiveBoxes.globalSettings).getValue()!; final globalSettings = Hive.box<GlobalSettings>(HiveBoxes.globalSettings).getValue()!;
globalSettings.currentLoggedInUser = localUserId; globalSettings.currentLoggedInUser = localUserId;
await globalSettings.save(); await globalSettings.save();
emit( emit(
AuthenticationState.authenticated( AuthenticationState.authenticated(
apiVersion: apiVersion, apiVersion: apiVersion,
@@ -110,8 +109,8 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
globalSettings.currentLoggedInUser = localUserId; globalSettings.currentLoggedInUser = localUserId;
await globalSettings.save(); await globalSettings.save();
final response = await _sessionManager.client.get("/api/"); final apiVersion = await _getApiVersion(_sessionManager.client);
final apiVersion = response.headers["x-api-version"] as int;
emit(AuthenticationState.authenticated( emit(AuthenticationState.authenticated(
localUserId: localUserId, localUserId: localUserId,
apiVersion: apiVersion, apiVersion: apiVersion,
@@ -188,8 +187,7 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
authToken: authentication.token, authToken: authentication.token,
baseUrl: userAccount.serverUrl, baseUrl: userAccount.serverUrl,
); );
final response = await _sessionManager.client.get("/api/"); final apiVersion = await _getApiVersion(_sessionManager.client);
final apiVersion = response.headers["x-api-version"] as int;
emit( emit(
AuthenticationState.authenticated( AuthenticationState.authenticated(
apiVersion: apiVersion, apiVersion: apiVersion,
@@ -247,26 +245,34 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
baseUrl: serverUrl, baseUrl: serverUrl,
clientCertificate: clientCert, clientCertificate: clientCert,
); );
final authApi = PaperlessAuthenticationApiImpl(sessionManager.client);
final authApi = _apiFactory.createAuthenticationApi(sessionManager.client);
final token = await authApi.login( final token = await authApi.login(
username: credentials.username!, username: credentials.username!,
password: credentials.password!, password: credentials.password!,
); );
sessionManager.updateSettings( sessionManager.updateSettings(
baseUrl: serverUrl, baseUrl: serverUrl,
clientCertificate: clientCert, clientCertificate: clientCert,
authToken: token, authToken: token,
); );
final userAccountBox = Hive.box<LocalUserAccount>(HiveBoxes.localUserAccount); final userAccountBox = Hive.box<LocalUserAccount>(HiveBoxes.localUserAccount);
final userStateBox = Hive.box<LocalUserAppState>(HiveBoxes.localUserAppState); final userStateBox = Hive.box<LocalUserAppState>(HiveBoxes.localUserAppState);
if (userAccountBox.containsKey(localUserId)) { if (userAccountBox.containsKey(localUserId)) {
throw Exception("User with id $localUserId already exists!"); 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 // Create user account
await userAccountBox.put( await userAccountBox.put(
@@ -275,7 +281,7 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
id: localUserId, id: localUserId,
settings: LocalUserSettings(), settings: LocalUserSettings(),
serverUrl: serverUrl, serverUrl: serverUrl,
paperlessUserId: 1, paperlessUser: serverUser,
), ),
); );
@@ -295,6 +301,11 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
), ),
); );
userCredentialsBox.close(); userCredentialsBox.close();
return serverUserId; return serverUser.id;
}
Future<int> _getApiVersion(Dio dio) async {
final response = await dio.get("/api/");
return int.parse(response.headers.value('x-api-version') ?? "3");
} }
} }

View File

@@ -1,6 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
@@ -37,18 +36,20 @@ class SavedViewCubit extends Cubit<SavedViewState> {
}, },
); );
_savedViewRepository.subscribe(this, (views) { _savedViewRepository.addListener(
emit( this,
state.maybeWhen( onChanged: (views) {
loaded: emit(
(savedViews, correspondents, documentTypes, tags, storagePaths) => state.maybeWhen(
(state as _SavedViewLoadedState).copyWith( loaded: (savedViews, correspondents, documentTypes, tags, storagePaths) =>
savedViews: views, (state as _SavedViewLoadedState).copyWith(
savedViews: views.savedViews,
),
orElse: () => state,
), ),
orElse: () => state, );
), },
); );
});
} }
Future<SavedView> add(SavedView view) async { Future<SavedView> add(SavedView view) async {
@@ -77,7 +78,7 @@ class SavedViewCubit extends Cubit<SavedViewState> {
@override @override
Future<void> close() { Future<void> close() {
_savedViewRepository.unsubscribe(this); _savedViewRepository.removeListener(this);
_labelRepository.removeListener(this); _labelRepository.removeListener(this);
return super.close(); return super.close();
} }

View File

@@ -76,12 +76,13 @@ class _SavedViewDetailsPageState extends State<SavedViewDetailsPage>
isLoading: state.isLoading, isLoading: state.isLoading,
hasLoaded: state.hasLoaded, hasLoaded: state.hasLoaded,
onTap: (document) { onTap: (document) {
Navigator.pushNamed( Navigator.push(
context, context,
DocumentDetailsRoute.routeName, MaterialPageRoute(
arguments: DocumentDetailsRouteArguments( builder: (context) => DocumentDetailsRoute(
document: document, document: document,
isLabelClickable: false, isLabelClickable: false,
),
), ),
); );
}, },

View File

@@ -86,11 +86,11 @@ class ManageAccountsPage extends StatelessWidget {
final child = SizedBox( final child = SizedBox(
width: double.maxFinite, width: double.maxFinite,
child: ListTile( child: ListTile(
title: Text(account.username), title: Text(account.paperlessUser.username),
subtitle: Column( subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
if (account.fullName != null) Text(account.fullName!), if (account.paperlessUser.fullName != null) Text(account.paperlessUser.fullName!),
Text( Text(
account.serverUrl.replaceFirst(RegExp(r'https://?'), ''), account.serverUrl.replaceFirst(RegExp(r'https://?'), ''),
style: TextStyle( style: TextStyle(
@@ -99,7 +99,7 @@ class ManageAccountsPage extends StatelessWidget {
), ),
], ],
), ),
isThreeLine: account.fullName != null, isThreeLine: account.paperlessUser.fullName != null,
leading: UserAvatar( leading: UserAvatar(
account: account, account: account,
userId: userId, userId: userId,

View File

@@ -22,7 +22,7 @@ class SettingsPage extends StatelessWidget {
final host = user!.serverUrl.replaceFirst(RegExp(r"https?://"), ""); final host = user!.serverUrl.replaceFirst(RegExp(r"https?://"), "");
return ListTile( return ListTile(
title: Text( title: Text(
S.of(context)!.loggedInAs(user.username) + "@$host", S.of(context)!.loggedInAs(user.paperlessUser.username) + "@$host",
style: Theme.of(context).textTheme.labelSmall, style: Theme.of(context).textTheme.labelSmall,
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),

View File

@@ -1,5 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/database/tables/local_user_account.dart'; import 'package:paperless_mobile/core/database/tables/local_user_account.dart';
import 'package:paperless_mobile/core/repository/user_repository.dart';
class UserAvatar extends StatelessWidget { class UserAvatar extends StatelessWidget {
final String userId; final String userId;
@@ -16,7 +19,7 @@ class UserAvatar extends StatelessWidget {
final backgroundColor = Colors.primaries[userId.hashCode % Colors.primaries.length]; final backgroundColor = Colors.primaries[userId.hashCode % Colors.primaries.length];
final foregroundColor = backgroundColor.computeLuminance() > 0.5 ? Colors.black : Colors.white; final foregroundColor = backgroundColor.computeLuminance() > 0.5 ? Colors.black : Colors.white;
return CircleAvatar( return CircleAvatar(
child: Text((account.fullName ?? account.username) child: Text((account.paperlessUser.fullName ?? account.paperlessUser.username)
.split(" ") .split(" ")
.take(2) .take(2)
.map((e) => e.substring(0, 1)) .map((e) => e.substring(0, 1))

View File

@@ -36,10 +36,8 @@ class _SimilarDocumentsViewState extends State<SimilarDocumentsView>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocConsumer<ConnectivityCubit, ConnectivityState>( return BlocConsumer<ConnectivityCubit, ConnectivityState>(
listenWhen: (previous, current) => listenWhen: (previous, current) => !previous.isConnected && current.isConnected,
!previous.isConnected && current.isConnected, listener: (context, state) => context.read<SimilarDocumentsCubit>().initialize(),
listener: (context, state) =>
context.read<SimilarDocumentsCubit>().initialize(),
builder: (context, connectivity) { builder: (context, connectivity) {
return BlocBuilder<SimilarDocumentsCubit, SimilarDocumentsState>( return BlocBuilder<SimilarDocumentsCubit, SimilarDocumentsState>(
builder: (context, state) { builder: (context, state) {
@@ -48,9 +46,7 @@ class _SimilarDocumentsViewState extends State<SimilarDocumentsView>
child: OfflineWidget(), child: OfflineWidget(),
); );
} }
if (state.hasLoaded && if (state.hasLoaded && !state.isLoading && state.documents.isEmpty) {
!state.isLoading &&
state.documents.isEmpty) {
return SliverToBoxAdapter( return SliverToBoxAdapter(
child: Center( child: Center(
child: Text(S.of(context)!.noItemsFound), child: Text(S.of(context)!.noItemsFound),
@@ -65,13 +61,10 @@ class _SimilarDocumentsViewState extends State<SimilarDocumentsView>
hasLoaded: state.hasLoaded, hasLoaded: state.hasLoaded,
enableHeroAnimation: false, enableHeroAnimation: false,
onTap: (document) { onTap: (document) {
Navigator.pushNamed( pushDocumentDetailsRoute(
context, context,
DocumentDetailsRoute.routeName, document: document,
arguments: DocumentDetailsRouteArguments( isLabelClickable: false,
document: document,
isLabelClickable: false,
),
); );
}, },
correspondents: state.correspondents, correspondents: state.correspondents,

View File

@@ -5,7 +5,6 @@ import 'package:device_info_plus/device_info_plus.dart';
import 'package:dynamic_color/dynamic_color.dart'; import 'package:dynamic_color/dynamic_color.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart' as cm;
import 'package:flutter_displaymode/flutter_displaymode.dart'; import 'package:flutter_displaymode/flutter_displaymode.dart';
import 'package:flutter_native_splash/flutter_native_splash.dart'; import 'package:flutter_native_splash/flutter_native_splash.dart';
import 'package:hive_flutter/adapters.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/dio_http_error_interceptor.dart';
import 'package:paperless_mobile/core/interceptor/language_header.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/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/security/session_manager.dart';
import 'package:paperless_mobile/core/service/connectivity_status_service.dart'; import 'package:paperless_mobile/core/service/connectivity_status_service.dart';
import 'package:paperless_mobile/core/service/dio_file_service.dart';
import 'package:paperless_mobile/core/type/types.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/app_intro/application_intro_slideshow.dart';
import 'package:paperless_mobile/features/home/view/home_route.dart'; import 'package:paperless_mobile/features/home/view/home_route.dart';
@@ -125,6 +121,8 @@ void main() async {
languageHeaderInterceptor.preferredLocaleSubtag = globalSettings.preferredLocaleSubtag; languageHeaderInterceptor.preferredLocaleSubtag = globalSettings.preferredLocaleSubtag;
}); });
final apiFactory = PaperlessApiFactoryImpl(sessionManager);
runApp( runApp(
MultiProvider( MultiProvider(
providers: [ providers: [
@@ -139,9 +137,17 @@ void main() async {
child: MultiBlocProvider( child: MultiBlocProvider(
providers: [ providers: [
BlocProvider<ConnectivityCubit>.value(value: connectivityCubit), BlocProvider<ConnectivityCubit>.value(value: connectivityCubit),
BlocProvider(
create: (context) => AuthenticationCubit(
localAuthService,
apiFactory,
sessionManager,
),
child: Container(),
)
], ],
child: PaperlessMobileEntrypoint( child: PaperlessMobileEntrypoint(
paperlessProviderFactory: PaperlessApiFactoryImpl(sessionManager), paperlessProviderFactory: apiFactory,
), ),
), ),
), ),
@@ -187,9 +193,6 @@ class _PaperlessMobileEntrypointState extends State<PaperlessMobileEntrypoint> {
localizationsDelegates: const [ localizationsDelegates: const [
...S.localizationsDelegates, ...S.localizationsDelegates,
], ],
routes: {
DocumentDetailsRoute.routeName: (context) => const DocumentDetailsRoute(),
},
home: AuthenticationWrapper( home: AuthenticationWrapper(
paperlessProviderFactory: widget.paperlessProviderFactory, paperlessProviderFactory: widget.paperlessProviderFactory,
), ),
@@ -217,7 +220,10 @@ class _AuthenticationWrapperState extends State<AuthenticationWrapper> {
@override @override
void didChangeDependencies() { void didChangeDependencies() {
super.didChangeDependencies(); super.didChangeDependencies();
FlutterNativeSplash.remove(); context
.read<AuthenticationCubit>()
.restoreSessionState()
.then((value) => FlutterNativeSplash.remove());
} }
@override @override

View File

@@ -1,34 +1,44 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
import 'package:paperless_mobile/core/repository/label_repository.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/cubit/document_details_cubit.dart';
import 'package:paperless_mobile/features/document_details/view/pages/document_details_page.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 { class DocumentDetailsRoute extends StatelessWidget {
static const String routeName = "/documentDetails"; final DocumentModel document;
const DocumentDetailsRoute({super.key}); 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final args = ModalRoute.of(context)!.settings.arguments
as DocumentDetailsRouteArguments;
return BlocProvider( return BlocProvider(
create: (context) => DocumentDetailsCubit( create: (_) => DocumentDetailsCubit(
context.read(), context.read(),
context.read(), context.read(),
context.read(), context.read(),
context.read(), context.read(),
initialDocument: args.document, initialDocument: document,
), ),
child: RepositoryProvider.value( lazy: false,
value: context.read<LabelRepository>(), child: DocumentDetailsPage(
child: DocumentDetailsPage( allowEdit: allowEdit,
allowEdit: args.allowEdit, isLabelClickable: isLabelClickable,
isLabelClickable: args.isLabelClickable, titleAndContentQueryString: titleAndContentQueryString,
titleAndContentQueryString: args.titleAndContentQueryString,
),
), ),
); );
} }
@@ -47,3 +57,33 @@ class DocumentDetailsRouteArguments {
this.titleAndContentQueryString, this.titleAndContentQueryString,
}); });
} }
Future<void> 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,
),
),
));
}

View File

@@ -51,7 +51,7 @@ class UserModel with _$UserModel {
if (value.firstName == null) { if (value.firstName == null) {
return value.lastName; return value.lastName;
} }
return value.firstName! + (value.lastName ?? ''); return "${value.firstName!} ${value.lastName ?? ''}";
}, },
); );

View File

@@ -18,7 +18,12 @@ class PaperlessServerStatsApiImpl implements PaperlessServerStatsApi {
@override @override
Future<PaperlessServerInformationModel> getServerInformation() async { Future<PaperlessServerInformationModel> 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 = final version =
response.headers[PaperlessServerInformationModel.versionHeader]?.first ?? 'unknown'; response.headers[PaperlessServerInformationModel.versionHeader]?.first ?? 'unknown';
final apiVersion = int.tryParse( final apiVersion = int.tryParse(

View File

@@ -2,5 +2,5 @@ import 'package:paperless_api/paperless_api.dart';
abstract class PaperlessUserApi { abstract class PaperlessUserApi {
Future<int> findCurrentUserId(); Future<int> findCurrentUserId();
Future<UserModel> find(int id); Future<UserModel> findCurrentUser();
} }

View File

@@ -1,6 +1,5 @@
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_api/src/modules/user_api/paperless_user_api.dart';
class PaperlessUserApiV2Impl implements PaperlessUserApi { class PaperlessUserApiV2Impl implements PaperlessUserApi {
final Dio client; final Dio client;
@@ -11,13 +10,13 @@ class PaperlessUserApiV2Impl implements PaperlessUserApi {
Future<int> findCurrentUserId() async { Future<int> findCurrentUserId() async {
final response = await client.get("/api/ui_settings/"); final response = await client.get("/api/ui_settings/");
if (response.statusCode == 200) { if (response.statusCode == 200) {
return response.data['user']['id']; return response.data['user_id'];
} }
throw const PaperlessServerException.unknown(); throw const PaperlessServerException.unknown();
} }
@override @override
Future<UserModel> find(int id) async { Future<UserModel> findCurrentUser() async {
final response = await client.get("/api/ui_settings/"); final response = await client.get("/api/ui_settings/");
if (response.statusCode == 200) { if (response.statusCode == 200) {
return UserModelV2.fromJson(response.data); return UserModelV2.fromJson(response.data);

View File

@@ -1,7 +1,9 @@
import 'package:paperless_api/src/models/user_model.dart'; import 'package:paperless_api/src/models/user_model.dart';
abstract class PaperlessUserApiV3 { abstract class PaperlessUserApiV3 {
Future<Iterable<UserModel>> findWhere({ Future<UserModelV3> find(int id);
Future<Iterable<UserModelV3>> findAll();
Future<Iterable<UserModelV3>> findWhere({
String startsWith, String startsWith,
String endsWith, String endsWith,
String contains, String contains,

View File

@@ -1,7 +1,5 @@
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_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 { class PaperlessUserApiV3Impl implements PaperlessUserApi, PaperlessUserApiV3 {
final Dio dio; final Dio dio;
@@ -9,7 +7,7 @@ class PaperlessUserApiV3Impl implements PaperlessUserApi, PaperlessUserApiV3 {
PaperlessUserApiV3Impl(this.dio); PaperlessUserApiV3Impl(this.dio);
@override @override
Future<UserModel> find(int id) async { Future<UserModelV3> find(int id) async {
final response = await dio.get("/api/users/$id/"); final response = await dio.get("/api/users/$id/");
if (response.statusCode == 200) { if (response.statusCode == 200) {
return UserModelV3.fromJson(response.data); return UserModelV3.fromJson(response.data);
@@ -18,7 +16,7 @@ class PaperlessUserApiV3Impl implements PaperlessUserApi, PaperlessUserApiV3 {
} }
@override @override
Future<Iterable<UserModel>> findWhere({ Future<Iterable<UserModelV3>> findWhere({
String startsWith = '', String startsWith = '',
String endsWith = '', String endsWith = '',
String contains = '', String contains = '',
@@ -31,9 +29,9 @@ class PaperlessUserApiV3Impl implements PaperlessUserApi, PaperlessUserApiV3 {
"username__iexact": username, "username__iexact": username,
}); });
if (response.statusCode == 200) { if (response.statusCode == 200) {
return PagedSearchResult<UserModel>.fromJson( return PagedSearchResult<UserModelV3>.fromJson(
response.data, response.data,
UserModelV3.fromJson as UserModel Function(Object?), UserModelV3.fromJson as UserModelV3 Function(Object?),
).results; ).results;
} }
throw const PaperlessServerException.unknown(); throw const PaperlessServerException.unknown();
@@ -43,8 +41,26 @@ class PaperlessUserApiV3Impl implements PaperlessUserApi, PaperlessUserApiV3 {
Future<int> findCurrentUserId() async { Future<int> findCurrentUserId() async {
final response = await dio.get("/api/ui_settings/"); final response = await dio.get("/api/ui_settings/");
if (response.statusCode == 200) { if (response.statusCode == 200) {
return response.data['user_id']; return response.data['user']['id'];
} }
throw const PaperlessServerException.unknown(); throw const PaperlessServerException.unknown();
} }
@override
Future<Iterable<UserModelV3>> findAll() async {
final response = await dio.get("/api/users/");
if (response.statusCode == 200) {
return PagedSearchResult<UserModelV3>.fromJson(
response.data,
UserModelV3.fromJson as UserModelV3 Function(Object?),
).results;
}
throw const PaperlessServerException.unknown();
}
@override
Future<UserModel> findCurrentUser() async {
final id = await findCurrentUserId();
return find(id);
}
} }