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 19 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
@@ -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.",
60 changes: 59 additions & 1 deletion lib/api/model/model.dart
Original file line number Diff line number Diff line change
@@ -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.)
@@ -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;
@@ -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] refer to the same Zulip conversation.
bool isSameAs(Conversation other);
}

/// The conversation a stream message is in.
@JsonSerializable(fieldRename: FieldRename.snake, createToJson: false)
@@ -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.
@@ -635,6 +678,21 @@ class DmConversation extends Conversation {

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

bool _equalIdSequences(Iterable<int> xs, Iterable<int> ys) {
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;
}

@override
bool isSameAs(Conversation other) {
if (other is! DmConversation) return false;
return _equalIdSequences(allRecipientIds, other.allRecipientIds);
}
}

/// A message or message-like object, for showing in a message list.
9 changes: 0 additions & 9 deletions lib/api/route/messages.dart
Original file line number Diff line number Diff line change
@@ -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, {
6 changes: 6 additions & 0 deletions lib/generated/l10n/zulip_localizations.dart
Original file line number Diff line number Diff line change
@@ -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:
3 changes: 3 additions & 0 deletions lib/generated/l10n/zulip_localizations_ar.dart
Original file line number Diff line number Diff line change
@@ -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)';
3 changes: 3 additions & 0 deletions lib/generated/l10n/zulip_localizations_en.dart
Original file line number Diff line number Diff line change
@@ -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)';
3 changes: 3 additions & 0 deletions lib/generated/l10n/zulip_localizations_ja.dart
Original file line number Diff line number Diff line change
@@ -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)';
3 changes: 3 additions & 0 deletions lib/generated/l10n/zulip_localizations_nb.dart
Original file line number Diff line number Diff line change
@@ -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)';
3 changes: 3 additions & 0 deletions lib/generated/l10n/zulip_localizations_pl.dart
Original file line number Diff line number Diff line change
@@ -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)';
3 changes: 3 additions & 0 deletions lib/generated/l10n/zulip_localizations_ru.dart
Original file line number Diff line number Diff line change
@@ -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)';
3 changes: 3 additions & 0 deletions lib/generated/l10n/zulip_localizations_sk.dart
Original file line number Diff line number Diff line change
@@ -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)';
3 changes: 3 additions & 0 deletions lib/generated/l10n/zulip_localizations_uk.dart
Original file line number Diff line number Diff line change
@@ -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)';
8 changes: 8 additions & 0 deletions lib/model/binding.dart
Original file line number Diff line number Diff line change
@@ -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.
@@ -365,6 +370,9 @@ class LiveZulipBinding extends ZulipBinding {
return url_launcher.closeInAppWebView();
}

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

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

Loading