feat: sincronizar cor do tema via account data da conta Matrix
A cor escolhida no Appearance passa a ser guardada em im.fluffychat.account_config (primary_color), em vez de apenas nas preferencias locais do dispositivo. - ApplicationAccountConfig ganha primaryColor, preservado em updateApplicationAccountConfig e no novo setApplicationAccountColor - ThemeController restaura a cor da conta activa, aguarda accountDataLoading e reage a alteracoes remotas via onSync - A cor final e derivada de _accountPrimaryColor ?? _localPrimaryColor, evitando corrida entre as preferencias locais e o sync - Matrix notifica a troca de conta activa em initMatrix, setActiveClient e logout, corrigindo o indice do cliente activo - Apagar o wallpaper preserva a cor; testes de serializacao do modelo
This commit is contained in:
@@ -10,6 +10,7 @@ import 'package:tatchat/utils/file_selector.dart';
|
|||||||
import 'package:tatchat/widgets/future_loading_dialog.dart';
|
import 'package:tatchat/widgets/future_loading_dialog.dart';
|
||||||
import 'package:tatchat/widgets/theme_builder.dart';
|
import 'package:tatchat/widgets/theme_builder.dart';
|
||||||
import 'package:material_ui/material_ui.dart';
|
import 'package:material_ui/material_ui.dart';
|
||||||
|
import 'package:matrix/matrix.dart';
|
||||||
|
|
||||||
import '../../widgets/matrix.dart';
|
import '../../widgets/matrix.dart';
|
||||||
import 'settings_style_view.dart';
|
import 'settings_style_view.dart';
|
||||||
@@ -22,11 +23,18 @@ class SettingsStyle extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class SettingsStyleController extends State<SettingsStyle> {
|
class SettingsStyleController extends State<SettingsStyle> {
|
||||||
void setChatColor(Color? color) {
|
Future<void> setChatColor(Color? color) async {
|
||||||
|
final client = Matrix.of(context).client;
|
||||||
|
final themeController = ThemeController.of(context);
|
||||||
AppSettings.colorSchemeSeedInt.setItem(
|
AppSettings.colorSchemeSeedInt.setItem(
|
||||||
color?.toARGB32() ?? AppSettings.colorSchemeSeedInt.defaultValue,
|
color?.toARGB32() ?? AppSettings.colorSchemeSeedInt.defaultValue,
|
||||||
);
|
);
|
||||||
ThemeController.of(context).setPrimaryColor(color);
|
await themeController.setPrimaryColor(color);
|
||||||
|
try {
|
||||||
|
await client.setApplicationAccountColor(color?.toARGB32());
|
||||||
|
} catch (e, s) {
|
||||||
|
Logs().e('Unable to save chat color', e, s);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> setWallpaper() async {
|
Future<void> setWallpaper() async {
|
||||||
@@ -105,12 +113,19 @@ class SettingsStyleController extends State<SettingsStyle> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void deleteChatWallpaper() => showFutureLoadingDialog(
|
void deleteChatWallpaper() {
|
||||||
|
final client = Matrix.of(context).client;
|
||||||
|
showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => Matrix.of(context).client.setApplicationAccountConfig(
|
future: () => client.setApplicationAccountConfig(
|
||||||
const ApplicationAccountConfig(wallpaperUrl: null, wallpaperBlur: null),
|
ApplicationAccountConfig(
|
||||||
|
wallpaperUrl: null,
|
||||||
|
wallpaperBlur: null,
|
||||||
|
primaryColor: client.applicationAccountConfig.primaryColor,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
|||||||
@@ -27,20 +27,35 @@ extension ApplicationAccountConfigExtension on Client {
|
|||||||
wallpaperOpacity:
|
wallpaperOpacity:
|
||||||
config.wallpaperOpacity ?? currentConfig.wallpaperOpacity,
|
config.wallpaperOpacity ?? currentConfig.wallpaperOpacity,
|
||||||
wallpaperBlur: config.wallpaperBlur ?? currentConfig.wallpaperBlur,
|
wallpaperBlur: config.wallpaperBlur ?? currentConfig.wallpaperBlur,
|
||||||
|
primaryColor: config.primaryColor ?? currentConfig.primaryColor,
|
||||||
).toJson(),
|
).toJson(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> setApplicationAccountColor(int? primaryColor) {
|
||||||
|
final currentConfig = applicationAccountConfig;
|
||||||
|
return setApplicationAccountConfig(
|
||||||
|
ApplicationAccountConfig(
|
||||||
|
wallpaperUrl: currentConfig.wallpaperUrl,
|
||||||
|
wallpaperOpacity: currentConfig.wallpaperOpacity,
|
||||||
|
wallpaperBlur: currentConfig.wallpaperBlur,
|
||||||
|
primaryColor: primaryColor,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ApplicationAccountConfig {
|
class ApplicationAccountConfig {
|
||||||
final Uri? wallpaperUrl;
|
final Uri? wallpaperUrl;
|
||||||
final double? wallpaperOpacity;
|
final double? wallpaperOpacity;
|
||||||
final double? wallpaperBlur;
|
final double? wallpaperBlur;
|
||||||
|
final int? primaryColor;
|
||||||
|
|
||||||
const ApplicationAccountConfig({
|
const ApplicationAccountConfig({
|
||||||
this.wallpaperUrl,
|
this.wallpaperUrl,
|
||||||
this.wallpaperOpacity,
|
this.wallpaperOpacity,
|
||||||
this.wallpaperBlur,
|
this.wallpaperBlur,
|
||||||
|
this.primaryColor,
|
||||||
});
|
});
|
||||||
|
|
||||||
static double _sanitizedOpacity(double? opacity) {
|
static double _sanitizedOpacity(double? opacity) {
|
||||||
@@ -58,11 +73,13 @@ class ApplicationAccountConfig {
|
|||||||
json.tryGet<double>('wallpaper_opacity'),
|
json.tryGet<double>('wallpaper_opacity'),
|
||||||
),
|
),
|
||||||
wallpaperBlur: json.tryGet<double>('wallpaper_blur'),
|
wallpaperBlur: json.tryGet<double>('wallpaper_blur'),
|
||||||
|
primaryColor: json.tryGet<int>('primary_color'),
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
'wallpaper_url': wallpaperUrl?.toString(),
|
'wallpaper_url': wallpaperUrl?.toString(),
|
||||||
'wallpaper_opacity': wallpaperOpacity,
|
'wallpaper_opacity': wallpaperOpacity,
|
||||||
'wallpaper_blur': wallpaperBlur,
|
'wallpaper_blur': wallpaperBlur,
|
||||||
|
'primary_color': primaryColor,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,10 +44,13 @@ class Matrix extends StatefulWidget {
|
|||||||
|
|
||||||
final SharedPreferences store;
|
final SharedPreferences store;
|
||||||
|
|
||||||
|
final ValueChanged<Client?>? onActiveClientChanged;
|
||||||
|
|
||||||
const Matrix({
|
const Matrix({
|
||||||
this.child,
|
this.child,
|
||||||
required this.clients,
|
required this.clients,
|
||||||
required this.store,
|
required this.store,
|
||||||
|
this.onActiveClientChanged,
|
||||||
this.queryParameters,
|
this.queryParameters,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
@@ -89,11 +92,21 @@ class MatrixState extends State<Matrix> {
|
|||||||
final i = widget.clients.indexWhere((c) => c == cl);
|
final i = widget.clients.indexWhere((c) => c == cl);
|
||||||
if (i != -1) {
|
if (i != -1) {
|
||||||
_activeClient = i;
|
_activeClient = i;
|
||||||
|
_notifyActiveClientChanged();
|
||||||
} else {
|
} else {
|
||||||
Logs().w('Tried to set an unknown client ${cl!.userID} as active');
|
Logs().w('Tried to set an unknown client ${cl!.userID} as active');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _notifyActiveClientChanged() {
|
||||||
|
final callback = widget.onActiveClientChanged;
|
||||||
|
if (callback == null) return;
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
if (!mounted) return;
|
||||||
|
callback(widget.clients.isEmpty ? null : client);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
List<Client?>? get currentBundle {
|
List<Client?>? get currentBundle {
|
||||||
if (!hasComplexBundles) {
|
if (!hasComplexBundles) {
|
||||||
return List.from(widget.clients);
|
return List.from(widget.clients);
|
||||||
@@ -256,19 +269,27 @@ class MatrixState extends State<Matrix> {
|
|||||||
.where((state) => state == LoginState.loggedOut)
|
.where((state) => state == LoginState.loggedOut)
|
||||||
.listen((_) {
|
.listen((_) {
|
||||||
final loggedInWithMultipleClients = widget.clients.length > 1;
|
final loggedInWithMultipleClients = widget.clients.length > 1;
|
||||||
|
final activeClient = client;
|
||||||
|
final removedIndex = widget.clients.indexOf(c);
|
||||||
|
final wasActive = removedIndex != -1 && activeClient == c;
|
||||||
|
|
||||||
_cancelSubs(c.clientName);
|
_cancelSubs(c.clientName);
|
||||||
widget.clients.remove(c);
|
widget.clients.remove(c);
|
||||||
ClientManager.removeClientNameFromStore(c.clientName, store);
|
ClientManager.removeClientNameFromStore(c.clientName, store);
|
||||||
InitWithRestoreExtension.deleteSessionBackup(name);
|
InitWithRestoreExtension.deleteSessionBackup(name);
|
||||||
|
if (removedIndex != -1 && _activeClient > removedIndex) {
|
||||||
|
_activeClient--;
|
||||||
|
}
|
||||||
|
if (wasActive) {
|
||||||
|
_activeClient = -1;
|
||||||
|
}
|
||||||
|
if (removedIndex != -1) {
|
||||||
|
_notifyActiveClientChanged();
|
||||||
|
}
|
||||||
|
|
||||||
if (loggedInWithMultipleClients) {
|
if (loggedInWithMultipleClients) {
|
||||||
final snackbarContext =
|
final snackbarContext =
|
||||||
TatuChatApp
|
TatuChatApp.router.routerDelegate.navigatorKey.currentContext ??
|
||||||
.router
|
|
||||||
.routerDelegate
|
|
||||||
.navigatorKey
|
|
||||||
.currentContext ??
|
|
||||||
context;
|
context;
|
||||||
|
|
||||||
if (!snackbarContext.mounted) return;
|
if (!snackbarContext.mounted) return;
|
||||||
@@ -349,6 +370,7 @@ class MatrixState extends State<Matrix> {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
_notifyActiveClientChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||||
|
|||||||
@@ -65,10 +65,15 @@ class TatuChatApp extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ThemeBuilder(
|
return ThemeBuilder(
|
||||||
builder: (context, themeMode, primaryColor) => MaterialApp.router(
|
builder: (context, themeMode, primaryColor, themeController) =>
|
||||||
|
MaterialApp.router(
|
||||||
title: AppSettings.applicationName.value,
|
title: AppSettings.applicationName.value,
|
||||||
themeMode: themeMode,
|
themeMode: themeMode,
|
||||||
theme: FluffyThemes.buildTheme(context, Brightness.light, primaryColor),
|
theme: FluffyThemes.buildTheme(
|
||||||
|
context,
|
||||||
|
Brightness.light,
|
||||||
|
primaryColor,
|
||||||
|
),
|
||||||
darkTheme: FluffyThemes.buildTheme(
|
darkTheme: FluffyThemes.buildTheme(
|
||||||
context,
|
context,
|
||||||
Brightness.dark,
|
Brightness.dark,
|
||||||
@@ -91,6 +96,9 @@ class TatuChatApp extends StatelessWidget {
|
|||||||
child: Matrix(
|
child: Matrix(
|
||||||
clients: clients,
|
clients: clients,
|
||||||
store: store,
|
store: store,
|
||||||
|
onActiveClientChanged: (client) {
|
||||||
|
themeController.setAccountClient(client);
|
||||||
|
},
|
||||||
child: CallOverlay(
|
child: CallOverlay(
|
||||||
child: testWidget ?? child ?? const SizedBox.shrink(),
|
child: testWidget ?? child ?? const SizedBox.shrink(),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -3,10 +3,14 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
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/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';
|
||||||
|
import 'package:matrix/matrix.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
@@ -15,6 +19,7 @@ class ThemeBuilder extends StatefulWidget {
|
|||||||
BuildContext context,
|
BuildContext context,
|
||||||
ThemeMode themeMode,
|
ThemeMode themeMode,
|
||||||
Color? primaryColor,
|
Color? primaryColor,
|
||||||
|
ThemeController controller,
|
||||||
)
|
)
|
||||||
builder;
|
builder;
|
||||||
|
|
||||||
@@ -36,6 +41,11 @@ class ThemeController extends State<ThemeBuilder> {
|
|||||||
SharedPreferences? _sharedPreferences;
|
SharedPreferences? _sharedPreferences;
|
||||||
ThemeMode? _themeMode;
|
ThemeMode? _themeMode;
|
||||||
Color? _primaryColor;
|
Color? _primaryColor;
|
||||||
|
Color? _localPrimaryColor;
|
||||||
|
Color? _accountPrimaryColor;
|
||||||
|
Client? _accountClient;
|
||||||
|
StreamSubscription<SyncUpdate>? _accountClientSubscription;
|
||||||
|
int _accountClientGeneration = 0;
|
||||||
|
|
||||||
ThemeMode get themeMode => _themeMode ?? ThemeMode.system;
|
ThemeMode get themeMode => _themeMode ?? ThemeMode.system;
|
||||||
|
|
||||||
@@ -44,6 +54,15 @@ class ThemeController extends State<ThemeBuilder> {
|
|||||||
static ThemeController of(BuildContext context) =>
|
static ThemeController of(BuildContext context) =>
|
||||||
Provider.of<ThemeController>(context, listen: false);
|
Provider.of<ThemeController>(context, listen: false);
|
||||||
|
|
||||||
|
void _updatePrimaryColor() {
|
||||||
|
if (!mounted) return;
|
||||||
|
final newColor = _accountPrimaryColor ?? _localPrimaryColor;
|
||||||
|
if (newColor == _primaryColor) return;
|
||||||
|
setState(() {
|
||||||
|
_primaryColor = newColor;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _loadData(_) async {
|
Future<void> _loadData(_) async {
|
||||||
final preferences = _sharedPreferences ??=
|
final preferences = _sharedPreferences ??=
|
||||||
await SharedPreferences.getInstance();
|
await SharedPreferences.getInstance();
|
||||||
@@ -51,12 +70,14 @@ class ThemeController extends State<ThemeBuilder> {
|
|||||||
final rawThemeMode = preferences.getString(widget.themeModeSettingsKey);
|
final rawThemeMode = preferences.getString(widget.themeModeSettingsKey);
|
||||||
final rawColor = preferences.getInt(widget.primaryColorSettingsKey);
|
final rawColor = preferences.getInt(widget.primaryColorSettingsKey);
|
||||||
|
|
||||||
|
if (!mounted) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
_themeMode = ThemeMode.values.singleWhereOrNull(
|
_themeMode = ThemeMode.values.singleWhereOrNull(
|
||||||
(value) => value.name == rawThemeMode,
|
(value) => value.name == rawThemeMode,
|
||||||
);
|
);
|
||||||
_primaryColor = rawColor == null ? null : Color(rawColor);
|
_localPrimaryColor = rawColor == null ? null : Color(rawColor);
|
||||||
});
|
});
|
||||||
|
_updatePrimaryColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> setThemeMode(ThemeMode newThemeMode) async {
|
Future<void> setThemeMode(ThemeMode newThemeMode) async {
|
||||||
@@ -69,6 +90,12 @@ class ThemeController extends State<ThemeBuilder> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> setPrimaryColor(Color? newPrimaryColor) async {
|
Future<void> setPrimaryColor(Color? newPrimaryColor) async {
|
||||||
|
if (!mounted) return;
|
||||||
|
setState(() {
|
||||||
|
_localPrimaryColor = newPrimaryColor;
|
||||||
|
_accountPrimaryColor = newPrimaryColor;
|
||||||
|
_primaryColor = newPrimaryColor;
|
||||||
|
});
|
||||||
final preferences = _sharedPreferences ??=
|
final preferences = _sharedPreferences ??=
|
||||||
await SharedPreferences.getInstance();
|
await SharedPreferences.getInstance();
|
||||||
if (newPrimaryColor == null) {
|
if (newPrimaryColor == null) {
|
||||||
@@ -79,9 +106,56 @@ class ThemeController extends State<ThemeBuilder> {
|
|||||||
newPrimaryColor.hexValue,
|
newPrimaryColor.hexValue,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
setState(() {
|
}
|
||||||
_primaryColor = newPrimaryColor;
|
|
||||||
});
|
void setAccountClient(Client? client) {
|
||||||
|
if (!mounted) return;
|
||||||
|
final generation = ++_accountClientGeneration;
|
||||||
|
_accountClientSubscription?.cancel();
|
||||||
|
_accountClientSubscription = null;
|
||||||
|
_accountClient = client;
|
||||||
|
_accountPrimaryColor = null;
|
||||||
|
_updatePrimaryColor();
|
||||||
|
if (client == null) return;
|
||||||
|
|
||||||
|
unawaited(_loadAccountClientColor(client, generation));
|
||||||
|
_accountClientSubscription = client.onSync.stream
|
||||||
|
.where(
|
||||||
|
(sync) =>
|
||||||
|
sync.accountData?.any(
|
||||||
|
(accountData) =>
|
||||||
|
accountData.type ==
|
||||||
|
ApplicationAccountConfigExtension.accountDataKey,
|
||||||
|
) ??
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.listen((_) => _applyAccountClientColor(client, generation));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _loadAccountClientColor(Client client, int generation) async {
|
||||||
|
final accountDataLoading = client.accountDataLoading;
|
||||||
|
if (accountDataLoading != null) {
|
||||||
|
try {
|
||||||
|
await accountDataLoading;
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
if (!mounted ||
|
||||||
|
generation != _accountClientGeneration ||
|
||||||
|
!identical(_accountClient, client)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_applyAccountClientColor(client, generation);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _applyAccountClientColor(Client client, int generation) {
|
||||||
|
if (!mounted ||
|
||||||
|
generation != _accountClientGeneration ||
|
||||||
|
!identical(_accountClient, client)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final primaryColor = client.applicationAccountConfig.primaryColor;
|
||||||
|
_accountPrimaryColor = primaryColor == null ? null : Color(primaryColor);
|
||||||
|
_updatePrimaryColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -95,9 +169,19 @@ class ThemeController extends State<ThemeBuilder> {
|
|||||||
return Provider(
|
return Provider(
|
||||||
create: (_) => this,
|
create: (_) => this,
|
||||||
child: DynamicColorBuilder(
|
child: DynamicColorBuilder(
|
||||||
builder: (light, _) =>
|
builder: (light, _) => widget.builder(
|
||||||
widget.builder(context, themeMode, primaryColor ?? light?.primary),
|
context,
|
||||||
|
themeMode,
|
||||||
|
primaryColor ?? light?.primary,
|
||||||
|
this,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_accountClientSubscription?.cancel();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
67
test/account_config_test.dart
Normal file
67
test/account_config_test.dart
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2026 TatuChat
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:tatchat/utils/account_config.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('ApplicationAccountConfig', () {
|
||||||
|
test('Parses primary color from account data', () {
|
||||||
|
final config = ApplicationAccountConfig.fromJson({
|
||||||
|
'primary_color': 0xFF6750A4,
|
||||||
|
});
|
||||||
|
expect(config.primaryColor, 0xFF6750A4);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Ignores primary color of unexpected type', () {
|
||||||
|
expect(
|
||||||
|
ApplicationAccountConfig.fromJson({
|
||||||
|
'primary_color': '4286369748',
|
||||||
|
}).primaryColor,
|
||||||
|
isNull,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
ApplicationAccountConfig.fromJson({'primary_color': 1.5}).primaryColor,
|
||||||
|
isNull,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Defaults primary color to null when absent', () {
|
||||||
|
expect(ApplicationAccountConfig.fromJson({}).primaryColor, isNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Serializes primary color', () {
|
||||||
|
const config = ApplicationAccountConfig(primaryColor: 0xFF00D084);
|
||||||
|
expect(config.toJson()['primary_color'], 0xFF00D084);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Serializes missing primary color as null', () {
|
||||||
|
const config = ApplicationAccountConfig(wallpaperOpacity: 0.5);
|
||||||
|
expect(config.toJson()['primary_color'], isNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Round trips through json', () {
|
||||||
|
final config = ApplicationAccountConfig(
|
||||||
|
wallpaperUrl: Uri.parse('mxc://example.invalid/wallpaper'),
|
||||||
|
wallpaperOpacity: 0.3,
|
||||||
|
wallpaperBlur: 4,
|
||||||
|
primaryColor: 0xFF6750A4,
|
||||||
|
);
|
||||||
|
final parsed = ApplicationAccountConfig.fromJson(config.toJson());
|
||||||
|
expect(parsed.wallpaperUrl, config.wallpaperUrl);
|
||||||
|
expect(parsed.wallpaperOpacity, config.wallpaperOpacity);
|
||||||
|
expect(parsed.wallpaperBlur, config.wallpaperBlur);
|
||||||
|
expect(parsed.primaryColor, config.primaryColor);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Keeps existing wallpaper defaults when parsing color only', () {
|
||||||
|
final config = ApplicationAccountConfig.fromJson({
|
||||||
|
'primary_color': 0xFF6750A4,
|
||||||
|
});
|
||||||
|
expect(config.wallpaperUrl, isNull);
|
||||||
|
expect(config.wallpaperOpacity, 1);
|
||||||
|
expect(config.wallpaperBlur, isNull);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user