Skip to content

Edit-message (8/n): Implement edit-message! #1498

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

Merged
merged 12 commits into from
May 19, 2025
Merged
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
62 changes: 59 additions & 3 deletions assets/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@
"@actionSheetOptionUnstarMessage": {
"description": "Label for unstar button on action sheet."
},
"actionSheetOptionEditMessage": "Edit message",
"@actionSheetOptionEditMessage": {
"description": "Label for the 'Edit message' button in the message action sheet."
},
"actionSheetOptionMarkTopicAsRead": "Mark topic as read",
"@actionSheetOptionMarkTopicAsRead": {
"description": "Option to mark a specific topic as read in the action sheet."
Expand All @@ -168,7 +172,7 @@
"server": {"type": "String", "example": "https://example.com"}
}
},
"errorCouldNotFetchMessageSource": "Could not fetch message source",
"errorCouldNotFetchMessageSource": "Could not fetch message source.",
"@errorCouldNotFetchMessageSource": {
"description": "Error message when the source of a message could not be fetched."
},
Expand Down Expand Up @@ -219,6 +223,10 @@
"@errorMessageNotSent": {
"description": "Error message for compose box when a message could not be sent."
},
"errorMessageEditNotSaved": "Message not saved",
"@errorMessageEditNotSaved": {
"description": "Error message for compose box when a message edit could not be saved."
},
"errorLoginCouldNotConnect": "Failed to connect to server:\n{url}",
"@errorLoginCouldNotConnect": {
"description": "Error message when the app could not connect to the server.",
Expand Down Expand Up @@ -309,6 +317,10 @@
"@errorUnstarMessageFailedTitle": {
"description": "Error title when unstarring a message failed."
},
"errorCouldNotEditMessageTitle": "Could not edit message",
"@errorCouldNotEditMessageTitle": {
"description": "Error title when an exception prevented us from opening the compose box for editing a message."
},
"successLinkCopied": "Link copied",
"@successLinkCopied": {
"description": "Success message after copy link action completed."
Expand All @@ -329,6 +341,46 @@
"@errorBannerCannotPostInChannelLabel": {
"description": "Error-banner text replacing the compose box when you do not have permission to send a message to the channel."
},
"composeBoxBannerLabelEditMessage": "Edit message",
"@composeBoxBannerLabelEditMessage": {
"description": "Label text for the compose-box banner when you are editing a message."
},
"composeBoxBannerButtonCancel": "Cancel",
"@composeBoxBannerButtonCancel": {
"description": "Label text for the 'Cancel' button in the compose-box banner when you are editing a message."
},
"composeBoxBannerButtonSave": "Save",
"@composeBoxBannerButtonSave": {
"description": "Label text for the 'Save' button in the compose-box banner when you are editing a message."
},
"editAlreadyInProgressTitle": "Cannot edit message",
"@editAlreadyInProgressTitle": {
"description": "Error title when a message edit cannot be saved because there is another edit already in progress."
},
"editAlreadyInProgressMessage": "An edit is already in progress. Please wait for it to complete.",
"@editAlreadyInProgressMessage": {
"description": "Error message when a message edit cannot be saved because there is another edit already in progress."
},
"savingMessageEditLabel": "SAVING EDIT…",
"@savingMessageEditLabel": {
"description": "Text on a message in the message list saying that a message edit request is processing. (Use ALL CAPS for cased alphabets: Latin, Greek, Cyrillic, etc.)"
},
"savingMessageEditFailedLabel": "EDIT NOT SAVED",
"@savingMessageEditFailedLabel": {
"description": "Text on a message in the message list saying that a message edit request failed. (Use ALL CAPS for cased alphabets: Latin, Greek, Cyrillic, etc.)"
},
"discardDraftConfirmationDialogTitle": "Discard the message you’re writing?",
"@discardDraftConfirmationDialogTitle": {
"description": "Title for a confirmation dialog for discarding message text that was typed into the compose box."
},
"discardDraftConfirmationDialogMessage": "When you edit a message, the content that was previously in the compose box is discarded.",
"@discardDraftConfirmationDialogMessage": {
"description": "Message for a confirmation dialog for discarding message text that was typed into the compose box."
},
"discardDraftConfirmationDialogConfirmButton": "Discard",
"@discardDraftConfirmationDialogConfirmButton": {
"description": "Label for the 'Discard' button on a confirmation dialog for discarding message text that was typed into the compose box."
},
"composeBoxAttachFilesTooltip": "Attach files",
"@composeBoxAttachFilesTooltip": {
"description": "Tooltip for compose box icon to attach a file to the message."
Expand Down Expand Up @@ -367,6 +419,10 @@
"destination": {"type": "String", "example": "#channel name > topic name"}
}
},
"preparingEditMessageContentInput": "Preparing…",
"@preparingEditMessageContentInput": {
"description": "Hint text for content input when the compose box is preparing to edit a message."
},
"composeBoxSendTooltip": "Send",
"@composeBoxSendTooltip": {
"description": "Tooltip for send button in compose box."
Expand Down Expand Up @@ -561,7 +617,7 @@
"url": {"type": "String", "example": "http://chat.example.com/"}
}
},
"errorInvalidResponse": "The server sent an invalid response",
"errorInvalidResponse": "The server sent an invalid response.",
"@errorInvalidResponse": {
"description": "Error message when an API call returned an invalid response."
},
Expand Down Expand Up @@ -591,7 +647,7 @@
"httpStatus": {"type": "int", "example": "500"}
}
},
"errorVideoPlayerFailed": "Unable to play the video",
"errorVideoPlayerFailed": "Unable to play the video.",
"@errorVideoPlayerFailed": {
"description": "Error message when a video fails to play."
},
Expand Down
32 changes: 29 additions & 3 deletions lib/api/model/submessage.dart
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class Submessage {
widgetData: widgetData,
pollEventSubmessages: submessages.skip(1),
messageSenderId: messageSenderId,
debugSubmessages: kDebugMode ? submessages : null,
);
case UnsupportedWidgetData():
assert(debugLog('Unsupported widgetData: ${widgetData.json}'));
Expand Down Expand Up @@ -368,11 +369,13 @@ class Poll extends ChangeNotifier {
required PollWidgetData widgetData,
required Iterable<Submessage> pollEventSubmessages,
required int messageSenderId,
required List<Submessage>? debugSubmessages,
}) {
final poll = Poll._(
messageSenderId: messageSenderId,
question: widgetData.extraData.question,
options: widgetData.extraData.options,
debugSubmessages: debugSubmessages,
);

for (final submessage in pollEventSubmessages) {
Expand All @@ -386,17 +389,23 @@ class Poll extends ChangeNotifier {
required this.messageSenderId,
required this.question,
required List<String> options,
required List<Submessage>? debugSubmessages,
}) {
for (int index = 0; index < options.length; index += 1) {
// Initial poll options use a placeholder senderId.
// See [PollEventSubmessage.optionKey] for details.
_addOption(senderId: null, idx: index, option: options[index]);
}
if (kDebugMode) {
_debugSubmessages = debugSubmessages;
}
}

final int messageSenderId;
String question;

List<Submessage>? _debugSubmessages;

/// The limit of options any single user can add to a poll.
///
/// See https://github.com/zulip/zulip/blob/304d948416465c1a085122af5d752f03d6797003/web/shared/src/poll_data.ts#L69-L71
Expand All @@ -417,6 +426,14 @@ class Poll extends ChangeNotifier {
}
_applyEvent(event.senderId, pollEventSubmessage);
notifyListeners();

if (kDebugMode) {
assert(_debugSubmessages != null);
_debugSubmessages!.add(Submessage(
senderId: event.senderId,
msgType: event.msgType,
content: event.content));
}
}

