diff --git a/lib/api/model/events.dart b/lib/api/model/events.dart index f8d89a1bac..b351ed5a03 100644 --- a/lib/api/model/events.dart +++ b/lib/api/model/events.dart @@ -25,6 +25,14 @@ abstract class Event { case 'update': return RealmUserUpdateEvent.fromJson(json); default: return UnexpectedEvent.fromJson(json); } + case 'stream': + final op = json['op'] as String; + switch (op) { + case 'create': return StreamCreateEvent.fromJson(json); + case 'delete': return StreamDeleteEvent.fromJson(json); + // TODO(#182): case 'update': … + default: return UnexpectedEvent.fromJson(json); + } case 'message': return MessageEvent.fromJson(json); case 'heartbeat': return HeartbeatEvent.fromJson(json); // TODO add many more event types @@ -179,6 +187,54 @@ class RealmUserUpdateEvent extends RealmUserEvent { Map toJson() => _$RealmUserUpdateEventToJson(this); } +/// A Zulip event of type `stream`. +abstract class StreamEvent extends Event { + @override + @JsonKey(includeToJson: true) + String get type => 'stream'; + + String get op; + + StreamEvent({required super.id}); +} + +/// A [StreamEvent] with op `create`: https://zulip.com/api/get-events#stream-create +@JsonSerializable(fieldRename: FieldRename.snake) +class StreamCreateEvent extends StreamEvent { + @override + String get op => 'create'; + + final List streams; + + StreamCreateEvent({required super.id, required this.streams}); + + factory StreamCreateEvent.fromJson(Map json) => + _$StreamCreateEventFromJson(json); + + @override + Map toJson() => _$StreamCreateEventToJson(this); +} + +/// A [StreamEvent] with op `delete`: https://zulip.com/api/get-events#stream-delete +@JsonSerializable(fieldRename: FieldRename.snake) +class StreamDeleteEvent extends StreamEvent { + @override + String get op => 'delete'; + + final List streams; + + StreamDeleteEvent({required super.id, required this.streams}); + + factory StreamDeleteEvent.fromJson(Map json) => + _$StreamDeleteEventFromJson(json); + + @override + Map toJson() => _$StreamDeleteEventToJson(this); +} + +// TODO(#182) StreamUpdateEvent, for a [StreamEvent] with op `update`: +// https://zulip.com/api/get-events#stream-update + /// 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 a5427da747..20d3d6d1eb 100644 --- a/lib/api/model/events.g.dart +++ b/lib/api/model/events.g.dart @@ -103,6 +103,36 @@ Map _$RealmUserUpdateEventToJson( 'new_email': instance.newEmail, }; +StreamCreateEvent _$StreamCreateEventFromJson(Map json) => + StreamCreateEvent( + id: json['id'] as int, + streams: (json['streams'] as List) + .map((e) => ZulipStream.fromJson(e as Map)) + .toList(), + ); + +Map _$StreamCreateEventToJson(StreamCreateEvent instance) => + { + 'id': instance.id, + 'type': instance.type, + 'streams': instance.streams, + }; + +StreamDeleteEvent _$StreamDeleteEventFromJson(Map json) => + StreamDeleteEvent( + id: json['id'] as int, + streams: (json['streams'] as List) + .map((e) => ZulipStream.fromJson(e as Map)) + .toList(), + ); + +Map _$StreamDeleteEventToJson(StreamDeleteEvent instance) => + { + 'id': instance.id, + 'type': instance.type, + 'streams': instance.streams, + }; + HeartbeatEvent _$HeartbeatEventFromJson(Map json) => HeartbeatEvent( id: json['id'] as int, diff --git a/lib/model/store.dart b/lib/model/store.dart index e7023ff3f1..734784bd46 100644 --- a/lib/model/store.dart +++ b/lib/model/store.dart @@ -239,6 +239,19 @@ class PerAccountStore extends ChangeNotifier { } autocompleteViewManager.handleRealmUserUpdateEvent(event); notifyListeners(); + } else if (event is StreamCreateEvent) { + assert(debugLog("server event: stream/create")); + streams.addEntries(event.streams.map((stream) => MapEntry(stream.streamId, stream))); + // (Don't touch `subscriptions`. If the user is subscribed to the stream, + // details will come in a later `subscription` event.) + notifyListeners(); + } else if (event is StreamDeleteEvent) { + assert(debugLog("server event: stream/delete")); + for (final stream in event.streams) { + streams.remove(stream.streamId); + subscriptions.remove(stream.streamId); + } + notifyListeners(); } else if (event is MessageEvent) { assert(debugLog("server event: message ${jsonEncode(event.message.toJson())}")); for (final view in _messageListViews) { diff --git a/test/model/test_store.dart b/test/model/test_store.dart index 5c411ae895..c97e22befd 100644 --- a/test/model/test_store.dart +++ b/test/model/test_store.dart @@ -68,4 +68,12 @@ extension PerAccountStoreTestExtension on PerAccountStore { addUser(user); } } + + void addStream(ZulipStream stream) { + addStreams([stream]); + } + + void addStreams(List streams) { + handleEvent(StreamCreateEvent(id: 1, streams: streams)); + } }