mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-04 08:15:41 +00:00
Review of the pop-out work found ways to lose text, a wrong sign-out and changes reaching ordinary browser tabs. - Drafts: a closing window hands text over only to a main window holding its login, and asks instead when the last save failed, the browser is offline, a save is running or plugins could still have a say. A failed rescue goes to "Continue draft". The opening window lets go only once the composer is on screen, also for Edit draft in a message window. Send options travel with the draft. Closing during a send asks first. - Sessions: answers are encrypted for the window that asked, and only a window holding the login answers. A renewal queued on the lock can be called off by a sign-out, signs nobody out once its account is gone, and takes the cached token after waiting (Rauthy `nbf`, #552). - Ordinary tabs run none of the host. No lock is held for life. - A message window hides after a refused session and waits for its folders; a composer stays on screen with its text. Another tab dropping a login it couldn't restore no longer closes pop-outs. - Plain-text mail stays selectable in the desktop app. Folder save renames reserved and shell names and skips one refused name. All display-mode checks know the overlay. The attachment bar keeps to one row. The earlier test that accepted text being gone after a failed rescue now asserts that it reaches the "Continue draft" slot.
76 lines
2.3 KiB
TypeScript
76 lines
2.3 KiB
TypeScript
"use client";
|
|
|
|
import { isStandaloneDisplayMode } from "@/lib/standalone-display";
|
|
import { useEffect } from "react";
|
|
import { parseWebcal } from "@/lib/protocol-handlers/webcal";
|
|
import { savePendingWebcal } from "@/lib/protocol-handlers/session";
|
|
import { useSettingsStore } from "@/stores/settings-store";
|
|
import { CalendarSkeleton, type CalendarSkeletonText } from "@/components/boot-skeleton/calendar-skeleton";
|
|
import type { RailLabels } from "@/components/boot-skeleton/shell";
|
|
|
|
|
|
function getProtocolPathPrefix(): string {
|
|
const marker = "/protocol/webcal";
|
|
const index = window.location.pathname.indexOf(marker);
|
|
return index > 0 ? window.location.pathname.slice(0, index) : "";
|
|
}
|
|
|
|
function returnToSourcePage() {
|
|
window.close();
|
|
|
|
window.setTimeout(() => {
|
|
if (window.history.length > 1) {
|
|
window.history.back();
|
|
}
|
|
}, 150);
|
|
}
|
|
|
|
function openFallbackAppTab(raw: string): boolean {
|
|
const url = `${getProtocolPathPrefix()}/protocol/webcal?url=${encodeURIComponent(raw)}&fallback=1`;
|
|
const opened = window.open(url, "_blank");
|
|
if (!opened) return false;
|
|
opened.opener = null;
|
|
return true;
|
|
}
|
|
|
|
function shouldOpenFallbackAppTab(): boolean {
|
|
return !isStandaloneDisplayMode() && window.history.length > 1;
|
|
}
|
|
|
|
interface WebcalProtocolClientProps {
|
|
openingText: string;
|
|
/** Rail labels for the phone bottom bar (sidebar.*). */
|
|
labels?: RailLabels;
|
|
/** The calendar skeleton's words (calendar.*). */
|
|
text?: CalendarSkeletonText;
|
|
}
|
|
|
|
export function WebcalProtocolClient({ openingText, labels, text }: WebcalProtocolClientProps) {
|
|
useEffect(() => {
|
|
const params = new URLSearchParams(window.location.search);
|
|
const raw = params.get("url");
|
|
const isFallbackAppTab = params.get("fallback") === "1";
|
|
|
|
if (raw) {
|
|
const parsed = parseWebcal(raw);
|
|
if (parsed) {
|
|
if (!isFallbackAppTab
|
|
&& useSettingsStore.getState().protocolOpenMode === "new-tab"
|
|
&& shouldOpenFallbackAppTab()
|
|
&& openFallbackAppTab(raw)) {
|
|
returnToSourcePage();
|
|
return;
|
|
}
|
|
|
|
savePendingWebcal(parsed);
|
|
}
|
|
}
|
|
|
|
window.location.replace(`${getProtocolPathPrefix()}/calendar`);
|
|
}, []);
|
|
|
|
// The calendar is where this goes next: its skeleton, with "Opening…" as
|
|
// the page's status.
|
|
return <CalendarSkeleton label={openingText} labels={labels} text={text} />;
|
|
}
|