Skip to content

Commit 8c20dc9

Browse files
committed
wip; store: Add global settings
Signed-off-by: Zixuan James Li <[email protected]>
1 parent 664300a commit 8c20dc9

File tree

7 files changed

+84
-14
lines changed

7 files changed

+84
-14
lines changed

lib/model/store.dart

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,19 @@ export 'database.dart' show Account, AccountsCompanion, AccountAlreadyExistsExce
5151
/// * [LiveGlobalStore], the implementation of this class that
5252
/// we use outside of tests.
5353
abstract class GlobalStore extends ChangeNotifier {
54-
GlobalStore({required Iterable<Account> accounts})
55-
: _accounts = Map.fromEntries(accounts.map((a) => MapEntry(a.id, a)));
54+
GlobalStore({
55+
required Iterable<Account> accounts,
56+
required GlobalSettingsData globalSettings,
57+
})
58+
: _accounts = Map.fromEntries(accounts.map((a) => MapEntry(a.id, a))),
59+
_globalSettings = globalSettings;
5660

5761
/// A cache of the [Accounts] table in the underlying data store.
5862
final Map<int, Account> _accounts;
5963

64+
/// A cache of the [GlobalSettingsData] singleton in the underlying data store.
65+
GlobalSettingsData _globalSettings;
66+
6067
// TODO settings (those that are per-device rather than per-account)
6168
// TODO push token, and other data corresponding to GlobalSessionState
6269

@@ -223,6 +230,23 @@ abstract class GlobalStore extends ChangeNotifier {
223230
/// Remove an account from the underlying data store.
224231
Future<void> doRemoveAccount(int accountId);
225232

233+
GlobalSettingsData get globalSettings => _globalSettings;
234+
235+
/// Update the global settings in the store, return the new version.
236+
///
237+
/// The global settings must already exist in the store.
238+
Future<GlobalSettingsData> updateGlobalSettings(GlobalSettingsCompanion data) async {
239+
await doUpdateGlobalSettings(data);
240+
_globalSettings = _globalSettings.copyWithCompanion(data);
241+
notifyListeners();
242+
return _globalSettings;
243+
}
244+
245+
/// Update the global settings in the underlying data store.
246+
///
247+
/// This should only be called from [updateGlobalSettings].
248+
Future<void> doUpdateGlobalSettings(GlobalSettingsCompanion data);
249+
226250
@override
227251
String toString() => '${objectRuntimeType(this, 'GlobalStore')}#${shortHash(this)}';
228252
}
@@ -757,6 +781,7 @@ class LiveGlobalStore extends GlobalStore {
757781
LiveGlobalStore._({
758782
required AppDatabase db,
759783
required super.accounts,
784+
required super.globalSettings,
760785
}) : _db = db;
761786

762787
@override
@@ -773,7 +798,10 @@ class LiveGlobalStore extends GlobalStore {
773798
static Future<GlobalStore> load() async {
774799
final db = AppDatabase(NativeDatabase.createInBackground(await _dbFile()));
775800
final accounts = await db.select(db.accounts).get();
776-
return LiveGlobalStore._(db: db, accounts: accounts);
801+
final globalSettings = await db.ensureGlobalSettings();
802+
return LiveGlobalStore._(db: db,
803+
accounts: accounts,
804+
globalSettings: globalSettings);
777805
}
778806

779807
/// The file path to use for the app database.
@@ -835,6 +863,12 @@ class LiveGlobalStore extends GlobalStore {
835863
assert(rowsAffected == 1);
836864
}
837865

866+
@override
867+
Future<void> doUpdateGlobalSettings(GlobalSettingsCompanion data) async {
868+
final rowsAffected = await _db.update(_db.globalSettings).write(data);
869+
assert(rowsAffected == 1);
870+
}
871+
838872
@override
839873
String toString() => '${objectRuntimeType(this, 'LiveGlobalStore')}#${shortHash(this)}';
840874
}

lib/widgets/content.dart

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import '../generated/l10n/zulip_localizations.dart';
1414
import '../model/avatar_url.dart';
1515
import '../model/binding.dart';
1616
import '../model/content.dart';
17+
import '../model/database.dart';
1718
import '../model/internal_link.dart';
1819
import 'code_block.dart';
1920
import 'dialog.dart';
@@ -1342,17 +1343,20 @@ void _launchUrl(BuildContext context, String urlString) async {
13421343
return;
13431344
}
13441345

1346+
final globalSettings = GlobalStoreWidget.of(context).globalSettings;
13451347
bool launched = false;
13461348
String? errorMessage;
13471349
try {
13481350
launched = await ZulipBinding.instance.launchUrl(url,
1349-
mode: switch (defaultTargetPlatform) {
1351+
mode: switch ((globalSettings.browserPreference, defaultTargetPlatform)) {
1352+
(BrowserPreference.embedded, _) => UrlLaunchMode.inAppBrowserView,
1353+
(BrowserPreference.external, _) => UrlLaunchMode.externalApplication,
13501354
// On iOS we prefer LaunchMode.externalApplication because (for
13511355
// HTTP URLs) LaunchMode.platformDefault uses SFSafariViewController,
13521356
// which gives an awkward UX as described here:
13531357
// https://chat.zulip.org/#narrow/stream/48-mobile/topic/in-app.20browser/near/1169118
1354-
TargetPlatform.iOS => UrlLaunchMode.externalApplication,
1355-
_ => UrlLaunchMode.platformDefault,
1358+
(BrowserPreference.unset, TargetPlatform.iOS) => UrlLaunchMode.externalApplication,
1359+
(BrowserPreference.unset, _) => UrlLaunchMode.platformDefault,
13561360
});
13571361
} on PlatformException catch (e) {
13581362
errorMessage = e.message;

lib/widgets/theme.dart

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,27 @@
11
import 'package:flutter/material.dart';
22

33
import '../api/model/model.dart';
4+
import '../model/database.dart';
45
import 'content.dart';
56
import 'emoji_reaction.dart';
67
import 'message_list.dart';
78
import 'channel_colors.dart';
9+
import 'store.dart';
810
import 'text.dart';
911

1012
ThemeData zulipThemeData(BuildContext context) {
1113
final DesignVariables designVariables;
1214
final List<ThemeExtension> themeExtensions;
13-
Brightness brightness = MediaQuery.platformBrightnessOf(context);
15+
final globalSettings = GlobalStoreWidget.of(context).globalSettings;
16+
Brightness brightness;
17+
switch (globalSettings.themeSetting) {
18+
case ThemeSetting.unset:
19+
brightness = MediaQuery.platformBrightnessOf(context);
20+
case ThemeSetting.light:
21+
brightness = Brightness.light;
22+
case ThemeSetting.dark:
23+
brightness = Brightness.dark;
24+
}
1425

1526
// This applies Material 3's color system to produce a palette of
1627
// appropriately matching and contrasting colors for use in a UI.

test/example_data.dart

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'package:zulip/api/model/submessage.dart';
88
import 'package:zulip/api/route/messages.dart';
99
import 'package:zulip/api/route/realm.dart';
1010
import 'package:zulip/api/route/channels.dart';
11+
import 'package:zulip/model/database.dart';
1112
import 'package:zulip/model/narrow.dart';
1213
import 'package:zulip/model/store.dart';
1314

@@ -805,8 +806,16 @@ ChannelUpdateEvent channelUpdateEvent(
805806
// The entire per-account or global state.
806807
//
807808

808-
TestGlobalStore globalStore({List<Account> accounts = const []}) {
809-
return TestGlobalStore(accounts: accounts);
809+
const globalSettings = GlobalSettingsData(
810+
themeSetting: ThemeSetting.unset,
811+
browserPreference: BrowserPreference.unset,
812+
);
813+
814+
TestGlobalStore globalStore({
815+
List<Account> accounts = const [],
816+
GlobalSettingsData globalSettings = globalSettings,
817+
}) {
818+
return TestGlobalStore(accounts: accounts, globalSettings: globalSettings);
810819
}
811820

812821
InitialSnapshot initialSnapshot({

test/model/binding.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import 'package:zulip/model/binding.dart';
1111
import 'package:zulip/model/store.dart';
1212
import 'package:zulip/widgets/app.dart';
1313

14+
import '../example_data.dart' as eg;
1415
import 'test_store.dart';
1516

1617
/// The binding instance used in tests.
@@ -85,7 +86,7 @@ class TestZulipBinding extends ZulipBinding {
8586
///
8687
/// Tests that access this getter, or that mount a [GlobalStoreWidget],
8788
/// should clean up by calling [reset].
88-
TestGlobalStore get globalStore => _globalStore ??= TestGlobalStore(accounts: []);
89+
TestGlobalStore get globalStore => _globalStore ??= eg.globalStore();
8990
TestGlobalStore? _globalStore;
9091

9192
bool _debugAlreadyLoadedStore = false;

test/model/store_test.dart

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ void main() {
406406
late FakeApiConnection connection;
407407

408408
Future<void> prepareStore({Account? account}) async {
409-
globalStore = TestGlobalStore(accounts: []);
409+
globalStore = eg.globalStore();
410410
account ??= eg.selfAccount;
411411
await globalStore.insertAccount(account.toCompanion(false));
412412
connection = (globalStore.apiConnectionFromAccount(account)
@@ -581,7 +581,7 @@ void main() {
581581
}
582582

583583
Future<void> preparePoll({int? lastEventId}) async {
584-
globalStore = TestGlobalStore(accounts: []);
584+
globalStore = eg.globalStore();
585585
await globalStore.add(eg.selfAccount, eg.initialSnapshot(
586586
lastEventId: lastEventId));
587587
await globalStore.perAccount(eg.selfAccount.id);
@@ -1086,7 +1086,10 @@ void main() {
10861086
}
10871087

10881088
class LoadingTestGlobalStore extends TestGlobalStore {
1089-
LoadingTestGlobalStore({required super.accounts});
1089+
LoadingTestGlobalStore({
1090+
required super.accounts,
1091+
super.globalSettings = eg.globalSettings,
1092+
});
10901093

10911094
Map<int, List<Completer<PerAccountStore>>> completers = {};
10921095

test/model/test_store.dart

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'package:zulip/api/model/events.dart';
22
import 'package:zulip/api/model/initial_snapshot.dart';
33
import 'package:zulip/api/model/model.dart';
4+
import 'package:zulip/model/database.dart';
45
import 'package:zulip/model/store.dart';
56
import 'package:zulip/widgets/store.dart';
67

@@ -22,7 +23,7 @@ import '../example_data.dart' as eg;
2223
///
2324
/// See also [TestZulipBinding.globalStore], which provides one of these.
2425
class TestGlobalStore extends GlobalStore {
25-
TestGlobalStore({required super.accounts});
26+
TestGlobalStore({required super.accounts, required super.globalSettings});
2627

2728
final Map<
2829
({Uri realmUrl, int? zulipFeatureLevel, String? email, String? apiKey}),
@@ -157,6 +158,13 @@ class TestGlobalStore extends GlobalStore {
157158
store: store, initialSnapshot: initialSnapshot);
158159
return Future.value(store);
159160
}
161+
162+
GlobalSettingsData? _globalSettings;
163+
164+
@override
165+
Future<void> doUpdateGlobalSettings(GlobalSettingsCompanion data) async {
166+
_globalSettings = _globalSettings!.copyWithCompanion(data);
167+
}
160168
}
161169

162170
extension PerAccountStoreTestExtension on PerAccountStore {

0 commit comments

Comments
 (0)