Skip to content

Commit 7bf8458

Browse files
gnpricechrisbobbe
authored andcommitted
api [nfc]: Introduce TopicName extension type
We'll use this to make a type-level distinction between a string that's being used to name a topic, and just any old string. That will help us in particular with #1250 the "general chat" feature, where the way we show a topic name to the user stops being necessarily just the same string that it appears as in the API. The next phase will be to migrate a bunch of places in our code to refer to TopicName when that's what they mean, instead of just String. During this phase, a TopicName can be freely used as a String, but not vice versa. So we'll do the migrations mostly in order of the data flow, from upstream to downstream. That will allow us to do them over a series of individually coherent commits, with a minimum of occasions where we temporarily introduce a conversion that won't be needed in the final result. That means: first, test data; then topics returned from the API to our code; then our internal models; then back to the API, this time for topics passed to the API from our code. After we have the type in place all over where it belongs, we'll start making use of the distinction, and then enforcing it.
1 parent bbe2d4c commit 7bf8458

File tree

5 files changed

+13
-5
lines changed

5 files changed

+13
-5
lines changed

lib/api/model/model.dart

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,14 @@ enum MessageFlag {
655655
String toJson() => _$MessageFlagEnumMap[this]!;
656656
}
657657

658+
/// The name of a Zulip topic.
659+
// TODO(#1250): Migrate all implicit uses as String; remove "implements String".
660+
extension type const TopicName(String _value) implements String {
661+
TopicName.fromJson(this._value);
662+
663+
String toJson() => _value;
664+
}
665+
658666
@JsonSerializable(fieldRename: FieldRename.snake)
659667
class StreamMessage extends Message {
660668
@override
@@ -674,7 +682,7 @@ class StreamMessage extends Message {
674682
// that will need new UI that we'll design then as part of that feature,
675683
// and ignoring the topics seems as good a fallback behavior as any.
676684
@JsonKey(name: 'subject')
677-
String topic;
685+
TopicName topic;
678686

679687
StreamMessage({
680688
required super.client,

lib/api/model/model.g.dart

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/model/message.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ class MessageStoreImpl with MessageStore {
221221
}
222222

223223
if (newTopic != null) {
224-
message.topic = newTopic;
224+
message.topic = TopicName(newTopic);
225225
}
226226

227227
if (!wasResolveOrUnresolve

test/api/model/model_checks.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ extension MessageChecks on Subject<Message> {
4848

4949
extension StreamMessageChecks on Subject<StreamMessage> {
5050
Subject<String?> get displayRecipient => has((e) => e.displayRecipient, 'displayRecipient');
51-
Subject<String> get topic => has((e) => e.topic, 'topic');
51+
Subject<TopicName> get topic => has((e) => e.topic, 'topic');
5252
}
5353

5454
extension ReactionsChecks on Subject<Reactions> {

test/api/model/model_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ void main() {
9090
check(Message.fromJson(baseStreamJson()
9191
..['subject'] = 'hello'
9292
)).isA<StreamMessage>()
93-
.topic.equals('hello');
93+
.topic.equals(const TopicName('hello'));
9494
});
9595

9696
test('match_subject -> matchTopic', () {

0 commit comments

Comments
 (0)