diff --git a/lib/api/model/events.dart b/lib/api/model/events.dart index 1b46132770..604604c287 100644 --- a/lib/api/model/events.dart +++ b/lib/api/model/events.dart @@ -19,6 +19,12 @@ abstract class Event { final type = json['type'] as String; switch (type) { case 'alert_words': return AlertWordsEvent.fromJson(json); + case 'realm_user': + switch(json['op'] as String) { + case 'update': return RealmUserUpdateEvent.fromJson(json); + // TODO add realm_user/add and realm_user/remove events + default: return UnexpectedEvent.fromJson(json); + } case 'message': return MessageEvent.fromJson(json); case 'heartbeat': return HeartbeatEvent.fromJson(json); // TODO add many more event types @@ -63,6 +69,76 @@ class AlertWordsEvent extends Event { Map toJson() => _$AlertWordsEventToJson(this); } +/// A Zulip event of type `realm_user` (only for op:update). +@JsonSerializable() +class RealmUserUpdateEvent extends Event { + @override + @JsonKey(includeToJson: true) + String get type => 'realm_user'; + + @override + @JsonKey(includeToJson: true) + String get op => 'update'; + + final RealmUserUpdateEventPerson person; + + RealmUserUpdateEvent({required super.id, required this.person}); + + factory RealmUserUpdateEvent.fromJson(Map json) => + _$RealmUserUpdateEventFromJson(json); + + @override + Map toJson() => _$RealmUserUpdateEventToJson(this); +} + +@JsonSerializable() +class RealmUserUpdateEventPerson { + final int user_id; + + final String? full_name; + + // TODO express that all four avatar-related properties will be present if any of them is + final String? avatar_url; + final String? avatar_url_medium; + final String? avatar_source; + final String? avatar_version; + + final String? timezone; + // final String? email; // Deprecated as redundant with user_id + + final int? bot_owner_id; + + final int? role; + + final bool? is_billing_admin; + + final String? delivery_email; // TODO Can also be 'None', distinct from null + + // final CustomProfileFieldValueUpdate? custom_profile_field; // TODO handle + + final String? new_email; + + RealmUserUpdateEventPerson({ + required this.user_id, + this.full_name, + this.avatar_url, + this.avatar_url_medium, + this.avatar_source, + this.avatar_version, + this.timezone, + this.bot_owner_id, + this.role, + this.is_billing_admin, // Can also be null? + this.delivery_email, + this.new_email, + }); + + factory RealmUserUpdateEventPerson.fromJson(Map json) => + _$RealmUserUpdateEventPersonFromJson(json); + + Map toJson() => _$RealmUserUpdateEventPersonToJson(this); +} + /// A Zulip event of type `message`. // TODO use [JsonSerializable] here too, using its customization features, // in order to skip the boilerplate in [fromJson] and [toJson]. diff --git a/lib/api/model/events.g.dart b/lib/api/model/events.g.dart index 20bc9c502a..517a6919e5 100644 --- a/lib/api/model/events.g.dart +++ b/lib/api/model/events.g.dart @@ -21,6 +21,57 @@ Map _$AlertWordsEventToJson(AlertWordsEvent instance) => 'alert_words': instance.alert_words, }; +RealmUserUpdateEvent _$RealmUserUpdateEventFromJson( + Map json) => + RealmUserUpdateEvent( + id: json['id'] as int, + person: RealmUserUpdateEventPerson.fromJson( + json['person'] as Map), + ); + +Map _$RealmUserUpdateEventToJson( + RealmUserUpdateEvent instance) => + { + 'id': instance.id, + 'type': instance.type, + 'op': instance.op, + 'person': instance.person, + }; + +RealmUserUpdateEventPerson _$RealmUserUpdateEventPersonFromJson( + Map json) => + RealmUserUpdateEventPerson( + user_id: json['user_id'] as int, + full_name: json['full_name'] as String?, + avatar_url: json['avatar_url'] as String?, + avatar_url_medium: json['avatar_url_medium'] as String?, + avatar_source: json['avatar_source'] as String?, + avatar_version: json['avatar_version'] as String?, + timezone: json['timezone'] as String?, + bot_owner_id: json['bot_owner_id'] as int?, + role: json['role'] as int?, + is_billing_admin: json['is_billing_admin'] as bool?, + delivery_email: json['delivery_email'] as String?, + new_email: json['new_email'] as String?, + ); + +Map _$RealmUserUpdateEventPersonToJson( + RealmUserUpdateEventPerson instance) => + { + 'user_id': instance.user_id, + 'full_name': instance.full_name, + 'avatar_url': instance.avatar_url, + 'avatar_url_medium': instance.avatar_url_medium, + 'avatar_source': instance.avatar_source, + 'avatar_version': instance.avatar_version, + 'timezone': instance.timezone, + 'bot_owner_id': instance.bot_owner_id, + 'role': instance.role, + 'is_billing_admin': instance.is_billing_admin, + 'delivery_email': instance.delivery_email, + 'new_email': instance.new_email, + }; + HeartbeatEvent _$HeartbeatEventFromJson(Map json) => HeartbeatEvent( id: json['id'] as int, diff --git a/lib/api/model/initial_snapshot.dart b/lib/api/model/initial_snapshot.dart index 312c637824..d93e4cc1cb 100644 --- a/lib/api/model/initial_snapshot.dart +++ b/lib/api/model/initial_snapshot.dart @@ -25,6 +25,9 @@ class InitialSnapshot { // TODO etc., etc. + final int user_id; + final String full_name; + InitialSnapshot({ this.queue_id, required this.last_event_id, @@ -34,6 +37,8 @@ class InitialSnapshot { required this.alert_words, required this.custom_profile_fields, required this.subscriptions, + required this.user_id, + required this.full_name, }); factory InitialSnapshot.fromJson(Map json) => diff --git a/lib/api/model/initial_snapshot.g.dart b/lib/api/model/initial_snapshot.g.dart index 42bbb4bf86..f142278b6c 100644 --- a/lib/api/model/initial_snapshot.g.dart +++ b/lib/api/model/initial_snapshot.g.dart @@ -22,6 +22,8 @@ InitialSnapshot _$InitialSnapshotFromJson(Map json) => subscriptions: (json['subscriptions'] as List) .map((e) => Subscription.fromJson(e as Map)) .toList(), + user_id: json['user_id'] as int, + full_name: json['full_name'] as String, ); Map _$InitialSnapshotToJson(InitialSnapshot instance) => @@ -34,4 +36,6 @@ Map _$InitialSnapshotToJson(InitialSnapshot instance) => 'alert_words': instance.alert_words, 'custom_profile_fields': instance.custom_profile_fields, 'subscriptions': instance.subscriptions, + 'user_id': instance.user_id, + 'full_name': instance.full_name, }; diff --git a/lib/model/store.dart b/lib/model/store.dart index 96d3080106..b2476221b5 100644 --- a/lib/model/store.dart +++ b/lib/model/store.dart @@ -65,6 +65,8 @@ class PerAccountStore extends ChangeNotifier { required this.last_event_id, required this.zulip_version, required this.subscriptions, + required this.user_id, + required this.full_name, }); /// Load the user's data from the server, and start an event queue going. @@ -93,6 +95,9 @@ class PerAccountStore extends ChangeNotifier { final String zulip_version; final Map subscriptions; + final int user_id; + String full_name; + // TODO lots more data. When adding, be sure to update handleEvent too. final Set _messageListViews = {}; @@ -144,6 +149,15 @@ class PerAccountStore extends ChangeNotifier { for (final view in _messageListViews) { view.maybeAddMessage(event.message); } + } else if (event is RealmUserUpdateEvent) { + debugPrint("server event: realm_user op:update"); + if (event.person.user_id == user_id) { + final String? new_full_name = event.person.full_name; + if (new_full_name != null) { + full_name = new_full_name; + notifyListeners(); + } + } } else if (event is UnexpectedEvent) { debugPrint("server event: ${jsonEncode(event.toJson())}"); // TODO log better } else { @@ -201,5 +215,7 @@ PerAccountStore processInitialSnapshot(Account account, last_event_id: initialSnapshot.last_event_id, zulip_version: initialSnapshot.zulip_version, subscriptions: subscriptions, + user_id: initialSnapshot.user_id, + full_name: initialSnapshot.full_name, ); } diff --git a/lib/widgets/app.dart b/lib/widgets/app.dart index 17336cffbe..b04a3eda18 100644 --- a/lib/widgets/app.dart +++ b/lib/widgets/app.dart @@ -57,6 +57,9 @@ class HomePage extends StatelessWidget { child: Column(children: [ const Text('🚧 Under construction 🚧'), const SizedBox(height: 12), + Text.rich(TextSpan( + text: 'You are: ', + children: [bold(store.full_name)])), Text.rich(TextSpan( text: 'Connected to: ', children: [bold(store.account.realmUrl)])), diff --git a/test/api/model/events_checks.dart b/test/api/model/events_checks.dart index 82e0256f0a..2bb6e1267a 100644 --- a/test/api/model/events_checks.dart +++ b/test/api/model/events_checks.dart @@ -17,6 +17,10 @@ extension AlertWordsEventChecks on Subject { Subject> get alert_words => has((e) => e.alert_words, 'alert_words'); } +extension RealmUserEventChecks on Subject { + Subject get person => has((e) => e.person, 'realm_user'); +} + extension MessageEventChecks on Subject { Subject get message => has((e) => e.message, 'message'); }