mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2025-12-09 10:08:00 -06:00
feat: Update translations, finish saved views rework, some other fixes
This commit is contained in:
@@ -10,7 +10,9 @@ import 'package:hive_flutter/adapters.dart';
|
||||
/// [callback] to return and returns the calculated value. Closes the box after.
|
||||
///
|
||||
Future<R?> withEncryptedBox<T, R>(
|
||||
String name, FutureOr<R?> Function(Box<T> box) callback) async {
|
||||
String name,
|
||||
FutureOr<R?> Function(Box<T> box) callback,
|
||||
) async {
|
||||
final key = await _getEncryptedBoxKey();
|
||||
final box = await Hive.openBox<T>(
|
||||
name,
|
||||
@@ -22,7 +24,11 @@ Future<R?> withEncryptedBox<T, R>(
|
||||
}
|
||||
|
||||
Future<Uint8List> _getEncryptedBoxKey() async {
|
||||
const secureStorage = FlutterSecureStorage();
|
||||
const secureStorage = FlutterSecureStorage(
|
||||
aOptions: AndroidOptions(
|
||||
encryptedSharedPreferences: true,
|
||||
),
|
||||
);
|
||||
if (!await secureStorage.containsKey(key: 'key')) {
|
||||
final key = Hive.generateSecureKey();
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import 'package:hive_flutter/adapters.dart';
|
||||
import 'package:paperless_mobile/core/config/hive/hive_config.dart';
|
||||
import 'package:paperless_mobile/core/database/tables/global_settings.dart';
|
||||
import 'package:paperless_mobile/core/database/tables/local_user_settings.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_settings.dart';
|
||||
|
||||
part 'local_user_account.g.dart';
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ import 'package:paperless_mobile/features/document_bulk_action/view/widgets/full
|
||||
import 'package:paperless_mobile/features/document_bulk_action/view/widgets/fullscreen_bulk_edit_tags_widget.dart';
|
||||
import 'package:paperless_mobile/features/home/view/model/api_version.dart';
|
||||
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.dart';
|
||||
import 'package:paperless_mobile/features/saved_view/view/add_saved_view_page.dart';
|
||||
import 'package:paperless_mobile/features/saved_view_details/cubit/saved_view_details_cubit.dart';
|
||||
import 'package:paperless_mobile/features/saved_view_details/view/saved_view_details_page.dart';
|
||||
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||
|
||||
@@ -8,25 +8,26 @@ abstract class PersistentRepository<T> extends HydratedCubit<T> {
|
||||
PersistentRepository(T initialState) : super(initialState);
|
||||
|
||||
void addListener(
|
||||
Object source, {
|
||||
Object subscriber, {
|
||||
required void Function(T) onChanged,
|
||||
}) {
|
||||
onChanged(state);
|
||||
_subscribers.putIfAbsent(source, () {
|
||||
_subscribers.putIfAbsent(subscriber, () {
|
||||
return stream.listen((event) => onChanged(event));
|
||||
});
|
||||
}
|
||||
|
||||
void removeListener(Object source) async {
|
||||
await _subscribers[source]?.cancel();
|
||||
_subscribers.remove(source);
|
||||
_subscribers
|
||||
..[source]?.cancel()
|
||||
..remove(source);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() {
|
||||
_subscribers.forEach((key, subscription) {
|
||||
subscription.cancel();
|
||||
});
|
||||
for (final subscriber in _subscribers.values) {
|
||||
subscriber.cancel();
|
||||
}
|
||||
return super.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,25 +54,26 @@ String translateError(BuildContext context, ErrorCode code) {
|
||||
ErrorCode.suggestionsQueryError => S.of(context)!.couldNotLoadSuggestions,
|
||||
ErrorCode.acknowledgeTasksError => S.of(context)!.couldNotAcknowledgeTasks,
|
||||
ErrorCode.correspondentDeleteFailed =>
|
||||
"Could not delete correspondent, please try again.", //TODO: INTL
|
||||
S.of(context)!.couldNotDeleteCorrespondent,
|
||||
ErrorCode.documentTypeDeleteFailed =>
|
||||
"Could not delete document type, please try again.",
|
||||
ErrorCode.tagDeleteFailed => "Could not delete tag, please try again.",
|
||||
ErrorCode.correspondentUpdateFailed =>
|
||||
"Could not update correspondent, please try again.",
|
||||
ErrorCode.documentTypeUpdateFailed =>
|
||||
"Could not update document type, please try again.",
|
||||
ErrorCode.tagUpdateFailed => "Could not update tag, please try again.",
|
||||
S.of(context)!.couldNotDeleteDocumentType,
|
||||
ErrorCode.tagDeleteFailed => S.of(context)!.couldNotDeleteTag,
|
||||
ErrorCode.storagePathDeleteFailed =>
|
||||
"Could not delete storage path, please try again.",
|
||||
S.of(context)!.couldNotDeleteStoragePath,
|
||||
ErrorCode.correspondentUpdateFailed =>
|
||||
S.of(context)!.couldNotUpdateCorrespondent,
|
||||
ErrorCode.documentTypeUpdateFailed =>
|
||||
S.of(context)!.couldNotUpdateDocumentType,
|
||||
ErrorCode.tagUpdateFailed => S.of(context)!.couldNotUpdateTag,
|
||||
ErrorCode.storagePathUpdateFailed =>
|
||||
"Could not update storage path, please try again.",
|
||||
S.of(context)!.couldNotUpdateStoragePath,
|
||||
ErrorCode.serverInformationLoadFailed =>
|
||||
"Could not load server information.",
|
||||
ErrorCode.serverStatisticsLoadFailed => "Could not load server statistics.",
|
||||
ErrorCode.uiSettingsLoadFailed => "Could not load UI settings",
|
||||
ErrorCode.loadTasksError => "Could not load tasks.",
|
||||
ErrorCode.userNotFound => "User could not be found.",
|
||||
ErrorCode.updateSavedViewError => "Could not update saved view.",
|
||||
S.of(context)!.couldNotLoadServerInformation,
|
||||
ErrorCode.serverStatisticsLoadFailed =>
|
||||
S.of(context)!.couldNotLoadStatistics,
|
||||
ErrorCode.uiSettingsLoadFailed => S.of(context)!.couldNotLoadUISettings,
|
||||
ErrorCode.loadTasksError => S.of(context)!.couldNotLoadTasks,
|
||||
ErrorCode.userNotFound => S.of(context)!.userNotFound,
|
||||
ErrorCode.updateSavedViewError => S.of(context)!.couldNotUpdateSavedView,
|
||||
};
|
||||
}
|
||||
|
||||
31
lib/core/widgets/dialog_utils/pop_with_unsaved_changes.dart
Normal file
31
lib/core/widgets/dialog_utils/pop_with_unsaved_changes.dart
Normal file
@@ -0,0 +1,31 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:paperless_mobile/core/widgets/dialog_utils/unsaved_changes_warning_dialog.dart';
|
||||
|
||||
class PopWithUnsavedChanges extends StatelessWidget {
|
||||
final bool Function() hasChangesPredicate;
|
||||
final Widget child;
|
||||
|
||||
const PopWithUnsavedChanges({
|
||||
super.key,
|
||||
required this.hasChangesPredicate,
|
||||
required this.child,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return WillPopScope(
|
||||
onWillPop: () async {
|
||||
if (hasChangesPredicate()) {
|
||||
final shouldPop = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => const UnsavedChangesWarningDialog(),
|
||||
) ??
|
||||
false;
|
||||
return shouldPop;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:paperless_mobile/core/widgets/dialog_utils/dialog_cancel_button.dart';
|
||||
import 'package:paperless_mobile/core/widgets/dialog_utils/dialog_confirm_button.dart';
|
||||
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||
|
||||
class UnsavedChangesWarningDialog extends StatelessWidget {
|
||||
const UnsavedChangesWarningDialog({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text("Discard changes?"),
|
||||
content: Text(
|
||||
"You have unsaved changes. Do you want to continue without saving? Your changes will be discarded.",
|
||||
),
|
||||
actions: [
|
||||
DialogCancelButton(),
|
||||
DialogConfirmButton(
|
||||
label: S.of(context)!.continueLabel,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
|
||||
class EmptyState extends StatelessWidget {
|
||||
final String title;
|
||||
final String subtitle;
|
||||
final Widget? bottomChild;
|
||||
|
||||
const EmptyState({
|
||||
Key? key,
|
||||
required this.title,
|
||||
required this.subtitle,
|
||||
this.bottomChild,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final size = MediaQuery.of(context).size;
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: size.height / 3,
|
||||
width: size.width / 3,
|
||||
child: SvgPicture.asset("assets/images/empty-state.svg"),
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
subtitle,
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
if (bottomChild != null) ...[bottomChild!] else ...[]
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user