@@ -35,6 +35,36 @@ mixin MessageStore {
35
35
/// All [Message] objects in the resulting list will be present in
36
36
/// [this.messages] .
37
37
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;
38
68
}
39
69
40
70
class MessageStoreImpl extends PerAccountStoreBase with MessageStore {
@@ -132,6 +162,56 @@ class MessageStoreImpl extends PerAccountStoreBase with MessageStore {
132
162
}
133
163
}
134
164
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
+
135
215
void handleUserTopicEvent (UserTopicEvent event) {
136
216
for (final view in _messageListViews) {
137
217
view.handleUserTopicEvent (event);
@@ -183,6 +263,12 @@ class MessageStoreImpl extends PerAccountStoreBase with MessageStore {
183
263
// The message is guaranteed to be edited.
184
264
// See also: https://zulip.com/api/get-events#update_message
185
265
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);
186
272
}
187
273
if (event.renderedContent != null ) {
188
274
assert (message.contentType == 'text/html' ,
@@ -245,6 +331,7 @@ class MessageStoreImpl extends PerAccountStoreBase with MessageStore {
245
331
void handleDeleteMessageEvent (DeleteMessageEvent event) {
246
332
for (final messageId in event.messageIds) {
247
333
messages.remove (messageId);
334
+ _editMessageRequests.remove (messageId);
248
335
}
249
336
for (final view in _messageListViews) {
250
337
view.handleDeleteMessageEvent (event);
0 commit comments