@@ -550,6 +550,15 @@ String? tryParseEmojiCodeToUnicode(String emojiCode) {
550550 }
551551}
552552
553+ /// The topic servers understand to mean "there is no topic".
554+ ///
555+ /// This should match
556+ /// https://github.com/zulip/zulip/blob/6.0/zerver/actions/message_edit.py#L940
557+ /// or similar logic at the latest `main` .
558+ // This is hardcoded in the server, and therefore untranslated; that's
559+ // zulip/zulip#3639.
560+ const String kNoTopicTopic = '(no topic)' ;
561+
553562/// The name of a Zulip topic.
554563// TODO(dart): Can we forbid calling Object members on this extension type?
555564// (The lack of "implements Object" ought to do that, but doesn't.)
@@ -600,6 +609,53 @@ extension type const TopicName(String _value) {
600609 /// using [canonicalize] .
601610 bool isSameAs (TopicName other) => canonicalize () == other.canonicalize ();
602611
612+ /// Process this topic to match how it would appear on a message object from
613+ /// the server.
614+ ///
615+ /// This returns the [TopicName] the server would be predicted to include
616+ /// in a message object resulting from sending to this [TopicName]
617+ /// in a [sendMessage] request.
618+ ///
619+ /// This [TopicName] is required to have no leading or trailing whitespace.
620+ ///
621+ /// For a client that supports empty topics, when FL>=334, the server converts
622+ /// `store.realmEmptyTopicDisplayName` to an empty string; when FL>=370,
623+ /// the server converts "(no topic)" to an empty string as well.
624+ ///
625+ /// See API docs:
626+ /// https://zulip.com/api/send-message#parameter-topic
627+ TopicName processLikeServer ({
628+ required int zulipFeatureLevel,
629+ required String ? realmEmptyTopicDisplayName,
630+ }) {
631+ assert (_value.trim () == _value);
632+ // TODO(server-10) simplify this away
633+ if (zulipFeatureLevel < 334 ) {
634+ // From the API docs:
635+ // > Before Zulip 10.0 (feature level 334), empty string was not a valid
636+ // > topic name for channel messages.
637+ assert (_value.isNotEmpty);
638+ return this ;
639+ }
640+
641+ // TODO(server-10) simplify this away
642+ if (zulipFeatureLevel < 370 && _value == kNoTopicTopic) {
643+ // From the API docs:
644+ // > Before Zulip 10.0 (feature level 370), "(no topic)" was not
645+ // > interpreted as an empty string.
646+ return TopicName (kNoTopicTopic);
647+ }
648+
649+ if (_value == kNoTopicTopic || _value == realmEmptyTopicDisplayName) {
650+ // From the API docs:
651+ // > When "(no topic)" or the value of realm_empty_topic_display_name
652+ // > found in the POST /register response is used for [topic],
653+ // > it is interpreted as an empty string.
654+ return TopicName ('' );
655+ }
656+ return TopicName (_value);
657+ }
658+
603659 TopicName .fromJson (this ._value);
604660
605661 String toJson () => apiName;
0 commit comments