void _applyEvent(int senderId, PollEventSubmessage event) {
Expand Down Expand Up @@ -472,9 +489,18 @@ class Poll extends ChangeNotifier {
}

static List<Submessage> toJson(Poll? poll) {
// Rather than maintaining a up-to-date submessages list, return as if it is
// empty, because we are not sending the submessages to the server anyway.
return [];
List<Submessage>? result;

if (kDebugMode) {
// Useful for setting up a message list with a poll message, which goes
// through this codepath (when preparing a fetch response).
result = poll?._debugSubmessages;
}

// In prod, rather than maintaining a up-to-date submessages list,
// return as if it is empty, because we are not sending the submessages
// to the server anyway.
return result ?? [];
}
}

Expand Down
90 changes: 87 additions & 3 deletions lib/generated/l10n/zulip_localizations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,12 @@ abstract class ZulipLocalizations {
/// **'Unstar message'**
String get actionSheetOptionUnstarMessage;

/// Label for the 'Edit message' button in the message action sheet.
///
/// In en, this message translates to:
/// **'Edit message'**
String get actionSheetOptionEditMessage;

/// Option to mark a specific topic as read in the action sheet.
///
/// In en, this message translates to:
Expand Down Expand Up @@ -359,7 +365,7 @@ abstract class ZulipLocalizations {
/// Error message when the source of a message could not be fetched.
///
/// In en, this message translates to:
/// **'Could not fetch message source'**
/// **'Could not fetch message source.'**
String get errorCouldNotFetchMessageSource;

/// Error message when copying the text of a message to the user's system clipboard failed.
Expand Down Expand Up @@ -414,6 +420,12 @@ abstract class ZulipLocalizations {
/// **'Message not sent'**
String get errorMessageNotSent;

/// Error message for compose box when a message edit could not be saved.
///
/// In en, this message translates to:
/// **'Message not saved'**
String get errorMessageEditNotSaved;

/// Error message when the app could not connect to the server.
///
/// In en, this message translates to:
Expand Down Expand Up @@ -526,6 +538,12 @@ abstract class ZulipLocalizations {
/// **'Failed to unstar message'**
String get errorUnstarMessageFailedTitle;

/// Error title when an exception prevented us from opening the compose box for editing a message.
///
/// In en, this message translates to:
/// **'Could not edit message'**
String get errorCouldNotEditMessageTitle;

/// Success message after copy link action completed.
///
/// In en, this message translates to:
Expand Down Expand Up @@ -556,6 +574,66 @@ abstract class ZulipLocalizations {
/// **'You do not have permission to post in this channel.'**
String get errorBannerCannotPostInChannelLabel;

/// Label text for the compose-box banner when you are editing a message.
///
/// In en, this message translates to:
/// **'Edit message'**
String get composeBoxBannerLabelEditMessage;

/// Label text for the 'Cancel' button in the compose-box banner when you are editing a message.
///
/// In en, this message translates to:
/// **'Cancel'**
String get composeBoxBannerButtonCancel;

/// Label text for the 'Save' button in the compose-box banner when you are editing a message.
///
/// In en, this message translates to:
/// **'Save'**
String get composeBoxBannerButtonSave;

/// Error title when a message edit cannot be saved because there is another edit already in progress.
///
/// In en, this message translates to:
/// **'Cannot edit message'**
String get editAlreadyInProgressTitle;

/// Error message when a message edit cannot be saved because there is another edit already in progress.
///
/// In en, this message translates to:
/// **'An edit is already in progress. Please wait for it to complete.'**
String get editAlreadyInProgressMessage;

/// Text on a message in the message list saying that a message edit request is processing. (Use ALL CAPS for cased alphabets: Latin, Greek, Cyrillic, etc.)
///
/// In en, this message translates to:
/// **'SAVING EDIT…'**
String get savingMessageEditLabel;

/// Text on a message in the message list saying that a message edit request failed. (Use ALL CAPS for cased alphabets: Latin, Greek, Cyrillic, etc.)
///
/// In en, this message translates to:
/// **'EDIT NOT SAVED'**
String get savingMessageEditFailedLabel;

/// Title for a confirmation dialog for discarding message text that was typed into the compose box.
///
/// In en, this message translates to:
/// **'Discard the message you’re writing?'**
String get discardDraftConfirmationDialogTitle;

/// Message for a confirmation dialog for discarding message text that was typed into the compose box.
///
/// In en, this message translates to:
/// **'When you edit a message, the content that was previously in the compose box is discarded.'**
String get discardDraftConfirmationDialogMessage;

/// Label for the 'Discard' button on a confirmation dialog for discarding message text that was typed into the compose box.
///
/// In en, this message translates to:
/// **'Discard'**
String get discardDraftConfirmationDialogConfirmButton;

/// Tooltip for compose box icon to attach a file to the message.
///
/// In en, this message translates to:
Expand Down Expand Up @@ -604,6 +682,12 @@ abstract class ZulipLocalizations {
/// **'Message {destination}'**
String composeBoxChannelContentHint(String destination);

/// Hint text for content input when the compose box is preparing to edit a message.
///
/// In en, this message translates to:
/// **'Preparing…'**
String get preparingEditMessageContentInput;

/// Tooltip for send button in compose box.
///
/// In en, this message translates to:
Expand Down Expand Up @@ -863,7 +947,7 @@ abstract class ZulipLocalizations {
/// Error message when an API call returned an invalid response.
///
/// In en, this message translates to:
/// **'The server sent an invalid response'**
/// **'The server sent an invalid response.'**
String get errorInvalidResponse;

/// Error message when a network request fails.
Expand Down Expand Up @@ -893,7 +977,7 @@ abstract class ZulipLocalizations {
/// Error message when a video fails to play.
///
/// In en, this message translates to:
/// **'Unable to play the video'**
/// **'Unable to play the video.'**
String get errorVideoPlayerFailed;

/// Error message when URL is empty
Expand Down
Loading