Auto-generate profile images on conversations.
This commit is contained in:
parent
85fe666edb
commit
961eb53915
|
@ -0,0 +1,67 @@
|
|||
const fs = require('fs');
|
||||
const mkdirp = require('mkdirp');
|
||||
const path = require('path');
|
||||
const jdenticon = require('jdenticon');
|
||||
|
||||
// Icon config
|
||||
jdenticon.config = {
|
||||
lightness: {
|
||||
color: [0.40, 0.80],
|
||||
grayscale: [0.30, 0.90],
|
||||
},
|
||||
saturation: {
|
||||
color: 0.50,
|
||||
grayscale: 0.00,
|
||||
},
|
||||
backColor: '#86444400',
|
||||
};
|
||||
|
||||
const { app } = require('electron').remote;
|
||||
|
||||
const userDataPath = app.getPath('userData');
|
||||
const PATH = path.join(userDataPath, 'profileImages');
|
||||
mkdirp.sync(PATH);
|
||||
|
||||
function hashCode(s) {
|
||||
let h = 0;
|
||||
for(let i = 0; i < s.length; i += 1)
|
||||
h = Math.imul(31, h) + s.charCodeAt(i) | 0;
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
const hasImage = pubKey => fs.existsSync(getImagePath(pubKey));
|
||||
|
||||
const getImagePath = pubKey => `${PATH}/${pubKey}.png`;
|
||||
const getOrCreateImagePath = pubKey => {
|
||||
const imagePath = getImagePath(pubKey);
|
||||
|
||||
// If the image doesn't exist then create it
|
||||
if (!hasImage(pubKey)) {
|
||||
/*
|
||||
We hash the pubKey and then pass that into jdenticon
|
||||
This is because if we pass pubKey directly,
|
||||
jdenticon trims the string and then generates a hash
|
||||
meaning public keys with the same characters at the beginning
|
||||
will get the same images
|
||||
*/
|
||||
const png = jdenticon.toPng(hashCode(pubKey), 50, 0.12);
|
||||
fs.writeFileSync(imagePath, png);
|
||||
}
|
||||
|
||||
return imagePath;
|
||||
};
|
||||
|
||||
const removeImage = pubKey => {
|
||||
if (hasImage(pubKey)) {
|
||||
fs.unlinkSync(getImagePath(pubKey));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getOrCreateImagePath,
|
||||
getImagePath,
|
||||
hasImage,
|
||||
removeImage,
|
||||
hashCode,
|
||||
};
|
|
@ -192,7 +192,7 @@
|
|||
return conversation;
|
||||
};
|
||||
|
||||
conversation.initialPromise = create();
|
||||
conversation.initialPromise = create().then(() => conversation.updateProfileAvatar());
|
||||
|
||||
return conversation;
|
||||
},
|
||||
|
@ -251,12 +251,16 @@
|
|||
conversations.add(collection.models);
|
||||
|
||||
this._initialFetchComplete = true;
|
||||
await Promise.all(
|
||||
conversations.map(conversation => conversation.updateLastMessage())
|
||||
);
|
||||
const promises = [];
|
||||
conversations.forEach(conversation => {
|
||||
promises.concat([
|
||||
conversation.updateLastMessage(),
|
||||
conversation.updateProfile(),
|
||||
conversation.updateProfileAvatar(),
|
||||
]);
|
||||
});
|
||||
await Promise.all(promises);
|
||||
|
||||
// Update profiles
|
||||
conversations.map(conversation => conversation.updateProfile());
|
||||
window.log.info('ConversationController: done with initial fetch');
|
||||
} catch (error) {
|
||||
window.log.error(
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
/* global BlockedNumberController: false */
|
||||
/* global ConversationController: false */
|
||||
/* global i18n: false */
|
||||
/* global libsignal: false */
|
||||
/* global profileImages: false */
|
||||
/* global storage: false */
|
||||
/* global textsecure: false */
|
||||
/* global Whisper: false */
|
||||
|
@ -174,6 +174,12 @@
|
|||
deleteAttachmentData,
|
||||
}
|
||||
);
|
||||
profileImages.removeImage(this.id);
|
||||
},
|
||||
|
||||
async updateProfileAvatar() {
|
||||
const path = profileImages.getOrCreateImagePath(this.id);
|
||||
await this.setProfileAvatar(path);
|
||||
},
|
||||
|
||||
async updateAndMerge(message) {
|
||||
|
@ -1730,34 +1736,12 @@
|
|||
}
|
||||
},
|
||||
async setProfileAvatar(avatarPath) {
|
||||
if (!avatarPath) {
|
||||
return;
|
||||
}
|
||||
|
||||
const avatar = await textsecure.messaging.getAvatar(avatarPath);
|
||||
const key = this.get('profileKey');
|
||||
if (!key) {
|
||||
return;
|
||||
}
|
||||
const keyBuffer = window.Signal.Crypto.base64ToArrayBuffer(key);
|
||||
|
||||
// decrypt
|
||||
const decrypted = await textsecure.crypto.decryptProfile(
|
||||
avatar,
|
||||
keyBuffer
|
||||
);
|
||||
|
||||
// update the conversation avatar only if hash differs
|
||||
if (decrypted) {
|
||||
const newAttributes = await window.Signal.Types.Conversation.maybeUpdateProfileAvatar(
|
||||
this.attributes,
|
||||
decrypted,
|
||||
{
|
||||
writeNewAttachmentData,
|
||||
deleteAttachmentData,
|
||||
}
|
||||
);
|
||||
this.set(newAttributes);
|
||||
const profileAvatar = this.get('profileAvatar');
|
||||
if (profileAvatar !== avatarPath) {
|
||||
this.set({ profileAvatar: avatarPath });
|
||||
await window.Signal.Data.updateConversation(this.id, this.attributes, {
|
||||
Conversation: Whisper.Conversation,
|
||||
});
|
||||
}
|
||||
},
|
||||
async setProfileKey(profileKey) {
|
||||
|
@ -1959,8 +1943,9 @@
|
|||
getAvatarPath() {
|
||||
const avatar = this.get('avatar') || this.get('profileAvatar');
|
||||
|
||||
if (avatar && avatar.path) {
|
||||
return getAbsoluteAttachmentPath(avatar.path);
|
||||
if (avatar) {
|
||||
if (avatar.path) return getAbsoluteAttachmentPath(avatar.path);
|
||||
return avatar;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -1970,8 +1955,10 @@
|
|||
const color = this.getColor();
|
||||
const avatar = this.get('avatar') || this.get('profileAvatar');
|
||||
|
||||
if (avatar && avatar.path) {
|
||||
return { url: getAbsoluteAttachmentPath(avatar.path), color };
|
||||
const url = avatar && avatar.path ? getAbsoluteAttachmentPath(avatar.path) : avatar;
|
||||
|
||||
if (url) {
|
||||
return { url, color };
|
||||
} else if (this.isPrivate()) {
|
||||
const symbol = this.isValid() ? '#' : '!';
|
||||
return {
|
||||
|
|
|
@ -334,6 +334,7 @@
|
|||
openConversation(conversation) {
|
||||
this.searchView.hideHints();
|
||||
if (conversation) {
|
||||
conversation.updateProfile();
|
||||
ConversationController.markAsSelected(conversation);
|
||||
this.conversation_stack.open(
|
||||
ConversationController.get(conversation.id)
|
||||
|
|
|
@ -271,6 +271,7 @@ window.libphonenumber = require('google-libphonenumber').PhoneNumberUtil.getInst
|
|||
window.libphonenumber.PhoneNumberFormat = require('google-libphonenumber').PhoneNumberFormat;
|
||||
window.loadImage = require('blueimp-load-image');
|
||||
window.getGuid = require('uuid/v4');
|
||||
window.profileImages = require('./app/profile_images');
|
||||
|
||||
window.React = require('react');
|
||||
window.ReactDOM = require('react-dom');
|
||||
|
|
|
@ -1835,6 +1835,10 @@
|
|||
&:hover {
|
||||
background-color: $color-dark-70;
|
||||
}
|
||||
|
||||
.module-avatar {
|
||||
background-color: $color-dark-85;
|
||||
}
|
||||
}
|
||||
|
||||
.module-conversation-list-item--has-unread {
|
||||
|
|
Loading…
Reference in New Issue