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