mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2025-12-09 08:08:14 -06:00
feat: Split settings, encrypted user credentials, preparations for multi user support
This commit is contained in:
36
lib/features/settings/global_app_settings.dart
Normal file
36
lib/features/settings/global_app_settings.dart
Normal file
@@ -0,0 +1,36 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:paperless_mobile/core/config/hive/hive_config.dart';
|
||||
import 'package:paperless_mobile/features/settings/model/color_scheme_option.dart';
|
||||
|
||||
part 'global_app_settings.g.dart';
|
||||
|
||||
@HiveType(typeId: HiveTypeIds.globalSettings)
|
||||
class GlobalAppSettings with ChangeNotifier, HiveObjectMixin {
|
||||
@HiveField(0)
|
||||
String preferredLocaleSubtag;
|
||||
|
||||
@HiveField(1)
|
||||
ThemeMode preferredThemeMode;
|
||||
|
||||
@HiveField(2)
|
||||
ColorSchemeOption preferredColorSchemeOption;
|
||||
|
||||
@HiveField(3)
|
||||
bool showOnboarding;
|
||||
|
||||
@HiveField(4)
|
||||
String? currentLoggedInUser;
|
||||
|
||||
GlobalAppSettings({
|
||||
required this.preferredLocaleSubtag,
|
||||
this.preferredThemeMode = ThemeMode.system,
|
||||
this.preferredColorSchemeOption = ColorSchemeOption.classic,
|
||||
this.showOnboarding = true,
|
||||
this.currentLoggedInUser,
|
||||
});
|
||||
|
||||
static GlobalAppSettings get boxedValue =>
|
||||
Hive.box<GlobalAppSettings>(HiveBoxes.globalSettings)
|
||||
.get(HiveBoxSingleValueKey.value)!;
|
||||
}
|
||||
@@ -1,4 +1,12 @@
|
||||
import 'package:hive_flutter/adapters.dart';
|
||||
import 'package:paperless_mobile/core/config/hive/hive_config.dart';
|
||||
|
||||
part 'color_scheme_option.g.dart';
|
||||
|
||||
@HiveType(typeId: HiveTypeIds.colorSchemeOption)
|
||||
enum ColorSchemeOption {
|
||||
@HiveField(0)
|
||||
classic,
|
||||
@HiveField(1)
|
||||
dynamic;
|
||||
}
|
||||
|
||||
16
lib/features/settings/user_app_settings.dart
Normal file
16
lib/features/settings/user_app_settings.dart
Normal file
@@ -0,0 +1,16 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:paperless_mobile/core/config/hive/hive_config.dart';
|
||||
import 'package:paperless_mobile/features/settings/model/color_scheme_option.dart';
|
||||
|
||||
part 'user_app_settings.g.dart';
|
||||
|
||||
@HiveType(typeId: HiveTypeIds.userSettings)
|
||||
class UserAppSettings with HiveObjectMixin {
|
||||
@HiveField(0)
|
||||
bool isBiometricAuthenticationEnabled;
|
||||
|
||||
UserAppSettings({
|
||||
this.isBiometricAuthenticationEnabled = false,
|
||||
});
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import 'package:paperless_mobile/core/repository/saved_view_repository.dart';
|
||||
import 'package:paperless_mobile/core/widgets/hint_card.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:paperless_mobile/features/login/cubit/authentication_cubit.dart';
|
||||
import 'package:paperless_mobile/features/settings/global_app_settings.dart';
|
||||
import 'package:paperless_mobile/features/settings/cubit/application_settings_cubit.dart';
|
||||
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||
|
||||
@@ -81,7 +82,7 @@ class AccountSettingsDialog extends StatelessWidget {
|
||||
Future<void> _onLogout(BuildContext context) async {
|
||||
try {
|
||||
await context.read<AuthenticationCubit>().logout();
|
||||
await context.read<ApplicationSettingsCubit>().clear();
|
||||
await context.read<GlobalAppSettings>();
|
||||
await context.read<LabelRepository>().clear();
|
||||
await context.read<SavedViewRepository>().clear();
|
||||
await HydratedBloc.storage.clear();
|
||||
|
||||
@@ -2,10 +2,12 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/core/bloc/paperless_server_information_cubit.dart';
|
||||
import 'package:paperless_mobile/core/bloc/paperless_server_information_state.dart';
|
||||
import 'package:paperless_mobile/features/settings/global_app_settings.dart';
|
||||
import 'package:paperless_mobile/features/settings/cubit/application_settings_cubit.dart';
|
||||
import 'package:paperless_mobile/features/settings/view/pages/application_settings_page.dart';
|
||||
import 'package:paperless_mobile/features/settings/view/pages/security_settings_page.dart';
|
||||
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class SettingsPage extends StatelessWidget {
|
||||
const SettingsPage({super.key});
|
||||
@@ -68,10 +70,7 @@ class SettingsPage extends StatelessWidget {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => BlocProvider.value(
|
||||
value: context.read<ApplicationSettingsCubit>(),
|
||||
child: page,
|
||||
),
|
||||
builder: (context) => page,
|
||||
maintainState: true,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,17 +1,27 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hive_flutter/adapters.dart';
|
||||
import 'package:paperless_mobile/core/config/hive/hive_config.dart';
|
||||
import 'package:paperless_mobile/features/login/services/authentication_service.dart';
|
||||
import 'package:paperless_mobile/features/settings/cubit/application_settings_cubit.dart';
|
||||
import 'package:paperless_mobile/features/settings/global_app_settings.dart';
|
||||
import 'package:paperless_mobile/features/settings/user_app_settings.dart';
|
||||
import 'package:paperless_mobile/features/settings/view/widgets/user_settings_builder.dart';
|
||||
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class BiometricAuthenticationSetting extends StatelessWidget {
|
||||
const BiometricAuthenticationSetting({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<ApplicationSettingsCubit, ApplicationSettingsState>(
|
||||
return UserSettingsBuilder(
|
||||
builder: (context, settings) {
|
||||
if (settings == null) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return SwitchListTile(
|
||||
value: settings.isLocalAuthenticationEnabled,
|
||||
value: settings.isBiometricAuthenticationEnabled,
|
||||
title: Text(S.of(context)!.biometricAuthentication),
|
||||
subtitle: Text(S.of(context)!.authenticateOnAppStart),
|
||||
onChanged: (val) async {
|
||||
@@ -19,12 +29,14 @@ class BiometricAuthenticationSetting extends StatelessWidget {
|
||||
S.of(context)!.authenticateToToggleBiometricAuthentication(
|
||||
val ? 'enable' : 'disable',
|
||||
);
|
||||
await context
|
||||
.read<ApplicationSettingsCubit>()
|
||||
.setIsBiometricAuthenticationEnabled(
|
||||
val,
|
||||
localizedReason: localizedReason,
|
||||
);
|
||||
|
||||
final isAuthenticated = await context
|
||||
.read<LocalAuthenticationService>()
|
||||
.authenticateLocalUser(localizedReason);
|
||||
if (isAuthenticated) {
|
||||
settings.isBiometricAuthenticationEnabled = val;
|
||||
settings.save();
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
@@ -2,11 +2,15 @@ import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hive_flutter/adapters.dart';
|
||||
import 'package:paperless_mobile/constants.dart';
|
||||
import 'package:paperless_mobile/core/config/hive/hive_config.dart';
|
||||
import 'package:paperless_mobile/core/translation/color_scheme_option_localization_mapper.dart';
|
||||
import 'package:paperless_mobile/core/widgets/hint_card.dart';
|
||||
import 'package:paperless_mobile/features/settings/cubit/application_settings_cubit.dart';
|
||||
import 'package:paperless_mobile/features/settings/global_app_settings.dart';
|
||||
import 'package:paperless_mobile/features/settings/model/color_scheme_option.dart';
|
||||
import 'package:paperless_mobile/features/settings/view/widgets/global_settings_builder.dart';
|
||||
import 'package:paperless_mobile/features/settings/view/widgets/radio_settings_dialog.dart';
|
||||
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||
|
||||
@@ -15,7 +19,7 @@ class ColorSchemeOptionSetting extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<ApplicationSettingsCubit, ApplicationSettingsState>(
|
||||
return GlobalSettingsBuilder(
|
||||
builder: (context, settings) {
|
||||
return ListTile(
|
||||
title: Text(S.of(context)!.colors),
|
||||
@@ -25,7 +29,7 @@ class ColorSchemeOptionSetting extends StatelessWidget {
|
||||
settings.preferredColorSchemeOption,
|
||||
),
|
||||
),
|
||||
onTap: () => showDialog(
|
||||
onTap: () => showDialog<ColorSchemeOption>(
|
||||
context: context,
|
||||
builder: (_) => RadioSettingsDialog<ColorSchemeOption>(
|
||||
titleText: S.of(context)!.colors,
|
||||
@@ -50,17 +54,13 @@ class ColorSchemeOptionSetting extends StatelessWidget {
|
||||
hintIcon: Icons.warning_amber,
|
||||
)
|
||||
: null,
|
||||
initialValue: context
|
||||
.read<ApplicationSettingsCubit>()
|
||||
.state
|
||||
.preferredColorSchemeOption,
|
||||
initialValue: settings.preferredColorSchemeOption,
|
||||
),
|
||||
).then(
|
||||
(value) {
|
||||
if (value != null) {
|
||||
context
|
||||
.read<ApplicationSettingsCubit>()
|
||||
.setColorSchemeOption(value);
|
||||
settings.preferredColorSchemeOption = value;
|
||||
settings.save();
|
||||
}
|
||||
},
|
||||
),
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/src/widgets/framework.dart';
|
||||
import 'package:flutter/src/widgets/placeholder.dart';
|
||||
import 'package:hive_flutter/adapters.dart';
|
||||
import 'package:paperless_mobile/core/config/hive/hive_config.dart';
|
||||
import 'package:paperless_mobile/features/settings/global_app_settings.dart';
|
||||
|
||||
class GlobalSettingsBuilder extends StatelessWidget {
|
||||
|
||||
final Widget Function(BuildContext context, GlobalAppSettings settings)
|
||||
builder;
|
||||
const GlobalSettingsBuilder({super.key, required this.builder});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ValueListenableBuilder(
|
||||
valueListenable:
|
||||
Hive.box<GlobalAppSettings>(HiveBoxes.globalSettings).listenable(),
|
||||
builder: (context, value, _) {
|
||||
final settings = value.get(HiveBoxSingleValueKey.value)!;
|
||||
return builder(context, settings);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hive_flutter/adapters.dart';
|
||||
import 'package:paperless_mobile/features/settings/cubit/application_settings_cubit.dart';
|
||||
import 'package:paperless_mobile/features/settings/global_app_settings.dart';
|
||||
import 'package:paperless_mobile/features/settings/view/widgets/radio_settings_dialog.dart';
|
||||
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||
import 'package:paperless_mobile/features/settings/view/widgets/global_settings_builder.dart';
|
||||
|
||||
class LanguageSelectionSetting extends StatefulWidget {
|
||||
const LanguageSelectionSetting({super.key});
|
||||
@@ -24,7 +27,7 @@ class _LanguageSelectionSettingState extends State<LanguageSelectionSetting> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<ApplicationSettingsCubit, ApplicationSettingsState>(
|
||||
return GlobalSettingsBuilder(
|
||||
builder: (context, settings) {
|
||||
return ListTile(
|
||||
title: Text(S.of(context)!.language),
|
||||
@@ -62,14 +65,12 @@ class _LanguageSelectionSettingState extends State<LanguageSelectionSetting> {
|
||||
label: _languageOptions['pl']! + "*",
|
||||
)
|
||||
],
|
||||
initialValue: context
|
||||
.read<ApplicationSettingsCubit>()
|
||||
.state
|
||||
.preferredLocaleSubtag,
|
||||
initialValue: settings.preferredLocaleSubtag,
|
||||
),
|
||||
).then((value) {
|
||||
if (value != null) {
|
||||
context.read<ApplicationSettingsCubit>().setLocale(value);
|
||||
settings.preferredLocaleSubtag = value;
|
||||
settings.save();
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/features/settings/cubit/application_settings_cubit.dart';
|
||||
import 'package:paperless_mobile/features/settings/view/widgets/global_settings_builder.dart';
|
||||
import 'package:paperless_mobile/features/settings/view/widgets/radio_settings_dialog.dart';
|
||||
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||
|
||||
@@ -9,7 +10,7 @@ class ThemeModeSetting extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<ApplicationSettingsCubit, ApplicationSettingsState>(
|
||||
return GlobalSettingsBuilder(
|
||||
builder: (context, settings) {
|
||||
return ListTile(
|
||||
title: Text(S.of(context)!.appearance),
|
||||
@@ -19,10 +20,7 @@ class ThemeModeSetting extends StatelessWidget {
|
||||
context: context,
|
||||
builder: (_) => RadioSettingsDialog<ThemeMode>(
|
||||
titleText: S.of(context)!.appearance,
|
||||
initialValue: context
|
||||
.read<ApplicationSettingsCubit>()
|
||||
.state
|
||||
.preferredThemeMode,
|
||||
initialValue: settings.preferredThemeMode,
|
||||
options: [
|
||||
RadioOption(
|
||||
value: ThemeMode.system,
|
||||
@@ -40,7 +38,8 @@ class ThemeModeSetting extends StatelessWidget {
|
||||
),
|
||||
).then((value) {
|
||||
if (value != null) {
|
||||
context.read<ApplicationSettingsCubit>().setThemeMode(value);
|
||||
settings.preferredThemeMode = value;
|
||||
settings.save();
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hive_flutter/adapters.dart';
|
||||
import 'package:paperless_mobile/core/config/hive/hive_config.dart';
|
||||
import 'package:paperless_mobile/features/settings/global_app_settings.dart';
|
||||
import 'package:paperless_mobile/features/settings/user_app_settings.dart';
|
||||
|
||||
class UserSettingsBuilder extends StatelessWidget {
|
||||
final Widget Function(
|
||||
BuildContext context,
|
||||
UserAppSettings? settings,
|
||||
) builder;
|
||||
|
||||
const UserSettingsBuilder({
|
||||
super.key,
|
||||
required this.builder,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ValueListenableBuilder<Box<UserAppSettings>>(
|
||||
valueListenable:
|
||||
Hive.box<UserAppSettings>(HiveBoxes.userSettings).listenable(),
|
||||
builder: (context, value, _) {
|
||||
final currentUser =
|
||||
Hive.box<GlobalAppSettings>(HiveBoxes.globalSettings)
|
||||
.get(HiveBoxSingleValueKey.value)
|
||||
?.currentLoggedInUser;
|
||||
if (currentUser != null) {
|
||||
final settings = value.get(currentUser);
|
||||
return builder(context, settings);
|
||||
} else {
|
||||
return builder(context, null);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user