From a5c06b5c7a96218ccc2b03e3fe5b56b8a51f982a Mon Sep 17 00:00:00 2001 From: Bas Date: Thu, 24 Oct 2024 16:10:11 +0300 Subject: [PATCH 01/19] feat(templates): Create draft on template item click --- src/app/app.component.ts | 9 +++++++++ src/app/compose/draftdesk.service.ts | 23 ++++++++++++++++++++++- src/app/rmmapi/messagelist.service.ts | 1 + 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 99c9ddf12..d64d991b1 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -919,6 +919,15 @@ export class AppComponent implements OnInit, AfterViewInit, CanvasTableSelectLis this.canvastable.rows.rowSelected(rowIndex, columnIndex, multiSelect); this.showSelectOperations = this.canvastable.rows.anySelected(); + if (this.selectedFolder === this.messagelistservice.templateFolderName) { + this.draftDeskService.newTemplateDraft( + this.canvastable.rows.getRowMessageId(rowIndex) + ) + this.drafts() + return + } + + if (this.canvastable.rows.hasChanges) { this.updateUrlFragment(this.canvastable.rows.getRowMessageId(rowIndex)); this.singlemailviewer.messageId = this.canvastable.rows.getRowMessageId(rowIndex); diff --git a/src/app/compose/draftdesk.service.ts b/src/app/compose/draftdesk.service.ts index d0bcede21..a6a831b8f 100644 --- a/src/app/compose/draftdesk.service.ts +++ b/src/app/compose/draftdesk.service.ts @@ -26,7 +26,7 @@ import { MailAddressInfo } from '../common/mailaddressinfo'; import { MessageListService } from '../rmmapi/messagelist.service'; import { MessageTableRowTool} from '../messagetable/messagetablerow'; import { Identity, ProfileService } from '../profiles/profile.service'; -import { from, of, BehaviorSubject } from 'rxjs'; +import { from, of, BehaviorSubject, forkJoin } from 'rxjs'; import { map, mergeMap, bufferCount, take, distinctUntilChanged } from 'rxjs/operators'; import moment from 'moment'; @@ -309,6 +309,27 @@ export class DraftDeskService { this.draftModels.next(models); } + public async newTemplateDraft( + messageId: number, + ) { + forkJoin([ + this.rmmapi.getMessageFields(messageId), + this.rmmapi.getCachedMessageContents(messageId) + ]).subscribe(([fields, contents]) => { + const draftFormModel = DraftFormModel.create( + -1, + this.mainIdentity(), + fields.from, + fields.subject + ) + + draftFormModel.msg_body = contents.text.text; + draftFormModel.html = contents.text.html; + + return this.newDraft(draftFormModel); + }) + } + public async newBugReport( local_search: boolean, keep_pane: boolean, diff --git a/src/app/rmmapi/messagelist.service.ts b/src/app/rmmapi/messagelist.service.ts index 75f8f589a..8a29406ce 100644 --- a/src/app/rmmapi/messagelist.service.ts +++ b/src/app/rmmapi/messagelist.service.ts @@ -62,6 +62,7 @@ export class MessageListService { trashFolderName = 'Trash'; spamFolderName = 'Spam'; + templateFolderName = 'Templates'; ignoreUnreadInFolders = [ 'Sent' ]; From 1778de137fadbff49c93d48b9a47e26f54f8ca20 Mon Sep 17 00:00:00 2001 From: Bas Date: Tue, 29 Oct 2024 17:09:17 +0200 Subject: [PATCH 02/19] fixup! feat(templates): Create draft on template item click --- src/app/app.component.ts | 8 +++++--- src/app/compose/draftdesk.service.ts | 4 +++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index d64d991b1..edb1fb8d4 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -916,10 +916,9 @@ export class AppComponent implements OnInit, AfterViewInit, CanvasTableSelectLis } public rowSelected(rowIndex: number, columnIndex: number, multiSelect?: boolean) { - this.canvastable.rows.rowSelected(rowIndex, columnIndex, multiSelect); - this.showSelectOperations = this.canvastable.rows.anySelected(); + const isSelect = (columnIndex === 0) || multiSelect - if (this.selectedFolder === this.messagelistservice.templateFolderName) { + if ((this.selectedFolder === this.messagelistservice.templateFolderName) && !isSelect) { this.draftDeskService.newTemplateDraft( this.canvastable.rows.getRowMessageId(rowIndex) ) @@ -927,6 +926,9 @@ export class AppComponent implements OnInit, AfterViewInit, CanvasTableSelectLis return } + this.canvastable.rows.rowSelected(rowIndex, columnIndex, multiSelect); + this.showSelectOperations = this.canvastable.rows.anySelected(); + if (this.canvastable.rows.hasChanges) { this.updateUrlFragment(this.canvastable.rows.getRowMessageId(rowIndex)); diff --git a/src/app/compose/draftdesk.service.ts b/src/app/compose/draftdesk.service.ts index a6a831b8f..b56b6645d 100644 --- a/src/app/compose/draftdesk.service.ts +++ b/src/app/compose/draftdesk.service.ts @@ -240,6 +240,7 @@ export class DraftDeskService { constructor(public rmmapi: RunboxWebmailAPI, private messagelistservice: MessageListService, + private rbwebmailapi: RunboxWebmailAPI, private profileService: ProfileService, private http: HttpClient ) { @@ -312,9 +313,10 @@ export class DraftDeskService { public async newTemplateDraft( messageId: number, ) { + forkJoin([ this.rmmapi.getMessageFields(messageId), - this.rmmapi.getCachedMessageContents(messageId) + this.rbwebmailapi.getMessageContents(messageId) ]).subscribe(([fields, contents]) => { const draftFormModel = DraftFormModel.create( -1, From 77822af1af1893e145d72d9499107756a7d5feb8 Mon Sep 17 00:00:00 2001 From: Bas Date: Tue, 29 Oct 2024 17:10:48 +0200 Subject: [PATCH 03/19] feat(templates): Save draft as template --- src/app/compose/compose.component.html | 35 ++++++++++++++------------ 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/app/compose/compose.component.html b/src/app/compose/compose.component.html index a4ad062fc..5650272b3 100644 --- a/src/app/compose/compose.component.html +++ b/src/app/compose/compose.component.html @@ -24,13 +24,16 @@ + + - - + + @@ -42,7 +45,7 @@ {{t.nameAndAddress}} - +
- +
-
- +
+ - +

Drop files here

@@ -127,11 +130,11 @@

Drop files here

-
+ {{this.model.preview}} - +
+ +
From 5912fb8cf7b214fafeafcd81403b744086ad1b75 Mon Sep 17 00:00:00 2001 From: Bas Date: Tue, 29 Oct 2024 17:12:37 +0200 Subject: [PATCH 04/19] fixup! feat(templates): Save draft as template --- src/app/compose/compose.component.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/app/compose/compose.component.ts b/src/app/compose/compose.component.ts index c0a5d4c94..b76291493 100644 --- a/src/app/compose/compose.component.ts +++ b/src/app/compose/compose.component.ts @@ -77,6 +77,7 @@ export class ComposeComponent implements AfterViewInit, OnDestroy, OnInit { public uploadRequest: Subscription = null; public saved: Date = null; public tinymce_plugin: TinyMCEPlugin; + public isTemplate: boolean = false; finishImageUpload: AsyncSubject = null; uploadProgress: BehaviorSubject = new BehaviorSubject(-1); has_pasted_signature: boolean; @@ -673,6 +674,7 @@ export class ComposeComponent implements AfterViewInit, OnDestroy, OnInit { } public submit(send: boolean = false) { + const { isTemplate } = this; if (this.savingInProgress) { return; } @@ -780,7 +782,7 @@ export class ComposeComponent implements AfterViewInit, OnDestroy, OnInit { } else { this.rmmapi.me.pipe(mergeMap((me) => { return this.http.post('/rest/v1/draft', { - type: 'draft', + type: isTemplate ? 'template' : 'draft', username: me.username, from: from && from.id ? from.from_name + '%' + from.email + '%' + from.id : from ? from.email : undefined, from_email: from ? from.email : '', @@ -796,6 +798,7 @@ export class ComposeComponent implements AfterViewInit, OnDestroy, OnInit { ctype: this.model.useHTML ? 'html' : null, save: send ? 'Send' : 'Save', mid: this.model.mid, + ...(isTemplate ? {tid: this.model.mid} : {}), attachments: this.model.attachments ? this.model.attachments .filter((att) => att.file !== 'UTF-8Q') @@ -839,6 +842,11 @@ export class ComposeComponent implements AfterViewInit, OnDestroy, OnInit { } } + public saveTemplate() { + this.isTemplate = true + this.submit(false); + } + ngOnDestroy() { if (this.editor) { this.tinymce_plugin.remove(this.editor); From e305d8492026acb413a9f9fe15adb1ebf5fd3662 Mon Sep 17 00:00:00 2001 From: Bas Date: Tue, 29 Oct 2024 17:50:23 +0200 Subject: [PATCH 05/19] fixup! fixup! feat(templates): Create draft on template item click --- src/app/compose/draftdesk.service.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/app/compose/draftdesk.service.ts b/src/app/compose/draftdesk.service.ts index b56b6645d..965890622 100644 --- a/src/app/compose/draftdesk.service.ts +++ b/src/app/compose/draftdesk.service.ts @@ -26,7 +26,7 @@ import { MailAddressInfo } from '../common/mailaddressinfo'; import { MessageListService } from '../rmmapi/messagelist.service'; import { MessageTableRowTool} from '../messagetable/messagetablerow'; import { Identity, ProfileService } from '../profiles/profile.service'; -import { from, of, BehaviorSubject, forkJoin } from 'rxjs'; +import { from, of, BehaviorSubject } from 'rxjs'; import { map, mergeMap, bufferCount, take, distinctUntilChanged } from 'rxjs/operators'; import moment from 'moment'; @@ -314,15 +314,17 @@ export class DraftDeskService { messageId: number, ) { - forkJoin([ - this.rmmapi.getMessageFields(messageId), - this.rbwebmailapi.getMessageContents(messageId) - ]).subscribe(([fields, contents]) => { + this.rbwebmailapi.getMessageContents(messageId).subscribe((contents) => { + const res: any = Object.assign({}, contents); + + const {to: {value: [{name, address}]}, subject} = res.headers + const to = new MailAddressInfo(name, address).nameAndAddress; + const draftFormModel = DraftFormModel.create( -1, this.mainIdentity(), - fields.from, - fields.subject + to, + subject ) draftFormModel.msg_body = contents.text.text; From 20177bb0462c543c71c5ffb107f4766c747b1c8f Mon Sep 17 00:00:00 2001 From: Bas Date: Wed, 30 Oct 2024 12:22:11 +0200 Subject: [PATCH 06/19] fixup! fixup! feat(templates): Create draft on template item click --- src/app/app.component.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index edb1fb8d4..aa9c1e2b6 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,18 +1,18 @@ // --------- BEGIN RUNBOX LICENSE --------- // Copyright (C) 2016-2022 Runbox Solutions AS (runbox.com). -// +// // This file is part of Runbox 7. -// +// // Runbox 7 is free software: You can redistribute it and/or modify it // under the terms of the GNU General Public License as published by the // Free Software Foundation, either version 3 of the License, or (at your // option) any later version. -// +// // Runbox 7 is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with Runbox 7. If not, see . // ---------- END RUNBOX LICENSE ---------- @@ -74,7 +74,7 @@ const LOCAL_STORAGE_SHOW_UNREAD_ONLY = 'rmm7mailViewerShowUnreadOnly'; const LOCAL_STORAGE_SHOW_POPULAR_RECIPIENTS = 'showPopularRecipients'; const LOCAL_STORAGE_INDEX_PROMPT = 'localSearchPromptDisplayed'; const TOOLBAR_LIST_BUTTON_WIDTH = 30; - + @Component({ moduleId: 'angular2/app/', // eslint-disable-next-line @angular-eslint/component-selector @@ -718,7 +718,7 @@ export class AppComponent implements OnInit, AfterViewInit, CanvasTableSelectLis ) - 1; } } - + public openMarkOpMenu() { this.showSelectMarkOpMenu = true; @@ -929,7 +929,6 @@ export class AppComponent implements OnInit, AfterViewInit, CanvasTableSelectLis this.canvastable.rows.rowSelected(rowIndex, columnIndex, multiSelect); this.showSelectOperations = this.canvastable.rows.anySelected(); - if (this.canvastable.rows.hasChanges) { this.updateUrlFragment(this.canvastable.rows.getRowMessageId(rowIndex)); this.singlemailviewer.messageId = this.canvastable.rows.getRowMessageId(rowIndex); From eb577e614727992191c979f10838222b803d810e Mon Sep 17 00:00:00 2001 From: Bas Date: Wed, 30 Oct 2024 12:28:19 +0200 Subject: [PATCH 07/19] fixup! fixup! feat(templates): Save draft as template --- src/app/compose/compose.component.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/app/compose/compose.component.ts b/src/app/compose/compose.component.ts index b76291493..9affe5db0 100644 --- a/src/app/compose/compose.component.ts +++ b/src/app/compose/compose.component.ts @@ -1,18 +1,18 @@ // --------- BEGIN RUNBOX LICENSE --------- // Copyright (C) 2016-2022 Runbox Solutions AS (runbox.com). -// +// // This file is part of Runbox 7. -// +// // Runbox 7 is free software: You can redistribute it and/or modify it // under the terms of the GNU General Public License as published by the // Free Software Foundation, either version 3 of the License, or (at your // option) any later version. -// +// // Runbox 7 is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with Runbox 7. If not, see . // ---------- END RUNBOX LICENSE ---------- @@ -675,10 +675,13 @@ export class ComposeComponent implements AfterViewInit, OnDestroy, OnInit { public submit(send: boolean = false) { const { isTemplate } = this; + if (this.savingInProgress) { return; } + this.savingInProgress = true; + if (send) { let validemails = false; validemails = isValidEmailArray(this.model.to); From 29471c80e35ae8bfd2eed3517a73805904fcc216 Mon Sep 17 00:00:00 2001 From: Bas Date: Wed, 30 Oct 2024 12:29:08 +0200 Subject: [PATCH 08/19] fixup! fixup! fixup! feat(templates): Create draft on template item click --- src/app/compose/draftdesk.service.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/app/compose/draftdesk.service.ts b/src/app/compose/draftdesk.service.ts index 965890622..4aeda173d 100644 --- a/src/app/compose/draftdesk.service.ts +++ b/src/app/compose/draftdesk.service.ts @@ -1,18 +1,18 @@ // --------- BEGIN RUNBOX LICENSE --------- // Copyright (C) 2016-2022 Runbox Solutions AS (runbox.com). -// +// // This file is part of Runbox 7. -// +// // Runbox 7 is free software: You can redistribute it and/or modify it // under the terms of the GNU General Public License as published by the // Free Software Foundation, either version 3 of the License, or (at your // option) any later version. -// +// // Runbox 7 is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with Runbox 7. If not, see . // ---------- END RUNBOX LICENSE ---------- @@ -131,7 +131,7 @@ export class DraftFormModel { const localTZ = moment.tz.guess(); const replyHeaderHTML = 'On ' + moment(mailObj.date, localTZ).format('yyyy-MM-DD HH:mm Z') - + ' ' + moment.tz(localTZ).format('z') + + ' ' + moment.tz(localTZ).format('z') + ', ' + (mailObj.from[0].name ? `"${mailObj.from[0].name}" <${mailObj.from[0].address}> wrote:` @@ -316,10 +316,8 @@ export class DraftDeskService { this.rbwebmailapi.getMessageContents(messageId).subscribe((contents) => { const res: any = Object.assign({}, contents); - const {to: {value: [{name, address}]}, subject} = res.headers const to = new MailAddressInfo(name, address).nameAndAddress; - const draftFormModel = DraftFormModel.create( -1, this.mainIdentity(), From b349ea9d6cc1582fa14fe3ac1fa5b960d21f3c89 Mon Sep 17 00:00:00 2001 From: Bas Date: Wed, 30 Oct 2024 12:31:39 +0200 Subject: [PATCH 09/19] fixup! fixup! fixup! feat(templates): Create draft on template item click --- src/app/app.component.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index aa9c1e2b6..7473ac6f6 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -921,9 +921,10 @@ export class AppComponent implements OnInit, AfterViewInit, CanvasTableSelectLis if ((this.selectedFolder === this.messagelistservice.templateFolderName) && !isSelect) { this.draftDeskService.newTemplateDraft( this.canvastable.rows.getRowMessageId(rowIndex) - ) - this.drafts() - return + ); + this.drafts(); + + return; } this.canvastable.rows.rowSelected(rowIndex, columnIndex, multiSelect); From 72d68cc9f070bb598ae05d1444c7e3659f314ac9 Mon Sep 17 00:00:00 2001 From: Bas Date: Thu, 31 Oct 2024 16:13:49 +0200 Subject: [PATCH 10/19] fixup! fixup! fixup! fixup! feat(templates): Create draft on template item click --- src/app/compose/draftdesk.service.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/app/compose/draftdesk.service.ts b/src/app/compose/draftdesk.service.ts index 4aeda173d..678c3b2d4 100644 --- a/src/app/compose/draftdesk.service.ts +++ b/src/app/compose/draftdesk.service.ts @@ -240,7 +240,6 @@ export class DraftDeskService { constructor(public rmmapi: RunboxWebmailAPI, private messagelistservice: MessageListService, - private rbwebmailapi: RunboxWebmailAPI, private profileService: ProfileService, private http: HttpClient ) { @@ -314,7 +313,7 @@ export class DraftDeskService { messageId: number, ) { - this.rbwebmailapi.getMessageContents(messageId).subscribe((contents) => { + this.rmmapi.getMessageContents(messageId).subscribe((contents) => { const res: any = Object.assign({}, contents); const {to: {value: [{name, address}]}, subject} = res.headers const to = new MailAddressInfo(name, address).nameAndAddress; From 6a24e984a5c246dee14e1e8df912af3204da6e2f Mon Sep 17 00:00:00 2001 From: Bas Date: Thu, 31 Oct 2024 16:16:12 +0200 Subject: [PATCH 11/19] fixup! feat(templates): Save draft as template --- src/app/compose/compose.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/compose/compose.component.html b/src/app/compose/compose.component.html index 5650272b3..ff3ac319a 100644 --- a/src/app/compose/compose.component.html +++ b/src/app/compose/compose.component.html @@ -25,7 +25,7 @@ - -