Attachment previews, forwarded attachments, row dividers
Attachments in the reading pane are tiles with a preview: images and a PDF's
first page, rendered on the server (Pillow, pypdfium2) as WebP of at most
480 px, cached beside the blob store and in the browser. Tiles are all one
size — at most 230 px wide, narrower so three still fit in a row — and frame
each preview by its shape: close to the tile's it fills it, very tall / wide /
square ones show whole over a blurred copy, small ones are not enlarged, PDFs
show the top of the page. The name bar (checkbox, type, name, size, download)
slides in on hover and adapts to the tile's width; files without a preview
get a large type icon. Previews of opened conversations are kept offline.
Forward now carries the original's attachments: new compose attachments
sharing the stored blobs (POST /messages/{id}/forward-attachments).
Mailbox display / Settings → Appearance: "Show a dividing line between
rows" (off by default, per device), with a new switch. Settings drop the
"On a phone…" note, and "Language" reads "Spellchecking language".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -40,6 +40,8 @@ export interface ComposerInit {
|
||||
// A reply/forward quotes the original: this attribution line sits above the
|
||||
// quoted message body (fetched from the original message's sanitized HTML).
|
||||
quote?: { attribution: string; messageId: string };
|
||||
// A forward: the message whose attachments go along with it.
|
||||
forwardFrom?: string;
|
||||
// A message kept on this device (written offline; lib/scheduled.ts).
|
||||
localId?: string;
|
||||
localState?: LocalState; // "scheduled" when reopened from Scheduled
|
||||
@@ -336,6 +338,25 @@ export function Composer({
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
// A forward carries the original's attachments (shared on the server, not
|
||||
// re-uploaded). Once per window: StrictMode runs effects twice in dev, so a
|
||||
// ref — not a cancel flag — keeps it from attaching them twice.
|
||||
const forwardedFrom = useRef(false);
|
||||
useEffect(() => {
|
||||
const source = init.forwardFrom;
|
||||
if (!source || init.draftId || forwardedFrom.current) return;
|
||||
forwardedFrom.current = true;
|
||||
void api
|
||||
.forwardAttachments(source)
|
||||
.then((rows) => {
|
||||
if (rows.length) setAttachments((prev) => [...rows, ...prev]);
|
||||
})
|
||||
.catch((e) => {
|
||||
if (!isOfflineError(e)) setAttachError(t("compose.forwardAttachFailed"));
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const addFiles = async (files: FileList | File[]) => {
|
||||
markDirty();
|
||||
setAttachError("");
|
||||
|
||||
Reference in New Issue
Block a user