feat: usar a cor escolhida tal e qual e opcao de suavizar
- TatuPalette.scheme aceita softenPrimary e passa a usar a seed como primary, onPrimary por contraste e surfaceTint, em vez da paleta tonal do Material 3 - nova opcao AppSettings.softenThemeColor (default false, ou seja cor exacta) - interruptor 'Suavizar cor do tema' em Configuracoes > Aparencia, com l10n en/pt - a opcao e sincronizada via account data (soften_theme_color) e mantida localmente como fallback, tal como a cor do tema - setApplicationAccountColor e updateApplicationAccountConfig preservam o novo campo - um unico sync de account data actualiza cor e opcao - testes de serializacao e round-trip para o novo campo
This commit is contained in:
@@ -59,6 +59,11 @@ enum AppSettings<T> {
|
|||||||
),
|
),
|
||||||
// colorSchemeSeed stored as ARGB int
|
// colorSchemeSeed stored as ARGB int
|
||||||
colorSchemeSeedInt<int>('chat.fluffy.color_scheme_seed', 0xFF00D084),
|
colorSchemeSeedInt<int>('chat.fluffy.color_scheme_seed', 0xFF00D084),
|
||||||
|
|
||||||
|
/// When false the chosen seed color is used verbatim as the theme primary
|
||||||
|
/// color. When true the Material 3 tonal palette is used instead, which
|
||||||
|
/// softens it.
|
||||||
|
softenThemeColor<bool>('chat.fluffy.soften_theme_color', false),
|
||||||
emojiSuggestionLocale<String>('emoji_suggestion_locale', ''),
|
emojiSuggestionLocale<String>('emoji_suggestion_locale', ''),
|
||||||
enableSoftLogout<bool>('chat.fluffy.enable_soft_logout', true),
|
enableSoftLogout<bool>('chat.fluffy.enable_soft_logout', true),
|
||||||
enableMatrixNativeOIDC<bool>('chat.fluffy.enable_matrix_native_oidc', true),
|
enableMatrixNativeOIDC<bool>('chat.fluffy.enable_matrix_native_oidc', true),
|
||||||
|
|||||||
@@ -97,7 +97,14 @@ abstract class TatuPalette {
|
|||||||
/// When a [seed] is provided (e.g. a color chosen by the user or a system
|
/// When a [seed] is provided (e.g. a color chosen by the user or a system
|
||||||
/// dynamic color), its tonal values are used for the primary family while
|
/// dynamic color), its tonal values are used for the primary family while
|
||||||
/// the brand surfaces, accent and error colors are preserved.
|
/// the brand surfaces, accent and error colors are preserved.
|
||||||
static ColorScheme scheme(Brightness brightness, [Color? seed]) {
|
///
|
||||||
|
/// Unless [softenPrimary] is set, the seed is used verbatim as the primary
|
||||||
|
/// color, because the Material 3 tonal palette noticeably dulls it down.
|
||||||
|
static ColorScheme scheme(
|
||||||
|
Brightness brightness, [
|
||||||
|
Color? seed,
|
||||||
|
bool softenPrimary = false,
|
||||||
|
]) {
|
||||||
final isDark = brightness == Brightness.dark;
|
final isDark = brightness == Brightness.dark;
|
||||||
final p = isDark ? _dark : _light;
|
final p = isDark ? _dark : _light;
|
||||||
final base = ColorScheme.fromSeed(
|
final base = ColorScheme.fromSeed(
|
||||||
@@ -106,12 +113,17 @@ abstract class TatuPalette {
|
|||||||
dynamicSchemeVariant: DynamicSchemeVariant.rainbow,
|
dynamicSchemeVariant: DynamicSchemeVariant.rainbow,
|
||||||
);
|
);
|
||||||
final useBrandPrimary = seed == null;
|
final useBrandPrimary = seed == null;
|
||||||
|
// Only force the exact color when the caller did not ask for the soft
|
||||||
|
// Material 3 palette. `null` here means "keep the value from [base]".
|
||||||
|
final exactPrimary = !useBrandPrimary && !softenPrimary ? seed : null;
|
||||||
return base.copyWith(
|
return base.copyWith(
|
||||||
primary: useBrandPrimary ? p.primary : null,
|
primary: useBrandPrimary ? p.primary : exactPrimary,
|
||||||
onPrimary: useBrandPrimary ? p.onPrimary : null,
|
onPrimary: useBrandPrimary
|
||||||
|
? p.onPrimary
|
||||||
|
: (exactPrimary == null ? null : _contrastOn(exactPrimary)),
|
||||||
primaryContainer: useBrandPrimary ? p.primaryContainer : null,
|
primaryContainer: useBrandPrimary ? p.primaryContainer : null,
|
||||||
onPrimaryContainer: useBrandPrimary ? p.onPrimaryContainer : null,
|
onPrimaryContainer: useBrandPrimary ? p.onPrimaryContainer : null,
|
||||||
surfaceTint: useBrandPrimary ? p.primary : null,
|
surfaceTint: useBrandPrimary ? p.primary : exactPrimary,
|
||||||
secondary: p.secondary,
|
secondary: p.secondary,
|
||||||
onSecondary: p.onSecondary,
|
onSecondary: p.onSecondary,
|
||||||
secondaryContainer: p.secondaryContainer,
|
secondaryContainer: p.secondaryContainer,
|
||||||
@@ -141,6 +153,12 @@ abstract class TatuPalette {
|
|||||||
surfaceBright: p.surfaceBright,
|
surfaceBright: p.surfaceBright,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Picks the readable foreground for an arbitrary background color.
|
||||||
|
static Color _contrastOn(Color background) =>
|
||||||
|
ThemeData.estimateBrightnessForColor(background) == Brightness.dark
|
||||||
|
? Colors.white
|
||||||
|
: Colors.black;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _Scheme {
|
class _Scheme {
|
||||||
@@ -209,4 +227,4 @@ class _Scheme {
|
|||||||
final Color surfaceHighest;
|
final Color surfaceHighest;
|
||||||
final Color surfaceDim;
|
final Color surfaceDim;
|
||||||
final Color surfaceBright;
|
final Color surfaceBright;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,8 +47,9 @@ abstract class FluffyThemes {
|
|||||||
BuildContext context,
|
BuildContext context,
|
||||||
Brightness brightness, [
|
Brightness brightness, [
|
||||||
Color? seed,
|
Color? seed,
|
||||||
|
bool softenPrimary = false,
|
||||||
]) {
|
]) {
|
||||||
final colorScheme = TatuPalette.scheme(brightness, seed);
|
final colorScheme = TatuPalette.scheme(brightness, seed, softenPrimary);
|
||||||
final isColumnMode = FluffyThemes.isColumnMode(context);
|
final isColumnMode = FluffyThemes.isColumnMode(context);
|
||||||
final dividerColor = brightness == Brightness.dark
|
final dividerColor = brightness == Brightness.dark
|
||||||
? colorScheme.surfaceContainerHighest
|
? colorScheme.surfaceContainerHighest
|
||||||
|
|||||||
@@ -3029,5 +3029,15 @@
|
|||||||
"@uninstallAppConfirm": {
|
"@uninstallAppConfirm": {
|
||||||
"type": "String",
|
"type": "String",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"softenThemeColor": "Soften theme color",
|
||||||
|
"@softenThemeColor": {
|
||||||
|
"type": "String",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"softenThemeColorSubtitle": "Use a soft Material 3 palette derived from the selected color instead of the exact color.",
|
||||||
|
"@softenThemeColorSubtitle": {
|
||||||
|
"type": "String",
|
||||||
|
"placeholders": {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2043,5 +2043,7 @@
|
|||||||
"type": "String",
|
"type": "String",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
"areYouSureDeletePost": "Eliminar"
|
"areYouSureDeletePost": "Eliminar",
|
||||||
|
"softenThemeColor": "Suavizar cor do tema",
|
||||||
|
"softenThemeColorSubtitle": "Usa uma paleta suave do Material 3 derivada da cor escolhida em vez da cor exata."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -130,6 +130,13 @@ class SettingsStyleController extends State<SettingsStyle> {
|
|||||||
ThemeMode get currentTheme => ThemeController.of(context).themeMode;
|
ThemeMode get currentTheme => ThemeController.of(context).themeMode;
|
||||||
Color? get currentColor => ThemeController.of(context).primaryColor;
|
Color? get currentColor => ThemeController.of(context).primaryColor;
|
||||||
|
|
||||||
|
bool get softenThemeColor => ThemeController.of(context).softenPrimaryColor;
|
||||||
|
|
||||||
|
Future<void> setSoftenThemeColor(bool soften) async {
|
||||||
|
await ThemeController.of(context).setSoftenPrimaryColor(soften);
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
void switchTheme(ThemeMode? newTheme) {
|
void switchTheme(ThemeMode? newTheme) {
|
||||||
if (newTheme == null) return;
|
if (newTheme == null) return;
|
||||||
switch (newTheme) {
|
switch (newTheme) {
|
||||||
|
|||||||
@@ -136,6 +136,13 @@ class SettingsStyleView extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
SwitchListTile(
|
||||||
|
secondary: const Icon(Icons.palette_outlined),
|
||||||
|
title: Text(L10n.of(context).softenThemeColor),
|
||||||
|
subtitle: Text(L10n.of(context).softenThemeColorSubtitle),
|
||||||
|
value: controller.softenThemeColor,
|
||||||
|
onChanged: controller.setSoftenThemeColor,
|
||||||
|
),
|
||||||
Divider(color: theme.dividerColor),
|
Divider(color: theme.dividerColor),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(
|
title: Text(
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ extension ApplicationAccountConfigExtension on Client {
|
|||||||
config.wallpaperOpacity ?? currentConfig.wallpaperOpacity,
|
config.wallpaperOpacity ?? currentConfig.wallpaperOpacity,
|
||||||
wallpaperBlur: config.wallpaperBlur ?? currentConfig.wallpaperBlur,
|
wallpaperBlur: config.wallpaperBlur ?? currentConfig.wallpaperBlur,
|
||||||
primaryColor: config.primaryColor ?? currentConfig.primaryColor,
|
primaryColor: config.primaryColor ?? currentConfig.primaryColor,
|
||||||
|
softenThemeColor:
|
||||||
|
config.softenThemeColor ?? currentConfig.softenThemeColor,
|
||||||
).toJson(),
|
).toJson(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -40,6 +42,20 @@ extension ApplicationAccountConfigExtension on Client {
|
|||||||
wallpaperOpacity: currentConfig.wallpaperOpacity,
|
wallpaperOpacity: currentConfig.wallpaperOpacity,
|
||||||
wallpaperBlur: currentConfig.wallpaperBlur,
|
wallpaperBlur: currentConfig.wallpaperBlur,
|
||||||
primaryColor: primaryColor,
|
primaryColor: primaryColor,
|
||||||
|
softenThemeColor: currentConfig.softenThemeColor,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> setApplicationAccountSoftenThemeColor(bool soften) {
|
||||||
|
final currentConfig = applicationAccountConfig;
|
||||||
|
return setApplicationAccountConfig(
|
||||||
|
ApplicationAccountConfig(
|
||||||
|
wallpaperUrl: currentConfig.wallpaperUrl,
|
||||||
|
wallpaperOpacity: currentConfig.wallpaperOpacity,
|
||||||
|
wallpaperBlur: currentConfig.wallpaperBlur,
|
||||||
|
primaryColor: currentConfig.primaryColor,
|
||||||
|
softenThemeColor: soften,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -50,12 +66,14 @@ class ApplicationAccountConfig {
|
|||||||
final double? wallpaperOpacity;
|
final double? wallpaperOpacity;
|
||||||
final double? wallpaperBlur;
|
final double? wallpaperBlur;
|
||||||
final int? primaryColor;
|
final int? primaryColor;
|
||||||
|
final bool? softenThemeColor;
|
||||||
|
|
||||||
const ApplicationAccountConfig({
|
const ApplicationAccountConfig({
|
||||||
this.wallpaperUrl,
|
this.wallpaperUrl,
|
||||||
this.wallpaperOpacity,
|
this.wallpaperOpacity,
|
||||||
this.wallpaperBlur,
|
this.wallpaperBlur,
|
||||||
this.primaryColor,
|
this.primaryColor,
|
||||||
|
this.softenThemeColor,
|
||||||
});
|
});
|
||||||
|
|
||||||
static double _sanitizedOpacity(double? opacity) {
|
static double _sanitizedOpacity(double? opacity) {
|
||||||
@@ -74,6 +92,7 @@ class ApplicationAccountConfig {
|
|||||||
),
|
),
|
||||||
wallpaperBlur: json.tryGet<double>('wallpaper_blur'),
|
wallpaperBlur: json.tryGet<double>('wallpaper_blur'),
|
||||||
primaryColor: json.tryGet<int>('primary_color'),
|
primaryColor: json.tryGet<int>('primary_color'),
|
||||||
|
softenThemeColor: json.tryGet<bool>('soften_theme_color'),
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
@@ -81,5 +100,6 @@ class ApplicationAccountConfig {
|
|||||||
'wallpaper_opacity': wallpaperOpacity,
|
'wallpaper_opacity': wallpaperOpacity,
|
||||||
'wallpaper_blur': wallpaperBlur,
|
'wallpaper_blur': wallpaperBlur,
|
||||||
'primary_color': primaryColor,
|
'primary_color': primaryColor,
|
||||||
|
'soften_theme_color': softenThemeColor,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,11 +73,13 @@ class TatuChatApp extends StatelessWidget {
|
|||||||
context,
|
context,
|
||||||
Brightness.light,
|
Brightness.light,
|
||||||
primaryColor,
|
primaryColor,
|
||||||
|
themeController.softenPrimaryColor,
|
||||||
),
|
),
|
||||||
darkTheme: FluffyThemes.buildTheme(
|
darkTheme: FluffyThemes.buildTheme(
|
||||||
context,
|
context,
|
||||||
Brightness.dark,
|
Brightness.dark,
|
||||||
primaryColor,
|
primaryColor,
|
||||||
|
themeController.softenPrimaryColor,
|
||||||
),
|
),
|
||||||
scrollBehavior: CustomScrollBehavior(),
|
scrollBehavior: CustomScrollBehavior(),
|
||||||
localizationsDelegates: [
|
localizationsDelegates: [
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:dynamic_color/dynamic_color.dart';
|
import 'package:dynamic_color/dynamic_color.dart';
|
||||||
|
import 'package:tatchat/config/setting_keys.dart';
|
||||||
import 'package:tatchat/utils/account_config.dart';
|
import 'package:tatchat/utils/account_config.dart';
|
||||||
import 'package:tatchat/utils/color_value.dart';
|
import 'package:tatchat/utils/color_value.dart';
|
||||||
import 'package:material_ui/material_ui.dart';
|
import 'package:material_ui/material_ui.dart';
|
||||||
@@ -43,6 +44,9 @@ class ThemeController extends State<ThemeBuilder> {
|
|||||||
Color? _primaryColor;
|
Color? _primaryColor;
|
||||||
Color? _localPrimaryColor;
|
Color? _localPrimaryColor;
|
||||||
Color? _accountPrimaryColor;
|
Color? _accountPrimaryColor;
|
||||||
|
bool _softenPrimaryColor = false;
|
||||||
|
bool _localSoftenPrimaryColor = false;
|
||||||
|
bool? _accountSoftenPrimaryColor;
|
||||||
Client? _accountClient;
|
Client? _accountClient;
|
||||||
StreamSubscription<SyncUpdate>? _accountClientSubscription;
|
StreamSubscription<SyncUpdate>? _accountClientSubscription;
|
||||||
int _accountClientGeneration = 0;
|
int _accountClientGeneration = 0;
|
||||||
@@ -51,6 +55,8 @@ class ThemeController extends State<ThemeBuilder> {
|
|||||||
|
|
||||||
Color? get primaryColor => _primaryColor;
|
Color? get primaryColor => _primaryColor;
|
||||||
|
|
||||||
|
bool get softenPrimaryColor => _softenPrimaryColor;
|
||||||
|
|
||||||
static ThemeController of(BuildContext context) =>
|
static ThemeController of(BuildContext context) =>
|
||||||
Provider.of<ThemeController>(context, listen: false);
|
Provider.of<ThemeController>(context, listen: false);
|
||||||
|
|
||||||
@@ -63,6 +69,15 @@ class ThemeController extends State<ThemeBuilder> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _updateSoftenPrimaryColor() {
|
||||||
|
if (!mounted) return;
|
||||||
|
final newValue = _accountSoftenPrimaryColor ?? _localSoftenPrimaryColor;
|
||||||
|
if (newValue == _softenPrimaryColor) return;
|
||||||
|
setState(() {
|
||||||
|
_softenPrimaryColor = newValue;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _loadData(_) async {
|
Future<void> _loadData(_) async {
|
||||||
final preferences = _sharedPreferences ??=
|
final preferences = _sharedPreferences ??=
|
||||||
await SharedPreferences.getInstance();
|
await SharedPreferences.getInstance();
|
||||||
@@ -76,8 +91,10 @@ class ThemeController extends State<ThemeBuilder> {
|
|||||||
(value) => value.name == rawThemeMode,
|
(value) => value.name == rawThemeMode,
|
||||||
);
|
);
|
||||||
_localPrimaryColor = rawColor == null ? null : Color(rawColor);
|
_localPrimaryColor = rawColor == null ? null : Color(rawColor);
|
||||||
|
_localSoftenPrimaryColor = AppSettings.softenThemeColor.value;
|
||||||
});
|
});
|
||||||
_updatePrimaryColor();
|
_updatePrimaryColor();
|
||||||
|
_updateSoftenPrimaryColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> setThemeMode(ThemeMode newThemeMode) async {
|
Future<void> setThemeMode(ThemeMode newThemeMode) async {
|
||||||
@@ -108,6 +125,23 @@ class ThemeController extends State<ThemeBuilder> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> setSoftenPrimaryColor(bool soften) async {
|
||||||
|
await AppSettings.softenThemeColor.setItem(soften);
|
||||||
|
if (!mounted) return;
|
||||||
|
setState(() {
|
||||||
|
_localSoftenPrimaryColor = soften;
|
||||||
|
_accountSoftenPrimaryColor = soften;
|
||||||
|
_softenPrimaryColor = soften;
|
||||||
|
});
|
||||||
|
final client = _accountClient;
|
||||||
|
if (client == null) return;
|
||||||
|
try {
|
||||||
|
await client.setApplicationAccountSoftenThemeColor(soften);
|
||||||
|
} catch (err) {
|
||||||
|
Logs().e('TatuChat: failed to sync soften theme color', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void setAccountClient(Client? client) {
|
void setAccountClient(Client? client) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
final generation = ++_accountClientGeneration;
|
final generation = ++_accountClientGeneration;
|
||||||
@@ -115,10 +149,12 @@ class ThemeController extends State<ThemeBuilder> {
|
|||||||
_accountClientSubscription = null;
|
_accountClientSubscription = null;
|
||||||
_accountClient = client;
|
_accountClient = client;
|
||||||
_accountPrimaryColor = null;
|
_accountPrimaryColor = null;
|
||||||
|
_accountSoftenPrimaryColor = null;
|
||||||
_updatePrimaryColor();
|
_updatePrimaryColor();
|
||||||
|
_updateSoftenPrimaryColor();
|
||||||
if (client == null) return;
|
if (client == null) return;
|
||||||
|
|
||||||
unawaited(_loadAccountClientColor(client, generation));
|
unawaited(_loadAccountClientConfig(client, generation));
|
||||||
_accountClientSubscription = client.onSync.stream
|
_accountClientSubscription = client.onSync.stream
|
||||||
.where(
|
.where(
|
||||||
(sync) =>
|
(sync) =>
|
||||||
@@ -129,10 +165,10 @@ class ThemeController extends State<ThemeBuilder> {
|
|||||||
) ??
|
) ??
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
.listen((_) => _applyAccountClientColor(client, generation));
|
.listen((_) => _applyAccountClientConfig(client, generation));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _loadAccountClientColor(Client client, int generation) async {
|
Future<void> _loadAccountClientConfig(Client client, int generation) async {
|
||||||
final accountDataLoading = client.accountDataLoading;
|
final accountDataLoading = client.accountDataLoading;
|
||||||
if (accountDataLoading != null) {
|
if (accountDataLoading != null) {
|
||||||
try {
|
try {
|
||||||
@@ -144,18 +180,21 @@ class ThemeController extends State<ThemeBuilder> {
|
|||||||
!identical(_accountClient, client)) {
|
!identical(_accountClient, client)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_applyAccountClientColor(client, generation);
|
_applyAccountClientConfig(client, generation);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _applyAccountClientColor(Client client, int generation) {
|
void _applyAccountClientConfig(Client client, int generation) {
|
||||||
if (!mounted ||
|
if (!mounted ||
|
||||||
generation != _accountClientGeneration ||
|
generation != _accountClientGeneration ||
|
||||||
!identical(_accountClient, client)) {
|
!identical(_accountClient, client)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final primaryColor = client.applicationAccountConfig.primaryColor;
|
final config = client.applicationAccountConfig;
|
||||||
|
final primaryColor = config.primaryColor;
|
||||||
_accountPrimaryColor = primaryColor == null ? null : Color(primaryColor);
|
_accountPrimaryColor = primaryColor == null ? null : Color(primaryColor);
|
||||||
|
_accountSoftenPrimaryColor = config.softenThemeColor;
|
||||||
_updatePrimaryColor();
|
_updatePrimaryColor();
|
||||||
|
_updateSoftenPrimaryColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -47,12 +47,14 @@ void main() {
|
|||||||
wallpaperOpacity: 0.3,
|
wallpaperOpacity: 0.3,
|
||||||
wallpaperBlur: 4,
|
wallpaperBlur: 4,
|
||||||
primaryColor: 0xFF6750A4,
|
primaryColor: 0xFF6750A4,
|
||||||
|
softenThemeColor: true,
|
||||||
);
|
);
|
||||||
final parsed = ApplicationAccountConfig.fromJson(config.toJson());
|
final parsed = ApplicationAccountConfig.fromJson(config.toJson());
|
||||||
expect(parsed.wallpaperUrl, config.wallpaperUrl);
|
expect(parsed.wallpaperUrl, config.wallpaperUrl);
|
||||||
expect(parsed.wallpaperOpacity, config.wallpaperOpacity);
|
expect(parsed.wallpaperOpacity, config.wallpaperOpacity);
|
||||||
expect(parsed.wallpaperBlur, config.wallpaperBlur);
|
expect(parsed.wallpaperBlur, config.wallpaperBlur);
|
||||||
expect(parsed.primaryColor, config.primaryColor);
|
expect(parsed.primaryColor, config.primaryColor);
|
||||||
|
expect(parsed.softenThemeColor, config.softenThemeColor);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Keeps existing wallpaper defaults when parsing color only', () {
|
test('Keeps existing wallpaper defaults when parsing color only', () {
|
||||||
@@ -63,5 +65,50 @@ void main() {
|
|||||||
expect(config.wallpaperOpacity, 1);
|
expect(config.wallpaperOpacity, 1);
|
||||||
expect(config.wallpaperBlur, isNull);
|
expect(config.wallpaperBlur, isNull);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Parses soften theme color from account data', () {
|
||||||
|
expect(
|
||||||
|
ApplicationAccountConfig.fromJson({
|
||||||
|
'soften_theme_color': true,
|
||||||
|
}).softenThemeColor,
|
||||||
|
isTrue,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
ApplicationAccountConfig.fromJson({
|
||||||
|
'soften_theme_color': false,
|
||||||
|
}).softenThemeColor,
|
||||||
|
isFalse,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Ignores soften theme color of unexpected type', () {
|
||||||
|
expect(
|
||||||
|
ApplicationAccountConfig.fromJson({
|
||||||
|
'soften_theme_color': 'yes',
|
||||||
|
}).softenThemeColor,
|
||||||
|
isNull,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Defaults soften theme color to null when absent', () {
|
||||||
|
expect(ApplicationAccountConfig.fromJson({}).softenThemeColor, isNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Serializes soften theme color', () {
|
||||||
|
const config = ApplicationAccountConfig(softenThemeColor: true);
|
||||||
|
expect(config.toJson()['soften_theme_color'], true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Serializes missing soften theme color as null', () {
|
||||||
|
const config = ApplicationAccountConfig(primaryColor: 0xFF00D084);
|
||||||
|
expect(config.toJson()['soften_theme_color'], isNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Round trips soften theme color through json', () {
|
||||||
|
final parsed = ApplicationAccountConfig.fromJson(
|
||||||
|
const ApplicationAccountConfig(softenThemeColor: true).toJson(),
|
||||||
|
);
|
||||||
|
expect(parsed.softenThemeColor, isTrue);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user