Fixes for quotes/schema upgrade, optimize media gallery load

Also: Fix for contact detail page; didn't show back button
This commit is contained in:
Scott Nonnenberg 2018-07-18 09:38:42 -07:00
parent 95976b10e7
commit db91560990
6 changed files with 109 additions and 138 deletions

View file

@ -25,10 +25,13 @@
Contact, Contact,
Errors, Errors,
Message, Message,
VisualAttachment,
PhoneNumber, PhoneNumber,
} = window.Signal.Types; } = window.Signal.Types;
const { upgradeMessageSchema, loadAttachmentData } = window.Signal.Migrations; const {
upgradeMessageSchema,
loadAttachmentData,
getAbsoluteAttachmentPath,
} = window.Signal.Migrations;
// TODO: Factor out private and group subclasses of Conversation // TODO: Factor out private and group subclasses of Conversation
@ -771,47 +774,6 @@
return _.without(this.get('members'), me); return _.without(this.get('members'), me);
}, },
blobToArrayBuffer(blob) {
return new Promise((resolve, reject) => {
const fileReader = new FileReader();
fileReader.onload = e => resolve(e.target.result);
fileReader.onerror = reject;
fileReader.onabort = reject;
fileReader.readAsArrayBuffer(blob);
});
},
async makeThumbnailAttachment(attachment) {
const { arrayBufferToObjectURL } = Util;
const attachmentWithData = await loadAttachmentData(attachment);
const { data, contentType } = attachmentWithData;
const objectUrl = arrayBufferToObjectURL({
data,
type: contentType,
});
const thumbnail = GoogleChrome.isImageTypeSupported(contentType)
? await VisualAttachment.makeImageThumbnail(128, objectUrl)
: await VisualAttachment.makeVideoThumbnail(128, objectUrl);
URL.revokeObjectURL(objectUrl);
const arrayBuffer = await this.blobToArrayBuffer(thumbnail);
const finalContentType = 'image/png';
const finalObjectUrl = arrayBufferToObjectURL({
data: arrayBuffer,
type: finalContentType,
});
return {
data: arrayBuffer,
objectUrl: finalObjectUrl,
contentType: finalContentType,
};
},
async makeQuote(quotedMessage) { async makeQuote(quotedMessage) {
const { getName } = Contact; const { getName } = Contact;
const contact = quotedMessage.getContact(); const contact = quotedMessage.getContact();
@ -830,29 +792,17 @@
text: body || embeddedContactName, text: body || embeddedContactName,
attachments: await Promise.all( attachments: await Promise.all(
(attachments || []).map(async attachment => { (attachments || []).map(async attachment => {
const { contentType } = attachment; const { contentType, fileName, thumbnail } = attachment;
const willMakeThumbnail =
GoogleChrome.isImageTypeSupported(contentType) ||
GoogleChrome.isVideoTypeSupported(contentType);
const makeThumbnail = async () => {
try {
if (willMakeThumbnail) {
return await this.makeThumbnailAttachment(attachment);
}
} catch (error) {
console.log(
'Failed to create quote thumbnail',
error && error.stack ? error.stack : error
);
}
return null;
};
return { return {
contentType, contentType,
fileName: attachment.fileName, fileName,
thumbnail: await makeThumbnail(), thumbnail: thumbnail
? {
...(await loadAttachmentData(thumbnail)),
path: getAbsoluteAttachmentPath(thumbnail.path),
}
: null,
}; };
}) })
), ),
@ -1409,25 +1359,41 @@
return false; return false;
} }
try {
if (
queryMessage.get('schemaVersion') < Message.CURRENT_SCHEMA_VERSION
) {
const upgradedMessage = await upgradeMessageSchema(
queryMessage.attributes
);
queryMessage.set(upgradedMessage);
await wrapDeferred(message.save());
}
} catch (error) {
console.log(
'Problem upgrading message quoted message from database',
Errors.toLogFormat(error)
);
return false;
}
const queryAttachments = queryMessage.attachments || []; const queryAttachments = queryMessage.attachments || [];
if (queryAttachments.length === 0) { if (queryAttachments.length === 0) {
return false; return false;
} }
const queryFirst = queryAttachments[0]; const queryFirst = queryAttachments[0];
try { const { thumbnail } = queryFirst;
// eslint-disable-next-line no-param-reassign
message.quoteThumbnail = await this.makeThumbnailAttachment(queryFirst); // eslint-disable-next-line no-param-reassign
return true; message.quoteThumbnail = {
} catch (error) { ...thumbnail,
console.log( objectUrl: getAbsoluteAttachmentPath(thumbnail.path),
'Problem loading attachment data for quoted message from database', };
Errors.toLogFormat(error)
); return true;
return false;
}
}, },
async loadQuotedMessage(message, quotedMessage) { loadQuotedMessage(message, quotedMessage) {
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
message.quotedMessage = quotedMessage; message.quotedMessage = quotedMessage;
@ -1451,19 +1417,20 @@
return; return;
} }
try { const queryFirst = quotedAttachments[0];
const queryFirst = quotedAttachments[0]; const { thumbnail } = queryFirst;
// eslint-disable-next-line no-param-reassign if (!thumbnail) {
message.quoteThumbnail = await this.makeThumbnailAttachment(queryFirst); return;
} catch (error) {
console.log(
'Problem loading attachment data for quoted message',
error && error.stack ? error.stack : error
);
} }
// eslint-disable-next-line no-param-reassign
message.quoteThumbnail = {
...thumbnail,
objectUrl: getAbsoluteAttachmentPath(thumbnail.path),
};
}, },
async loadQuoteThumbnail(message) { loadQuoteThumbnail(message) {
const { quote } = message.attributes; const { quote } = message.attributes;
const { attachments } = quote; const { attachments } = quote;
const first = attachments[0]; const first = attachments[0];
@ -1477,27 +1444,15 @@
if (!thumbnail) { if (!thumbnail) {
return false; return false;
} }
try { // If we update this data in place, there's the risk that this data could be
const thumbnailWithData = await loadAttachmentData(thumbnail); // saved back to the database
const { data, contentType } = thumbnailWithData; // eslint-disable-next-line no-param-reassign
thumbnailWithData.objectUrl = Util.arrayBufferToObjectURL({ message.quoteThumbnail = {
data, ...thumbnail,
type: contentType, objectUrl: getAbsoluteAttachmentPath(thumbnail.path),
}); };
// If we update this data in place, there's the risk that this data could be return true;
// saved back to the database
// eslint-disable-next-line no-param-reassign
message.quoteThumbnail = thumbnailWithData;
return true;
} catch (error) {
console.log(
'loadQuoteThumbnail: had trouble loading thumbnail data from disk',
error && error.stack ? error.stack : error
);
return false;
}
}, },
async processQuotes(messages) { async processQuotes(messages) {
const lookup = this.makeMessagesLookup(messages); const lookup = this.makeMessagesLookup(messages);
@ -1516,7 +1471,10 @@
} }
// 1. Load provided thumbnail // 1. Load provided thumbnail
const gotThumbnail = await this.loadQuoteThumbnail(message, quote); if (this.loadQuoteThumbnail(message, quote)) {
this.forceRender(message);
return;
}
// 2. Check to see if we've already loaded the target message into memory // 2. Check to see if we've already loaded the target message into memory
const { author, id } = quote; const { author, id } = quote;
@ -1524,13 +1482,7 @@
const quotedMessage = lookup[key]; const quotedMessage = lookup[key];
if (quotedMessage) { if (quotedMessage) {
await this.loadQuotedMessage(message, quotedMessage); this.loadQuotedMessage(message, quotedMessage);
this.forceRender(message);
return;
}
// No need to go further if we already have a thumbnail
if (gotThumbnail) {
this.forceRender(message); this.forceRender(message);
return; return;
} }
@ -1566,10 +1518,11 @@
const { schemaVersion } = attributes; const { schemaVersion } = attributes;
if (schemaVersion < Message.CURRENT_SCHEMA_VERSION) { if (schemaVersion < Message.CURRENT_SCHEMA_VERSION) {
const upgradedMessage = upgradeMessageSchema(attributes);
message.set(upgradedMessage);
// Yep, we really do want to wait for each of these // Yep, we really do want to wait for each of these
// eslint-disable-next-line no-await-in-loop // eslint-disable-next-line no-await-in-loop
const upgradedMessage = await upgradeMessageSchema(attributes);
message.set(upgradedMessage);
// eslint-disable-next-line no-await-in-loop
await wrapDeferred(message.save()); await wrapDeferred(message.save());
} }
} }

