diff --git a/assets/l10n/app_en.arb b/assets/l10n/app_en.arb index 73bcb373b0..96507309ee 100644 --- a/assets/l10n/app_en.arb +++ b/assets/l10n/app_en.arb @@ -1117,9 +1117,9 @@ "allChannelsPageTitle": {"type": "String", "example": "All channels"} } }, - "sharePageTitle": "Share", - "@sharePageTitle": { - "description": "Title for the page about sharing content received from other apps." + "shareChooseAccountLabel": "Choose an account", + "@shareChooseAccountLabel": { + "description": "Label for the page about selecting an account to share content received from other apps." }, "mainMenuMyProfile": "My profile", "@mainMenuMyProfile": { diff --git a/lib/api/model/initial_snapshot.dart b/lib/api/model/initial_snapshot.dart index 9250f75fa2..c003e5e055 100644 --- a/lib/api/model/initial_snapshot.dart +++ b/lib/api/model/initial_snapshot.dart @@ -90,6 +90,8 @@ class InitialSnapshot { final bool realmMandatoryTopics; + final String realmName; + /// The number of days until a user's account is treated as a full member. /// /// Search for "realm_waiting_period_threshold" in https://zulip.com/api/register-queue. @@ -105,6 +107,8 @@ class InitialSnapshot { final bool realmEnableReadReceipts; + final Uri realmIconUrl; + final bool realmPresenceDisabled; final Map realmDefaultExternalAccounts; @@ -176,11 +180,13 @@ class InitialSnapshot { required this.realmDeleteOwnMessagePolicy, required this.realmWildcardMentionPolicy, required this.realmMandatoryTopics, + required this.realmName, required this.realmWaitingPeriodThreshold, required this.realmMessageContentDeleteLimitSeconds, required this.realmAllowMessageEditing, required this.realmMessageContentEditLimitSeconds, required this.realmEnableReadReceipts, + required this.realmIconUrl, required this.realmPresenceDisabled, required this.realmDefaultExternalAccounts, required this.maxFileUploadSizeMib, diff --git a/lib/api/model/initial_snapshot.g.dart b/lib/api/model/initial_snapshot.g.dart index ffcbfba827..9f83da4eaf 100644 --- a/lib/api/model/initial_snapshot.g.dart +++ b/lib/api/model/initial_snapshot.g.dart @@ -98,6 +98,7 @@ InitialSnapshot _$InitialSnapshotFromJson( json['realm_wildcard_mention_policy'], ), realmMandatoryTopics: json['realm_mandatory_topics'] as bool, + realmName: json['realm_name'] as String, realmWaitingPeriodThreshold: (json['realm_waiting_period_threshold'] as num) .toInt(), realmMessageContentDeleteLimitSeconds: @@ -106,6 +107,7 @@ InitialSnapshot _$InitialSnapshotFromJson( realmMessageContentEditLimitSeconds: (json['realm_message_content_edit_limit_seconds'] as num?)?.toInt(), realmEnableReadReceipts: json['realm_enable_read_receipts'] as bool, + realmIconUrl: Uri.parse(json['realm_icon_url'] as String), realmPresenceDisabled: json['realm_presence_disabled'] as bool, realmDefaultExternalAccounts: (json['realm_default_external_accounts'] as Map).map( @@ -176,6 +178,7 @@ Map _$InitialSnapshotToJson( 'realm_delete_own_message_policy': instance.realmDeleteOwnMessagePolicy, 'realm_wildcard_mention_policy': instance.realmWildcardMentionPolicy, 'realm_mandatory_topics': instance.realmMandatoryTopics, + 'realm_name': instance.realmName, 'realm_waiting_period_threshold': instance.realmWaitingPeriodThreshold, 'realm_message_content_delete_limit_seconds': instance.realmMessageContentDeleteLimitSeconds, @@ -183,6 +186,7 @@ Map _$InitialSnapshotToJson( 'realm_message_content_edit_limit_seconds': instance.realmMessageContentEditLimitSeconds, 'realm_enable_read_receipts': instance.realmEnableReadReceipts, + 'realm_icon_url': instance.realmIconUrl.toString(), 'realm_presence_disabled': instance.realmPresenceDisabled, 'realm_default_external_accounts': instance.realmDefaultExternalAccounts, 'max_file_upload_size_mib': instance.maxFileUploadSizeMib, diff --git a/lib/api/route/realm.dart b/lib/api/route/realm.dart index 282790bef2..6fbd9181af 100644 --- a/lib/api/route/realm.dart +++ b/lib/api/route/realm.dart @@ -48,7 +48,7 @@ class GetServerSettingsResult { final Uri realmUrl; final String realmName; - final String realmIcon; + final Uri realmIcon; final String realmDescription; final bool realmWebPublicAccessEnabled; diff --git a/lib/api/route/realm.g.dart b/lib/api/route/realm.g.dart index c71df7d433..cc6e0c234f 100644 --- a/lib/api/route/realm.g.dart +++ b/lib/api/route/realm.g.dart @@ -31,7 +31,7 @@ GetServerSettingsResult _$GetServerSettingsResultFromJson( requireEmailFormatUsernames: json['require_email_format_usernames'] as bool, realmUrl: Uri.parse(json['realm_uri'] as String), realmName: json['realm_name'] as String, - realmIcon: json['realm_icon'] as String, + realmIcon: Uri.parse(json['realm_icon'] as String), realmDescription: json['realm_description'] as String, realmWebPublicAccessEnabled: json['realm_web_public_access_enabled'] as bool, ); @@ -50,7 +50,7 @@ Map _$GetServerSettingsResultToJson( 'require_email_format_usernames': instance.requireEmailFormatUsernames, 'realm_uri': instance.realmUrl.toString(), 'realm_name': instance.realmName, - 'realm_icon': instance.realmIcon, + 'realm_icon': instance.realmIcon.toString(), 'realm_description': instance.realmDescription, 'realm_web_public_access_enabled': instance.realmWebPublicAccessEnabled, }; diff --git a/lib/generated/l10n/zulip_localizations.dart b/lib/generated/l10n/zulip_localizations.dart index 44fadaa2ec..05b13a8291 100644 --- a/lib/generated/l10n/zulip_localizations.dart +++ b/lib/generated/l10n/zulip_localizations.dart @@ -1647,11 +1647,11 @@ abstract class ZulipLocalizations { String allChannelsPageTitle, ); - /// Title for the page about sharing content received from other apps. + /// Label for the page about selecting an account to share content received from other apps. /// /// In en, this message translates to: - /// **'Share'** - String get sharePageTitle; + /// **'Choose an account'** + String get shareChooseAccountLabel; /// Label for main-menu button leading to the user's own profile. /// diff --git a/lib/generated/l10n/zulip_localizations_ar.dart b/lib/generated/l10n/zulip_localizations_ar.dart index 6c9ef2fcc5..24f20f5df2 100644 --- a/lib/generated/l10n/zulip_localizations_ar.dart +++ b/lib/generated/l10n/zulip_localizations_ar.dart @@ -934,7 +934,7 @@ class ZulipLocalizationsAr extends ZulipLocalizations { } @override - String get sharePageTitle => 'Share'; + String get shareChooseAccountLabel => 'Choose an account'; @override String get mainMenuMyProfile => 'My profile'; diff --git a/lib/generated/l10n/zulip_localizations_de.dart b/lib/generated/l10n/zulip_localizations_de.dart index 9768e579e5..dcacb0b336 100644 --- a/lib/generated/l10n/zulip_localizations_de.dart +++ b/lib/generated/l10n/zulip_localizations_de.dart @@ -957,7 +957,7 @@ class ZulipLocalizationsDe extends ZulipLocalizations { } @override - String get sharePageTitle => 'Teilen'; + String get shareChooseAccountLabel => 'Choose an account'; @override String get mainMenuMyProfile => 'Mein Profil'; diff --git a/lib/generated/l10n/zulip_localizations_en.dart b/lib/generated/l10n/zulip_localizations_en.dart index 81ceadcb9c..29050c2fd2 100644 --- a/lib/generated/l10n/zulip_localizations_en.dart +++ b/lib/generated/l10n/zulip_localizations_en.dart @@ -934,7 +934,7 @@ class ZulipLocalizationsEn extends ZulipLocalizations { } @override - String get sharePageTitle => 'Share'; + String get shareChooseAccountLabel => 'Choose an account'; @override String get mainMenuMyProfile => 'My profile'; diff --git a/lib/generated/l10n/zulip_localizations_fr.dart b/lib/generated/l10n/zulip_localizations_fr.dart index 94c4848a9f..6a92a41817 100644 --- a/lib/generated/l10n/zulip_localizations_fr.dart +++ b/lib/generated/l10n/zulip_localizations_fr.dart @@ -948,7 +948,7 @@ class ZulipLocalizationsFr extends ZulipLocalizations { } @override - String get sharePageTitle => 'Share'; + String get shareChooseAccountLabel => 'Choose an account'; @override String get mainMenuMyProfile => 'My profile'; diff --git a/lib/generated/l10n/zulip_localizations_it.dart b/lib/generated/l10n/zulip_localizations_it.dart index a7b2cdd663..ba93ab0373 100644 --- a/lib/generated/l10n/zulip_localizations_it.dart +++ b/lib/generated/l10n/zulip_localizations_it.dart @@ -949,7 +949,7 @@ class ZulipLocalizationsIt extends ZulipLocalizations { } @override - String get sharePageTitle => 'Share'; + String get shareChooseAccountLabel => 'Choose an account'; @override String get mainMenuMyProfile => 'Il mio profilo'; diff --git a/lib/generated/l10n/zulip_localizations_ja.dart b/lib/generated/l10n/zulip_localizations_ja.dart index 09a9b822b4..0a2a80da45 100644 --- a/lib/generated/l10n/zulip_localizations_ja.dart +++ b/lib/generated/l10n/zulip_localizations_ja.dart @@ -913,7 +913,7 @@ class ZulipLocalizationsJa extends ZulipLocalizations { } @override - String get sharePageTitle => '共有'; + String get shareChooseAccountLabel => 'Choose an account'; @override String get mainMenuMyProfile => '自分のプロフィール'; diff --git a/lib/generated/l10n/zulip_localizations_nb.dart b/lib/generated/l10n/zulip_localizations_nb.dart index 8ee2e9c4da..5e1d05d03d 100644 --- a/lib/generated/l10n/zulip_localizations_nb.dart +++ b/lib/generated/l10n/zulip_localizations_nb.dart @@ -934,7 +934,7 @@ class ZulipLocalizationsNb extends ZulipLocalizations { } @override - String get sharePageTitle => 'Share'; + String get shareChooseAccountLabel => 'Choose an account'; @override String get mainMenuMyProfile => 'My profile'; diff --git a/lib/generated/l10n/zulip_localizations_pl.dart b/lib/generated/l10n/zulip_localizations_pl.dart index fcc527a284..0b88e6ba43 100644 --- a/lib/generated/l10n/zulip_localizations_pl.dart +++ b/lib/generated/l10n/zulip_localizations_pl.dart @@ -949,7 +949,7 @@ class ZulipLocalizationsPl extends ZulipLocalizations { } @override - String get sharePageTitle => 'Udostępnij'; + String get shareChooseAccountLabel => 'Choose an account'; @override String get mainMenuMyProfile => 'Mój profil'; diff --git a/lib/generated/l10n/zulip_localizations_ru.dart b/lib/generated/l10n/zulip_localizations_ru.dart index fedbacf98f..d1b9f67395 100644 --- a/lib/generated/l10n/zulip_localizations_ru.dart +++ b/lib/generated/l10n/zulip_localizations_ru.dart @@ -959,7 +959,7 @@ class ZulipLocalizationsRu extends ZulipLocalizations { } @override - String get sharePageTitle => 'Поделиться'; + String get shareChooseAccountLabel => 'Choose an account'; @override String get mainMenuMyProfile => 'Мой профиль'; diff --git a/lib/generated/l10n/zulip_localizations_sk.dart b/lib/generated/l10n/zulip_localizations_sk.dart index 4201d37be2..e857d958c0 100644 --- a/lib/generated/l10n/zulip_localizations_sk.dart +++ b/lib/generated/l10n/zulip_localizations_sk.dart @@ -936,7 +936,7 @@ class ZulipLocalizationsSk extends ZulipLocalizations { } @override - String get sharePageTitle => 'Share'; + String get shareChooseAccountLabel => 'Choose an account'; @override String get mainMenuMyProfile => 'Môj profil'; diff --git a/lib/generated/l10n/zulip_localizations_sl.dart b/lib/generated/l10n/zulip_localizations_sl.dart index ac70ca61da..48d99d421d 100644 --- a/lib/generated/l10n/zulip_localizations_sl.dart +++ b/lib/generated/l10n/zulip_localizations_sl.dart @@ -969,7 +969,7 @@ class ZulipLocalizationsSl extends ZulipLocalizations { } @override - String get sharePageTitle => 'Deli'; + String get shareChooseAccountLabel => 'Choose an account'; @override String get mainMenuMyProfile => 'Moj profil'; diff --git a/lib/generated/l10n/zulip_localizations_uk.dart b/lib/generated/l10n/zulip_localizations_uk.dart index ddfb79ac57..26d208e3bf 100644 --- a/lib/generated/l10n/zulip_localizations_uk.dart +++ b/lib/generated/l10n/zulip_localizations_uk.dart @@ -950,7 +950,7 @@ class ZulipLocalizationsUk extends ZulipLocalizations { } @override - String get sharePageTitle => 'Поділитися'; + String get shareChooseAccountLabel => 'Choose an account'; @override String get mainMenuMyProfile => 'Мій профіль'; diff --git a/lib/generated/l10n/zulip_localizations_zh.dart b/lib/generated/l10n/zulip_localizations_zh.dart index f974ce7573..dab7bb6806 100644 --- a/lib/generated/l10n/zulip_localizations_zh.dart +++ b/lib/generated/l10n/zulip_localizations_zh.dart @@ -934,7 +934,7 @@ class ZulipLocalizationsZh extends ZulipLocalizations { } @override - String get sharePageTitle => 'Share'; + String get shareChooseAccountLabel => 'Choose an account'; @override String get mainMenuMyProfile => 'My profile'; @@ -2014,9 +2014,6 @@ class ZulipLocalizationsZhHansCn extends ZulipLocalizationsZh { @override String get channelsEmptyPlaceholder => '您还没有订阅任何频道。'; - @override - String get sharePageTitle => '分享'; - @override String get mainMenuMyProfile => '个人资料'; @@ -3127,9 +3124,6 @@ class ZulipLocalizationsZhHantTw extends ZulipLocalizationsZh { return '您尚未訂閱任何頻道。請前往 $allChannelsPageTitle 並加入一些頻道。'; } - @override - String get sharePageTitle => '分享'; - @override String get mainMenuMyProfile => '我的設定檔'; diff --git a/lib/model/database.dart b/lib/model/database.dart index bce8b2f422..57dfc815f8 100644 --- a/lib/model/database.dart +++ b/lib/model/database.dart @@ -3,6 +3,7 @@ import 'package:drift/internal/versioned_schema.dart'; import 'package:drift/remote.dart'; import 'package:sqlite3/common.dart'; +import '../api/route/realm.dart'; import '../log.dart'; import 'legacy_app_data.dart'; import 'schema_versions.g.dart'; @@ -123,6 +124,20 @@ class Accounts extends Table { /// It never changes for a given account. Column get realmUrl => text().map(const UriConverter())(); + /// The name of the Zulip realm this account is on. + /// + /// This corresponds to [GetServerSettingsResult.realmName]. + /// + /// Nullable just because older versions of the app didn't store this. + Column get realmName => text().nullable()(); + + /// The icon URL of the Zulip realm this account is on. + /// + /// This corresponds to [GetServerSettingsResult.realmIcon]. + /// + /// Nullable just because older versions of the app didn't store this. + Column get realmIcon => text().map(const UriConverter()).nullable()(); + /// The Zulip user ID of this account. /// /// This is the identifier the server uses for the account. @@ -164,7 +179,7 @@ class AppDatabase extends _$AppDatabase { // information on using the build_runner. // * Write a migration in `_migrationSteps` below. // * Write tests. - static const int latestSchemaVersion = 11; // See note. + static const int latestSchemaVersion = 12; // See note. @override int get schemaVersion => latestSchemaVersion; @@ -259,6 +274,10 @@ class AppDatabase extends _$AppDatabase { 'value': Variable(firstAccountId), })); }, + from11To12: (Migrator m, Schema12 schema) async { + await m.addColumn(schema.accounts, schema.accounts.realmName); + await m.addColumn(schema.accounts, schema.accounts.realmIcon); + }, ); Future _createLatestSchema(Migrator m) async { diff --git a/lib/model/database.g.dart b/lib/model/database.g.dart index bfca5d5857..69f6d9d7a3 100644 --- a/lib/model/database.g.dart +++ b/lib/model/database.g.dart @@ -954,6 +954,26 @@ class $AccountsTable extends Accounts with TableInfo<$AccountsTable, Account> { type: DriftSqlType.string, requiredDuringInsert: true, ).withConverter($AccountsTable.$converterrealmUrl); + static const VerificationMeta _realmNameMeta = const VerificationMeta( + 'realmName', + ); + @override + late final GeneratedColumn realmName = GeneratedColumn( + 'realm_name', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + late final GeneratedColumnWithTypeConverter realmIcon = + GeneratedColumn( + 'realm_icon', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ).withConverter($AccountsTable.$converterrealmIconn); static const VerificationMeta _userIdMeta = const VerificationMeta('userId'); @override late final GeneratedColumn userId = GeneratedColumn( @@ -1029,6 +1049,8 @@ class $AccountsTable extends Accounts with TableInfo<$AccountsTable, Account> { List get $columns => [ id, realmUrl, + realmName, + realmIcon, userId, email, apiKey, @@ -1052,6 +1074,12 @@ class $AccountsTable extends Accounts with TableInfo<$AccountsTable, Account> { if (data.containsKey('id')) { context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); } + if (data.containsKey('realm_name')) { + context.handle( + _realmNameMeta, + realmName.isAcceptableOrUnknown(data['realm_name']!, _realmNameMeta), + ); + } if (data.containsKey('user_id')) { context.handle( _userIdMeta, @@ -1140,6 +1168,16 @@ class $AccountsTable extends Accounts with TableInfo<$AccountsTable, Account> { data['${effectivePrefix}realm_url'], )!, ), + realmName: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}realm_name'], + ), + realmIcon: $AccountsTable.$converterrealmIconn.fromSql( + attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}realm_icon'], + ), + ), userId: attachedDatabase.typeMapping.read( DriftSqlType.int, data['${effectivePrefix}user_id'], @@ -1177,6 +1215,9 @@ class $AccountsTable extends Accounts with TableInfo<$AccountsTable, Account> { } static TypeConverter $converterrealmUrl = const UriConverter(); + static TypeConverter $converterrealmIcon = const UriConverter(); + static TypeConverter $converterrealmIconn = + NullAwareTypeConverter.wrap($converterrealmIcon); } class Account extends DataClass implements Insertable { @@ -1193,6 +1234,20 @@ class Account extends DataClass implements Insertable { /// It never changes for a given account. final Uri realmUrl; + /// The name of the Zulip realm this account is on. + /// + /// This corresponds to [GetServerSettingsResult.realmName]. + /// + /// Nullable just because older versions of the app didn't store this. + final String? realmName; + + /// The icon URL of the Zulip realm this account is on. + /// + /// This corresponds to [GetServerSettingsResult.realmIcon]. + /// + /// Nullable just because older versions of the app didn't store this. + final Uri? realmIcon; + /// The Zulip user ID of this account. /// /// This is the identifier the server uses for the account. @@ -1207,6 +1262,8 @@ class Account extends DataClass implements Insertable { const Account({ required this.id, required this.realmUrl, + this.realmName, + this.realmIcon, required this.userId, required this.email, required this.apiKey, @@ -1224,6 +1281,14 @@ class Account extends DataClass implements Insertable { $AccountsTable.$converterrealmUrl.toSql(realmUrl), ); } + if (!nullToAbsent || realmName != null) { + map['realm_name'] = Variable(realmName); + } + if (!nullToAbsent || realmIcon != null) { + map['realm_icon'] = Variable( + $AccountsTable.$converterrealmIconn.toSql(realmIcon), + ); + } map['user_id'] = Variable(userId); map['email'] = Variable(email); map['api_key'] = Variable(apiKey); @@ -1242,6 +1307,12 @@ class Account extends DataClass implements Insertable { return AccountsCompanion( id: Value(id), realmUrl: Value(realmUrl), + realmName: realmName == null && nullToAbsent + ? const Value.absent() + : Value(realmName), + realmIcon: realmIcon == null && nullToAbsent + ? const Value.absent() + : Value(realmIcon), userId: Value(userId), email: Value(email), apiKey: Value(apiKey), @@ -1264,6 +1335,8 @@ class Account extends DataClass implements Insertable { return Account( id: serializer.fromJson(json['id']), realmUrl: serializer.fromJson(json['realmUrl']), + realmName: serializer.fromJson(json['realmName']), + realmIcon: serializer.fromJson(json['realmIcon']), userId: serializer.fromJson(json['userId']), email: serializer.fromJson(json['email']), apiKey: serializer.fromJson(json['apiKey']), @@ -1279,6 +1352,8 @@ class Account extends DataClass implements Insertable { return { 'id': serializer.toJson(id), 'realmUrl': serializer.toJson(realmUrl), + 'realmName': serializer.toJson(realmName), + 'realmIcon': serializer.toJson(realmIcon), 'userId': serializer.toJson(userId), 'email': serializer.toJson(email), 'apiKey': serializer.toJson(apiKey), @@ -1292,6 +1367,8 @@ class Account extends DataClass implements Insertable { Account copyWith({ int? id, Uri? realmUrl, + Value realmName = const Value.absent(), + Value realmIcon = const Value.absent(), int? userId, String? email, String? apiKey, @@ -1302,6 +1379,8 @@ class Account extends DataClass implements Insertable { }) => Account( id: id ?? this.id, realmUrl: realmUrl ?? this.realmUrl, + realmName: realmName.present ? realmName.value : this.realmName, + realmIcon: realmIcon.present ? realmIcon.value : this.realmIcon, userId: userId ?? this.userId, email: email ?? this.email, apiKey: apiKey ?? this.apiKey, @@ -1318,6 +1397,8 @@ class Account extends DataClass implements Insertable { return Account( id: data.id.present ? data.id.value : this.id, realmUrl: data.realmUrl.present ? data.realmUrl.value : this.realmUrl, + realmName: data.realmName.present ? data.realmName.value : this.realmName, + realmIcon: data.realmIcon.present ? data.realmIcon.value : this.realmIcon, userId: data.userId.present ? data.userId.value : this.userId, email: data.email.present ? data.email.value : this.email, apiKey: data.apiKey.present ? data.apiKey.value : this.apiKey, @@ -1341,6 +1422,8 @@ class Account extends DataClass implements Insertable { return (StringBuffer('Account(') ..write('id: $id, ') ..write('realmUrl: $realmUrl, ') + ..write('realmName: $realmName, ') + ..write('realmIcon: $realmIcon, ') ..write('userId: $userId, ') ..write('email: $email, ') ..write('apiKey: $apiKey, ') @@ -1356,6 +1439,8 @@ class Account extends DataClass implements Insertable { int get hashCode => Object.hash( id, realmUrl, + realmName, + realmIcon, userId, email, apiKey, @@ -1370,6 +1455,8 @@ class Account extends DataClass implements Insertable { (other is Account && other.id == this.id && other.realmUrl == this.realmUrl && + other.realmName == this.realmName && + other.realmIcon == this.realmIcon && other.userId == this.userId && other.email == this.email && other.apiKey == this.apiKey && @@ -1382,6 +1469,8 @@ class Account extends DataClass implements Insertable { class AccountsCompanion extends UpdateCompanion { final Value id; final Value realmUrl; + final Value realmName; + final Value realmIcon; final Value userId; final Value email; final Value apiKey; @@ -1392,6 +1481,8 @@ class AccountsCompanion extends UpdateCompanion { const AccountsCompanion({ this.id = const Value.absent(), this.realmUrl = const Value.absent(), + this.realmName = const Value.absent(), + this.realmIcon = const Value.absent(), this.userId = const Value.absent(), this.email = const Value.absent(), this.apiKey = const Value.absent(), @@ -1403,6 +1494,8 @@ class AccountsCompanion extends UpdateCompanion { AccountsCompanion.insert({ this.id = const Value.absent(), required Uri realmUrl, + this.realmName = const Value.absent(), + this.realmIcon = const Value.absent(), required int userId, required String email, required String apiKey, @@ -1419,6 +1512,8 @@ class AccountsCompanion extends UpdateCompanion { static Insertable custom({ Expression? id, Expression? realmUrl, + Expression? realmName, + Expression? realmIcon, Expression? userId, Expression? email, Expression? apiKey, @@ -1430,6 +1525,8 @@ class AccountsCompanion extends UpdateCompanion { return RawValuesInsertable({ if (id != null) 'id': id, if (realmUrl != null) 'realm_url': realmUrl, + if (realmName != null) 'realm_name': realmName, + if (realmIcon != null) 'realm_icon': realmIcon, if (userId != null) 'user_id': userId, if (email != null) 'email': email, if (apiKey != null) 'api_key': apiKey, @@ -1443,6 +1540,8 @@ class AccountsCompanion extends UpdateCompanion { AccountsCompanion copyWith({ Value? id, Value? realmUrl, + Value? realmName, + Value? realmIcon, Value? userId, Value? email, Value? apiKey, @@ -1454,6 +1553,8 @@ class AccountsCompanion extends UpdateCompanion { return AccountsCompanion( id: id ?? this.id, realmUrl: realmUrl ?? this.realmUrl, + realmName: realmName ?? this.realmName, + realmIcon: realmIcon ?? this.realmIcon, userId: userId ?? this.userId, email: email ?? this.email, apiKey: apiKey ?? this.apiKey, @@ -1475,6 +1576,14 @@ class AccountsCompanion extends UpdateCompanion { $AccountsTable.$converterrealmUrl.toSql(realmUrl.value), ); } + if (realmName.present) { + map['realm_name'] = Variable(realmName.value); + } + if (realmIcon.present) { + map['realm_icon'] = Variable( + $AccountsTable.$converterrealmIconn.toSql(realmIcon.value), + ); + } if (userId.present) { map['user_id'] = Variable(userId.value); } @@ -1504,6 +1613,8 @@ class AccountsCompanion extends UpdateCompanion { return (StringBuffer('AccountsCompanion(') ..write('id: $id, ') ..write('realmUrl: $realmUrl, ') + ..write('realmName: $realmName, ') + ..write('realmIcon: $realmIcon, ') ..write('userId: $userId, ') ..write('email: $email, ') ..write('apiKey: $apiKey, ') @@ -2099,6 +2210,8 @@ typedef $$AccountsTableCreateCompanionBuilder = AccountsCompanion Function({ Value id, required Uri realmUrl, + Value realmName, + Value realmIcon, required int userId, required String email, required String apiKey, @@ -2111,6 +2224,8 @@ typedef $$AccountsTableUpdateCompanionBuilder = AccountsCompanion Function({ Value id, Value realmUrl, + Value realmName, + Value realmIcon, Value userId, Value email, Value apiKey, @@ -2140,6 +2255,17 @@ class $$AccountsTableFilterComposer builder: (column) => ColumnWithTypeConverterFilters(column), ); + ColumnFilters get realmName => $composableBuilder( + column: $table.realmName, + builder: (column) => ColumnFilters(column), + ); + + ColumnWithTypeConverterFilters get realmIcon => + $composableBuilder( + column: $table.realmIcon, + builder: (column) => ColumnWithTypeConverterFilters(column), + ); + ColumnFilters get userId => $composableBuilder( column: $table.userId, builder: (column) => ColumnFilters(column), @@ -2195,6 +2321,16 @@ class $$AccountsTableOrderingComposer builder: (column) => ColumnOrderings(column), ); + ColumnOrderings get realmName => $composableBuilder( + column: $table.realmName, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get realmIcon => $composableBuilder( + column: $table.realmIcon, + builder: (column) => ColumnOrderings(column), + ); + ColumnOrderings get userId => $composableBuilder( column: $table.userId, builder: (column) => ColumnOrderings(column), @@ -2246,6 +2382,12 @@ class $$AccountsTableAnnotationComposer GeneratedColumnWithTypeConverter get realmUrl => $composableBuilder(column: $table.realmUrl, builder: (column) => column); + GeneratedColumn get realmName => + $composableBuilder(column: $table.realmName, builder: (column) => column); + + GeneratedColumnWithTypeConverter get realmIcon => + $composableBuilder(column: $table.realmIcon, builder: (column) => column); + GeneratedColumn get userId => $composableBuilder(column: $table.userId, builder: (column) => column); @@ -2306,6 +2448,8 @@ class $$AccountsTableTableManager ({ Value id = const Value.absent(), Value realmUrl = const Value.absent(), + Value realmName = const Value.absent(), + Value realmIcon = const Value.absent(), Value userId = const Value.absent(), Value email = const Value.absent(), Value apiKey = const Value.absent(), @@ -2316,6 +2460,8 @@ class $$AccountsTableTableManager }) => AccountsCompanion( id: id, realmUrl: realmUrl, + realmName: realmName, + realmIcon: realmIcon, userId: userId, email: email, apiKey: apiKey, @@ -2328,6 +2474,8 @@ class $$AccountsTableTableManager ({ Value id = const Value.absent(), required Uri realmUrl, + Value realmName = const Value.absent(), + Value realmIcon = const Value.absent(), required int userId, required String email, required String apiKey, @@ -2338,6 +2486,8 @@ class $$AccountsTableTableManager }) => AccountsCompanion.insert( id: id, realmUrl: realmUrl, + realmName: realmName, + realmIcon: realmIcon, userId: userId, email: email, apiKey: apiKey, diff --git a/lib/model/legacy_app_data.dart b/lib/model/legacy_app_data.dart index 5f6197f0fc..145b5277d3 100644 --- a/lib/model/legacy_app_data.dart +++ b/lib/model/legacy_app_data.dart @@ -94,6 +94,8 @@ Future migrateLegacyAppData(AppDatabase db) async { try { await db.createAccount(AccountsCompanion.insert( realmUrl: account.realm, + // no realmName; legacy app didn't record it + // no realmIcon; legacy app didn't record it userId: account.userId!, email: account.email, apiKey: account.apiKey, diff --git a/lib/model/schema_versions.g.dart b/lib/model/schema_versions.g.dart index 194eccd6b5..a6622b8df7 100644 --- a/lib/model/schema_versions.g.dart +++ b/lib/model/schema_versions.g.dart @@ -748,6 +748,116 @@ final class Schema11 extends i0.VersionedSchema { ); } +final class Schema12 extends i0.VersionedSchema { + Schema12({required super.database}) : super(version: 12); + @override + late final List entities = [ + globalSettings, + boolGlobalSettings, + intGlobalSettings, + accounts, + ]; + late final Shape6 globalSettings = Shape6( + source: i0.VersionedTable( + entityName: 'global_settings', + withoutRowId: false, + isStrict: false, + tableConstraints: [], + columns: [_column_9, _column_10, _column_13, _column_14, _column_15], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape3 boolGlobalSettings = Shape3( + source: i0.VersionedTable( + entityName: 'bool_global_settings', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(name)'], + columns: [_column_11, _column_12], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape7 intGlobalSettings = Shape7( + source: i0.VersionedTable( + entityName: 'int_global_settings', + withoutRowId: false, + isStrict: false, + tableConstraints: ['PRIMARY KEY(name)'], + columns: [_column_11, _column_16], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape8 accounts = Shape8( + source: i0.VersionedTable( + entityName: 'accounts', + withoutRowId: false, + isStrict: false, + tableConstraints: [ + 'UNIQUE(realm_url, user_id)', + 'UNIQUE(realm_url, email)', + ], + columns: [ + _column_0, + _column_1, + _column_17, + _column_18, + _column_2, + _column_3, + _column_4, + _column_5, + _column_6, + _column_7, + _column_8, + ], + attachedDatabase: database, + ), + alias: null, + ); +} + +class Shape8 extends i0.VersionedTable { + Shape8({required super.source, required super.alias}) : super.aliased(); + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get realmUrl => + columnsByName['realm_url']! as i1.GeneratedColumn; + i1.GeneratedColumn get realmName => + columnsByName['realm_name']! as i1.GeneratedColumn; + i1.GeneratedColumn get realmIcon => + columnsByName['realm_icon']! as i1.GeneratedColumn; + i1.GeneratedColumn get userId => + columnsByName['user_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get email => + columnsByName['email']! as i1.GeneratedColumn; + i1.GeneratedColumn get apiKey => + columnsByName['api_key']! as i1.GeneratedColumn; + i1.GeneratedColumn get zulipVersion => + columnsByName['zulip_version']! as i1.GeneratedColumn; + i1.GeneratedColumn get zulipMergeBase => + columnsByName['zulip_merge_base']! as i1.GeneratedColumn; + i1.GeneratedColumn get zulipFeatureLevel => + columnsByName['zulip_feature_level']! as i1.GeneratedColumn; + i1.GeneratedColumn get ackedPushToken => + columnsByName['acked_push_token']! as i1.GeneratedColumn; +} + +i1.GeneratedColumn _column_17(String aliasedName) => + i1.GeneratedColumn( + 'realm_name', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); +i1.GeneratedColumn _column_18(String aliasedName) => + i1.GeneratedColumn( + 'realm_icon', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i0.MigrationStepWithVersion migrationSteps({ required Future Function(i1.Migrator m, Schema2 schema) from1To2, required Future Function(i1.Migrator m, Schema3 schema) from2To3, @@ -759,6 +869,7 @@ i0.MigrationStepWithVersion migrationSteps({ required Future Function(i1.Migrator m, Schema9 schema) from8To9, required Future Function(i1.Migrator m, Schema10 schema) from9To10, required Future Function(i1.Migrator m, Schema11 schema) from10To11, + required Future Function(i1.Migrator m, Schema12 schema) from11To12, }) { return (currentVersion, database) async { switch (currentVersion) { @@ -812,6 +923,11 @@ i0.MigrationStepWithVersion migrationSteps({ final migrator = i1.Migrator(database, schema); await from10To11(migrator, schema); return 11; + case 11: + final schema = Schema12(database: database); + final migrator = i1.Migrator(database, schema); + await from11To12(migrator, schema); + return 12; default: throw ArgumentError.value('Unknown migration from $currentVersion'); } @@ -829,6 +945,7 @@ i1.OnUpgrade stepByStep({ required Future Function(i1.Migrator m, Schema9 schema) from8To9, required Future Function(i1.Migrator m, Schema10 schema) from9To10, required Future Function(i1.Migrator m, Schema11 schema) from10To11, + required Future Function(i1.Migrator m, Schema12 schema) from11To12, }) => i0.VersionedSchema.stepByStepHelper( step: migrationSteps( from1To2: from1To2, @@ -841,5 +958,6 @@ i1.OnUpgrade stepByStep({ from8To9: from8To9, from9To10: from9To10, from10To11: from10To11, + from11To12: from11To12, ), ); diff --git a/lib/model/store.dart b/lib/model/store.dart index fe503da36e..b9a772c18c 100644 --- a/lib/model/store.dart +++ b/lib/model/store.dart @@ -323,6 +323,28 @@ abstract class GlobalStore extends ChangeNotifier { zulipFeatureLevel: Value(data.zulipFeatureLevel))); } + /// Update an account with [realmName] and [realmIcon], returning the new version. + /// + /// The account must already exist in the store. + Future updateRealmData(int accountId, { + required String realmName, + required Uri realmIcon, + }) async { + final account = getAccount(accountId)!; + if (account.realmName == realmName && account.realmIcon == realmIcon) { + return account; + } + + return updateAccount(accountId, AccountsCompanion( + realmName: account.realmName != realmName + ? Value(realmName) + : const Value.absent(), + realmIcon: account.realmIcon != realmIcon + ? Value(realmIcon) + : const Value.absent(), + )); + } + /// Update an account in the underlying data store. Future doUpdateAccount(int accountId, AccountsCompanion data); @@ -401,6 +423,10 @@ abstract class PerAccountStoreBase { /// Always equal to `account.realmUrl` and `connection.realmUrl`. Uri get realmUrl => connection.realmUrl; + String? get realmName => account.realmName; + + Uri? get realmIcon => account.realmIcon; + /// Resolve [reference] as a URL relative to [realmUrl]. /// /// This returns null if [reference] fails to parse as a URL. @@ -1107,6 +1133,12 @@ class UpdateMachine { await globalStore.updateZulipVersionData(accountId, zulipVersionData); connection.zulipFeatureLevel = zulipVersionData.zulipFeatureLevel; } + if (globalStore.getAccount(accountId) != null) { + // TODO(#668) update realmName and realmIcon on realm update events + await globalStore.updateRealmData(accountId, + realmName: initialSnapshot.realmName, + realmIcon: initialSnapshot.realmIconUrl); + } final store = PerAccountStore.fromInitialSnapshot( globalStore: globalStore, diff --git a/lib/widgets/login.dart b/lib/widgets/login.dart index 465c5409bf..664a24509a 100644 --- a/lib/widgets/login.dart +++ b/lib/widgets/login.dart @@ -406,6 +406,8 @@ class _LoginPageState extends State { try { accountId = await globalStore.insertAccount(AccountsCompanion.insert( realmUrl: realmUrl, + realmName: Value(widget.serverSettings.realmName), + realmIcon: Value(widget.serverSettings.realmIcon), email: email, apiKey: apiKey, userId: userId, diff --git a/lib/widgets/share.dart b/lib/widgets/share.dart index df776e4d1e..b90f03d965 100644 --- a/lib/widgets/share.dart +++ b/lib/widgets/share.dart @@ -5,20 +5,25 @@ import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:mime/mime.dart'; +import '../api/core.dart'; +import '../api/route/realm.dart'; import '../generated/l10n/zulip_localizations.dart'; import '../host/android_intents.dart'; import '../log.dart'; import '../model/binding.dart'; import '../model/narrow.dart'; +import 'action_sheet.dart'; import 'app.dart'; -import 'color.dart'; import 'compose_box.dart'; +import 'content.dart'; import 'dialog.dart'; +import 'home.dart'; +import 'icons.dart'; import 'message_list.dart'; -import 'page.dart'; import 'recent_dm_conversations.dart'; import 'store.dart'; import 'subscription_list.dart'; +import 'text.dart'; import 'theme.dart'; // Responds to receiving shared content from other apps. @@ -99,16 +104,16 @@ class ShareService { mimeType: mimeType); }); - unawaited(navigator.push( - SharePage.buildRoute( - accountId: accountId, - sharedFiles: sharedFiles, - sharedText: intentSendEvent.extraText))); + ShareDialog.show( + pageContext: context, + initialAccountId: accountId, + sharedFiles: sharedFiles, + sharedText: intentSendEvent.extraText); } } -class SharePage extends StatelessWidget { - const SharePage({ +class ShareDialog extends StatelessWidget { + const ShareDialog({ super.key, required this.sharedFiles, required this.sharedText, @@ -117,16 +122,27 @@ class SharePage extends StatelessWidget { final Iterable? sharedFiles; final String? sharedText; - static AccountRoute buildRoute({ - required int accountId, + static void show({ + required BuildContext pageContext, + required int initialAccountId, required Iterable? sharedFiles, required String? sharedText, - }) { - return MaterialAccountWidgetRoute( - accountId: accountId, - page: SharePage( - sharedFiles: sharedFiles, - sharedText: sharedText)); + }) async { + unawaited(showModalBottomSheet( + context: pageContext, + // Clip.hardEdge looks bad; Clip.antiAliasWithSaveLayer looks pixel-perfect + // on my iPhone 13 Pro but is marked as "much slower": + // https://api.flutter.dev/flutter/dart-ui/Clip.html + clipBehavior: Clip.antiAlias, + useSafeArea: true, + isScrollControlled: true, + builder: (_) { + return PerAccountStoreWidget( + accountId: initialAccountId, + child: ShareDialog( + sharedFiles: sharedFiles, + sharedText: sharedText)); + })); } void _handleNarrowSelect(BuildContext context, Narrow narrow) { @@ -171,28 +187,89 @@ class SharePage extends StatelessWidget { @override Widget build(BuildContext context) { - final zulipLocalizations = ZulipLocalizations.of(context); + final globalStore = GlobalStoreWidget.of(context); + final store = PerAccountStoreWidget.of(context); final designVariables = DesignVariables.of(context); + final zulipLocalizations = ZulipLocalizations.of(context); + + // We should already have the `store.realmIcon` after the + // PerAccountStore has completed loading, hence the `!` here. + final realmIconUrl = store.realmUrl.resolveUri(store.realmIcon!); + + final labelStyle = TextStyle( + fontSize: 18, + height: 24 / 18, + letterSpacing: 0, + ).merge(weightVariableTextStyle(context, wght: 500)); + + Widget mkLabel(String text) { + return Text( + text, + style: labelStyle, + overflow: TextOverflow.ellipsis, + maxLines: 1); + } + + final hasMultipleAccounts = + List.unmodifiable(globalStore.accountIds).length > 1; return DefaultTabController( length: 2, - child: Scaffold( - appBar: AppBar( - title: Text(zulipLocalizations.sharePageTitle), - bottom: TabBar( - indicatorColor: designVariables.icon, - labelColor: designVariables.foreground, - unselectedLabelColor: designVariables.foreground.withFadedAlpha(0.7), + child: Column(children: [ + Row(children: [ + GestureDetector( + onTap: hasMultipleAccounts + ? () { + ChooseAccountForShareDialog.show( + pageContext: context, + selectedAccountId: store.accountId, + sharedFiles: sharedFiles, + sharedText: sharedText); + } + : null, + child: SizedBox.square( + dimension: 42, + child: Padding( + padding: const EdgeInsets.all(7), + child: RealmContentNetworkImage(realmIconUrl))), + ), + Expanded(child: TabBar( + labelStyle: labelStyle, + labelColor: designVariables.iconSelected, + unselectedLabelColor: designVariables.icon, + indicatorWeight: 0, + indicator: BoxDecoration(border: Border( + bottom: BorderSide( + color: designVariables.iconSelected, + width: 4.0))), + indicatorSize: TabBarIndicatorSize.label, + dividerHeight: 0, splashFactory: NoSplash.splashFactory, tabs: [ - Tab(text: zulipLocalizations.channelsPageTitle), - Tab(text: zulipLocalizations.recentDmConversationsPageTitle), + SizedBox( + height: 42, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + spacing: 4, + children: [ + Icon(size: 24, ZulipIcons.hash_italic), + Flexible(child: mkLabel(zulipLocalizations.channelsPageTitle)), + ])), + SizedBox( + height: 42, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + spacing: 4, + children: [ + Icon(size: 24, ZulipIcons.two_person), + Flexible(child: mkLabel(zulipLocalizations.recentDmConversationsPageTitle)), + ])), ])), - body: TabBarView(children: [ + ]), + Expanded(child: TabBarView(children: [ SubscriptionListPageBody( showTopicListButtonInActionSheet: false, hideChannelsIfUserCantSendMessage: true, - allowGoToAllChannels: false, onChannelSelect: (narrow) => _handleNarrowSelect(context, narrow), // TODO(#412) add onTopicSelect, Currently when user lands on the // channel feed page from subscription list page and they tap @@ -204,6 +281,131 @@ class SharePage extends StatelessWidget { RecentDmConversationsPageBody( hideDmsIfUserCantPost: true, onDmSelect: (narrow) => _handleNarrowSelect(context, narrow)), - ]))); + ])), + ])); + } +} + +class ChooseAccountForShareDialog extends StatefulWidget { + const ChooseAccountForShareDialog({ + super.key, + required this.sharedFiles, + required this.sharedText, + }); + + final Iterable? sharedFiles; + final String? sharedText; + + static void show({ + required BuildContext pageContext, + required int selectedAccountId, + required Iterable? sharedFiles, + required String? sharedText, + }) async { + unawaited(showModalBottomSheet( + context: pageContext, + // Clip.hardEdge looks bad; Clip.antiAliasWithSaveLayer looks pixel-perfect + // on my iPhone 13 Pro but is marked as "much slower": + // https://api.flutter.dev/flutter/dart-ui/Clip.html + clipBehavior: Clip.antiAlias, + useSafeArea: true, + isScrollControlled: true, + builder: (_) { + return SafeArea( + minimum: const EdgeInsets.only(bottom: 16), + child: ChooseAccountForShareDialog( + sharedFiles: sharedFiles, + sharedText: sharedText)); + })); + } + + @override + State createState() => _ChooseAccountForShareDialogState(); +} + +class _ChooseAccountForShareDialogState extends State { + late List accountIds; + bool _hasUpdatedAccountsOnce = false; + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + + final globalStore = GlobalStoreWidget.of(context); + accountIds = List.unmodifiable(globalStore.accountIds); + + if (_hasUpdatedAccountsOnce) return; + _hasUpdatedAccountsOnce = true; + + for (final accountId in accountIds) { + final account = globalStore.getAccount(accountId); + if (account == null) continue; + + unawaited(() async { + final GetServerSettingsResult serverSettings; + final connection = globalStore.apiConnection( + realmUrl: account.realmUrl, + zulipFeatureLevel: null); + try { + serverSettings = await getServerSettings(connection); + } catch (_) { + return; + } finally { + connection.close(); + } + + if (globalStore.getAccount(accountId) != null) { + await globalStore.updateRealmData( + accountId, + realmName: serverSettings.realmName, + realmIcon: serverSettings.realmIcon); + } + }()); + } + } + + @override + Widget build(BuildContext context) { + final globalStore = GlobalStoreWidget.of(context); + final zulipLocalizations = ZulipLocalizations.of(context); + + final content = SliverList.builder( + itemCount: accountIds.length, + itemBuilder: (context, index) { + final accountId = accountIds[index]; + final account = globalStore.getAccount(accountId); + if (account == null) { + return const SizedBox.shrink(); + } + + return ListTile( + onTap: () { + // First change home page account to the selected account. + HomePage.navigate(context, accountId: accountId); + // Then push a new share dialog for the selected account. + ShareDialog.show( + pageContext: context, + initialAccountId: accountId, + sharedFiles: widget.sharedFiles, + sharedText: widget.sharedText); + }, + leading: AspectRatio( + aspectRatio: 1, + child: account.realmIcon != null + ? Image.network( + account.realmUrl.resolveUri(account.realmIcon!).toString(), + headers: userAgentHeader()) + : null), + title: Text(account.realmName ?? account.realmUrl.toString()), + subtitle: Text(account.email)); + }); + + return DraggableScrollableModalBottomSheet( + header: Padding( + padding: const EdgeInsets.only(top: 8), + child: BottomSheetHeader(title: zulipLocalizations.shareChooseAccountLabel)), + contentSliver: SliverPadding( + padding: const EdgeInsets.symmetric(horizontal: 8), + sliver: content)); } } diff --git a/test/example_data.dart b/test/example_data.dart index 9bc82b9d6d..a7969524b2 100644 --- a/test/example_data.dart +++ b/test/example_data.dart @@ -92,6 +92,9 @@ int utcTimestamp([DateTime? dateTime]) { final Uri realmUrl = Uri.parse('https://chat.example/'); Uri get _realmUrl => realmUrl; +final Uri realmIcon = Uri.parse('/user_avatars/2/realm/icon.png?version=3'); +Uri get _realmIcon => realmIcon; + const String recentZulipVersion = '9.0'; const int recentZulipFeatureLevel = 382; const int futureZulipFeatureLevel = 9999; @@ -109,7 +112,7 @@ GetServerSettingsResult serverSettings({ bool? requireEmailFormatUsernames, Uri? realmUrl, String? realmName, - String? realmIcon, + Uri? realmIcon, String? realmDescription, bool? realmWebPublicAccessEnabled, }) { @@ -125,7 +128,7 @@ GetServerSettingsResult serverSettings({ requireEmailFormatUsernames: requireEmailFormatUsernames ?? true, realmUrl: realmUrl ?? _realmUrl, realmName: realmName ?? 'Example Zulip organization', - realmIcon: realmIcon ?? '$realmUrl/icon.png', + realmIcon: realmIcon ?? _realmIcon, realmDescription: realmDescription ?? 'An example Zulip organization', realmWebPublicAccessEnabled: realmWebPublicAccessEnabled ?? false, ); @@ -308,6 +311,8 @@ User user({ Account account({ int? id, Uri? realmUrl, + String? realmName, + Uri? realmIcon, required User user, String? apiKey, int? zulipFeatureLevel, @@ -323,6 +328,8 @@ Account account({ return Account( id: id ?? 1000, // TODO generate example IDs realmUrl: realmUrl ?? _realmUrl, + realmName: realmName ?? 'Example Zulip organization', + realmIcon: realmIcon ?? _realmIcon, email: email, apiKey: apiKey ?? 'aeouasdf', userId: user.userId, @@ -1281,11 +1288,13 @@ InitialSnapshot initialSnapshot({ RealmDeleteOwnMessagePolicy? realmDeleteOwnMessagePolicy, RealmWildcardMentionPolicy? realmWildcardMentionPolicy, bool? realmMandatoryTopics, + String? realmName, int? realmWaitingPeriodThreshold, int? realmMessageContentDeleteLimitSeconds, bool? realmAllowMessageEditing, int? realmMessageContentEditLimitSeconds, bool? realmEnableReadReceipts, + Uri? realmIconUrl, bool? realmPresenceDisabled, Map? realmDefaultExternalAccounts, int? maxFileUploadSizeMib, @@ -1341,11 +1350,13 @@ InitialSnapshot initialSnapshot({ realmDeleteOwnMessagePolicy: realmDeleteOwnMessagePolicy, realmWildcardMentionPolicy: realmWildcardMentionPolicy ?? RealmWildcardMentionPolicy.everyone, realmMandatoryTopics: realmMandatoryTopics ?? true, + realmName: realmName ?? 'Example Zulip organization', realmWaitingPeriodThreshold: realmWaitingPeriodThreshold ?? 0, realmMessageContentDeleteLimitSeconds: realmMessageContentDeleteLimitSeconds, realmAllowMessageEditing: realmAllowMessageEditing ?? true, realmMessageContentEditLimitSeconds: realmMessageContentEditLimitSeconds, realmEnableReadReceipts: realmEnableReadReceipts ?? true, + realmIconUrl: realmIconUrl ?? _realmIcon, realmPresenceDisabled: realmPresenceDisabled ?? false, realmDefaultExternalAccounts: realmDefaultExternalAccounts ?? {}, maxFileUploadSizeMib: maxFileUploadSizeMib ?? 25, diff --git a/test/model/database_test.dart b/test/model/database_test.dart index 47f93c8e84..e5bed24a1b 100644 --- a/test/model/database_test.dart +++ b/test/model/database_test.dart @@ -15,6 +15,7 @@ import 'schemas/schema_v4.dart' as v4; import 'schemas/schema_v5.dart' as v5; import 'schemas/schema_v10.dart' as v10; import 'schemas/schema_v11.dart' as v11; +import 'schemas/schema_v12.dart' as v12; import 'store_checks.dart'; void main() { @@ -189,6 +190,8 @@ void main() { test('create account', () async { final accountData = AccountsCompanion.insert( realmUrl: Uri.parse('https://chat.example/'), + realmName: Value('Example Zulip organization'), + realmIcon: Value(Uri.parse('/user_avatars/2/realm/icon.png?version=3')), userId: 1, email: 'asdf@example.org', apiKey: '1234', @@ -454,6 +457,35 @@ void main() { check(await after.select(after.intGlobalSettings).get()).isEmpty(); await after.close(); }); + + test('upgrade to v12, with data', () async { + final schema = await verifier.schemaAt(11); + final before = v11.DatabaseAtV11(schema.newConnection()); + await before.into(before.accounts).insert(v11.AccountsCompanion.insert( + realmUrl: 'https://chat.example/', + userId: 1, + email: 'asdf@example.org', + apiKey: '1234', + zulipVersion: '11.2', + zulipMergeBase: const Value('11.2'), + zulipFeatureLevel: 420, + )); + final accountV11 = await before.select(before.accounts).watchSingle().first; + await before.close(); + + final db = AppDatabase(schema.newConnection()); + await verifier.migrateAndValidate(db, 12); + await db.close(); + + final after = v12.DatabaseAtV12(schema.newConnection()); + final account = await after.select(after.accounts).getSingle(); + check(account.toJson()).deepEquals({ + ...accountV11.toJson(), + 'realmName': null, + 'realmIcon': null, + }); + await after.close(); + }); }); } diff --git a/test/model/schemas/drift_schema_v12.json b/test/model/schemas/drift_schema_v12.json new file mode 100644 index 0000000000..ffadc0f160 --- /dev/null +++ b/test/model/schemas/drift_schema_v12.json @@ -0,0 +1 @@ +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":false},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"global_settings","was_declared_in_moor":false,"columns":[{"name":"theme_setting","getter_name":"themeSetting","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumNameConverter(ThemeSetting.values)","dart_type_name":"ThemeSetting"}},{"name":"browser_preference","getter_name":"browserPreference","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumNameConverter(BrowserPreference.values)","dart_type_name":"BrowserPreference"}},{"name":"visit_first_unread","getter_name":"visitFirstUnread","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumNameConverter(VisitFirstUnreadSetting.values)","dart_type_name":"VisitFirstUnreadSetting"}},{"name":"mark_read_on_scroll","getter_name":"markReadOnScroll","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumNameConverter(MarkReadOnScrollSetting.values)","dart_type_name":"MarkReadOnScrollSetting"}},{"name":"legacy_upgrade_state","getter_name":"legacyUpgradeState","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumNameConverter(LegacyUpgradeState.values)","dart_type_name":"LegacyUpgradeState"}}],"is_virtual":false,"without_rowid":false,"constraints":[]}},{"id":1,"references":[],"type":"table","data":{"name":"bool_global_settings","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"value","getter_name":"value","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"value\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"value\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[],"explicit_pk":["name"]}},{"id":2,"references":[],"type":"table","data":{"name":"int_global_settings","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"value","getter_name":"value","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[],"explicit_pk":["name"]}},{"id":3,"references":[],"type":"table","data":{"name":"accounts","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","dialectAwareDefaultConstraints":{"sqlite":"PRIMARY KEY AUTOINCREMENT"},"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"realm_url","getter_name":"realmUrl","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const UriConverter()","dart_type_name":"Uri"}},{"name":"realm_name","getter_name":"realmName","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"realm_icon","getter_name":"realmIcon","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const UriConverter()","dart_type_name":"Uri"}},{"name":"user_id","getter_name":"userId","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"api_key","getter_name":"apiKey","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"zulip_version","getter_name":"zulipVersion","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"zulip_merge_base","getter_name":"zulipMergeBase","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"zulip_feature_level","getter_name":"zulipFeatureLevel","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"acked_push_token","getter_name":"ackedPushToken","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[],"unique_keys":[["realm_url","user_id"],["realm_url","email"]]}}]} \ No newline at end of file diff --git a/test/model/schemas/schema.dart b/test/model/schemas/schema.dart index 1d78a44317..073a86078f 100644 --- a/test/model/schemas/schema.dart +++ b/test/model/schemas/schema.dart @@ -14,6 +14,7 @@ import 'schema_v8.dart' as v8; import 'schema_v9.dart' as v9; import 'schema_v10.dart' as v10; import 'schema_v11.dart' as v11; +import 'schema_v12.dart' as v12; class GeneratedHelper implements SchemaInstantiationHelper { @override @@ -41,10 +42,12 @@ class GeneratedHelper implements SchemaInstantiationHelper { return v10.DatabaseAtV10(db); case 11: return v11.DatabaseAtV11(db); + case 12: + return v12.DatabaseAtV12(db); default: throw MissingSchemaException(version, versions); } } - static const versions = const [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; + static const versions = const [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; } diff --git a/test/model/schemas/schema_v12.dart b/test/model/schemas/schema_v12.dart new file mode 100644 index 0000000000..91775cbad3 --- /dev/null +++ b/test/model/schemas/schema_v12.dart @@ -0,0 +1,1277 @@ +// dart format width=80 +// GENERATED CODE, DO NOT EDIT BY HAND. +// ignore_for_file: type=lint +import 'package:drift/drift.dart'; + +class GlobalSettings extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + GlobalSettings(this.attachedDatabase, [this._alias]); + late final GeneratedColumn themeSetting = GeneratedColumn( + 'theme_setting', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn browserPreference = + GeneratedColumn( + 'browser_preference', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn visitFirstUnread = GeneratedColumn( + 'visit_first_unread', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn markReadOnScroll = GeneratedColumn( + 'mark_read_on_scroll', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn legacyUpgradeState = + GeneratedColumn( + 'legacy_upgrade_state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + themeSetting, + browserPreference, + visitFirstUnread, + markReadOnScroll, + legacyUpgradeState, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'global_settings'; + @override + Set get $primaryKey => const {}; + @override + GlobalSettingsData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return GlobalSettingsData( + themeSetting: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}theme_setting'], + ), + browserPreference: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}browser_preference'], + ), + visitFirstUnread: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}visit_first_unread'], + ), + markReadOnScroll: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}mark_read_on_scroll'], + ), + legacyUpgradeState: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}legacy_upgrade_state'], + ), + ); + } + + @override + GlobalSettings createAlias(String alias) { + return GlobalSettings(attachedDatabase, alias); + } +} + +class GlobalSettingsData extends DataClass + implements Insertable { + final String? themeSetting; + final String? browserPreference; + final String? visitFirstUnread; + final String? markReadOnScroll; + final String? legacyUpgradeState; + const GlobalSettingsData({ + this.themeSetting, + this.browserPreference, + this.visitFirstUnread, + this.markReadOnScroll, + this.legacyUpgradeState, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (!nullToAbsent || themeSetting != null) { + map['theme_setting'] = Variable(themeSetting); + } + if (!nullToAbsent || browserPreference != null) { + map['browser_preference'] = Variable(browserPreference); + } + if (!nullToAbsent || visitFirstUnread != null) { + map['visit_first_unread'] = Variable(visitFirstUnread); + } + if (!nullToAbsent || markReadOnScroll != null) { + map['mark_read_on_scroll'] = Variable(markReadOnScroll); + } + if (!nullToAbsent || legacyUpgradeState != null) { + map['legacy_upgrade_state'] = Variable(legacyUpgradeState); + } + return map; + } + + GlobalSettingsCompanion toCompanion(bool nullToAbsent) { + return GlobalSettingsCompanion( + themeSetting: themeSetting == null && nullToAbsent + ? const Value.absent() + : Value(themeSetting), + browserPreference: browserPreference == null && nullToAbsent + ? const Value.absent() + : Value(browserPreference), + visitFirstUnread: visitFirstUnread == null && nullToAbsent + ? const Value.absent() + : Value(visitFirstUnread), + markReadOnScroll: markReadOnScroll == null && nullToAbsent + ? const Value.absent() + : Value(markReadOnScroll), + legacyUpgradeState: legacyUpgradeState == null && nullToAbsent + ? const Value.absent() + : Value(legacyUpgradeState), + ); + } + + factory GlobalSettingsData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return GlobalSettingsData( + themeSetting: serializer.fromJson(json['themeSetting']), + browserPreference: serializer.fromJson( + json['browserPreference'], + ), + visitFirstUnread: serializer.fromJson(json['visitFirstUnread']), + markReadOnScroll: serializer.fromJson(json['markReadOnScroll']), + legacyUpgradeState: serializer.fromJson( + json['legacyUpgradeState'], + ), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'themeSetting': serializer.toJson(themeSetting), + 'browserPreference': serializer.toJson(browserPreference), + 'visitFirstUnread': serializer.toJson(visitFirstUnread), + 'markReadOnScroll': serializer.toJson(markReadOnScroll), + 'legacyUpgradeState': serializer.toJson(legacyUpgradeState), + }; + } + + GlobalSettingsData copyWith({ + Value themeSetting = const Value.absent(), + Value browserPreference = const Value.absent(), + Value visitFirstUnread = const Value.absent(), + Value markReadOnScroll = const Value.absent(), + Value legacyUpgradeState = const Value.absent(), + }) => GlobalSettingsData( + themeSetting: themeSetting.present ? themeSetting.value : this.themeSetting, + browserPreference: browserPreference.present + ? browserPreference.value + : this.browserPreference, + visitFirstUnread: visitFirstUnread.present + ? visitFirstUnread.value + : this.visitFirstUnread, + markReadOnScroll: markReadOnScroll.present + ? markReadOnScroll.value + : this.markReadOnScroll, + legacyUpgradeState: legacyUpgradeState.present + ? legacyUpgradeState.value + : this.legacyUpgradeState, + ); + GlobalSettingsData copyWithCompanion(GlobalSettingsCompanion data) { + return GlobalSettingsData( + themeSetting: data.themeSetting.present + ? data.themeSetting.value + : this.themeSetting, + browserPreference: data.browserPreference.present + ? data.browserPreference.value + : this.browserPreference, + visitFirstUnread: data.visitFirstUnread.present + ? data.visitFirstUnread.value + : this.visitFirstUnread, + markReadOnScroll: data.markReadOnScroll.present + ? data.markReadOnScroll.value + : this.markReadOnScroll, + legacyUpgradeState: data.legacyUpgradeState.present + ? data.legacyUpgradeState.value + : this.legacyUpgradeState, + ); + } + + @override + String toString() { + return (StringBuffer('GlobalSettingsData(') + ..write('themeSetting: $themeSetting, ') + ..write('browserPreference: $browserPreference, ') + ..write('visitFirstUnread: $visitFirstUnread, ') + ..write('markReadOnScroll: $markReadOnScroll, ') + ..write('legacyUpgradeState: $legacyUpgradeState') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + themeSetting, + browserPreference, + visitFirstUnread, + markReadOnScroll, + legacyUpgradeState, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is GlobalSettingsData && + other.themeSetting == this.themeSetting && + other.browserPreference == this.browserPreference && + other.visitFirstUnread == this.visitFirstUnread && + other.markReadOnScroll == this.markReadOnScroll && + other.legacyUpgradeState == this.legacyUpgradeState); +} + +class GlobalSettingsCompanion extends UpdateCompanion { + final Value themeSetting; + final Value browserPreference; + final Value visitFirstUnread; + final Value markReadOnScroll; + final Value legacyUpgradeState; + final Value rowid; + const GlobalSettingsCompanion({ + this.themeSetting = const Value.absent(), + this.browserPreference = const Value.absent(), + this.visitFirstUnread = const Value.absent(), + this.markReadOnScroll = const Value.absent(), + this.legacyUpgradeState = const Value.absent(), + this.rowid = const Value.absent(), + }); + GlobalSettingsCompanion.insert({ + this.themeSetting = const Value.absent(), + this.browserPreference = const Value.absent(), + this.visitFirstUnread = const Value.absent(), + this.markReadOnScroll = const Value.absent(), + this.legacyUpgradeState = const Value.absent(), + this.rowid = const Value.absent(), + }); + static Insertable custom({ + Expression? themeSetting, + Expression? browserPreference, + Expression? visitFirstUnread, + Expression? markReadOnScroll, + Expression? legacyUpgradeState, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (themeSetting != null) 'theme_setting': themeSetting, + if (browserPreference != null) 'browser_preference': browserPreference, + if (visitFirstUnread != null) 'visit_first_unread': visitFirstUnread, + if (markReadOnScroll != null) 'mark_read_on_scroll': markReadOnScroll, + if (legacyUpgradeState != null) + 'legacy_upgrade_state': legacyUpgradeState, + if (rowid != null) 'rowid': rowid, + }); + } + + GlobalSettingsCompanion copyWith({ + Value? themeSetting, + Value? browserPreference, + Value? visitFirstUnread, + Value? markReadOnScroll, + Value? legacyUpgradeState, + Value? rowid, + }) { + return GlobalSettingsCompanion( + themeSetting: themeSetting ?? this.themeSetting, + browserPreference: browserPreference ?? this.browserPreference, + visitFirstUnread: visitFirstUnread ?? this.visitFirstUnread, + markReadOnScroll: markReadOnScroll ?? this.markReadOnScroll, + legacyUpgradeState: legacyUpgradeState ?? this.legacyUpgradeState, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (themeSetting.present) { + map['theme_setting'] = Variable(themeSetting.value); + } + if (browserPreference.present) { + map['browser_preference'] = Variable(browserPreference.value); + } + if (visitFirstUnread.present) { + map['visit_first_unread'] = Variable(visitFirstUnread.value); + } + if (markReadOnScroll.present) { + map['mark_read_on_scroll'] = Variable(markReadOnScroll.value); + } + if (legacyUpgradeState.present) { + map['legacy_upgrade_state'] = Variable(legacyUpgradeState.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('GlobalSettingsCompanion(') + ..write('themeSetting: $themeSetting, ') + ..write('browserPreference: $browserPreference, ') + ..write('visitFirstUnread: $visitFirstUnread, ') + ..write('markReadOnScroll: $markReadOnScroll, ') + ..write('legacyUpgradeState: $legacyUpgradeState, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class BoolGlobalSettings extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + BoolGlobalSettings(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn value = GeneratedColumn( + 'value', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("value" IN (0, 1))', + ), + ); + @override + List get $columns => [name, value]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'bool_global_settings'; + @override + Set get $primaryKey => {name}; + @override + BoolGlobalSettingsData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return BoolGlobalSettingsData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + value: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}value'], + )!, + ); + } + + @override + BoolGlobalSettings createAlias(String alias) { + return BoolGlobalSettings(attachedDatabase, alias); + } +} + +class BoolGlobalSettingsData extends DataClass + implements Insertable { + final String name; + final bool value; + const BoolGlobalSettingsData({required this.name, required this.value}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['value'] = Variable(value); + return map; + } + + BoolGlobalSettingsCompanion toCompanion(bool nullToAbsent) { + return BoolGlobalSettingsCompanion(name: Value(name), value: Value(value)); + } + + factory BoolGlobalSettingsData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return BoolGlobalSettingsData( + name: serializer.fromJson(json['name']), + value: serializer.fromJson(json['value']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'value': serializer.toJson(value), + }; + } + + BoolGlobalSettingsData copyWith({String? name, bool? value}) => + BoolGlobalSettingsData( + name: name ?? this.name, + value: value ?? this.value, + ); + BoolGlobalSettingsData copyWithCompanion(BoolGlobalSettingsCompanion data) { + return BoolGlobalSettingsData( + name: data.name.present ? data.name.value : this.name, + value: data.value.present ? data.value.value : this.value, + ); + } + + @override + String toString() { + return (StringBuffer('BoolGlobalSettingsData(') + ..write('name: $name, ') + ..write('value: $value') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(name, value); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is BoolGlobalSettingsData && + other.name == this.name && + other.value == this.value); +} + +class BoolGlobalSettingsCompanion + extends UpdateCompanion { + final Value name; + final Value value; + final Value rowid; + const BoolGlobalSettingsCompanion({ + this.name = const Value.absent(), + this.value = const Value.absent(), + this.rowid = const Value.absent(), + }); + BoolGlobalSettingsCompanion.insert({ + required String name, + required bool value, + this.rowid = const Value.absent(), + }) : name = Value(name), + value = Value(value); + static Insertable custom({ + Expression? name, + Expression? value, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (value != null) 'value': value, + if (rowid != null) 'rowid': rowid, + }); + } + + BoolGlobalSettingsCompanion copyWith({ + Value? name, + Value? value, + Value? rowid, + }) { + return BoolGlobalSettingsCompanion( + name: name ?? this.name, + value: value ?? this.value, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (value.present) { + map['value'] = Variable(value.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('BoolGlobalSettingsCompanion(') + ..write('name: $name, ') + ..write('value: $value, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class IntGlobalSettings extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + IntGlobalSettings(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn value = GeneratedColumn( + 'value', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [name, value]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'int_global_settings'; + @override + Set get $primaryKey => {name}; + @override + IntGlobalSettingsData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return IntGlobalSettingsData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + value: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}value'], + )!, + ); + } + + @override + IntGlobalSettings createAlias(String alias) { + return IntGlobalSettings(attachedDatabase, alias); + } +} + +class IntGlobalSettingsData extends DataClass + implements Insertable { + final String name; + final int value; + const IntGlobalSettingsData({required this.name, required this.value}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['value'] = Variable(value); + return map; + } + + IntGlobalSettingsCompanion toCompanion(bool nullToAbsent) { + return IntGlobalSettingsCompanion(name: Value(name), value: Value(value)); + } + + factory IntGlobalSettingsData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return IntGlobalSettingsData( + name: serializer.fromJson(json['name']), + value: serializer.fromJson(json['value']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'value': serializer.toJson(value), + }; + } + + IntGlobalSettingsData copyWith({String? name, int? value}) => + IntGlobalSettingsData( + name: name ?? this.name, + value: value ?? this.value, + ); + IntGlobalSettingsData copyWithCompanion(IntGlobalSettingsCompanion data) { + return IntGlobalSettingsData( + name: data.name.present ? data.name.value : this.name, + value: data.value.present ? data.value.value : this.value, + ); + } + + @override + String toString() { + return (StringBuffer('IntGlobalSettingsData(') + ..write('name: $name, ') + ..write('value: $value') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(name, value); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is IntGlobalSettingsData && + other.name == this.name && + other.value == this.value); +} + +class IntGlobalSettingsCompanion + extends UpdateCompanion { + final Value name; + final Value value; + final Value rowid; + const IntGlobalSettingsCompanion({ + this.name = const Value.absent(), + this.value = const Value.absent(), + this.rowid = const Value.absent(), + }); + IntGlobalSettingsCompanion.insert({ + required String name, + required int value, + this.rowid = const Value.absent(), + }) : name = Value(name), + value = Value(value); + static Insertable custom({ + Expression? name, + Expression? value, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (value != null) 'value': value, + if (rowid != null) 'rowid': rowid, + }); + } + + IntGlobalSettingsCompanion copyWith({ + Value? name, + Value? value, + Value? rowid, + }) { + return IntGlobalSettingsCompanion( + name: name ?? this.name, + value: value ?? this.value, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (value.present) { + map['value'] = Variable(value.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('IntGlobalSettingsCompanion(') + ..write('name: $name, ') + ..write('value: $value, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class Accounts extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + Accounts(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'PRIMARY KEY AUTOINCREMENT', + ), + ); + late final GeneratedColumn realmUrl = GeneratedColumn( + 'realm_url', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn realmName = GeneratedColumn( + 'realm_name', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn realmIcon = GeneratedColumn( + 'realm_icon', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn email = GeneratedColumn( + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn apiKey = GeneratedColumn( + 'api_key', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn zulipVersion = GeneratedColumn( + 'zulip_version', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn zulipMergeBase = GeneratedColumn( + 'zulip_merge_base', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn zulipFeatureLevel = GeneratedColumn( + 'zulip_feature_level', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn ackedPushToken = GeneratedColumn( + 'acked_push_token', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + realmUrl, + realmName, + realmIcon, + userId, + email, + apiKey, + zulipVersion, + zulipMergeBase, + zulipFeatureLevel, + ackedPushToken, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'accounts'; + @override + Set get $primaryKey => {id}; + @override + List> get uniqueKeys => [ + {realmUrl, userId}, + {realmUrl, email}, + ]; + @override + AccountsData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return AccountsData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}id'], + )!, + realmUrl: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}realm_url'], + )!, + realmName: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}realm_name'], + ), + realmIcon: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}realm_icon'], + ), + userId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}user_id'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, + apiKey: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}api_key'], + )!, + zulipVersion: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}zulip_version'], + )!, + zulipMergeBase: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}zulip_merge_base'], + ), + zulipFeatureLevel: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}zulip_feature_level'], + )!, + ackedPushToken: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}acked_push_token'], + ), + ); + } + + @override + Accounts createAlias(String alias) { + return Accounts(attachedDatabase, alias); + } +} + +class AccountsData extends DataClass implements Insertable { + final int id; + final String realmUrl; + final String? realmName; + final String? realmIcon; + final int userId; + final String email; + final String apiKey; + final String zulipVersion; + final String? zulipMergeBase; + final int zulipFeatureLevel; + final String? ackedPushToken; + const AccountsData({ + required this.id, + required this.realmUrl, + this.realmName, + this.realmIcon, + required this.userId, + required this.email, + required this.apiKey, + required this.zulipVersion, + this.zulipMergeBase, + required this.zulipFeatureLevel, + this.ackedPushToken, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['realm_url'] = Variable(realmUrl); + if (!nullToAbsent || realmName != null) { + map['realm_name'] = Variable(realmName); + } + if (!nullToAbsent || realmIcon != null) { + map['realm_icon'] = Variable(realmIcon); + } + map['user_id'] = Variable(userId); + map['email'] = Variable(email); + map['api_key'] = Variable(apiKey); + map['zulip_version'] = Variable(zulipVersion); + if (!nullToAbsent || zulipMergeBase != null) { + map['zulip_merge_base'] = Variable(zulipMergeBase); + } + map['zulip_feature_level'] = Variable(zulipFeatureLevel); + if (!nullToAbsent || ackedPushToken != null) { + map['acked_push_token'] = Variable(ackedPushToken); + } + return map; + } + + AccountsCompanion toCompanion(bool nullToAbsent) { + return AccountsCompanion( + id: Value(id), + realmUrl: Value(realmUrl), + realmName: realmName == null && nullToAbsent + ? const Value.absent() + : Value(realmName), + realmIcon: realmIcon == null && nullToAbsent + ? const Value.absent() + : Value(realmIcon), + userId: Value(userId), + email: Value(email), + apiKey: Value(apiKey), + zulipVersion: Value(zulipVersion), + zulipMergeBase: zulipMergeBase == null && nullToAbsent + ? const Value.absent() + : Value(zulipMergeBase), + zulipFeatureLevel: Value(zulipFeatureLevel), + ackedPushToken: ackedPushToken == null && nullToAbsent + ? const Value.absent() + : Value(ackedPushToken), + ); + } + + factory AccountsData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return AccountsData( + id: serializer.fromJson(json['id']), + realmUrl: serializer.fromJson(json['realmUrl']), + realmName: serializer.fromJson(json['realmName']), + realmIcon: serializer.fromJson(json['realmIcon']), + userId: serializer.fromJson(json['userId']), + email: serializer.fromJson(json['email']), + apiKey: serializer.fromJson(json['apiKey']), + zulipVersion: serializer.fromJson(json['zulipVersion']), + zulipMergeBase: serializer.fromJson(json['zulipMergeBase']), + zulipFeatureLevel: serializer.fromJson(json['zulipFeatureLevel']), + ackedPushToken: serializer.fromJson(json['ackedPushToken']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'realmUrl': serializer.toJson(realmUrl), + 'realmName': serializer.toJson(realmName), + 'realmIcon': serializer.toJson(realmIcon), + 'userId': serializer.toJson(userId), + 'email': serializer.toJson(email), + 'apiKey': serializer.toJson(apiKey), + 'zulipVersion': serializer.toJson(zulipVersion), + 'zulipMergeBase': serializer.toJson(zulipMergeBase), + 'zulipFeatureLevel': serializer.toJson(zulipFeatureLevel), + 'ackedPushToken': serializer.toJson(ackedPushToken), + }; + } + + AccountsData copyWith({ + int? id, + String? realmUrl, + Value realmName = const Value.absent(), + Value realmIcon = const Value.absent(), + int? userId, + String? email, + String? apiKey, + String? zulipVersion, + Value zulipMergeBase = const Value.absent(), + int? zulipFeatureLevel, + Value ackedPushToken = const Value.absent(), + }) => AccountsData( + id: id ?? this.id, + realmUrl: realmUrl ?? this.realmUrl, + realmName: realmName.present ? realmName.value : this.realmName, + realmIcon: realmIcon.present ? realmIcon.value : this.realmIcon, + userId: userId ?? this.userId, + email: email ?? this.email, + apiKey: apiKey ?? this.apiKey, + zulipVersion: zulipVersion ?? this.zulipVersion, + zulipMergeBase: zulipMergeBase.present + ? zulipMergeBase.value + : this.zulipMergeBase, + zulipFeatureLevel: zulipFeatureLevel ?? this.zulipFeatureLevel, + ackedPushToken: ackedPushToken.present + ? ackedPushToken.value + : this.ackedPushToken, + ); + AccountsData copyWithCompanion(AccountsCompanion data) { + return AccountsData( + id: data.id.present ? data.id.value : this.id, + realmUrl: data.realmUrl.present ? data.realmUrl.value : this.realmUrl, + realmName: data.realmName.present ? data.realmName.value : this.realmName, + realmIcon: data.realmIcon.present ? data.realmIcon.value : this.realmIcon, + userId: data.userId.present ? data.userId.value : this.userId, + email: data.email.present ? data.email.value : this.email, + apiKey: data.apiKey.present ? data.apiKey.value : this.apiKey, + zulipVersion: data.zulipVersion.present + ? data.zulipVersion.value + : this.zulipVersion, + zulipMergeBase: data.zulipMergeBase.present + ? data.zulipMergeBase.value + : this.zulipMergeBase, + zulipFeatureLevel: data.zulipFeatureLevel.present + ? data.zulipFeatureLevel.value + : this.zulipFeatureLevel, + ackedPushToken: data.ackedPushToken.present + ? data.ackedPushToken.value + : this.ackedPushToken, + ); + } + + @override + String toString() { + return (StringBuffer('AccountsData(') + ..write('id: $id, ') + ..write('realmUrl: $realmUrl, ') + ..write('realmName: $realmName, ') + ..write('realmIcon: $realmIcon, ') + ..write('userId: $userId, ') + ..write('email: $email, ') + ..write('apiKey: $apiKey, ') + ..write('zulipVersion: $zulipVersion, ') + ..write('zulipMergeBase: $zulipMergeBase, ') + ..write('zulipFeatureLevel: $zulipFeatureLevel, ') + ..write('ackedPushToken: $ackedPushToken') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + realmUrl, + realmName, + realmIcon, + userId, + email, + apiKey, + zulipVersion, + zulipMergeBase, + zulipFeatureLevel, + ackedPushToken, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is AccountsData && + other.id == this.id && + other.realmUrl == this.realmUrl && + other.realmName == this.realmName && + other.realmIcon == this.realmIcon && + other.userId == this.userId && + other.email == this.email && + other.apiKey == this.apiKey && + other.zulipVersion == this.zulipVersion && + other.zulipMergeBase == this.zulipMergeBase && + other.zulipFeatureLevel == this.zulipFeatureLevel && + other.ackedPushToken == this.ackedPushToken); +} + +class AccountsCompanion extends UpdateCompanion { + final Value id; + final Value realmUrl; + final Value realmName; + final Value realmIcon; + final Value userId; + final Value email; + final Value apiKey; + final Value zulipVersion; + final Value zulipMergeBase; + final Value zulipFeatureLevel; + final Value ackedPushToken; + const AccountsCompanion({ + this.id = const Value.absent(), + this.realmUrl = const Value.absent(), + this.realmName = const Value.absent(), + this.realmIcon = const Value.absent(), + this.userId = const Value.absent(), + this.email = const Value.absent(), + this.apiKey = const Value.absent(), + this.zulipVersion = const Value.absent(), + this.zulipMergeBase = const Value.absent(), + this.zulipFeatureLevel = const Value.absent(), + this.ackedPushToken = const Value.absent(), + }); + AccountsCompanion.insert({ + this.id = const Value.absent(), + required String realmUrl, + this.realmName = const Value.absent(), + this.realmIcon = const Value.absent(), + required int userId, + required String email, + required String apiKey, + required String zulipVersion, + this.zulipMergeBase = const Value.absent(), + required int zulipFeatureLevel, + this.ackedPushToken = const Value.absent(), + }) : realmUrl = Value(realmUrl), + userId = Value(userId), + email = Value(email), + apiKey = Value(apiKey), + zulipVersion = Value(zulipVersion), + zulipFeatureLevel = Value(zulipFeatureLevel); + static Insertable custom({ + Expression? id, + Expression? realmUrl, + Expression? realmName, + Expression? realmIcon, + Expression? userId, + Expression? email, + Expression? apiKey, + Expression? zulipVersion, + Expression? zulipMergeBase, + Expression? zulipFeatureLevel, + Expression? ackedPushToken, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (realmUrl != null) 'realm_url': realmUrl, + if (realmName != null) 'realm_name': realmName, + if (realmIcon != null) 'realm_icon': realmIcon, + if (userId != null) 'user_id': userId, + if (email != null) 'email': email, + if (apiKey != null) 'api_key': apiKey, + if (zulipVersion != null) 'zulip_version': zulipVersion, + if (zulipMergeBase != null) 'zulip_merge_base': zulipMergeBase, + if (zulipFeatureLevel != null) 'zulip_feature_level': zulipFeatureLevel, + if (ackedPushToken != null) 'acked_push_token': ackedPushToken, + }); + } + + AccountsCompanion copyWith({ + Value? id, + Value? realmUrl, + Value? realmName, + Value? realmIcon, + Value? userId, + Value? email, + Value? apiKey, + Value? zulipVersion, + Value? zulipMergeBase, + Value? zulipFeatureLevel, + Value? ackedPushToken, + }) { + return AccountsCompanion( + id: id ?? this.id, + realmUrl: realmUrl ?? this.realmUrl, + realmName: realmName ?? this.realmName, + realmIcon: realmIcon ?? this.realmIcon, + userId: userId ?? this.userId, + email: email ?? this.email, + apiKey: apiKey ?? this.apiKey, + zulipVersion: zulipVersion ?? this.zulipVersion, + zulipMergeBase: zulipMergeBase ?? this.zulipMergeBase, + zulipFeatureLevel: zulipFeatureLevel ?? this.zulipFeatureLevel, + ackedPushToken: ackedPushToken ?? this.ackedPushToken, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (realmUrl.present) { + map['realm_url'] = Variable(realmUrl.value); + } + if (realmName.present) { + map['realm_name'] = Variable(realmName.value); + } + if (realmIcon.present) { + map['realm_icon'] = Variable(realmIcon.value); + } + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (email.present) { + map['email'] = Variable(email.value); + } + if (apiKey.present) { + map['api_key'] = Variable(apiKey.value); + } + if (zulipVersion.present) { + map['zulip_version'] = Variable(zulipVersion.value); + } + if (zulipMergeBase.present) { + map['zulip_merge_base'] = Variable(zulipMergeBase.value); + } + if (zulipFeatureLevel.present) { + map['zulip_feature_level'] = Variable(zulipFeatureLevel.value); + } + if (ackedPushToken.present) { + map['acked_push_token'] = Variable(ackedPushToken.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('AccountsCompanion(') + ..write('id: $id, ') + ..write('realmUrl: $realmUrl, ') + ..write('realmName: $realmName, ') + ..write('realmIcon: $realmIcon, ') + ..write('userId: $userId, ') + ..write('email: $email, ') + ..write('apiKey: $apiKey, ') + ..write('zulipVersion: $zulipVersion, ') + ..write('zulipMergeBase: $zulipMergeBase, ') + ..write('zulipFeatureLevel: $zulipFeatureLevel, ') + ..write('ackedPushToken: $ackedPushToken') + ..write(')')) + .toString(); + } +} + +class DatabaseAtV12 extends GeneratedDatabase { + DatabaseAtV12(QueryExecutor e) : super(e); + late final GlobalSettings globalSettings = GlobalSettings(this); + late final BoolGlobalSettings boolGlobalSettings = BoolGlobalSettings(this); + late final IntGlobalSettings intGlobalSettings = IntGlobalSettings(this); + late final Accounts accounts = Accounts(this); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [ + globalSettings, + boolGlobalSettings, + intGlobalSettings, + accounts, + ]; + @override + int get schemaVersion => 12; +} diff --git a/test/model/store_checks.dart b/test/model/store_checks.dart index 36a59c2e16..dac078a434 100644 --- a/test/model/store_checks.dart +++ b/test/model/store_checks.dart @@ -19,6 +19,8 @@ extension GlobalSettingsDataChecks on Subject { extension AccountChecks on Subject { Subject get id => has((x) => x.id, 'id'); Subject get realmUrl => has((x) => x.realmUrl, 'realmUrl'); + Subject get realmName => has((x) => x.realmName, 'realmName'); + Subject get realmIcon => has((x) => x.realmIcon, 'realmIcon'); Subject get userId => has((x) => x.userId, 'userId'); Subject get email => has((x) => x.email, 'email'); Subject get apiKey => has((x) => x.apiKey, 'apiKey'); diff --git a/test/model/store_test.dart b/test/model/store_test.dart index e522174507..27c94bb8dd 100644 --- a/test/model/store_test.dart +++ b/test/model/store_test.dart @@ -350,7 +350,7 @@ void main() { // TODO test database gets updated correctly (an integration test with sqlite?) }); - + test('GlobalStore.updateZulipVersionData', () async { final [currentZulipVersion, newZulipVersion ] = ['10.0-beta2-302-gf5b08b11f4', '10.0-beta2-351-g75ac8fe961']; @@ -376,6 +376,20 @@ void main() { zulipFeatureLevel: newZulipFeatureLevel)); }); + test('GlobalStore.updateRealmData', () async { + final selfAccount = eg.selfAccount.copyWith( + realmName: Value('Organization A'), + realmIcon: Value(Uri.parse('/image-a.png'))); + final globalStore = eg.globalStore(accounts: [selfAccount]); + final updated = await globalStore.updateRealmData(selfAccount.id, + realmName: 'Organization B', + realmIcon: Uri.parse('/image-b.png')); + check(globalStore.getAccount(selfAccount.id)).identicalTo(updated); + check(updated).equals(selfAccount.copyWith( + realmName: Value('Organization B'), + realmIcon: Value(Uri.parse('/image-b.png')))); + }); + group('GlobalStore.removeAccount', () { void checkGlobalStore(GlobalStore store, int accountId, { required bool expectAccount, @@ -509,18 +523,24 @@ void main() { test('updates account from snapshot', () => awaitFakeAsync((async) async { final account = eg.account(user: eg.selfUser, + realmName: 'Organization A', + realmIcon: Uri.parse('/image-a.png'), zulipVersion: '6.0+gabcd', zulipMergeBase: '6.0', zulipFeatureLevel: 123, ); await prepareStore(account: account); check(globalStore.getAccount(account.id)).isNotNull() + ..realmName.equals('Organization A') + ..realmIcon.equals(Uri.parse('/image-a.png')) ..zulipVersion.equals('6.0+gabcd') ..zulipMergeBase.equals('6.0') ..zulipFeatureLevel.equals(123); globalStore.useCachedApiConnections = true; connection.prepare(json: eg.initialSnapshot( + realmName: 'Organization B', + realmIconUrl: Uri.parse('/image-b.png'), zulipVersion: '8.0+g9876', zulipMergeBase: '8.0', zulipFeatureLevel: 234, @@ -529,6 +549,8 @@ void main() { updateMachine.debugPauseLoop(); check(globalStore.getAccount(account.id)).isNotNull() ..identicalTo(updateMachine.store.account) + ..realmName.equals('Organization B') + ..realmIcon.equals(Uri.parse('/image-b.png')) ..zulipVersion.equals('8.0+g9876') ..zulipMergeBase.equals('8.0') ..zulipFeatureLevel.equals(234); diff --git a/test/model/test_store.dart b/test/model/test_store.dart index e706f3139f..a0e50e8489 100644 --- a/test/model/test_store.dart +++ b/test/model/test_store.dart @@ -100,6 +100,8 @@ mixin _DatabaseMixin on GlobalStore { return Account( id: accountId, realmUrl: data.realmUrl.value, + realmName: data.realmName.value, + realmIcon: data.realmIcon.value, userId: data.userId.value, email: data.email.value, apiKey: data.apiKey.value, diff --git a/test/widgets/login_test.dart b/test/widgets/login_test.dart index 9bc4159f72..34e6e7e5c8 100644 --- a/test/widgets/login_test.dart +++ b/test/widgets/login_test.dart @@ -10,8 +10,8 @@ import 'package:zulip/api/model/web_auth.dart'; import 'package:zulip/api/route/account.dart'; import 'package:zulip/api/route/realm.dart'; import 'package:zulip/model/binding.dart'; -import 'package:zulip/model/database.dart'; import 'package:zulip/model/localizations.dart'; +import 'package:zulip/model/store.dart'; import 'package:zulip/widgets/app.dart'; import 'package:zulip/widgets/home.dart'; import 'package:zulip/widgets/login.dart'; @@ -248,7 +248,10 @@ void main() { } testWidgets('basic happy case', (tester) async { - final serverSettings = eg.serverSettings(); + final serverSettings = eg.serverSettings( + realmName: 'Some organization', + realmIcon: Uri.parse('/some-image.png'), + ); await prepare(tester, serverSettings); takeStartingRoutes(); check(pushedRoutes).isEmpty(); @@ -257,7 +260,9 @@ void main() { await login(tester, eg.selfAccount); check(testBinding.globalStore.accounts).single .equals(eg.selfAccount.copyWith( - id: testBinding.globalStore.accounts.single.id)); + id: testBinding.globalStore.accounts.single.id, + realmName: Value('Some organization'), + realmIcon: Value(Uri.parse('/some-image.png')))); }); testWidgets('logging into a second account', (tester) async { @@ -342,6 +347,8 @@ void main() { signupUrl: '/accounts/register/social/google', ); final serverSettings = eg.serverSettings( + realmName: 'Some organization', + realmIcon: Uri.parse('/some-image.png'), externalAuthenticationMethods: [method]); prepareBoringImageHttpClient(); // icon on social-auth button await prepare(tester, serverSettings); @@ -376,7 +383,10 @@ void main() { check(testBinding.takeCloseInAppWebViewCallCount()).equals(1); final account = testBinding.globalStore.accounts.single; - check(account).equals(eg.selfAccount.copyWith(id: account.id)); + check(account).equals(eg.selfAccount.copyWith( + id: account.id, + realmName: Value('Some organization'), + realmIcon: Value(Uri.parse('/some-image.png')))); check(pushedRoutes).single.isA() ..accountId.equals(account.id) ..page.isA();