Say a missing folder is missing, not empty
A folder id the account does not have rendered the ordinary empty state -- "Nothing here. This folder is empty." That is a claim about a folder that is not there, so a stale link read as a folder that had emptied itself rather than one that was gone (#111). It now goes to the inbox and says why. Inbox is the kinder landing than a dead end for a bookmark that has outlived its folder, but swapping one folder for another without a word would be its own small lie, so it does not do that either. The condition worth writing a test around is not the unknown id, it is the one guarding it. The folder list arrives after the first paint, so for a moment *every* id is unknown, the right one included. Without that gate this redirects on every cold load, from the folder the reader actually asked for, and looks exactly like a flaky link -- a worse bug than the one being fixed and a harder one to see. `isUnknownMailbox` is a small pure function so that case can be pinned down rather than reasoned about. Only ever reachable from outside the app, which is why it went unnoticed: the sidebar links to ids that exist. A bookmark to a deleted folder, or a folder link passed between accounts, is where it bites. Verified against the mock: an unknown id lands on the inbox with the message and a full list rather than an empty one, and a cold load straight into a real folder stays in that folder with nothing said. Closes #111.
This commit is contained in:
@@ -14,6 +14,7 @@ import { LabelPicker } from "./LabelPicker";
|
||||
import type { Id } from "@/jmap/types";
|
||||
import { confirmDialog } from "@/ui/dialog";
|
||||
import { toast } from "@/ui/toast";
|
||||
import { isUnknownMailbox } from "@/lib/mailboxRoute";
|
||||
import { scheduledMailboxIdFrom, useScheduled } from "@/store/scheduled";
|
||||
|
||||
export function MailView({ mailboxId, threadId, search }: { mailboxId?: string; threadId?: string; search?: boolean }) {
|
||||
@@ -39,6 +40,26 @@ export function MailView({ mailboxId, threadId, search }: { mailboxId?: string;
|
||||
if (!search && !mailboxId && inboxId) navigate(`/mail/${inboxId}`, { replace: true });
|
||||
}, [search, mailboxId, inboxId, navigate]);
|
||||
|
||||
/*
|
||||
* A folder id this account does not have.
|
||||
*
|
||||
* It used to render the ordinary empty state -- "Nothing here. This folder is
|
||||
* empty." -- which is a claim about a folder that is not there, so a stale
|
||||
* link read as a folder that had emptied itself rather than one that was
|
||||
* gone (#111). Only reachable from outside the app: the sidebar links to ids
|
||||
* that exist.
|
||||
*
|
||||
* Inbox is the kinder landing than a dead end, but silently swapping one
|
||||
* folder for another would be its own small lie, so it says what happened.
|
||||
* `mailboxesLoaded` gates it: without that, every cold load redirects in the
|
||||
* moment before the folder list arrives.
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (!isUnknownMailbox({ mailboxId, mailboxes, loaded: mailboxesLoaded, search }) || !inboxId) return;
|
||||
toast.show("That folder no longer exists. Showing your inbox instead.");
|
||||
navigate(`/mail/${inboxId}`, { replace: true });
|
||||
}, [search, mailboxId, mailboxesLoaded, mailboxes, inboxId, navigate]);
|
||||
|
||||
// Build & run the list query
|
||||
const listQuery = useMemo<ListQuery | null>(() => {
|
||||
if (search) {
|
||||
|
||||
Reference in New Issue
Block a user