mirror of
https://github.com/RetroShare/RSNewWebUI.git
synced 2026-09-14 11:05:47 +05:00
fix: remove iframe and use a div with contenteditable attribute instead, this commit contains a whole lot of changes
This commit is contained in:
parent
94ed218c61
commit
eef7a4c515
@ -86,7 +86,7 @@ const Layout = () => {
|
||||
: futil.proxyObj[currentItem.slice(1)].map((item) =>
|
||||
m('tr', [
|
||||
m('td.results__name', [m('i.fas.fa-file'), m('span', item.fName)]),
|
||||
m('td.results__size', futil.makeFriendlyUnit(item.fSize.xstr64)),
|
||||
m('td.results__size', futil.makeFriendlyUnit(item.fSize.xint64)),
|
||||
m('td.results__hash', item.fHash),
|
||||
m(
|
||||
'td.results__download',
|
||||
|
||||
@ -3,9 +3,8 @@ const rs = require('rswebui');
|
||||
const widget = require('widgets');
|
||||
|
||||
const Layout = () => {
|
||||
const data = {
|
||||
const Data = {
|
||||
subject: '',
|
||||
mailBody: '',
|
||||
identity: null,
|
||||
recipients: {
|
||||
to: {
|
||||
@ -25,34 +24,82 @@ const Layout = () => {
|
||||
},
|
||||
},
|
||||
};
|
||||
// async function loadDetails(attrs) {}
|
||||
return {
|
||||
oninit: ({ attrs: { ownId, allUsers } }) => {
|
||||
data.identity = ownId[0];
|
||||
Object.keys(data.recipients).forEach((item) => {
|
||||
data.recipients[item].inputList = allUsers;
|
||||
oninit: async (v) => {
|
||||
console.log('function loadDetails');
|
||||
const { ownId, allUsers, msgType } = await v.attrs;
|
||||
console.log(allUsers, ownId);
|
||||
console.log('attrs: ', await v.attrs);
|
||||
if (msgType === 'compose') {
|
||||
Data.identity = ownId[0];
|
||||
console.log(ownId);
|
||||
console.log('id: ', Data.identity);
|
||||
}
|
||||
Object.keys(Data.recipients).forEach((item) => {
|
||||
Data.recipients[item].inputList = allUsers;
|
||||
});
|
||||
console.log('Data before reply init: ', Data);
|
||||
if (msgType === 'reply') {
|
||||
const { sender, recipients, subject, replyMessage } = await v.attrs;
|
||||
console.log('id: ', sender);
|
||||
Data.identity = sender[0];
|
||||
// console.log('id: ', Data.identity);
|
||||
Data.recipients.to.sendList = recipients;
|
||||
const tmb = document.querySelector('#composerMailBody');
|
||||
tmb.innerHTML = `<br><br><p>-----Original Message-----</p>${replyMessage}`;
|
||||
Data.subject = subject.substring(0, 4) === 'Re: ' ? subject : `Re: ${subject}`;
|
||||
if (sender.length > 0) Data.identity = ownId.filter((id) => id === sender[0].mGroupId);
|
||||
console.log('Data after reply init: ', Data);
|
||||
}
|
||||
},
|
||||
view: ({ attrs: { ownId, allUsers } }) => {
|
||||
// get recipientType from the function call to handle events for all recipient types
|
||||
function handleInput(e, recipientType) {
|
||||
data.recipients[recipientType].inputVal = e.target.value;
|
||||
data.recipients[recipientType].inputList = allUsers.filter((item) =>
|
||||
Data.recipients[recipientType].inputVal = e.target.value;
|
||||
Data.recipients[recipientType].inputList = allUsers.filter((item) =>
|
||||
item.mGroupName.toLowerCase().includes(e.target.value.toLowerCase())
|
||||
);
|
||||
m.redraw();
|
||||
}
|
||||
function handleClick(item, recipientType) {
|
||||
data.recipients[recipientType].sendList.push(item);
|
||||
// reset values
|
||||
data.recipients[recipientType].inputVal = '';
|
||||
data.recipients[recipientType].inputList = allUsers;
|
||||
m.redraw();
|
||||
Data.recipients[recipientType].sendList.push(item);
|
||||
// reset current input values after a sender is selected
|
||||
Data.recipients[recipientType].inputVal = '';
|
||||
Data.recipients[recipientType].inputList = allUsers;
|
||||
}
|
||||
function removeSelectedItem(recipient, recipientType) {
|
||||
data.recipients[recipientType].sendList = data.recipients[recipientType].sendList.filter(
|
||||
Data.recipients[recipientType].sendList = Data.recipients[recipientType].sendList.filter(
|
||||
(item) => item.mGroupId !== recipient.mGroupId
|
||||
);
|
||||
m.redraw();
|
||||
}
|
||||
function sendMail() {
|
||||
const to = Data.recipients.to.sendList.map((toItem) => toItem.mGroupId);
|
||||
const cc = Data.recipients.cc.sendList.map((ccItem) => ccItem.mGroupId);
|
||||
const bcc = Data.recipients.bcc.sendList.map((bccItem) => bccItem.mGroupId);
|
||||
console.log(to, cc, bcc);
|
||||
const { identity: from, subject } = Data;
|
||||
console.log(from, subject);
|
||||
const tmb = document.querySelector('#composerMailBody');
|
||||
console.log(tmb.innerHTML);
|
||||
// rs.rsJsonApiRequest('/rsMsgs/sendMail', { from, subject, mailBody, to, cc, bcc }).then(
|
||||
// (res) => {
|
||||
// if (res.body.retval) {
|
||||
// Object.keys(Data.recipients).forEach((recipientType) => {
|
||||
// Data.recipients[recipientType].sendList = [];
|
||||
// });
|
||||
// Data.subject = '';
|
||||
// Data.mailBody = '';
|
||||
// m.redraw();
|
||||
// }
|
||||
// const success = res.body.retval < 1;
|
||||
// widget.popupMessage(
|
||||
// m('.widget', [
|
||||
// m('.widget__heading', m('h3', success ? 'Success' : 'Error')),
|
||||
// m('.widget__body', m('p', success ? 'Mail sent successfully' : res.body.errorMsg)),
|
||||
// ])
|
||||
// );
|
||||
// }
|
||||
// );
|
||||
}
|
||||
return m('.widget', [
|
||||
m('.widget__heading', m('h3', 'Compose a mail')),
|
||||
@ -62,8 +109,10 @@ const Layout = () => {
|
||||
m(
|
||||
'select[id=idtags]',
|
||||
{
|
||||
value: data.identity,
|
||||
onchange: (e) => (data.identity = ownId[e.target.selectedIndex]),
|
||||
value: Data.identity,
|
||||
onchange: (e) => {
|
||||
Data.identity = ownId[e.target.selectedIndex];
|
||||
},
|
||||
},
|
||||
ownId &&
|
||||
ownId.map((id) =>
|
||||
@ -78,12 +127,39 @@ const Layout = () => {
|
||||
),
|
||||
]),
|
||||
m('.compose-mail__recipients', [
|
||||
Object.keys(data.recipients).map((recipientType) =>
|
||||
m('.compose-mail__recipients__container', [
|
||||
m('label.bold', 'To: '),
|
||||
m('.recipients', [
|
||||
Data.recipients.to.sendList.length > 0 &&
|
||||
Data.recipients.to.sendList.map((recipient) =>
|
||||
m('.recipients__selected', [
|
||||
m('span', recipient.mGroupName),
|
||||
m('i.fas.fa-times', {
|
||||
onclick: () => removeSelectedItem(recipient, 'to'),
|
||||
}),
|
||||
])
|
||||
),
|
||||
m('.recipients__input', [
|
||||
m('input[type=text].recipients__input-field', {
|
||||
value: Data.recipients.to.inputVal,
|
||||
oninput: (e) => handleInput(e, 'to'),
|
||||
}),
|
||||
m('ul.recipients__input-list[autocomplete=off]', [
|
||||
Data.recipients.to.inputList.length > 0
|
||||
? Data.recipients.to.inputList.map((item) =>
|
||||
m('li', { onclick: () => handleClick(item, 'to') }, item.mGroupName)
|
||||
)
|
||||
: m('li', 'No Item'),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
['cc', 'bcc'].map((recipientType) =>
|
||||
m('.compose-mail__recipients__container', [
|
||||
m('label.bold', `${recipientType}: `),
|
||||
m('.recipients', [
|
||||
data.recipients[recipientType].sendList.length > 0 &&
|
||||
data.recipients[recipientType].sendList.map((recipient) =>
|
||||
Data.recipients[recipientType].sendList.length > 0 &&
|
||||
Data.recipients[recipientType].sendList.map((recipient) =>
|
||||
m('.recipients__selected', [
|
||||
m('span', recipient.mGroupName),
|
||||
m('i.fas.fa-times', {
|
||||
@ -93,12 +169,12 @@ const Layout = () => {
|
||||
),
|
||||
m('.recipients__input', [
|
||||
m('input[type=text].recipients__input-field', {
|
||||
value: data.recipients[recipientType].inputVal,
|
||||
value: Data.recipients[recipientType].inputVal,
|
||||
oninput: (e) => handleInput(e, recipientType),
|
||||
}),
|
||||
m('ul.recipients__input-list[autocomplete=off]', [
|
||||
data.recipients[recipientType].inputList.length > 0
|
||||
? data.recipients[recipientType].inputList.map((item) =>
|
||||
Data.recipients[recipientType].inputList.length > 0
|
||||
? Data.recipients[recipientType].inputList.map((item) =>
|
||||
m(
|
||||
'li',
|
||||
{ onclick: () => handleClick(item, recipientType) },
|
||||
@ -113,54 +189,21 @@ const Layout = () => {
|
||||
),
|
||||
]),
|
||||
m('input.compose-mail__subject[type=text][placeholder=Subject]', {
|
||||
value: data.subject,
|
||||
oninput: (e) => (data.subject = e.target.value),
|
||||
}),
|
||||
m('textarea.compose-mail__message[placeholder=Message]', {
|
||||
value: data.mailBody,
|
||||
oninput: (e) => (data.mailBody = e.target.value),
|
||||
value: Data.subject,
|
||||
oninput: (e) => (Data.subject = e.target.value),
|
||||
}),
|
||||
m('.compose-mail__message', [
|
||||
m('.compose-mail__message-body[placeholder=Message][contenteditable]#composerMailBody'),
|
||||
]),
|
||||
allUsers &&
|
||||
m(
|
||||
'button.compose-mail__send-btn',
|
||||
{
|
||||
onclick: () => {
|
||||
const to = data.recipients.to.sendList.map((toItem) => toItem.mGroupId);
|
||||
const cc = data.recipients.cc.sendList.map((ccItem) => ccItem.mGroupId);
|
||||
const bcc = data.recipients.bcc.sendList.map((bccItem) => bccItem.mGroupId);
|
||||
const { identity, subject, mailBody } = data;
|
||||
rs.rsJsonApiRequest('/rsMsgs/sendMail', {
|
||||
from: identity,
|
||||
subject,
|
||||
mailBody,
|
||||
to,
|
||||
cc,
|
||||
bcc,
|
||||
}).then((res) => {
|
||||
if (res.body.retval) {
|
||||
Object.keys(data.recipients).forEach((item) => {
|
||||
data.recipients[item].sendList = [];
|
||||
});
|
||||
data.subject = '';
|
||||
data.mailBody = '';
|
||||
m.redraw();
|
||||
}
|
||||
res.body.retval < 1
|
||||
? widget.popupMessage([m('h3', 'Error'), m('hr'), m('p', res.body.errorMsg)])
|
||||
: widget.popupMessage([
|
||||
m('h3', 'Success'),
|
||||
m('hr'),
|
||||
m('p', 'Mail Sent successfully'),
|
||||
]);
|
||||
});
|
||||
},
|
||||
},
|
||||
[m('span', 'Send Mail'), m('i.fas.fa-paper-plane')]
|
||||
),
|
||||
m('button.compose-mail__send-btn', { onclick: sendMail }, [
|
||||
m('span', 'Send Mail'),
|
||||
m('i.fas.fa-paper-plane'),
|
||||
]),
|
||||
]),
|
||||
]);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = Layout();
|
||||
module.exports = Layout;
|
||||
|
||||
@ -5,8 +5,8 @@ const peopleUtil = require('people/people_util');
|
||||
const compose = require('mail/mail_compose');
|
||||
|
||||
const composeData = {
|
||||
allUsers: undefined,
|
||||
ownId: undefined,
|
||||
allUsers: [],
|
||||
ownId: [],
|
||||
};
|
||||
|
||||
const Messages = {
|
||||
@ -92,7 +92,6 @@ const Layout = () => {
|
||||
composeData.ownId.splice(i, 1); // workaround for id '0'
|
||||
}
|
||||
}
|
||||
// identity = ownId[0];
|
||||
});
|
||||
composeData.allUsers = await peopleUtil.sortUsers(rs.userList.users);
|
||||
},
|
||||
@ -104,7 +103,7 @@ const Layout = () => {
|
||||
sent: Messages.sent.length,
|
||||
trash: Messages.trash.length,
|
||||
};
|
||||
const sectionsquickviewSize = {
|
||||
const sectionsQuickviewSize = {
|
||||
starred: Messages.starred.length,
|
||||
system: Messages.system.length,
|
||||
spam: Messages.spam.length,
|
||||
@ -118,13 +117,7 @@ const Layout = () => {
|
||||
|
||||
return [
|
||||
m('.side-bar', [
|
||||
m(
|
||||
'button.mail-compose-btn',
|
||||
{
|
||||
onclick: () => (showCompose = true),
|
||||
},
|
||||
'Compose'
|
||||
),
|
||||
m('button.mail-compose-btn', { onclick: () => (showCompose = true) }, 'Compose'),
|
||||
m(util.Sidebar, {
|
||||
tabs: Object.keys(sections),
|
||||
size: sectionsSize,
|
||||
@ -132,36 +125,31 @@ const Layout = () => {
|
||||
}),
|
||||
m(util.SidebarQuickView, {
|
||||
tabs: Object.keys(sectionsquickview),
|
||||
size: sectionsquickviewSize,
|
||||
size: sectionsQuickviewSize,
|
||||
baseRoute: '/mail/',
|
||||
}),
|
||||
]),
|
||||
m(
|
||||
'.node-panel',
|
||||
m('.widget', [
|
||||
m('.top-heading', [
|
||||
m(
|
||||
'select.mail-tag',
|
||||
{
|
||||
value: tagselect.showval,
|
||||
onchange: (e) => {
|
||||
tagselect.showval = tagselect.opts[e.target.selectedIndex];
|
||||
m.route.get().split('/').length < 4 &&
|
||||
m('.top-heading', [
|
||||
m(
|
||||
'select.mail-tag',
|
||||
{
|
||||
value: tagselect.showval,
|
||||
onchange: (e) => (tagselect.showval = tagselect.opts[e.target.selectedIndex]),
|
||||
},
|
||||
},
|
||||
[tagselect.opts.map((o) => m('option', { value: o }, o.toLocaleString()))]
|
||||
),
|
||||
m(util.SearchBar, {
|
||||
list: {},
|
||||
}),
|
||||
]),
|
||||
[tagselect.opts.map((opt) => m('option', { value: opt }, opt.toLocaleString()))]
|
||||
),
|
||||
m(util.SearchBar, { list: {} }),
|
||||
]),
|
||||
vnode.children,
|
||||
])
|
||||
),
|
||||
m(
|
||||
'.composePopupOverlay',
|
||||
{
|
||||
style: { display: showCompose ? 'block' : 'none' },
|
||||
},
|
||||
{ style: { display: showCompose ? 'block' : 'none' } },
|
||||
m(
|
||||
'.composePopup',
|
||||
composeData.allUsers &&
|
||||
@ -169,14 +157,9 @@ const Layout = () => {
|
||||
m(compose, {
|
||||
allUsers: composeData.allUsers,
|
||||
ownId: composeData.ownId,
|
||||
msgType: 'compose',
|
||||
}),
|
||||
m(
|
||||
'button.red.close-btn',
|
||||
{
|
||||
onclick: () => (showCompose = false),
|
||||
},
|
||||
m('i.fas.fa-times')
|
||||
)
|
||||
m('button.red.close-btn', { onclick: () => (showCompose = false) }, m('i.fas.fa-times'))
|
||||
)
|
||||
),
|
||||
];
|
||||
@ -186,23 +169,11 @@ const Layout = () => {
|
||||
|
||||
module.exports = {
|
||||
composeData,
|
||||
view: (v) => {
|
||||
const tab = v.attrs.tab;
|
||||
view: ({ attrs, attrs: { tab, msgId } }) => {
|
||||
// TODO: utilize multiple routing params
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(v.attrs, 'msgId')) {
|
||||
return m(
|
||||
Layout,
|
||||
m(util.MessageView, {
|
||||
id: v.attrs.msgId,
|
||||
})
|
||||
);
|
||||
if (Object.prototype.hasOwnProperty.call(attrs, 'msgId')) {
|
||||
return m(Layout, m(util.MessageView, { msgId }));
|
||||
}
|
||||
return m(
|
||||
Layout,
|
||||
m(sections[tab] || sectionsquickview[tab], {
|
||||
list: Messages[tab].reverse(),
|
||||
})
|
||||
);
|
||||
return m(Layout, m(sections[tab] || sectionsquickview[tab], { list: Messages[tab].reverse() }));
|
||||
},
|
||||
};
|
||||
|
||||
@ -3,6 +3,8 @@ const rs = require('rswebui');
|
||||
const util = require('files/files_util');
|
||||
const widget = require('widgets');
|
||||
const peopleUtil = require('people/people_util');
|
||||
const compose = require('mail/mail_compose');
|
||||
const composeData = require('mail/mail_resolver');
|
||||
|
||||
// rsmsgs.h
|
||||
const RS_MSG_BOXMASK = 0x000f;
|
||||
@ -49,35 +51,36 @@ const MessageSummary = () => {
|
||||
let isStarred = false;
|
||||
let msgStatus = '';
|
||||
let fromUserInfo;
|
||||
function starMessage(e) {
|
||||
isStarred = !isStarred;
|
||||
rs.rsJsonApiRequest('/rsMsgs/MessageStar', { msgId: details.msgId, mark: isStarred });
|
||||
// Stop event bubbling, both functions for supporting IE & FF
|
||||
e.stopImmediatePropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
return {
|
||||
oninit: async (v) => {
|
||||
const res = await rs.rsJsonApiRequest('/rsMsgs/getMessage', {
|
||||
oninit: (v) => {
|
||||
rs.rsJsonApiRequest('/rsMsgs/getMessage', {
|
||||
msgId: v.attrs.details.msgId,
|
||||
});
|
||||
if (res.body.retval) {
|
||||
details = res.body.msg;
|
||||
files = details.files;
|
||||
|
||||
isStarred = (details.msgflags & 0xf00) === RS_MSG_STAR;
|
||||
|
||||
const flag = details.msgflags & 0xf0;
|
||||
if (flag === RS_MSG_NEW || flag === RS_MSG_UNREAD_BY_USER) {
|
||||
msgStatus = 'unread';
|
||||
} else {
|
||||
msgStatus = 'read';
|
||||
}
|
||||
}
|
||||
if (details && details.from && details.from._addr_string) {
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsIdentity/getIdDetails',
|
||||
{
|
||||
id: details.from._addr_string,
|
||||
},
|
||||
(data) => {
|
||||
fromUserInfo = data.details;
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.body.retval) {
|
||||
details = res.body.msg;
|
||||
files = details.files;
|
||||
isStarred = (details.msgflags & 0xf00) === RS_MSG_STAR;
|
||||
const flag = details.msgflags & 0xf0;
|
||||
msgStatus = flag === RS_MSG_NEW || flag === RS_MSG_UNREAD_BY_USER ? 'unread' : 'read';
|
||||
}
|
||||
);
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
if (details?.from?._addr_string) {
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsIdentity/getIdDetails',
|
||||
{ id: details.from._addr_string },
|
||||
(data) => (fromUserInfo = data.details)
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
view: (v) =>
|
||||
m(
|
||||
@ -86,31 +89,17 @@ const MessageSummary = () => {
|
||||
key: details.msgId,
|
||||
class: msgStatus,
|
||||
onclick: () =>
|
||||
m.route.set('/mail/:tab/:msgId', {
|
||||
tab: v.attrs.category,
|
||||
msgId: details.msgId,
|
||||
}),
|
||||
m.route.set('/mail/:tab/:msgId', { tab: v.attrs.category, msgId: details.msgId }),
|
||||
},
|
||||
[
|
||||
m(
|
||||
'td',
|
||||
m('input.star-check[type=checkbox][id=msg-' + details.msgId + ']', {
|
||||
checked: isStarred,
|
||||
}),
|
||||
m(`input.star-check[type=checkbox][id=msg-${details.msgId}]`, { checked: isStarred }),
|
||||
// Use label with [for] to manipulate hidden checkbox
|
||||
m(
|
||||
'label.star-check[for=msg-' + details.msgId + ']',
|
||||
`label.star-check[for=msg-${details.msgId}]`,
|
||||
{
|
||||
onclick: (e) => {
|
||||
isStarred = !isStarred;
|
||||
rs.rsJsonApiRequest('/rsMsgs/MessageStar', {
|
||||
msgId: details.msgId,
|
||||
mark: isStarred,
|
||||
});
|
||||
// Stop event bubbling, both functions for supporting IE & FF
|
||||
e.stopImmediatePropagation();
|
||||
e.preventDefault();
|
||||
},
|
||||
onclick: starMessage,
|
||||
class: (details.msgflags & 0xf00) === RS_MSG_STAR ? 'starred' : 'unstarred',
|
||||
},
|
||||
m('i.fas.fa-star')
|
||||
@ -129,6 +118,19 @@ const MessageSummary = () => {
|
||||
};
|
||||
|
||||
const AttachmentSection = () => {
|
||||
function handleAttachmentDownload(item) {
|
||||
const { fname: fileName, hash, size: xstr64 } = item;
|
||||
const flags = util.RS_FILE_REQ_ANONYMOUS_ROUTING;
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsFiles/FileRequest',
|
||||
{ fileName, hash, flags, size: { xstr64 } },
|
||||
(status) =>
|
||||
widget.popupMessage([
|
||||
m('i.fas.fa-file-medical'),
|
||||
m('h3', `File is ${status.retval ? 'being' : 'already'} downloaded!`),
|
||||
])
|
||||
).catch((error) => console.log('error: ', error));
|
||||
}
|
||||
return {
|
||||
view: (v) =>
|
||||
m('table.attachment-container', [
|
||||
@ -141,54 +143,13 @@ const AttachmentSection = () => {
|
||||
]),
|
||||
m(
|
||||
'tbody',
|
||||
v.attrs.files.map((item) =>
|
||||
v.attrs.files.map((file) =>
|
||||
m('tr.attachment', [
|
||||
m('td.attachment__name', [m('i.fas.fa-file'), m('span', item.fname)]),
|
||||
m(
|
||||
'td.attachment__from',
|
||||
rs.userList.userMap[item.from._addr_string]
|
||||
? rs.userList.userMap[item.from._addr_string]
|
||||
: '[Unknown]'
|
||||
),
|
||||
m('td.attachment__size', humanReadableSize(item.size.xint64)),
|
||||
m('td.attachment__date', new Date(item.ts * 1000).toLocaleString()),
|
||||
m(
|
||||
'td',
|
||||
m(
|
||||
'button',
|
||||
{
|
||||
onclick: () => {
|
||||
try {
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsFiles/FileRequest',
|
||||
{
|
||||
fileName: item.fname,
|
||||
hash: item.hash,
|
||||
flags: util.RS_FILE_REQ_ANONYMOUS_ROUTING,
|
||||
size: {
|
||||
xstr64: item.size.xstr64,
|
||||
},
|
||||
},
|
||||
(status) => {
|
||||
status.retval
|
||||
? widget.popupMessage([
|
||||
m('i.fas.fa-file-medical'),
|
||||
m('h3', 'File is being downloaded!'),
|
||||
])
|
||||
: widget.popupMessage([
|
||||
m('i.fas.fa-file-medical'),
|
||||
m('h3', 'File is already downloaded!'),
|
||||
]);
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
console.log('error: ', error);
|
||||
}
|
||||
},
|
||||
},
|
||||
'Download'
|
||||
)
|
||||
),
|
||||
m('td.attachment__name', [m('i.fas.fa-file'), m('span', file.fname)]),
|
||||
m('td.attachment__from', rs.userList.userMap[file.from._addr_string] || '[Unknown]'),
|
||||
m('td.attachment__size', humanReadableSize(file.size.xint64)),
|
||||
m('td.attachment__date', new Date(file.ts * 1000).toLocaleString()),
|
||||
m('td', m('button', { onclick: () => handleAttachmentDownload(file) }, 'Download')),
|
||||
])
|
||||
)
|
||||
),
|
||||
@ -197,134 +158,144 @@ const AttachmentSection = () => {
|
||||
};
|
||||
|
||||
const MessageView = () => {
|
||||
let details = {};
|
||||
let message = '';
|
||||
const files = [];
|
||||
const toList = {};
|
||||
const ccList = {};
|
||||
const bccList = {};
|
||||
let showReplyCompose = false;
|
||||
const replyData = {
|
||||
sender: [],
|
||||
recipients: [],
|
||||
};
|
||||
const MailData = {
|
||||
msgId: '',
|
||||
sender: {},
|
||||
message: '',
|
||||
subject: '',
|
||||
destinations: [],
|
||||
toList: {},
|
||||
ccList: {},
|
||||
bccList: {},
|
||||
files: [],
|
||||
};
|
||||
function handleMailDelete() {
|
||||
widget.popupMessage([
|
||||
m('p', 'Are you sure you want to delete this mail?'),
|
||||
m(
|
||||
'button',
|
||||
{
|
||||
onclick: () => {
|
||||
rs.rsJsonApiRequest('/rsMsgs/MessageToTrash', { msgId: MailData.msgId, bTrash: true });
|
||||
rs.rsJsonApiRequest('/rsMsgs/MessageDelete', { msgId: MailData.msgId }).then((res) => {
|
||||
widget.popupMessage(
|
||||
m('.widget', [
|
||||
m('.widget__heading', m('h3', res.body.retval ? 'Success' : 'Error')),
|
||||
m(
|
||||
'.widget__body',
|
||||
m('p', res.body.retval ? 'Mail Deleted.' : 'Error in Deleting.')
|
||||
),
|
||||
])
|
||||
);
|
||||
m.route.set('/mail/:tab', { tab: m.route.param().tab });
|
||||
});
|
||||
},
|
||||
},
|
||||
'Delete'
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
return {
|
||||
oninit: async (v) => {
|
||||
const res = await rs.rsJsonApiRequest('/rsMsgs/getMessage', {
|
||||
msgId: v.attrs.id,
|
||||
});
|
||||
if (res.body.retval) {
|
||||
details = res.body.msg;
|
||||
res.body.msg.files.forEach((element) => {
|
||||
files.push({ ...element, from: res.body.msg.from, ts: res.body.msg.ts });
|
||||
});
|
||||
// regex to detect html tags
|
||||
// better regex? /<[a-z][\s\S]*>/gi
|
||||
message = /<\/*[a-z][^>]+?>/gi.test(details.msg)
|
||||
? details.msg
|
||||
: `<p style="white-space: pre">${details.msg}</p>`;
|
||||
}
|
||||
details?.destinations?.map((destDetail) => {
|
||||
if (destDetail._mode === MSG_ADDRESS_MODE_TO && !toList[destDetail._addr_string]) {
|
||||
toList[destDetail._addr_string] = destDetail;
|
||||
} else if (destDetail._mode === MSG_ADDRESS_MODE_CC && !ccList[destDetail._addr_string]) {
|
||||
ccList[destDetail._addr_string] = destDetail;
|
||||
} else if (destDetail._mode === MSG_ADDRESS_MODE_BCC && !bccList[destDetail._addr_string]) {
|
||||
bccList[destDetail._addr_string] = destDetail;
|
||||
oninit: (v) => {
|
||||
rs.rsJsonApiRequest('/rsMsgs/getMessage', {
|
||||
msgId: v.attrs.msgId,
|
||||
}).then(async (res) => {
|
||||
if (res.body.retval) {
|
||||
const msgDetails = res.body.msg;
|
||||
msgDetails.files.forEach((element) =>
|
||||
MailData.files.push({ ...element, from: msgDetails.from, ts: msgDetails.ts })
|
||||
);
|
||||
// regex to detect html tags, better regex? /<[a-z][\s\S]*>/gi
|
||||
MailData.message = /<\/*[a-z][^>]+?>/gi.test(msgDetails.msg)
|
||||
? msgDetails.msg
|
||||
: `<p style="white-space: pre">${msgDetails.msg}</p>`;
|
||||
document.querySelector('#msgView').innerHTML = MailData.message;
|
||||
MailData.sender = msgDetails.from;
|
||||
console.log('mail sender id: ', MailData.sender._addr_string);
|
||||
MailData.subject = msgDetails.title;
|
||||
MailData.destinations = msgDetails.destinations;
|
||||
}
|
||||
MailData?.destinations?.map((destDetail) => {
|
||||
const { _addr_string: addrString, _mode: mode } = destDetail; // destructuring + renaming
|
||||
if (mode === MSG_ADDRESS_MODE_TO && !MailData.toList[addrString]) {
|
||||
MailData.toList[addrString] = destDetail;
|
||||
} else if (mode === MSG_ADDRESS_MODE_CC && !MailData.ccList[addrString]) {
|
||||
MailData.ccList[addrString] = destDetail;
|
||||
} else if (mode === MSG_ADDRESS_MODE_BCC && !MailData.bccList[addrString]) {
|
||||
MailData.bccList[addrString] = destDetail;
|
||||
}
|
||||
});
|
||||
console.log('tolist: ', MailData.toList);
|
||||
peopleUtil.ownIds(async (data) => {
|
||||
composeData.ownId = await data;
|
||||
replyData.recipients = composeData.ownId.filter((id) =>
|
||||
Object.prototype.hasOwnProperty.call(MailData.toList, id)
|
||||
);
|
||||
console.log('recipient: ', replyData.recipients, 'ownIds: ', composeData.ownId);
|
||||
for (let i = 0; i < composeData.ownId.length; i++) {
|
||||
if (Number(composeData.ownId[i]) === 0) {
|
||||
composeData.ownId.splice(i, 1); // workaround for id '0'
|
||||
}
|
||||
}
|
||||
});
|
||||
composeData.allUsers = peopleUtil.sortUsers(rs.userList.users);
|
||||
replyData.sender = composeData.allUsers.filter(
|
||||
(user) => user.mGroupId === MailData.sender._addr_string
|
||||
);
|
||||
await rs.rsJsonApiRequest(
|
||||
'/rsIdentity/getIdDetails',
|
||||
{ id: MailData?.sender?._addr_string },
|
||||
(data) => (MailData.avatar = data?.details?.mAvatar)
|
||||
);
|
||||
});
|
||||
await rs.rsJsonApiRequest(
|
||||
'/rsIdentity/getIdDetails',
|
||||
{
|
||||
id: details.from._addr_string,
|
||||
},
|
||||
(data) => (details = { ...details, avatar: data.details.mAvatar })
|
||||
);
|
||||
},
|
||||
view: () =>
|
||||
m(
|
||||
'.msg-view',
|
||||
{
|
||||
key: details.msgId,
|
||||
},
|
||||
[
|
||||
m('.msg-view-nav', [
|
||||
m(
|
||||
'a[title=Back]',
|
||||
{
|
||||
onclick: () =>
|
||||
m.route.set('/mail/:tab', {
|
||||
tab: m.route.param().tab,
|
||||
}),
|
||||
},
|
||||
{ onclick: () => m.route.set('/mail/:tab', { tab: m.route.param().tab }) },
|
||||
m('i.fas.fa-arrow-left')
|
||||
),
|
||||
m('.msg-view-nav__action', [
|
||||
m('button', 'Reply'),
|
||||
m('button', { onclick: () => (showReplyCompose = true) }, 'Reply'),
|
||||
m('button', 'Reply All'),
|
||||
m('button', 'Forward'),
|
||||
m(
|
||||
'button',
|
||||
{
|
||||
onclick: () =>
|
||||
widget.popupMessage([
|
||||
m('p', 'Are you sure you want to delete this mail?'),
|
||||
m(
|
||||
'button',
|
||||
{
|
||||
onclick: async () => {
|
||||
rs.rsJsonApiRequest('/rsMsgs/MessageToTrash', {
|
||||
msgId: details.msgId,
|
||||
bTrash: true,
|
||||
});
|
||||
const res = await rs.rsJsonApiRequest('/rsMsgs/MessageDelete', {
|
||||
msgId: details.msgId,
|
||||
});
|
||||
res.body.retval
|
||||
? widget.popupMessage([
|
||||
m('h3', 'Success'),
|
||||
m('hr'),
|
||||
m('p', 'Mail Deleted.'),
|
||||
])
|
||||
: widget.popupMessage([
|
||||
m('h3', 'Error'),
|
||||
m('hr'),
|
||||
m('p', res.body.errorMessage),
|
||||
]);
|
||||
m.redraw();
|
||||
m.route.set('/mail/:tab', {
|
||||
tab: m.route.param().tab,
|
||||
});
|
||||
},
|
||||
},
|
||||
'Delete'
|
||||
),
|
||||
]),
|
||||
},
|
||||
'Delete'
|
||||
),
|
||||
m('button', { onclick: handleMailDelete }, 'Delete'),
|
||||
]),
|
||||
]),
|
||||
m('.msg-view__header', [
|
||||
m('h3', details.title),
|
||||
m('h3', MailData.subject),
|
||||
m('.msg-details', [
|
||||
details.from &&
|
||||
MailData.sender &&
|
||||
m(peopleUtil.UserAvatar, {
|
||||
avatar: details.avatar,
|
||||
firstLetter: rs.userList.userMap[details.from._addr_string]
|
||||
? rs.userList.userMap[details.from._addr_string].slice(0, 1).toUpperCase()
|
||||
avatar: MailData.avatar,
|
||||
firstLetter: rs.userList.userMap[MailData.sender._addr_string]
|
||||
? rs.userList.userMap[MailData.sender._addr_string].slice(0, 1).toUpperCase()
|
||||
: '',
|
||||
}),
|
||||
m('.msg-details__info', [
|
||||
details.from &&
|
||||
MailData.sender &&
|
||||
m('.msg-details__info-item', [
|
||||
m('b', 'From: '),
|
||||
rs.userList.userMap[details.from._addr_string] || 'Unknown',
|
||||
rs.userList.userMap[MailData.sender._addr_string] || 'Unknown',
|
||||
]),
|
||||
toList &&
|
||||
Object.keys(toList).length > 0 &&
|
||||
MailData.toList &&
|
||||
Object.keys(MailData.toList).length > 0 &&
|
||||
// TODO: optimize javascript for show-more and show-less working
|
||||
m('.msg-details__info-item', [
|
||||
m('b', 'To: '),
|
||||
m(
|
||||
'#truncate',
|
||||
Object.keys(toList).map((key, index) =>
|
||||
Object.keys(MailData.toList).map((key, index) =>
|
||||
m('span', `${rs.userList.userMap[key]}, `)
|
||||
)
|
||||
),
|
||||
@ -332,7 +303,7 @@ const MessageView = () => {
|
||||
'button[id=show-more]',
|
||||
{
|
||||
style: {
|
||||
display: Object.keys(toList).length > 10 ? 'block' : 'none',
|
||||
display: Object.keys(MailData.toList).length > 10 ? 'block' : 'none',
|
||||
},
|
||||
onclick: () => {
|
||||
document.querySelector('#show-more').style.display = 'none';
|
||||
@ -356,45 +327,55 @@ const MessageView = () => {
|
||||
'less'
|
||||
),
|
||||
]),
|
||||
ccList &&
|
||||
Object.keys(ccList).length > 0 &&
|
||||
MailData.ccList &&
|
||||
Object.keys(MailData.ccList).length > 0 &&
|
||||
m('.msg-details__info-item', [
|
||||
m('b', 'CC: '),
|
||||
Object.keys(ccList).map((key, index) =>
|
||||
m('b', 'Cc: '),
|
||||
Object.keys(MailData.ccList).map((key, index) =>
|
||||
m('p', `${rs.userList.userMap[key]}, `)
|
||||
),
|
||||
]),
|
||||
bccList &&
|
||||
Object.keys(bccList).length > 0 &&
|
||||
MailData.bccList &&
|
||||
Object.keys(MailData.bccList).length > 0 &&
|
||||
m('.msg-details__info-item', [
|
||||
m('b', 'BCC: '),
|
||||
Object.keys(bccList).map((key, index) =>
|
||||
m('b', 'Bcc: '),
|
||||
Object.keys(MailData.bccList).map((key, index) =>
|
||||
m('p', `${rs.userList.userMap[key]}, `)
|
||||
),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
m(
|
||||
'.msg-view__body',
|
||||
m(
|
||||
'iframe[title=message]',
|
||||
{
|
||||
srcdoc: message,
|
||||
},
|
||||
message
|
||||
)
|
||||
),
|
||||
files.length > 0 &&
|
||||
m('.msg-view__body', m('#msgView')),
|
||||
MailData.files.length > 0 &&
|
||||
m('.msg-view__attachment', [
|
||||
m('h3', 'Attachments'),
|
||||
m('.msg-view__attachment-items', [
|
||||
m(AttachmentSection, {
|
||||
files,
|
||||
}),
|
||||
]),
|
||||
m('.msg-view__attachment-items', m(AttachmentSection, { files: MailData.files })),
|
||||
]),
|
||||
]
|
||||
],
|
||||
m(
|
||||
'.composePopupOverlay',
|
||||
{ style: { display: showReplyCompose ? 'block' : 'none' } },
|
||||
m(
|
||||
'.composePopup',
|
||||
composeData.allUsers &&
|
||||
composeData.ownId &&
|
||||
m(compose, {
|
||||
allUsers: composeData.allUsers,
|
||||
ownId: composeData.ownId,
|
||||
msgType: 'reply',
|
||||
sender: replyData.recipients,
|
||||
recipients: replyData.sender,
|
||||
subject: MailData.subject,
|
||||
replyMessage: MailData.message,
|
||||
}),
|
||||
m(
|
||||
'button.red.close-btn',
|
||||
{ onclick: () => (showReplyCompose = false) },
|
||||
m('i.fas.fa-times')
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
};
|
||||
};
|
||||
@ -418,17 +399,13 @@ const Table = () => {
|
||||
const SearchBar = () => {
|
||||
let searchString = '';
|
||||
return {
|
||||
view: (v) =>
|
||||
view: ({ attrs: { list } }) =>
|
||||
m('input[type=text][placeholder=Search Subject].searchbar', {
|
||||
value: searchString,
|
||||
oninput: (e) => {
|
||||
searchString = e.target.value.toLowerCase();
|
||||
for (const hash in v.attrs.list) {
|
||||
if (v.attrs.list[hash].fname.toLowerCase().indexOf(searchString) > -1) {
|
||||
v.attrs.list[hash].isSearched = true;
|
||||
} else {
|
||||
v.attrs.list[hash].isSearched = false;
|
||||
}
|
||||
for (const hash in list) {
|
||||
list[hash].isSearched = list[hash].fname.toLowerCase().indexOf(searchString) > -1;
|
||||
}
|
||||
},
|
||||
}),
|
||||
@ -442,10 +419,10 @@ const activeSideLink = {
|
||||
|
||||
const Sidebar = () => {
|
||||
return {
|
||||
view: (v) =>
|
||||
view: ({ attrs: { tabs, baseRoute, size } }) =>
|
||||
m(
|
||||
'.sidebar',
|
||||
v.attrs.tabs.map((panelName, index) =>
|
||||
tabs.map((panelName, index) =>
|
||||
m(
|
||||
m.route.Link,
|
||||
{
|
||||
@ -454,11 +431,9 @@ const Sidebar = () => {
|
||||
activeSideLink.sideactive = index;
|
||||
activeSideLink.quicksideactive = -1;
|
||||
},
|
||||
href: v.attrs.baseRoute + panelName,
|
||||
href: baseRoute + panelName,
|
||||
},
|
||||
v.attrs.size[panelName] > 0
|
||||
? panelName + ' (' + v.attrs.size[panelName] + ')'
|
||||
: panelName
|
||||
size[panelName] > 0 ? `${panelName} (${size[panelName]})` : panelName
|
||||
)
|
||||
)
|
||||
),
|
||||
@ -468,11 +443,11 @@ const Sidebar = () => {
|
||||
const SidebarQuickView = () => {
|
||||
// for the Mail tab, to be moved later.
|
||||
return {
|
||||
view: (v) =>
|
||||
view: ({ attrs: { tabs, baseRoute, size } }) =>
|
||||
m(
|
||||
'.sidebarquickview',
|
||||
m('h6.bold', 'Quick View'),
|
||||
v.attrs.tabs.map((panelName, index) =>
|
||||
tabs.map((panelName, index) =>
|
||||
m(
|
||||
m.route.Link,
|
||||
{
|
||||
@ -482,11 +457,9 @@ const SidebarQuickView = () => {
|
||||
activeSideLink.quicksideactive = index;
|
||||
activeSideLink.sideactive = -1;
|
||||
},
|
||||
href: v.attrs.baseRoute + panelName,
|
||||
href: baseRoute + panelName,
|
||||
},
|
||||
v.attrs.size[panelName] > 0
|
||||
? panelName + ' (' + v.attrs.size[panelName] + ')'
|
||||
: panelName
|
||||
size[panelName] > 0 ? `${panelName} (${size[panelName]})` : panelName
|
||||
)
|
||||
)
|
||||
),
|
||||
|
||||
@ -26,7 +26,7 @@ const navIcon = {
|
||||
};
|
||||
|
||||
const navbar = () => {
|
||||
let isCollapsed = false;
|
||||
let isCollapsed = true;
|
||||
return {
|
||||
view: (vnode) =>
|
||||
m(
|
||||
|
||||
@ -8,17 +8,20 @@ function checksudo(id) {
|
||||
const UserAvatar = () => ({
|
||||
view: (v) => {
|
||||
const imageURI = v.attrs.avatar;
|
||||
console.log(imageURI);
|
||||
return imageURI === undefined || imageURI.mData.base64 === ''
|
||||
? m('div.defaultAvatar', {
|
||||
// image isn't getting loaded
|
||||
// ? m('img.defaultAvatar', {
|
||||
// src: '../data/user.png'
|
||||
// })
|
||||
}, m('p', v.attrs.firstLetter))
|
||||
? m(
|
||||
'div.defaultAvatar',
|
||||
{
|
||||
// image isn't getting loaded
|
||||
// ? m('img.defaultAvatar', {
|
||||
// src: '../data/user.png'
|
||||
// })
|
||||
},
|
||||
m('p', v.attrs.firstLetter)
|
||||
)
|
||||
: m('img.avatar', {
|
||||
src: 'data:image/png;base64,' + imageURI.mData.base64,
|
||||
});
|
||||
src: 'data:image/png;base64,' + imageURI.mData.base64,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@ -60,13 +63,13 @@ function sortIds(list) {
|
||||
return list;
|
||||
}
|
||||
|
||||
async function ownIds(consumer = (list) => { }, onlySigned = false) {
|
||||
async function ownIds(consumer = (list) => {}, onlySigned = false) {
|
||||
await rs.rsJsonApiRequest('/rsIdentity/getOwnSignedIds', {}, (owns) => {
|
||||
if (onlySigned) {
|
||||
consumer(sortIds(owns.ids));
|
||||
} else {
|
||||
rs.rsJsonApiRequest('/rsIdentity/getOwnPseudonimousIds', {}, (pseudo) =>
|
||||
consumer(sortIds(pseudo.ids.concat(owns.ids)))
|
||||
consumer(sortIds(pseudo?.ids.concat(owns.ids)))
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -119,7 +122,10 @@ const regularcontactInfo = () => {
|
||||
[
|
||||
m('h4', details.mNickname),
|
||||
details.mNickname &&
|
||||
m(UserAvatar, { avatar: details.mAvatar, firstLetter: details.mNickname.slice(0, 1).toUpperCase() }),
|
||||
m(UserAvatar, {
|
||||
avatar: details.mAvatar,
|
||||
firstLetter: details.mNickname.slice(0, 1).toUpperCase(),
|
||||
}),
|
||||
m('.details', [
|
||||
m('p', 'ID:'),
|
||||
m('p', details.mId),
|
||||
|
||||
@ -58,6 +58,16 @@ input {
|
||||
|
||||
a {
|
||||
cursor: pointer;
|
||||
&[title='Back'] {
|
||||
width: max-content;
|
||||
height: max-content;
|
||||
padding: 0.475rem 0.75rem;
|
||||
border-radius: 50%;
|
||||
transition: 100ms;
|
||||
&:hover {
|
||||
background: $light-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
table {
|
||||
|
||||
@ -42,7 +42,7 @@ html:focus-within {
|
||||
body {
|
||||
text-rendering: optimizeSpeed;
|
||||
line-height: 1.5;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-family: Arial, Helvetica, sans-serif !important;
|
||||
}
|
||||
|
||||
/* A elements that don't have a class get default styles */
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
position: absolute;
|
||||
color: #555;
|
||||
width: 40%;
|
||||
min-height: 250px;
|
||||
min-height: 10rem;
|
||||
height: max-content;
|
||||
padding: 1.5rem;
|
||||
inset: 0;
|
||||
@ -21,8 +21,17 @@
|
||||
background-color: white;
|
||||
border-radius: 1rem;
|
||||
animation: fadein 0.5s;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
& button:last-child {
|
||||
margin-top: auto;
|
||||
}
|
||||
& .close-btn {
|
||||
position: absolute;
|
||||
right: 1.5rem;
|
||||
}
|
||||
// Remove default widget padding
|
||||
& .widget {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,7 +80,7 @@
|
||||
}
|
||||
& li {
|
||||
list-style: none;
|
||||
padding: 0.5rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
cursor: pointer;
|
||||
background: white;
|
||||
border: 1px solid $light-color;
|
||||
@ -109,10 +109,22 @@
|
||||
}
|
||||
&__message {
|
||||
padding: 0.5rem 0;
|
||||
border: none;
|
||||
height: 100%;
|
||||
border-radius: 0;
|
||||
resize: none;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
&-body {
|
||||
padding: 0;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
resize: none;
|
||||
height: 100%;
|
||||
outline: transparent;
|
||||
}
|
||||
& .replybox {
|
||||
border: none;
|
||||
padding: 0;
|
||||
height: max-content;
|
||||
}
|
||||
}
|
||||
&__send-btn {
|
||||
display: flex;
|
||||
@ -165,11 +177,7 @@
|
||||
&__body {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
iframe {
|
||||
width: 100%;
|
||||
height: 98%;
|
||||
border: none;
|
||||
}
|
||||
font-size: 14px !important;
|
||||
}
|
||||
&__attachment {
|
||||
height: 50%;
|
||||
@ -386,7 +394,7 @@ table.attachment-container {
|
||||
inset: 0;
|
||||
margin: auto;
|
||||
width: 80%;
|
||||
height: 80%;
|
||||
height: 90%;
|
||||
& > .widget {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user