Feature/protocol handlers
* Added account selection for protocol links when multiple connected accounts are available, including mailto: links * Added support for handling mailto: links in an already-open PWA/session instead of always opening a new tab * Added webcal: protocol handling for calendar links * Added account selection for webcal: links when multiple calendar-capable accounts are connected * Added an import-or-subscribe choice for detected webcal calendars * Added protocol handler settings for registering mail and calendar handlers and choosing the open mode * Added service worker/session coordination for passing protocol requests between browser/PWA contexts * Added tests and translations for the new protocol handler flows
This commit is contained in:
committed by
Linus Rath
parent
8b0e2052cf
commit
3f444a8912
@@ -0,0 +1,48 @@
|
||||
export interface ParsedWebcal {
|
||||
originalUrl: string;
|
||||
subscriptionUrl: string;
|
||||
suggestedName: string;
|
||||
}
|
||||
|
||||
function stripControlChars(value: string): string {
|
||||
return value.replace(/[\u0000-\u001F\u007F]/g, "").trim();
|
||||
}
|
||||
|
||||
function extensionlessName(value: string): string {
|
||||
return value.replace(/\.(ics|ical)$/i, "");
|
||||
}
|
||||
|
||||
function decodePathSegment(value: string): string {
|
||||
try {
|
||||
return decodeURIComponent(value);
|
||||
} catch {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
export function parseWebcal(raw: string): ParsedWebcal | null {
|
||||
let url: URL;
|
||||
|
||||
try {
|
||||
url = new URL(raw);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (url.protocol === "webcal:" || url.protocol === "webcals:") {
|
||||
url = new URL(raw.replace(/^webcals?:/i, "https:"));
|
||||
} else if (url.protocol !== "http:" && url.protocol !== "https:") {
|
||||
return null;
|
||||
}
|
||||
|
||||
const subscriptionUrl = url.toString();
|
||||
const queryName = stripControlChars(url.searchParams.get("name") || "");
|
||||
const pathSegment = stripControlChars(decodePathSegment(url.pathname.split("/").filter(Boolean).pop() || ""));
|
||||
const suggestedName = queryName || extensionlessName(pathSegment) || url.hostname;
|
||||
|
||||
return {
|
||||
originalUrl: raw,
|
||||
subscriptionUrl,
|
||||
suggestedName,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user