mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-09-08 17:07:03 +03:00
Speedup handling of identical avatars
This commit is contained in:
parent
f12de9a3fd
commit
490d808cdb
2 changed files with 62 additions and 58 deletions
|
|
@ -39,6 +39,8 @@
|
||||||
avatars = new Map,
|
avatars = new Map,
|
||||||
ncAvatars = new Map,
|
ncAvatars = new Map,
|
||||||
templateId = 'MailMessageView',
|
templateId = 'MailMessageView',
|
||||||
|
b64 = data => btoa(unescape(encodeURIComponent(data))),
|
||||||
|
b64url = data => b64(data).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''),
|
||||||
getBimiSelector = msg => {
|
getBimiSelector = msg => {
|
||||||
// Get 's' value out of 'v=BIMI1; s=foo;'
|
// Get 's' value out of 'v=BIMI1; s=foo;'
|
||||||
let bimiSelector = msg.headers().valueByName('BIMI-Selector');
|
let bimiSelector = msg.headers().valueByName('BIMI-Selector');
|
||||||
|
|
@ -46,9 +48,21 @@
|
||||||
return bimiSelector || '';
|
return bimiSelector || '';
|
||||||
},
|
},
|
||||||
getBimiId = msg => ('pass' == msg.from[0].dkimStatus ? 1 : 0) + '-' + getBimiSelector(msg),
|
getBimiId = msg => ('pass' == msg.from[0].dkimStatus ? 1 : 0) + '-' + getBimiSelector(msg),
|
||||||
getAvatarUrl = msg => `?Avatar/${getBimiId(msg)}/${msg.avatar}`,
|
email = msg => msg.from[0].email.toLowerCase(),
|
||||||
getAvatarUid = msg => `${getBimiId(msg)}/${msg.from[0].email.toLowerCase()}`,
|
getAvatarUid = msg => `${getBimiId(msg)}/${email(msg)}`,
|
||||||
getAvatar = msg => ncAvatars.get(msg.from[0].email.toLowerCase()) || avatars.get(getAvatarUid(msg)),
|
getAvatar = msg => ncAvatars.get(email(msg)) || avatars.get(getAvatarUid(msg)),
|
||||||
|
getAvatarUri = msg => {
|
||||||
|
if ('remote' === msg.avatar) {
|
||||||
|
msg.avatar = `?Avatar/${getBimiId(msg)}/${b64url(email(msg))}`;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
else if (!msg.avatar.startsWith('data:')) {
|
||||||
|
msg.avatar = null;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
return msg.avatar;
|
||||||
|
},
|
||||||
|
|
||||||
hash = async txt => {
|
hash = async txt => {
|
||||||
if (/^[0-9a-f]{15,}$/i.test(txt)) {
|
if (/^[0-9a-f]{15,}$/i.test(txt)) {
|
||||||
return txt;
|
return txt;
|
||||||
|
|
@ -64,58 +78,47 @@
|
||||||
(from.name?.split(/[^\p{L}]+/gu) || []).reduce((a, s) => a + (s[0] || ''), '')
|
(from.name?.split(/[^\p{L}]+/gu) || []).reduce((a, s) => a + (s[0] || ''), '')
|
||||||
.slice(0,2)
|
.slice(0,2)
|
||||||
.toUpperCase(),
|
.toUpperCase(),
|
||||||
setIdenticon = (from, fn) => hash(from.email).then(hash =>
|
setIdenticon = (msg, fn) => hash(msg.from[0].email).then(hash => {
|
||||||
fn('data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(window.identiconSvg(
|
const uri = 'data:image/svg+xml;base64,' + b64(window.identiconSvg(
|
||||||
hash,
|
hash,
|
||||||
fromChars(from),
|
fromChars(msg.from[0]),
|
||||||
window.getComputedStyle(getEl('rl-app'), null).getPropertyValue('font-family')
|
window.getComputedStyle(getEl('rl-app'), null).getPropertyValue('font-family')
|
||||||
)))))
|
));
|
||||||
),
|
// avatars.set(getAvatarUid(msg), uri);
|
||||||
|
fn(uri);
|
||||||
|
}),
|
||||||
|
|
||||||
addQueue = (msg, fn) => {
|
addQueue = (msg, fn) => {
|
||||||
msg.from?.[0] && setIdenticon(msg.from[0], fn);
|
if (msg.from?.[0]) {
|
||||||
if (rl.pluginSettingsGet('avatars', 'delay')) {
|
if (getAvatarUri(msg)) {
|
||||||
queue.push([msg, fn]);
|
if (rl.pluginSettingsGet('avatars', 'delay')) {
|
||||||
runQueue();
|
setIdenticon(msg, fn);
|
||||||
} else if (msg.avatar) {
|
queue.push([msg, fn]);
|
||||||
fn(getAvatarUrl(msg));
|
runQueue();
|
||||||
|
} else {
|
||||||
|
fn(msg.avatar);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setIdenticon(msg, fn);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
runQueue = (() => {
|
runQueue = (() => {
|
||||||
let item = queue.shift();
|
let item = queue.shift();
|
||||||
while (item) {
|
while (item) {
|
||||||
if (item[0].from) {
|
let url = getAvatar(item[0]);
|
||||||
let url = getAvatar(item[0]),
|
if (url) {
|
||||||
uid = getAvatarUid(item[0]);
|
item[1](url);
|
||||||
if (url) {
|
item = queue.shift();
|
||||||
item[1](url);
|
continue;
|
||||||
item = queue.shift();
|
} else if (item[0].avatar) {
|
||||||
continue;
|
item[1](item[0].avatar);
|
||||||
} else if (item[0].avatar) {
|
|
||||||
item[1](getAvatarUrl(item[0]));
|
|
||||||
} else if (!avatars.has(uid)) {
|
|
||||||
let from = item[0].from[0];
|
|
||||||
rl.pluginRemoteRequest((iError, data) => {
|
|
||||||
if (!iError && data?.Result.type) {
|
|
||||||
url = `data:${data.Result.type};base64,${data.Result.data}`;
|
|
||||||
avatars.set(uid, url);
|
|
||||||
item[1](url);
|
|
||||||
} else {
|
|
||||||
avatars.set(uid, '');
|
|
||||||
}
|
|
||||||
runQueue();
|
|
||||||
}, 'Avatar', {
|
|
||||||
bimi: 'pass' == from.dkimStatus ? 1 : 0,
|
|
||||||
bimiSelector: getBimiSelector(item[0]),
|
|
||||||
email: from.email
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
runQueue();
|
runQueue();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}).debounce(1000);
|
}).debounce(1000);
|
||||||
|
|
||||||
// addEventListener('DOMContentLoaded', () => {
|
|
||||||
/**
|
/**
|
||||||
* Modify templates
|
* Modify templates
|
||||||
*/
|
*/
|
||||||
|
|
@ -188,13 +191,19 @@
|
||||||
fn = url=>{element.src = url};
|
fn = url=>{element.src = url};
|
||||||
element.onerror = ()=>{
|
element.onerror = ()=>{
|
||||||
element.onerror = null;
|
element.onerror = null;
|
||||||
setIdenticon(msg.from[0], fn);
|
setIdenticon(msg, fn);
|
||||||
};
|
};
|
||||||
if (url) {
|
if (url) {
|
||||||
fn(url);
|
fn(url);
|
||||||
} else if (msg.avatar?.startsWith('data:')) {
|
} else if (msg.avatar?.startsWith('data:')) {
|
||||||
fn(msg.avatar);
|
fn(msg.avatar);
|
||||||
} else {
|
} else {
|
||||||
|
element.onload = ()=>{
|
||||||
|
if (!element.src.startsWith('data:')) {
|
||||||
|
element.onload = null;
|
||||||
|
avatars.set(getAvatarUid(msg), element.src);
|
||||||
|
}
|
||||||
|
};
|
||||||
addQueue(msg, fn);
|
addQueue(msg, fn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -221,11 +230,8 @@
|
||||||
if (url) {
|
if (url) {
|
||||||
fn(url);
|
fn(url);
|
||||||
} else if (msg.avatar) {
|
} else if (msg.avatar) {
|
||||||
fn(msg.avatar.startsWith('data:') ? msg.avatar : getAvatarUrl(msg));
|
fn(getAvatarUri(msg));
|
||||||
} else {
|
} else {
|
||||||
// let from = msg.from[0];
|
|
||||||
// view.viewUserPic(`?Avatar/${'pass' == from.dkimStatus ? 1 : 0}/${encodeURIComponent(from.email)}`);
|
|
||||||
// view.viewUserPicVisible(true);
|
|
||||||
addQueue(msg, fn);
|
addQueue(msg, fn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
|
||||||
NAME = 'Avatars',
|
NAME = 'Avatars',
|
||||||
AUTHOR = 'SnappyMail',
|
AUTHOR = 'SnappyMail',
|
||||||
URL = 'https://snappymail.eu/',
|
URL = 'https://snappymail.eu/',
|
||||||
VERSION = '1.20',
|
VERSION = '1.21',
|
||||||
RELEASE = '2024-08-26',
|
RELEASE = '2024-10-28',
|
||||||
REQUIRED = '2.33.0',
|
REQUIRED = '2.33.0',
|
||||||
CATEGORY = 'Contacts',
|
CATEGORY = 'Contacts',
|
||||||
LICENSE = 'MIT',
|
LICENSE = 'MIT',
|
||||||
|
|
@ -79,11 +79,9 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
|
||||||
|| ($this->Config()->Get('plugin', 'bimi', false) && 'pass' == $mFrom['dkimStatus'])
|
|| ($this->Config()->Get('plugin', 'bimi', false) && 'pass' == $mFrom['dkimStatus'])
|
||||||
|| ($this->Config()->Get('plugin', 'favicon', false) && 'pass' == $mFrom['dkimStatus'])
|
|| ($this->Config()->Get('plugin', 'favicon', false) && 'pass' == $mFrom['dkimStatus'])
|
||||||
)
|
)
|
||||||
) try {
|
) {
|
||||||
// Base64Url
|
// return \MailSo\Base\Utils::UrlSafeBase64Encode(\mb_strtolower($mFrom['email']));
|
||||||
return \SnappyMail\Crypt::EncryptUrlSafe($mFrom['email']);
|
return 'remote';
|
||||||
} catch (\Throwable $e) {
|
|
||||||
\SnappyMail\Log::error('Crypt', $e->getMessage());
|
|
||||||
}
|
}
|
||||||
if ('pass' == $mFrom['dkimStatus'] && $this->Config()->Get('plugin', 'service', true)) {
|
if ('pass' == $mFrom['dkimStatus'] && $this->Config()->Get('plugin', 'service', true)) {
|
||||||
// 'data:image/png;base64,[a-zA-Z0-9+/=]'
|
// 'data:image/png;base64,[a-zA-Z0-9+/=]'
|
||||||
|
|
@ -112,17 +110,17 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GET /?Avatar/${bimi}/Encrypted(${from.email})
|
* GET /?Avatar/${bimi}/Encoded(${from.email})
|
||||||
* Nextcloud Mail uses insecure unencrypted 'index.php/apps/mail/api/avatars/url/local%40example.com'
|
* Nextcloud Mail uses insecure unencoded 'index.php/apps/mail/api/avatars/url/local%40example.com'
|
||||||
*/
|
*/
|
||||||
// public function ServiceAvatar(...$aParts)
|
// public function ServiceAvatar(...$aParts)
|
||||||
public function ServiceAvatar(string $sServiceName, string $sBimi, string $sEncryptedEmail)
|
public function ServiceAvatar(string $sServiceName, string $sBimi, string $sEncodedEmail)
|
||||||
{
|
{
|
||||||
$maxAge = 86400;
|
$maxAge = 86400;
|
||||||
$sEmail = \SnappyMail\Crypt::DecryptUrlSafe($sEncryptedEmail);
|
$sEmail = \MailSo\Base\Utils::UrlSafeBase64Decode($sEncodedEmail);
|
||||||
$aBimi = \explode('-', $sBimi, 2);
|
$aBimi = \explode('-', $sBimi, 2);
|
||||||
$sBimiSelector = isset($aBimi[1]) ? $aBimi[1] : 'default';
|
$sBimiSelector = isset($aBimi[1]) ? $aBimi[1] : 'default';
|
||||||
// $sEmail && \MailSo\Base\Http::setETag("{$sBimiSelector}-{$sEncryptedEmail}");
|
// $sEmail && \MailSo\Base\Http::setETag("{$sBimiSelector}-{$sEncodedEmail}");
|
||||||
if ($sEmail && ($aResult = $this->getAvatar($sEmail, !empty($aBimi[0]), $sBimiSelector))) {
|
if ($sEmail && ($aResult = $this->getAvatar($sEmail, !empty($aBimi[0]), $sBimiSelector))) {
|
||||||
\header("Cache-Control: max-age={$maxAge}, private");
|
\header("Cache-Control: max-age={$maxAge}, private");
|
||||||
\header('Expires: '.\gmdate('D, j M Y H:i:s', $maxAge + \time()).' UTC');
|
\header('Expires: '.\gmdate('D, j M Y H:i:s', $maxAge + \time()).' UTC');
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue