more refactor

This commit is contained in:
Christien Rioux 2024-01-08 21:37:08 -05:00
parent ba4ef05a28
commit b83aa3a64b
39 changed files with 722 additions and 514 deletions

View file

@ -0,0 +1 @@
export 'preferences.dart';

View file

@ -0,0 +1,57 @@
import 'package:change_case/change_case.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import '../../theme/theme.dart';
part 'preferences.freezed.dart';
part 'preferences.g.dart';
// Lock preference changes how frequently the messenger locks its
// interface and requires the identitySecretKey to be entered (pin/password/etc)
@freezed
class LockPreference with _$LockPreference {
const factory LockPreference({
required int inactivityLockSecs,
required bool lockWhenSwitching,
required 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,
);
}
// Theme supports multiple translations
enum LanguagePreference {
englishUS;
factory LanguagePreference.fromJson(dynamic j) =>
LanguagePreference.values.byName((j as String).toCamelCase());
String toJson() => name.toPascalCase();
static const LanguagePreference defaults = LanguagePreference.englishUS;
}
// Preferences are stored in a table locally and globally affect all
// accounts imported/added and the app in general
@freezed
class Preferences with _$Preferences {
const factory Preferences({
required ThemePreferences themePreferences,
required LanguagePreference language,
required LockPreference locking,
}) = _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);
}

View file

