diff --git a/frontend/src/components/MessageList.jsx b/frontend/src/components/MessageList.jsx index 43ebe780..63d9c7e9 100644 --- a/frontend/src/components/MessageList.jsx +++ b/frontend/src/components/MessageList.jsx @@ -10,6 +10,7 @@ import { useSwipeRow } from '../hooks/useSwipeRow.js'; import ContextMenu from './ContextMenu.jsx'; import RowHoverActions from './RowHoverActions.jsx'; import GtdTabList from './GtdTabList.jsx'; +import GtdZeroPet from './GtdZeroPet.jsx'; import { useUiScale, descale } from '../hooks/useUiScale.js'; import { gtdActiveForContext, buildGtdDisplaySections, GTD_COLORS, GTD_CHIP_BG, sectionBadge, isSelectedRow, @@ -20,6 +21,7 @@ import { openReplyFromMessage, openForwardFromMessage } from '../utils/composeFr import SenderAvatarImage from './SenderAvatarImage.jsx'; import { shortcutBus } from '../utils/shortcutBus.js'; import { createLatestRequest } from '../utils/latestRequest.js'; +import { resolveMessageListEmptyVisual } from '../utils/messageListEmptyState.js'; import { pendingMarkReadMap, completedMarkReadMap, setPending } from '../utils/pendingReads.js'; import { applyDeleteGuard, clearDeleteGuard, clearPendingDelete, setCompletedDelete, setPendingDelete } from '../utils/pendingDeletes.js'; @@ -3798,8 +3800,15 @@ function UndoBar({ notification, onDismiss, showTopBorder }) { function EmptyState({ folderSyncing, searchQuery, unreadOnly, selectedFolder, accounts, onClearSearch, onShowAll, onCompose }) { const { t } = useTranslation(); + const visual = resolveMessageListEmptyVisual({ + folderSyncing, + searchQuery, + unreadOnly, + hasAccounts: accounts.length > 0, + selectedFolder, + }); - if (folderSyncing) { + if (visual === 'syncing') { return (
-
- {isInbox ? ( - - - - - ) : ( + {isInbox ? ( +
+ +
+ ) : ( +
- )} -
+
+ )}
{isInbox ? 'Inbox is empty' : 'Nothing here'}
diff --git a/frontend/src/utils/messageListEmptyState.js b/frontend/src/utils/messageListEmptyState.js new file mode 100644 index 00000000..ff03fb2d --- /dev/null +++ b/frontend/src/utils/messageListEmptyState.js @@ -0,0 +1,13 @@ +export function resolveMessageListEmptyVisual({ + folderSyncing, + searchQuery, + unreadOnly, + hasAccounts, + selectedFolder, +}) { + if (folderSyncing) return 'syncing'; + if (searchQuery) return 'search'; + if (unreadOnly) return 'unread'; + if (!hasAccounts) return 'no-accounts'; + return !selectedFolder || selectedFolder === 'INBOX' ? 'inbox-zero' : 'folder'; +} diff --git a/frontend/src/utils/messageListEmptyState.test.js b/frontend/src/utils/messageListEmptyState.test.js new file mode 100644 index 00000000..12f06f51 --- /dev/null +++ b/frontend/src/utils/messageListEmptyState.test.js @@ -0,0 +1,21 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { resolveMessageListEmptyVisual } from './messageListEmptyState.js'; + +test('uses the pet visual only for an unfiltered empty inbox', () => { + const base = { + folderSyncing: false, + searchQuery: '', + unreadOnly: false, + hasAccounts: true, + selectedFolder: 'INBOX', + }; + + assert.equal(resolveMessageListEmptyVisual(base), 'inbox-zero'); + assert.equal(resolveMessageListEmptyVisual({ ...base, selectedFolder: null }), 'inbox-zero'); + assert.equal(resolveMessageListEmptyVisual({ ...base, selectedFolder: 'Archive' }), 'folder'); + assert.equal(resolveMessageListEmptyVisual({ ...base, searchQuery: 'invoice' }), 'search'); + assert.equal(resolveMessageListEmptyVisual({ ...base, unreadOnly: true }), 'unread'); + assert.equal(resolveMessageListEmptyVisual({ ...base, folderSyncing: true }), 'syncing'); + assert.equal(resolveMessageListEmptyVisual({ ...base, hasAccounts: false }), 'no-accounts'); +});