fix: Adapt to new provider strucutre

This commit is contained in:
Anton Stubenbord
2023-05-11 12:37:17 +02:00
parent d5c68e023c
commit f388f77d63
43 changed files with 540 additions and 1254 deletions

View File

@@ -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<ServerInformationState> {
final PaperlessServerStatsApi _api;
ServerInformationCubit(this._api) : super(ServerInformationState());
Future<void> updateInformation() async {
final information = await _api.getServerInformation();
emit(ServerInformationState(
isLoaded: true,
information: information,
));
}
}

View File

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

View File

@@ -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<LocalUserAccount>(HiveBoxes.localUserAccount)
.get(Hive.box<GlobalSettings>(HiveBoxes.globalSettings).getValue()!.currentLoggedInUser)!;
}

View File

@@ -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<GlobalSettings>(HiveBoxes.globalSettings).getValue()!.currentLoggedInUser!;
return Hive.box<LocalUserAppState>(HiveBoxes.localUserAppState).get(currentLocalUserId)!;
}
}

View File

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

View File

@@ -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<void> pushDocumentSearchPage(BuildContext context) {
final currentUser =
Hive.box<GlobalSettings>(HiveBoxes.globalSettings).getValue()!.currentLoggedInUser;
return Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => MultiProvider(
providers: [
Provider.value(value: context.read<LabelRepository>()),
Provider.value(value: context.read<PaperlessDocumentsApi>()),
Provider.value(value: context.read<DocumentChangedNotifier>()),
Provider.value(value: context.read<CacheManager>()),
],
builder: (context, _) {
return BlocProvider(
create: (context) => DocumentSearchCubit(
context.read(),
context.read(),
context.read(),
Hive.box<LocalUserAppState>(HiveBoxes.localUserAppState).get(currentUser)!,
),
child: const DocumentSearchPage(),
);
},
),
),
);
}
Future<void> 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<ApiVersion>()),
Provider.value(value: context.read<LabelRepository>()),
Provider.value(value: context.read<DocumentChangedNotifier>()),
Provider.value(value: context.read<PaperlessDocumentsApi>()),
Provider.value(value: context.read<LocalNotificationService>()),
Provider.value(value: context.read<CacheManager>()),
if (context.read<ApiVersion>().hasMultiUserSupport)
Provider.value(value: context.read<UserRepository>()),
],
child: DocumentDetailsRoute(
document: document,
isLabelClickable: isLabelClickable,
),
),
),
);
}
Future<void> pushSavedViewDetailsRoute(BuildContext context, {required SavedView savedView}) {
return Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => MultiProvider(
providers: [
Provider.value(value: context.read<LabelRepository>()),
Provider.value(value: context.read<DocumentChangedNotifier>()),
Provider.value(value: context.read<PaperlessDocumentsApi>()),
Provider.value(value: context.read<CacheManager>()),
],
child: SavedViewDetailsPage(
onDelete: context.read<SavedViewCubit>().remove,
),
builder: (_, child) {
return BlocProvider(
create: (context) => SavedViewDetailsCubit(
context.read(),
context.read(),
context.read(),
LocalUserAppState.current,
savedView: savedView,
),
child: SavedViewDetailsPage(onDelete: context.read<SavedViewCubit>().remove),
);
},
),
),
);
}

View File

@@ -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
}
}

View File

@@ -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<ServerInformationCubit>(),
builder: (_) => Provider.value(
value: context.read<ApiVersion>(),
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.',
),
],

View File