@ -0,0 +1,407 @@
// 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 'preferences.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#custom-getters-and-methods');
LockPreference _$LockPreferenceFromJson(Map<String, dynamic> json) {
return _LockPreference.fromJson(json);
}
/// @nodoc
mixin _$LockPreference {
int get inactivityLockSecs => throw _privateConstructorUsedError;
bool get lockWhenSwitching => throw _privateConstructorUsedError;
bool get lockWithSystemLock => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$LockPreferenceCopyWith<LockPreference> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $LockPreferenceCopyWith<$Res> {
factory $LockPreferenceCopyWith(
LockPreference value, $Res Function(LockPreference) then) =
_$LockPreferenceCopyWithImpl<$Res, LockPreference>;
@useResult
$Res call(
{int inactivityLockSecs,
bool lockWhenSwitching,
bool lockWithSystemLock});
}
/// @nodoc
class _$LockPreferenceCopyWithImpl<$Res, $Val extends LockPreference>
implements $LockPreferenceCopyWith<$Res> {
_$LockPreferenceCopyWithImpl(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? inactivityLockSecs = null,
Object? lockWhenSwitching = null,
Object? lockWithSystemLock = null,
}) {
return _then(_value.copyWith(
inactivityLockSecs: null == inactivityLockSecs
? _value.inactivityLockSecs
: inactivityLockSecs // ignore: cast_nullable_to_non_nullable
as int,
lockWhenSwitching: null == lockWhenSwitching
? _value.lockWhenSwitching
: lockWhenSwitching // ignore: cast_nullable_to_non_nullable
as bool,
lockWithSystemLock: null == lockWithSystemLock
? _value.lockWithSystemLock
: lockWithSystemLock // ignore: cast_nullable_to_non_nullable
as bool,
) as $Val);
}
}
/// @nodoc
abstract class _$$LockPreferenceImplCopyWith<$Res>
implements $LockPreferenceCopyWith<$Res> {
factory _$$LockPreferenceImplCopyWith(_$LockPreferenceImpl value,
$Res Function(_$LockPreferenceImpl) then) =
__$$LockPreferenceImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{int inactivityLockSecs,
bool lockWhenSwitching,
bool lockWithSystemLock});
}
/// @nodoc
class __$$LockPreferenceImplCopyWithImpl<$Res>
extends _$LockPreferenceCopyWithImpl<$Res, _$LockPreferenceImpl>
implements _$$LockPreferenceImplCopyWith<$Res> {
__$$LockPreferenceImplCopyWithImpl(
_$LockPreferenceImpl _value, $Res Function(_$LockPreferenceImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@override
$Res call({
Object? inactivityLockSecs = null,
Object? lockWhenSwitching = null,
Object? lockWithSystemLock = null,
}) {
return _then(_$LockPreferenceImpl(
inactivityLockSecs: null == inactivityLockSecs
? _value.inactivityLockSecs
: inactivityLockSecs // ignore: cast_nullable_to_non_nullable
as int,
lockWhenSwitching: null == lockWhenSwitching
? _value.lockWhenSwitching
: lockWhenSwitching // ignore: cast_nullable_to_non_nullable
as bool,
lockWithSystemLock: null == lockWithSystemLock
? _value.lockWithSystemLock
: lockWithSystemLock // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
/// @nodoc
@JsonSerializable()
class _$LockPreferenceImpl implements _LockPreference {
const _$LockPreferenceImpl(
{required this.inactivityLockSecs,
required this.lockWhenSwitching,
required this.lockWithSystemLock});
factory _$LockPreferenceImpl.fromJson(Map<String, dynamic> json) =>
_$$LockPreferenceImplFromJson(json);
@override
final int inactivityLockSecs;
@override
final bool lockWhenSwitching;
@override
final bool lockWithSystemLock;
@override
String toString() {
return 'LockPreference(inactivityLockSecs: $inactivityLockSecs, lockWhenSwitching: $lockWhenSwitching, lockWithSystemLock: $lockWithSystemLock)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$LockPreferenceImpl &&
(identical(other.inactivityLockSecs, inactivityLockSecs) ||
other.inactivityLockSecs == inactivityLockSecs) &&
(identical(other.lockWhenSwitching, lockWhenSwitching) ||
other.lockWhenSwitching == lockWhenSwitching) &&
(identical(other.lockWithSystemLock, lockWithSystemLock) ||
other.lockWithSystemLock == lockWithSystemLock));
}
@JsonKey(ignore: true)
@override
int get hashCode => Object.hash(
runtimeType, inactivityLockSecs, lockWhenSwitching, lockWithSystemLock);
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$LockPreferenceImplCopyWith<_$LockPreferenceImpl> get copyWith =>
__$$LockPreferenceImplCopyWithImpl<_$LockPreferenceImpl>(
this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$LockPreferenceImplToJson(
this,
);
}
}
abstract class _LockPreference implements LockPreference {
const factory _LockPreference(
{required final int inactivityLockSecs,
required final bool lockWhenSwitching,
required final bool lockWithSystemLock}) = _$LockPreferenceImpl;
factory _LockPreference.fromJson(Map<String, dynamic> json) =
_$LockPreferenceImpl.fromJson;
@override
int get inactivityLockSecs;
@override
bool get lockWhenSwitching;
@override
bool get lockWithSystemLock;
@override
@JsonKey(ignore: true)
_$$LockPreferenceImplCopyWith<_$LockPreferenceImpl> get copyWith =>
throw _privateConstructorUsedError;
}
Preferences _$PreferencesFromJson(Map<String, dynamic> json) {
return _Preferences.fromJson(json);
}
/// @nodoc
mixin _$Preferences {
ThemePreferences get themePreferences => throw _privateConstructorUsedError;
LanguagePreference get language => throw _privateConstructorUsedError;
LockPreference get locking => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$PreferencesCopyWith<Preferences> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $PreferencesCopyWith<$Res> {
factory $PreferencesCopyWith(
Preferences value, $Res Function(Preferences) then) =
_$PreferencesCopyWithImpl<$Res, Preferences>;
@useResult
$Res call(
{ThemePreferences themePreferences,
LanguagePreference language,
LockPreference locking});
$ThemePreferencesCopyWith<$Res> get themePreferences;
$LockPreferenceCopyWith<$Res> get locking;
}
/// @nodoc
class _$PreferencesCopyWithImpl<$Res, $Val extends Preferences>
implements $PreferencesCopyWith<$Res> {
_$PreferencesCopyWithImpl(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? themePreferences = null,
Object? language = null,
Object? locking = null,
}) {
return _then(_value.copyWith(
themePreferences: null == themePreferences
? _value.themePreferences
: themePreferences // ignore: cast_nullable_to_non_nullable
as ThemePreferences,
language: null == language
? _value.language
: language // ignore: cast_nullable_to_non_nullable
as LanguagePreference,
locking: null == locking
? _value.locking
: locking // ignore: cast_nullable_to_non_nullable
as LockPreference,
) 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);
});
}
@override
@pragma('vm:prefer-inline')
$LockPreferenceCopyWith<$Res> get locking {
return $LockPreferenceCopyWith<$Res>(_value.locking, (value) {
return _then(_value.copyWith(locking: value) as $Val);
});
}
}
/// @nodoc
abstract class _$$PreferencesImplCopyWith<$Res>
implements $PreferencesCopyWith<$Res> {
factory _$$PreferencesImplCopyWith(
_$PreferencesImpl value, $Res Function(_$PreferencesImpl) then) =
__$$PreferencesImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{ThemePreferences themePreferences,
LanguagePreference language,
LockPreference locking});
@override
$ThemePreferencesCopyWith<$Res> get themePreferences;
@override
$LockPreferenceCopyWith<$Res> get locking;
}
/// @nodoc
class __$$PreferencesImplCopyWithImpl<$Res>
extends _$PreferencesCopyWithImpl<$Res, _$PreferencesImpl>
implements _$$PreferencesImplCopyWith<$Res> {
__$$PreferencesImplCopyWithImpl(
_$PreferencesImpl _value, $Res Function(_$PreferencesImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@override
$Res call({
Object? themePreferences = null,
Object? language = null,
Object? locking = null,
}) {
return _then(_$PreferencesImpl(
themePreferences: null == themePreferences
? _value.themePreferences
: themePreferences // ignore: cast_nullable_to_non_nullable
as ThemePreferences,
language: null == language
? _value.language
: language // ignore: cast_nullable_to_non_nullable
as LanguagePreference,
locking: null == locking
? _value.locking
: locking // ignore: cast_nullable_to_non_nullable
as LockPreference,
));
}
}
/// @nodoc
@JsonSerializable()
class _$PreferencesImpl implements _Preferences {
const _$PreferencesImpl(
{required this.themePreferences,
required this.language,
required this.locking});
factory _$PreferencesImpl.fromJson(Map<String, dynamic> json) =>
_$$PreferencesImplFromJson(json);
@override
final ThemePreferences themePreferences;
@override
final LanguagePreference language;
@override
final LockPreference locking;
@override
String toString() {
return 'Preferences(themePreferences: $themePreferences, language: $language, locking: $locking)';
}
@override
bool operator ==(Object other) {
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));
}
@JsonKey(ignore: true)
@override
int get hashCode =>
Object.hash(runtimeType, themePreferences, language, locking);
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$PreferencesImplCopyWith<_$PreferencesImpl> get copyWith =>
__$$PreferencesImplCopyWithImpl<_$PreferencesImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$PreferencesImplToJson(
this,
);
}
}
abstract class _Preferences implements Preferences {
const factory _Preferences(
{required final ThemePreferences themePreferences,
required final LanguagePreference language,
required final LockPreference locking}) = _$PreferencesImpl;
factory _Preferences.fromJson(Map<String, dynamic> json) =
_$PreferencesImpl.fromJson;
@override
ThemePreferences get themePreferences;
@override
LanguagePreference get language;
@override
LockPreference get locking;
@override
@JsonKey(ignore: true)
_$$PreferencesImplCopyWith<_$PreferencesImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View file

@ -0,0 +1,36 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'preferences.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$LockPreferenceImpl _$$LockPreferenceImplFromJson(Map<String, dynamic> json) =>
_$LockPreferenceImpl(
inactivityLockSecs: json['inactivity_lock_secs'] as int,
lockWhenSwitching: json['lock_when_switching'] as bool,
lockWithSystemLock: json['lock_with_system_lock'] as bool,
);
Map<String, dynamic> _$$LockPreferenceImplToJson(
_$LockPreferenceImpl instance) =>
<String, dynamic>{
'inactivity_lock_secs': instance.inactivityLockSecs,
'lock_when_switching': instance.lockWhenSwitching,
'lock_with_system_lock': instance.lockWithSystemLock,
};
_$PreferencesImpl _$$PreferencesImplFromJson(Map<String, dynamic> json) =>
_$PreferencesImpl(
themePreferences: ThemePreferences.fromJson(json['theme_preferences']),
language: LanguagePreference.fromJson(json['language']),
locking: LockPreference.fromJson(json['locking']),
);
Map<String, dynamic> _$$PreferencesImplToJson(_$PreferencesImpl instance) =>
<String, dynamic>{
'theme_preferences': instance.themePreferences.toJson(),
'language': instance.language.toJson(),
'locking': instance.locking.toJson(),
};

View file

@ -0,0 +1,9 @@
import '../tools/tools.dart';
import 'settings.dart';
xxx convert to non-asyncvalue based wrapper since there's always a default here
class PreferencesCubit extends StreamWrapperCubit<Preferences> {
PreferencesCubit(PreferencesRepository repository)
: super(repository.stream, defaultState: repository.value);
}

View file

@ -0,0 +1,34 @@
import 'dart:async';
import 'package:shared_preferences/shared_preferences.dart';
import '../tools/tools.dart';
import 'models/models.dart';
class PreferencesRepository {
PreferencesRepository._();
late final SharedPreferencesValue<Preferences> _data;
Preferences get value => _data.requireValue;
Stream<Preferences> get stream => _data.stream;
//////////////////////////////////////////////////////////////
/// Singleton initialization
static PreferencesRepository instance = PreferencesRepository._();
Future<void> init() async {
final sharedPreferences = await SharedPreferences.getInstance();
_data = SharedPreferencesValue<Preferences>(
sharedPreferences: sharedPreferences,
keyName: 'preferences',
valueFromJson: (obj) =>
obj != null ? Preferences.fromJson(obj) : Preferences.defaults,
valueToJson: (val) => val.toJson());
await _data.get();
}
Future<void> set(Preferences value) => _data.set(value);
Future<Preferences> get() => _data.get();
}

View file

@ -0,0 +1,4 @@
export 'models/models.dart';
export 'preferences_cubit.dart';
export 'preferences_repository.dart';
export 'settings_page.dart';

View file

@ -0,0 +1,129 @@
import 'package:animated_theme_switcher/animated_theme_switcher.dart';
import 'package:awesome_extensions/awesome_extensions.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:flutter_translate/flutter_translate.dart';
import 'package:veilid_support/veilid_support.dart';
import '../layout/default_app_bar.dart';
import '../theme/theme.dart';
import '../veilid_processor/veilid_processor.dart';
import 'preferences_cubit.dart';
import 'preferences_repository.dart';
import 'settings.dart';
class SettingsPage extends StatefulWidget {
const SettingsPage({super.key});
@override
SettingsPageState createState() => SettingsPageState();
}
class SettingsPageState extends State<SettingsPage> {
final _formKey = GlobalKey<FormBuilderState>();
static const String formFieldTheme = 'theme';
static const String formFieldBrightness = 'brightness';
@override
void initState() {
super.initState();
}
List<DropdownMenuItem<dynamic>> _getThemeDropdownItems() {
const colorPrefs = ColorPreference.values;
final colorNames = {
ColorPreference.scarlet: translate('themes.scarlet'),
ColorPreference.vapor: translate('themes.vapor'),
ColorPreference.babydoll: translate('themes.babydoll'),
ColorPreference.gold: translate('themes.gold'),
ColorPreference.garden: translate('themes.garden'),
ColorPreference.forest: translate('themes.forest'),
ColorPreference.arctic: translate('themes.arctic'),
ColorPreference.lapis: translate('themes.lapis'),
ColorPreference.eggplant: translate('themes.eggplant'),
ColorPreference.lime: translate('themes.lime'),
ColorPreference.grim: translate('themes.grim'),
ColorPreference.contrast: translate('themes.contrast')
};
return colorPrefs
.map((e) => DropdownMenuItem(value: e, child: Text(colorNames[e]!)))
.toList();
}
List<DropdownMenuItem<dynamic>> _getBrightnessDropdownItems() {
const brightnessPrefs = BrightnessPreference.values;
final brightnessNames = {
BrightnessPreference.system: translate('brightness.system'),
BrightnessPreference.light: translate('brightness.light'),
BrightnessPreference.dark: translate('brightness.dark')
};
return brightnessPrefs
.map(
(e) => DropdownMenuItem(value: e, child: Text(brightnessNames[e]!)))
.toList();
}
@override
Widget build(BuildContext context) => BlocBuilder<PreferencesCubit,
AsyncValue<Preferences>>(
builder: (context, state) => ThemeSwitchingArea(
child: Scaffold(
// resizeToAvoidBottomInset: false,
appBar: DefaultAppBar(
title: Text(translate('settings_page.titlebar')),
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () => context.pop<void>(),
),
actions: <Widget>[
const SignalStrengthMeterWidget().paddingLTRB(16, 0, 16, 0),
]),
body: FormBuilder(
key: _formKey,
child: ListView(
children: [
ThemeSwitcher.withTheme(
builder: (_, switcher, theme) => FormBuilderDropdown(
name: formFieldTheme,
decoration: InputDecoration(
label:
Text(translate('settings_page.color_theme'))),
items: _getThemeDropdownItems(),
initialValue: themePreferences.colorPreference,
onChanged: (value) async {
final newPrefs = themePreferences.copyWith(
colorPreference: value as ColorPreference);
await themeService.save(newPrefs);
switcher.changeTheme(
theme: themeService.get(newPrefs));
ref.invalidate(themeServiceProvider);
setState(() {});
})),
ThemeSwitcher.withTheme(
builder: (_, switcher, theme) => FormBuilderDropdown(
name: formFieldBrightness,
decoration: InputDecoration(
label: Text(
translate('settings_page.brightness_mode'))),
items: _getBrightnessDropdownItems(),
initialValue: themePreferences.brightnessPreference,
onChanged: (value) async {
final newPrefs = themePreferences.copyWith(
brightnessPreference:
value as BrightnessPreference);
await themeService.save(newPrefs);
switcher.changeTheme(
theme: themeService.get(newPrefs));
ref.invalidate(themeServiceProvider);
setState(() {});
})),
],
),
).paddingSymmetric(horizontal: 24, vertical: 8),
)));
}