Skip to content

Commit 3ba352f

Browse files
committed
settings
Signed-off-by: Zixuan James Li <[email protected]>
1 parent d5780b5 commit 3ba352f

12 files changed

+234
-24
lines changed

assets/l10n/app_en.arb

+16
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,22 @@
653653
"@messageIsMovedLabel": {
654654
"description": "Label for a moved message. (Use ALL CAPS for cased alphabets: Latin, Greek, Cyrillic, etc.)"
655655
},
656+
"themeSettingLabel": "THEME",
657+
"@themeSettingLabel": {
658+
"description": "Label for theme setting. (Use ALL CAPS for cased alphabets: Latin, Greek, Cyrillic, etc.)"
659+
},
660+
"themeSettingDarkLabel": "Dark",
661+
"@themeSettingDarkLabel": {
662+
"description": "Label for dark theme setting."
663+
},
664+
"themeSettingLightLabel": "Light",
665+
"@themeSettingLightLabel": {
666+
"description": "Label for light theme setting."
667+
},
668+
"themeSettingSystemLabel": "System",
669+
"@themeSettingSystemLabel": {
670+
"description": "Label for following system theme setting."
671+
},
656672
"pollWidgetQuestionMissing": "No question.",
657673
"@pollWidgetQuestionMissing": {
658674
"description": "Text to display for a poll when the question is missing"

lib/generated/l10n/zulip_localizations.dart

+24
Original file line numberDiff line numberDiff line change
@@ -975,6 +975,30 @@ abstract class ZulipLocalizations {
975975
/// **'MOVED'**
976976
String get messageIsMovedLabel;
977977

978+
/// Label for theme setting. (Use ALL CAPS for cased alphabets: Latin, Greek, Cyrillic, etc.)
979+
///
980+
/// In en, this message translates to:
981+
/// **'THEME'**
982+
String get themeSettingLabel;
983+
984+
/// Label for dark theme setting.
985+
///
986+
/// In en, this message translates to:
987+
/// **'Dark'**
988+
String get themeSettingDarkLabel;
989+
990+
/// Label for light theme setting.
991+
///
992+
/// In en, this message translates to:
993+
/// **'Light'**
994+
String get themeSettingLightLabel;
995+
996+
/// Label for following system theme setting.
997+
///
998+
/// In en, this message translates to:
999+
/// **'System'**
1000+
String get themeSettingSystemLabel;
1001+
9781002
/// Text to display for a poll when the question is missing
9791003
///
9801004
/// In en, this message translates to:

lib/generated/l10n/zulip_localizations_ar.dart

+12
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,18 @@ class ZulipLocalizationsAr extends ZulipLocalizations {
517517
@override
518518
String get messageIsMovedLabel => 'MOVED';
519519

520+
@override
521+
String get themeSettingLabel => 'THEME';
522+
523+
@override
524+
String get themeSettingDarkLabel => 'Dark';
525+
526+
@override
527+
String get themeSettingLightLabel => 'Light';
528+
529+
@override
530+
String get themeSettingSystemLabel => 'System';
531+
520532
@override
521533
String get pollWidgetQuestionMissing => 'No question.';
522534

lib/generated/l10n/zulip_localizations_en.dart

+12
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,18 @@ class ZulipLocalizationsEn extends ZulipLocalizations {
517517
@override
518518
String get messageIsMovedLabel => 'MOVED';
519519

520+
@override
521+
String get themeSettingLabel => 'THEME';
522+
523+
@override
524+
String get themeSettingDarkLabel => 'Dark';
525+
526+
@override
527+
String get themeSettingLightLabel => 'Light';
528+
529+
@override
530+
String get themeSettingSystemLabel => 'System';
531+
520532
@override
521533
String get pollWidgetQuestionMissing => 'No question.';
522534

lib/generated/l10n/zulip_localizations_ja.dart

+12
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,18 @@ class ZulipLocalizationsJa extends ZulipLocalizations {
517517
@override
518518
String get messageIsMovedLabel => 'MOVED';
519519

520+
@override
521+
String get themeSettingLabel => 'THEME';
522+
523+
@override
524+
String get themeSettingDarkLabel => 'Dark';
525+
526+
@override
527+
String get themeSettingLightLabel => 'Light';
528+
529+
@override
530+
String get themeSettingSystemLabel => 'System';
531+
520532
@override
521533
String get pollWidgetQuestionMissing => 'No question.';
522534

lib/generated/l10n/zulip_localizations_nb.dart

+12
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,18 @@ class ZulipLocalizationsNb extends ZulipLocalizations {
517517
@override
518518
String get messageIsMovedLabel => 'MOVED';
519519

520+
@override
521+
String get themeSettingLabel => 'THEME';
522+
523+
@override
524+
String get themeSettingDarkLabel => 'Dark';
525+
526+
@override
527+
String get themeSettingLightLabel => 'Light';
528+
529+
@override
530+
String get themeSettingSystemLabel => 'System';
531+
520532
@override
521533
String get pollWidgetQuestionMissing => 'No question.';
522534

lib/generated/l10n/zulip_localizations_pl.dart

+12
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,18 @@ class ZulipLocalizationsPl extends ZulipLocalizations {
517517
@override
518518
String get messageIsMovedLabel => 'PRZENIESIONO';
519519

520+
@override
521+
String get themeSettingLabel => 'THEME';
522+
523+
@override
524+
String get themeSettingDarkLabel => 'Dark';
525+
526+
@override
527+
String get themeSettingLightLabel => 'Light';
528+
529+
@override
530+
String get themeSettingSystemLabel => 'System';
531+
520532
@override
521533
String get pollWidgetQuestionMissing => 'Brak pytania.';
522534

lib/generated/l10n/zulip_localizations_ru.dart

+12
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,18 @@ class ZulipLocalizationsRu extends ZulipLocalizations {
517517
@override
518518
String get messageIsMovedLabel => 'ПЕРЕМЕЩЕНО';
519519

520+
@override
521+
String get themeSettingLabel => 'THEME';
522+
523+
@override
524+
String get themeSettingDarkLabel => 'Dark';
525+
526+
@override
527+
String get themeSettingLightLabel => 'Light';
528+
529+
@override
530+
String get themeSettingSystemLabel => 'System';
531+
520532
@override
521533
String get pollWidgetQuestionMissing => 'Нет вопроса.';
522534

lib/generated/l10n/zulip_localizations_sk.dart

+12
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,18 @@ class ZulipLocalizationsSk extends ZulipLocalizations {
517517
@override
518518
String get messageIsMovedLabel => 'PRESUNUTÉ';
519519

520+
@override
521+
String get themeSettingLabel => 'THEME';
522+
523+
@override
524+
String get themeSettingDarkLabel => 'Dark';
525+
526+
@override
527+
String get themeSettingLightLabel => 'Light';
528+
529+
@override
530+
String get themeSettingSystemLabel => 'System';
531+
520532
@override
521533
String get pollWidgetQuestionMissing => 'Bez otázky.';
522534

lib/model/settings.dart

+11-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import '../generated/l10n/zulip_localizations.dart';
2+
13
/// The visual theme of the app.
24
///
35
/// See [zulipThemeData] for how themes are determined.
@@ -12,6 +14,13 @@ enum ThemeSetting {
1214
light,
1315

1416
/// Corresponds to [Brightness.dark].
15-
dark,
16-
}
17+
dark;
1718

19+
String displayName(ZulipLocalizations zulipLocalizations) {
20+
return switch (this) {
21+
ThemeSetting.unset => zulipLocalizations.themeSettingSystemLabel,
22+
ThemeSetting.light => zulipLocalizations.themeSettingLightLabel,
23+
ThemeSetting.dark => zulipLocalizations.themeSettingDarkLabel,
24+
};
25+
}
26+
}

lib/widgets/settings.dart

+66-22
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1+
import 'dart:async';
2+
13
import 'package:drift/drift.dart' hide Column;
4+
import 'package:flutter/foundation.dart';
25
import 'package:flutter/material.dart';
36

47
import '../generated/l10n/zulip_localizations.dart';
58
import '../model/database.dart';
69
import '../model/settings.dart';
710
import 'page.dart';
811
import 'store.dart';
12+
import 'text.dart';
913

1014
class SettingsPage extends StatelessWidget {
1115
const SettingsPage({super.key});
@@ -18,46 +22,86 @@ class SettingsPage extends StatelessWidget {
1822
Widget build(BuildContext context) {
1923
final globalStore = GlobalStoreWidget.of(context);
2024
final zulipLocalizations = ZulipLocalizations.of(context);
25+
final themeData = Theme.of(context);
2126

2227
return Scaffold(
2328
appBar: AppBar(title: Text(zulipLocalizations.settingsPageTitle)),
24-
body: Column(children: [
25-
_Select(initialValue: globalStore.globalSettings.themeSetting),
26-
]));
29+
body: SafeArea(
30+
bottom: false,
31+
child: SingleChildScrollView(
32+
child: SafeArea(
33+
minimum: const EdgeInsets.only(bottom: 8),
34+
child: Column(children: [
35+
Theme(
36+
data: themeData.copyWith(splashColor: Colors.transparent),
37+
child: _ThemeSetting(initialValue: globalStore.globalSettings.themeSetting)),
38+
]),
39+
))));
2740
}
2841
}
2942

30-
class _Select extends StatefulWidget {
31-
const _Select({required this.initialValue});
43+
class _ThemeSetting extends StatefulWidget {
44+
const _ThemeSetting({required this.initialValue});
3245

3346
final ThemeSetting initialValue;
3447

3548
@override
36-
State<_Select> createState() => _SelectState();
49+
State<_ThemeSetting> createState() => _ThemeSettingState();
3750
}
3851

39-
class _SelectState extends State<_Select> {
40-
late ThemeSetting value = widget.initialValue;
52+
class _ThemeSettingState extends State<_ThemeSetting> {
53+
late ThemeSetting currentThemeSetting = widget.initialValue;
54+
static const entryHeight = 38.0;
4155

4256
@override
4357
Widget build(BuildContext context) {
4458
final globalStore = GlobalStoreWidget.of(context);
45-
return Column(
46-
children: [
47-
const ListTile(title: Text('Theme')),
48-
for (final option in ThemeSetting.values)
49-
ListTile(
50-
title: Text(option.name),
51-
leading: Radio.adaptive(
52-
value: option,
53-
groupValue: value,
59+
final zulipLocalizations = ZulipLocalizations.of(context);
60+
final themeSettingOptions = [
61+
ThemeSetting.dark,
62+
ThemeSetting.light,
63+
ThemeSetting.unset,
64+
];
65+
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
66+
return ListTileTheme(
67+
data: const ListTileThemeData(
68+
contentPadding: EdgeInsets.symmetric(horizontal: 8),
69+
dense: true,
70+
minVerticalPadding: 0,
71+
minTileHeight: 38,
72+
73+
horizontalTitleGap: 0,
74+
minLeadingWidth: 38,
75+
),
76+
child: Column(
77+
children: [
78+
Container(
79+
height: entryHeight,
80+
padding: const EdgeInsets.symmetric(horizontal: 16),
81+
alignment: Alignment.bottomLeft,
82+
child: Text(zulipLocalizations.themeSettingLabel,
83+
style: const TextStyle(
84+
fontSize: 18,
85+
height: 19 / 18,
86+
).merge(weightVariableTextStyle(context, wght: 600)))),
87+
for (final themeSettingOption in themeSettingOptions)
88+
RadioListTile(
89+
title: Text(themeSettingOption.displayName(zulipLocalizations),
90+
style: const TextStyle(
91+
fontSize: 19,
92+
height: 26 / 19)),
93+
groupValue: currentThemeSetting,
94+
value: themeSettingOption,
95+
// This hides the circular overlay over the Radio, and leaves
96+
// the overlay over the ListTile unaffected.
97+
overlayColor: const WidgetStatePropertyAll(Colors.transparent),
5498
onChanged: (newValue) {
5599
setState(() {
56-
value = newValue!;
100+
currentThemeSetting = newValue!;
57101
});
58-
globalStore.updateGlobalSettings(GlobalSettingsCompanion(
59-
themeSetting: Value(value)));
60-
})),
61-
]);
102+
unawaited(globalStore.updateGlobalSettings(GlobalSettingsCompanion(
103+
themeSetting: Value(currentThemeSetting))));
104+
}),
105+
]));
62106
}
63107
}

test/widgets/settings_test.dart

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import 'package:checks/checks.dart';
2+
import 'package:flutter_test/flutter_test.dart';
3+
import 'package:zulip/model/settings.dart';
4+
import 'package:zulip/widgets/settings.dart';
5+
6+
import '../model/binding.dart';
7+
import 'test_app.dart';
8+
import '../example_data.dart' as eg;
9+
10+
void main() {
11+
TestZulipBinding.ensureInitialized();
12+
13+
testWidgets('Theme setting initial value', (tester) async {
14+
15+
});
16+
17+
testWidgets('Update theme setting', (tester) async {
18+
addTearDown(testBinding.reset);
19+
await testBinding.globalStore.add(eg.selfAccount, eg.initialSnapshot());
20+
await tester.pumpWidget(TestZulipApp(
21+
accountId: eg.selfAccount.id,
22+
child: const SettingsPage()));
23+
await tester.pump();
24+
await tester.pump();
25+
check(testBinding.globalStore.globalSettings.themeSetting).equals(ThemeSetting.unset);
26+
27+
await tester.tap(find.text('Light'));
28+
check(testBinding.globalStore.globalSettings.themeSetting).equals(ThemeSetting.light);
29+
30+
await tester.tap(find.text('Dark'));
31+
check(testBinding.globalStore.globalSettings.themeSetting).equals(ThemeSetting.dark);
32+
});
33+
}

0 commit comments

Comments
 (0)