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

wip; Store some client settings #1167

Draft
wants to merge 8 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
4 changes: 2 additions & 2 deletions lib/model/binding.dart
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ abstract class ZulipBinding {
}

/// Get the app's singleton [GlobalStore],
/// calling [loadGlobalStore] if not already loaded.
/// loading it asynchronously if not already loaded.
///
/// Where possible, use [GlobalStoreWidget.of] to get access to a [GlobalStore].
/// Use this method only in contexts like notifications where
Expand Down Expand Up @@ -312,7 +312,7 @@ class PackageInfo {

/// A concrete binding for use in the live application.
///
/// The global store returned by [loadGlobalStore], and consequently by
/// The global store returned by [getGlobalStore], and consequently by
/// [GlobalStoreWidget.of] in application code, will be a [LiveGlobalStore].
/// It therefore uses a live server and live, persistent local database.
///
Expand Down
96 changes: 62 additions & 34 deletions lib/model/database.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import 'dart:io';

import 'package:drift/drift.dart';
import 'package:drift/native.dart';
import 'package:drift/remote.dart';
import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart';
import 'package:sqlite3/common.dart';

part 'database.g.dart';
Expand Down Expand Up @@ -39,29 +34,55 @@ class Accounts extends Table {

Column<String> get ackedPushToken => text().nullable()();

// If adding a column, be sure to add it to copyWithCompanion too.

@override
List<Set<Column<Object>>> get uniqueKeys => [
{realmUrl, userId},
{realmUrl, email},
];
}

extension AccountExtension on Account {
Account copyWithCompanion(AccountsCompanion data) { // TODO(drift): generate this
return Account(
id: data.id.present ? data.id.value : id,
realmUrl: data.realmUrl.present ? data.realmUrl.value : realmUrl,
userId: data.userId.present ? data.userId.value : userId,
email: data.email.present ? data.email.value : email,
apiKey: data.apiKey.present ? data.apiKey.value : apiKey,
zulipVersion: data.zulipVersion.present ? data.zulipVersion.value : zulipVersion,
zulipMergeBase: data.zulipMergeBase.present ? data.zulipMergeBase.value : zulipMergeBase,
zulipFeatureLevel: data.zulipFeatureLevel.present ? data.zulipFeatureLevel.value : zulipFeatureLevel,
ackedPushToken: data.ackedPushToken.present ? data.ackedPushToken.value : ackedPushToken,
);
}
/// The visual theme of the app.
///
/// See [zulipThemeData] for how themes are determined.
enum ThemeSetting {
/// Corresponds to the default platform setting.
none,

/// Corresponds to [Brightness.light].
light,

/// Corresponds to [Brightness.dark].
dark,
}

/// What browser the user has set to use for opening links in messages.
///
/// See https://chat.zulip.org/#narrow/stream/48-mobile/topic/in-app.20browser
/// for the reasoning behind these options.
enum BrowserPreference {
/// Use [UrlLaunchMode.externalApplication] on iOS,
/// [UrlLaunchMode.platformDefault] on Android.
none,

/// Use the in-app browser.
embedded,

/// Use the user's default browser app.
external,
}

/// The table of the user's chosen settings independent of account, on this
/// client.
///
/// These apply across all the user's accounts on this client (i.e. on this
/// install of the app on this device).
@DataClassName('GlobalSettingsData')
class GlobalSettings extends Table {
// TODO(db): Maybe make this optional and/or use clientDefault?
Column<String> get themeSetting => textEnum<ThemeSetting>()();

// TODO(db): Maybe make this optional and/or use clientDefault?
Column<String> get browserPreference => textEnum<BrowserPreference>()();
}

class UriConverter extends TypeConverter<Uri, String> {
Expand All @@ -70,29 +91,18 @@ class UriConverter extends TypeConverter<Uri, String> {
@override Uri fromSql(String fromDb) => Uri.parse(fromDb);
}

LazyDatabase _openConnection() {
return LazyDatabase(() async {
// TODO decide if this path is the right one to use
final dbFolder = await getApplicationDocumentsDirectory();
final file = File(path.join(dbFolder.path, 'db.sqlite'));
return NativeDatabase.createInBackground(file);
});
}

@DriftDatabase(tables: [Accounts])
@DriftDatabase(tables: [Accounts, GlobalSettings])
class AppDatabase extends _$AppDatabase {
AppDatabase(super.e);

AppDatabase.live() : this(_openConnection());

// When updating the schema:
// * Make the change in the table classes, and bump schemaVersion.
// * Export the new schema and generate test migrations:
// $ tools/check --fix drift
// * Write a migration in `onUpgrade` below.
// * Write tests.
@override
int get schemaVersion => 2; // See note.
int get schemaVersion => 3; // See note.

@override
MigrationStrategy get migration {
Expand All @@ -118,6 +128,10 @@ class AppDatabase extends _$AppDatabase {
if (from < 2 && 2 <= to) {
await m.addColumn(accounts, accounts.ackedPushToken);
}

if (from < 3 && 3 <= to) {
await m.createTable(globalSettings);
}
// New migrations go here.
}
);
Expand All @@ -138,6 +152,20 @@ class AppDatabase extends _$AppDatabase {
rethrow;
}
}

Future<GlobalSettingsData> ensureGlobalSettings() async {
final settings = await select(globalSettings).getSingleOrNull();
// TODO(db): Enforce the singleton constraint more robustly.
if (settings != null) {
return settings;
}

await into(globalSettings).insert(GlobalSettingsCompanion.insert(
themeSetting: ThemeSetting.none,
browserPreference: BrowserPreference.none,
));
return select(globalSettings).getSingle();
}
}

class AccountAlreadyExistsException implements Exception {}
Loading
Loading