mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2025-12-10 08:07:59 -06:00
feat: Migrate to go_router
This commit is contained in:
@@ -1,330 +0,0 @@
|
||||
import 'dart:async';
|
||||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
|
||||
import 'package:paperless_mobile/core/config/hive/hive_config.dart';
|
||||
import 'package:paperless_mobile/core/database/tables/global_settings.dart';
|
||||
import 'package:paperless_mobile/core/database/tables/local_user_account.dart';
|
||||
import 'package:paperless_mobile/core/global/constants.dart';
|
||||
import 'package:paperless_mobile/core/navigation/push_routes.dart';
|
||||
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||
import 'package:paperless_mobile/core/repository/saved_view_repository.dart';
|
||||
import 'package:paperless_mobile/core/service/file_description.dart';
|
||||
import 'package:paperless_mobile/core/translation/error_code_localization_mapper.dart';
|
||||
import 'package:paperless_mobile/features/document_scan/view/scanner_page.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/pages/documents_page.dart';
|
||||
import 'package:paperless_mobile/features/home/view/route_description.dart';
|
||||
import 'package:paperless_mobile/features/inbox/cubit/inbox_cubit.dart';
|
||||
import 'package:paperless_mobile/features/inbox/view/pages/inbox_page.dart';
|
||||
import 'package:paperless_mobile/features/labels/view/pages/labels_page.dart';
|
||||
import 'package:paperless_mobile/features/notifications/services/local_notification_service.dart';
|
||||
import 'package:paperless_mobile/features/sharing/share_intent_queue.dart';
|
||||
import 'package:paperless_mobile/features/tasks/cubit/task_status_cubit.dart';
|
||||
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
|
||||
import 'package:responsive_builder/responsive_builder.dart';
|
||||
|
||||
/// Wrapper around all functionality for a logged in user.
|
||||
/// Performs initialization logic.
|
||||
class HomePage extends StatefulWidget {
|
||||
final int paperlessApiVersion;
|
||||
const HomePage({Key? key, required this.paperlessApiVersion})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
_HomePageState createState() => _HomePageState();
|
||||
}
|
||||
|
||||
class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
|
||||
int _currentIndex = 0;
|
||||
Timer? _inboxTimer;
|
||||
late final StreamSubscription _shareMediaSubscription;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
|
||||
final currentUser = Hive.box<GlobalSettings>(HiveBoxes.globalSettings)
|
||||
.getValue()!
|
||||
.currentLoggedInUser!;
|
||||
// For sharing files coming from outside the app while the app is still opened
|
||||
_shareMediaSubscription = ReceiveSharingIntent.getMediaStream().listen(
|
||||
(files) =>
|
||||
ShareIntentQueue.instance.addAll(files, userId: currentUser));
|
||||
// For sharing files coming from outside the app while the app is closed
|
||||
ReceiveSharingIntent.getInitialMedia().then((files) =>
|
||||
ShareIntentQueue.instance.addAll(files, userId: currentUser));
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||
_listenForReceivedFiles();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
}
|
||||
|
||||
void _listenToInboxChanges() {
|
||||
if (LocalUserAccount.current.paperlessUser.canViewTags) {
|
||||
_inboxTimer = Timer.periodic(const Duration(seconds: 60), (timer) {
|
||||
if (!mounted) {
|
||||
timer.cancel();
|
||||
} else {
|
||||
context.read<InboxCubit>().refreshItemsInInboxCount();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||
switch (state) {
|
||||
case AppLifecycleState.resumed:
|
||||
log('App is now in foreground');
|
||||
context.read<ConnectivityCubit>().reload();
|
||||
log("Reloaded device connectivity state");
|
||||
if (!(_inboxTimer?.isActive ?? true)) {
|
||||
_listenToInboxChanges();
|
||||
}
|
||||
break;
|
||||
case AppLifecycleState.inactive:
|
||||
case AppLifecycleState.paused:
|
||||
case AppLifecycleState.detached:
|
||||
default:
|
||||
log('App is now in background');
|
||||
_inboxTimer?.cancel();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
_inboxTimer?.cancel();
|
||||
_shareMediaSubscription.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _listenForReceivedFiles() async {
|
||||
final currentUser = Hive.box<GlobalSettings>(HiveBoxes.globalSettings)
|
||||
.getValue()!
|
||||
.currentLoggedInUser!;
|
||||
if (ShareIntentQueue.instance.userHasUnhandlesFiles(currentUser)) {
|
||||
await _handleReceivedFile(ShareIntentQueue.instance.pop(currentUser)!);
|
||||
}
|
||||
ShareIntentQueue.instance.addListener(() async {
|
||||
final queue = ShareIntentQueue.instance;
|
||||
while (queue.userHasUnhandlesFiles(currentUser)) {
|
||||
final file = queue.pop(currentUser)!;
|
||||
await _handleReceivedFile(file);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool _isFileTypeSupported(SharedMediaFile file) {
|
||||
return supportedFileExtensions.contains(
|
||||
file.path.split('.').last.toLowerCase(),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _handleReceivedFile(final SharedMediaFile file) async {
|
||||
SharedMediaFile mediaFile;
|
||||
if (Platform.isIOS) {
|
||||
// Workaround for file not found on iOS: https://stackoverflow.com/a/72813212
|
||||
mediaFile = SharedMediaFile(
|
||||
file.path.replaceAll('file://', ''),
|
||||
file.thumbnail,
|
||||
file.duration,
|
||||
file.type,
|
||||
);
|
||||
} else {
|
||||
mediaFile = file;
|
||||
}
|
||||
debugPrint("Consuming media file: ${mediaFile.path}");
|
||||
if (!_isFileTypeSupported(mediaFile)) {
|
||||
Fluttertoast.showToast(
|
||||
msg: translateError(context, ErrorCode.unsupportedFileFormat),
|
||||
);
|
||||
if (Platform.isAndroid) {
|
||||
// As stated in the docs, SystemNavigator.pop() is ignored on IOS to comply with HCI guidelines.
|
||||
await SystemNavigator.pop();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!LocalUserAccount.current.paperlessUser.canCreateDocuments) {
|
||||
Fluttertoast.showToast(
|
||||
msg: "You do not have the permissions to upload documents.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
final fileDescription = FileDescription.fromPath(mediaFile.path);
|
||||
if (await File(mediaFile.path).exists()) {
|
||||
final bytes = await File(mediaFile.path).readAsBytes();
|
||||
final result = await pushDocumentUploadPreparationPage(
|
||||
context,
|
||||
bytes: bytes,
|
||||
filename: fileDescription.filename,
|
||||
title: fileDescription.filename,
|
||||
fileExtension: fileDescription.extension,
|
||||
);
|
||||
if (result?.success ?? false) {
|
||||
await Fluttertoast.showToast(
|
||||
msg: S.of(context)!.documentSuccessfullyUploadedProcessing,
|
||||
);
|
||||
SystemNavigator.pop();
|
||||
}
|
||||
} else {
|
||||
Fluttertoast.showToast(
|
||||
msg: S.of(context)!.couldNotAccessReceivedFile,
|
||||
toastLength: Toast.LENGTH_LONG,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final destinations = [
|
||||
RouteDescription(
|
||||
icon: const Icon(Icons.description_outlined),
|
||||
selectedIcon: Icon(
|
||||
Icons.description,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
label: S.of(context)!.documents,
|
||||
),
|
||||
if (LocalUserAccount.current.paperlessUser.canCreateDocuments)
|
||||
RouteDescription(
|
||||
icon: const Icon(Icons.document_scanner_outlined),
|
||||
selectedIcon: Icon(
|
||||
Icons.document_scanner,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
label: S.of(context)!.scanner,
|
||||
),
|
||||
RouteDescription(
|
||||
icon: const Icon(Icons.sell_outlined),
|
||||
selectedIcon: Icon(
|
||||
Icons.sell,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
label: S.of(context)!.labels,
|
||||
),
|
||||
if (LocalUserAccount.current.paperlessUser.canViewTags)
|
||||
RouteDescription(
|
||||
icon: const Icon(Icons.inbox_outlined),
|
||||
selectedIcon: Icon(
|
||||
Icons.inbox,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
label: S.of(context)!.inbox,
|
||||
badgeBuilder: (icon) => BlocBuilder<InboxCubit, InboxState>(
|
||||
builder: (context, state) {
|
||||
return Badge.count(
|
||||
isLabelVisible: state.itemsInInboxCount > 0,
|
||||
count: state.itemsInInboxCount,
|
||||
child: icon,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
];
|
||||
final routes = <Widget>[
|
||||
const DocumentsPage(),
|
||||
if (LocalUserAccount.current.paperlessUser.canCreateDocuments)
|
||||
const ScannerPage(),
|
||||
const LabelsPage(),
|
||||
if (LocalUserAccount.current.paperlessUser.canViewTags) const InboxPage(),
|
||||
];
|
||||
return MultiBlocListener(
|
||||
listeners: [
|
||||
BlocListener<ConnectivityCubit, ConnectivityState>(
|
||||
// If app was started offline, load data once it comes back online.
|
||||
listenWhen: (previous, current) =>
|
||||
previous != ConnectivityState.connected &&
|
||||
current == ConnectivityState.connected,
|
||||
listener: (context, state) async {
|
||||
try {
|
||||
debugPrint(
|
||||
"[HomePage] BlocListener#listener: "
|
||||
"Loading saved views and labels...",
|
||||
);
|
||||
await Future.wait([
|
||||
context.read<LabelRepository>().initialize(),
|
||||
context.read<SavedViewRepository>().initialize(),
|
||||
]);
|
||||
debugPrint("[HomePage] BlocListener#listener: "
|
||||
"Saved views and labels successfully loaded.");
|
||||
} catch (error, stackTrace) {
|
||||
debugPrint(
|
||||
'[HomePage] BlocListener.listener: '
|
||||
'An error occurred while loading saved views and labels.\n'
|
||||
'${error.toString()}',
|
||||
);
|
||||
debugPrintStack(stackTrace: stackTrace);
|
||||
}
|
||||
},
|
||||
),
|
||||
BlocListener<TaskStatusCubit, TaskStatusState>(
|
||||
listener: (context, state) {
|
||||
if (state.task != null) {
|
||||
// Handle local notifications on task change (only when app is running for now).
|
||||
context
|
||||
.read<LocalNotificationService>()
|
||||
.notifyTaskChanged(state.task!);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
child: ResponsiveBuilder(
|
||||
builder: (context, sizingInformation) {
|
||||
if (!sizingInformation.isMobile) {
|
||||
return Scaffold(
|
||||
body: Row(
|
||||
children: [
|
||||
NavigationRail(
|
||||
labelType: NavigationRailLabelType.all,
|
||||
destinations: destinations
|
||||
.map((e) => e.toNavigationRailDestination())
|
||||
.toList(),
|
||||
selectedIndex: _currentIndex,
|
||||
onDestinationSelected: _onNavigationChanged,
|
||||
),
|
||||
const VerticalDivider(thickness: 1, width: 1),
|
||||
Expanded(
|
||||
child: routes[_currentIndex],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
return Scaffold(
|
||||
bottomNavigationBar: NavigationBar(
|
||||
labelBehavior: NavigationDestinationLabelBehavior.alwaysShow,
|
||||
elevation: 4.0,
|
||||
selectedIndex: _currentIndex,
|
||||
onDestinationSelected: _onNavigationChanged,
|
||||
destinations:
|
||||
destinations.map((e) => e.toNavigationDestination()).toList(),
|
||||
),
|
||||
body: routes[_currentIndex],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _onNavigationChanged(index) {
|
||||
if (_currentIndex != index) {
|
||||
setState(() => _currentIndex = index);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||
import 'package:hive_flutter/adapters.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/config/hive/hive_config.dart';
|
||||
import 'package:paperless_mobile/core/database/tables/local_user_account.dart';
|
||||
import 'package:paperless_mobile/core/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';
|
||||
import 'package:paperless_mobile/features/settings/view/widgets/global_settings_builder.dart';
|
||||
import 'package:paperless_mobile/features/tasks/cubit/task_status_cubit.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class HomeRoute extends StatelessWidget {
|
||||
/// The id of the currently authenticated user (e.g. demo@paperless.example.com)
|
||||
final String localUserId;
|
||||
|
||||
/// The Paperless API version of the currently connected instance
|
||||
final int paperlessApiVersion;
|
||||
|
||||
// A factory providing the API implementations given an API version
|
||||
final PaperlessApiFactory paperlessProviderFactory;
|
||||
|
||||
const HomeRoute({
|
||||
super.key,
|
||||
required this.paperlessApiVersion,
|
||||
required this.paperlessProviderFactory,
|
||||
required this.localUserId,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GlobalSettingsBuilder(
|
||||
builder: (context, settings) {
|
||||
final currentLocalUserId = settings.currentLoggedInUser;
|
||||
if (currentLocalUserId == null) {
|
||||
// This is the case when the current user logs out of the app.
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
final currentUser =
|
||||
Hive.box<LocalUserAccount>(HiveBoxes.localUserAccount)
|
||||
.get(currentLocalUserId)!;
|
||||
final apiVersion = ApiVersion(paperlessApiVersion);
|
||||
return MultiProvider(
|
||||
providers: [
|
||||
Provider.value(value: apiVersion),
|
||||
Provider<CacheManager>(
|
||||
create: (context) => CacheManager(
|
||||
Config(
|
||||
// Isolated cache per user.
|
||||
localUserId,
|
||||
fileService:
|
||||
DioFileService(context.read<SessionManager>().client),
|
||||
),
|
||||
),
|
||||
),
|
||||
ProxyProvider<SessionManager, PaperlessDocumentsApi>(
|
||||
update: (context, value, previous) =>
|
||||
paperlessProviderFactory.createDocumentsApi(
|
||||
value.client,
|
||||
apiVersion: paperlessApiVersion,
|
||||
),
|
||||
),
|
||||
ProxyProvider<SessionManager, PaperlessLabelsApi>(
|
||||
update: (context, value, previous) =>
|
||||
paperlessProviderFactory.createLabelsApi(
|
||||
value.client,
|
||||
apiVersion: paperlessApiVersion,
|
||||
),
|
||||
),
|
||||
ProxyProvider<SessionManager, PaperlessSavedViewsApi>(
|
||||
update: (context, value, previous) =>
|
||||
paperlessProviderFactory.createSavedViewsApi(
|
||||
value.client,
|
||||
apiVersion: paperlessApiVersion,
|
||||
),
|
||||
),
|
||||
ProxyProvider<SessionManager, PaperlessServerStatsApi>(
|
||||
update: (context, value, previous) =>
|
||||
paperlessProviderFactory.createServerStatsApi(
|
||||
value.client,
|
||||
apiVersion: paperlessApiVersion,
|
||||
),
|
||||
),
|
||||
ProxyProvider<SessionManager, PaperlessTasksApi>(
|
||||
update: (context, value, previous) =>
|
||||
paperlessProviderFactory.createTasksApi(
|
||||
value.client,
|
||||
apiVersion: paperlessApiVersion,
|
||||
),
|
||||
),
|
||||
if (apiVersion.hasMultiUserSupport)
|
||||
ProxyProvider<SessionManager, PaperlessUserApiV3>(
|
||||
update: (context, value, previous) => PaperlessUserApiV3Impl(
|
||||
value.client,
|
||||
),
|
||||
),
|
||||
],
|
||||
builder: (context, child) {
|
||||
return MultiProvider(
|
||||
providers: [
|
||||
ProxyProvider<PaperlessLabelsApi, LabelRepository>(
|
||||
update: (context, value, previous) {
|
||||
final repo = LabelRepository(value);
|
||||
if (currentUser.paperlessUser.canViewCorrespondents) {
|
||||
repo.findAllCorrespondents();
|
||||
}
|
||||
if (currentUser.paperlessUser.canViewDocumentTypes) {
|
||||
repo.findAllDocumentTypes();
|
||||
}
|
||||
if (currentUser.paperlessUser.canViewTags) {
|
||||
repo.findAllTags();
|
||||
}
|
||||
if (currentUser.paperlessUser.canViewStoragePaths) {
|
||||
repo.findAllStoragePaths();
|
||||
}
|
||||
return repo;
|
||||
},
|
||||
),
|
||||
ProxyProvider<PaperlessSavedViewsApi, SavedViewRepository>(
|
||||
update: (context, value, previous) {
|
||||
final repo = SavedViewRepository(value);
|
||||
if (currentUser.paperlessUser.canViewSavedViews) {
|
||||
repo.initialize();
|
||||
}
|
||||
return repo;
|
||||
},
|
||||
),
|
||||
],
|
||||
builder: (context, child) {
|
||||
return MultiProvider(
|
||||
providers: [
|
||||
ProxyProvider3<
|
||||
PaperlessDocumentsApi,
|
||||
DocumentChangedNotifier,
|
||||
LabelRepository,
|
||||
DocumentsCubit>(
|
||||
update:
|
||||
(context, docApi, notifier, labelRepo, previous) =>
|
||||
DocumentsCubit(
|
||||
docApi,
|
||||
notifier,
|
||||
labelRepo,
|
||||
Hive.box<LocalUserAppState>(HiveBoxes.localUserAppState)
|
||||
.get(currentLocalUserId)!,
|
||||
)..initialize(),
|
||||
),
|
||||
Provider(
|
||||
create: (context) =>
|
||||
DocumentScannerCubit(context.read())),
|
||||
ProxyProvider4<
|
||||
PaperlessDocumentsApi,
|
||||
PaperlessServerStatsApi,
|
||||
LabelRepository,
|
||||
DocumentChangedNotifier,
|
||||
InboxCubit>(
|
||||
update: (context, docApi, statsApi, labelRepo, notifier,
|
||||
previous) =>
|
||||
InboxCubit(
|
||||
docApi,
|
||||
statsApi,
|
||||
labelRepo,
|
||||
notifier,
|
||||
)..initialize(),
|
||||
),
|
||||
ProxyProvider<SavedViewRepository, SavedViewCubit>(
|
||||
update: (context, savedViewRepo, previous) =>
|
||||
SavedViewCubit(savedViewRepo),
|
||||
),
|
||||
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: HomePage(paperlessApiVersion: paperlessApiVersion),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
208
lib/features/home/view/home_shell_widget.dart
Normal file
208
lib/features/home/view/home_shell_widget.dart
Normal file
@@ -0,0 +1,208 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||
import 'package:hive_flutter/adapters.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/config/hive/hive_config.dart';
|
||||
import 'package:paperless_mobile/core/database/tables/local_user_account.dart';
|
||||
import 'package:paperless_mobile/core/database/tables/local_user_app_state.dart';
|
||||
import 'package:paperless_mobile/core/factory/paperless_api_factory.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/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';
|
||||
import 'package:paperless_mobile/features/settings/view/widgets/global_settings_builder.dart';
|
||||
import 'package:paperless_mobile/features/tasks/cubit/task_status_cubit.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class HomeShellWidget extends StatelessWidget {
|
||||
/// The id of the currently authenticated user (e.g. demo@paperless.example.com)
|
||||
final String localUserId;
|
||||
|
||||
/// The Paperless API version of the currently connected instance
|
||||
final int paperlessApiVersion;
|
||||
|
||||
// A factory providing the API implementations given an API version
|
||||
final PaperlessApiFactory paperlessProviderFactory;
|
||||
|
||||
final Widget child;
|
||||
|
||||
const HomeShellWidget({
|
||||
super.key,
|
||||
required this.paperlessApiVersion,
|
||||
required this.paperlessProviderFactory,
|
||||
required this.localUserId,
|
||||
required this.child,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GlobalSettingsBuilder(
|
||||
builder: (context, settings) {
|
||||
final currentUserId = settings.loggedInUserId;
|
||||
if (currentUserId == null) {
|
||||
// This is the case when the current user logs out of the app.
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
final apiVersion = ApiVersion(paperlessApiVersion);
|
||||
return ValueListenableBuilder(
|
||||
valueListenable:
|
||||
Hive.box<LocalUserAccount>(HiveBoxes.localUserAccount)
|
||||
.listenable(keys: [currentUserId]),
|
||||
builder: (context, box, _) {
|
||||
final currentLocalUser = box.get(currentUserId)!;
|
||||
return MultiProvider(
|
||||
providers: [
|
||||
Provider.value(value: currentLocalUser),
|
||||
Provider.value(value: apiVersion),
|
||||
Provider(
|
||||
create: (context) => CacheManager(
|
||||
Config(
|
||||
// Isolated cache per user.
|
||||
localUserId,
|
||||
fileService:
|
||||
DioFileService(context.read<SessionManager>().client),
|
||||
),
|
||||
),
|
||||
),
|
||||
Provider(
|
||||
create: (context) =>
|
||||
paperlessProviderFactory.createDocumentsApi(
|
||||
context.read<SessionManager>().client,
|
||||
apiVersion: paperlessApiVersion,
|
||||
),
|
||||
),
|
||||
Provider(
|
||||
create: (context) => paperlessProviderFactory.createLabelsApi(
|
||||
context.read<SessionManager>().client,
|
||||
apiVersion: paperlessApiVersion,
|
||||
),
|
||||
),
|
||||
Provider(
|
||||
create: (context) =>
|
||||
paperlessProviderFactory.createSavedViewsApi(
|
||||
context.read<SessionManager>().client,
|
||||
apiVersion: paperlessApiVersion,
|
||||
),
|
||||
),
|
||||
Provider(
|
||||
create: (context) =>
|
||||
paperlessProviderFactory.createServerStatsApi(
|
||||
context.read<SessionManager>().client,
|
||||
apiVersion: paperlessApiVersion,
|
||||
),
|
||||
),
|
||||
Provider(
|
||||
create: (context) => paperlessProviderFactory.createTasksApi(
|
||||
context.read<SessionManager>().client,
|
||||
apiVersion: paperlessApiVersion,
|
||||
),
|
||||
),
|
||||
if (currentLocalUser.hasMultiUserSupport)
|
||||
Provider(
|
||||
create: (context) => PaperlessUserApiV3Impl(
|
||||
context.read<SessionManager>().client,
|
||||
),
|
||||
),
|
||||
],
|
||||
builder: (context, _) {
|
||||
return MultiProvider(
|
||||
providers: [
|
||||
Provider(
|
||||
create: (context) {
|
||||
final repo = LabelRepository(context.read());
|
||||
if (currentLocalUser
|
||||
.paperlessUser.canViewCorrespondents) {
|
||||
repo.findAllCorrespondents();
|
||||
}
|
||||
if (currentLocalUser
|
||||
.paperlessUser.canViewDocumentTypes) {
|
||||
repo.findAllDocumentTypes();
|
||||
}
|
||||
if (currentLocalUser.paperlessUser.canViewTags) {
|
||||
repo.findAllTags();
|
||||
}
|
||||
if (currentLocalUser
|
||||
.paperlessUser.canViewStoragePaths) {
|
||||
repo.findAllStoragePaths();
|
||||
}
|
||||
return repo;
|
||||
},
|
||||
),
|
||||
Provider(
|
||||
create: (context) {
|
||||
final repo = SavedViewRepository(context.read());
|
||||
if (currentLocalUser.paperlessUser.canViewSavedViews) {
|
||||
repo.initialize();
|
||||
}
|
||||
return repo;
|
||||
},
|
||||
),
|
||||
],
|
||||
builder: (context, _) {
|
||||
return MultiProvider(
|
||||
providers: [
|
||||
Provider(
|
||||
create: (context) => DocumentsCubit(
|
||||
context.read(),
|
||||
context.read(),
|
||||
context.read(),
|
||||
Hive.box<LocalUserAppState>(
|
||||
HiveBoxes.localUserAppState)
|
||||
.get(currentUserId)!,
|
||||
)..initialize(),
|
||||
),
|
||||
Provider(
|
||||
create: (context) =>
|
||||
DocumentScannerCubit(context.read()),
|
||||
),
|
||||
if (currentLocalUser.paperlessUser.canViewDocuments &&
|
||||
currentLocalUser.paperlessUser.canViewTags)
|
||||
Provider(
|
||||
create: (context) => InboxCubit(
|
||||
context.read(),
|
||||
context.read(),
|
||||
context.read(),
|
||||
context.read(),
|
||||
).initialize(),
|
||||
),
|
||||
Provider(
|
||||
create: (context) => SavedViewCubit(
|
||||
context.read(),
|
||||
),
|
||||
),
|
||||
Provider(
|
||||
create: (context) => LabelCubit(
|
||||
context.read(),
|
||||
),
|
||||
),
|
||||
Provider(
|
||||
create: (context) => TaskStatusCubit(
|
||||
context.read(),
|
||||
),
|
||||
),
|
||||
if (currentLocalUser.hasMultiUserSupport)
|
||||
Provider(
|
||||
create: (context) => UserRepository(
|
||||
context.read(),
|
||||
)..initialize(),
|
||||
),
|
||||
],
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
class ApiVersion {
|
||||
final int version;
|
||||
|
||||
ApiVersion(this.version);
|
||||
const ApiVersion(this.version);
|
||||
|
||||
bool get hasMultiUserSupport => version >= 3;
|
||||
|
||||
}
|
||||
|
||||
168
lib/features/home/view/scaffold_with_navigation_bar.dart
Normal file
168
lib/features/home/view/scaffold_with_navigation_bar.dart
Normal file
@@ -0,0 +1,168 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/features/app_drawer/view/app_drawer.dart';
|
||||
import 'package:paperless_mobile/features/inbox/cubit/inbox_cubit.dart';
|
||||
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||
|
||||
const _landingPage = 0;
|
||||
const _documentsIndex = 1;
|
||||
const _scannerIndex = 2;
|
||||
const _labelsIndex = 3;
|
||||
const _inboxIndex = 4;
|
||||
|
||||
class ScaffoldWithNavigationBar extends StatefulWidget {
|
||||
final UserModel authenticatedUser;
|
||||
final StatefulNavigationShell navigationShell;
|
||||
const ScaffoldWithNavigationBar({
|
||||
super.key,
|
||||
required this.authenticatedUser,
|
||||
required this.navigationShell,
|
||||
});
|
||||
|
||||
@override
|
||||
State<ScaffoldWithNavigationBar> createState() =>
|
||||
ScaffoldWithNavigationBarState();
|
||||
}
|
||||
|
||||
class ScaffoldWithNavigationBarState extends State<ScaffoldWithNavigationBar> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final disabledColor = Theme.of(context).disabledColor;
|
||||
final primaryColor = Theme.of(context).colorScheme.primary;
|
||||
return Scaffold(
|
||||
drawer: const AppDrawer(),
|
||||
bottomNavigationBar: NavigationBar(
|
||||
selectedIndex: widget.navigationShell.currentIndex,
|
||||
onDestinationSelected: (index) {
|
||||
switch (index) {
|
||||
case _landingPage:
|
||||
widget.navigationShell.goBranch(index);
|
||||
break;
|
||||
case _documentsIndex:
|
||||
if (widget.authenticatedUser.canViewDocuments) {
|
||||
widget.navigationShell.goBranch(index);
|
||||
} else {
|
||||
showSnackBar(
|
||||
context, "You do not have permission to access this page.");
|
||||
}
|
||||
break;
|
||||
case _scannerIndex:
|
||||
if (widget.authenticatedUser.canCreateDocuments) {
|
||||
widget.navigationShell.goBranch(index);
|
||||
} else {
|
||||
showSnackBar(
|
||||
context, "You do not have permission to access this page.");
|
||||
}
|
||||
break;
|
||||
case _labelsIndex:
|
||||
if (widget.authenticatedUser.canViewAnyLabel) {
|
||||
widget.navigationShell.goBranch(index);
|
||||
} else {
|
||||
showSnackBar(
|
||||
context, "You do not have permission to access this page.");
|
||||
}
|
||||
break;
|
||||
case _inboxIndex:
|
||||
if (widget.authenticatedUser.canViewDocuments &&
|
||||
widget.authenticatedUser.canViewTags) {
|
||||
widget.navigationShell.goBranch(index);
|
||||
} else {
|
||||
showSnackBar(
|
||||
context, "You do not have permission to access this page.");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
destinations: [
|
||||
NavigationDestination(
|
||||
icon: Icon(Icons.home_outlined),
|
||||
selectedIcon: Icon(
|
||||
Icons.home,
|
||||
color: primaryColor,
|
||||
),
|
||||
label: "Home", //TODO: INTL
|
||||
),
|
||||
NavigationDestination(
|
||||
icon: Icon(
|
||||
Icons.description_outlined,
|
||||
color: !widget.authenticatedUser.canViewDocuments
|
||||
? disabledColor
|
||||
: null,
|
||||
),
|
||||
selectedIcon: Icon(
|
||||
Icons.description,
|
||||
color: primaryColor,
|
||||
),
|
||||
label: S.of(context)!.documents,
|
||||
),
|
||||
NavigationDestination(
|
||||
icon: Icon(
|
||||
Icons.document_scanner_outlined,
|
||||
color: !widget.authenticatedUser.canCreateDocuments
|
||||
? disabledColor
|
||||
: null,
|
||||
),
|
||||
selectedIcon: Icon(
|
||||
Icons.document_scanner,
|
||||
color: primaryColor,
|
||||
),
|
||||
label: S.of(context)!.scanner,
|
||||
),
|
||||
NavigationDestination(
|
||||
icon: Icon(
|
||||
Icons.sell_outlined,
|
||||
color: !widget.authenticatedUser.canViewAnyLabel
|
||||
? disabledColor
|
||||
: null,
|
||||
),
|
||||
selectedIcon: Icon(
|
||||
Icons.sell,
|
||||
color: primaryColor,
|
||||
),
|
||||
label: S.of(context)!.labels,
|
||||
),
|
||||
NavigationDestination(
|
||||
icon: Builder(builder: (context) {
|
||||
if (!(widget.authenticatedUser.canViewDocuments &&
|
||||
widget.authenticatedUser.canViewTags)) {
|
||||
return Icon(
|
||||
Icons.close,
|
||||
color: disabledColor,
|
||||
);
|
||||
}
|
||||
return BlocBuilder<InboxCubit, InboxState>(
|
||||
builder: (context, state) {
|
||||
return Badge.count(
|
||||
isLabelVisible: state.itemsInInboxCount > 0,
|
||||
count: state.itemsInInboxCount,
|
||||
child: const Icon(Icons.inbox_outlined),
|
||||
);
|
||||
},
|
||||
);
|
||||
}),
|
||||
selectedIcon: BlocBuilder<InboxCubit, InboxState>(
|
||||
builder: (context, state) {
|
||||
return Badge.count(
|
||||
isLabelVisible: state.itemsInInboxCount > 0,
|
||||
count: state.itemsInInboxCount,
|
||||
child: Icon(
|
||||
Icons.inbox,
|
||||
color: primaryColor,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
label: S.of(context)!.inbox,
|
||||
),
|
||||
],
|
||||
),
|
||||
body: widget.navigationShell,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user