mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-31 00:12:39 +00:00
feat: keep the selection while reading a message in mountain view #1231
This commit is contained in:
@@ -296,6 +296,44 @@ describe('ThreadListItem opening a thread on the phone', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// #1231: opening a message threw away the ticked rows. Mountain View on the
|
||||
// desktop keeps them, as Gmail does, so selecting can carry on after reading.
|
||||
describe('ThreadListItem keeping the selection when a message opens', () => {
|
||||
function open(interfaceLayout: 'mountain-view' | 'default', emails: Email[]) {
|
||||
useSettingsStore.setState({ interfaceLayout, mailLayout: 'split' });
|
||||
useUIStore.setState({ isMobile: false, isTablet: false, isDesktop: true });
|
||||
useEmailStore.setState({ selectedEmailKeys: new Set(['other']), selectedMailbox: 'inbox' });
|
||||
const onEmailSelect = vi.fn();
|
||||
const [thread] = groupEmailsByThread(emails);
|
||||
render(
|
||||
<ThreadListItem thread={thread} isExpanded={false} onToggleExpand={() => {}} onEmailSelect={onEmailSelect} />,
|
||||
);
|
||||
fireEvent.click(screen.getByText('Test Subject'));
|
||||
return onEmailSelect;
|
||||
}
|
||||
const conversation = () => [
|
||||
makeEmail({ id: 'older', receivedAt: '2024-01-14T10:00:00Z' }),
|
||||
makeEmail({ id: 'newer', receivedAt: '2024-01-15T10:00:00Z' }),
|
||||
];
|
||||
|
||||
it('keeps the ticked rows when a Mountain View message opens', () => {
|
||||
const onEmailSelect = open('mountain-view', [makeEmail()]);
|
||||
expect(onEmailSelect).toHaveBeenCalledTimes(1);
|
||||
expect([...useEmailStore.getState().selectedEmailKeys]).toEqual(['other']);
|
||||
});
|
||||
|
||||
it('keeps them when a Mountain View conversation opens', () => {
|
||||
const onEmailSelect = open('mountain-view', conversation());
|
||||
expect(onEmailSelect).toHaveBeenCalledWith(expect.objectContaining({ id: 'newer' }));
|
||||
expect([...useEmailStore.getState().selectedEmailKeys]).toEqual(['other']);
|
||||
});
|
||||
|
||||
it('still clears them in the default layout', () => {
|
||||
open('default', [makeEmail()]);
|
||||
expect(useEmailStore.getState().selectedEmailKeys.size).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ThreadListItem Mountain View phone row layout', () => {
|
||||
it('sets the star at the end of the preview line, not under the time', () => {
|
||||
useSettingsStore.setState({ interfaceLayout: 'mountain-view', mailLayout: 'split', showPreview: true, density: 'regular' } as never);
|
||||
|
||||
@@ -438,7 +438,9 @@ const SingleEmailItem = React.forwardRef<HTMLDivElement, SingleEmailItemProps>(
|
||||
e.preventDefault();
|
||||
selectRangeEmails(email);
|
||||
} else {
|
||||
if (selectedEmailKeys.size > 0) clearSelection();
|
||||
// Mountain View keeps the ticked rows while a message is read, as
|
||||
// Gmail does, so selecting can carry on afterwards. (#1231)
|
||||
if (selectedEmailKeys.size > 0 && !mvRow) clearSelection();
|
||||
onClick();
|
||||
}
|
||||
};
|
||||
@@ -971,7 +973,8 @@ export const ThreadListItem = React.forwardRef<HTMLDivElement, ThreadListItemPro
|
||||
}
|
||||
|
||||
if (mvRow || mvMobileRow) {
|
||||
if (selectedEmailKeys.size > 0) clearSelection();
|
||||
// Desktop keeps the ticked rows while the conversation is read (#1231).
|
||||
if (selectedEmailKeys.size > 0 && !mvRow) clearSelection();
|
||||
onEmailSelect(latestEmail);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -804,7 +804,7 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
|
||||
},
|
||||
onArchive: async () => {
|
||||
if (isScheduledView) return;
|
||||
if (selectedEmailKeys.size > 0 && client) {
|
||||
if (shortcutsTargetSelection && client) {
|
||||
const count = selectedEmailKeys.size;
|
||||
await runBatchEmailAction(() => batchArchive(client), {
|
||||
success: t('notifications.emails_archived', { count }),
|
||||
@@ -819,7 +819,7 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
|
||||
},
|
||||
onDelete: async () => {
|
||||
if (isScheduledView) return;
|
||||
if (selectedEmailKeys.size > 0 && client) {
|
||||
if (shortcutsTargetSelection && client) {
|
||||
const currentMailbox = mailboxes.find(m => m.id === selectedMailbox);
|
||||
const permanent = bulkDeleteIsPermanent(currentMailbox);
|
||||
if (shouldConfirmBulkDelete(currentMailbox)) {
|
||||
@@ -849,7 +849,7 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
|
||||
onMarkAsUnread: async () => {
|
||||
if (isScheduledView) return;
|
||||
if (!client) return;
|
||||
if (selectedEmailKeys.size > 0) {
|
||||
if (shortcutsTargetSelection) {
|
||||
await batchMarkAsRead(client, false);
|
||||
} else if (selectedEmail) {
|
||||
await markAsRead(client, selectedEmail.id, false);
|
||||
@@ -858,7 +858,7 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
|
||||
onMarkAsRead: async () => {
|
||||
if (isScheduledView) return;
|
||||
if (!client) return;
|
||||
if (selectedEmailKeys.size > 0) {
|
||||
if (shortcutsTargetSelection) {
|
||||
await batchMarkAsRead(client, true);
|
||||
} else if (selectedEmail) {
|
||||
await markAsRead(client, selectedEmail.id, true);
|
||||
@@ -872,7 +872,7 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
|
||||
// is a no-op there too.
|
||||
if (['sent', 'drafts', 'scheduled'].includes(currentMailbox?.role || '')) return;
|
||||
const isInJunk = currentMailbox?.role === 'junk';
|
||||
if (selectedEmailKeys.size > 0 && client) {
|
||||
if (shortcutsTargetSelection && client) {
|
||||
// The spam actions take bare ids of the viewed account: resolve the
|
||||
// selection keys through the loaded rows and thread members.
|
||||
const ids = resolveSelectedEmails(useEmailStore.getState()).map(e => e.id);
|
||||
@@ -981,6 +981,10 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
|
||||
}), [activeEmails, selectedEmail, client, selectedMailbox, isMobile, isTablet, selectedEmailKeys, mailboxes, isScheduledView, mvShell, showCrossStarred]);
|
||||
|
||||
// Initialize keyboard shortcuts
|
||||
// Mountain View keeps the ticked rows while a message is read (#1231). The
|
||||
// shortcuts then act on the message on screen, as its toolbar does, and
|
||||
// reach the selection again back in the list.
|
||||
const shortcutsTargetSelection = selectedEmailKeys.size > 0 && !(mvShell && !cursorOnly && selectedEmail);
|
||||
useKeyboardShortcuts({
|
||||
enabled: isAuthenticated && !showComposer,
|
||||
emails: activeEmails,
|
||||
|
||||
Reference in New Issue
Block a user