View file

@ -283,12 +283,14 @@
processAttachment(attachment, externalObjectUrl) { processAttachment(attachment, externalObjectUrl) {
const { thumbnail } = attachment; const { thumbnail } = attachment;
const objectUrl = (thumbnail && thumbnail.objectUrl) || externalObjectUrl; const objectUrl = (thumbnail && thumbnail.objectUrl) || externalObjectUrl;
const path = thumbnail && thumbnail.path;
const thumbnailWithObjectUrl = !objectUrl const thumbnailWithObjectUrl =
? null !objectUrl && !path
: Object.assign({}, attachment.thumbnail || {}, { ? null
objectUrl, : Object.assign({}, attachment.thumbnail || {}, {
}); objectUrl: objectUrl || path,
});
return Object.assign({}, attachment, { return Object.assign({}, attachment, {
isVoiceMessage: Signal.Types.Attachment.isVoiceMessage(attachment), isVoiceMessage: Signal.Types.Attachment.isVoiceMessage(attachment),

View file

@ -167,7 +167,7 @@
openConversation(conversation) { openConversation(conversation) {
if (conversation) { if (conversation) {
this.openInbox().then(() => { this.openInbox().then(() => {
this.inboxView.openConversation(null, conversation); this.inboxView.openConversation(conversation);
}); });
} }
}, },

View file

@ -7,6 +7,7 @@
/* global Signal: false */ /* global Signal: false */
/* global storage: false */ /* global storage: false */
/* global Whisper: false */ /* global Whisper: false */
/* global wrapDeferred: false */
// eslint-disable-next-line func-names // eslint-disable-next-line func-names
(function() { (function() {
@ -14,6 +15,11 @@
window.Whisper = window.Whisper || {}; window.Whisper = window.Whisper || {};
const { Migrations } = Signal; const { Migrations } = Signal;
const { Message } = window.Signal.Types;
const {
upgradeMessageSchema,
getAbsoluteAttachmentPath,
} = window.Signal.Migrations;
Whisper.ExpiredToast = Whisper.ToastView.extend({ Whisper.ExpiredToast = Whisper.ToastView.extend({
render_attributes() { render_attributes() {
@ -603,13 +609,29 @@
} }
); );
// NOTE: Could we show grid previews from disk as well? // First we upgrade these messages to ensure that they have thumbnails
const loadMessages = Signal.Components.Types.Message.loadWithObjectURL( for (let max = rawMedia.length, i = 0; i < max; i += 1) {
Migrations.loadMessage const message = rawMedia[i];
); const { schemaVersion } = message;
const media = await loadMessages(rawMedia);
if (schemaVersion < Message.CURRENT_SCHEMA_VERSION) {
// Yep, we really do want to wait for each of these
// eslint-disable-next-line no-await-in-loop
rawMedia[i] = await upgradeMessageSchema(message);
const model = new Whisper.Message(rawMedia[i]);
// eslint-disable-next-line no-await-in-loop
await wrapDeferred(model.save());
}
}
const media = rawMedia.map(mediaMessage =>
Object.assign({}, mediaMessage, {
objectURL: getAbsoluteAttachmentPath(
mediaMessage.attachments[0].path
),
})
);
const { getAbsoluteAttachmentPath } = Signal.Migrations;
const saveAttachment = async ({ message } = {}) => { const saveAttachment = async ({ message } = {}) => {
const attachment = message.attachments[0]; const attachment = message.attachments[0];
const timestamp = message.received_at; const timestamp = message.received_at;
@ -629,13 +651,6 @@
} }
case 'media': { case 'media': {
const mediaWithObjectURL = media.map(mediaMessage =>
Object.assign({}, mediaMessage, {
objectURL: getAbsoluteAttachmentPath(
mediaMessage.attachments[0].path
),
})
);
const selectedIndex = media.findIndex( const selectedIndex = media.findIndex(
mediaMessage => mediaMessage.id === message.id mediaMessage => mediaMessage.id === message.id
); );
@ -643,7 +658,7 @@
className: 'lightbox-wrapper', className: 'lightbox-wrapper',
Component: Signal.Components.LightboxGallery, Component: Signal.Components.LightboxGallery,
props: { props: {
messages: mediaWithObjectURL, messages: media,
onSave: () => saveAttachment({ message }), onSave: () => saveAttachment({ message }),
selectedIndex, selectedIndex,
}, },
@ -1055,6 +1070,7 @@
}); });
this.listenBack(view); this.listenBack(view);
this.updateHeader();
}, },
async openConversation(number) { async openConversation(number) {

View file

@ -247,10 +247,9 @@
} }
}, },
openConversation(conversation) { openConversation(conversation) {
ConversationController.markAsSelected(conversation);
this.searchView.hideHints(); this.searchView.hideHints();
if (conversation) { if (conversation) {
ConversationController.markAsSelected(conversation);
this.conversation_stack.open( this.conversation_stack.open(
ConversationController.get(conversation.id) ConversationController.get(conversation.id)
); );

View file

@ -179,7 +179,8 @@
// height calculation. // height calculation.
.bottom-bar .quote-wrapper { .bottom-bar .quote-wrapper {
margin-right: 5px; margin-right: 5px;
margin-bottom: 5px; margin-bottom: 6px;
margin-top: 3px;
} }
.send .quote-wrapper { .send .quote-wrapper {