@@ -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<DocumentDetailsPage> {
@override
Widget build(BuildContext context) {
final apiVersion = context.watch<ApiVersion>();
final tabLength = 4 + (apiVersion.supportsPermissions ? 1 : 0);
return WillPopScope(
onWillPop: () async {
Navigator.of(context).pop(context.read<DocumentDetailsCubit>().state.document);
return false;
},
child: DefaultTabController(
length: 4,
length: tabLength,
child: BlocListener<ConnectivityCubit, ConnectivityState>(
listenWhen: (previous, current) => !previous.isConnected && current.isConnected,
listener: (context, state) {
@@ -65,7 +73,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
child: Scaffold(
extendBodyBehindAppBar: false,
floatingActionButtonLocation: FloatingActionButtonLocation.endDocked,
floatingActionButton: widget.allowEdit ? _buildEditButton() : null,
floatingActionButton: _buildEditButton(),
bottomNavigationBar: _buildBottomAppBar(),
body: NestedScrollView(
headerSliverBuilder: (context, innerBoxIsScrolled) => [
@@ -147,6 +155,15 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
),
),
),
if (apiVersion.supportsPermissions)
Tab(
child: Text(
"Permissions",
style: TextStyle(
color: Theme.of(context).colorScheme.onPrimaryContainer,
),
),
),
],
),
),
@@ -220,6 +237,15 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
),
],
),
CustomScrollView(
controller: _pagingScrollController,
slivers: [
SliverOverlapInjector(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
),
const DocumentPermissionsWidget(),
],
),
],
),
),
@@ -234,25 +260,29 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
}
Widget _buildEditButton() {
bool canEdit = context.watchInternetConnection;
final apiVersion = context.watch<ApiVersion>();
if (apiVersion.supportsPermissions) {
canEdit =
LocalUserAccount.current.paperlessUser.hasPermission(UserPermissions.changeDocument);
}
if (!canEdit) {
return const SizedBox.shrink();
}
return BlocBuilder<DocumentDetailsCubit, DocumentDetailsState>(
builder: (context, state) {
// final _filteredSuggestions =
// state.suggestions?.documentDifference(state.document);
return BlocBuilder<ConnectivityCubit, ConnectivityState>(
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<DocumentDetailsPage> {
child: BlocBuilder<ConnectivityCubit, ConnectivityState>(
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<DocumentDetailsPage> {
Future<void> _onOpen(DocumentModel document) async {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => DocumentView(
builder: (_) => DocumentView(
documentBytes: context.read<PaperlessDocumentsApi>().getPreview(document.id),
),
),

View File

@@ -29,7 +29,11 @@ class _DocumentMetaDataWidgetState extends State<DocumentMetaDataWidget> {
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(

View File

@@ -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(),
);
}
}

View File

@@ -264,9 +264,12 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
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],
);

View File

@@ -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;

View File

@@ -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<void> showDocumentSearchPage(BuildContext context) {
final currentUser =
Hive.box<GlobalSettings>(HiveBoxes.globalSettings).getValue()!.currentLoggedInUser;
return Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => BlocProvider(
create: (context) => DocumentSearchCubit(
context.read(),
context.read(),
context.read(),
Hive.box<LocalUserAppState>(HiveBoxes.localUserAppState).get(currentUser)!,
),
child: const DocumentSearchPage(),
),
),
);
}
import 'package:provider/provider.dart';
class DocumentSearchPage extends StatefulWidget {
const DocumentSearchPage({super.key});

View File

@@ -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<ServerInformationCubit>(),
builder: (_) => Provider.value(
value: context.read<ApiVersion>(),
child: const ManageAccountsPage(),
),
);

View File

@@ -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<DocumentsState> with DocumentPagingBlocMixin {
class DocumentsCubit extends HydratedCubit<DocumentsState> with DocumentPagingBlocMixin {
@override
final PaperlessDocumentsApi api;
@@ -123,4 +123,14 @@ class DocumentsCubit extends Cubit<DocumentsState> with DocumentPagingBlocMixin
_userState.currentDocumentFilter = filter;
await _userState.save();
}
@override
DocumentsState? fromJson(Map<String, dynamic> json) {
return DocumentsState.fromJson(json);
}
@override
Map<String, dynamic>? toJson(DocumentsState state) {
return state.toJson();
}
}

View File

@@ -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<Uint8List> documentBytes;
@@ -17,36 +15,37 @@ class DocumentView extends StatefulWidget {
}
class _DocumentViewState extends State<DocumentView> {
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<DefaultBuilderOptions>(
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<DefaultBuilderOptions>(
// options: const DefaultBuilderOptions(
// loaderSwitchDuration: Duration(milliseconds: 500),
// ),
// pageLoaderBuilder: (context) => const Center(
// child: CircularProgressIndicator(),
// ),
// ),
// scrollDirection: Axis.vertical,
// controller: _pdfController,
// ),
// );
}
}

View File

@@ -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<DocumentsPage> with SingleTickerProvider
builder: (context, state) {
return AddSavedViewPage(
currentFilter: filter,
correspondents: state.correspondents,
documentTypes: state.documentTypes,
storagePaths: state.storagePaths,
tags: state.tags,
correspondents: context.read<LabelRepository>().state.correspondents,
documentTypes: context.read<LabelRepository>().state.documentTypes,
storagePaths: context.read<LabelRepository>().state.storagePaths,
tags: context.read<LabelRepository>().state.tags,
);
},
),

View File

@@ -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<SortFieldSelectionBottomSheet> createState() =>
_SortFieldSelectionBottomSheetState();
State<SortFieldSelectionBottomSheet> createState() => _SortFieldSelectionBottomSheetState();
}
class _SortFieldSelectionBottomSheetState
extends State<SortFieldSelectionBottomSheet> {
class _SortFieldSelectionBottomSheetState extends State<SortFieldSelectionBottomSheet> {
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);
},
);
}
}

