A mailbox is the hardest thing this library gets asked to do, because half the zones are folders β clients, finance, team β and the other half are verbs: reply, spam, delete. The gesture is the same; what happens next is not, and that difference lives entirely in the host. Read the message, then file it or act on it β by dragging, by pressing a letter, or by simply clicking the folder.
onSort receives the message and the zone; everything after that is the host's business. Five of
these zones move a message, three do something to it, and one of the three opens the compose sheet and refuses
the filing until it is sent β the deck accepts that, because a rejected onSort brings the card
home.
onSort: async (mail, zone) => {
if (zone.id === 'reply') {
const sent = await compose(mail); // opens the sheet, resolves on send
if (!sent) throw new Error('cancelled'); // β¦and the card comes back to the pile
return;
}
if (zone.id === 'trash') return api.trash(mail.id);
if (zone.id === 'spam') return api.spam(mail.id); // and it teaches the filter
return api.move(mail.id, zone.id);
}
A click or a tap on a folder files the message straight away β no drag. That is the default everywhere
(tapZones), because dragging is expressive and slow, and when the zones are on screen and within
reach, pointing at the answer is simply faster. Everything else still works alongside it β the keys, the drag,
the throw β and in multi-zone mode a tap goes back to meaning pick rather than file. The
toolbar buttons are the same thing again: deck.commit(zone), from another corner of the
interface.
The envelope: sender, domain, subject, whether anything is attached. Never the folder a message ended up in until you put it there. Two newsletters from the same domain teach it the domain; a client who writes about invoices teaches it that invoice beats the person. That is the whole argument for learning from the gesture β nobody writes those rules down, and everybody has them.