2022-11-23 00:44:57 +08:00
|
|
|
(rl => {
|
2022-11-29 18:49:26 +08:00
|
|
|
|
2022-11-23 00:44:57 +08:00
|
|
|
const
|
2022-11-23 17:22:49 +08:00
|
|
|
queue = [],
|
|
|
|
avatars = new Map,
|
2022-11-29 18:49:26 +08:00
|
|
|
ncAvatars = new Map,
|
2022-11-23 17:22:49 +08:00
|
|
|
templateId = 'MailMessageView',
|
|
|
|
getAvatarUid = msg => {
|
|
|
|
let from = msg.from[0],
|
|
|
|
bimi = 'pass' == from.dkimStatus ? 1 : 0;
|
2022-11-23 18:35:34 +08:00
|
|
|
return `${bimi}/${from.email.toLowerCase()}`;
|
2022-11-23 17:22:49 +08:00
|
|
|
},
|
2022-11-29 18:49:26 +08:00
|
|
|
getAvatar = msg => ncAvatars.get(msg.from[0].email.toLowerCase()) || avatars.get(getAvatarUid(msg)),
|
2022-12-01 00:03:07 +08:00
|
|
|
hash = async txt => {
|
|
|
|
if (/^[0-9a-f]{15,}$/i.test(txt)) {
|
|
|
|
return txt;
|
|
|
|
}
|
|
|
|
const hashArray = Array.from(new Uint8Array(
|
|
|
|
// await crypto.subtle.digest('SHA-256', (new TextEncoder()).encode(txt.toLowerCase()))
|
|
|
|
await crypto.subtle.digest('SHA-1', (new TextEncoder()).encode(txt.toLowerCase()))
|
|
|
|
));
|
|
|
|
return hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); // convert bytes to hex string
|
|
|
|
},
|
|
|
|
fromChars = from =>
|
2022-12-02 02:09:05 +08:00
|
|
|
// (from.name?.split(/[^\p{Lu}]+/gu) || []).reduce((a, s) => a + (s || '')), '')
|
|
|
|
(from.name?.split(/[^\p{L}]+/gu) || []).reduce((a, s) => a + (s[0] || ''), '')
|
2022-12-01 00:03:07 +08:00
|
|
|
.slice(0,2)
|
|
|
|
.toUpperCase(),
|
2022-12-02 02:09:05 +08:00
|
|
|
setIdenticon = (from, fn) => window.identiconSvg && hash(from.email).then(hash =>
|
|
|
|
fn('data:image/svg+xml;base64,' + btoa(window.identiconSvg(hash, fromChars(from))))
|
|
|
|
),
|
|
|
|
addQueue = (msg, fn) => {
|
|
|
|
setIdenticon(msg.from[0], fn);
|
|
|
|
queue.push([msg, fn]);
|
|
|
|
runQueue();
|
|
|
|
},
|
2022-11-23 17:22:49 +08:00
|
|
|
runQueue = (() => {
|
|
|
|
let item = queue.shift();
|
|
|
|
while (item) {
|
2022-12-02 05:47:57 +08:00
|
|
|
let url = getAvatar(item[0]),
|
|
|
|
uid = getAvatarUid(item[0]);
|
2022-11-23 17:22:49 +08:00
|
|
|
if (url) {
|
|
|
|
item[1](url);
|
|
|
|
item = queue.shift();
|
2022-12-02 05:47:57 +08:00
|
|
|
} else if (!avatars.has(uid)) {
|
2022-11-23 17:22:49 +08:00
|
|
|
let from = item[0].from[0];
|
|
|
|
rl.pluginRemoteRequest((iError, data) => {
|
|
|
|
if (!iError && data?.Result.type) {
|
|
|
|
url = `data:${data.Result.type};base64,${data.Result.data}`;
|
2022-12-02 05:47:57 +08:00
|
|
|
avatars.set(uid, url);
|
2022-11-23 17:22:49 +08:00
|
|
|
item[1](url);
|
2022-12-02 05:47:57 +08:00
|
|
|
} else {
|
|
|
|
avatars.set(uid, '');
|
2022-11-23 17:22:49 +08:00
|
|
|
}
|
|
|
|
runQueue();
|
|
|
|
}, 'Avatar', {
|
|
|
|
bimi: 'pass' == from.dkimStatus ? 1 : 0,
|
|
|
|
email: from.email
|
|
|
|
});
|
|
|
|
break;
|
2022-12-02 05:47:57 +08:00
|
|
|
} else {
|
|
|
|
runQueue();
|
|
|
|
break;
|
2022-11-23 17:22:49 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}).debounce(1000);
|
|
|
|
|
2022-11-29 18:49:26 +08:00
|
|
|
/**
|
|
|
|
* Loads images from Nextcloud contacts
|
|
|
|
*/
|
|
|
|
addEventListener('DOMContentLoaded', () => {
|
2022-11-29 22:56:21 +08:00
|
|
|
// rl.pluginSettingsGet('avatars', 'nextcloud');
|
2022-11-29 18:49:26 +08:00
|
|
|
if (parent.OC) {
|
|
|
|
const OC = () => parent.OC,
|
|
|
|
nsDAV = 'DAV:',
|
|
|
|
nsNC = 'http://nextcloud.com/ns',
|
|
|
|
nsCard = 'urn:ietf:params:xml:ns:carddav',
|
|
|
|
getElementsByTagName = (parent, namespace, localName) => parent.getElementsByTagNameNS(namespace, localName),
|
|
|
|
getElementValue = (parent, namespace, localName) =>
|
|
|
|
getElementsByTagName(parent, namespace, localName)?.item(0)?.textContent,
|
|
|
|
generateUrl = path => OC().webroot + '/remote.php' + path;
|
|
|
|
if (OC().requestToken) {
|
|
|
|
fetch(generateUrl(`/dav/addressbooks/users/${OC().currentUser}/contacts/`), {
|
|
|
|
mode: 'same-origin',
|
|
|
|
cache: 'no-cache',
|
|
|
|
redirect: 'error',
|
|
|
|
credentials: 'same-origin',
|
|
|
|
method: 'REPORT',
|
|
|
|
headers: {
|
|
|
|
requesttoken: OC().requestToken,
|
|
|
|
'Content-Type': 'application/xml; charset=utf-8',
|
|
|
|
Depth: 1
|
|
|
|
},
|
|
|
|
body: '<x4:addressbook-query xmlns:x4="urn:ietf:params:xml:ns:carddav"><x0:prop xmlns:x0="DAV:"><x4:address-data><x4:prop name="EMAIL"/></x4:address-data><x3:has-photo xmlns:x3="http://nextcloud.com/ns"/></x0:prop></x4:addressbook-query>'
|
|
|
|
})
|
|
|
|
.then(response => (response.status < 400) ? response.text() : Promise.reject(new Error({ response })))
|
|
|
|
.then(text => {
|
|
|
|
const
|
|
|
|
xmlParser = new DOMParser(),
|
|
|
|
responseList = getElementsByTagName(
|
|
|
|
xmlParser.parseFromString(text, 'application/xml').documentElement,
|
|
|
|
nsDAV,
|
|
|
|
'response');
|
|
|
|
for (let i = 0; i < responseList.length; ++i) {
|
|
|
|
const item = responseList.item(i);
|
|
|
|
if (1 == getElementValue(item, nsNC, 'has-photo')) {
|
2022-12-01 00:03:07 +08:00
|
|
|
[...getElementValue(item, nsCard, 'address-data').matchAll(/EMAIL.*?:([^@\r\n]+@[^@\r\n]+)/g)]
|
|
|
|
.forEach(match => {
|
2022-11-29 18:49:26 +08:00
|
|
|
ncAvatars.set(
|
|
|
|
match[1].toLowerCase(),
|
|
|
|
getElementValue(item, nsDAV, 'href') + '?photo'
|
|
|
|
);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2022-11-23 17:22:49 +08:00
|
|
|
ko.bindingHandlers.fromPic = {
|
|
|
|
init: (element, self, dummy, msg) => {
|
|
|
|
if (msg) {
|
|
|
|
let url = getAvatar(msg),
|
2022-12-02 02:09:05 +08:00
|
|
|
from = msg.from[0],
|
2022-11-23 17:22:49 +08:00
|
|
|
fn = url=>{element.src = url};
|
|
|
|
if (url) {
|
|
|
|
fn(url);
|
2022-11-27 23:05:44 +08:00
|
|
|
} else if (msg.avatar) {
|
2022-12-02 02:09:05 +08:00
|
|
|
element.onerror = () => setIdenticon(from, fn);
|
|
|
|
fn(`?Avatar/${'pass' == from.dkimStatus ? 1 : 0}/${msg.avatar}`);
|
2022-11-23 17:22:49 +08:00
|
|
|
} else {
|
2022-12-02 02:09:05 +08:00
|
|
|
addQueue(msg, fn);
|
2022-11-23 17:22:49 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2022-11-23 00:44:57 +08:00
|
|
|
|
|
|
|
addEventListener('rl-view-model.create', e => {
|
|
|
|
if (templateId === e.detail.viewModelTemplateID) {
|
|
|
|
|
|
|
|
const
|
|
|
|
template = document.getElementById(templateId),
|
|
|
|
messageItemHeader = template.content.querySelector('.messageItemHeader');
|
|
|
|
|
|
|
|
if (messageItemHeader) {
|
|
|
|
messageItemHeader.prepend(Element.fromHTML(
|
2022-11-28 17:09:24 +08:00
|
|
|
`<img class="fromPic" data-bind="visible: viewUserPicVisible, attr: {'src': viewUserPic() }" loading="lazy">`
|
2022-11-23 00:44:57 +08:00
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
let view = e.detail;
|
|
|
|
view.viewUserPic = ko.observable('');
|
|
|
|
view.viewUserPicVisible = ko.observable(false);
|
|
|
|
|
|
|
|
view.message.subscribe(msg => {
|
2022-11-23 06:54:32 +08:00
|
|
|
view.viewUserPicVisible(false);
|
2022-11-23 00:44:57 +08:00
|
|
|
if (msg) {
|
2022-11-23 17:22:49 +08:00
|
|
|
let url = getAvatar(msg),
|
|
|
|
fn = url => {
|
|
|
|
view.viewUserPic(url);
|
2022-11-23 00:44:57 +08:00
|
|
|
view.viewUserPicVisible(true);
|
2022-11-23 17:22:49 +08:00
|
|
|
};
|
|
|
|
if (url) {
|
|
|
|
fn(url);
|
2022-11-27 23:05:44 +08:00
|
|
|
} else if (msg.avatar) {
|
2022-12-02 02:09:05 +08:00
|
|
|
fn(`?Avatar/${'pass' == msg.from[0].dkimStatus ? 1 : 0}/${msg.avatar}`);
|
2022-11-23 17:22:49 +08:00
|
|
|
} else {
|
2022-12-02 02:09:05 +08:00
|
|
|
// let from = msg.from[0];
|
|
|
|
// view.viewUserPic(`?Avatar/${'pass' == from.dkimStatus ? 1 : 0}/${encodeURIComponent(from.email)}`);
|
2022-11-23 17:22:49 +08:00
|
|
|
// view.viewUserPicVisible(true);
|
2022-12-02 02:09:05 +08:00
|
|
|
addQueue(msg, fn);
|
2022-11-23 17:22:49 +08:00
|
|
|
}
|
2022-11-23 00:44:57 +08:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2022-11-23 17:22:49 +08:00
|
|
|
|
2022-11-23 06:54:32 +08:00
|
|
|
if ('MailMessageList' === e.detail.viewModelTemplateID) {
|
2022-11-23 17:22:49 +08:00
|
|
|
document.getElementById('MailMessageList').content.querySelector('.messageCheckbox')
|
2022-11-28 17:09:24 +08:00
|
|
|
.append(Element.fromHTML(`<img class="fromPic" data-bind="fromPic:$data" loading="lazy">`));
|
2022-11-23 06:54:32 +08:00
|
|
|
}
|
2022-11-23 00:44:57 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
})(window.rl);
|