Skip to content

local echo (7/7): Support simplified version of local echo #1453

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions assets/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,10 @@
"@messageIsMovedLabel": {
"description": "Label for a moved message. (Use ALL CAPS for cased alphabets: Latin, Greek, Cyrillic, etc.)"
},
"messageIsntSentLabel": "MESSAGE ISN'T SENT. CHECK YOUR CONNECTION.",
"@messageIsntSentLabel": {
"description": "Label for a message that isn't sent. (Use ALL CAPS for cased alphabets: Latin, Greek, Cyrillic, etc.)"
},
"pollVoterNames": "({voterNames})",
"@pollVoterNames": {
"description": "The list of people who voted for a poll option, wrapped in parentheses.",
Expand Down
58 changes: 57 additions & 1 deletion lib/api/model/model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,15 @@ String? tryParseEmojiCodeToUnicode(String emojiCode) {
}
}

/// The topic servers understand to mean "there is no topic".
///
/// This should match
/// https://github.com/zulip/zulip/blob/6.0/zerver/actions/message_edit.py#L940
/// or similar logic at the latest `main`.
// This is hardcoded in the server, and therefore untranslated; that's
// zulip/zulip#3639.
const String kNoTopicTopic = '(no topic)';

/// The name of a Zulip topic.
// TODO(dart): Can we forbid calling Object members on this extension type?
// (The lack of "implements Object" ought to do that, but doesn't.)
Expand Down Expand Up @@ -586,6 +595,30 @@ extension type const TopicName(String _value) {
/// using [canonicalize].
bool isSameAs(TopicName other) => canonicalize() == other.canonicalize();

/// Convert this topic to match how it would appear on a message object from
/// the server, assuming the topic is originally for a send-message request.
///
/// For a client that does not support empty topics,
/// a modern server (FL>=334) would convert "(no topic)" and empty topics to
/// `store.realmEmptyTopicDisplayName`.
///
/// See also: https://zulip.com/api/send-message#parameter-topic
TopicName interpretAsServer({
required int zulipFeatureLevel,
required String? realmEmptyTopicDisplayName,
}) {
if (zulipFeatureLevel < 334) {
assert(_value.isNotEmpty);
return this;
}
if (_value == kNoTopicTopic || _value.isEmpty) {
// TODO(#1250): this assumes that the 'support_empty_topics'
// client_capability is false; update this when we set it to true
return TopicName(realmEmptyTopicDisplayName!);
}
return TopicName(_value);
}

TopicName.fromJson(this._value);

String toJson() => apiName;
Expand All @@ -596,7 +629,10 @@ extension type const TopicName(String _value) {
/// Different from [MessageDestination], this information comes from
/// [getMessages] or [getEvents], identifying the conversation that contains a
/// message.
sealed class Conversation {}
sealed class Conversation {
/// Whether [this] and [other] have the same canonical form.
bool isSameAs(Conversation other);
}

/// The conversation a stream message is in.
@JsonSerializable(fieldRename: FieldRename.snake, createToJson: false)
Expand All @@ -622,6 +658,13 @@ class StreamConversation extends Conversation {

factory StreamConversation.fromJson(Map<String, dynamic> json) =>
_$StreamConversationFromJson(json);

@override
bool isSameAs(Conversation other) {
return other is StreamConversation
&& streamId == other.streamId
&& topic.isSameAs(other.topic);
}
}

/// The conversation a DM message is in.
Expand All @@ -635,6 +678,19 @@ class DmConversation extends Conversation {

DmConversation({required this.allRecipientIds})
: assert(isSortedWithoutDuplicates(allRecipientIds.toList()));

@override
bool isSameAs(Conversation other) {
if (other is! DmConversation) return false;
final xs = allRecipientIds;
final ys = other.allRecipientIds;
if (xs.length != ys.length) return false;
final xs_ = xs.iterator; final ys_ = ys.iterator;
while (xs_.moveNext() && ys_.moveNext()) {
if (xs_.current != ys_.current) return false;
}
return true;
}
}

/// A message or message-like object, for showing in a message list.
Expand Down
9 changes: 0 additions & 9 deletions lib/api/route/messages.dart
Original file line number Diff line number Diff line change
Expand Up @@ -169,15 +169,6 @@ const int kMaxTopicLengthCodePoints = 60;
// https://zulip.com/api/send-message#parameter-content
const int kMaxMessageLengthCodePoints = 10000;

/// The topic servers understand to mean "there is no topic".
///
/// This should match
/// https://github.com/zulip/zulip/blob/6.0/zerver/actions/message_edit.py#L940
/// or similar logic at the latest `main`.
// This is hardcoded in the server, and therefore untranslated; that's
// zulip/zulip#3639.
const String kNoTopicTopic = '(no topic)';

/// https://zulip.com/api/send-message
Future<SendMessageResult> sendMessage(
ApiConnection connection, {
Expand Down
6 changes: 6 additions & 0 deletions lib/generated/l10n/zulip_localizations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1169,6 +1169,12 @@ abstract class ZulipLocalizations {
/// **'MOVED'**
String get messageIsMovedLabel;

/// Label for a message that isn't sent. (Use ALL CAPS for cased alphabets: Latin, Greek, Cyrillic, etc.)
///
/// In en, this message translates to:
/// **'MESSAGE ISN\'T SENT. CHECK YOUR CONNECTION.'**
String get messageIsntSentLabel;

/// The list of people who voted for a poll option, wrapped in parentheses.
///
/// In en, this message translates to:
Expand Down
3 changes: 3 additions & 0 deletions lib/generated/l10n/zulip_localizations_ar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,9 @@ class ZulipLocalizationsAr extends ZulipLocalizations {
@override
String get messageIsMovedLabel => 'MOVED';

@override
String get messageIsntSentLabel => 'MESSAGE ISN\'T SENT. CHECK YOUR CONNECTION.';

@override
String pollVoterNames(String voterNames) {
return '($voterNames)';
Expand Down
3 changes: 3 additions & 0 deletions lib/generated/l10n/zulip_localizations_en.dart
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,9 @@ class ZulipLocalizationsEn extends ZulipLocalizations {
@override
String get messageIsMovedLabel => 'MOVED';

@override
String get messageIsntSentLabel => 'MESSAGE ISN\'T SENT. CHECK YOUR CONNECTION.';

@override
String pollVoterNames(String voterNames) {
return '($voterNames)';
Expand Down
3 changes: 3 additions & 0 deletions lib/generated/l10n/zulip_localizations_ja.dart
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,9 @@ class ZulipLocalizationsJa extends ZulipLocalizations {
@override
String get messageIsMovedLabel => 'MOVED';

@override
String get messageIsntSentLabel => 'MESSAGE ISN\'T SENT. CHECK YOUR CONNECTION.';

@override
String pollVoterNames(String voterNames) {
return '($voterNames)';
Expand Down
3 changes: 3 additions & 0 deletions lib/generated/l10n/zulip_localizations_nb.dart
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,9 @@ class ZulipLocalizationsNb extends ZulipLocalizations {
@override
String get messageIsMovedLabel => 'MOVED';

@override
String get messageIsntSentLabel => 'MESSAGE ISN\'T SENT. CHECK YOUR CONNECTION.';

@override
String pollVoterNames(String voterNames) {
return '($voterNames)';
Expand Down
3 changes: 3 additions & 0 deletions lib/generated/l10n/zulip_localizations_pl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,9 @@ class ZulipLocalizationsPl extends ZulipLocalizations {
@override
String get messageIsMovedLabel => 'PRZENIESIONO';

@override
String get messageIsntSentLabel => 'MESSAGE ISN\'T SENT. CHECK YOUR CONNECTION.';

@override
String pollVoterNames(String voterNames) {
return '($voterNames)';
Expand Down
3 changes: 3 additions & 0 deletions lib/generated/l10n/zulip_localizations_ru.dart
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,9 @@ class ZulipLocalizationsRu extends ZulipLocalizations {
@override
String get messageIsMovedLabel => 'ПЕРЕМЕЩЕНО';

@override
String get messageIsntSentLabel => 'MESSAGE ISN\'T SENT. CHECK YOUR CONNECTION.';

@override
String pollVoterNames(String voterNames) {
return '($voterNames)';
Expand Down
3 changes: 3 additions & 0 deletions lib/generated/l10n/zulip_localizations_sk.dart
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,9 @@ class ZulipLocalizationsSk extends ZulipLocalizations {
@override
String get messageIsMovedLabel => 'PRESUNUTÉ';

@override
String get messageIsntSentLabel => 'MESSAGE ISN\'T SENT. CHECK YOUR CONNECTION.';

@override
String pollVoterNames(String voterNames) {
return '($voterNames)';
Expand Down
3 changes: 3 additions & 0 deletions lib/generated/l10n/zulip_localizations_uk.dart
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,9 @@ class ZulipLocalizationsUk extends ZulipLocalizations {
@override
String get messageIsMovedLabel => 'ПЕРЕМІЩЕНО';

@override
String get messageIsntSentLabel => 'MESSAGE ISN\'T SENT. CHECK YOUR CONNECTION.';

@override
String pollVoterNames(String voterNames) {
return '($voterNames)';
Expand Down
8 changes: 8 additions & 0 deletions lib/model/binding.dart
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@ abstract class ZulipBinding {
/// This wraps [url_launcher.closeInAppWebView].
Future<void> closeInAppWebView();

/// Provides access to the current UTC date and time.
///
/// Outside tests, this just calls [DateTime.timestamp].
DateTime utcNow();

/// Provides access to a new stopwatch.
///
/// Outside tests, this just calls the [Stopwatch] constructor.
Expand Down Expand Up @@ -365,6 +370,9 @@ class LiveZulipBinding extends ZulipBinding {
return url_launcher.closeInAppWebView();
}

@override
DateTime utcNow() => DateTime.timestamp();

@override
Stopwatch stopwatch() => Stopwatch();

Expand Down
Loading