Skip to content

Commit 80da3ae

Browse files
committed
store: Store global settings on global store
To add integration tests for global store with sqlite, we might want to switch to LiveGlobalStore, or at least make part of it testable with an in-memory database (like we do for database_test.dart). Leaving that to the future. Signed-off-by: Zixuan James Li <[email protected]>
1 parent c84aed0 commit 80da3ae

File tree

5 files changed

+83
-7
lines changed

5 files changed

+83
-7
lines changed

lib/model/store.dart

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,31 @@ export 'database.dart' show Account, AccountsCompanion, AccountAlreadyExistsExce
5353
/// * [LiveGlobalStore], the implementation of this class that
5454
/// we use outside of tests.
5555
abstract class GlobalStore extends ChangeNotifier {
56-
GlobalStore({required Iterable<Account> accounts})
57-
: _accounts = Map.fromEntries(accounts.map((a) => MapEntry(a.id, a)));
56+
GlobalStore({
57+
required GlobalSettingsData globalSettings,
58+
required Iterable<Account> accounts,
59+
})
60+
: _globalSettings = globalSettings,
61+
_accounts = Map.fromEntries(accounts.map((a) => MapEntry(a.id, a)));
62+
63+
/// A cache of the [GlobalSettingsData] singleton in the underlying data store.
64+
GlobalSettingsData get globalSettings => _globalSettings;
65+
GlobalSettingsData _globalSettings;
66+
67+
/// Update the global settings in the store, returning the new version.
68+
///
69+
/// The global settings must already exist in the store.
70+
Future<GlobalSettingsData> updateGlobalSettings(GlobalSettingsCompanion data) async {
71+
await doUpdateGlobalSettings(data);
72+
_globalSettings = _globalSettings.copyWithCompanion(data);
73+
notifyListeners();
74+
return _globalSettings;
75+
}
76+
77+
/// Update the global settings in the underlying data store.
78+
///
79+
/// This should only be called from [updateGlobalSettings].
80+
Future<void> doUpdateGlobalSettings(GlobalSettingsCompanion data);
5881

5982
/// A cache of the [Accounts] table in the underlying data store.
6083
final Map<int, Account> _accounts;
@@ -814,6 +837,7 @@ Uri? tryResolveUrl(Uri baseUrl, String reference) {
814837
class LiveGlobalStore extends GlobalStore {
815838
LiveGlobalStore._({
816839
required AppDatabase db,
840+
required super.globalSettings,
817841
required super.accounts,
818842
}) : _db = db;
819843

@@ -830,8 +854,11 @@ class LiveGlobalStore extends GlobalStore {
830854
// by doing this loading up front before constructing a [GlobalStore].
831855
static Future<GlobalStore> load() async {
832856
final db = AppDatabase(NativeDatabase.createInBackground(await _dbFile()));
857+
final globalSettings = await db.ensureGlobalSettings();
833858
final accounts = await db.select(db.accounts).get();
834-
return LiveGlobalStore._(db: db, accounts: accounts);
859+
return LiveGlobalStore._(db: db,
860+
globalSettings: globalSettings,
861+
accounts: accounts);
835862
}
836863

837864
/// The file path to use for the app database.
@@ -855,6 +882,12 @@ class LiveGlobalStore extends GlobalStore {
855882

856883
final AppDatabase _db;
857884

885+
@override
886+
Future<void> doUpdateGlobalSettings(GlobalSettingsCompanion data) async {
887+
final rowsAffected = await _db.update(_db.globalSettings).write(data);
888+
assert(rowsAffected == 1);
889+
}
890+
858891
@override
859892
Future<PerAccountStore> doLoadPerAccount(int accountId) async {
860893
final updateMachine = await UpdateMachine.load(this, accountId);

test/example_data.dart

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -885,9 +885,16 @@ GlobalSettingsData globalSettings({
885885
themeSetting: themeSetting,
886886
);
887887
}
888+
const _globalSettings = globalSettings;
888889

889-
TestGlobalStore globalStore({List<Account> accounts = const []}) {
890-
return TestGlobalStore(accounts: accounts);
890+
TestGlobalStore globalStore({
891+
GlobalSettingsData? globalSettings,
892+
List<Account> accounts = const [],
893+
}) {
894+
return TestGlobalStore(
895+
globalSettings: globalSettings ?? _globalSettings(),
896+
accounts: accounts,
897+
);
891898
}
892899
const _globalStore = globalStore;
893900

test/model/store_checks.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ extension AccountChecks on Subject<Account> {
2222
}
2323

2424
extension GlobalStoreChecks on Subject<GlobalStore> {
25+
Subject<GlobalSettingsData> get globalSettings => has((x) => x.globalSettings, 'globalSettings');
2526
Subject<Iterable<Account>> get accounts => has((x) => x.accounts, 'accounts');
2627
Subject<Iterable<int>> get accountIds => has((x) => x.accountIds, 'accountIds');
2728
Subject<Iterable<({ int accountId, Account account })>> get accountEntries => has((x) => x.accountEntries, 'accountEntries');

test/model/store_test.dart

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import 'package:zulip/api/route/messages.dart';
1414
import 'package:zulip/api/route/realm.dart';
1515
import 'package:zulip/log.dart';
1616
import 'package:zulip/model/actions.dart';
17+
import 'package:zulip/model/database.dart';
18+
import 'package:zulip/model/settings.dart';
1719
import 'package:zulip/model/store.dart';
1820
import 'package:zulip/notifications/receive.dart';
1921

@@ -29,6 +31,31 @@ import 'test_store.dart';
2931
void main() {
3032
TestZulipBinding.ensureInitialized();
3133

34+
group('GlobalStore.updateGlobalSettings', () {
35+
test('smoke', () async {
36+
final globalStore = eg.globalStore();
37+
check(globalStore).globalSettings.themeSetting.equals(null);
38+
39+
final result = await globalStore.updateGlobalSettings(
40+
GlobalSettingsCompanion(themeSetting: Value(ThemeSetting.dark)));
41+
check(globalStore).globalSettings.themeSetting.equals(ThemeSetting.dark);
42+
check(result).equals(globalStore.globalSettings);
43+
});
44+
45+
test('should notify listeners', () async {
46+
int notifyCount = 0;
47+
final globalStore = eg.globalStore();
48+
globalStore.addListener(() => notifyCount++);
49+
check(notifyCount).equals(0);
50+
51+
await globalStore.updateGlobalSettings(
52+
GlobalSettingsCompanion(themeSetting: Value(ThemeSetting.light)));
53+
check(notifyCount).equals(1);
54+
});
55+
56+
// TODO integration tests with sqlite
57+
});
58+
3259
final account1 = eg.selfAccount.copyWith(id: 1);
3360
final account2 = eg.otherAccount.copyWith(id: 2);
3461

@@ -1125,7 +1152,9 @@ void main() {
11251152
}
11261153

11271154
class LoadingTestGlobalStore extends TestGlobalStore {
1128-
LoadingTestGlobalStore({required super.accounts});
1155+
LoadingTestGlobalStore({
1156+
required super.accounts,
1157+
}) : super(globalSettings: eg.globalSettings());
11291158

11301159
Map<int, List<Completer<PerAccountStore>>> completers = {};
11311160

test/model/test_store.dart

Lines changed: 7 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,12 @@ 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.globalSettings, required super.accounts});
27+
28+
@override
29+
Future<void> doUpdateGlobalSettings(GlobalSettingsCompanion data) async {
30+
// Nothing to do.
31+
}
2632

2733
final Map<
2834
({Uri realmUrl, int? zulipFeatureLevel, String? email, String? apiKey}),

0 commit comments

Comments
 (0)