diff --git a/src/app/app.component.html b/src/app/app.component.html
index ec21e8698..adc6fbdbc 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -347,6 +347,17 @@
No Message Selected
Inline previews
+
+
+
column.cacheKey === 'subject');
+ const defaultSelectedColumn = subjectColumnIndex > -1 ? subjectColumnIndex : 3;
+ this.canvastable.rowWrapModeWrapColumn = defaultSelectedColumn;
+ this.canvastable.rowWrapModeDefaultSelectedColumn = defaultSelectedColumn;
this.autoAdjustColumnWidths();
}
diff --git a/src/app/common/messagelist.spec.ts b/src/app/common/messagelist.spec.ts
new file mode 100644
index 000000000..9b0c8d2f8
--- /dev/null
+++ b/src/app/common/messagelist.spec.ts
@@ -0,0 +1,73 @@
+// --------- 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 ----------
+
+import { MailAddressInfo } from './mailaddressinfo';
+import { MessageList } from './messagelist';
+
+describe('MessageList', () => {
+ const makeMessage = (to: MailAddressInfo[], cc: MailAddressInfo[] = []) => ({
+ id: 1,
+ messageDate: new Date('2026-01-01T12:00:00Z'),
+ from: [new MailAddressInfo('Sender', 'sender@example.com')],
+ to,
+ cc,
+ subject: 'Subject',
+ plaintext: '',
+ size: 100,
+ attachment: false,
+ answeredFlag: false,
+ flaggedFlag: false
+ });
+
+ it('adds a recipient address column when enabled for incoming folders', () => {
+ const rows = new MessageList([
+ makeMessage([
+ new MailAddressInfo('Alias', 'alias+shopping@runbox.com')
+ ], [
+ new MailAddressInfo('Copy', 'copy@example.com')
+ ])
+ ]);
+
+ const columns = rows.getCanvasTableColumns({
+ selectedFolder: 'Inbox',
+ displayRecipientColumn: true
+ });
+ const recipientColumn = columns.find((column) => column.cacheKey === 'recipient');
+
+ expect(recipientColumn).toBeTruthy();
+ expect(columns.findIndex((column) => column.cacheKey === 'recipient')).toBeLessThan(
+ columns.findIndex((column) => column.cacheKey === 'subject')
+ );
+ expect(recipientColumn.getValue(0)).toBe('alias+shopping@runbox.com, copy@example.com');
+ });
+
+ it('does not add a duplicate recipient column for Sent folders', () => {
+ const rows = new MessageList([
+ makeMessage([new MailAddressInfo('Recipient', 'recipient@example.com')])
+ ]);
+
+ const columns = rows.getCanvasTableColumns({
+ selectedFolder: 'Sent',
+ displayRecipientColumn: true
+ });
+
+ expect(columns.find((column) => column.cacheKey === 'recipient')).toBeUndefined();
+ expect(columns.find((column) => column.cacheKey === 'from').name).toBe('To');
+ });
+});
diff --git a/src/app/common/messagelist.ts b/src/app/common/messagelist.ts
index dd7e50249..3f7e1e02d 100644
--- a/src/app/common/messagelist.ts
+++ b/src/app/common/messagelist.ts
@@ -19,6 +19,7 @@
import { MessageDisplay } from '../common/messagedisplay';
import { MessageInfo } from './messageinfo';
+import { MailAddressInfo } from './mailaddressinfo';
import { MessageTableRowTool} from '../messagetable/messagetablerow';
import { CanvasTableColumn } from '../canvastable/canvastablecolumn';
@@ -62,6 +63,22 @@ export class MessageList extends MessageDisplay {
'';
}
+ getRecipientColumnValueForRow(rowIndex: number): string {
+ const rowobj = this.rows[rowIndex];
+ return this.formatRecipientAddresses([].concat(rowobj.to || [], rowobj.cc || []));
+ }
+
+ private formatRecipientAddresses(recipients: MailAddressInfo[]): string {
+ return recipients
+ .map((mailAddr) => mailAddr.address || mailAddr.nameAndAddress || '')
+ .filter((address) => address.length > 0)
+ .join(', ');
+ }
+
+ private shouldShowRecipientColumn(app: { displayRecipientColumn: boolean; selectedFolder: string }): boolean {
+ return app.displayRecipientColumn && app.selectedFolder.indexOf('Sent') !== 0;
+ }
+
// filter visible rows by whatever options the frontend has
filterBy(options: Map) {
this.rows = this._rows;
@@ -155,6 +172,18 @@ export class MessageList extends MessageDisplay {
}
];
+ if (this.shouldShowRecipientColumn(app)) {
+ columns.splice(3, 0, {
+ name: 'Recipient',
+ cacheKey: 'recipient',
+ sortColumn: null,
+ rowWrapModeHidden: true,
+ getValue: (rowIndex: number): string => this.getRecipientColumnValueForRow(rowIndex),
+ draggable: true,
+ width: 220
+ });
+ }
+
return columns;
}
}
diff --git a/src/app/xapian/searchmessagedisplay.spec.ts b/src/app/xapian/searchmessagedisplay.spec.ts
new file mode 100644
index 000000000..cfd5b6bc2
--- /dev/null
+++ b/src/app/xapian/searchmessagedisplay.spec.ts
@@ -0,0 +1,62 @@
+// --------- 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 ----------
+
+import { SearchMessageDisplay } from './searchmessagedisplay';
+
+describe('SearchMessageDisplay', () => {
+ const makeDisplay = () => new SearchMessageDisplay({
+ getDocData: () => ({
+ from: 'Sender',
+ subject: 'Subject',
+ recipients: ['alias@runbox.com', 'alias+tag@runbox.com'],
+ textcontent: '',
+ attachment: false,
+ answered: false,
+ flagged: false
+ })
+ }, [[42]]);
+
+ it('adds a recipient address column for indexed incoming results', () => {
+ const columns = makeDisplay().getCanvasTableColumns({
+ selectedFolder: 'Inbox',
+ displayFolderColumn: false,
+ displayRecipientColumn: true,
+ viewmode: 'messages'
+ });
+ const recipientColumn = columns.find((column) => column.cacheKey === 'recipient');
+
+ expect(recipientColumn).toBeTruthy();
+ expect(columns.findIndex((column) => column.cacheKey === 'recipient')).toBeLessThan(
+ columns.findIndex((column) => column.cacheKey === 'subject')
+ );
+ expect(recipientColumn.getValue(0)).toBe('alias@runbox.com, alias+tag@runbox.com');
+ });
+
+ it('keeps Sent folder results on the existing To column when not showing all folders', () => {
+ const columns = makeDisplay().getCanvasTableColumns({
+ selectedFolder: 'Sent',
+ displayFolderColumn: false,
+ displayRecipientColumn: true,
+ viewmode: 'messages'
+ });
+
+ expect(columns.find((column) => column.cacheKey === 'recipient')).toBeUndefined();
+ expect(columns.find((column) => column.cacheKey === 'from').name).toBe('To');
+ });
+});
diff --git a/src/app/xapian/searchmessagedisplay.ts b/src/app/xapian/searchmessagedisplay.ts
index 8bd686cb7..021cddc3a 100644
--- a/src/app/xapian/searchmessagedisplay.ts
+++ b/src/app/xapian/searchmessagedisplay.ts
@@ -54,6 +54,17 @@ export class SearchMessageDisplay extends MessageDisplay {
filterBy(options: Map) {
}
+ getRecipientColumnValueForRow(rowIndex: number): string {
+ return this.searchService.getDocData(this.getRowId(rowIndex)).recipients.join(', ');
+ }
+
+ private shouldShowRecipientColumn(
+ app: { displayRecipientColumn: boolean; displayFolderColumn: boolean; selectedFolder: string }
+ ): boolean {
+ return app.displayRecipientColumn
+ && (app.displayFolderColumn || app.selectedFolder.indexOf('Sent') !== 0);
+ }
+
// columns
// app is a Component (currently)
public getCanvasTableColumns(app: any): CanvasTableColumn[] {
@@ -107,6 +118,18 @@ export class SearchMessageDisplay extends MessageDisplay {
}
];
+ if (this.shouldShowRecipientColumn(app)) {
+ columns.splice(3, 0, {
+ name: 'Recipient',
+ draggable: true,
+ cacheKey: 'recipient',
+ sortColumn: null,
+ rowWrapModeHidden: true,
+ getValue: (rowIndex): string => this.getRecipientColumnValueForRow(rowIndex),
+ width: 220
+ });
+ }
+
if (app.viewmode === 'conversations') {
// Array containing row (conversation) objects waiting to be counted
let currentCountObject = null;