Correctly handle server errors in multi-deletion

pull/592/head
Maxim Shishmarev 6 years ago
parent 8677fb15a0
commit 7c96990119

@ -2487,30 +2487,32 @@
return false; return false;
} }
let success; const invalidMessages = messages.filter(m => !m.getServerId());
const shouldBeDeleted = []; const pendingMessages = messages.filter(m => m.getServerId());
if (messages.length > 1) {
success = await channelAPI.deleteMessages(
messages.map(m => m.getServerId())
);
} else {
success = await channelAPI.deleteMessages([messages[0].getServerId()]);
}
messages.forEach(m => { let deletedServerIds = [];
let ignoredServerIds = [];
// If the message has errors it is likely not saved if (pendingMessages.length > 0) {
// on the server, so we delete it locally unconditionally const result = await channelAPI.deleteMessages(
const shouldDeleteLocally = success || m.hasErrors() || !m.getServerId(); pendingMessages.map(m => m.getServerId())
);
if (shouldDeleteLocally) { deletedServerIds = result.deletedIds;
this.removeMessage(m.id); ignoredServerIds = result.ignoredIds;
} }
}); const toDeleteLocallyServerIds = _.union(
deletedServerIds,
ignoredServerIds
);
let toDeleteLocally = messages.filter(m =>
toDeleteLocallyServerIds.includes(m.getServerId())
);
toDeleteLocally = _.union(toDeleteLocally, invalidMessages);
toDeleteLocally.forEach(m => this.removeMessage(m.id));
/// TODO: not sure what to return here return toDeleteLocally;
return shouldDeleteLocally;
}, },
removeMessage(messageId) { removeMessage(messageId) {

@ -525,18 +525,42 @@ class LokiPublicChannelAPI {
this.modStatus ? `loki/v1/moderation/messages` : `loki/v1/messages`, this.modStatus ? `loki/v1/moderation/messages` : `loki/v1/messages`,
{ method: 'DELETE', params: { ids: serverIds } } { method: 'DELETE', params: { ids: serverIds } }
); );
if (!res.err && res.response) { if (!res.err) {
const deletedIds = res.response.data
.filter(d => d.is_deleted)
.map(d => d.id);
if (deletedIds.length > 0) {
log.info(`deleted ${serverIds} on ${this.baseChannelUrl}`); log.info(`deleted ${serverIds} on ${this.baseChannelUrl}`);
return true;
} }
// fire an alert
log.warn(`failed to delete ${serverIds} on ${this.baseChannelUrl}`); const failedIds = res.response.data
.filter(d => !d.is_deleted)
.map(d => d.id);
if (failedIds.length > 0) {
log.warn(`failed to delete ${failedIds} on ${this.baseChannelUrl}`);
}
// Note: if there is no entry for message, we assume it wasn't found
// on the server, so it is not treated as explicitly failed
const ignoredIds = _.difference(
serverIds,
_.union(failedIds, deletedIds)
);
if (ignoredIds.length > 0) {
log.warn(`No response for ${ignoredIds} on ${this.baseChannelUrl}`);
}
return { deletedIds, ignoredIds };
}
if (canThrow) { if (canThrow) {
throw new textsecure.PublicChatError( throw new textsecure.PublicChatError(
'Failed to delete public chat message' 'Failed to delete public chat message'
); );
} }
return false; return { deletedIds: [], ignoredIds: [] };
} }
// used for sending messages // used for sending messages

@ -1385,18 +1385,21 @@
})(); })();
const doDelete = async () => { const doDelete = async () => {
let toDeleteLocally;
if (this.model.isPublic()) { if (this.model.isPublic()) {
const success = await this.model.deletePublicMessages(messages); toDeleteLocally = await this.model.deletePublicMessages(messages);
if (!success) { if (toDeleteLocally.length === 0) {
// Message failed to delete from server, show error? // Message failed to delete from server, show error?
return; return;
} }
} else { } else {
messages.forEach(m => this.model.messageCollection.remove(m.id)); messages.forEach(m => this.model.messageCollection.remove(m.id));
toDeleteLocally = messages;
} }
await Promise.all( await Promise.all(
messages.map(async m => { toDeleteLocally.map(async m => {
await window.Signal.Data.removeMessage(m.id, { await window.Signal.Data.removeMessage(m.id, {
Message: Whisper.Message, Message: Whisper.Message,
}); });

@ -1076,9 +1076,7 @@ export class Message extends React.PureComponent<Props, State> {
)} )}
> >
{this.renderError(isIncoming)} {this.renderError(isIncoming)}
{isRss {isRss ? null : this.renderMenu(!isIncoming, triggerId)}
? null
: this.renderMenu(!isIncoming, triggerId)}
<div <div
className={classNames( className={classNames(
'module-message__container', 'module-message__container',
@ -1101,7 +1099,7 @@ export class Message extends React.PureComponent<Props, State> {
{this.renderSendMessageButton()} {this.renderSendMessageButton()}
</div> </div>
{this.renderError(!isIncoming)} {this.renderError(!isIncoming)}
{(isRss || multiSelectMode) {isRss || multiSelectMode
? null ? null
: this.renderMenu(isIncoming, triggerId)} : this.renderMenu(isIncoming, triggerId)}
{multiSelectMode ? null : this.renderContextMenu(triggerId)} {multiSelectMode ? null : this.renderContextMenu(triggerId)}

@ -145,7 +145,10 @@
"method": "render", "method": "render",
"comment": "Usage has been approved by Ryan Tharp on 2019-07-22" "comment": "Usage has been approved by Ryan Tharp on 2019-07-22"
} }
] ],
// Reasonable functions can exceed the default of 100 lines
// due to auto-formatting
"max-func-body-length": [true, 150]
}, },
"rulesDirectory": ["node_modules/tslint-microsoft-contrib"] "rulesDirectory": ["node_modules/tslint-microsoft-contrib"]
} }

Loading…
Cancel
Save