Merge pull request #6 from LINUXexpert-org/default-mail-handler
Offer ihasmail as the browser's mailto: handler
This commit is contained in:
@@ -4,7 +4,7 @@ import { Calendar, ChevronsUpDown, FolderOpen, HelpCircle, Mail, Menu as MenuIco
|
||||
import { useSession } from "@/store/session";
|
||||
import { useSettings } from "@/store/settings";
|
||||
import { useMail } from "@/store/mail";
|
||||
import { useCompose } from "@/store/compose";
|
||||
import { draftFromMailto, useCompose } from "@/store/compose";
|
||||
import { Avatar, useIsMobile } from "@/ui/misc";
|
||||
import { MenuItem, MenuSep, MenuTitle, Popover, useMenu } from "@/ui/popover";
|
||||
import { SearchBar } from "./SearchBar";
|
||||
@@ -42,9 +42,7 @@ export function AppShell({ children }: { children: ReactNode }) {
|
||||
}
|
||||
const mailto = params.get("mailto");
|
||||
if (mailto) {
|
||||
const [addr, qs] = mailto.replace(/^mailto:/, "").split("?");
|
||||
const q = new URLSearchParams(qs ?? "");
|
||||
openCompose({ to: addr ? addr.split(",").map((e) => ({ name: null, email: e.trim() })) : [], subject: q.get("subject") ?? "", html: q.get("body") ? `<div>${q.get("body")}</div>` : "" });
|
||||
openCompose(draftFromMailto(mailto));
|
||||
navigate("/mail", { replace: true });
|
||||
}
|
||||
}, [openCompose, navigate]);
|
||||
|
||||
@@ -4,7 +4,7 @@ import { FilterFromMessageDialog } from "./FilterFromMessage";
|
||||
import type { Email, EmailAddress, EmailBodyPart, Id } from "@/jmap/types";
|
||||
import { useMail } from "@/store/mail";
|
||||
import { useSettings } from "@/store/settings";
|
||||
import { useCompose } from "@/store/compose";
|
||||
import { draftFromMailto, useCompose } from "@/store/compose";
|
||||
import { useContacts } from "@/store/contacts";
|
||||
import { client } from "@/jmap/client";
|
||||
import { formatFullDate, formatListDate, formatSize } from "@/lib/format";
|
||||
@@ -103,9 +103,8 @@ export const MessageView = memo(function MessageView({ email: e, expanded, onTog
|
||||
const mailto = urls.find((u) => u.startsWith("mailto:"));
|
||||
const http = urls.find((u) => /^https?:/i.test(u));
|
||||
if (mailto) {
|
||||
const [addr, qs] = mailto.slice(7).split("?");
|
||||
const q = new URLSearchParams(qs ?? "");
|
||||
useCompose.getState().open({ to: [{ name: null, email: addr ?? "" }], subject: q.get("subject") ?? "unsubscribe", html: `<div>${q.get("body") ?? "unsubscribe"}</div>`, text: q.get("body") ?? "unsubscribe" });
|
||||
const fields = draftFromMailto(mailto);
|
||||
useCompose.getState().open({ ...fields, subject: fields.subject || "unsubscribe", html: fields.html ?? "<div>unsubscribe</div>", text: fields.text ?? "unsubscribe" });
|
||||
toast.show("Unsubscribe message prepared — just hit Send");
|
||||
} else if (http) {
|
||||
window.open(http, "_blank", "noopener,noreferrer");
|
||||
@@ -272,9 +271,7 @@ function HtmlBody({ html, bodyStyle, onShowImages }: { html: string; bodyStyle:
|
||||
const href = a.getAttribute("href") ?? "";
|
||||
if (href.startsWith("mailto:")) {
|
||||
ev.preventDefault();
|
||||
const [addr, qs] = href.slice(7).split("?");
|
||||
const q = new URLSearchParams(qs ?? "");
|
||||
openCompose({ to: addr ? addr.split(",").map((x) => ({ name: null, email: decodeURIComponent(x.trim()) })) : [], subject: q.get("subject") ?? "", html: q.get("body") ? `<div>${q.get("body")}</div>` : "" });
|
||||
openCompose(draftFromMailto(href));
|
||||
return;
|
||||
}
|
||||
if (/^(javascript|data|vbscript):/i.test(href)) {
|
||||
|
||||
@@ -2,6 +2,15 @@ import { useSettings } from "@/store/settings";
|
||||
import { Switch } from "@/ui/misc";
|
||||
import { browserTimeZone, listTimeZones } from "@/lib/dates";
|
||||
import { toast } from "@/ui/toast";
|
||||
import { useState } from "react";
|
||||
import {
|
||||
canUnregisterMailtoHandler,
|
||||
isInstalledApp,
|
||||
mailtoHandlerRequested,
|
||||
mailtoHandlerSupport,
|
||||
registerMailtoHandler,
|
||||
unregisterMailtoHandler,
|
||||
} from "@/lib/mailhandler";
|
||||
import {
|
||||
browserLocale,
|
||||
formatClock,
|
||||
@@ -154,6 +163,9 @@ export function GeneralSettings() {
|
||||
</div>
|
||||
<p className="hint">Preview: {formatFullDateTime(SAMPLE)}</p>
|
||||
|
||||
<h2>Default mail app</h2>
|
||||
<MailHandlerSettings />
|
||||
|
||||
<h2>Backup</h2>
|
||||
<div className="row wrap">
|
||||
<button className="btn" onClick={() => { const blob = new Blob([exportJson()], { type: "application/json" }); const a = document.createElement("a"); a.href = URL.createObjectURL(blob); a.download = "ihasmail-settings.json"; a.click(); }}>Export settings</button>
|
||||
@@ -167,3 +179,54 @@ export function GeneralSettings() {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Offer ihasmail as the browser's handler for `mailto:` links. The browser
|
||||
* owns the decision, and nothing can read the answer back, so this states what
|
||||
* it can and points at the browser's own settings for the rest.
|
||||
*/
|
||||
function MailHandlerSettings() {
|
||||
const support = mailtoHandlerSupport();
|
||||
const [requested, setRequested] = useState(mailtoHandlerRequested);
|
||||
|
||||
const ask = () => {
|
||||
try {
|
||||
registerMailtoHandler();
|
||||
setRequested(true);
|
||||
toast.success("Your browser will ask whether to open mail links in ihasmail");
|
||||
} catch (err) {
|
||||
toast.error(`Your browser refused the request: ${(err as Error).message}`);
|
||||
}
|
||||
};
|
||||
|
||||
const remove = () => {
|
||||
unregisterMailtoHandler();
|
||||
setRequested(false);
|
||||
toast.show("Removed. Mail links will open in whatever your browser falls back to.");
|
||||
};
|
||||
|
||||
if (support === "unsupported") {
|
||||
return <p className="hint">This browser cannot register apps for <code>mailto:</code> links. Safari, in particular, has no such API — you can still make ihasmail the default from your operating system if you install it as an app.</p>;
|
||||
}
|
||||
if (support === "insecure") {
|
||||
return <p className="hint">Registering for <code>mailto:</code> links requires a secure (HTTPS) connection.</p>;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<p className="hint">
|
||||
Open <code>mailto:</code> links — in web pages, documents and other apps — in ihasmail instead of a desktop mail client.
|
||||
Your browser will ask you to confirm, and you can change it later in its own settings (Chrome: Settings › Privacy and security › Site settings › Protocol handlers; Firefox: Settings › General › Applications).
|
||||
</p>
|
||||
<div className="row wrap">
|
||||
<button className="btn btn-primary" onClick={ask}>{requested ? "Ask again" : "Make ihasmail the default mail app"}</button>
|
||||
{requested && canUnregisterMailtoHandler() && <button className="btn btn-ghost" onClick={remove}>Remove</button>}
|
||||
</div>
|
||||
{requested && <p className="hint mt-8">Requested in this browser. Whether it took effect is up to the browser — check its settings if mail links still open elsewhere.</p>}
|
||||
{!isInstalledApp() && (
|
||||
<p className="hint mt-8">
|
||||
For a system-wide default, install ihasmail as an app first (in Chrome: the install icon in the address bar). Your operating system can then offer ihasmail directly wherever it asks which mail app to use.
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user