settings / preferences upate

This commit is contained in:
Christien Rioux 2024-07-26 16:51:03 -04:00
parent 1455aabe6c
commit d962f98786
26 changed files with 1015 additions and 125 deletions

View File

@ -219,7 +219,26 @@
"settings_page": {
"titlebar": "Settings",
"color_theme": "Color Theme",
"brightness_mode": "Brightness Mode"
"brightness_mode": "Brightness Mode",
"display_beta_warning": "Display beta warning on startup",
"none": "None",
"in_app": "In-app",
"push": "Push",
"in_app_or_push": "In-app or Push",
"enable_badge": "Enable icon 'badge' bubble",
"enable_notifications": "Enable notifications",
"message_notification_content": "Message notification content",
"invitation_accepted": "On invitation accept/reject",
"message_received": "On message received",
"message_sent": "On message sent",
"name_and_content": "Name and content",
"name_only": "Name only",
"nothing": "Nothing",
"bonk": "Bonk",
"boop": "Boop",
"badeep": "Badeep",
"beep_badeep": "Beep-Badeep",
"custom": "Custom"
},
"developer": {
"title": "Developer Logs",

BIN
assets/sounds/badeep.wav Normal file

Binary file not shown.

Binary file not shown.

BIN
assets/sounds/bonk.wav Normal file

Binary file not shown.

BIN
assets/sounds/boop.wav Normal file

Binary file not shown.

View File

@ -43,7 +43,7 @@ class VeilidChatApp extends StatelessWidget {
void _reloadTheme(BuildContext context) {
log.info('Reloading theme');
final theme =
PreferencesRepository.instance.value.themePreferences.themeData();
PreferencesRepository.instance.value.themePreference.themeData();
ThemeSwitcher.of(context).changeTheme(theme: theme);
// Hack to reload translations

View File

@ -12,6 +12,7 @@ import 'package:url_launcher/url_launcher_string.dart';
import 'package:veilid_support/veilid_support.dart';
import '../../account_manager/account_manager.dart';
import '../../settings/settings.dart';
import '../../theme/theme.dart';
import 'drawer_menu/drawer_menu.dart';
import 'home_account_invalid.dart';
@ -41,7 +42,17 @@ class HomeScreenState extends State<HomeScreen>
.indexWhere((x) => x.superIdentity.recordKey == activeLocalAccount);
final canClose = activeIndex != -1;
unawaited(_doBetaDialog(context));
final displayBetaWarning = context
.read<PreferencesCubit>()
.state
.asData
?.value
.notificationsPreference
.displayBetaWarning ??
true;
if (displayBetaWarning) {
await _doBetaDialog(context);
}
if (!canClose) {
await _zoomDrawerController.open!();
@ -51,10 +62,13 @@ class HomeScreenState extends State<HomeScreen>
}
Future<void> _doBetaDialog(BuildContext context) async {
var displayBetaWarning = true;
await QuickAlert.show(
context: context,
title: translate('splash.beta_title'),
widget: RichText(
context: context,
title: translate('splash.beta_title'),
widget: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
RichText(
textAlign: TextAlign.center,
text: TextSpan(
children: <TextSpan>[
@ -77,7 +91,28 @@ class HomeScreenState extends State<HomeScreen>
],
),
),
type: QuickAlertType.warning);
Row(mainAxisSize: MainAxisSize.min, children: [
StatefulBuilder(
builder: (context, setState) => Checkbox.adaptive(
value: displayBetaWarning,
onChanged: (value) {
setState(() {
displayBetaWarning = value ?? true;
});
},
)),
Text(translate('settings_page.display_beta_warning'),
style: const TextStyle(color: Colors.black)),
]),
]),
type: QuickAlertType.warning,
);
final preferencesInstance = PreferencesRepository.instance;
await preferencesInstance.set(preferencesInstance.value.copyWith(
notificationsPreference: preferencesInstance
.value.notificationsPreference
.copyWith(displayBetaWarning: displayBetaWarning)));
}
Widget _buildAccountPage(

View File

@ -36,7 +36,7 @@ void main() async {
WidgetsFlutterBinding.ensureInitialized();
await PreferencesRepository.instance.init();
final initialThemeData =
PreferencesRepository.instance.value.themePreferences.themeData();
PreferencesRepository.instance.value.themePreference.themeData();
// Manage window on desktop platforms
await initializeWindowControl();

View File

@ -0,0 +1,2 @@
export 'notifications_preference.dart';
export 'notifications_state.dart';

View File

@ -0,0 +1,69 @@
import 'package:change_case/change_case.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'notifications_preference.freezed.dart';
part 'notifications_preference.g.dart';
@freezed
class NotificationsPreference with _$NotificationsPreference {
const factory NotificationsPreference({
@Default(true) bool displayBetaWarning,
@Default(true) bool enableBadge,
@Default(true) bool enableNotifications,
@Default(MessageNotificationContent.nameAndContent)
MessageNotificationContent messageNotificationContent,
@Default(NotificationMode.inAppOrPush)
NotificationMode onInvitationAcceptedMode,
@Default(SoundEffect.beepBaDeep) SoundEffect onInvitationAcceptedSound,
@Default(NotificationMode.inAppOrPush)
NotificationMode onMessageReceivedMode,
@Default(SoundEffect.boop) SoundEffect onMessageReceivedSound,
@Default(SoundEffect.bonk) SoundEffect onMessageSentSound,
}) = _NotificationsPreference;
factory NotificationsPreference.fromJson(dynamic json) =>
_$NotificationsPreferenceFromJson(json as Map<String, dynamic>);
static const NotificationsPreference defaults = NotificationsPreference();
}
enum NotificationMode {
none,
inApp,
push,
inAppOrPush;
factory NotificationMode.fromJson(dynamic j) =>
NotificationMode.values.byName((j as String).toCamelCase());
String toJson() => name.toPascalCase();
static const NotificationMode defaults = NotificationMode.none;
}
enum MessageNotificationContent {
nothing,
nameOnly,
nameAndContent;
factory MessageNotificationContent.fromJson(dynamic j) =>
MessageNotificationContent.values.byName((j as String).toCamelCase());
String toJson() => name.toPascalCase();
static const MessageNotificationContent defaults =
MessageNotificationContent.nothing;
}
enum SoundEffect {
none,
bonk,
boop,
baDeep,
beepBaDeep,
custom;
factory SoundEffect.fromJson(dynamic j) =>
SoundEffect.values.byName((j as String).toCamelCase());
String toJson() => name.toPascalCase();
static const SoundEffect defaults = SoundEffect.none;
}

View File

@ -0,0 +1,358 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'notifications_preference.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
NotificationsPreference _$NotificationsPreferenceFromJson(
Map<String, dynamic> json) {
return _NotificationsPreference.fromJson(json);
}
/// @nodoc
mixin _$NotificationsPreference {
bool get displayBetaWarning => throw _privateConstructorUsedError;
bool get enableBadge => throw _privateConstructorUsedError;
bool get enableNotifications => throw _privateConstructorUsedError;
MessageNotificationContent get messageNotificationContent =>
throw _privateConstructorUsedError;
NotificationMode get onInvitationAcceptedMode =>
throw _privateConstructorUsedError;
SoundEffect get onInvitationAcceptedSound =>
throw _privateConstructorUsedError;
NotificationMode get onMessageReceivedMode =>
throw _privateConstructorUsedError;
SoundEffect get onMessageReceivedSound => throw _privateConstructorUsedError;
SoundEffect get onMessageSentSound => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$NotificationsPreferenceCopyWith<NotificationsPreference> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $NotificationsPreferenceCopyWith<$Res> {
factory $NotificationsPreferenceCopyWith(NotificationsPreference value,
$Res Function(NotificationsPreference) then) =
_$NotificationsPreferenceCopyWithImpl<$Res, NotificationsPreference>;
@useResult
$Res call(
{bool displayBetaWarning,
bool enableBadge,
bool enableNotifications,
MessageNotificationContent messageNotificationContent,
NotificationMode onInvitationAcceptedMode,
SoundEffect onInvitationAcceptedSound,
NotificationMode onMessageReceivedMode,
SoundEffect onMessageReceivedSound,
SoundEffect onMessageSentSound});
}
/// @nodoc
class _$NotificationsPreferenceCopyWithImpl<$Res,
$Val extends NotificationsPreference>
implements $NotificationsPreferenceCopyWith<$Res> {
_$NotificationsPreferenceCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
@pragma('vm:prefer-inline')
@override
$Res call({
Object? displayBetaWarning = null,
Object? enableBadge = null,
Object? enableNotifications = null,
Object? messageNotificationContent = null,
Object? onInvitationAcceptedMode = null,
Object? onInvitationAcceptedSound = null,
Object? onMessageReceivedMode = null,
Object? onMessageReceivedSound = null,
Object? onMessageSentSound = null,
}) {
return _then(_value.copyWith(
displayBetaWarning: null == displayBetaWarning
? _value.displayBetaWarning
: displayBetaWarning // ignore: cast_nullable_to_non_nullable
as bool,
enableBadge: null == enableBadge
? _value.enableBadge
: enableBadge // ignore: cast_nullable_to_non_nullable
as bool,
enableNotifications: null == enableNotifications
? _value.enableNotifications
: enableNotifications // ignore: cast_nullable_to_non_nullable
as bool,
messageNotificationContent: null == messageNotificationContent
? _value.messageNotificationContent
: messageNotificationContent // ignore: cast_nullable_to_non_nullable
as MessageNotificationContent,
onInvitationAcceptedMode: null == onInvitationAcceptedMode
? _value.onInvitationAcceptedMode
: onInvitationAcceptedMode // ignore: cast_nullable_to_non_nullable
as NotificationMode,
onInvitationAcceptedSound: null == onInvitationAcceptedSound
? _value.onInvitationAcceptedSound
: onInvitationAcceptedSound // ignore: cast_nullable_to_non_nullable
as SoundEffect,
onMessageReceivedMode: null == onMessageReceivedMode
? _value.onMessageReceivedMode
: onMessageReceivedMode // ignore: cast_nullable_to_non_nullable
as NotificationMode,
onMessageReceivedSound: null == onMessageReceivedSound
? _value.onMessageReceivedSound
: onMessageReceivedSound // ignore: cast_nullable_to_non_nullable
as SoundEffect,
onMessageSentSound: null == onMessageSentSound
? _value.onMessageSentSound
: onMessageSentSound // ignore: cast_nullable_to_non_nullable
as SoundEffect,
) as $Val);
}
}
/// @nodoc
abstract class _$$NotificationsPreferenceImplCopyWith<$Res>
implements $NotificationsPreferenceCopyWith<$Res> {
factory _$$NotificationsPreferenceImplCopyWith(
_$NotificationsPreferenceImpl value,
$Res Function(_$NotificationsPreferenceImpl) then) =
__$$NotificationsPreferenceImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{bool displayBetaWarning,
bool enableBadge,
bool enableNotifications,
MessageNotificationContent messageNotificationContent,
NotificationMode onInvitationAcceptedMode,
SoundEffect onInvitationAcceptedSound,
NotificationMode onMessageReceivedMode,
SoundEffect onMessageReceivedSound,
SoundEffect onMessageSentSound});
}
/// @nodoc
class __$$NotificationsPreferenceImplCopyWithImpl<$Res>
extends _$NotificationsPreferenceCopyWithImpl<$Res,
_$NotificationsPreferenceImpl>
implements _$$NotificationsPreferenceImplCopyWith<$Res> {
__$$NotificationsPreferenceImplCopyWithImpl(
_$NotificationsPreferenceImpl _value,
$Res Function(_$NotificationsPreferenceImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@override
$Res call({
Object? displayBetaWarning = null,
Object? enableBadge = null,
Object? enableNotifications = null,
Object? messageNotificationContent = null,
Object? onInvitationAcceptedMode = null,
Object? onInvitationAcceptedSound = null,
Object? onMessageReceivedMode = null,
Object? onMessageReceivedSound = null,
Object? onMessageSentSound = null,
}) {
return _then(_$NotificationsPreferenceImpl(
displayBetaWarning: null == displayBetaWarning
? _value.displayBetaWarning
: displayBetaWarning // ignore: cast_nullable_to_non_nullable
as bool,
enableBadge: null == enableBadge
? _value.enableBadge
: enableBadge // ignore: cast_nullable_to_non_nullable
as bool,
enableNotifications: null == enableNotifications
? _value.enableNotifications
: enableNotifications // ignore: cast_nullable_to_non_nullable
as bool,
messageNotificationContent: null == messageNotificationContent
? _value.messageNotificationContent
: messageNotificationContent // ignore: cast_nullable_to_non_nullable
as MessageNotificationContent,
onInvitationAcceptedMode: null == onInvitationAcceptedMode
? _value.onInvitationAcceptedMode
: onInvitationAcceptedMode // ignore: cast_nullable_to_non_nullable
as NotificationMode,
onInvitationAcceptedSound: null == onInvitationAcceptedSound
? _value.onInvitationAcceptedSound
: onInvitationAcceptedSound // ignore: cast_nullable_to_non_nullable
as SoundEffect,
onMessageReceivedMode: null == onMessageReceivedMode
? _value.onMessageReceivedMode
: onMessageReceivedMode // ignore: cast_nullable_to_non_nullable
as NotificationMode,
onMessageReceivedSound: null == onMessageReceivedSound
? _value.onMessageReceivedSound
: onMessageReceivedSound // ignore: cast_nullable_to_non_nullable
as SoundEffect,
onMessageSentSound: null == onMessageSentSound
? _value.onMessageSentSound
: onMessageSentSound // ignore: cast_nullable_to_non_nullable
as SoundEffect,
));
}
}
/// @nodoc
@JsonSerializable()
class _$NotificationsPreferenceImpl implements _NotificationsPreference {
const _$NotificationsPreferenceImpl(
{this.displayBetaWarning = true,
this.enableBadge = true,
this.enableNotifications = true,
this.messageNotificationContent =
MessageNotificationContent.nameAndContent,
this.onInvitationAcceptedMode = NotificationMode.inAppOrPush,
this.onInvitationAcceptedSound = SoundEffect.beepBaDeep,
this.onMessageReceivedMode = NotificationMode.inAppOrPush,
this.onMessageReceivedSound = SoundEffect.boop,
this.onMessageSentSound = SoundEffect.bonk});
factory _$NotificationsPreferenceImpl.fromJson(Map<String, dynamic> json) =>
_$$NotificationsPreferenceImplFromJson(json);
@override
@JsonKey()
final bool displayBetaWarning;
@override
@JsonKey()
final bool enableBadge;
@override
@JsonKey()
final bool enableNotifications;
@override
@JsonKey()
final MessageNotificationContent messageNotificationContent;
@override
@JsonKey()
final NotificationMode onInvitationAcceptedMode;
@override
@JsonKey()
final SoundEffect onInvitationAcceptedSound;
@override
@JsonKey()
final NotificationMode onMessageReceivedMode;
@override
@JsonKey()
final SoundEffect onMessageReceivedSound;
@override
@JsonKey()
final SoundEffect onMessageSentSound;
@override
String toString() {
return 'NotificationsPreference(displayBetaWarning: $displayBetaWarning, enableBadge: $enableBadge, enableNotifications: $enableNotifications, messageNotificationContent: $messageNotificationContent, onInvitationAcceptedMode: $onInvitationAcceptedMode, onInvitationAcceptedSound: $onInvitationAcceptedSound, onMessageReceivedMode: $onMessageReceivedMode, onMessageReceivedSound: $onMessageReceivedSound, onMessageSentSound: $onMessageSentSound)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$NotificationsPreferenceImpl &&
(identical(other.displayBetaWarning, displayBetaWarning) ||
other.displayBetaWarning == displayBetaWarning) &&
(identical(other.enableBadge, enableBadge) ||
other.enableBadge == enableBadge) &&
(identical(other.enableNotifications, enableNotifications) ||
other.enableNotifications == enableNotifications) &&
(identical(other.messageNotificationContent,
messageNotificationContent) ||
other.messageNotificationContent ==
messageNotificationContent) &&
(identical(
other.onInvitationAcceptedMode, onInvitationAcceptedMode) ||
other.onInvitationAcceptedMode == onInvitationAcceptedMode) &&
(identical(other.onInvitationAcceptedSound,
onInvitationAcceptedSound) ||
other.onInvitationAcceptedSound == onInvitationAcceptedSound) &&
(identical(other.onMessageReceivedMode, onMessageReceivedMode) ||
other.onMessageReceivedMode == onMessageReceivedMode) &&
(identical(other.onMessageReceivedSound, onMessageReceivedSound) ||
other.onMessageReceivedSound == onMessageReceivedSound) &&
(identical(other.onMessageSentSound, onMessageSentSound) ||
other.onMessageSentSound == onMessageSentSound));
}
@JsonKey(ignore: true)
@override
int get hashCode => Object.hash(
runtimeType,
displayBetaWarning,
enableBadge,
enableNotifications,
messageNotificationContent,
onInvitationAcceptedMode,
onInvitationAcceptedSound,
onMessageReceivedMode,
onMessageReceivedSound,
onMessageSentSound);
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$NotificationsPreferenceImplCopyWith<_$NotificationsPreferenceImpl>
get copyWith => __$$NotificationsPreferenceImplCopyWithImpl<
_$NotificationsPreferenceImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$NotificationsPreferenceImplToJson(
this,
);
}
}
abstract class _NotificationsPreference implements NotificationsPreference {
const factory _NotificationsPreference(
{final bool displayBetaWarning,
final bool enableBadge,
final bool enableNotifications,
final MessageNotificationContent messageNotificationContent,
final NotificationMode onInvitationAcceptedMode,
final SoundEffect onInvitationAcceptedSound,
final NotificationMode onMessageReceivedMode,
final SoundEffect onMessageReceivedSound,
final SoundEffect onMessageSentSound}) = _$NotificationsPreferenceImpl;
factory _NotificationsPreference.fromJson(Map<String, dynamic> json) =
_$NotificationsPreferenceImpl.fromJson;
@override
bool get displayBetaWarning;
@override
bool get enableBadge;
@override
bool get enableNotifications;
@override
MessageNotificationContent get messageNotificationContent;
@override
NotificationMode get onInvitationAcceptedMode;
@override
SoundEffect get onInvitationAcceptedSound;
@override
NotificationMode get onMessageReceivedMode;
@override
SoundEffect get onMessageReceivedSound;
@override
SoundEffect get onMessageSentSound;
@override
@JsonKey(ignore: true)
_$$NotificationsPreferenceImplCopyWith<_$NotificationsPreferenceImpl>
get copyWith => throw _privateConstructorUsedError;
}

View File

@ -0,0 +1,50 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'notifications_preference.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$NotificationsPreferenceImpl _$$NotificationsPreferenceImplFromJson(
Map<String, dynamic> json) =>
_$NotificationsPreferenceImpl(
displayBetaWarning: json['display_beta_warning'] as bool? ?? true,
enableBadge: json['enable_badge'] as bool? ?? true,
enableNotifications: json['enable_notifications'] as bool? ?? true,
messageNotificationContent: json['message_notification_content'] == null
? MessageNotificationContent.nameAndContent
: MessageNotificationContent.fromJson(
json['message_notification_content']),
onInvitationAcceptedMode: json['on_invitation_accepted_mode'] == null
? NotificationMode.inAppOrPush
: NotificationMode.fromJson(json['on_invitation_accepted_mode']),
onInvitationAcceptedSound: json['on_invitation_accepted_sound'] == null
? SoundEffect.beepBaDeep
: SoundEffect.fromJson(json['on_invitation_accepted_sound']),
onMessageReceivedMode: json['on_message_received_mode'] == null
? NotificationMode.inAppOrPush
: NotificationMode.fromJson(json['on_message_received_mode']),
onMessageReceivedSound: json['on_message_received_sound'] == null
? SoundEffect.boop
: SoundEffect.fromJson(json['on_message_received_sound']),
onMessageSentSound: json['on_message_sent_sound'] == null
? SoundEffect.bonk
: SoundEffect.fromJson(json['on_message_sent_sound']),
);
Map<String, dynamic> _$$NotificationsPreferenceImplToJson(
_$NotificationsPreferenceImpl instance) =>
<String, dynamic>{
'display_beta_warning': instance.displayBetaWarning,
'enable_badge': instance.enableBadge,
'enable_notifications': instance.enableNotifications,
'message_notification_content':
instance.messageNotificationContent.toJson(),
'on_invitation_accepted_mode': instance.onInvitationAcceptedMode.toJson(),
'on_invitation_accepted_sound':
instance.onInvitationAcceptedSound.toJson(),
'on_message_received_mode': instance.onMessageReceivedMode.toJson(),
'on_message_received_sound': instance.onMessageReceivedSound.toJson(),
'on_message_sent_sound': instance.onMessageSentSound.toJson(),
};

View File

@ -1,3 +1,3 @@
export 'cubits/notifications_cubit.dart';
export 'models/notifications_state.dart';
export 'views/notifications_widget.dart';
export 'models/models.dart';
export 'views/views.dart';

View File

@ -0,0 +1,285 @@
import 'package:awesome_extensions/awesome_extensions.dart';
import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:flutter_translate/flutter_translate.dart';
import '../../settings/settings.dart';
import '../../theme/theme.dart';
import '../notifications.dart';
const String formFieldDisplayBetaWarning = 'displayBetaWarning';
const String formFieldEnableBadge = 'enableBadge';
const String formFieldEnableNotifications = 'enableNotifications';
const String formFieldMessageNotificationContent = 'messageNotificationContent';
const String formFieldInvitationAcceptMode = 'invitationAcceptMode';
const String formFieldInvitationAcceptSound = 'invitationAcceptSound';
const String formFieldMessageReceivedMode = 'messageReceivedMode';
const String formFieldMessageReceivedSound = 'messageReceivedSound';
const String formFieldMessageSentSound = 'messageSentSound';
Widget buildSettingsPageNotificationPreferences(
{required BuildContext context, required void Function() onChanged}) {
final theme = Theme.of(context);
final scale = theme.extension<ScaleScheme>()!;
final scaleConfig = theme.extension<ScaleConfig>()!;
final textTheme = theme.textTheme;
final preferencesRepository = PreferencesRepository.instance;
final notificationsPreference =
preferencesRepository.value.notificationsPreference;
Future<void> updatePreferences(
NotificationsPreference newNotificationsPreference) async {
final newPrefs = preferencesRepository.value
.copyWith(notificationsPreference: newNotificationsPreference);
await preferencesRepository.set(newPrefs);
onChanged();
}
List<DropdownMenuItem<NotificationMode>> notificationModeItems() {
final out = <DropdownMenuItem<NotificationMode>>[];
final items = [
(NotificationMode.none, true, translate('settings_page.none')),
(NotificationMode.inApp, true, translate('settings_page.in_app')),
(NotificationMode.push, false, translate('settings_page.push')),
(
NotificationMode.inAppOrPush,
true,
translate('settings_page.in_app_or_push')
),
];
for (final x in items) {
out.add(DropdownMenuItem(
value: x.$1,
enabled: x.$2,
child: Text(x.$3, style: textTheme.labelSmall)));
}
return out;
}
List<DropdownMenuItem<SoundEffect>> soundEffectItems() {
final out = <DropdownMenuItem<SoundEffect>>[];
final items = [
(SoundEffect.none, true, translate('settings_page.none')),
(SoundEffect.bonk, true, translate('settings_page.bonk')),
(SoundEffect.boop, true, translate('settings_page.boop')),
(SoundEffect.baDeep, true, translate('settings_page.badeep')),
(SoundEffect.beepBaDeep, true, translate('settings_page.beep_badeep')),
(SoundEffect.custom, false, translate('settings_page.custom')),
];
for (final x in items) {
out.add(DropdownMenuItem(
value: x.$1,
enabled: x.$2,
child: Text(x.$3, style: textTheme.labelSmall)));
}
return out;
}
List<DropdownMenuItem<MessageNotificationContent>>
messageNotificationContentItems() {
final out = <DropdownMenuItem<MessageNotificationContent>>[];
final items = [
(
MessageNotificationContent.nameAndContent,
true,
translate('settings_page.name_and_content')
),
(
MessageNotificationContent.nameOnly,
true,
translate('settings_page.name_only')
),
(
MessageNotificationContent.nothing,
true,
translate('settings_page.nothing')
),
];
for (final x in items) {
out.add(DropdownMenuItem(
value: x.$1,
enabled: x.$2,
child: Text(x.$3, style: textTheme.labelSmall)));
}
return out;
}
return DecoratedBox(
decoration: ShapeDecoration(
shape: RoundedRectangleBorder(
side: BorderSide(width: 2, color: scale.primaryScale.border),
borderRadius:
BorderRadius.circular(8 * scaleConfig.borderRadiusScale))),
child: Column(mainAxisSize: MainAxisSize.min, children: [
// Display Beta Warning
FormBuilderCheckbox(
name: formFieldDisplayBetaWarning,
side: BorderSide(color: scale.primaryScale.border, width: 2),
title: Text(translate('settings_page.display_beta_warning'),
style: textTheme.labelMedium),
initialValue: notificationsPreference.displayBetaWarning,
onChanged: (value) async {
if (value == null) {
return;
}
final newNotificationsPreference =
notificationsPreference.copyWith(displayBetaWarning: value);
await updatePreferences(newNotificationsPreference);
}),
// Enable Badge
FormBuilderCheckbox(
name: formFieldEnableBadge,
side: BorderSide(color: scale.primaryScale.border, width: 2),
title: Text(translate('settings_page.enable_badge'),
style: textTheme.labelMedium),
initialValue: notificationsPreference.enableBadge,
onChanged: (value) async {
if (value == null) {
return;
}
final newNotificationsPreference =
notificationsPreference.copyWith(enableBadge: value);
await updatePreferences(newNotificationsPreference);
}),
// Enable Notifications
FormBuilderCheckbox(
name: formFieldEnableNotifications,
side: BorderSide(color: scale.primaryScale.border, width: 2),
title: Text(translate('settings_page.enable_notifications'),
style: textTheme.labelMedium),
initialValue: notificationsPreference.enableNotifications,
onChanged: (value) async {
if (value == null) {
return;
}
final newNotificationsPreference =
notificationsPreference.copyWith(enableNotifications: value);
await updatePreferences(newNotificationsPreference);
}),
FormBuilderDropdown(
name: formFieldMessageNotificationContent,
isDense: false,
decoration: InputDecoration(
labelText: translate('settings_page.message_notification_content')),
enabled: notificationsPreference.enableNotifications,
initialValue: notificationsPreference.messageNotificationContent,
onChanged: (value) async {
if (value == null) {
return;
}
final newNotificationsPreference = notificationsPreference.copyWith(
messageNotificationContent: value);
await updatePreferences(newNotificationsPreference);
},
items: messageNotificationContentItems(),
).paddingAll(8),
// Notifications
Table(
defaultVerticalAlignment: TableCellVerticalAlignment.middle,
children: [
TableRow(children: [
// Invitation accepted
Text(
textAlign: TextAlign.right,
translate('settings_page.invitation_accepted'))
.paddingAll(8),
FormBuilderDropdown(
name: formFieldInvitationAcceptMode,
isDense: false,
enabled: notificationsPreference.enableNotifications,
initialValue: notificationsPreference.onInvitationAcceptedMode,
onChanged: (value) async {
if (value == null) {
return;
}
final newNotificationsPreference = notificationsPreference
.copyWith(onInvitationAcceptedMode: value);
await updatePreferences(newNotificationsPreference);
},
items: notificationModeItems(),
).paddingAll(4),
FormBuilderDropdown(
name: formFieldInvitationAcceptSound,
isDense: false,
enabled: notificationsPreference.enableNotifications,
initialValue: notificationsPreference.onInvitationAcceptedSound,
onChanged: (value) async {
if (value == null) {
return;
}
final newNotificationsPreference = notificationsPreference
.copyWith(onInvitationAcceptedSound: value);
await updatePreferences(newNotificationsPreference);
},
items: soundEffectItems(),
).paddingAll(4)
]),
// Message received
TableRow(children: [
Text(
textAlign: TextAlign.right,
translate('settings_page.message_received'))
.paddingAll(8),
FormBuilderDropdown(
name: formFieldMessageReceivedMode,
isDense: false,
enabled: notificationsPreference.enableNotifications,
initialValue: notificationsPreference.onMessageReceivedMode,
onChanged: (value) async {
if (value == null) {
return;
}
final newNotificationsPreference = notificationsPreference
.copyWith(onMessageReceivedMode: value);
await updatePreferences(newNotificationsPreference);
},
items: notificationModeItems(),
).paddingAll(4),
FormBuilderDropdown(
name: formFieldMessageReceivedSound,
isDense: false,
enabled: notificationsPreference.enableNotifications,
initialValue: notificationsPreference.onMessageReceivedSound,
onChanged: (value) async {
if (value == null) {
return;
}
final newNotificationsPreference = notificationsPreference
.copyWith(onMessageReceivedSound: value);
await updatePreferences(newNotificationsPreference);
},
items: soundEffectItems(),
).paddingAll(4)
]),
// Message sent
TableRow(children: [
Text(
textAlign: TextAlign.right,
translate('settings_page.message_sent'))
.paddingAll(8),
const SizedBox.shrink(),
FormBuilderDropdown(
name: formFieldMessageSentSound,
isDense: false,
enabled: notificationsPreference.enableNotifications,
initialValue: notificationsPreference.onMessageSentSound,
onChanged: (value) async {
if (value == null) {
return;
}
final newNotificationsPreference = notificationsPreference
.copyWith(onMessageSentSound: value);
await updatePreferences(newNotificationsPreference);
},
items: soundEffectItems(),
).paddingAll(4)
]),
]).paddingAll(8)
]).paddingAll(8),
);
}

View File

@ -0,0 +1,2 @@
export 'notifications_preferences.dart';
export 'notifications_widget.dart';

View File

@ -1,6 +1,7 @@
import 'package:change_case/change_case.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import '../../notifications/notifications.dart';
import '../../theme/theme.dart';
part 'preferences.freezed.dart';
@ -11,19 +12,15 @@ part 'preferences.g.dart';
@freezed
class LockPreference with _$LockPreference {
const factory LockPreference({
required int inactivityLockSecs,
required bool lockWhenSwitching,
required bool lockWithSystemLock,
@Default(0) int inactivityLockSecs,
@Default(false) bool lockWhenSwitching,
@Default(false) bool lockWithSystemLock,
}) = _LockPreference;
factory LockPreference.fromJson(dynamic json) =>
_$LockPreferenceFromJson(json as Map<String, dynamic>);
static const LockPreference defaults = LockPreference(
inactivityLockSecs: 0,
lockWhenSwitching: false,
lockWithSystemLock: false,
);
static const LockPreference defaults = LockPreference();
}
// Theme supports multiple translations
@ -42,16 +39,15 @@ enum LanguagePreference {
@freezed
class Preferences with _$Preferences {
const factory Preferences({
required ThemePreferences themePreferences,
required LanguagePreference language,
required LockPreference locking,
@Default(ThemePreferences.defaults) ThemePreferences themePreference,
@Default(LanguagePreference.defaults) LanguagePreference languagePreference,
@Default(LockPreference.defaults) LockPreference lockPreference,
@Default(NotificationsPreference.defaults)
NotificationsPreference notificationsPreference,
}) = _Preferences;
factory Preferences.fromJson(dynamic json) =>
_$PreferencesFromJson(json as Map<String, dynamic>);
static const Preferences defaults = Preferences(
themePreferences: ThemePreferences.defaults,
language: LanguagePreference.defaults,
locking: LockPreference.defaults);
static const Preferences defaults = Preferences();
}

View File

@ -126,18 +126,21 @@ class __$$LockPreferenceImplCopyWithImpl<$Res>
@JsonSerializable()
class _$LockPreferenceImpl implements _LockPreference {
const _$LockPreferenceImpl(
{required this.inactivityLockSecs,
required this.lockWhenSwitching,
required this.lockWithSystemLock});
{this.inactivityLockSecs = 0,
this.lockWhenSwitching = false,
this.lockWithSystemLock = false});
factory _$LockPreferenceImpl.fromJson(Map<String, dynamic> json) =>
_$$LockPreferenceImplFromJson(json);
@override
@JsonKey()
final int inactivityLockSecs;
@override
@JsonKey()
final bool lockWhenSwitching;
@override
@JsonKey()
final bool lockWithSystemLock;
@override
@ -180,9 +183,9 @@ class _$LockPreferenceImpl implements _LockPreference {
abstract class _LockPreference implements LockPreference {
const factory _LockPreference(
{required final int inactivityLockSecs,
required final bool lockWhenSwitching,
required final bool lockWithSystemLock}) = _$LockPreferenceImpl;
{final int inactivityLockSecs,
final bool lockWhenSwitching,
final bool lockWithSystemLock}) = _$LockPreferenceImpl;
factory _LockPreference.fromJson(Map<String, dynamic> json) =
_$LockPreferenceImpl.fromJson;
@ -205,9 +208,12 @@ Preferences _$PreferencesFromJson(Map<String, dynamic> json) {
/// @nodoc
mixin _$Preferences {
ThemePreferences get themePreferences => throw _privateConstructorUsedError;
LanguagePreference get language => throw _privateConstructorUsedError;
LockPreference get locking => throw _privateConstructorUsedError;
ThemePreferences get themePreference => throw _privateConstructorUsedError;
LanguagePreference get languagePreference =>
throw _privateConstructorUsedError;
LockPreference get lockPreference => throw _privateConstructorUsedError;
NotificationsPreference get notificationsPreference =>
throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
@ -222,12 +228,14 @@ abstract class $PreferencesCopyWith<$Res> {
_$PreferencesCopyWithImpl<$Res, Preferences>;
@useResult
$Res call(
{ThemePreferences themePreferences,
LanguagePreference language,
LockPreference locking});
{ThemePreferences themePreference,
LanguagePreference languagePreference,
LockPreference lockPreference,
NotificationsPreference notificationsPreference});
$ThemePreferencesCopyWith<$Res> get themePreferences;
$LockPreferenceCopyWith<$Res> get locking;
$ThemePreferencesCopyWith<$Res> get themePreference;
$LockPreferenceCopyWith<$Res> get lockPreference;
$NotificationsPreferenceCopyWith<$Res> get notificationsPreference;
}
/// @nodoc
@ -243,39 +251,53 @@ class _$PreferencesCopyWithImpl<$Res, $Val extends Preferences>
@pragma('vm:prefer-inline')
@override
$Res call({
Object? themePreferences = null,
Object? language = null,
Object? locking = null,
Object? themePreference = null,
Object? languagePreference = null,
Object? lockPreference = null,
Object? notificationsPreference = null,
}) {
return _then(_value.copyWith(
themePreferences: null == themePreferences
? _value.themePreferences
: themePreferences // ignore: cast_nullable_to_non_nullable
themePreference: null == themePreference
? _value.themePreference
: themePreference // ignore: cast_nullable_to_non_nullable
as ThemePreferences,
language: null == language
? _value.language
: language // ignore: cast_nullable_to_non_nullable
languagePreference: null == languagePreference
? _value.languagePreference
: languagePreference // ignore: cast_nullable_to_non_nullable
as LanguagePreference,
locking: null == locking
? _value.locking
: locking // ignore: cast_nullable_to_non_nullable
lockPreference: null == lockPreference
? _value.lockPreference
: lockPreference // ignore: cast_nullable_to_non_nullable
as LockPreference,
notificationsPreference: null == notificationsPreference
? _value.notificationsPreference
: notificationsPreference // ignore: cast_nullable_to_non_nullable
as NotificationsPreference,
) as $Val);
}
@override
@pragma('vm:prefer-inline')
$ThemePreferencesCopyWith<$Res> get themePreferences {
return $ThemePreferencesCopyWith<$Res>(_value.themePreferences, (value) {
return _then(_value.copyWith(themePreferences: value) as $Val);
$ThemePreferencesCopyWith<$Res> get themePreference {
return $ThemePreferencesCopyWith<$Res>(_value.themePreference, (value) {
return _then(_value.copyWith(themePreference: value) as $Val);
});
}
@override
@pragma('vm:prefer-inline')
$LockPreferenceCopyWith<$Res> get locking {
return $LockPreferenceCopyWith<$Res>(_value.locking, (value) {
return _then(_value.copyWith(locking: value) as $Val);
$LockPreferenceCopyWith<$Res> get lockPreference {
return $LockPreferenceCopyWith<$Res>(_value.lockPreference, (value) {
return _then(_value.copyWith(lockPreference: value) as $Val);
});
}
@override
@pragma('vm:prefer-inline')
$NotificationsPreferenceCopyWith<$Res> get notificationsPreference {
return $NotificationsPreferenceCopyWith<$Res>(
_value.notificationsPreference, (value) {
return _then(_value.copyWith(notificationsPreference: value) as $Val);
});
}
}
@ -289,14 +311,17 @@ abstract class _$$PreferencesImplCopyWith<$Res>
@override
@useResult
$Res call(
{ThemePreferences themePreferences,
LanguagePreference language,
LockPreference locking});
{ThemePreferences themePreference,
LanguagePreference languagePreference,
LockPreference lockPreference,
NotificationsPreference notificationsPreference});
@override
$ThemePreferencesCopyWith<$Res> get themePreferences;
$ThemePreferencesCopyWith<$Res> get themePreference;
@override
$LockPreferenceCopyWith<$Res> get locking;
$LockPreferenceCopyWith<$Res> get lockPreference;
@override
$NotificationsPreferenceCopyWith<$Res> get notificationsPreference;
}
/// @nodoc
@ -310,23 +335,28 @@ class __$$PreferencesImplCopyWithImpl<$Res>
@pragma('vm:prefer-inline')
@override
$Res call({
Object? themePreferences = null,
Object? language = null,
Object? locking = null,
Object? themePreference = null,
Object? languagePreference = null,
Object? lockPreference = null,
Object? notificationsPreference = null,
}) {
return _then(_$PreferencesImpl(
themePreferences: null == themePreferences
? _value.themePreferences
: themePreferences // ignore: cast_nullable_to_non_nullable
themePreference: null == themePreference
? _value.themePreference
: themePreference // ignore: cast_nullable_to_non_nullable
as ThemePreferences,
language: null == language
? _value.language
: language // ignore: cast_nullable_to_non_nullable
languagePreference: null == languagePreference
? _value.languagePreference
: languagePreference // ignore: cast_nullable_to_non_nullable
as LanguagePreference,
locking: null == locking
? _value.locking
: locking // ignore: cast_nullable_to_non_nullable
lockPreference: null == lockPreference
? _value.lockPreference
: lockPreference // ignore: cast_nullable_to_non_nullable
as LockPreference,
notificationsPreference: null == notificationsPreference
? _value.notificationsPreference
: notificationsPreference // ignore: cast_nullable_to_non_nullable
as NotificationsPreference,
));
}
}
@ -335,23 +365,30 @@ class __$$PreferencesImplCopyWithImpl<$Res>
@JsonSerializable()
class _$PreferencesImpl implements _Preferences {
const _$PreferencesImpl(
{required this.themePreferences,
required this.language,
required this.locking});
{this.themePreference = ThemePreferences.defaults,
this.languagePreference = LanguagePreference.defaults,
this.lockPreference = LockPreference.defaults,
this.notificationsPreference = NotificationsPreference.defaults});
factory _$PreferencesImpl.fromJson(Map<String, dynamic> json) =>
_$$PreferencesImplFromJson(json);
@override
final ThemePreferences themePreferences;
@JsonKey()
final ThemePreferences themePreference;
@override
final LanguagePreference language;
@JsonKey()
final LanguagePreference languagePreference;
@override
final LockPreference locking;
@JsonKey()
final LockPreference lockPreference;
@override
@JsonKey()
final NotificationsPreference notificationsPreference;
@override
String toString() {
return 'Preferences(themePreferences: $themePreferences, language: $language, locking: $locking)';
return 'Preferences(themePreference: $themePreference, languagePreference: $languagePreference, lockPreference: $lockPreference, notificationsPreference: $notificationsPreference)';
}
@override
@ -359,17 +396,21 @@ class _$PreferencesImpl implements _Preferences {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$PreferencesImpl &&
(identical(other.themePreferences, themePreferences) ||
other.themePreferences == themePreferences) &&
(identical(other.language, language) ||
other.language == language) &&
(identical(other.locking, locking) || other.locking == locking));
(identical(other.themePreference, themePreference) ||
other.themePreference == themePreference) &&
(identical(other.languagePreference, languagePreference) ||
other.languagePreference == languagePreference) &&
(identical(other.lockPreference, lockPreference) ||
other.lockPreference == lockPreference) &&
(identical(
other.notificationsPreference, notificationsPreference) ||
other.notificationsPreference == notificationsPreference));
}
@JsonKey(ignore: true)
@override
int get hashCode =>
Object.hash(runtimeType, themePreferences, language, locking);
int get hashCode => Object.hash(runtimeType, themePreference,
languagePreference, lockPreference, notificationsPreference);
@JsonKey(ignore: true)
@override
@ -387,19 +428,23 @@ class _$PreferencesImpl implements _Preferences {
abstract class _Preferences implements Preferences {
const factory _Preferences(
{required final ThemePreferences themePreferences,
required final LanguagePreference language,
required final LockPreference locking}) = _$PreferencesImpl;
{final ThemePreferences themePreference,
final LanguagePreference languagePreference,
final LockPreference lockPreference,
final NotificationsPreference notificationsPreference}) =
_$PreferencesImpl;
factory _Preferences.fromJson(Map<String, dynamic> json) =
_$PreferencesImpl.fromJson;
@override
ThemePreferences get themePreferences;
ThemePreferences get themePreference;
@override
LanguagePreference get language;
LanguagePreference get languagePreference;
@override
LockPreference get locking;
LockPreference get lockPreference;
@override
NotificationsPreference get notificationsPreference;
@override
@JsonKey(ignore: true)
_$$PreferencesImplCopyWith<_$PreferencesImpl> get copyWith =>

View File

@ -8,9 +8,9 @@ part of 'preferences.dart';
_$LockPreferenceImpl _$$LockPreferenceImplFromJson(Map<String, dynamic> json) =>
_$LockPreferenceImpl(
inactivityLockSecs: (json['inactivity_lock_secs'] as num).toInt(),
lockWhenSwitching: json['lock_when_switching'] as bool,
lockWithSystemLock: json['lock_with_system_lock'] as bool,
inactivityLockSecs: (json['inactivity_lock_secs'] as num?)?.toInt() ?? 0,
lockWhenSwitching: json['lock_when_switching'] as bool? ?? false,
lockWithSystemLock: json['lock_with_system_lock'] as bool? ?? false,
);
Map<String, dynamic> _$$LockPreferenceImplToJson(
@ -23,14 +23,24 @@ Map<String, dynamic> _$$LockPreferenceImplToJson(
_$PreferencesImpl _$$PreferencesImplFromJson(Map<String, dynamic> json) =>
_$PreferencesImpl(
themePreferences: ThemePreferences.fromJson(json['theme_preferences']),
language: LanguagePreference.fromJson(json['language']),
locking: LockPreference.fromJson(json['locking']),
themePreference: json['theme_preference'] == null
? ThemePreferences.defaults
: ThemePreferences.fromJson(json['theme_preference']),
languagePreference: json['language_preference'] == null
? LanguagePreference.defaults
: LanguagePreference.fromJson(json['language_preference']),
lockPreference: json['lock_preference'] == null
? LockPreference.defaults
: LockPreference.fromJson(json['lock_preference']),
notificationsPreference: json['notifications_preference'] == null
? NotificationsPreference.defaults
: NotificationsPreference.fromJson(json['notifications_preference']),
);
Map<String, dynamic> _$$PreferencesImplToJson(_$PreferencesImpl instance) =>
<String, dynamic>{
'theme_preferences': instance.themePreferences.toJson(),
'language': instance.language.toJson(),
'locking': instance.locking.toJson(),
'theme_preference': instance.themePreference.toJson(),
'language_preference': instance.languagePreference.toJson(),
'lock_preference': instance.lockPreference.toJson(),
'notifications_preference': instance.notificationsPreference.toJson(),
};

View File

@ -6,6 +6,7 @@ import 'package:flutter_translate/flutter_translate.dart';
import 'package:go_router/go_router.dart';
import '../layout/default_app_bar.dart';
import '../notifications/notifications.dart';
import '../theme/theme.dart';
import '../veilid_processor/veilid_processor.dart';
import 'settings.dart';
@ -49,6 +50,8 @@ class SettingsPageState extends State<SettingsPage> {
context: context, onChanged: () => setState(() {})),
buildSettingsPageBrightnessPreferences(
context: context, onChanged: () => setState(() {})),
buildSettingsPageNotificationPreferences(
context: context, onChanged: () => setState(() {})),
].map((x) => x.paddingLTRB(0, 0, 0, 8)).toList(),
),
).paddingSymmetric(horizontal: 24, vertical: 16),

View File

@ -49,19 +49,16 @@ enum ColorPreference {
@freezed
class ThemePreferences with _$ThemePreferences {
const factory ThemePreferences({
required BrightnessPreference brightnessPreference,
required ColorPreference colorPreference,
required double displayScale,
@Default(BrightnessPreference.system)
BrightnessPreference brightnessPreference,
@Default(ColorPreference.vapor) ColorPreference colorPreference,
@Default(1) double displayScale,
}) = _ThemePreferences;
factory ThemePreferences.fromJson(dynamic json) =>
_$ThemePreferencesFromJson(json as Map<String, dynamic>);
static const ThemePreferences defaults = ThemePreferences(
colorPreference: ColorPreference.vapor,
brightnessPreference: BrightnessPreference.system,
displayScale: 1,
);
static const ThemePreferences defaults = ThemePreferences();
}
extension ThemePreferencesExt on ThemePreferences {

View File

@ -127,18 +127,21 @@ class __$$ThemePreferencesImplCopyWithImpl<$Res>
@JsonSerializable()
class _$ThemePreferencesImpl implements _ThemePreferences {
const _$ThemePreferencesImpl(
{required this.brightnessPreference,
required this.colorPreference,
required this.displayScale});
{this.brightnessPreference = BrightnessPreference.system,
this.colorPreference = ColorPreference.vapor,
this.displayScale = 1});
factory _$ThemePreferencesImpl.fromJson(Map<String, dynamic> json) =>
_$$ThemePreferencesImplFromJson(json);
@override
@JsonKey()
final BrightnessPreference brightnessPreference;
@override
@JsonKey()
final ColorPreference colorPreference;
@override
@JsonKey()
final double displayScale;
@override
@ -181,9 +184,9 @@ class _$ThemePreferencesImpl implements _ThemePreferences {
abstract class _ThemePreferences implements ThemePreferences {
const factory _ThemePreferences(
{required final BrightnessPreference brightnessPreference,
required final ColorPreference colorPreference,
required final double displayScale}) = _$ThemePreferencesImpl;
{final BrightnessPreference brightnessPreference,
final ColorPreference colorPreference,
final double displayScale}) = _$ThemePreferencesImpl;
factory _ThemePreferences.fromJson(Map<String, dynamic> json) =
_$ThemePreferencesImpl.fromJson;

View File

@ -9,10 +9,13 @@ part of 'theme_preference.dart';
_$ThemePreferencesImpl _$$ThemePreferencesImplFromJson(
Map<String, dynamic> json) =>
_$ThemePreferencesImpl(
brightnessPreference:
BrightnessPreference.fromJson(json['brightness_preference']),
colorPreference: ColorPreference.fromJson(json['color_preference']),
displayScale: (json['display_scale'] as num).toDouble(),
brightnessPreference: json['brightness_preference'] == null
? BrightnessPreference.system
: BrightnessPreference.fromJson(json['brightness_preference']),
colorPreference: json['color_preference'] == null
? ColorPreference.vapor
: ColorPreference.fromJson(json['color_preference']),
displayScale: (json['display_scale'] as num?)?.toDouble() ?? 1,
);
Map<String, dynamic> _$$ThemePreferencesImplToJson(

View File

@ -24,7 +24,7 @@ List<DropdownMenuItem<dynamic>> _getBrightnessDropdownItems() {
Widget buildSettingsPageBrightnessPreferences(
{required BuildContext context, required void Function() onChanged}) {
final preferencesRepository = PreferencesRepository.instance;
final themePreferences = preferencesRepository.value.themePreferences;
final themePreferences = preferencesRepository.value.themePreference;
return ThemeSwitcher.withTheme(
builder: (_, switcher, theme) => FormBuilderDropdown(
name: formFieldBrightness,
@ -36,7 +36,7 @@ Widget buildSettingsPageBrightnessPreferences(
final newThemePrefs = themePreferences.copyWith(
brightnessPreference: value as BrightnessPreference);
final newPrefs = preferencesRepository.value
.copyWith(themePreferences: newThemePrefs);
.copyWith(themePreference: newThemePrefs);
await preferencesRepository.set(newPrefs);
switcher.changeTheme(theme: newThemePrefs.themeData());

View File

@ -34,7 +34,7 @@ List<DropdownMenuItem<dynamic>> _getThemeDropdownItems() {
Widget buildSettingsPageColorPreferences(
{required BuildContext context, required void Function() onChanged}) {
final preferencesRepository = PreferencesRepository.instance;
final themePreferences = preferencesRepository.value.themePreferences;
final themePreferences = preferencesRepository.value.themePreference;
return ThemeSwitcher.withTheme(
builder: (_, switcher, theme) => FormBuilderDropdown(
name: formFieldTheme,
@ -46,7 +46,7 @@ Widget buildSettingsPageColorPreferences(
final newThemePrefs = themePreferences.copyWith(
colorPreference: value as ColorPreference);
final newPrefs = preferencesRepository.value
.copyWith(themePreferences: newThemePrefs);
.copyWith(themePreference: newThemePrefs);
await preferencesRepository.set(newPrefs);
switcher.changeTheme(theme: newThemePrefs.themeData());

View File

@ -19,6 +19,14 @@ extension BorderExt on Widget {
child: this);
}
extension SizeToFixExt on Widget {
FittedBox fit({BoxFit? fit, Key? key}) => FittedBox(
key: key,
fit: fit ?? BoxFit.scaleDown,
child: this,
);
}
extension ModalProgressExt on Widget {
BlurryModalProgressHUD withModalHUD(BuildContext context, bool isLoading) {
final theme = Theme.of(context);

View File

@ -160,6 +160,11 @@ flutter:
- assets/images/ellet.png
# Printing
- assets/js/pdf/3.2.146/pdf.min.js
# Sounds
- assets/sounds/bonk.wav
- assets/sounds/boop.wav
- assets/sounds/badeep.wav
- assets/sounds/beepbadeep.wav
# Fonts
fonts:
- family: Source Code Pro