Auto-generate profile images on conversations.

pull/68/head
Mikunj 7 years ago
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; return conversation;
}; };
conversation.initialPromise = create(); conversation.initialPromise = create().then(() => conversation.updateProfileAvatar());
return conversation; return conversation;
}, },
@ -251,12 +251,16 @@
conversations.add(collection.models); conversations.add(collection.models);
this._initialFetchComplete = true; this._initialFetchComplete = true;
await Promise.all( const promises = [];
conversations.map(conversation => conversation.updateLastMessage()) 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'); window.log.info('ConversationController: done with initial fetch');
} catch (error) { } catch (error) {
window.log.error( window.log.error(

@ -3,7 +3,7 @@
/* global BlockedNumberController: false */ /* global BlockedNumberController: false */
/* global ConversationController: false */ /* global ConversationController: false */
/* global i18n: false */ /* global i18n: false */
/* global libsignal: false */ /* global profileImages: false */
/* global storage: false */ /* global storage: false */
/* global textsecure: false */ /* global textsecure: false */
/* global Whisper: false */ /* global Whisper: false */
@ -174,6 +174,12 @@
deleteAttachmentData, deleteAttachmentData,
} }
); );
profileImages.removeImage(this.id);
},
async updateProfileAvatar() {
const path = profileImages.getOrCreateImagePath(this.id);
await this.setProfileAvatar(path);
}, },
async updateAndMerge(message) { async updateAndMerge(message) {
@ -1730,34 +1736,12 @@
} }
}, },
async setProfileAvatar(avatarPath) { async setProfileAvatar(avatarPath) {
if (!avatarPath) { const profileAvatar = this.get('profileAvatar');
return; if (profileAvatar !== avatarPath) {
} this.set({ profileAvatar: avatarPath });
await window.Signal.Data.updateConversation(this.id, this.attributes, {
const avatar = await textsecure.messaging.getAvatar(avatarPath); Conversation: Whisper.Conversation,
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);
} }
}, },
async setProfileKey(profileKey) { async setProfileKey(profileKey) {
@ -1959,8 +1943,9 @@
getAvatarPath() { getAvatarPath() {
const avatar = this.get('avatar') || this.get('profileAvatar'); const avatar = this.get('avatar') || this.get('profileAvatar');
if (avatar && avatar.path) { if (avatar) {
return getAbsoluteAttachmentPath(avatar.path); if (avatar.path) return getAbsoluteAttachmentPath(avatar.path);
return avatar;
} }
return null; return null;
@ -1970,8 +1955,10 @@
const color = this.getColor(); const color = this.getColor();
const avatar = this.get('avatar') || this.get('profileAvatar'); const avatar = this.get('avatar') || this.get('profileAvatar');
if (avatar && avatar.path) { const url = avatar && avatar.path ? getAbsoluteAttachmentPath(avatar.path) : avatar;
return { url: getAbsoluteAttachmentPath(avatar.path), color };
if (url) {
return { url, color };
} else if (this.isPrivate()) { } else if (this.isPrivate()) {
const symbol = this.isValid() ? '#' : '!'; const symbol = this.isValid() ? '#' : '!';
return { return {

@ -334,6 +334,7 @@
openConversation(conversation) { openConversation(conversation) {
this.searchView.hideHints(); this.searchView.hideHints();
if (conversation) { if (conversation) {
conversation.updateProfile();
ConversationController.markAsSelected(conversation); ConversationController.markAsSelected(conversation);
this.conversation_stack.open( this.conversation_stack.open(
ConversationController.get(conversation.id) ConversationController.get(conversation.id)

@ -271,6 +271,7 @@ window.libphonenumber = require('google-libphonenumber').PhoneNumberUtil.getInst
window.libphonenumber.PhoneNumberFormat = require('google-libphonenumber').PhoneNumberFormat; window.libphonenumber.PhoneNumberFormat = require('google-libphonenumber').PhoneNumberFormat;
window.loadImage = require('blueimp-load-image'); window.loadImage = require('blueimp-load-image');
window.getGuid = require('uuid/v4'); window.getGuid = require('uuid/v4');
window.profileImages = require('./app/profile_images');
window.React = require('react'); window.React = require('react');
window.ReactDOM = require('react-dom'); window.ReactDOM = require('react-dom');

@ -1835,6 +1835,10 @@
&:hover { &:hover {
background-color: $color-dark-70; background-color: $color-dark-70;
} }
.module-avatar {
background-color: $color-dark-85;
}
} }
.module-conversation-list-item--has-unread { .module-conversation-list-item--has-unread {

Loading…
Cancel
Save