Consolidates a message's "reply", "reply all", and "forward" button into 2 buttons
and put them in the top-right corner of the Message screen. The Composer is removed from the Message screen and become a screen on its own that's shown when the user clicks one of these "reply" buttons in the Message screen. Signed-off-by: Cyrille Bollu <cyrpub@bollu.be> =========================================== (here's the detail of this squashed commit) Fixed a typo in a console.log message Consolidate reply buttons: 1) Adds 3 action buttons in Message component (they are not yet properly placed but at least they work) 2) Removes the Composer component from the Message component; It is now only shown within the FolderContent component. 3) Updates FolderContent component to display Composer not only for 'new' messages, but also for reply, reply all, and forward messages 4) Updates NewMessageDetail to handle the reply, reply all, and forward cases Fixes buildFowardSubject to buildForwardSubject Consolidate message reply buttons: Wraps the 3 ActionButtons in an Actions component Consolidate message reply buttons: Removes old forward button Consolidate message reply buttons: fixes a travis prettier/prettier error Consolidate message reply buttons: Removes registration of Composer component from Message.vue as it is not used anymore there. Consolidate message reply buttons: fixes some travis no-redeclare errors Consolidate message reply buttons: fixes a travis prettier/prettier error Adds 'reply-all' and 'reply' icons using @icon-black-white instead of @icon-color for icon-reply, icon-reply-all, and icon-forward * In Message.vue, message-action "Reply all" is always visible and switches to "Reply" when there's only one recipient in the original message. * An Actions menu is added to show "Reply to sender only" and "forward" buttons when needed. Better formatting of the 'reply' button Fixes the initializing of a new message's data when replying or forwarding a message. Puts the "Send' button next to the attachments buttons in the Composer Corrects the @include icon-black-white for icons reply, replay-all, and forward Better positioning of the message's actions buttons and removal of the "reply" label to save space Nicer reply button Sets component appcontentdetails' width to 100% (this doesn't leave enough space for the "appt-content-list" div though) Modifies reply-all.vsg and forward.svg so that every <path></path> tag is on a single line, as nextcloud server's IconsCacher's colorizeSvg method doesn't support those tags being on multiple lines. * Puts email actions button on the far right side (justified) * Tries to put a label on action "reply" when screen is large enough (doesn't work yet though) Fixes a typo in forward.svg Do not use "width: 100%" for appcontentdetails but rather "flex-grow: 1" so as to not shrink app-content-list's width Tidies up 'forward' and 'reply-all' icons Sets focus to message body when Composer is opened Removes unused generateIconUrl Message function Removes reply button's label on screen smaller than 600px wide. Removes transparency=0,5 from icon reply.svg Adds buttons 'mark read/unread', and 'delete' as message actions Ellipses message title and adresses when space is short. Message wouldn't be delete when therewas no next/envelope. Optimise reply.svg Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com> Signed-off-by: Cyrille Bollu <cyrpub@bollu.be> Consolidated reply buttons: Replaces the "Send" button by an action menu in the toright corner of the Composer component. Signed-off-by: Cyrille Bollu <cyrpub@bollu.be> Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at> Signed-off-by: Cyrille Bollu <cyrpub@bollu.be> The height of the Composer 'send button shouldn't grow when attachments are added. 2 eslint fixes
This commit is contained in:
Родитель
f650fb6d1e
Коммит
69c01c6466
|
@ -290,12 +290,13 @@
|
|||
height: 90px;
|
||||
z-index: 100;
|
||||
position: fixed; // ie fallback
|
||||
position: -webkit-sticky; // ios/safari fallback
|
||||
position: sticky;
|
||||
top: $header-height;
|
||||
background: -webkit-linear-gradient(var(--color-main-background) , var(--color-main-background) 80%, rgba(255,255,255,0));
|
||||
background: -o-linear-gradient(var(--color-main-background) , var(--color-main-background) 80%, rgba(255,255,255,0));
|
||||
background: -moz-linear-gradient(var(--color-main-background) , var(--color-main-background) 80%, rgba(255,255,255,0));
|
||||
background: linear-gradient(var(--color-main-background) , var(--color-main-background) 80%, rgba(255,255,255,0));
|
||||
background: -webkit-linear-gradient(var(--color-main-background), var(--color-main-background) 80%, rgba(255,255,255,0));
|
||||
background: -o-linear-gradient(var(--color-main-background), var(--color-main-background) 80%, rgba(255,255,255,0));
|
||||
background: -moz-linear-gradient(var(--color-main-background), var(--color-main-background) 80%, rgba(255,255,255,0));
|
||||
background: linear-gradient(var(--color-main-background), var(--color-main-background) 80%, rgba(255,255,255,0));
|
||||
}
|
||||
|
||||
.transparency {
|
||||
|
@ -349,8 +350,9 @@
|
|||
@include icon-color('trash', 'mail', $color-black);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@include icon-black-white('reply', 'mail', 1);
|
||||
@include icon-black-white('reply-all', 'mail', 1);
|
||||
@include icon-black-white('forward', 'mail', 1);
|
||||
|
||||
// FIXES for core apps.scss
|
||||
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
@media only screen and (max-width: 768px) {
|
||||
|
||||
/* full width for message list on mobile */
|
||||
#mail-messages {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* correctly center loading icon on mobile */
|
||||
#mail-message-list-loading {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* overlay message detail on top of message list */
|
||||
#mail-message {
|
||||
z-index: 100;
|
||||
background: #fff;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
box-shadow: 0 0 100px rgba(100, 100, 100, .9);
|
||||
}
|
||||
|
||||
#mail-message-close {
|
||||
display: block;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 45px;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
background-size: 24px;
|
||||
z-index: 101;
|
||||
cursor: pointer;
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=40)";
|
||||
opacity: .4;
|
||||
}
|
||||
#mail-message-close:hover,
|
||||
#mail-message-close:focus {
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)";
|
||||
opacity: .6;
|
||||
}
|
||||
|
||||
.hidden-mobile {
|
||||
display: none;
|
||||
}
|
||||
|
||||
textarea.message-body {
|
||||
padding-right: 12px;
|
||||
}
|
||||
|
||||
#mail-content,
|
||||
.mail-message-attachments {
|
||||
margin: 0 10px 50px 30px;
|
||||
}
|
||||
|
||||
/* end of media query */
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
@media only screen and (max-width: $breakpoint-mobile) {
|
||||
/* full width for message list on mobile */
|
||||
#mail-messages {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* correctly center loading icon on mobile */
|
||||
#mail-message-list-loading {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* overlay message detail on top of message list */
|
||||
#mail-message {
|
||||
z-index: 100;
|
||||
background: #fff;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
#mail-message-close {
|
||||
display: block;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 45px;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
background-size: 24px;
|
||||
z-index: 101;
|
||||
cursor: pointer;
|
||||
-ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=40)';
|
||||
opacity: 0.4;
|
||||
}
|
||||
#mail-message-close:hover,
|
||||
#mail-message-close:focus {
|
||||
-ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=60)';
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.hidden-mobile {
|
||||
display: none;
|
||||
}
|
||||
|
||||
textarea.message-body {
|
||||
padding-right: 12px;
|
||||
}
|
||||
|
||||
#mail-content,
|
||||
.mail-message-attachments {
|
||||
margin: 0 10px 50px 30px;
|
||||
}
|
||||
|
||||
/* reply-forward actions align to the far right */
|
||||
#mail-message-header #mail-message-actions {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 319 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" height="16" width="16"><path d="M1 15s.391-7.803 7-10V1l7 7-7 7v-4c-5.116 0-7 4-7 4z"/></svg>
|
После Ширина: | Высота: | Размер: 187 B |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 331 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" height="16" width="16"><path d="M15 13.5s-.308-6.13-5.5-7.857V2.5L4 8l5.5 5.5v-3.143c4.02 0 5.5 3.143 5.5 3.143z"/><path d="M6.768 2.232L1 8l5.768 5.768V12l-4-4 4-4z"/></svg>
|
После Ширина: | Высота: | Размер: 268 B |
|
@ -1,6 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="16" width="16" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<g opacity=".5" transform="matrix(-1,0,0,1,16,-1036.4)">
|
||||
<path d="m1 1051.4s0.39147-7.8025 7-10v-4l7 7-7 7v-4c-5.1157 0-7 4-7 4z"/>
|
||||
</g>
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16"><path d="M15 15s-.4-7.8-7-10V1L1 8l7 7v-4c5.1 0 7 4 7 4z"/></svg>
|
До Ширина: | Высота: | Размер: 423 B После Ширина: | Высота: | Размер: 128 B |
|
@ -3110,7 +3110,7 @@
|
|||
},
|
||||
"strip-ansi": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
|
||||
"resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
|
||||
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
@ -3468,7 +3468,7 @@
|
|||
},
|
||||
"json5": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
|
||||
"resolved": "http://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
|
||||
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
@ -3488,7 +3488,7 @@
|
|||
},
|
||||
"minimist": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
|
||||
"dev": true
|
||||
},
|
||||
|
@ -4128,7 +4128,7 @@
|
|||
},
|
||||
"strip-ansi": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
|
||||
"resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
|
||||
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
@ -5322,7 +5322,7 @@
|
|||
"dependencies": {
|
||||
"minimist": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
|
||||
}
|
||||
}
|
||||
|
@ -5523,7 +5523,7 @@
|
|||
},
|
||||
"strip-ansi": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
|
||||
"resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
|
||||
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
@ -5890,7 +5890,7 @@
|
|||
},
|
||||
"json5": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
|
||||
"resolved": "http://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
|
||||
"integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE="
|
||||
},
|
||||
"jsprim": {
|
||||
|
@ -6189,7 +6189,7 @@
|
|||
"dependencies": {
|
||||
"minimist": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
|
||||
"dev": true
|
||||
}
|
||||
|
@ -6305,7 +6305,7 @@
|
|||
},
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
||||
"resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
|
||||
"dev": true
|
||||
},
|
||||
|
@ -6486,7 +6486,7 @@
|
|||
},
|
||||
"yargs": {
|
||||
"version": "13.2.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.2.tgz",
|
||||
"resolved": "http://registry.npmjs.org/yargs/-/yargs-13.2.2.tgz",
|
||||
"integrity": "sha512-WyEoxgyTD3w5XRpAQNYUB9ycVH/PQrToaTXdYXRdOXvEy1l19br+VJsc0vcO8PTGg5ro/l/GY7F/JMEBmI0BxA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
@ -8371,7 +8371,7 @@
|
|||
},
|
||||
"yargs": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz",
|
||||
"resolved": "http://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz",
|
||||
"integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
@ -8927,7 +8927,7 @@
|
|||
},
|
||||
"strip-ansi": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
|
||||
"resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
|
||||
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
@ -8946,7 +8946,7 @@
|
|||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
|
@ -9052,7 +9052,7 @@
|
|||
},
|
||||
"strip-ansi": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
|
||||
"resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
|
||||
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
@ -9528,7 +9528,7 @@
|
|||
},
|
||||
"json5": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
|
||||
"resolved": "http://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
|
||||
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
@ -9548,7 +9548,7 @@
|
|||
},
|
||||
"minimist": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
|
||||
"dev": true
|
||||
},
|
||||
|
@ -10960,7 +10960,7 @@
|
|||
},
|
||||
"wrap-ansi": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
|
||||
"resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
|
||||
"integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
@ -11223,7 +11223,7 @@
|
|||
},
|
||||
"yargs": {
|
||||
"version": "12.0.5",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz",
|
||||
"resolved": "http://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz",
|
||||
"integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
|
|
@ -111,7 +111,7 @@ export const buildReplySubject = original => {
|
|||
// TODO: https://en.wikipedia.org/wiki/List_of_email_subject_abbreviations#Abbreviations_in_other_languages
|
||||
const forwardPrepends = ['fwd']
|
||||
|
||||
export const buildFowardSubject = original => {
|
||||
export const buildForwardSubject = original => {
|
||||
if (forwardPrepends.some(prepend => original.toLowerCase().startsWith(`${prepend}:`))) {
|
||||
return original
|
||||
}
|
||||
|
|
|
@ -91,6 +91,7 @@
|
|||
</div>
|
||||
<div class="composer-fields">
|
||||
<textarea
|
||||
ref="body"
|
||||
v-model="bodyVal"
|
||||
v-autosize
|
||||
name="body"
|
||||
|
@ -100,12 +101,14 @@
|
|||
@keypress="onBodyKeyPress"
|
||||
></textarea>
|
||||
</div>
|
||||
<Actions class="submit-message-wrapper app-content-list-item-menu" menu-align="right">
|
||||
<ActionButton icon="icon-mail" @click="onSend">{{ t('mail', 'Reply') }}</ActionButton>
|
||||
<ActionButton icon="icon-delete" @click="onSend">{{ t('mail', 'Reply all') }}</ActionButton>
|
||||
<ActionButton icon="icon-delete" @click="onSend">{{ t('mail', 'Forward') }}</ActionButton>
|
||||
</Actions>
|
||||
<ComposerAttachments v-model="attachments" @upload="onAttachmentsUploading" />
|
||||
<div class="composer-actions">
|
||||
<div>
|
||||
<ComposerAttachments v-model="attachments" @upload="onAttachmentsUploading" />
|
||||
</div>
|
||||
<div>
|
||||
<input class="submit-message send primary" type="submit" :value="submitButtonTitle" @click="onSend" />
|
||||
</div>
|
||||
</div>
|
||||
<span v-if="savingDraft === true" id="draft-status">{{ t('mail', 'Saving draft …') }}</span>
|
||||
<span v-else-if="savingDraft === false" id="draft-status">{{ t('mail', 'Draft saved') }}</span>
|
||||
</div>
|
||||
|
@ -137,7 +140,6 @@ import {findRecipient} from '../service/AutocompleteService'
|
|||
import Loading from './Loading'
|
||||
import Logger from '../logger'
|
||||
import ComposerAttachments from './ComposerAttachments'
|
||||
import {Actions, ActionButton} from 'nextcloud-vue'
|
||||
|
||||
const debouncedSearch = debouncePromise(findRecipient, 500)
|
||||
|
||||
|
@ -154,8 +156,6 @@ const STATES = Object.seal({
|
|||
export default {
|
||||
name: 'Composer',
|
||||
components: {
|
||||
Actions,
|
||||
ActionButton,
|
||||
ComposerAttachments,
|
||||
Loading,
|
||||
Multiselect,
|
||||
|
@ -243,6 +243,10 @@ export default {
|
|||
}
|
||||
this.bodyVal = this.bodyWithSignature(this.selectedAlias, this.body)
|
||||
},
|
||||
mounted: function() {
|
||||
this.$refs.body.focus()
|
||||
this.$refs.body.setSelectionRange(0, 0)
|
||||
},
|
||||
methods: {
|
||||
recipientToRfc822(recipient) {
|
||||
if (recipient.email === recipient.label) {
|
||||
|
@ -382,6 +386,12 @@ export default {
|
|||
margin: 0;
|
||||
}
|
||||
|
||||
.composer-actions {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.composer-fields.mail-account > .multiselect {
|
||||
max-width: none;
|
||||
min-height: auto;
|
||||
|
@ -465,13 +475,6 @@ label.bcc-label {
|
|||
textarea.reply {
|
||||
min-height: 100px;
|
||||
}
|
||||
|
||||
.submit-message-wrapper {
|
||||
position: fixed;
|
||||
top: 135px;
|
||||
right: 15px;
|
||||
z-index: 9999; /* always on top */
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
|
|
|
@ -73,7 +73,11 @@ export default {
|
|||
return this.hasMessages && this.$route.name === 'message'
|
||||
},
|
||||
newMessage() {
|
||||
return this.$route.params.messageUid === 'new'
|
||||
return (
|
||||
this.$route.params.messageUid === 'new' ||
|
||||
this.$route.params.messageUid === 'reply' ||
|
||||
this.$route.params.messageUid === 'replyAll'
|
||||
)
|
||||
},
|
||||
envelopes() {
|
||||
if (this.searchQuery === undefined) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<AppContentDetails>
|
||||
<AppContentDetails id="mail-message">
|
||||
<Loading v-if="loading" />
|
||||
<Error
|
||||
v-else-if="!message"
|
||||
|
@ -10,38 +10,54 @@
|
|||
</Error>
|
||||
<template v-else>
|
||||
<div id="mail-message-header" class="section">
|
||||
<h2 :title="message.subject">{{ message.subject }}</h2>
|
||||
<p class="transparency">
|
||||
<AddressList :entries="message.from" />
|
||||
to
|
||||
<!-- TODO: translate -->
|
||||
<AddressList :entries="message.to" />
|
||||
<template v-if="message.cc.length">
|
||||
(cc
|
||||
<div id="mail-message-header-fields">
|
||||
<h2 :title="message.subject">{{ message.subject }}</h2>
|
||||
<p class="transparency">
|
||||
<AddressList :entries="message.from" />
|
||||
to
|
||||
<!-- TODO: translate -->
|
||||
<AddressList :entries="message.cc" /><!--
|
||||
-->)
|
||||
</template>
|
||||
</p>
|
||||
<AddressList :entries="message.to" />
|
||||
<template v-if="message.cc.length">
|
||||
(cc
|
||||
<!-- TODO: translate -->
|
||||
<AddressList :entries="message.cc" /><!--
|
||||
-->)
|
||||
</template>
|
||||
</p>
|
||||
</div>
|
||||
<div id="mail-message-actions">
|
||||
<div
|
||||
:class="
|
||||
hasMultipleRecipients
|
||||
? 'icon-reply-all-white button primary'
|
||||
: 'icon-reply-white button primary'
|
||||
"
|
||||
@click="hasMultipleRecipients ? replyAll() : replyMessage()"
|
||||
>
|
||||
<span class="action-label">{{ t('mail', 'Reply') }}</span>
|
||||
</div>
|
||||
<Actions id="mail-message-actions-menu" class="app-content-list-item-menu" menu-align="right">
|
||||
<ActionButton v-if="hasMultipleRecipients" icon="icon-reply" @click="replyMessage">
|
||||
{{ t('mail', 'Reply to sender only') }}
|
||||
</ActionButton>
|
||||
<ActionButton icon="icon-forward" @click="forwardMessage">
|
||||
{{ t('mail', 'Forward') }}
|
||||
</ActionButton>
|
||||
<ActionButton icon="icon-mail" @click="onToggleSeen">
|
||||
{{ envelope.flags.unseen ? t('mail', 'Mark read') : t('mail', 'Mark unread') }}
|
||||
</ActionButton>
|
||||
<ActionButton icon="icon-delete" @click="onDelete">
|
||||
{{ t('mail', 'Delete') }}
|
||||
</ActionButton>
|
||||
</Actions>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mail-message-body">
|
||||
<MessageHTMLBody v-if="message.hasHtmlBody" :url="htmlUrl" @loaded="onHtmlBodyLoaded" />
|
||||
<MessagePlainTextBody v-else :body="message.body" :signature="message.signature" />
|
||||
<MessageAttachments :attachments="message.attachments" />
|
||||
<div id="reply-composer"></div>
|
||||
<input id="forward-button" type="button" :value="t('mail', 'Forward')" @click="forwardMessage" />
|
||||
</div>
|
||||
<Composer
|
||||
v-if="!message.hasHtmlBody || htmlBodyLoaded"
|
||||
:from-account="message.accountId"
|
||||
:to="replyRecipient.to"
|
||||
:cc="replyRecipient.cc"
|
||||
:subject="replySubject"
|
||||
:body="replyBody"
|
||||
:reply-to="replyTo"
|
||||
:send="sendReply"
|
||||
:draft="saveReplyDraft"
|
||||
/>
|
||||
</template>
|
||||
</AppContentDetails>
|
||||
</template>
|
||||
|
@ -49,10 +65,10 @@
|
|||
<script>
|
||||
import AppContentDetails from 'nextcloud-vue/dist/Components/AppContentDetails'
|
||||
import {generateUrl} from 'nextcloud-router'
|
||||
import {Actions, ActionButton} from 'nextcloud-vue'
|
||||
|
||||
import AddressList from './AddressList'
|
||||
import {buildReplyBody, buildRecipients as buildReplyRecipients, buildReplySubject} from '../ReplyBuilder'
|
||||
import Composer from './Composer'
|
||||
import Error from './Error'
|
||||
import {getRandomMessageErrorMessage} from '../util/ErrorMessageFactory'
|
||||
import {htmlToText} from '../util/HtmlHelper'
|
||||
|
@ -66,9 +82,10 @@ import {saveDraft, sendMessage} from '../service/MessageService'
|
|||
export default {
|
||||
name: 'Message',
|
||||
components: {
|
||||
ActionButton,
|
||||
Actions,
|
||||
AddressList,
|
||||
AppContentDetails,
|
||||
Composer,
|
||||
Error,
|
||||
Loading,
|
||||
MessageAttachments,
|
||||
|
@ -85,6 +102,7 @@ export default {
|
|||
replyRecipient: {},
|
||||
replySubject: '',
|
||||
replyBody: '',
|
||||
envelope: '',
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -102,6 +120,9 @@ export default {
|
|||
messageId: this.message.id,
|
||||
}
|
||||
},
|
||||
hasMultipleRecipients() {
|
||||
return this.replyRecipient.to.concat(this.replyRecipient.cc).length > 1
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
$route(to, from) {
|
||||
|
@ -148,6 +169,7 @@ export default {
|
|||
label: account.name,
|
||||
email: account.emailAddress,
|
||||
})
|
||||
|
||||
this.replySubject = buildReplySubject(message.subject)
|
||||
|
||||
if (!message.hasHtmlBody) {
|
||||
|
@ -156,13 +178,13 @@ export default {
|
|||
|
||||
this.loading = false
|
||||
|
||||
const envelope = this.$store.getters.getEnvelope(message.accountId, message.folderId, message.id)
|
||||
if (!envelope.flags.unseen) {
|
||||
this.envelope = this.$store.getters.getEnvelope(message.accountId, message.folderId, message.id)
|
||||
if (!this.envelope.flags.unseen) {
|
||||
// Already seen -> no change necessary
|
||||
return
|
||||
}
|
||||
|
||||
return this.$store.dispatch('toggleEnvelopeSeen', envelope)
|
||||
return this.$store.dispatch('toggleEnvelopeSeen', this.envelope)
|
||||
})
|
||||
.catch(error => {
|
||||
Logger.error('could not load message ', {messageUid, error})
|
||||
|
@ -193,6 +215,32 @@ export default {
|
|||
sendReply(data) {
|
||||
return sendMessage(data.account, data)
|
||||
},
|
||||
replyMessage() {
|
||||
this.$router.push({
|
||||
name: 'message',
|
||||
params: {
|
||||
accountId: this.$route.params.accountId,
|
||||
folderId: this.$route.params.folderId,
|
||||
messageUid: 'reply',
|
||||
},
|
||||
query: {
|
||||
uid: this.message.uid,
|
||||
},
|
||||
})
|
||||
},
|
||||
replyAll() {
|
||||
this.$router.push({
|
||||
name: 'message',
|
||||
params: {
|
||||
accountId: this.$route.params.accountId,
|
||||
folderId: this.$route.params.folderId,
|
||||
messageUid: 'replyAll',
|
||||
},
|
||||
query: {
|
||||
uid: this.message.uid,
|
||||
},
|
||||
})
|
||||
},
|
||||
forwardMessage() {
|
||||
this.$router.push({
|
||||
name: 'message',
|
||||
|
@ -206,22 +254,93 @@ export default {
|
|||
},
|
||||
})
|
||||
},
|
||||
onToggleSeen() {
|
||||
this.$store.dispatch('toggleEnvelopeSeen', this.envelope)
|
||||
},
|
||||
onDelete(e) {
|
||||
// Don't try to navigate to the deleted message
|
||||
e.preventDefault()
|
||||
|
||||
let envelopes = this.$store.getters.getEnvelopes(this.$route.params.accountId, this.$route.params.folderId)
|
||||
const idx = envelopes.indexOf(this.envelope)
|
||||
|
||||
let next
|
||||
if (idx === -1) {
|
||||
Logger.debug('envelope to delete does not exist in envelope list')
|
||||
return
|
||||
} else if (idx === 0) {
|
||||
next = envelopes[idx + 1]
|
||||
} else {
|
||||
next = envelopes[idx - 1]
|
||||
}
|
||||
|
||||
this.$emit('delete', this.envelope)
|
||||
this.$store.dispatch('deleteMessage', this.envelope)
|
||||
|
||||
if (!next) {
|
||||
Logger.debug('no next/previous envelope, not navigating')
|
||||
return
|
||||
}
|
||||
|
||||
// Keep the selected account-folder combination, but navigate to a different message
|
||||
// (it's not a bug that we don't use next.accountId and next.folderId here)
|
||||
this.$router.push({
|
||||
name: 'message',
|
||||
params: {
|
||||
accountId: this.$route.params.accountId,
|
||||
folderId: this.$route.params.folderId,
|
||||
messageUid: next.uid,
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
<style lang="scss">
|
||||
#mail-message {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.mail-message-body {
|
||||
margin-bottom: 100px;
|
||||
}
|
||||
|
||||
#mail-message-header h2,
|
||||
#mail-message-header p {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding-bottom: 7px;
|
||||
margin-bottom: 0;
|
||||
#mail-message-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 30px 0;
|
||||
// somehow ios doesn't care about this !important rule
|
||||
// so we have to manually set left/right padding to chidren
|
||||
// for 100% to be used
|
||||
box-sizing: content-box !important;
|
||||
height: 44px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#mail-message-header-fields {
|
||||
// initial width
|
||||
width: 0;
|
||||
padding-left: 44px;
|
||||
// grow and try to fill 100%
|
||||
flex: 1 1 auto;
|
||||
h2,
|
||||
p {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding-bottom: 7px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.transparency {
|
||||
opacity: 0.6;
|
||||
a {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#mail-content,
|
||||
|
@ -249,16 +368,43 @@ export default {
|
|||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
#mail-message-header .transparency {
|
||||
opacity: 0.6;
|
||||
#mail-message-actions {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
margin-left: 10px;
|
||||
margin-right: 35px;
|
||||
height: 44px;
|
||||
}
|
||||
|
||||
#mail-message-header .transparency a {
|
||||
font-weight: bold;
|
||||
.icon-reply-white,
|
||||
.icon-reply-all-white {
|
||||
height: 44px;
|
||||
min-width: 44px;
|
||||
margin: 0;
|
||||
padding: 11px 10px 10px 25px;
|
||||
}
|
||||
|
||||
/* Show action button label and move icon to the left
|
||||
on screens larger than 600px */
|
||||
@media only screen and (max-width: 600px) {
|
||||
.action-label {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@media only screen and (min-width: 600px) {
|
||||
.icon-reply-white,
|
||||
.icon-reply-all-white {
|
||||
background-position: 5px center;
|
||||
}
|
||||
}
|
||||
|
||||
#mail-message-actions-menu {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
@media print {
|
||||
#mail-message-header {
|
||||
#mail-message-header-fields {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
<script>
|
||||
import AppContentDetails from 'nextcloud-vue/dist/Components/AppContentDetails'
|
||||
|
||||
import {buildFowardSubject, buildReplyBody} from '../ReplyBuilder'
|
||||
import {buildForwardSubject, buildReplyBody, buildReplySubject} from '../ReplyBuilder'
|
||||
import Composer from './Composer'
|
||||
import {getRandomMessageErrorMessage} from '../util/ErrorMessageFactory'
|
||||
import Error from './Error'
|
||||
|
@ -64,12 +64,24 @@ export default {
|
|||
// Forwarded message
|
||||
|
||||
const message = this.$store.getters.getMessageByUid(this.$route.query.uid)
|
||||
Logger.debug('forwaring message', message)
|
||||
Logger.debug('forwarding or replying to message', message)
|
||||
|
||||
// message headers set for 'reply' actions by default
|
||||
let subject = buildReplySubject(message.subject)
|
||||
let msgTo = message.from
|
||||
let msgCc = []
|
||||
if (this.$route.params.messageUid === 'replyAll') {
|
||||
msgCc = message.to.concat(message.cc)
|
||||
} else if (this.$route.params.messageUid !== 'reply') {
|
||||
// forward
|
||||
subject = buildForwardSubject(message.subject)
|
||||
msgTo = []
|
||||
}
|
||||
|
||||
return {
|
||||
to: [],
|
||||
cc: [],
|
||||
subject: buildFowardSubject(message.subject),
|
||||
to: msgTo,
|
||||
cc: msgCc,
|
||||
subject: subject,
|
||||
body: buildReplyBody(message.bodyText, message.from[0], message.dateInt),
|
||||
}
|
||||
} else {
|
||||
|
|
Загрузка…
Ссылка в новой задаче