mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2025-12-06 13:15:49 -06:00
fix: Improve receiving shares
This commit is contained in:
@@ -18,7 +18,6 @@ class HiveBoxes {
|
|||||||
static const localUserCredentials = 'localUserCredentials';
|
static const localUserCredentials = 'localUserCredentials';
|
||||||
static const localUserAccount = 'localUserAccount';
|
static const localUserAccount = 'localUserAccount';
|
||||||
static const localUserAppState = 'localUserAppState';
|
static const localUserAppState = 'localUserAppState';
|
||||||
static const localUserSettings = 'localUserSettings';
|
|
||||||
static const hosts = 'hosts';
|
static const hosts = 'hosts';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ 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/global_settings.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/database/tables/local_user_app_state.dart';
|
import 'package:paperless_mobile/core/database/tables/local_user_app_state.dart';
|
||||||
import 'package:paperless_mobile/core/database/tables/local_user_settings.dart';
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Opens an encrypted box, calls [callback] with the now opened box, awaits
|
/// Opens an encrypted box, calls [callback] with the now opened box, awaits
|
||||||
@@ -53,8 +52,6 @@ extension HiveBoxAccessors on HiveInterface {
|
|||||||
box<LocalUserAccount>(HiveBoxes.localUserAccount);
|
box<LocalUserAccount>(HiveBoxes.localUserAccount);
|
||||||
Box<LocalUserAppState> get localUserAppStateBox =>
|
Box<LocalUserAppState> get localUserAppStateBox =>
|
||||||
box<LocalUserAppState>(HiveBoxes.localUserAppState);
|
box<LocalUserAppState>(HiveBoxes.localUserAppState);
|
||||||
Box<LocalUserSettings> get localUserSettingsBox =>
|
|
||||||
box<LocalUserSettings>(HiveBoxes.localUserSettings);
|
|
||||||
Box<GlobalSettings> get globalSettingsBox =>
|
Box<GlobalSettings> get globalSettingsBox =>
|
||||||
box<GlobalSettings>(HiveBoxes.globalSettings);
|
box<GlobalSettings>(HiveBoxes.globalSettings);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ class AppDrawer extends StatelessWidget {
|
|||||||
final child = ListTile(
|
final child = ListTile(
|
||||||
dense: true,
|
dense: true,
|
||||||
leading: const Icon(Icons.drive_folder_upload_outlined),
|
leading: const Icon(Icons.drive_folder_upload_outlined),
|
||||||
title: const Text("Upload Queue"),
|
title: const Text("Pending Files"),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
UploadQueueRoute().push(context);
|
UploadQueueRoute().push(context);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:open_filex/open_filex.dart';
|
import 'package:open_filex/open_filex.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
|
import 'package:paperless_mobile/core/database/tables/local_user_account.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/label_repository.dart';
|
||||||
import 'package:paperless_mobile/core/service/file_description.dart';
|
import 'package:paperless_mobile/core/service/file_description.dart';
|
||||||
@@ -120,6 +121,7 @@ class DocumentDetailsCubit extends Cubit<DocumentDetailsState> {
|
|||||||
Future<void> downloadDocument({
|
Future<void> downloadDocument({
|
||||||
bool downloadOriginal = false,
|
bool downloadOriginal = false,
|
||||||
required String locale,
|
required String locale,
|
||||||
|
required String userId,
|
||||||
}) async {
|
}) async {
|
||||||
if (state.metaData == null) {
|
if (state.metaData == null) {
|
||||||
await loadMetaData();
|
await loadMetaData();
|
||||||
@@ -141,6 +143,7 @@ class DocumentDetailsCubit extends Cubit<DocumentDetailsState> {
|
|||||||
filePath: filePath,
|
filePath: filePath,
|
||||||
finished: true,
|
finished: true,
|
||||||
locale: locale,
|
locale: locale,
|
||||||
|
userId: userId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,6 +153,7 @@ class DocumentDetailsCubit extends Cubit<DocumentDetailsState> {
|
|||||||
filePath: filePath,
|
filePath: filePath,
|
||||||
finished: false,
|
finished: false,
|
||||||
locale: locale,
|
locale: locale,
|
||||||
|
userId: userId,
|
||||||
);
|
);
|
||||||
|
|
||||||
await _api.downloadToFile(
|
await _api.downloadToFile(
|
||||||
@@ -163,6 +167,7 @@ class DocumentDetailsCubit extends Cubit<DocumentDetailsState> {
|
|||||||
filePath: filePath,
|
filePath: filePath,
|
||||||
finished: true,
|
finished: true,
|
||||||
locale: locale,
|
locale: locale,
|
||||||
|
userId: userId,
|
||||||
);
|
);
|
||||||
debugPrint("Downloaded file to $filePath");
|
debugPrint("Downloaded file to $filePath");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.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_account.dart';
|
||||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
import 'package:paperless_mobile/extensions/flutter_extensions.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/dialogs/select_file_type_dialog.dart';
|
import 'package:paperless_mobile/features/document_details/view/dialogs/select_file_type_dialog.dart';
|
||||||
@@ -90,9 +91,11 @@ class _DocumentDownloadButtonState extends State<DocumentDownloadButton> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setState(() => _isDownloadPending = true);
|
setState(() => _isDownloadPending = true);
|
||||||
|
final userId = context.read<LocalUserAccount>().id;
|
||||||
await context.read<DocumentDetailsCubit>().downloadDocument(
|
await context.read<DocumentDetailsCubit>().downloadDocument(
|
||||||
downloadOriginal: original,
|
downloadOriginal: original,
|
||||||
locale: globalSettings.preferredLocaleSubtag,
|
locale: globalSettings.preferredLocaleSubtag,
|
||||||
|
userId: userId,
|
||||||
);
|
);
|
||||||
// showSnackBar(context, S.of(context)!.documentSuccessfullyDownloaded);
|
// showSnackBar(context, S.of(context)!.documentSuccessfullyDownloaded);
|
||||||
} on PaperlessApiException catch (error, stackTrace) {
|
} on PaperlessApiException catch (error, stackTrace) {
|
||||||
|
|||||||
@@ -305,13 +305,13 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
|
|||||||
var mergedDocument = document.copyWith(
|
var mergedDocument = document.copyWith(
|
||||||
title: values[fkTitle],
|
title: values[fkTitle],
|
||||||
created: values[fkCreatedDate],
|
created: values[fkCreatedDate],
|
||||||
documentType: () => (values[fkDocumentType] as IdQueryParameter)
|
documentType: () => (values[fkDocumentType] as IdQueryParameter?)
|
||||||
.whenOrNull(fromId: (id) => id),
|
?.whenOrNull(fromId: (id) => id),
|
||||||
correspondent: () => (values[fkCorrespondent] as IdQueryParameter)
|
correspondent: () => (values[fkCorrespondent] as IdQueryParameter?)
|
||||||
.whenOrNull(fromId: (id) => id),
|
?.whenOrNull(fromId: (id) => id),
|
||||||
storagePath: () => (values[fkStoragePath] as IdQueryParameter)
|
storagePath: () => (values[fkStoragePath] as IdQueryParameter?)
|
||||||
.whenOrNull(fromId: (id) => id),
|
?.whenOrNull(fromId: (id) => id),
|
||||||
tags: (values[fkTags] as IdsTagsQuery).include,
|
tags: (values[fkTags] as IdsTagsQuery?)?.include,
|
||||||
content: values[fkContent],
|
content: values[fkContent],
|
||||||
);
|
);
|
||||||
setState(() {
|
setState(() {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import 'package:paperless_mobile/features/document_scan/view/widgets/scanned_ima
|
|||||||
import 'package:paperless_mobile/features/document_search/view/sliver_search_bar.dart';
|
import 'package:paperless_mobile/features/document_search/view/sliver_search_bar.dart';
|
||||||
import 'package:paperless_mobile/features/document_upload/view/document_upload_preparation_page.dart';
|
import 'package:paperless_mobile/features/document_upload/view/document_upload_preparation_page.dart';
|
||||||
import 'package:paperless_mobile/features/documents/view/pages/document_view.dart';
|
import 'package:paperless_mobile/features/documents/view/pages/document_view.dart';
|
||||||
import 'package:paperless_mobile/features/tasks/cubit/task_status_cubit.dart';
|
import 'package:paperless_mobile/features/tasks/model/pending_tasks_notifier.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||||
import 'package:paperless_mobile/helpers/connectivity_aware_action_wrapper.dart';
|
import 'package:paperless_mobile/helpers/connectivity_aware_action_wrapper.dart';
|
||||||
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ class _DocumentUploadPreparationPageState
|
|||||||
static final fileNameDateFormat = DateFormat("yyyy_MM_ddTHH_mm_ss");
|
static final fileNameDateFormat = DateFormat("yyyy_MM_ddTHH_mm_ss");
|
||||||
|
|
||||||
final GlobalKey<FormBuilderState> _formKey = GlobalKey();
|
final GlobalKey<FormBuilderState> _formKey = GlobalKey();
|
||||||
Color? _titleColor;
|
|
||||||
Map<String, String> _errors = {};
|
Map<String, String> _errors = {};
|
||||||
bool _isUploadLoading = false;
|
bool _isUploadLoading = false;
|
||||||
late bool _syncTitleAndFilename;
|
late bool _syncTitleAndFilename;
|
||||||
@@ -71,10 +70,6 @@ class _DocumentUploadPreparationPageState
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_syncTitleAndFilename = widget.filename == null && widget.title == null;
|
_syncTitleAndFilename = widget.filename == null && widget.title == null;
|
||||||
_computeAverageColor().then((value) {
|
|
||||||
_titleColor =
|
|
||||||
value.computeLuminance() > 0.5 ? Colors.black : Colors.white;
|
|
||||||
});
|
|
||||||
initializeDateFormatting();
|
initializeDateFormatting();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,9 +97,7 @@ class _DocumentUploadPreparationPageState
|
|||||||
handle:
|
handle:
|
||||||
NestedScrollView.sliverOverlapAbsorberHandleFor(context),
|
NestedScrollView.sliverOverlapAbsorberHandleFor(context),
|
||||||
sliver: SliverAppBar(
|
sliver: SliverAppBar(
|
||||||
leading: BackButton(
|
leading: BackButton(),
|
||||||
color: _titleColor,
|
|
||||||
),
|
|
||||||
pinned: true,
|
pinned: true,
|
||||||
expandedHeight: 150,
|
expandedHeight: 150,
|
||||||
flexibleSpace: FlexibleSpaceBar(
|
flexibleSpace: FlexibleSpaceBar(
|
||||||
@@ -112,20 +105,17 @@ class _DocumentUploadPreparationPageState
|
|||||||
future: widget.fileBytes,
|
future: widget.fileBytes,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (!snapshot.hasData) {
|
if (!snapshot.hasData) {
|
||||||
return const SizedBox.shrink();
|
return SizedBox.shrink();
|
||||||
}
|
}
|
||||||
return FileThumbnail(
|
return FileThumbnail(
|
||||||
bytes: snapshot.data!,
|
bytes: snapshot.data!,
|
||||||
fit: BoxFit.fitWidth,
|
fit: BoxFit.fitWidth,
|
||||||
|
width: MediaQuery.sizeOf(context).width,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
title: Text(
|
title: Text(S.of(context)!.prepareDocument),
|
||||||
S.of(context)!.prepareDocument,
|
collapseMode: CollapseMode.pin,
|
||||||
style: TextStyle(
|
|
||||||
color: _titleColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
bottom: _isUploadLoading
|
bottom: _isUploadLoading
|
||||||
? PreferredSize(
|
? PreferredSize(
|
||||||
@@ -416,32 +406,32 @@ class _DocumentUploadPreparationPageState
|
|||||||
return source.replaceAll(RegExp(r"[\W_]"), "_").toLowerCase();
|
return source.replaceAll(RegExp(r"[\W_]"), "_").toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Color> _computeAverageColor() async {
|
// Future<Color> _computeAverageColor() async {
|
||||||
final bitmap = img.decodeImage(await widget.fileBytes);
|
// final bitmap = img.decodeImage(await widget.fileBytes);
|
||||||
if (bitmap == null) {
|
// if (bitmap == null) {
|
||||||
return Colors.black;
|
// return Colors.black;
|
||||||
}
|
// }
|
||||||
int redBucket = 0;
|
// int redBucket = 0;
|
||||||
int greenBucket = 0;
|
// int greenBucket = 0;
|
||||||
int blueBucket = 0;
|
// int blueBucket = 0;
|
||||||
int pixelCount = 0;
|
// int pixelCount = 0;
|
||||||
|
|
||||||
for (int y = 0; y < bitmap.height; y++) {
|
// for (int y = 0; y < bitmap.height; y++) {
|
||||||
for (int x = 0; x < bitmap.width; x++) {
|
// for (int x = 0; x < bitmap.width; x++) {
|
||||||
final c = bitmap.getPixel(x, y);
|
// final c = bitmap.getPixel(x, y);
|
||||||
|
|
||||||
pixelCount++;
|
// pixelCount++;
|
||||||
redBucket += c.r.toInt();
|
// redBucket += c.r.toInt();
|
||||||
greenBucket += c.g.toInt();
|
// greenBucket += c.g.toInt();
|
||||||
blueBucket += c.b.toInt();
|
// blueBucket += c.b.toInt();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
return Color.fromRGBO(
|
// return Color.fromRGBO(
|
||||||
redBucket ~/ pixelCount,
|
// redBucket ~/ pixelCount,
|
||||||
greenBucket ~/ pixelCount,
|
// greenBucket ~/ pixelCount,
|
||||||
blueBucket ~/ pixelCount,
|
// blueBucket ~/ pixelCount,
|
||||||
1,
|
// 1,
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import 'package:paperless_mobile/features/documents/view/widgets/selection/view_
|
|||||||
import 'package:paperless_mobile/features/documents/view/widgets/sort_documents_button.dart';
|
import 'package:paperless_mobile/features/documents/view/widgets/sort_documents_button.dart';
|
||||||
import 'package:paperless_mobile/features/labels/cubit/label_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/saved_view/cubit/saved_view_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/tasks/cubit/task_status_cubit.dart';
|
import 'package:paperless_mobile/features/tasks/model/pending_tasks_notifier.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||||
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||||
import 'package:paperless_mobile/routes/typed/branches/documents_route.dart';
|
import 'package:paperless_mobile/routes/typed/branches/documents_route.dart';
|
||||||
@@ -59,7 +59,7 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
context.read<PendingTasksNotifier>().addListener(_onTasksChanged);
|
// context.read<PendingTasksNotifier>().addListener(_onTasksChanged);
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
_nestedScrollViewKey.currentState!.innerController
|
_nestedScrollViewKey.currentState!.innerController
|
||||||
.addListener(_scrollExtentChangedListener);
|
.addListener(_scrollExtentChangedListener);
|
||||||
@@ -126,8 +126,7 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
|||||||
void dispose() {
|
void dispose() {
|
||||||
_nestedScrollViewKey.currentState?.innerController
|
_nestedScrollViewKey.currentState?.innerController
|
||||||
.removeListener(_scrollExtentChangedListener);
|
.removeListener(_scrollExtentChangedListener);
|
||||||
context.read<PendingTasksNotifier>().removeListener(_onTasksChanged);
|
// context.read<PendingTasksNotifier>().removeListener(_onTasksChanged);
|
||||||
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||||
import 'package:hive_flutter/adapters.dart';
|
import 'package:hive_flutter/adapters.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
@@ -16,9 +17,14 @@ 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/home/view/model/api_version.dart';
|
||||||
import 'package:paperless_mobile/features/inbox/cubit/inbox_cubit.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/labels/cubit/label_cubit.dart';
|
||||||
|
import 'package:paperless_mobile/features/login/cubit/authentication_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_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/settings/view/widgets/global_settings_builder.dart';
|
||||||
import 'package:paperless_mobile/features/tasks/cubit/task_status_cubit.dart';
|
import 'package:paperless_mobile/features/tasks/model/pending_tasks_notifier.dart';
|
||||||
|
import 'package:paperless_mobile/routes/typed/branches/landing_route.dart';
|
||||||
|
import 'package:paperless_mobile/routes/typed/top_level/login_route.dart';
|
||||||
|
import 'package:paperless_mobile/routes/typed/top_level/switching_accounts_route.dart';
|
||||||
|
import 'package:paperless_mobile/routes/typed/top_level/verify_identity_route.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class HomeShellWidget extends StatelessWidget {
|
class HomeShellWidget extends StatelessWidget {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import 'package:paperless_mobile/core/service/connectivity_status_service.dart';
|
|||||||
import 'package:paperless_mobile/core/service/file_service.dart';
|
import 'package:paperless_mobile/core/service/file_service.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';
|
||||||
|
import 'package:paperless_mobile/features/login/model/reachability_status.dart';
|
||||||
import 'package:paperless_mobile/features/login/services/authentication_service.dart';
|
import 'package:paperless_mobile/features/login/services/authentication_service.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||||
|
|
||||||
@@ -44,34 +45,35 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
|
|||||||
ClientCertificate? clientCertificate,
|
ClientCertificate? clientCertificate,
|
||||||
}) async {
|
}) async {
|
||||||
assert(credentials.username != null && credentials.password != null);
|
assert(credentials.username != null && credentials.password != null);
|
||||||
|
emit(const CheckingLoginState());
|
||||||
final localUserId = "${credentials.username}@$serverUrl";
|
final localUserId = "${credentials.username}@$serverUrl";
|
||||||
_debugPrintMessage(
|
_debugPrintMessage(
|
||||||
"login",
|
"login",
|
||||||
"Trying to login $localUserId...",
|
"Trying to login $localUserId...",
|
||||||
);
|
);
|
||||||
await _addUser(
|
try {
|
||||||
localUserId,
|
await _addUser(
|
||||||
serverUrl,
|
localUserId,
|
||||||
credentials,
|
serverUrl,
|
||||||
clientCertificate,
|
credentials,
|
||||||
_sessionManager,
|
clientCertificate,
|
||||||
);
|
_sessionManager,
|
||||||
|
);
|
||||||
|
|
||||||
// Mark logged in user as currently active user.
|
// Mark logged in user as currently active user.
|
||||||
final globalSettings =
|
final globalSettings =
|
||||||
Hive.box<GlobalSettings>(HiveBoxes.globalSettings).getValue()!;
|
Hive.box<GlobalSettings>(HiveBoxes.globalSettings).getValue()!;
|
||||||
globalSettings.loggedInUserId = localUserId;
|
globalSettings.loggedInUserId = localUserId;
|
||||||
await globalSettings.save();
|
await globalSettings.save();
|
||||||
|
|
||||||
emit(
|
emit(AuthenticatedState(localUserId: localUserId));
|
||||||
AuthenticatedState(
|
_debugPrintMessage(
|
||||||
localUserId: localUserId,
|
"login",
|
||||||
),
|
"User successfully logged in.",
|
||||||
);
|
);
|
||||||
_debugPrintMessage(
|
} catch (error) {
|
||||||
"login",
|
emit(const UnauthenticatedState());
|
||||||
"User successfully logged in.",
|
}
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Switches to another account if it exists.
|
/// Switches to another account if it exists.
|
||||||
@@ -156,10 +158,8 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> removeAccount(String userId) async {
|
Future<void> removeAccount(String userId) async {
|
||||||
final userAccountBox =
|
final userAccountBox = Hive.localUserAccountBox;
|
||||||
Hive.box<LocalUserAccount>(HiveBoxes.localUserAccount);
|
final userAppStateBox = Hive.localUserAppStateBox;
|
||||||
final userAppStateBox =
|
|
||||||
Hive.box<LocalUserAppState>(HiveBoxes.localUserAppState);
|
|
||||||
await FileService.clearUserData(userId: userId);
|
await FileService.clearUserData(userId: userId);
|
||||||
await userAccountBox.delete(userId);
|
await userAccountBox.delete(userId);
|
||||||
await userAppStateBox.delete(userId);
|
await userAppStateBox.delete(userId);
|
||||||
@@ -263,9 +263,13 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
|
|||||||
"restoreSessionState",
|
"restoreSessionState",
|
||||||
"Current session state successfully updated.",
|
"Current session state successfully updated.",
|
||||||
);
|
);
|
||||||
final hasInternetConnection =
|
final isPaperlessServerReachable =
|
||||||
await _connectivityService.isConnectedToInternet();
|
await _connectivityService.isPaperlessServerReachable(
|
||||||
if (hasInternetConnection) {
|
localUserAccount.serverUrl,
|
||||||
|
authentication.clientCertificate,
|
||||||
|
) ==
|
||||||
|
ReachabilityStatus.reachable;
|
||||||
|
if (isPaperlessServerReachable) {
|
||||||
_debugPrintMessage(
|
_debugPrintMessage(
|
||||||
"restoreSessionMState",
|
"restoreSessionMState",
|
||||||
"Updating server user...",
|
"Updating server user...",
|
||||||
@@ -283,7 +287,7 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
|
|||||||
} else {
|
} else {
|
||||||
_debugPrintMessage(
|
_debugPrintMessage(
|
||||||
"restoreSessionMState",
|
"restoreSessionMState",
|
||||||
"Skipping update of server user (no internet connection).",
|
"Skipping update of server user (server could not be reached).",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,14 +299,18 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> logout() async {
|
Future<void> logout([bool removeAccount = false]) async {
|
||||||
|
emit(const LogginOutState());
|
||||||
_debugPrintMessage(
|
_debugPrintMessage(
|
||||||
"logout",
|
"logout",
|
||||||
"Trying to log out current user...",
|
"Trying to log out current user...",
|
||||||
);
|
);
|
||||||
await _resetExternalState();
|
await _resetExternalState();
|
||||||
final globalSettings =
|
final globalSettings = Hive.globalSettingsBox.getValue()!;
|
||||||
Hive.box<GlobalSettings>(HiveBoxes.globalSettings).getValue()!;
|
final userId = globalSettings.loggedInUserId!;
|
||||||
|
if (removeAccount) {
|
||||||
|
this.removeAccount(userId);
|
||||||
|
}
|
||||||
globalSettings.loggedInUserId = null;
|
globalSettings.loggedInUserId = null;
|
||||||
await globalSettings.save();
|
await globalSettings.save();
|
||||||
|
|
||||||
@@ -459,19 +467,32 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
|
|||||||
return serverUser.id;
|
return serverUser.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<int> _getApiVersion(Dio dio) async {
|
Future<int> _getApiVersion(
|
||||||
|
Dio dio, {
|
||||||
|
Duration? timeout,
|
||||||
|
int defaultValue = 2,
|
||||||
|
}) async {
|
||||||
_debugPrintMessage(
|
_debugPrintMessage(
|
||||||
"_getApiVersion",
|
"_getApiVersion",
|
||||||
"Trying to fetch API version...",
|
"Trying to fetch API version...",
|
||||||
);
|
);
|
||||||
final response = await dio.get("/api/");
|
try {
|
||||||
final apiVersion =
|
final response = await dio.get(
|
||||||
int.parse(response.headers.value('x-api-version') ?? "3");
|
"/api/",
|
||||||
_debugPrintMessage(
|
options: Options(
|
||||||
"_getApiVersion",
|
sendTimeout: timeout,
|
||||||
"API version ($apiVersion) successfully retrieved.",
|
),
|
||||||
);
|
);
|
||||||
return apiVersion;
|
final apiVersion =
|
||||||
|
int.parse(response.headers.value('x-api-version') ?? "3");
|
||||||
|
_debugPrintMessage(
|
||||||
|
"_getApiVersion",
|
||||||
|
"API version ($apiVersion) successfully retrieved.",
|
||||||
|
);
|
||||||
|
return apiVersion;
|
||||||
|
} on DioException catch (e) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetches possibly updated (permissions, name, updated server version and thus new user model, ...) remote user data.
|
/// Fetches possibly updated (permissions, name, updated server version and thus new user model, ...) remote user data.
|
||||||
|
|||||||
@@ -15,6 +15,14 @@ class RequiresLocalAuthenticationState extends AuthenticationState {
|
|||||||
const RequiresLocalAuthenticationState();
|
const RequiresLocalAuthenticationState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CheckingLoginState extends AuthenticationState {
|
||||||
|
const CheckingLoginState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class LogginOutState extends AuthenticationState {
|
||||||
|
const LogginOutState();
|
||||||
|
}
|
||||||
|
|
||||||
class AuthenticatedState extends AuthenticationState {
|
class AuthenticatedState extends AuthenticationState {
|
||||||
final String localUserId;
|
final String localUserId;
|
||||||
|
|
||||||
|
|||||||
@@ -57,72 +57,76 @@ class _AddAccountPageState extends State<AddAccountPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final localAccounts =
|
return ValueListenableBuilder(
|
||||||
Hive.box<LocalUserAccount>(HiveBoxes.localUserAccount);
|
valueListenable:
|
||||||
return Scaffold(
|
Hive.box<LocalUserAccount>(HiveBoxes.localUserAccount).listenable(),
|
||||||
resizeToAvoidBottomInset: false,
|
builder: (context, localAccounts, child) {
|
||||||
body: FormBuilder(
|
return Scaffold(
|
||||||
key: _formKey,
|
resizeToAvoidBottomInset: false,
|
||||||
child: PageView(
|
body: FormBuilder(
|
||||||
controller: _pageController,
|
key: _formKey,
|
||||||
scrollBehavior: NeverScrollableScrollBehavior(),
|
child: PageView(
|
||||||
children: [
|
controller: _pageController,
|
||||||
if (widget.showLocalAccounts && localAccounts.isNotEmpty)
|
scrollBehavior: NeverScrollableScrollBehavior(),
|
||||||
Scaffold(
|
children: [
|
||||||
appBar: AppBar(
|
if (widget.showLocalAccounts && localAccounts.isNotEmpty)
|
||||||
title: Text(S.of(context)!.logInToExistingAccount),
|
Scaffold(
|
||||||
),
|
appBar: AppBar(
|
||||||
bottomNavigationBar: BottomAppBar(
|
title: Text(S.of(context)!.logInToExistingAccount),
|
||||||
child: Row(
|
),
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
bottomNavigationBar: BottomAppBar(
|
||||||
children: [
|
child: Row(
|
||||||
FilledButton(
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
child: Text(S.of(context)!.goToLogin),
|
children: [
|
||||||
onPressed: () {
|
FilledButton(
|
||||||
_pageController.nextPage(
|
child: Text(S.of(context)!.goToLogin),
|
||||||
duration: const Duration(milliseconds: 300),
|
onPressed: () {
|
||||||
curve: Curves.easeInOut,
|
_pageController.nextPage(
|
||||||
);
|
duration: const Duration(milliseconds: 300),
|
||||||
},
|
curve: Curves.easeInOut,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
|
body: ListView.builder(
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final account = localAccounts.values.elementAt(index);
|
||||||
|
return Card(
|
||||||
|
child: UserAccountListTile(
|
||||||
|
account: account,
|
||||||
|
onTap: () {
|
||||||
|
context
|
||||||
|
.read<AuthenticationCubit>()
|
||||||
|
.switchAccount(account.id);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
itemCount: localAccounts.length,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
ServerConnectionPage(
|
||||||
body: ListView.builder(
|
titleText: widget.titleString,
|
||||||
itemBuilder: (context, index) {
|
formBuilderKey: _formKey,
|
||||||
final account = localAccounts.values.elementAt(index);
|
onContinue: () {
|
||||||
return Card(
|
_pageController.nextPage(
|
||||||
child: UserAccountListTile(
|
duration: const Duration(milliseconds: 300),
|
||||||
account: account,
|
curve: Curves.easeInOut,
|
||||||
onTap: () {
|
|
||||||
context
|
|
||||||
.read<AuthenticationCubit>()
|
|
||||||
.switchAccount(account.id);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
itemCount: localAccounts.length,
|
|
||||||
),
|
),
|
||||||
),
|
ServerLoginPage(
|
||||||
ServerConnectionPage(
|
formBuilderKey: _formKey,
|
||||||
titleText: widget.titleString,
|
submitText: widget.submitText,
|
||||||
formBuilderKey: _formKey,
|
onSubmit: _login,
|
||||||
onContinue: () {
|
),
|
||||||
_pageController.nextPage(
|
],
|
||||||
duration: const Duration(milliseconds: 300),
|
|
||||||
curve: Curves.easeInOut,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
ServerLoginPage(
|
),
|
||||||
formBuilderKey: _formKey,
|
);
|
||||||
submitText: widget.submitText,
|
},
|
||||||
onSubmit: _login,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import 'package:hive_flutter/adapters.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/global_settings.dart';
|
import 'package:paperless_mobile/core/database/tables/global_settings.dart';
|
||||||
|
import 'package:paperless_mobile/core/model/info_message_exception.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/login/cubit/authentication_cubit.dart';
|
import 'package:paperless_mobile/features/login/cubit/authentication_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/login/model/client_certificate.dart';
|
import 'package:paperless_mobile/features/login/model/client_certificate.dart';
|
||||||
@@ -71,6 +72,8 @@ class LoginPage extends StatelessWidget {
|
|||||||
stackTrace,
|
stackTrace,
|
||||||
); //TODO: Check if we can show error message directly on field here.
|
); //TODO: Check if we can show error message directly on field here.
|
||||||
}
|
}
|
||||||
|
} on InfoMessageException catch (error) {
|
||||||
|
showInfoMessage(context, error);
|
||||||
} catch (unknownError, stackTrace) {
|
} catch (unknownError, stackTrace) {
|
||||||
showGenericError(context, unknownError.toString(), stackTrace);
|
showGenericError(context, unknownError.toString(), stackTrace);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ class LocalNotificationService {
|
|||||||
|
|
||||||
LocalNotificationService();
|
LocalNotificationService();
|
||||||
|
|
||||||
|
final Map<String, List<int>> _pendingNotifications = {};
|
||||||
|
|
||||||
Future<void> initialize() async {
|
Future<void> initialize() async {
|
||||||
const AndroidInitializationSettings initializationSettingsAndroid =
|
const AndroidInitializationSettings initializationSettingsAndroid =
|
||||||
AndroidInitializationSettings('paperless_logo_green');
|
AndroidInitializationSettings('paperless_logo_green');
|
||||||
@@ -51,6 +53,7 @@ class LocalNotificationService {
|
|||||||
required String filePath,
|
required String filePath,
|
||||||
required bool finished,
|
required bool finished,
|
||||||
required String locale,
|
required String locale,
|
||||||
|
required String userId,
|
||||||
}) async {
|
}) async {
|
||||||
final tr = await S.delegate.load(Locale(locale));
|
final tr = await S.delegate.load(Locale(locale));
|
||||||
|
|
||||||
@@ -88,6 +91,15 @@ class LocalNotificationService {
|
|||||||
).toJson(),
|
).toJson(),
|
||||||
),
|
),
|
||||||
); //TODO: INTL
|
); //TODO: INTL
|
||||||
|
_addNotification(userId, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _addNotification(String userId, int notificationId) {
|
||||||
|
_pendingNotifications.update(
|
||||||
|
userId,
|
||||||
|
(notifications) => [...notifications, notificationId],
|
||||||
|
ifAbsent: () => [notificationId],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> notifyFileSaved({
|
Future<void> notifyFileSaved({
|
||||||
@@ -119,24 +131,20 @@ class LocalNotificationService {
|
|||||||
),
|
),
|
||||||
iOS: DarwinNotificationDetails(
|
iOS: DarwinNotificationDetails(
|
||||||
attachments: [
|
attachments: [
|
||||||
DarwinNotificationAttachment(
|
DarwinNotificationAttachment(filePath),
|
||||||
filePath,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
payload: jsonEncode(
|
payload: jsonEncode(
|
||||||
OpenDownloadedDocumentPayload(
|
OpenDownloadedDocumentPayload(filePath: filePath).toJson(),
|
||||||
filePath: filePath,
|
|
||||||
).toJson(),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: INTL
|
//TODO: INTL
|
||||||
Future<void> notifyTaskChanged(Task task) {
|
Future<void> notifyTaskChanged(Task task, {required String userId}) async {
|
||||||
log("[LocalNotificationService] notifyTaskChanged: ${task.toString()}");
|
log("[LocalNotificationService] notifyTaskChanged: ${task.toString()}");
|
||||||
int id = task.id;
|
int id = task.id + 1000;
|
||||||
final status = task.status;
|
final status = task.status;
|
||||||
late String title;
|
late String title;
|
||||||
late String? body;
|
late String? body;
|
||||||
@@ -171,7 +179,7 @@ class LocalNotificationService {
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return _plugin.show(
|
await _plugin.show(
|
||||||
id,
|
id,
|
||||||
title,
|
title,
|
||||||
body,
|
body,
|
||||||
@@ -204,6 +212,13 @@ class LocalNotificationService {
|
|||||||
),
|
),
|
||||||
payload: jsonEncode(payload),
|
payload: jsonEncode(payload),
|
||||||
);
|
);
|
||||||
|
_addNotification(userId, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> cancelUserNotifications(String userId) async {
|
||||||
|
await Future.wait([
|
||||||
|
for (var id in _pendingNotifications[userId] ?? []) _plugin.cancel(id),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onDidReceiveLocalNotification(
|
void onDidReceiveLocalNotification(
|
||||||
@@ -272,6 +287,7 @@ class LocalNotificationService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@protected
|
||||||
void onDidReceiveBackgroundNotificationResponse(NotificationResponse response) {
|
void onDidReceiveBackgroundNotificationResponse(NotificationResponse response) {
|
||||||
//TODO: When periodic background inbox check is implemented, notification tap is handled here
|
//TODO: When periodic background inbox check is implemented, notification tap is handled here
|
||||||
debugPrint(response.toString());
|
debugPrint(response.toString());
|
||||||
|
|||||||
@@ -70,12 +70,9 @@ class ManageAccountsPage extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
onSelected: (value) async {
|
onSelected: (value) async {
|
||||||
if (value == 0) {
|
if (value == 0) {
|
||||||
final currentUser = globalSettings.loggedInUserId!;
|
|
||||||
await context.read<AuthenticationCubit>().logout();
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
await context
|
await context
|
||||||
.read<AuthenticationCubit>()
|
.read<AuthenticationCubit>()
|
||||||
.removeAccount(currentUser);
|
.logout(true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,20 +1,24 @@
|
|||||||
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:bloc/bloc.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:paperless_mobile/core/service/file_service.dart';
|
import 'package:paperless_mobile/core/service/file_service.dart';
|
||||||
import 'package:path/path.dart' as p;
|
import 'package:path/path.dart' as p;
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
|
|
||||||
part 'receive_share_state.dart';
|
part 'receive_share_state.dart';
|
||||||
|
|
||||||
class ConsumptionChangeNotifier extends ChangeNotifier {
|
class ConsumptionChangeNotifier extends ChangeNotifier {
|
||||||
List<File> pendingFiles = [];
|
List<File> pendingFiles = [];
|
||||||
|
|
||||||
ConsumptionChangeNotifier();
|
final Completer _restored = Completer();
|
||||||
|
|
||||||
|
Future<void> get isInitialized => _restored.future;
|
||||||
|
|
||||||
Future<void> loadFromConsumptionDirectory({required String userId}) async {
|
Future<void> loadFromConsumptionDirectory({required String userId}) async {
|
||||||
pendingFiles = await _getCurrentFiles(userId);
|
pendingFiles = await _getCurrentFiles(userId);
|
||||||
|
if (!_restored.isCompleted) {
|
||||||
|
_restored.complete();
|
||||||
|
}
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:intl/intl.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/extensions/flutter_extensions.dart';
|
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||||
import 'package:paperless_mobile/features/sharing/cubit/receive_share_cubit.dart';
|
import 'package:paperless_mobile/features/sharing/cubit/receive_share_cubit.dart';
|
||||||
@@ -20,13 +16,13 @@ class ConsumptionQueueView extends StatelessWidget {
|
|||||||
final currentUser = context.watch<LocalUserAccount>();
|
final currentUser = context.watch<LocalUserAccount>();
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text("Upload Queue"), //TODO: INTL
|
title: Text("Pending Files"), //TODO: INTL
|
||||||
),
|
),
|
||||||
body: Consumer<ConsumptionChangeNotifier>(
|
body: Consumer<ConsumptionChangeNotifier>(
|
||||||
builder: (context, value, child) {
|
builder: (context, value, child) {
|
||||||
if (value.pendingFiles.isEmpty) {
|
if (value.pendingFiles.isEmpty) {
|
||||||
return Center(
|
return Center(
|
||||||
child: Text("No pending files."),
|
child: Text("There are no pending files."), //TODO: INTL
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return ListView.builder(
|
return ListView.builder(
|
||||||
@@ -34,7 +30,37 @@ class ConsumptionQueueView extends StatelessWidget {
|
|||||||
final file = value.pendingFiles.elementAt(index);
|
final file = value.pendingFiles.elementAt(index);
|
||||||
final filename = p.basename(file.path);
|
final filename = p.basename(file.path);
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(filename),
|
title: Text(
|
||||||
|
filename,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
maxLines: 1,
|
||||||
|
),
|
||||||
|
subtitle: Row(
|
||||||
|
children: [
|
||||||
|
ActionChip(
|
||||||
|
label: Text(S.of(context)!.upload),
|
||||||
|
avatar: const Icon(Icons.file_upload_outlined),
|
||||||
|
onPressed: () {
|
||||||
|
consumeLocalFile(
|
||||||
|
context,
|
||||||
|
file: file,
|
||||||
|
userId: currentUser.id,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
ActionChip(
|
||||||
|
label: Text(S.of(context)!.discard),
|
||||||
|
avatar: const Icon(Icons.delete),
|
||||||
|
onPressed: () {
|
||||||
|
context.read<ConsumptionChangeNotifier>().discardFile(
|
||||||
|
file,
|
||||||
|
userId: currentUser.id,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
leading: Padding(
|
leading: Padding(
|
||||||
padding: const EdgeInsets.all(4),
|
padding: const EdgeInsets.all(4),
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
@@ -46,60 +72,7 @@ class ConsumptionQueueView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
trailing: IconButton(
|
|
||||||
icon: Icon(Icons.delete),
|
|
||||||
onPressed: () {
|
|
||||||
context
|
|
||||||
.read<ConsumptionChangeNotifier>()
|
|
||||||
.discardFile(file, userId: currentUser.id);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
return Row(
|
|
||||||
mainAxisSize: MainAxisSize.max,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Text(filename, maxLines: 1),
|
|
||||||
SizedBox(
|
|
||||||
height: 56,
|
|
||||||
child: ListView(
|
|
||||||
scrollDirection: Axis.horizontal,
|
|
||||||
children: [
|
|
||||||
ActionChip(
|
|
||||||
label: Text(S.of(context)!.upload),
|
|
||||||
avatar: Icon(Icons.file_upload_outlined),
|
|
||||||
onPressed: () {
|
|
||||||
consumeLocalFile(
|
|
||||||
context,
|
|
||||||
file: file,
|
|
||||||
userId: currentUser.id,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
SizedBox(width: 8),
|
|
||||||
ActionChip(
|
|
||||||
label: Text(S.of(context)!.discard),
|
|
||||||
avatar: Icon(Icons.delete),
|
|
||||||
onPressed: () {
|
|
||||||
context
|
|
||||||
.read<ConsumptionChangeNotifier>()
|
|
||||||
.discardFile(
|
|
||||||
file,
|
|
||||||
userId: currentUser.id,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
).padded(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
).padded();
|
|
||||||
},
|
},
|
||||||
itemCount: value.pendingFiles.length,
|
itemCount: value.pendingFiles.length,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import 'package:paperless_mobile/core/widgets/dialog_utils/dialog_cancel_button.
|
|||||||
import 'package:paperless_mobile/core/widgets/dialog_utils/dialog_confirm_button.dart';
|
import 'package:paperless_mobile/core/widgets/dialog_utils/dialog_confirm_button.dart';
|
||||||
import 'package:paperless_mobile/core/widgets/future_or_builder.dart';
|
import 'package:paperless_mobile/core/widgets/future_or_builder.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||||
import 'package:transparent_image/transparent_image.dart';
|
import 'package:paperless_mobile/features/sharing/view/widgets/file_thumbnail.dart';
|
||||||
|
|
||||||
class DiscardSharedFileDialog extends StatelessWidget {
|
class DiscardSharedFileDialog extends StatelessWidget {
|
||||||
final FutureOr<Uint8List> bytes;
|
final FutureOr<Uint8List> bytes;
|
||||||
@@ -24,13 +24,13 @@ class DiscardSharedFileDialog extends StatelessWidget {
|
|||||||
if (!snapshot.hasData) {
|
if (!snapshot.hasData) {
|
||||||
return const CircularProgressIndicator();
|
return const CircularProgressIndicator();
|
||||||
}
|
}
|
||||||
return LimitedBox(
|
return ClipRRect(
|
||||||
maxHeight: 200,
|
borderRadius: BorderRadius.circular(12),
|
||||||
maxWidth: 200,
|
child: FileThumbnail(
|
||||||
child: FadeInImage(
|
bytes: snapshot.data!,
|
||||||
fit: BoxFit.contain,
|
width: 150,
|
||||||
placeholder: MemoryImage(kTransparentImage),
|
height: 100,
|
||||||
image: MemoryImage(snapshot.data!),
|
fit: BoxFit.cover,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:paperless_mobile/core/widgets/dialog_utils/dialog_cancel_button.dart';
|
||||||
|
import 'package:paperless_mobile/core/widgets/dialog_utils/dialog_confirm_button.dart';
|
||||||
|
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||||
|
|
||||||
|
class PendingFilesInfoDialog extends StatelessWidget {
|
||||||
|
final List<File> pendingFiles;
|
||||||
|
const PendingFilesInfoDialog({super.key, required this.pendingFiles});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final fileCount = pendingFiles.length;
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text("Pending Files"),
|
||||||
|
content: Text(
|
||||||
|
"$fileCount files are waiting to be uploaded. Do you want to upload them now?",
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
DialogCancelButton(),
|
||||||
|
DialogConfirmButton(
|
||||||
|
label: S.of(context)!.upload,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,13 +28,15 @@ class FileThumbnail extends StatefulWidget {
|
|||||||
|
|
||||||
class _FileThumbnailState extends State<FileThumbnail> {
|
class _FileThumbnailState extends State<FileThumbnail> {
|
||||||
late String? mimeType;
|
late String? mimeType;
|
||||||
|
late final Future<Uint8List?> _fileBytes;
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
mimeType = widget.file != null
|
mimeType = widget.file != null
|
||||||
? mime.lookupMimeType(widget.file!.path)
|
? mime.lookupMimeType(widget.file!.path)
|
||||||
: mime.lookupMimeType('', headerBytes: widget.bytes);
|
: mime.lookupMimeType('', headerBytes: widget.bytes);
|
||||||
|
_fileBytes = widget.file?.readAsBytes().then(_convertPdfToPng) ??
|
||||||
|
_convertPdfToPng(widget.bytes!);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -45,8 +47,7 @@ class _FileThumbnailState extends State<FileThumbnail> {
|
|||||||
height: widget.height,
|
height: widget.height,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: FutureBuilder<Uint8List?>(
|
child: FutureBuilder<Uint8List?>(
|
||||||
future: widget.file?.readAsBytes().then(_convertPdfToPng) ??
|
future: _fileBytes,
|
||||||
_convertPdfToPng(widget.bytes!),
|
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (!snapshot.hasData) {
|
if (!snapshot.hasData) {
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
|
|||||||
@@ -10,15 +10,16 @@ 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/config/hive/hive_extensions.dart';
|
import 'package:paperless_mobile/core/config/hive/hive_extensions.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/service/connectivity_status_service.dart';
|
||||||
import 'package:paperless_mobile/features/document_upload/view/document_upload_preparation_page.dart';
|
import 'package:paperless_mobile/features/document_upload/view/document_upload_preparation_page.dart';
|
||||||
import 'package:paperless_mobile/features/notifications/services/local_notification_service.dart';
|
import 'package:paperless_mobile/features/notifications/services/local_notification_service.dart';
|
||||||
import 'package:paperless_mobile/features/sharing/cubit/receive_share_cubit.dart';
|
import 'package:paperless_mobile/features/sharing/cubit/receive_share_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/sharing/view/dialog/discard_shared_file_dialog.dart';
|
import 'package:paperless_mobile/features/sharing/view/dialog/discard_shared_file_dialog.dart';
|
||||||
import 'package:paperless_mobile/features/tasks/cubit/task_status_cubit.dart';
|
import 'package:paperless_mobile/features/sharing/view/dialog/pending_files_info_dialog.dart';
|
||||||
|
import 'package:paperless_mobile/features/tasks/model/pending_tasks_notifier.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||||
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||||
import 'package:paperless_mobile/routes/typed/branches/scanner_route.dart';
|
import 'package:paperless_mobile/routes/typed/branches/scanner_route.dart';
|
||||||
import 'package:paperless_mobile/routes/typed/branches/upload_queue_route.dart';
|
|
||||||
import 'package:path/path.dart' as p;
|
import 'package:path/path.dart' as p;
|
||||||
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
|
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
|
||||||
|
|
||||||
@@ -39,57 +40,40 @@ class _UploadQueueShellState extends State<UploadQueueShell> {
|
|||||||
ReceiveSharingIntent.getInitialMedia().then(_onReceiveSharedFiles);
|
ReceiveSharingIntent.getInitialMedia().then(_onReceiveSharedFiles);
|
||||||
_subscription =
|
_subscription =
|
||||||
ReceiveSharingIntent.getMediaStream().listen(_onReceiveSharedFiles);
|
ReceiveSharingIntent.getMediaStream().listen(_onReceiveSharedFiles);
|
||||||
|
context.read<PendingTasksNotifier>().addListener(_onTasksChanged);
|
||||||
|
|
||||||
// WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
// WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
// context.read<ReceiveShareCubit>().loadFromConsumptionDirectory(
|
// final notifier = context.read<ConsumptionChangeNotifier>();
|
||||||
// userId: context.read<LocalUserAccount>().id,
|
// await notifier.isInitialized;
|
||||||
// );
|
// final pendingFiles = notifier.pendingFiles;
|
||||||
// final state = context.read<ReceiveShareCubit>().state;
|
// if (pendingFiles.isEmpty) {
|
||||||
// print("Current state is " + state.toString());
|
// return;
|
||||||
// final files = state.files;
|
// }
|
||||||
// if (files.isNotEmpty) {
|
|
||||||
// showSnackBar(
|
// final shouldProcess = await showDialog<bool>(
|
||||||
|
// context: context,
|
||||||
|
// builder: (context) =>
|
||||||
|
// PendingFilesInfoDialog(pendingFiles: pendingFiles),
|
||||||
|
// ) ??
|
||||||
|
// false;
|
||||||
|
// if (shouldProcess) {
|
||||||
|
// final userId = context.read<LocalUserAccount>().id;
|
||||||
|
// await consumeLocalFiles(
|
||||||
// context,
|
// context,
|
||||||
// "You have ${files.length} shared files waiting to be uploaded.",
|
// files: pendingFiles,
|
||||||
// action: SnackBarActionConfig(
|
// userId: userId,
|
||||||
// label: "Show me",
|
|
||||||
// onPressed: () {
|
|
||||||
// UploadQueueRoute().push(context);
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
// );
|
// );
|
||||||
// // showDialog(
|
|
||||||
// // context: context,
|
|
||||||
// // builder: (context) => AlertDialog(
|
|
||||||
// // title: Text("Pending files"),
|
|
||||||
// // content: Text(
|
|
||||||
// // "You have ${files.length} files waiting to be uploaded.",
|
|
||||||
// // ),
|
|
||||||
// // actions: [
|
|
||||||
// // TextButton(
|
|
||||||
// // child: Text(S.of(context)!.gotIt),
|
|
||||||
// // onPressed: () {
|
|
||||||
// // Navigator.pop(context);
|
|
||||||
// // UploadQueueRoute().push(context);
|
|
||||||
// // },
|
|
||||||
// // ),
|
|
||||||
// // ],
|
|
||||||
// // ),
|
|
||||||
// // );
|
|
||||||
// }
|
// }
|
||||||
// });
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void didChangeDependencies() {
|
|
||||||
super.didChangeDependencies();
|
|
||||||
context.read<PendingTasksNotifier>().addListener(_onTasksChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onTasksChanged() {
|
void _onTasksChanged() {
|
||||||
final taskNotifier = context.read<PendingTasksNotifier>();
|
final taskNotifier = context.read<PendingTasksNotifier>();
|
||||||
|
final userId = context.read<LocalUserAccount>().id;
|
||||||
for (var task in taskNotifier.value.values) {
|
for (var task in taskNotifier.value.values) {
|
||||||
context.read<LocalNotificationService>().notifyTaskChanged(task);
|
context
|
||||||
|
.read<LocalNotificationService>()
|
||||||
|
.notifyTaskChanged(task, userId: userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,23 +87,18 @@ class _UploadQueueShellState extends State<UploadQueueShell> {
|
|||||||
files: files,
|
files: files,
|
||||||
userId: userId,
|
userId: userId,
|
||||||
);
|
);
|
||||||
final localFiles = notifier.pendingFiles;
|
consumeLocalFiles(
|
||||||
for (int i = 0; i < localFiles.length; i++) {
|
context,
|
||||||
final file = localFiles[i];
|
files: files,
|
||||||
await consumeLocalFile(
|
userId: userId,
|
||||||
context,
|
exitAppAfterConsumed: true,
|
||||||
file: file,
|
);
|
||||||
userId: userId,
|
|
||||||
exitAppAfterConsumed: i == localFiles.length - 1,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_subscription?.cancel();
|
_subscription?.cancel();
|
||||||
context.read<PendingTasksNotifier>().removeListener(_onTasksChanged);
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,28 +114,43 @@ Future<void> consumeLocalFile(
|
|||||||
required String userId,
|
required String userId,
|
||||||
bool exitAppAfterConsumed = false,
|
bool exitAppAfterConsumed = false,
|
||||||
}) async {
|
}) async {
|
||||||
|
final filename = p.basename(file.path);
|
||||||
|
final hasInternetConnection =
|
||||||
|
await context.read<ConnectivityStatusService>().isConnectedToInternet();
|
||||||
|
if (!hasInternetConnection) {
|
||||||
|
showSnackBar(
|
||||||
|
context,
|
||||||
|
"Could not consume $filename", //TODO: INTL
|
||||||
|
details: S.of(context)!.youreOffline,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
final consumptionNotifier = context.read<ConsumptionChangeNotifier>();
|
final consumptionNotifier = context.read<ConsumptionChangeNotifier>();
|
||||||
final taskNotifier = context.read<PendingTasksNotifier>();
|
final taskNotifier = context.read<PendingTasksNotifier>();
|
||||||
final ioFile = File(file.path);
|
|
||||||
// if (!await ioFile.exists()) {
|
|
||||||
// Fluttertoast.showToast(
|
|
||||||
// msg: S.of(context)!.couldNotAccessReceivedFile,
|
|
||||||
// toastLength: Toast.LENGTH_LONG,
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
final bytes = ioFile.readAsBytes();
|
final bytes = file.readAsBytes();
|
||||||
final shouldDirectlyUpload =
|
final shouldDirectlyUpload =
|
||||||
Hive.globalSettingsBox.getValue()!.skipDocumentPreprarationOnUpload;
|
Hive.globalSettingsBox.getValue()!.skipDocumentPreprarationOnUpload;
|
||||||
if (shouldDirectlyUpload) {
|
if (shouldDirectlyUpload) {
|
||||||
final taskId = await context.read<PaperlessDocumentsApi>().create(
|
try {
|
||||||
await bytes,
|
final taskId = await context.read<PaperlessDocumentsApi>().create(
|
||||||
filename: p.basename(file.path),
|
await bytes,
|
||||||
title: p.basenameWithoutExtension(file.path),
|
filename: filename,
|
||||||
);
|
title: p.basenameWithoutExtension(file.path),
|
||||||
consumptionNotifier.discardFile(file, userId: userId);
|
);
|
||||||
if (taskId != null) {
|
consumptionNotifier.discardFile(file, userId: userId);
|
||||||
taskNotifier.listenToTaskChanges(taskId);
|
if (taskId != null) {
|
||||||
|
taskNotifier.listenToTaskChanges(taskId);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
await Fluttertoast.showToast(
|
||||||
|
msg: S.of(context)!.couldNotUploadDocument,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
} finally {
|
||||||
|
if (exitAppAfterConsumed) {
|
||||||
|
SystemNavigator.pop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
final result = await DocumentUploadRoute(
|
final result = await DocumentUploadRoute(
|
||||||
@@ -193,3 +187,20 @@ Future<void> consumeLocalFile(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> consumeLocalFiles(
|
||||||
|
BuildContext context, {
|
||||||
|
required List<File> files,
|
||||||
|
required String userId,
|
||||||
|
bool exitAppAfterConsumed = false,
|
||||||
|
}) async {
|
||||||
|
for (int i = 0; i < files.length; i++) {
|
||||||
|
final file = files[i];
|
||||||
|
await consumeLocalFile(
|
||||||
|
context,
|
||||||
|
file: file,
|
||||||
|
userId: userId,
|
||||||
|
exitAppAfterConsumed: exitAppAfterConsumed && (i == files.length - 1),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
|
||||||
|
|
||||||
class PendingTasksNotifier extends ValueNotifier<Map<String, Task>> {
|
|
||||||
final PaperlessTasksApi _api;
|
|
||||||
PendingTasksNotifier(this._api) : super({});
|
|
||||||
|
|
||||||
void listenToTaskChanges(String taskId) {
|
|
||||||
_api.listenForTaskChanges(taskId).forEach((task) {
|
|
||||||
value = {...value, taskId: task};
|
|
||||||
notifyListeners();
|
|
||||||
}).whenComplete(
|
|
||||||
() {
|
|
||||||
value = value..remove(taskId);
|
|
||||||
notifyListeners();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> acknowledgeTasks(Iterable<String> taskIds) async {
|
|
||||||
final tasks = value.values.where((task) => taskIds.contains(task.taskId));
|
|
||||||
await Future.wait([for (var task in tasks) _api.acknowledgeTask(task)]);
|
|
||||||
value = value..removeWhere((key, value) => taskIds.contains(key));
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
68
lib/features/tasks/model/pending_tasks_notifier.dart
Normal file
68
lib/features/tasks/model/pending_tasks_notifier.dart
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
|
|
||||||
|
class PendingTasksNotifier extends ValueNotifier<Map<String, Task>> {
|
||||||
|
final PaperlessTasksApi _api;
|
||||||
|
|
||||||
|
final Map<String, StreamSubscription> _subscriptions = {};
|
||||||
|
|
||||||
|
PendingTasksNotifier(this._api) : super({});
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
stopListeningToTaskChanges();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void listenToTaskChanges(String taskId) {
|
||||||
|
final sub = _api.listenForTaskChanges(taskId).listen(
|
||||||
|
(task) {
|
||||||
|
if (value.containsKey(taskId)) {
|
||||||
|
final oldTask = value[taskId]!;
|
||||||
|
if (oldTask.status != task.status) {
|
||||||
|
// Only notify of changes if task status has changed...
|
||||||
|
value = {...value, taskId: task};
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
value = {...value, taskId: task};
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
sub
|
||||||
|
..onDone(() {
|
||||||
|
sub.cancel();
|
||||||
|
value = value..remove(taskId);
|
||||||
|
notifyListeners();
|
||||||
|
})
|
||||||
|
..onError((_) {
|
||||||
|
sub.cancel();
|
||||||
|
value = value..remove(taskId);
|
||||||
|
notifyListeners();
|
||||||
|
});
|
||||||
|
|
||||||
|
_subscriptions.putIfAbsent(taskId, () => sub);
|
||||||
|
}
|
||||||
|
|
||||||
|
void stopListeningToTaskChanges([String? taskId]) {
|
||||||
|
if (taskId != null) {
|
||||||
|
_subscriptions[taskId]?.cancel();
|
||||||
|
_subscriptions.remove(taskId);
|
||||||
|
} else {
|
||||||
|
_subscriptions.forEach((key, value) {
|
||||||
|
value.cancel();
|
||||||
|
_subscriptions.remove(key);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> acknowledgeTasks(Iterable<String> taskIds) async {
|
||||||
|
final tasks = value.values.where((task) => taskIds.contains(task.taskId));
|
||||||
|
await Future.wait([for (var task in tasks) _api.acknowledgeTask(task)]);
|
||||||
|
value = value..removeWhere((key, value) => taskIds.contains(key));
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,6 +28,7 @@ import 'package:paperless_mobile/core/exception/server_message_exception.dart';
|
|||||||
import 'package:paperless_mobile/core/factory/paperless_api_factory.dart';
|
import 'package:paperless_mobile/core/factory/paperless_api_factory.dart';
|
||||||
import 'package:paperless_mobile/core/factory/paperless_api_factory_impl.dart';
|
import 'package:paperless_mobile/core/factory/paperless_api_factory_impl.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/model/info_message_exception.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/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';
|
||||||
@@ -37,7 +38,9 @@ import 'package:paperless_mobile/features/notifications/services/local_notificat
|
|||||||
import 'package:paperless_mobile/features/settings/view/widgets/global_settings_builder.dart';
|
import 'package:paperless_mobile/features/settings/view/widgets/global_settings_builder.dart';
|
||||||
import 'package:paperless_mobile/features/sharing/model/share_intent_queue.dart';
|
import 'package:paperless_mobile/features/sharing/model/share_intent_queue.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||||
|
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||||
import 'package:paperless_mobile/routes/navigation_keys.dart';
|
import 'package:paperless_mobile/routes/navigation_keys.dart';
|
||||||
|
import 'package:paperless_mobile/routes/routes.dart';
|
||||||
import 'package:paperless_mobile/routes/typed/branches/documents_route.dart';
|
import 'package:paperless_mobile/routes/typed/branches/documents_route.dart';
|
||||||
import 'package:paperless_mobile/routes/typed/branches/inbox_route.dart';
|
import 'package:paperless_mobile/routes/typed/branches/inbox_route.dart';
|
||||||
import 'package:paperless_mobile/routes/typed/branches/labels_route.dart';
|
import 'package:paperless_mobile/routes/typed/branches/labels_route.dart';
|
||||||
@@ -47,6 +50,8 @@ import 'package:paperless_mobile/routes/typed/branches/scanner_route.dart';
|
|||||||
import 'package:paperless_mobile/routes/typed/branches/upload_queue_route.dart';
|
import 'package:paperless_mobile/routes/typed/branches/upload_queue_route.dart';
|
||||||
import 'package:paperless_mobile/routes/typed/shells/provider_shell_route.dart';
|
import 'package:paperless_mobile/routes/typed/shells/provider_shell_route.dart';
|
||||||
import 'package:paperless_mobile/routes/typed/shells/scaffold_shell_route.dart';
|
import 'package:paperless_mobile/routes/typed/shells/scaffold_shell_route.dart';
|
||||||
|
import 'package:paperless_mobile/routes/typed/top_level/checking_login_route.dart';
|
||||||
|
import 'package:paperless_mobile/routes/typed/top_level/logging_out_route.dart';
|
||||||
import 'package:paperless_mobile/routes/typed/top_level/login_route.dart';
|
import 'package:paperless_mobile/routes/typed/top_level/login_route.dart';
|
||||||
import 'package:paperless_mobile/routes/typed/top_level/settings_route.dart';
|
import 'package:paperless_mobile/routes/typed/top_level/settings_route.dart';
|
||||||
import 'package:paperless_mobile/routes/typed/top_level/switching_accounts_route.dart';
|
import 'package:paperless_mobile/routes/typed/top_level/switching_accounts_route.dart';
|
||||||
@@ -234,6 +239,8 @@ class _GoRouterShellState extends State<GoRouterShell> {
|
|||||||
$loginRoute,
|
$loginRoute,
|
||||||
$verifyIdentityRoute,
|
$verifyIdentityRoute,
|
||||||
$switchingAccountsRoute,
|
$switchingAccountsRoute,
|
||||||
|
$logginOutRoute,
|
||||||
|
$checkingLoginRoute,
|
||||||
ShellRoute(
|
ShellRoute(
|
||||||
navigatorKey: rootNavigatorKey,
|
navigatorKey: rootNavigatorKey,
|
||||||
builder: ProviderShellRoute(widget.apiFactory).build,
|
builder: ProviderShellRoute(widget.apiFactory).build,
|
||||||
@@ -280,19 +287,33 @@ class _GoRouterShellState extends State<GoRouterShell> {
|
|||||||
listener: (context, state) {
|
listener: (context, state) {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case UnauthenticatedState():
|
case UnauthenticatedState():
|
||||||
const LoginRoute().go(context);
|
_router.goNamed(R.login);
|
||||||
break;
|
break;
|
||||||
case RequiresLocalAuthenticationState():
|
case RequiresLocalAuthenticationState():
|
||||||
const VerifyIdentityRoute().go(context);
|
_router.goNamed(R.verifyIdentity);
|
||||||
break;
|
break;
|
||||||
case SwitchingAccountsState():
|
case SwitchingAccountsState():
|
||||||
const SwitchingAccountsRoute().go(context);
|
final userId = context.read<LocalUserAccount>().id;
|
||||||
|
context
|
||||||
|
.read<LocalNotificationService>()
|
||||||
|
.cancelUserNotifications(userId);
|
||||||
|
_router.goNamed(R.switchingAccounts);
|
||||||
break;
|
break;
|
||||||
case AuthenticatedState():
|
case AuthenticatedState():
|
||||||
const LandingRoute().go(context);
|
_router.goNamed(R.landing);
|
||||||
|
break;
|
||||||
|
case CheckingLoginState():
|
||||||
|
_router.goNamed(R.checkingLogin);
|
||||||
|
break;
|
||||||
|
case LogginOutState():
|
||||||
|
final userId = context.read<LocalUserAccount>().id;
|
||||||
|
context
|
||||||
|
.read<LocalNotificationService>()
|
||||||
|
.cancelUserNotifications(userId);
|
||||||
|
_router.goNamed(R.loggingOut);
|
||||||
break;
|
break;
|
||||||
case AuthenticationErrorState():
|
case AuthenticationErrorState():
|
||||||
const LoginRoute().go(context);
|
_router.goNamed(R.login);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -21,4 +21,6 @@ class R {
|
|||||||
static const linkedDocuments = "linkedDocuments";
|
static const linkedDocuments = "linkedDocuments";
|
||||||
static const bulkEditDocuments = "bulkEditDocuments";
|
static const bulkEditDocuments = "bulkEditDocuments";
|
||||||
static const uploadQueue = "uploadQueue";
|
static const uploadQueue = "uploadQueue";
|
||||||
|
static const checkingLogin = "checkingLogin";
|
||||||
|
static const loggingOut = "loggingOut";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,11 +61,15 @@ class ProviderShellRoute extends ShellRouteData {
|
|||||||
) {
|
) {
|
||||||
final currentUserId = Hive.box<GlobalSettings>(HiveBoxes.globalSettings)
|
final currentUserId = Hive.box<GlobalSettings>(HiveBoxes.globalSettings)
|
||||||
.getValue()!
|
.getValue()!
|
||||||
.loggedInUserId!;
|
.loggedInUserId;
|
||||||
|
if (currentUserId == null) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
final authenticatedUser =
|
final authenticatedUser =
|
||||||
Hive.box<LocalUserAccount>(HiveBoxes.localUserAccount).get(
|
Hive.box<LocalUserAccount>(HiveBoxes.localUserAccount).get(
|
||||||
currentUserId,
|
currentUserId,
|
||||||
)!;
|
)!;
|
||||||
|
|
||||||
return HomeShellWidget(
|
return HomeShellWidget(
|
||||||
localUserId: authenticatedUser.id,
|
localUserId: authenticatedUser.id,
|
||||||
paperlessApiVersion: authenticatedUser.apiVersion,
|
paperlessApiVersion: authenticatedUser.apiVersion,
|
||||||
|
|||||||
23
lib/routes/typed/top_level/checking_login_route.dart
Normal file
23
lib/routes/typed/top_level/checking_login_route.dart
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:paperless_mobile/routes/routes.dart';
|
||||||
|
|
||||||
|
part 'checking_login_route.g.dart';
|
||||||
|
|
||||||
|
@TypedGoRoute<CheckingLoginRoute>(
|
||||||
|
path: "/checking-login",
|
||||||
|
name: R.checkingLogin,
|
||||||
|
)
|
||||||
|
class CheckingLoginRoute extends GoRouteData {
|
||||||
|
const CheckingLoginRoute();
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, GoRouterState state) {
|
||||||
|
return Scaffold(
|
||||||
|
body: Center(
|
||||||
|
child: Text("Logging in..."),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
23
lib/routes/typed/top_level/logging_out_route.dart
Normal file
23
lib/routes/typed/top_level/logging_out_route.dart
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:paperless_mobile/routes/routes.dart';
|
||||||
|
|
||||||
|
part 'logging_out_route.g.dart';
|
||||||
|
|
||||||
|
@TypedGoRoute<LogginOutRoute>(
|
||||||
|
path: "/logging-out",
|
||||||
|
name: R.loggingOut,
|
||||||
|
)
|
||||||
|
class LogginOutRoute extends GoRouteData {
|
||||||
|
const LogginOutRoute();
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, GoRouterState state) {
|
||||||
|
return Scaffold(
|
||||||
|
body: Center(
|
||||||
|
child: Text("Logging out..."),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user