Replace my old ResizeObserver workaround for RainLoop bugs with proper CSS flex.

And removed wysiwyg resize() in favor of flexbox.
This commit is contained in:
djmaze 2021-11-05 14:47:19 +01:00
parent d305e090c5
commit 789dc264f7
11 changed files with 386 additions and 441 deletions

View file

@ -29,12 +29,6 @@ export class HtmlEditor {
this.onBlur = onBlur; this.onBlur = onBlur;
this.onModeChange = onModeChange; this.onModeChange = onModeChange;
this.resize = (() => {
try {
this.editor && this.editor.resize(element.clientWidth, element.clientHeight);
} catch (e) {} // eslint-disable-line no-empty
}).throttle(100);
if (element) { if (element) {
let editor; let editor;
@ -42,7 +36,6 @@ export class HtmlEditor {
this.onReady = fn => onReady.push(fn); this.onReady = fn => onReady.push(fn);
const readyCallback = () => { const readyCallback = () => {
this.editor = editor; this.editor = editor;
this.resize();
this.onReady = fn => fn(); this.onReady = fn => fn();
onReady.forEach(fn => fn()); onReady.forEach(fn => fn());
}; };

View file

@ -540,12 +540,6 @@ class SquireUI
focus() { focus() {
('plain' == this.mode ? this.plain : this.squire).focus(); ('plain' == this.mode ? this.plain : this.squire).focus();
} }
resize(width, height) {
height = Math.max(200, (height - this.wysiwyg.offsetTop)) + 'px';
this.wysiwyg.style.height = height;
this.plain.style.height = height;
}
} }
this.SquireUI = SquireUI; this.SquireUI = SquireUI;

View file

@ -2,21 +2,32 @@
.b-compose { .b-compose {
&.modal { &.modal {
width: 98%; display: flex;
flex-direction: column;
height: calc(100% - 52px);
max-width: 1000px; max-width: 1000px;
min-height: calc(100% - 52px); width: 98%;
} }
.modal-body { .modal-body {
padding: 0; padding: 0;
display: flex;
flex-direction: column;
height: 100%;
} }
.textAreaParent, .attachmentAreaParent { .textAreaParent, .attachmentAreaParent {
box-sizing: border-box; box-sizing: border-box;
height: 100%;
min-height: 200px; min-height: 200px;
position: relative; position: relative;
} }
.textAreaParent {
display: flex;
flex-direction: column;
}
.attachmentAreaParent { .attachmentAreaParent {
border-top: 1px solid #ccc; border-top: 1px solid #ccc;
overflow-y: auto; overflow-y: auto;

View file

@ -13,6 +13,11 @@ html.rl-no-preview-pane {
.messageList { .messageList {
height: 100%; height: 100%;
background-color: #fff;
border: 1px solid @rlMainDarkColor;
border-radius: @rlMainBorderRadius;
box-shadow: @rlMainShadow;
z-index: 101;
.toolbar { .toolbar {
position: absolute; position: absolute;
@ -25,14 +30,15 @@ html.rl-no-preview-pane {
white-space: nowrap; white-space: nowrap;
} }
.b-message-list-wrapper {
display: flex;
flex-direction: column;
height: 100%;
}
.b-footer { .b-footer {
position: absolute; flex-shrink: 0;
bottom: 0;
right: 0;
left: 0;
height: 30px;
padding: 7px; padding: 7px;
z-index: 101;
background-color: var(--message-list-toolbar-bg-color, #eee); background-color: var(--message-list-toolbar-bg-color, #eee);
// #gradient > .vertical(#f4f4f4, #dfdfdf); // #gradient > .vertical(#f4f4f4, #dfdfdf);
@ -59,23 +65,9 @@ html.rl-no-preview-pane {
padding-right: 8px; padding-right: 8px;
} }
.b-message-list-wrapper {
border: 1px solid @rlMainDarkColor;
height: 100%;
box-shadow: @rlMainShadow;
border-radius: @rlMainBorderRadius;
z-index: 101;
}
.second-toolbar { .second-toolbar {
position: absolute; flex-shrink: 0;
top: 0;
right: 0;
left: 0;
height: 29px;
padding: 10px 8px 10px 11px; padding: 10px 8px 10px 11px;
z-index: 101;
white-space: nowrap; white-space: nowrap;
background-color: var(--message-list-toolbar-bg-color, #eee); background-color: var(--message-list-toolbar-bg-color, #eee);
@ -108,18 +100,11 @@ html.rl-no-preview-pane {
} }
.b-content { .b-content {
position: absolute; height: 100%;
top: 50px;
bottom: 45px;
left: 0;
right: 0;
padding: 0; padding: 0;
overflow: auto; overflow: auto;
z-index: 101;
scroll-behavior: smooth; scroll-behavior: smooth;
background-color: #fff;
.listClear { .listClear {
text-align: center; text-align: center;
padding: 10px; padding: 10px;
@ -179,8 +164,7 @@ html.rl-no-preview-pane {
} }
} }
&.focused .b-message-list-wrapper { &.focused {
background-color: #000;
border-color: #9d9d9d; border-color: #9d9d9d;
} }
} }

View file

@ -3,6 +3,9 @@
} }
.messageView { .messageView {
background-color: #fff;
border: 1px solid @rlMainDarkColor;
border-radius: @rlMainBorderRadius;
height: 100%; height: 100%;
.toolbar { .toolbar {
@ -16,75 +19,79 @@
} }
.b-content { .b-content {
background-color: #fff;
border: 1px solid @rlMainDarkColor;
border-radius: @rlMainBorderRadius;
height: 100%; height: 100%;
}
.b-message-view-checked-helper { .b-message {
text-align: center; display: flex;
font-size: 70px; flex-direction: column;
line-height: 70px; height: 100%;
padding-top: 100px; }
color: #999;
.icon-mail { .b-message-view-checked-helper {
font-size: 100px; text-align: center;
font-size: 50px; font-size: 70px;
line-height: 90px; line-height: 70px;
padding-left: 10px; padding-top: 100px;
} color: #999;
.icon-mail {
font-size: 100px;
font-size: 50px;
line-height: 90px;
padding-left: 10px;
}
}
.b-message-view-desc {
text-align: center;
font-size: 24px;
line-height: 30px;
color: #999;
padding: 120px 10px 0 10px;
}
.b-message-view-desc.error {
color: #DA4F49;
}
.message-fixed-button-toolbar {
z-index: 100;
position: absolute;
top: 33px;
right: 10px;
}
.infoParent {
cursor: pointer;
opacity: 0.5;
&:hover {
opacity: 1;
}
}
.flagParent {
cursor: pointer;
margin: 0 0.25em;
.flagOn {
color: orange;
} }
.b-message-view-desc { &.flagOff:not(:hover) {
text-align: center;
font-size: 24px;
line-height: 30px;
color: #999;
padding: 120px 10px 0 10px;
}
.b-message-view-desc.error {
color: #DA4F49;
}
.message-fixed-button-toolbar {
z-index: 100;
position: absolute;
top: 33px;
right: 10px;
}
.infoParent {
cursor: pointer;
opacity: 0.5; opacity: 0.5;
&:hover {
opacity: 1;
}
}
.flagParent {
cursor: pointer;
margin: 0 0.25em;
.flagOn {
color: orange;
}
&.flagOff:not(:hover) {
opacity: 0.5;
}
} }
} }
.messageItemHeader { .messageItemHeader {
padding: 10px;
background-color: #f8f8f8; background-color: #f8f8f8;
border-bottom: 1px solid #ddd; border-bottom: 1px solid #ddd;
border-radius: @rlMainBorderRadius @rlMainBorderRadius 0 0; border-radius: @rlMainBorderRadius @rlMainBorderRadius 0 0;
padding: 10px;
flex-shrink: 0;
.subjectParent { .subjectParent {
font-size: 18px; font-size: 18px;
@ -162,11 +169,7 @@
.messageItem { .messageItem {
color: #000; color: #000;
position: absolute; height: 100%;
top: 0;
bottom: 0;
left: 0;
right: 0;
overflow: auto; overflow: auto;
scroll-behavior: smooth; scroll-behavior: smooth;
@ -443,8 +446,7 @@
} }
} }
&.focused .b-content { &.focused {
z-index: 101;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
border-color: darken(@rlMainDarkColor, 5%); border-color: darken(@rlMainDarkColor, 5%);
} }

View file

@ -1,9 +1,10 @@
.squire-toolbar { .squire-toolbar {
padding: 2px;
border-bottom: 1px solid #b6b6b6; border-bottom: 1px solid #b6b6b6;
min-height: 28px;
overflow-x: auto; overflow-x: auto;
overflow-y: hidden; overflow-y: hidden;
padding: 2px;
white-space: nowrap; white-space: nowrap;
} }
@ -38,6 +39,7 @@
.squire-wysiwyg, .squire-plain { .squire-wysiwyg, .squire-plain {
box-sizing: border-box; box-sizing: border-box;
font-size: 13px; font-size: 13px;
height: 100%;
line-height: 16px; line-height: 16px;
min-height: 200px; min-height: 200px;
overflow: auto; overflow: auto;
@ -155,16 +157,22 @@ Secondly, we can't rely on MUA's what to do with :empty
} }
/* @media (hover: none) */ /* @media (hover: none) */
.rl-mobile .textAreaParent:not(:focus-within) .squire-toolbar { @media screen and (max-height: 600px) {
display: none; .rl-mobile .textAreaParent:not(:focus-within) .squire-toolbar {
} display: none;
.rl-mobile .squire-toolbar { }
background: #EEE; .rl-mobile .squire-toolbar {
padding: 1px; background: #EEE;
position: fixed; padding: 1px;
bottom: 0; position: fixed;
left: 0; top: 18px;
right: 0; left: 0;
right: 0;
/*
transform: translateY(-42px);
z-index: 9;
*/
}
} }
.rl-mobile .b-compose.modal, .rl-mobile .b-compose.modal,

View file

@ -312,8 +312,6 @@ class ComposePopupView extends AbstractViewPopup {
} }
}); });
this.resizeObserver = new ResizeObserver(this.resizerTrigger.throttle(50).bind(this));
decorateKoCommands(this, { decorateKoCommands(this, {
sendCommand: self => self.canBeSentOrSaved(), sendCommand: self => self.canBeSentOrSaved(),
saveCommand: self => self.canBeSentOrSaved(), saveCommand: self => self.canBeSentOrSaved(),
@ -641,8 +639,6 @@ class ComposePopupView extends AbstractViewPopup {
rl.route.on(); rl.route.on();
this.resizeObserver.disconnect();
(getFullscreenElement() === this.oContent) && exitFullscreen(); (getFullscreenElement() === this.oContent) && exitFullscreen();
} }
@ -709,10 +705,6 @@ class ComposePopupView extends AbstractViewPopup {
onShow(type, oMessageOrArray, aToEmails, aCcEmails, aBccEmails, sCustomSubject, sCustomPlainText) { onShow(type, oMessageOrArray, aToEmails, aCcEmails, aBccEmails, sCustomSubject, sCustomPlainText) {
rl.route.off(); rl.route.off();
const ro = this.resizeObserver;
ro.observe(ro.compose);
ro.observe(ro.header);
this.autosaveStart(); this.autosaveStart();
this.viewModelDom.dataset.wysiwyg = SettingsUserStore.editorDefaultType(); this.viewModelDom.dataset.wysiwyg = SettingsUserStore.editorDefaultType();
@ -1088,7 +1080,7 @@ class ComposePopupView extends AbstractViewPopup {
} }
} }
onBuild(dom) { onBuild() {
// initUploader // initUploader
if (this.composeUploaderButton()) { if (this.composeUploaderButton()) {
@ -1249,12 +1241,6 @@ class ComposePopupView extends AbstractViewPopup {
return false; return false;
}); });
const ro = this.resizeObserver;
ro.compose = dom.querySelector('.b-compose');
ro.header = dom.querySelector('.b-header');
ro.toolbar = dom.querySelector('.b-header-toolbar');
ro.els = [dom.querySelector('.textAreaParent'), dom.querySelector('.attachmentAreaParent')];
this.editor(editor => editor[this.isPlainEditor()?'modePlain':'modeWysiwyg']()); this.editor(editor => editor[this.isPlainEditor()?'modePlain':'modeWysiwyg']());
// Fullscreen must be on app, else other popups fail // Fullscreen must be on app, else other popups fail
@ -1463,16 +1449,6 @@ class ComposePopupView extends AbstractViewPopup {
item => item.id item => item.id
); );
} }
resizerTrigger() {
let ro = this.resizeObserver,
height = Math.max(200, ro.compose.clientHeight - ro.header.offsetHeight - ro.toolbar.offsetHeight) + 'px';
if (ro.height !== height) {
ro.height = height;
ro.els.forEach(element => element.style.height = height);
this.oEditor && this.oEditor.resize();
}
}
} }
export { ComposePopupView, ComposePopupView as default }; export { ComposePopupView, ComposePopupView as default };

View file

@ -60,7 +60,6 @@ export class MailMessageView extends AbstractViewRight {
} }
}, this.messageVisibility); }, this.messageVisibility);
this.oHeaderDom = null;
this.oMessageScrollerDom = null; this.oMessageScrollerDom = null;
this.addObservables({ this.addObservables({
@ -116,7 +115,6 @@ export class MailMessageView extends AbstractViewRight {
this.viewUid = ''; this.viewUid = '';
this.viewHash = ''; this.viewHash = '';
this.addObservables({ this.addObservables({
viewBodyTopValue: 0,
viewSubject: '', viewSubject: '',
viewFromShort: '', viewFromShort: '',
viewFromDkimData: ['none', ''], viewFromDkimData: ['none', ''],
@ -304,10 +302,6 @@ export class MailMessageView extends AbstractViewRight {
showMessageComposer([sType, MessageUserStore.message()]); showMessageComposer([sType, MessageUserStore.message()]);
} }
checkHeaderHeight() {
this.oHeaderDom && this.viewBodyTopValue(MessageUserStore.message() ? this.oHeaderDom.offsetHeight : 0);
}
onBuild(dom) { onBuild(dom) {
this.oMessageScrollerDom = dom.querySelector('.messageItem'); this.oMessageScrollerDom = dom.querySelector('.messageItem');
@ -316,19 +310,8 @@ export class MailMessageView extends AbstractViewRight {
this.showFullInfo.subscribe(value => Local.set(ClientSideKeyName.MessageHeaderFullInfo, value ? '1' : '0')); this.showFullInfo.subscribe(value => Local.set(ClientSideKeyName.MessageHeaderFullInfo, value ? '1' : '0'));
let el = dom.querySelector('.messageItemHeader'); let event = 'fullscreenchange',
this.oHeaderDom = el; el = dom.querySelector('.b-content');
if (el) {
if (!this.resizeObserver) {
this.resizeObserver = new ResizeObserver(this.checkHeaderHeight.debounce(50).bind(this));
}
this.resizeObserver.observe(el);
} else if (this.resizeObserver) {
this.resizeObserver.disconnect();
}
let event = 'fullscreenchange';
el = dom.querySelector('.b-message-view-wrapper');
if (!el.requestFullscreen && el.webkitRequestFullscreen) { if (!el.requestFullscreen && el.webkitRequestFullscreen) {
el.requestFullscreen = el.webkitRequestFullscreen; el.requestFullscreen = el.webkitRequestFullscreen;
event = 'webkit'+event; event = 'webkit'+event;

View file

@ -23,297 +23,295 @@
</div> </div>
</div> </div>
</div> </div>
<div class="b-content b-message-view-wrapper"> <div class="b-content">
<div style="height: 100%"> <div class="b-message-view-backdrop" data-bind="visible: moveAction">
<div class="b-message-view-backdrop" data-bind="visible: moveAction"> <div class="backdrop-message">
<div class="backdrop-message"> <i class="fontastic">📁</i>
<i class="fontastic">📁</i> <span data-i18n="MESSAGE/MESSAGE_VIEW_MOVE_DESC"></span>
<span data-i18n="MESSAGE/MESSAGE_VIEW_MOVE_DESC"></span>
</div>
</div>
<div class="b-message-view-checked-helper" data-bind="visible: !message() && '' === messageError() && hasCheckedMessages()">
<span data-bind="text: printableCheckedMessageCount()"></span>
<i class="icon-mail"></i>
</div> </div>
</div>
<div class="b-message-view-checked-helper" data-bind="visible: !message() && '' === messageError() && hasCheckedMessages()">
<span data-bind="text: printableCheckedMessageCount()"></span>
<i class="icon-mail"></i>
</div>
<div class="b-message-view-desc error" data-bind="visible: !message() && '' !== messageError() && !hasCheckedMessages(), text: messageError()"> <div class="b-message-view-desc error" data-bind="visible: !message() && '' !== messageError() && !hasCheckedMessages(), text: messageError()">
</div> </div>
<div class="b-message-view-desc" data-i18n="MESSAGE/MESSAGE_VIEW_DESC" data-bind="visible: !message() && '' === messageError() && !hasCheckedMessages()"> <div class="b-message-view-desc" data-i18n="MESSAGE/MESSAGE_VIEW_DESC" data-bind="visible: !message() && '' === messageError() && !hasCheckedMessages()">
</div> </div>
<div class="message-fixed-button-toolbar" data-bind="visible: message"> <div class="message-fixed-button-toolbar" data-bind="visible: message">
<div class="btn-group" style="margin-right: -11px;"> <div class="btn-group" style="margin-right: -11px;">
<a class="btn btn-thin-2 btn-transparent buttonReply fontastic" <a class="btn btn-thin-2 btn-transparent buttonReply fontastic"
data-bind="visible: 'reply' === lastReplyAction(), command: replyCommand" data-i18n="[title]MESSAGE/BUTTON_REPLY">←</a> data-bind="visible: 'reply' === lastReplyAction(), command: replyCommand" data-i18n="[title]MESSAGE/BUTTON_REPLY">←</a>
<a class="btn btn-thin-2 btn-transparent buttonReplyAll fontastic" <a class="btn btn-thin-2 btn-transparent buttonReplyAll fontastic"
data-bind="visible: 'replyall' === lastReplyAction(), command: replyAllCommand" data-i18n="[title]MESSAGE/BUTTON_REPLY_ALL">↞</a> data-bind="visible: 'replyall' === lastReplyAction(), command: replyAllCommand" data-i18n="[title]MESSAGE/BUTTON_REPLY_ALL">↞</a>
<a class="btn btn-thin-2 btn-transparent buttonForward fontastic" <a class="btn btn-thin-2 btn-transparent buttonForward fontastic"
data-bind="visible: 'forward' === lastReplyAction(), command: forwardCommand" data-i18n="[title]MESSAGE/BUTTON_FORWARD">→</a> data-bind="visible: 'forward' === lastReplyAction(), command: forwardCommand" data-i18n="[title]MESSAGE/BUTTON_FORWARD">→</a>
<div class="btn-group" data-bind="registerBootstrapDropdown: true" style="display: inline-block"> <div class="btn-group" data-bind="registerBootstrapDropdown: true" style="display: inline-block">
<a class="btn btn-thin-2 btn-transparent dropdown-toggle" id="more-view-dropdown-id" href="#" tabindex="-1" style="margin-left: -4px; margin-right: 2px"></a> <a class="btn btn-thin-2 btn-transparent dropdown-toggle" id="more-view-dropdown-id" href="#" tabindex="-1" style="margin-left: -4px; margin-right: 2px"></a>
<ul class="dropdown-menu right-edge" role="menu" aria-labelledby="more-view-dropdown-id"> <ul class="dropdown-menu right-edge" role="menu" aria-labelledby="more-view-dropdown-id">
<div data-bind="visible: !isDraftFolder()"> <div data-bind="visible: !isDraftFolder()">
<li role="presentation"> <li role="presentation">
<a href="#" tabindex="-1" data-bind="command: replyCommand"> <a href="#" tabindex="-1" data-bind="command: replyCommand">
<i class="fontastic"></i> <i class="fontastic"></i>
<span data-i18n="MESSAGE/BUTTON_REPLY"></span> <span data-i18n="MESSAGE/BUTTON_REPLY"></span>
</a> </a>
</li> </li>
<li role="presentation"> <li role="presentation">
<a href="#" tabindex="-1" data-bind="command: replyAllCommand"> <a href="#" tabindex="-1" data-bind="command: replyAllCommand">
<i class="fontastic"></i> <i class="fontastic"></i>
<span data-i18n="MESSAGE/BUTTON_REPLY_ALL"></span> <span data-i18n="MESSAGE/BUTTON_REPLY_ALL"></span>
</a> </a>
</li> </li>
<li role="presentation"> <li role="presentation">
<a href="#" tabindex="-1" data-bind="command: forwardCommand"> <a href="#" tabindex="-1" data-bind="command: forwardCommand">
<i class="fontastic"></i> <i class="fontastic"></i>
<span data-i18n="MESSAGE/BUTTON_FORWARD"></span> <span data-i18n="MESSAGE/BUTTON_FORWARD"></span>
</a> </a>
</li> </li>
<li role="presentation"> <li role="presentation">
<a href="#" tabindex="-1" data-bind="command: editAsNewCommand"> <a href="#" tabindex="-1" data-bind="command: editAsNewCommand">
<i class="fontastic">🖉</i> <i class="fontastic">🖉</i>
<span data-i18n="MESSAGE/BUTTON_EDIT_AS_NEW"></span> <span data-i18n="MESSAGE/BUTTON_EDIT_AS_NEW"></span>
</a> </a>
</li> </li>
<li role="presentation"> <li role="presentation">
<a href="#" tabindex="-1" data-bind="command: forwardAsAttachmentCommand"> <a href="#" tabindex="-1" data-bind="command: forwardAsAttachmentCommand">
<i class="fontastic"></i> <i class="fontastic"></i>
<span data-i18n="MESSAGE/BUTTON_FORWARD_AS_ATTACHMENT"></span> <span data-i18n="MESSAGE/BUTTON_FORWARD_AS_ATTACHMENT"></span>
</a> </a>
</li> </li>
</div> </div>
<div class="dividerbar"> <div class="dividerbar">
<li role="presentation" data-bind="visible: !isDraftFolder() && !isArchiveFolder() && !isArchiveDisabled()"> <li role="presentation" data-bind="visible: !isDraftFolder() && !isArchiveFolder() && !isArchiveDisabled()">
<a target="_blank" href="#" tabindex="-1" data-bind="command: archiveCommand"> <a target="_blank" href="#" tabindex="-1" data-bind="command: archiveCommand">
<i class="icon-archive"></i> <i class="icon-archive"></i>
<span data-i18n="GLOBAL/TO_ARCHIVE"></span> <span data-i18n="GLOBAL/TO_ARCHIVE"></span>
</a> </a>
</li> </li>
<li role="presentation" data-bind="visible: !isDraftFolder() && !isSentFolder() && !isSpamFolder() && !isSpamDisabled()"> <li role="presentation" data-bind="visible: !isDraftFolder() && !isSentFolder() && !isSpamFolder() && !isSpamDisabled()">
<a target="_blank" href="#" tabindex="-1" data-bind="command: spamCommand"> <a target="_blank" href="#" tabindex="-1" data-bind="command: spamCommand">
<i class="fontastic"></i> <i class="fontastic"></i>
<span data-i18n="GLOBAL/SPAM"></span> <span data-i18n="GLOBAL/SPAM"></span>
</a> </a>
</li> </li>
<li role="presentation" data-bind="visible: !isDraftFolder() && !isSentFolder() && isSpamFolder() && !isSpamDisabled()"> <li role="presentation" data-bind="visible: !isDraftFolder() && !isSentFolder() && isSpamFolder() && !isSpamDisabled()">
<a target="_blank" href="#" tabindex="-1" data-bind="command: notSpamCommand"> <a target="_blank" href="#" tabindex="-1" data-bind="command: notSpamCommand">
<i class="icon-check-mark-circle-two"></i> <i class="icon-check-mark-circle-two"></i>
<span data-i18n="GLOBAL/NOT_SPAM"></span> <span data-i18n="GLOBAL/NOT_SPAM"></span>
</a> </a>
</li> </li>
<li role="presentation"> <li role="presentation">
<a target="_blank" href="#" tabindex="-1" data-bind="command: deleteCommand"> <a target="_blank" href="#" tabindex="-1" data-bind="command: deleteCommand">
<i class="fontastic">🗑</i> <i class="fontastic">🗑</i>
<span data-i18n="GLOBAL/DELETE"></span> <span data-i18n="GLOBAL/DELETE"></span>
</a> </a>
</li> </li>
<li role="presentation" data-bind="command: deleteWithoutMoveCommand"> <li role="presentation" data-bind="command: deleteWithoutMoveCommand">
<a href="#" tabindex="-1"> <a href="#" tabindex="-1">
<i class="fontastic">🗑</i> <i class="fontastic">🗑</i>
<span data-i18n="MESSAGE_LIST/BUTTON_DELETE_WITHOUT_MOVE"></span> <span data-i18n="MESSAGE_LIST/BUTTON_DELETE_WITHOUT_MOVE"></span>
</a> </a>
</li> </li>
<li class="dividerbar" role="presentation" data-bind="visible: message() && message().hasUnsubsribeLinks()"> <li class="dividerbar" role="presentation" data-bind="visible: message() && message().hasUnsubsribeLinks()">
<a target="_blank" href="#" tabindex="-1" data-bind="attr: { href: viewUnsubscribeLink }"> <a target="_blank" href="#" tabindex="-1" data-bind="attr: { href: viewUnsubscribeLink }">
<i class="fontastic"></i> <i class="fontastic"></i>
<span data-i18n="MESSAGE/BUTTON_UNSUBSCRIBE"></span> <span data-i18n="MESSAGE/BUTTON_UNSUBSCRIBE"></span>
</a> </a>
</li> </li>
</div> </div>
<div data-bind="visible: allowMessageActions" class="dividerbar"> <div data-bind="visible: allowMessageActions" class="dividerbar">
<li role="presentation"> <li role="presentation">
<a target="_blank" href="#" tabindex="-1" data-bind="click: function () { message() && message().printMessage(); } "> <a target="_blank" href="#" tabindex="-1" data-bind="click: function () { message() && message().printMessage(); } ">
<i class="fontastic">🖨</i> <i class="fontastic">🖨</i>
<span data-i18n="MESSAGE/MENU_PRINT"></span> <span data-i18n="MESSAGE/MENU_PRINT"></span>
</a> </a>
</li> </li>
<li role="presentation"> <li role="presentation">
<a target="_blank" href="#" tabindex="-1" data-bind="click: function () { message() && message().viewPopupMessage(); }"> <a target="_blank" href="#" tabindex="-1" data-bind="click: function () { message() && message().viewPopupMessage(); }">
<i class="icon-popup"></i> <i class="icon-popup"></i>
<span data-i18n="MESSAGE/BUTTON_IN_NEW_WINDOW"></span> <span data-i18n="MESSAGE/BUTTON_IN_NEW_WINDOW"></span>
</a> </a>
</li> </li>
<li class="dividerbar" role="presentation"> <li class="dividerbar" role="presentation">
<a target="_blank" href="#" tabindex="-1" data-bind="attr: { href: viewViewLink }"> <a target="_blank" href="#" tabindex="-1" data-bind="attr: { href: viewViewLink }">
<i class="icon-file-code"></i> <i class="icon-file-code"></i>
<span data-i18n="MESSAGE/MENU_VIEW_ORIGINAL"></span> <span data-i18n="MESSAGE/MENU_VIEW_ORIGINAL"></span>
</a> </a>
</li> </li>
<li role="presentation"> <li role="presentation">
<a target="_blank" href="#" tabindex="-1" data-bind="attr: { href: viewDownloadLink }"> <a target="_blank" href="#" tabindex="-1" data-bind="attr: { href: viewDownloadLink }">
<i class="icon-download"></i> <i class="icon-download"></i>
<span data-i18n="MESSAGE/MENU_DOWNLOAD_ORIGINAL"></span> <span data-i18n="MESSAGE/MENU_DOWNLOAD_ORIGINAL"></span>
</a> </a>
</li> </li>
</div> </div>
</ul> </ul>
</div>
</div>
<div class="btn-group hide-mobile" data-bind="visible: isDraftFolder()" data-i18n="[title]MESSAGE/BUTTON_EDIT" style="margin-right: 5px">
<a class="btn btn-success buttonEdit fontastic" data-bind="command: messageEditCommand">🖉</a>
</div> </div>
</div> </div>
<div data-bind="visible: message"> <div class="btn-group hide-mobile" data-bind="visible: isDraftFolder()" data-i18n="[title]MESSAGE/BUTTON_EDIT" style="margin-right: 5px">
<div class="messageItemHeader" data-bind="css: {'emptySubject': '' === viewSubject()}"> <a class="btn btn-success buttonEdit fontastic" data-bind="command: messageEditCommand">🖉</a>
<div class="subjectParent"> </div>
<button class="close" data-bind="command: closeMessageCommand" style="margin-top: -2px;">×</button> </div>
<span class="infoParent g-ui-user-select-none fontastic" data-bind="click: function() { showFullInfo(!showFullInfo()); }"></span>
<span class="flagParent g-ui-user-select-none flagOff fontastic" data-bind="text: viewIsFlagged() ? '★' : '☆', css: {'flagOn': viewIsFlagged, 'flagOff': !viewIsFlagged()}"></span> <div class="b-message" data-bind="visible: message">
<b style="color: red; margin-right: 5px" data-bind="visible: viewIsImportant">!</b> <div class="messageItemHeader" data-bind="css: {'emptySubject': '' === viewSubject()}">
<span class="subject" data-bind="text: viewSubject, title: viewSubject, event: { 'dblclick': toggleFullScreen }"></span> <div class="subjectParent">
<span class="emptySubjectText" data-i18n="MESSAGE/EMPTY_SUBJECT_TEXT" data-bind="event: { 'dblclick': toggleFullScreen }"></span> <button class="close" data-bind="command: closeMessageCommand" style="margin-top: -2px;">×</button>
</div> <span class="infoParent g-ui-user-select-none fontastic" data-bind="click: function() { showFullInfo(!showFullInfo()); }"></span>
<div data-bind="hidden: showFullInfo(), event: { 'dblclick': toggleFullScreen }"> <span class="flagParent g-ui-user-select-none flagOff fontastic" data-bind="text: viewIsFlagged() ? '★' : '☆', css: {'flagOn': viewIsFlagged, 'flagOff': !viewIsFlagged()}"></span>
<div class="informationShort"> <b style="color: red; margin-right: 5px" data-bind="visible: viewIsImportant">!</b>
<span class="from" data-bind="html: viewFromShort, title: viewFrom"></span> <span class="subject" data-bind="text: viewSubject, title: viewSubject, event: { 'dblclick': toggleFullScreen }"></span>
<span data-bind="visible: viewFromDkimVisibility"> <span class="emptySubjectText" data-i18n="MESSAGE/EMPTY_SUBJECT_TEXT" data-bind="event: { 'dblclick': toggleFullScreen }"></span>
&nbsp; </div>
<i data-bind="css: viewFromDkimStatusIconClass, title: viewFromDkimStatusTitle"></i> <div data-bind="hidden: showFullInfo(), event: { 'dblclick': toggleFullScreen }">
</span> <div class="informationShort">
<div data-bind="visible: 0 < viewTimeStamp()"> <span class="from" data-bind="html: viewFromShort, title: viewFrom"></span>
(<time class="date" data-moment-format="FULL" data-bind="moment: viewTimeStamp"></time>) <span data-bind="visible: viewFromDkimVisibility">
</div> &nbsp;
</div> <i data-bind="css: viewFromDkimStatusIconClass, title: viewFromDkimStatusTitle"></i>
<div class="informationShortWrp"> </span>
<div class="informationShort" data-bind="visible: viewTo"> <div data-bind="visible: 0 < viewTimeStamp()">
<span data-i18n="GLOBAL/TO"></span>:&nbsp; (<time class="date" data-moment-format="FULL" data-bind="moment: viewTimeStamp"></time>)
<span data-bind="text: viewTo"></span>
</div>
<div class="informationShort" data-bind="visible: viewCc">
<span data-i18n="GLOBAL/CC"></span>:&nbsp;
<span data-bind="text: viewCc"></span>
</div>
</div>
<div class="informationShort" data-bind="visible: viewSpamStatus()">
<span data-i18n="MESSAGE/SPAM_SCORE"></span>:&nbsp;
<meter min="0" max="100" optimum="0" low="33" high="66" data-bind="value: viewSpamScore, title: viewSpamStatus"></meter>
</div> </div>
</div> </div>
<div class="informationFull" data-bind="visible: showFullInfo()"> <div class="informationShortWrp">
<table> <div class="informationShort" data-bind="visible: viewTo">
<tr data-bind="visible: '' !== viewFrom()"> <span data-i18n="GLOBAL/TO"></span>:&nbsp;
<td data-i18n="GLOBAL/FROM"></td> <span data-bind="text: viewTo"></span>
<td><span data-bind="text: viewFrom, title: viewFrom"></span> </div>
<span data-bind="visible: viewFromDkimVisibility"> <div class="informationShort" data-bind="visible: viewCc">
&nbsp; <span data-i18n="GLOBAL/CC"></span>:&nbsp;
<i data-bind="css: viewFromDkimStatusIconClass, title: viewFromDkimStatusTitle"></i> <span data-bind="text: viewCc"></span>
</span> </div>
</td> </div>
</tr> <div class="informationShort" data-bind="visible: viewSpamStatus()">
<tr data-bind="visible: '' !== viewTo()"> <span data-i18n="MESSAGE/SPAM_SCORE"></span>:&nbsp;
<td data-i18n="GLOBAL/TO"></td> <meter min="0" max="100" optimum="0" low="33" high="66" data-bind="value: viewSpamScore, title: viewSpamStatus"></meter>
<td data-bind="text: viewTo, title: viewTo"></td> </div>
</tr> </div>
<tr data-bind="visible: '' !== viewCc()"> <div class="informationFull" data-bind="visible: showFullInfo()">
<td data-i18n="GLOBAL/CC"></td> <table>
<td data-bind="text: viewCc, title: viewCc"></td> <tr data-bind="visible: '' !== viewFrom()">
</tr> <td data-i18n="GLOBAL/FROM"></td>
<tr data-bind="visible: '' !== viewBcc()"> <td><span data-bind="text: viewFrom, title: viewFrom"></span>
<td data-i18n="GLOBAL/BCC"></td> <span data-bind="visible: viewFromDkimVisibility">
<td data-bind="text: viewBcc, title: viewBcc"></td>
</tr>
<tr data-bind="visible: '' !== viewReplyTo()">
<td data-i18n="GLOBAL/REPLY_TO"></td>
<td data-bind="text: viewReplyTo, title: viewReplyTo"></td>
</tr>
<tr data-bind="visible: 0 < viewTimeStamp()">
<td data-i18n="MESSAGE/LABEL_DATE"></td>
<td>
<time data-moment-format="FULL" data-bind="moment: viewTimeStamp"></time>
&nbsp; &nbsp;
(<time data-moment-format="FROMNOW" data-bind="moment: viewTimeStamp"></time>) <i data-bind="css: viewFromDkimStatusIconClass, title: viewFromDkimStatusTitle"></i>
</td> </span>
</tr> </td>
<tr data-bind="visible: 0 < viewSpamScore()"> </tr>
<td data-i18n="MESSAGE/SPAM_SCORE"></td> <tr data-bind="visible: '' !== viewTo()">
<td><span data-bind="text: viewSpamScore, title: viewSpamStatus"></span>%</td> <td data-i18n="GLOBAL/TO"></td>
</tr> <td data-bind="text: viewTo, title: viewTo"></td>
<tr data-bind="visible: '' !== viewFrom()"> </tr>
<td data-i18n="POPUPS_FILTER/SELECT_FIELD_SIZE"></td> <tr data-bind="visible: '' !== viewCc()">
<td class="size" data-bind="text: viewSize"></td> <td data-i18n="GLOBAL/CC"></td>
</tr> <td data-bind="text: viewCc, title: viewCc"></td>
</table> </tr>
</div> <tr data-bind="visible: '' !== viewBcc()">
<div class="hasVirus" data-bind="visible: hasVirus()" data-i18n="MESSAGE/HAS_VIRUS_WARNING"></div> <td data-i18n="GLOBAL/BCC"></td>
<td data-bind="text: viewBcc, title: viewBcc"></td>
</tr>
<tr data-bind="visible: '' !== viewReplyTo()">
<td data-i18n="GLOBAL/REPLY_TO"></td>
<td data-bind="text: viewReplyTo, title: viewReplyTo"></td>
</tr>
<tr data-bind="visible: 0 < viewTimeStamp()">
<td data-i18n="MESSAGE/LABEL_DATE"></td>
<td>
<time data-moment-format="FULL" data-bind="moment: viewTimeStamp"></time>
&nbsp;
(<time data-moment-format="FROMNOW" data-bind="moment: viewTimeStamp"></time>)
</td>
</tr>
<tr data-bind="visible: 0 < viewSpamScore()">
<td data-i18n="MESSAGE/SPAM_SCORE"></td>
<td><span data-bind="text: viewSpamScore, title: viewSpamStatus"></span>%</td>
</tr>
<tr data-bind="visible: '' !== viewFrom()">
<td data-i18n="POPUPS_FILTER/SELECT_FIELD_SIZE"></td>
<td class="size" data-bind="text: viewSize"></td>
</tr>
</table>
</div> </div>
<div class="messageItem" data-bind="css: viewLineAsCss(), attr: {'style': 'top:' + viewBodyTopValue() + 'px' }"> <div class="hasVirus" data-bind="visible: hasVirus()" data-i18n="MESSAGE/HAS_VIRUS_WARNING"></div>
<div tabindex="0" data-bind="hasfocus: messageDomFocused"> </div>
<div class="messageItem" data-bind="css: viewLineAsCss()">
<div tabindex="0" data-bind="hasfocus: messageDomFocused">
<div> <div>
<span class="buttonFull" data-bind="click: toggleFullScreen"> <span class="buttonFull" data-bind="click: toggleFullScreen">
<i data-bind="css: { 'icon-arrows-out': !fullScreenMode(), 'icon-arrows-in': fullScreenMode }"></i> <i data-bind="css: { 'icon-arrows-out': !fullScreenMode(), 'icon-arrows-in': fullScreenMode }"></i>
</span> </span>
<div class="loading g-ui-min-height-300" data-bind="visible: messageLoadingThrottle()"> <div class="loading g-ui-min-height-300" data-bind="visible: messageLoadingThrottle()">
<i class="icon-spinner"></i> <i class="icon-spinner"></i>
<span data-i18n="GLOBAL/LOADING"></span> <span data-i18n="GLOBAL/LOADING"></span>
</div>
</div> </div>
</div>
<div class="g-ui-min-height-300" data-bind="visible: !messageLoadingThrottle()"> <div class="g-ui-min-height-300" data-bind="visible: !messageLoadingThrottle()">
<div class="bodySubHeader"> <div class="bodySubHeader">
<div class="showImages" data-bind="visible: message() && message().hasImages(), click: showImages"> <div class="showImages" data-bind="visible: message() && message().hasImages(), click: showImages">
<i class="fontastic">🖼</i> <i class="fontastic">🖼</i>
<span class="text" data-i18n="MESSAGE/BUTTON_SHOW_IMAGES"></span> <span class="text" data-i18n="MESSAGE/BUTTON_SHOW_IMAGES"></span>
</div> </div>
<div class="readReceipt" data-bind="visible: message() && !isDraftOrSentFolder() && '' !== message().readReceipt() && !message().isReadReceipt(), click: readReceipt"> <div class="readReceipt" data-bind="visible: message() && !isDraftOrSentFolder() && '' !== message().readReceipt() && !message().isReadReceipt(), click: readReceipt">
<i class="icon-mail"></i> <i class="icon-mail"></i>
<span class="text" data-i18n="MESSAGE/BUTTON_NOTIFY_READ_RECEIPT"></span> <span class="text" data-i18n="MESSAGE/BUTTON_NOTIFY_READ_RECEIPT"></span>
</div> </div>
<div class="attachmentsPlace" data-bind="visible: message() && message().attachments().hasVisible(), <div class="attachmentsPlace" data-bind="visible: message() && message().attachments().hasVisible(),
css: {'selection-mode' : showAttachmnetControls, 'unselectedAttachmentsError': highlightUnselectedAttachments}"> css: {'selection-mode' : showAttachmnetControls, 'unselectedAttachmentsError': highlightUnselectedAttachments}">
<ul class="attachmentList" data-bind="foreach: message() ? message().attachments() : []"> <ul class="attachmentList" data-bind="foreach: message() ? message().attachments() : []">
<li class="attachmentItem" draggable="true" <li class="attachmentItem" draggable="true"
data-bind="visible: !isLinked, event: { 'dragstart': eventDragStart }, attr: { 'title': fileName }, css: {'checked': checked}"> data-bind="visible: !isLinked, event: { 'dragstart': eventDragStart }, attr: { 'title': fileName }, css: {'checked': checked}">
<div class="attachmentIconParent" data-bind="css: { 'hasPreview': hasPreview(), 'hasPreplay': hasPreplay(), 'isImage': isImage() }"> <div class="attachmentIconParent" data-bind="css: { 'hasPreview': hasPreview(), 'hasPreplay': hasPreplay(), 'isImage': isImage() }">
<i class="hidePreview iconMain" data-bind="css: iconClass()"></i> <i class="hidePreview iconMain" data-bind="css: iconClass()"></i>
<div class="showPreview"> <div class="showPreview">
<a data-bind="css: {'attachmentImagePreview': isImage()}, attr: { 'title': fileName, 'href': linkPreviewMain() }" target="_blank"> <a data-bind="css: {'attachmentImagePreview': isImage()}, attr: { 'title': fileName, 'href': linkPreviewMain() }" target="_blank">
<i class="iconMain" data-bind="css: iconClass()"></i>
<div class="iconBG" data-bind="attr: { 'style': linkThumbnailPreviewStyle() }"></div>
<div class="iconPreview fontastic">👁</div>
</a>
</div>
<div class="showPreplay">
<i class="iconMain" data-bind="css: iconClass()"></i> <i class="iconMain" data-bind="css: iconClass()"></i>
<div class="iconPreview fontastic"></div> <div class="iconBG" data-bind="attr: { 'style': linkThumbnailPreviewStyle() }"></div>
</div> <div class="iconPreview fontastic">👁</div>
</a>
</div> </div>
<div class="attachmentNameParent"> <div class="showPreplay">
<div style="white-space: nowrap; text-overflow: ellipsis; overflow: hidden;" class="attachmentName" data-bind="text: fileName"></div> <i class="iconMain" data-bind="css: iconClass()"></i>
<div class="attachmentSize" data-bind="text: friendlySize"></div> <div class="iconPreview fontastic"></div>
</div> </div>
<div class="checkboxAttachment fontastic" </div>
data-bind="text: checked() ? '☑' : '☐', <div class="attachmentNameParent">
click: function () { checked(!checked()); return false }"></div> <div style="white-space: nowrap; text-overflow: ellipsis; overflow: hidden;" class="attachmentName" data-bind="text: fileName"></div>
</li> <div class="attachmentSize" data-bind="text: friendlySize"></div>
</ul> </div>
<i class="fontastic controls-handle" data-bind="visible: allowAttachmnetControls() && !showAttachmnetControls(), click: function () { showAttachmnetControls(true); showAttachmnetControlsState(true); }"></i> <div class="checkboxAttachment fontastic"
</div> data-bind="text: checked() ? '☑' : '☐',
click: function () { checked(!checked()); return false }"></div>
<div class="attachmentsControls" </li>
data-bind="visible: showAttachmnetControls() && message() && message().attachments().hasVisible()"> </ul>
<i class="fontastic controls-handle" data-bind="visible: allowAttachmnetControls() && !showAttachmnetControls(), click: function () { showAttachmnetControls(true); showAttachmnetControlsState(true); }"></i>
<span data-bind="visible: downloadAsZipAllowed">
<i class="fontastic iconcolor-red" data-bind="visible: downloadAsZipError"></i>
<i class="icon-file-archive" data-bind="visible: !downloadAsZipError(),
css: {'icon-file-archive': !downloadAsZipLoading(), 'icon-spinner': downloadAsZipLoading()}"></i>
<span class="g-ui-link" data-bind="click: downloadAsZip"
data-i18n="MESSAGE/LINK_DOWNLOAD_AS_ZIP"></span>
</span>
<button type="button" class="close" style="margin-right: 5px;"
data-bind="click: function () { showAttachmnetControls(false); showAttachmnetControlsState(false); }">×</button>
</div>
</div> </div>
<div class="bodyText g-ui-min-height-300" <div class="attachmentsControls"
data-bind="initDom: messagesBodiesDom"></div> data-bind="visible: showAttachmnetControls() && message() && message().attachments().hasVisible()">
<span data-bind="visible: downloadAsZipAllowed">
<i class="fontastic iconcolor-red" data-bind="visible: downloadAsZipError"></i>
<i class="icon-file-archive" data-bind="visible: !downloadAsZipError(),
css: {'icon-file-archive': !downloadAsZipLoading(), 'icon-spinner': downloadAsZipLoading()}"></i>
<span class="g-ui-link" data-bind="click: downloadAsZip"
data-i18n="MESSAGE/LINK_DOWNLOAD_AS_ZIP"></span>
</span>
<button type="button" class="close" style="margin-right: 5px;"
data-bind="click: function () { showAttachmnetControls(false); showAttachmnetControlsState(false); }">×</button>
</div>
</div> </div>
<div class="bodyText g-ui-min-height-300"
data-bind="initDom: messagesBodiesDom"></div>
</div> </div>
</div> </div>
</div> </div>

View file

@ -153,7 +153,7 @@
</table> </table>
</div> </div>
<div class="attachmentAreaParent b-content" data-bind="visible: attachmentsPlace"> <div class="attachmentAreaParent" data-bind="visible: attachmentsPlace">
<div class="b-attachment-place" data-bind="visible: addAttachmentEnabled() && dragAndDropVisible(), initDom: composeUploaderDropPlace, css: {'dragAndDropOver': dragAndDropOver}" <div class="b-attachment-place" data-bind="visible: addAttachmentEnabled() && dragAndDropVisible(), initDom: composeUploaderDropPlace, css: {'dragAndDropOver': dragAndDropOver}"
data-i18n="COMPOSE/ATTACH_DROP_FILES_DESC"></div> data-i18n="COMPOSE/ATTACH_DROP_FILES_DESC"></div>
<ul class="attachmentList" data-bind="foreach: attachments"> <ul class="attachmentList" data-bind="foreach: attachments">

View file

@ -71,15 +71,11 @@
border-top-color: var(--main-color); border-top-color: var(--main-color);
} }
.messageList .b-message-list-wrapper {
border-color: #555;
}
.well, .well,
.modal, .modal,
.b-settins-right .b-content, .b-settins-right .b-content,
.messageList .b-content, .messageList,
.messageView .b-content, .messageView,
.b-admin-right .b-content, .b-admin-right .b-content,
.b-compose .b-attachment-place { .b-compose .b-attachment-place {
background-color: #000; background-color: #000;