@@ -35,6 +35,36 @@ mixin MessageStore {
3535 /// All [Message] objects in the resulting list will be present in
3636 /// [this.messages] .
3737 void reconcileMessages (List <Message > messages);
38+
39+ /// Whether the current edit request for the given message, if any, has failed.
40+ ///
41+ /// Will be null if there is no current edit request.
42+ /// Will be false if the current request hasn't failed
43+ /// and the update-message event hasn't arrived.
44+ bool ? getEditMessageErrorStatus (int messageId);
45+
46+ /// Edit a message's content, via a request to the server.
47+ ///
48+ /// Should only be called when there is no current edit request for [messageId] ,
49+ /// i.e., [getEditMessageErrorStatus] returns null for [messageId] .
50+ ///
51+ /// See also:
52+ /// * [getEditMessageErrorStatus]
53+ /// * [takeFailedMessageEdit]
54+ void editMessage ({required int messageId, required String newContent});
55+
56+ /// Forgets the failed edit request and returns the attempted new content.
57+ ///
58+ /// Should only be called when there is a failed request,
59+ /// per [getEditMessageErrorStatus] .
60+ String takeFailedMessageEdit (int messageId);
61+ }
62+
63+ class _EditMessageRequestStatus {
64+ _EditMessageRequestStatus ({required this .hasError, required this .newContent});
65+
66+ bool hasError;
67+ final String newContent;
3868}
3969
4070class MessageStoreImpl extends PerAccountStoreBase with MessageStore {
@@ -132,6 +162,56 @@ class MessageStoreImpl extends PerAccountStoreBase with MessageStore {
132162 }
133163 }
134164
165+ @override
166+ bool ? getEditMessageErrorStatus (int messageId) =>
167+ _editMessageRequests[messageId]? .hasError;
168+
169+ final Map <int , _EditMessageRequestStatus > _editMessageRequests = {};
170+
171+ @override
172+ void editMessage ({
173+ required int messageId,
174+ required String newContent,
175+ }) async {
176+ if (_editMessageRequests.containsKey (messageId)) {
177+ throw StateError ('an edit request is already in progress' );
178+ }
179+
180+ _editMessageRequests[messageId] = _EditMessageRequestStatus (
181+ hasError: false , newContent: newContent);
182+ _notifyMessageListViewsForOneMessage (messageId);
183+ try {
184+ await updateMessage (connection, messageId: messageId, content: newContent);
185+ // On success, we'll clear the status from _editMessageRequests
186+ // when we get the event.
187+ } catch (e) {
188+ // TODO(log) if e is something unexpected
189+
190+ final status = _editMessageRequests[messageId];
191+ if (status == null ) {
192+ // The event actually arrived before this request failed
193+ // (can happen with network issues).
194+ // Or, the message was deleted.
195+ return ;
196+ }
197+ status.hasError = true ;
198+ _notifyMessageListViewsForOneMessage (messageId);
199+ }
200+ }
201+
202+ @override
203+ String takeFailedMessageEdit (int messageId) {
204+ final status = _editMessageRequests.remove (messageId);
205+ _notifyMessageListViewsForOneMessage (messageId);
206+ if (status == null ) {
207+ throw StateError ('called takeFailedMessageEdit, but no edit' );
208+ }
209+ if (! status.hasError) {
210+ throw StateError ("called takeFailedMessageEdit, but edit hasn't failed" );
211+ }
212+ return status.newContent;
213+ }
214+
135215 void handleUserTopicEvent (UserTopicEvent event) {
136216 for (final view in _messageListViews) {
137217 view.handleUserTopicEvent (event);
@@ -183,6 +263,12 @@ class MessageStoreImpl extends PerAccountStoreBase with MessageStore {
183263 // The message is guaranteed to be edited.
184264 // See also: https://zulip.com/api/get-events#update_message
185265 message.editState = MessageEditState .edited;
266+
267+ // Clear the edit-message progress feedback.
268+ // This makes a rare bug where we might clear the feedback too early,
269+ // if the user raced with themself to edit the same message
270+ // from multiple clients.
271+ _editMessageRequests.remove (message.id);
186272 }
187273 if (event.renderedContent != null ) {
188274 assert (message.contentType == 'text/html' ,
@@ -245,6 +331,7 @@ class MessageStoreImpl extends PerAccountStoreBase with MessageStore {
245331 void handleDeleteMessageEvent (DeleteMessageEvent event) {
246332 for (final messageId in event.messageIds) {
247333 messages.remove (messageId);
334+ _editMessageRequests.remove (messageId);
248335 }
249336 for (final view in _messageListViews) {
250337 view.handleDeleteMessageEvent (event);
0 commit comments