Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom theme #1251

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 22 additions & 7 deletions lib/src/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import 'package:lichess_mobile/src/navigation.dart';
import 'package:lichess_mobile/src/network/connectivity.dart';
import 'package:lichess_mobile/src/network/http.dart';
import 'package:lichess_mobile/src/network/socket.dart';
import 'package:lichess_mobile/src/styles/lichess_colors.dart';
import 'package:lichess_mobile/src/styles/styles.dart';
import 'package:lichess_mobile/src/utils/screen.dart';

Expand Down Expand Up @@ -147,13 +148,27 @@ class _AppState extends ConsumerState<Application> {
final dynamicColorScheme =
brightness == Brightness.light ? fixedLightScheme : fixedDarkScheme;

final colorScheme =
generalPrefs.systemColors && dynamicColorScheme != null
? dynamicColorScheme
: ColorScheme.fromSeed(
seedColor: boardTheme.colors.darkSquare,
brightness: brightness,
);
ColorScheme colorScheme;
if (generalPrefs.customThemeEnabled) {
if (generalPrefs.customThemeSeed != null) {
colorScheme = ColorScheme.fromSeed(
seedColor: generalPrefs.customThemeSeed!,
brightness: brightness,
);
} else if (dynamicColorScheme != null) {
colorScheme = dynamicColorScheme;
} else {
colorScheme = ColorScheme.fromSeed(
seedColor: LichessColors.primary[500]!,
brightness: brightness,
);
}
} else {
colorScheme = ColorScheme.fromSeed(
seedColor: boardTheme.colors.darkSquare,
brightness: brightness,
);
}

final cupertinoThemeData = CupertinoThemeData(
primaryColor: colorScheme.primary,
Expand Down
26 changes: 18 additions & 8 deletions lib/src/init.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import 'package:lichess_mobile/src/db/secure_storage.dart';
import 'package:lichess_mobile/src/model/notifications/notification_service.dart';
import 'package:lichess_mobile/src/model/notifications/notifications.dart';
import 'package:lichess_mobile/src/model/settings/board_preferences.dart';
import 'package:lichess_mobile/src/model/settings/general_preferences.dart';
import 'package:lichess_mobile/src/model/settings/preferences_storage.dart';
import 'package:lichess_mobile/src/utils/chessboard.dart';
import 'package:lichess_mobile/src/utils/color_palette.dart';
Expand Down Expand Up @@ -95,14 +96,23 @@ Future<void> androidDisplayInitialization(WidgetsBinding widgetsBinding) async {
await DynamicColorPlugin.getCorePalette().then((value) {
setCorePalette(value);

if (getCorePalette() != null &&
prefs.getString(PrefCategory.board.storageKey) == null) {
prefs.setString(
PrefCategory.board.storageKey,
jsonEncode(
BoardPrefs.defaults.copyWith(boardTheme: BoardTheme.system),
),
);
if (getCorePalette() != null) {
if (prefs.getString(PrefCategory.general.storageKey) == null) {
prefs.setString(
PrefCategory.general.storageKey,
jsonEncode(
GeneralPrefs.defaults.copyWith(customThemeEnabled: true),
),
);
}
if (prefs.getString(PrefCategory.board.storageKey) == null) {
prefs.setString(
PrefCategory.board.storageKey,
jsonEncode(
BoardPrefs.defaults.copyWith(boardTheme: BoardTheme.system),
),
);
}
}
});
} catch (e) {
Expand Down
43 changes: 15 additions & 28 deletions lib/src/model/settings/general_preferences.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import 'dart:ui' show Locale;
import 'dart:ui' show Color, Locale;

import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:lichess_mobile/src/model/settings/board_preferences.dart';
import 'package:lichess_mobile/src/model/settings/preferences_storage.dart';
import 'package:lichess_mobile/src/utils/json.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'general_preferences.freezed.dart';
Expand All @@ -28,7 +29,7 @@ class GeneralPreferences extends _$GeneralPreferences
return fetch();
}

Future<void> setThemeMode(BackgroundThemeMode themeMode) {
Future<void> setBackgroundThemeMode(BackgroundThemeMode themeMode) {
return save(state.copyWith(themeMode: themeMode));
}

Expand All @@ -48,9 +49,9 @@ class GeneralPreferences extends _$GeneralPreferences
return save(state.copyWith(masterVolume: volume));
}

Future<void> toggleSystemColors() async {
await save(state.copyWith(systemColors: !state.systemColors));
if (state.systemColors == false) {
Future<void> toggleCustomTheme() async {
await save(state.copyWith(customThemeEnabled: !state.customThemeEnabled));
if (state.customThemeEnabled == false) {
final boardTheme = ref.read(boardPreferencesProvider).boardTheme;
if (boardTheme == BoardTheme.system) {
await ref
Expand All @@ -63,27 +64,10 @@ class GeneralPreferences extends _$GeneralPreferences
.setBoardTheme(BoardTheme.system);
}
}
}

Map<String, dynamic>? _localeToJson(Locale? locale) {
return locale != null
? {
'languageCode': locale.languageCode,
'countryCode': locale.countryCode,
'scriptCode': locale.scriptCode,
}
: null;
}

Locale? _localeFromJson(Map<String, dynamic>? json) {
if (json == null) {
return null;
Future<void> setCustomThemeSeed(Color? color) {
return save(state.copyWith(customThemeSeed: color));
}
return Locale.fromSubtags(
languageCode: json['languageCode'] as String,
countryCode: json['countryCode'] as String?,
scriptCode: json['scriptCode'] as String?,
);
}

@Freezed(fromJson: true, toJson: true)
Expand All @@ -99,19 +83,22 @@ class GeneralPrefs with _$GeneralPrefs implements Serializable {
required SoundTheme soundTheme,
@JsonKey(defaultValue: 0.8) required double masterVolume,

/// Should enable system color palette (android 12+ only)
@JsonKey(defaultValue: true) required bool systemColors,
/// Should enable custom theme
@JsonKey(defaultValue: false) required bool customThemeEnabled,

/// Custom theme seed color
@ColorConverter() Color? customThemeSeed,

/// Locale to use in the app, use system locale if null
@JsonKey(toJson: _localeToJson, fromJson: _localeFromJson) Locale? locale,
@LocaleConverter() Locale? locale,
}) = _GeneralPrefs;

static const defaults = GeneralPrefs(
themeMode: BackgroundThemeMode.system,
isSoundEnabled: true,
soundTheme: SoundTheme.standard,
masterVolume: 0.8,
systemColors: true,
customThemeEnabled: true,
);

factory GeneralPrefs.fromJson(Map<String, dynamic> json) {
Expand Down
6 changes: 6 additions & 0 deletions lib/src/utils/color_palette.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ void setCorePalette(CorePalette? palette) {
}
}

Color? getCorePalettePrimary() {
return _corePalette?.primary != null
? Color(_corePalette!.primary.get(50))
: null;
}

/// Get the core palette if available (android 12+ only).
CorePalette? getCorePalette() {
return _corePalette;
Expand Down
58 changes: 58 additions & 0 deletions lib/src/utils/json.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,64 @@
import 'dart:ui' show Color, Locale;

import 'package:deep_pick/deep_pick.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:lichess_mobile/src/model/common/uci.dart';

class LocaleConverter implements JsonConverter<Locale?, Map<String, dynamic>?> {
const LocaleConverter();

@override
Locale? fromJson(Map<String, dynamic>? json) {
if (json == null) {
return null;
}
return Locale.fromSubtags(
languageCode: json['languageCode'] as String,
countryCode: json['countryCode'] as String?,
scriptCode: json['scriptCode'] as String?,
);
}

@override
Map<String, dynamic>? toJson(Locale? locale) {
return locale != null
? {
'languageCode': locale.languageCode,
'countryCode': locale.countryCode,
'scriptCode': locale.scriptCode,
}
: null;
}
}

class ColorConverter implements JsonConverter<Color?, Map<String, dynamic>?> {
const ColorConverter();

@override
Color? fromJson(Map<String, dynamic>? json) {
return json != null
? Color.from(
alpha: json['a'] as double,
red: json['r'] as double,
green: json['g'] as double,
blue: json['b'] as double,
)
: null;
}

@override
Map<String, dynamic>? toJson(Color? color) {
return color != null
? {
'a': color.a,
'r': color.r,
'g': color.g,
'b': color.b,
}
: null;
}
}

extension UciExtension on Pick {
/// Matches a UciCharPair from a string.
UciCharPair asUciCharPairOrThrow() {
Expand Down
3 changes: 0 additions & 3 deletions lib/src/view/puzzle/puzzle_tab_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -423,9 +423,6 @@ class _PuzzleMenuListTile extends StatelessWidget {
leading: Icon(
icon,
size: Styles.mainListTileIconSize,
color: Theme.of(context).platform == TargetPlatform.iOS
? CupertinoTheme.of(context).primaryColor
: Theme.of(context).colorScheme.primary,
),
title: Text(title, style: Styles.mainListTileTitle),
subtitle: Text(subtitle, maxLines: 3),
Expand Down
1 change: 0 additions & 1 deletion lib/src/view/settings/account_preferences_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,6 @@ class _AccountPreferencesScreenState
subtitle: Text(
context.l10n.preferencesExplainShowPlayerRatings,
maxLines: 5,
textAlign: TextAlign.justify,
),
value: data.showRatings.value,
onChanged: isLoading
Expand Down
2 changes: 1 addition & 1 deletion lib/src/view/settings/app_background_mode_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class _Body extends ConsumerWidget {

void onChanged(BackgroundThemeMode? value) => ref
.read(generalPreferencesProvider.notifier)
.setThemeMode(value ?? BackgroundThemeMode.system);
.setBackgroundThemeMode(value ?? BackgroundThemeMode.system);

return SafeArea(
child: ListView(
Expand Down
16 changes: 3 additions & 13 deletions lib/src/view/settings/board_theme_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:lichess_mobile/src/model/settings/board_preferences.dart';
import 'package:lichess_mobile/src/model/settings/general_preferences.dart';
import 'package:lichess_mobile/src/utils/color_palette.dart';
import 'package:lichess_mobile/src/utils/l10n_context.dart';
import 'package:lichess_mobile/src/utils/system.dart';
import 'package:lichess_mobile/src/widgets/list.dart';
import 'package:lichess_mobile/src/widgets/platform.dart';

Expand Down Expand Up @@ -40,20 +39,11 @@ class _Body extends ConsumerWidget {
final boardTheme =
ref.watch(boardPreferencesProvider.select((p) => p.boardTheme));

final hasSystemColors =
ref.watch(generalPreferencesProvider.select((p) => p.systemColors));

final androidVersion = ref.watch(androidVersionProvider).whenOrNull(
data: (v) => v,
);
final hasSystemColors = getCorePalette() != null;

final choices = BoardTheme.values
.where(
(t) =>
t != BoardTheme.system ||
(hasSystemColors &&
androidVersion != null &&
androidVersion.sdkInt >= 31),
(t) => t != BoardTheme.system || hasSystemColors,
)
.toList();

Expand Down
4 changes: 3 additions & 1 deletion lib/src/view/settings/settings_tab_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,9 @@ class _Body extends ConsumerWidget {
Text(AppBackgroundModeScreen.themeTitle(context, t)),
onSelectedItemChanged: (BackgroundThemeMode? value) => ref
.read(generalPreferencesProvider.notifier)
.setThemeMode(value ?? BackgroundThemeMode.system),
.setBackgroundThemeMode(
value ?? BackgroundThemeMode.system,
),
);
} else {
pushPlatformRoute(
Expand Down
Loading
Loading