View File

@@ -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<DocumentsCubit>()
.updateCurrentFilter(
(filter) => filter.copyWith(
sortField: field,
sortOrder: order,
),
),
onSubmit: (field, order) {
return context.read<DocumentsCubit>().updateCurrentFilter(
(filter) => filter.copyWith(
sortField: field,
sortOrder: order,
),
);
},
correspondents: state.correspondents,
documentTypes: state.documentTypes,
storagePaths: state.storagePaths,

View File

@@ -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<HomePage> with WidgetsBindingObserver {
@override
Widget build(BuildContext context) {
final userId =
Hive.box<GlobalSettings>(HiveBoxes.globalSettings).getValue()!.currentLoggedInUser!;
final destinations = [
RouteDescription(
icon: const Icon(Icons.description_outlined),
@@ -235,6 +234,7 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
];
final routes = <Widget>[
const DocumentsPage(),
if (LocalUserAccount.current.paperlessUser.hasPermission(UserPermissions.changeDocument))
const ScannerPage(),
const LabelsPage(),
const InboxPage(),
@@ -303,7 +303,6 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
Future.wait([
context.read<LabelRepository>().initialize(),
context.read<SavedViewRepository>().findAll(),
context.read<ServerInformationCubit>().updateInformation(),
]).onError<PaperlessServerException>((error, stackTrace) {
showErrorMessage(context, error, stackTrace);
throw error;

View File

@@ -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<CacheManager>(
create: (context) => CacheManager(
Config(
@@ -85,6 +87,12 @@ class HomeRoute extends StatelessWidget {
apiVersion: paperlessApiVersion,
),
),
if (paperlessApiVersion >= 3)
ProxyProvider<SessionManager, PaperlessUserApiV3>(
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<PaperlessDocumentsApi, DocumentChangedNotifier, LabelRepository,
DocumentsCubit>(
@@ -120,24 +128,23 @@ class HomeRoute extends StatelessWidget {
notifier,
)..initialize(),
),
ProxyProvider2<SavedViewRepository, LabelRepository, SavedViewCubit>(
update: (context, savedViewRepo, labelRepo, previous) => SavedViewCubit(
ProxyProvider<SavedViewRepository, SavedViewCubit>(
update: (context, savedViewRepo, previous) => SavedViewCubit(
savedViewRepo,
labelRepo,
)..initialize(),
),
ProxyProvider<PaperlessServerStatsApi, ServerInformationCubit>(
update: (context, value, previous) =>
ServerInformationCubit(value)..updateInformation(),
),
ProxyProvider<LabelRepository, LabelCubit>(
update: (context, value, previous) => LabelCubit(value),
),
ProxyProvider<PaperlessTasksApi, TaskStatusCubit>(
update: (context, value, previous) => TaskStatusCubit(value),
),
if (paperlessApiVersion >= 3)
ProxyProvider<PaperlessUserApiV3, UserRepository>(
update: (context, value, previous) => UserRepository(value)..initialize(),
),
],
child: const HomePage(),
child: HomePage(paperlessApiVersion: paperlessApiVersion),
);
},
);

View File

@@ -0,0 +1,8 @@
class ApiVersion {
final int version;
ApiVersion(this.version);
bool get supportsPermissions => version >= 3;
bool get hasMultiUserSupport => version >= 3;
}

View File

@@ -2,6 +2,7 @@ import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/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';

View File

@@ -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';

View File

@@ -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<AuthenticationState> {
final LocalAuthenticationService _localAuthService;
@@ -111,6 +110,12 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
final apiVersion = await _getApiVersion(_sessionManager.client);
await _updateRemoteUser(
_sessionManager,
Hive.box<LocalUserAccount>(HiveBoxes.localUserAccount).get(localUserId)!,
apiVersion,
);
emit(AuthenticationState.authenticated(
localUserId: localUserId,
apiVersion: apiVersion,
@@ -163,10 +168,10 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
// If there is nothing to restore, we can quit here.
return;
}
final localUserAccountBox = Hive.box<LocalUserAccount>(HiveBoxes.localUserAccount);
final localUserAccount = localUserAccountBox.get(localUserId)!;
final userAccount = Hive.box<LocalUserAccount>(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<AuthenticationState> {
}
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,
@@ -308,4 +318,20 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
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<void> _updateRemoteUser(
SessionManager sessionManager,
LocalUserAccount localUserAccount,
int apiVersion,
) async {
final updatedPaperlessUser = await _apiFactory
.createUserApi(
sessionManager.client,
apiVersion: apiVersion,
)
.findCurrentUser();
localUserAccount.paperlessUser = updatedPaperlessUser;
await localUserAccount.save();
}
}

View File

@@ -11,38 +11,14 @@ part 'saved_view_cubit.freezed.dart';
class SavedViewCubit extends Cubit<SavedViewState> {
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<SavedViewState> {
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<SavedViewState> {
@override
Future<void> close() {
_savedViewRepository.removeListener(this);
_labelRepository.removeListener(this);
return super.close();
}
}

View File

File diff suppressed because it is too large Load Diff

View File

@@ -2,32 +2,12 @@ part of 'saved_view_cubit.dart';
@freezed
class SavedViewState with _$SavedViewState {
const factory SavedViewState.initial({
required Map<int, Correspondent> correspondents,
required Map<int, DocumentType> documentTypes,
required Map<int, Tag> tags,
required Map<int, StoragePath> storagePaths,
}) = _SavedViewIntialState;
const factory SavedViewState.initial() = _SavedViewIntialState;
const factory SavedViewState.loading({
required Map<int, Correspondent> correspondents,
required Map<int, DocumentType> documentTypes,
required Map<int, Tag> tags,
required Map<int, StoragePath> storagePaths,
}) = _SavedViewLoadingState;
const factory SavedViewState.loading() = _SavedViewLoadingState;
const factory SavedViewState.loaded({
required Map<int, SavedView> savedViews,
required Map<int, Correspondent> correspondents,
required Map<int, DocumentType> documentTypes,
required Map<int, Tag> tags,
required Map<int, StoragePath> storagePaths,
}) = _SavedViewLoadedState;
const factory SavedViewState.loaded({required Map<int, SavedView> savedViews}) =
_SavedViewLoadedState;
const factory SavedViewState.error({
required Map<int, Correspondent> correspondents,
required Map<int, DocumentType> documentTypes,
required Map<int, Tag> tags,
required Map<int, StoragePath> storagePaths,
}) = _SavedViewErrorState;
const factory SavedViewState.error() = _SavedViewErrorState;
}

View File

@@ -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<SavedViewCubit, SavedViewState>(
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<LocalUserAppState>(HiveBoxes.localUserAppState).get(
Hive.box<GlobalSettings>(HiveBoxes.globalSettings)
.getValue()!
.currentLoggedInUser!,
)!,
savedView: view,
),
),
],
child: SavedViewDetailsPage(
onDelete: context.read<SavedViewCubit>().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.",
),

View File

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
import 'package:paperless_mobile/core/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<SavedViewDetailsPage>
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,

View File

@@ -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<ApiVersion>(
builder: (context, value, child) {
if (value.version >= 3) {
return const ListTile(
leading: Icon(Icons.admin_panel_settings),
title: Text("Manage permissions"), //TODO : INTL
);
}
return const SizedBox.shrink();
},
)
],
);
},

View File

@@ -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<ServerInformationCubit, ServerInformationState>(
builder: (context, state) {
subtitle: FutureBuilder<PaperlessServerInformationModel>(
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,
);

View File

@@ -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;

View File

@@ -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<AuthenticationWrapper> {
@override
void didChangeDependencies() {
super.didChangeDependencies();
context
.read<AuthenticationCubit>()
.restoreSessionState()
.then((value) => FlutterNativeSplash.remove());
context.read<AuthenticationCubit>().restoreSessionState().then((value) {
FlutterNativeSplash.remove();
});
}
@override

View File

@@ -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<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

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

View File

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

View File

@@ -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)

View File

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

View File

@@ -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!;
}
}

View File

@@ -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

View File

@@ -52,7 +52,7 @@ class PaperlessUserApiV3Impl implements PaperlessUserApi, PaperlessUserApiV3 {
if (response.statusCode == 200) {
return PagedSearchResult<UserModelV3>.fromJson(
response.data,
UserModelV3.fromJson as UserModelV3 Function(Object?),
(json) => UserModelV3.fromJson(json as dynamic),
).results;
}
throw const PaperlessServerException.unknown();