diff --git a/src/app/app.component.html b/src/app/app.component.html
index ec21e8698..c4e54aeb2 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -413,6 +413,10 @@
No Message Selected
+
+
+ {{ messageListEmptyNotice }}
+
0) {
+ if (this.canvastable.rows) {
const options = new Map();
options.set('unreadOnly', this.unreadMessagesOnlyCheckbox);
options.set('searchText', this.searchText);
this.canvastable.rows.filterBy(options);
+ this.updateMessageListEmptyNotice();
this.canvastable.hasChanges = true;
}
}
+ private updateMessageListEmptyNotice() {
+ this.messageListEmptyNotice = getEmptyMessageListNotice({
+ hasVisibleRows: this.canvastable.rows.rowCount() > 0,
+ ignoredUnreadFolders: this.messagelistservice.ignoreUnreadInFolders,
+ searchText: this.searchText,
+ selectedFolder: this.selectedFolder,
+ showingSearchResults: this.showingSearchResults,
+ showingWebSocketSearchResults: this.showingWebSocketSearchResults,
+ unreadOnly: this.unreadMessagesOnlyCheckbox,
+ });
+ }
+
public clearSelection() {
if (this.canvastable.rows) {
this.canvastable.rows.clearSelection();
diff --git a/src/app/message-list-empty-notice.spec.ts b/src/app/message-list-empty-notice.spec.ts
new file mode 100644
index 000000000..44c1de5ff
--- /dev/null
+++ b/src/app/message-list-empty-notice.spec.ts
@@ -0,0 +1,43 @@
+// --------- BEGIN RUNBOX LICENSE ---------
+// Copyright (C) 2016-2026 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 { getEmptyMessageListNotice } from './message-list-empty-notice';
+
+describe('getEmptyMessageListNotice', () => {
+ it('shows a clear notice when Unread only hides all messages in a folder', () => {
+ const notice = getEmptyMessageListNotice({
+ hasVisibleRows: false,
+ ignoredUnreadFolders: ['Sent'],
+ selectedFolder: 'Inbox',
+ unreadOnly: true,
+ });
+
+ expect(notice).toBe('No unread messages in Inbox.');
+ });
+
+ it('does not show an empty-state notice while rows are visible', () => {
+ const notice = getEmptyMessageListNotice({
+ hasVisibleRows: true,
+ selectedFolder: 'Inbox',
+ unreadOnly: true,
+ });
+
+ expect(notice).toBeNull();
+ });
+});
diff --git a/src/app/message-list-empty-notice.ts b/src/app/message-list-empty-notice.ts
new file mode 100644
index 000000000..08c4fb0ec
--- /dev/null
+++ b/src/app/message-list-empty-notice.ts
@@ -0,0 +1,48 @@
+// --------- BEGIN RUNBOX LICENSE ---------
+// Copyright (C) 2016-2026 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 ----------
+
+export interface EmptyMessageListNoticeContext {
+ hasVisibleRows: boolean;
+ ignoredUnreadFolders?: string[];
+ searchText?: string;
+ selectedFolder?: string;
+ showingSearchResults?: boolean;
+ showingWebSocketSearchResults?: boolean;
+ unreadOnly?: boolean;
+}
+
+export function getEmptyMessageListNotice(context: EmptyMessageListNoticeContext): string {
+ if (context.hasVisibleRows) {
+ return null;
+ }
+
+ const selectedFolder = context.selectedFolder || 'this folder';
+ const ignoresUnreadOnly = (context.ignoredUnreadFolders || []).includes(selectedFolder);
+ if (context.unreadOnly && !ignoresUnreadOnly) {
+ return `No unread messages in ${selectedFolder}.`;
+ }
+
+ const searchIsActive = (context.searchText || '').length >= 3
+ || context.showingWebSocketSearchResults;
+ if (searchIsActive) {
+ return 'No messages match this search.';
+ }
+
+ return `No messages in ${selectedFolder}.`;
+}