feat(mail): complete Quick Access message launches

This commit is contained in:
2026-08-20 02:39:41 +02:00
parent 9ac8847559
commit 2ad122056e
10 changed files with 219 additions and 12 deletions
+46 -3
View File
@@ -1,5 +1,6 @@
import { useEffect, useMemo, useRef, useState } from "react";
import { Activity, ChevronRight, Database, Home, Mail, MailOpen, Paperclip, RefreshCw, Search, X } from "lucide-react";
import { useLocation } from "react-router";
import { ToolbarGroup, ActionToolbar,
ActionBlockerHint,
Button,
@@ -30,6 +31,7 @@ import {
"../../api/mail";
import { buildMailboxFolderTree, findFolderNodeId, folderAncestorIds, type MailFolderNode } from "./mailboxFolders";
import { isMailboxMessageRead, mailboxSyncState, type MailboxSyncProvenance } from "./mailboxDisplay";
import { mailboxLaunchFolder, parseMailboxLaunch, type MailboxLaunch } from "./mailboxLaunch";
const MAILBOX_DOCUMENTATION = {
topicId: "mail.workflow.read-mailbox",
@@ -38,6 +40,7 @@ const MAILBOX_DOCUMENTATION = {
export default function MailboxPage({ settings, auth }: { settings: ApiSettings; auth: AuthInfo }) {
const navigate = useGuardedNavigate();
const location = useLocation();
const [profiles, setProfiles] = useState<MailServerProfile[]>([]);
const [selectedProfileId, setSelectedProfileId] = useState("");
const [folders, setFolders] = useState<MailImapFolderResponse[]>([]);
@@ -67,6 +70,7 @@ export default function MailboxPage({ settings, auth }: { settings: ApiSettings;
const messageDetailRequestRef = useRef(0);
const mailboxPageCursorsRef = useRef<Record<string, string | null>>({});
const skipNextMessageLoadRef = useRef(false);
const launchRequestRef = useRef<MailboxLaunch | null>(parseMailboxLaunch(location.search));
const selectedProfile = profiles.find((profile) => profile.id === selectedProfileId) ?? null;
const imapProfiles = useMemo(() => profiles.filter((profile) => profile.is_active && profile.imap), [profiles]);
@@ -99,6 +103,21 @@ export default function MailboxPage({ settings, auth }: { settings: ApiSettings;
: "";
useEffect(() => {void loadProfiles();}, [settings.apiBaseUrl, settings.apiKey, settings.accessToken]);
useEffect(() => {
const request = parseMailboxLaunch(location.search);
launchRequestRef.current = request;
if (!profiles.length || (!request.profileId && !request.folder && !request.folderRole && !request.messageUid)) return;
const usable = profiles.filter((profile) => profile.is_active && profile.imap);
const targetProfileId = request.profileId && usable.some((profile) => profile.id === request.profileId)
? request.profileId
: selectedProfileId || usable[0]?.id || "";
if (!targetProfileId) return;
if (targetProfileId !== selectedProfileId) {
selectProfile(targetProfileId);
return;
}
void loadMailboxBootstrap(targetProfileId);
}, [location.search]);
useEffect(() => {selectedMessageKeyRef.current = selectedMessageKeyState;}, [selectedMessageKeyState]);
useEffect(() => {if (messagePage > messagePageCount) setMessagePage(messagePageCount);}, [messagePage, messagePageCount]);
useEffect(() => {if (selectedProfileId) void loadMailboxBootstrap(selectedProfileId);}, [selectedProfileId]);
@@ -111,6 +130,19 @@ export default function MailboxPage({ settings, auth }: { settings: ApiSettings;
void loadMessages(selectedProfileId, selectedFolder, messagePage, messagePageSize);
}, [foldersReady, messagePage, messagePageSize, selectedProfileId, selectedFolder]);
useEffect(() => {
const request = launchRequestRef.current;
if (!request || !foldersReady || loadingMessages) return;
const targetProfileMatches = !request.profileId || request.profileId === selectedProfileId;
const targetFolder = mailboxLaunchFolder(request, selectedProfile);
if (!targetProfileMatches || (targetFolder && targetFolder !== selectedFolder)) return;
const target = request.messageUid
? messages.find((message) => message.uid === request.messageUid)
: null;
launchRequestRef.current = null;
if (target) void openMessage(target);
}, [foldersReady, loadingMessages, messages, selectedFolder, selectedProfile, selectedProfileId]);
useEffect(() => {
const handlePreviewShortcut = (event: KeyboardEvent) => {
if (isEditableTarget(event.target)) return;
@@ -158,8 +190,15 @@ export default function MailboxPage({ settings, auth }: { settings: ApiSettings;
try {
const loaded = await listMailServerProfiles(settings);
const usable = loaded.filter((profile) => profile.is_active && profile.imap);
const requestedProfileId = parseMailboxLaunch(location.search).profileId;
setProfiles(loaded);
setSelectedProfileId((current) => current && usable.some((profile) => profile.id === current) ? current : usable[0]?.id ?? "");
setSelectedProfileId((current) =>
requestedProfileId && usable.some((profile) => profile.id === requestedProfileId)
? requestedProfileId
: current && usable.some((profile) => profile.id === current)
? current
: usable[0]?.id ?? ""
);
if (usable.length === 0) {
setFolders([]);
setFoldersLoadedForProfile("");
@@ -182,9 +221,13 @@ export default function MailboxPage({ settings, auth }: { settings: ApiSettings;
async function loadMailboxBootstrap(profileId = selectedProfileId, refresh = false) {
if (!profileId) return;
const profileInbox = profiles.find((profile) => profile.id === profileId)?.imap?.folder_mappings?.inbox || "";
const requestedFolder = foldersLoadedForProfile === profileId && selectedFolder
const requestedLaunch = launchRequestRef.current;
const requestedLaunchFolder = requestedLaunch && (!requestedLaunch.profileId || requestedLaunch.profileId === profileId)
? mailboxLaunchFolder(requestedLaunch, profiles.find((profile) => profile.id === profileId) ?? null)
: null;
const requestedFolder = requestedLaunchFolder || (foldersLoadedForProfile === profileId && selectedFolder
? selectedFolder
: profileInbox || "INBOX";
: profileInbox || "INBOX");
const folderRequestId = ++folderRequestRef.current;
const messageRequestId = ++messageListRequestRef.current;
messageDetailRequestRef.current += 1;