On message delete, ensure that all external files are deleted
This commit is contained in:
parent
e80857562a
commit
34231168a7
7 changed files with 73 additions and 79 deletions
|
@ -880,7 +880,7 @@
|
||||||
messageDescriptor.id,
|
messageDescriptor.id,
|
||||||
messageDescriptor.type
|
messageDescriptor.type
|
||||||
);
|
);
|
||||||
await conversation.save({ profileSharing: true });
|
await wrapDeferred(conversation.save({ profileSharing: true }));
|
||||||
return confirm();
|
return confirm();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1769,7 +1769,6 @@
|
||||||
Whisper.Notifications.add({
|
Whisper.Notifications.add({
|
||||||
conversationId,
|
conversationId,
|
||||||
iconUrl,
|
iconUrl,
|
||||||
imageUrl: message.getImageUrl(),
|
|
||||||
isExpiringMessage,
|
isExpiringMessage,
|
||||||
message: message.getNotificationText(),
|
message: message.getNotificationText(),
|
||||||
messageId,
|
messageId,
|
||||||
|
|
|
@ -20,8 +20,7 @@
|
||||||
|
|
||||||
const { Message: TypedMessage, Contact, PhoneNumber } = Signal.Types;
|
const { Message: TypedMessage, Contact, PhoneNumber } = Signal.Types;
|
||||||
const {
|
const {
|
||||||
// loadAttachmentData,
|
deleteExternalMessageFiles,
|
||||||
deleteAttachmentData,
|
|
||||||
getAbsoluteAttachmentPath,
|
getAbsoluteAttachmentPath,
|
||||||
} = Signal.Migrations;
|
} = Signal.Migrations;
|
||||||
|
|
||||||
|
@ -69,7 +68,6 @@
|
||||||
|
|
||||||
this.OUR_NUMBER = textsecure.storage.user.getNumber();
|
this.OUR_NUMBER = textsecure.storage.user.getNumber();
|
||||||
|
|
||||||
this.on('change:attachments', this.updateImageUrl);
|
|
||||||
this.on('destroy', this.onDestroy);
|
this.on('destroy', this.onDestroy);
|
||||||
this.on('change:expirationStartTimestamp', this.setToExpire);
|
this.on('change:expirationStartTimestamp', this.setToExpire);
|
||||||
this.on('change:expireTimer', this.setToExpire);
|
this.on('change:expireTimer', this.setToExpire);
|
||||||
|
@ -223,54 +221,15 @@
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
},
|
},
|
||||||
async onDestroy() {
|
onDestroy() {
|
||||||
this.revokeImageUrl();
|
this.unload();
|
||||||
const attachments = this.get('attachments');
|
|
||||||
await Promise.all(attachments.map(deleteAttachmentData));
|
return deleteExternalMessageFiles(this.attributes);
|
||||||
},
|
|
||||||
updateImageUrl() {
|
|
||||||
this.revokeImageUrl();
|
|
||||||
const attachment = this.get('attachments')[0];
|
|
||||||
if (attachment) {
|
|
||||||
const blob = new Blob([attachment.data], {
|
|
||||||
type: attachment.contentType,
|
|
||||||
});
|
|
||||||
this.imageUrl = URL.createObjectURL(blob);
|
|
||||||
} else {
|
|
||||||
this.imageUrl = null;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
unload() {
|
unload() {
|
||||||
if (this.quoteThumbnail) {
|
|
||||||
URL.revokeObjectURL(this.quoteThumbnail.objectUrl);
|
|
||||||
this.quoteThumbnail = null;
|
|
||||||
}
|
|
||||||
if (this.quotedMessage) {
|
if (this.quotedMessage) {
|
||||||
this.quotedMessage = null;
|
this.quotedMessage = null;
|
||||||
}
|
}
|
||||||
const quote = this.get('quote');
|
|
||||||
const attachments = (quote && quote.attachments) || [];
|
|
||||||
attachments.forEach(attachment => {
|
|
||||||
if (attachment.thumbnail && attachment.thumbnail.objectUrl) {
|
|
||||||
URL.revokeObjectURL(attachment.thumbnail.objectUrl);
|
|
||||||
// eslint-disable-next-line no-param-reassign
|
|
||||||
attachment.thumbnail.objectUrl = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.revokeImageUrl();
|
|
||||||
},
|
|
||||||
revokeImageUrl() {
|
|
||||||
if (this.imageUrl) {
|
|
||||||
URL.revokeObjectURL(this.imageUrl);
|
|
||||||
this.imageUrl = null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getImageUrl() {
|
|
||||||
if (this.imageUrl === undefined) {
|
|
||||||
this.updateImageUrl();
|
|
||||||
}
|
|
||||||
return this.imageUrl;
|
|
||||||
},
|
},
|
||||||
getQuoteObjectUrl() {
|
getQuoteObjectUrl() {
|
||||||
const thumbnail = this.quoteThumbnail;
|
const thumbnail = this.quoteThumbnail;
|
||||||
|
|
|
@ -110,12 +110,14 @@ function initializeMigrations({
|
||||||
const readAttachmentData = createReader(attachmentsPath);
|
const readAttachmentData = createReader(attachmentsPath);
|
||||||
const loadAttachmentData = Type.loadData(readAttachmentData);
|
const loadAttachmentData = Type.loadData(readAttachmentData);
|
||||||
const getAbsoluteAttachmentPath = createAbsolutePathGetter(attachmentsPath);
|
const getAbsoluteAttachmentPath = createAbsolutePathGetter(attachmentsPath);
|
||||||
|
const deleteOnDisk = Attachments.createDeleter(attachmentsPath);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
attachmentsPath,
|
attachmentsPath,
|
||||||
deleteAttachmentData: Type.deleteData(
|
deleteExternalMessageFiles: MessageType.deleteAllExternalFiles({
|
||||||
Attachments.createDeleter(attachmentsPath)
|
deleteAttachmentData: Type.deleteData(deleteOnDisk),
|
||||||
),
|
deleteOnDisk,
|
||||||
|
}),
|
||||||
getAbsoluteAttachmentPath,
|
getAbsoluteAttachmentPath,
|
||||||
getPlaceholderMigrations,
|
getPlaceholderMigrations,
|
||||||
loadAttachmentData,
|
loadAttachmentData,
|
||||||
|
|
|
@ -158,26 +158,28 @@ exports.loadData = readAttachmentData => {
|
||||||
// deleteData :: (RelativePath -> IO Unit)
|
// deleteData :: (RelativePath -> IO Unit)
|
||||||
// Attachment ->
|
// Attachment ->
|
||||||
// IO Unit
|
// IO Unit
|
||||||
exports.deleteData = deleteAttachmentData => {
|
exports.deleteData = deleteOnDisk => {
|
||||||
if (!is.function(deleteAttachmentData)) {
|
if (!is.function(deleteOnDisk)) {
|
||||||
throw new TypeError("'deleteAttachmentData' must be a function");
|
throw new TypeError('deleteData: deleteOnDisk must be a function');
|
||||||
}
|
}
|
||||||
|
|
||||||
return async attachment => {
|
return async attachment => {
|
||||||
if (!exports.isValid(attachment)) {
|
if (!exports.isValid(attachment)) {
|
||||||
throw new TypeError("'attachment' is not valid");
|
throw new TypeError('deleteData: attachment is not valid');
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasDataInMemory = exports.hasData(attachment);
|
const { path, thumbnail, screenshot } = attachment;
|
||||||
if (hasDataInMemory) {
|
if (is.string(path)) {
|
||||||
return;
|
await deleteOnDisk(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is.string(attachment.path)) {
|
if (thumbnail && is.string(thumbnail.path)) {
|
||||||
throw new TypeError("'attachment.path' is required");
|
await deleteOnDisk(thumbnail.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
await deleteAttachmentData(attachment.path);
|
if (screenshot && is.string(screenshot.path)) {
|
||||||
|
await deleteOnDisk(screenshot.path);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -361,6 +361,52 @@ exports.createAttachmentLoader = loadAttachmentData => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.deleteAllExternalFiles = ({ deleteAttachmentData, deleteOnDisk }) => {
|
||||||
|
if (!isFunction(deleteAttachmentData)) {
|
||||||
|
throw new TypeError(
|
||||||
|
'deleteAllExternalFiles: deleteAttachmentData must be a function'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isFunction(deleteOnDisk)) {
|
||||||
|
throw new TypeError(
|
||||||
|
'deleteAllExternalFiles: deleteOnDisk must be a function'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return async message => {
|
||||||
|
const { attachments, quote, contact } = message;
|
||||||
|
|
||||||
|
if (attachments && attachments.length) {
|
||||||
|
await Promise.all(attachments.map(deleteAttachmentData));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (quote && quote.attachments && quote.attachments.length) {
|
||||||
|
await Promise.all(
|
||||||
|
quote.attachments.map(async attachment => {
|
||||||
|
const { thumbnail } = attachment;
|
||||||
|
|
||||||
|
if (thumbnail.path) {
|
||||||
|
await deleteOnDisk(thumbnail.path);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contact && contact.length) {
|
||||||
|
await Promise.all(
|
||||||
|
contact.map(async item => {
|
||||||
|
const { avatar } = item;
|
||||||
|
|
||||||
|
if (avatar && avatar.avatar && avatar.avatar.path) {
|
||||||
|
await deleteOnDisk(avatar.avatar.path);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
// createAttachmentDataWriter :: (RelativePath -> IO Unit)
|
// createAttachmentDataWriter :: (RelativePath -> IO Unit)
|
||||||
// Message ->
|
// Message ->
|
||||||
// IO (Promise Message)
|
// IO (Promise Message)
|
||||||
|
|
|
@ -27,29 +27,15 @@
|
||||||
var source = '+14155555555';
|
var source = '+14155555555';
|
||||||
|
|
||||||
describe('MessageCollection', function() {
|
describe('MessageCollection', function() {
|
||||||
before(function() {
|
before(async function() {
|
||||||
return Promise.all([deleteAllMessages(), ConversationController.load()]);
|
await deleteAllMessages();
|
||||||
|
ConversationController.reset();
|
||||||
|
await ConversationController.load();
|
||||||
});
|
});
|
||||||
after(function() {
|
after(function() {
|
||||||
return deleteAllMessages();
|
return deleteAllMessages();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has no image url', function() {
|
|
||||||
var messages = new Whisper.MessageCollection();
|
|
||||||
var message = messages.add(attributes);
|
|
||||||
assert.isNull(message.getImageUrl());
|
|
||||||
});
|
|
||||||
|
|
||||||
it('updates image url', function() {
|
|
||||||
var messages = new Whisper.MessageCollection();
|
|
||||||
var message = messages.add({ attachments: [attachment] });
|
|
||||||
|
|
||||||
var firstUrl = message.getImageUrl();
|
|
||||||
message.updateImageUrl();
|
|
||||||
var secondUrl = message.getImageUrl();
|
|
||||||
assert.notEqual(secondUrl, firstUrl);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('gets outgoing contact', function() {
|
it('gets outgoing contact', function() {
|
||||||
var messages = new Whisper.MessageCollection();
|
var messages = new Whisper.MessageCollection();
|
||||||
var message = messages.add(attributes);
|
var message = messages.add(attributes);
|
||||||
|
|
Loading…
Reference in a new issue