From dc0b4120599da600c48d970419f0cdcbda6bf5a3 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Fri, 5 Dec 2025 18:20:29 +0000 Subject: [PATCH 1/3] fix: use Tauri opener plugin for mailto links on all Tauri platforms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "Contact Us" buttons were not working on Tauri (iOS, Android, desktop) because mailto: links require the Tauri opener plugin to open the system's default email client. Changes: - Updated AccountMenu.tsx to use handleOpenExternalUrl for mailto links - Enhanced handleOpenExternalUrl to detect ALL Tauri platforms (not just mobile) - Updated pricing.tsx to use Tauri opener plugin for mailto links - Added proper fallback to window.location.href for web platforms Fixes #337 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Anthony --- frontend/src/components/AccountMenu.tsx | 36 ++++++++++++++------- frontend/src/routes/pricing.tsx | 42 +++++++++++++++++++++++-- 2 files changed, 64 insertions(+), 14 deletions(-) diff --git a/frontend/src/components/AccountMenu.tsx b/frontend/src/components/AccountMenu.tsx index 9905115b3..ebad92549 100644 --- a/frontend/src/components/AccountMenu.tsx +++ b/frontend/src/components/AccountMenu.tsx @@ -175,19 +175,27 @@ export function AccountMenu() { const handleOpenExternalUrl = async (url: string) => { try { - // Check if we're on a mobile platform - if (isMobile()) { + // Check if we're on any Tauri platform (mobile or desktop) + const { isTauri } = await import("@tauri-apps/api/core"); + const isInTauri = await isTauri(); + + if (isInTauri) { const { invoke } = await import("@tauri-apps/api/core"); await invoke("plugin:opener|open_url", { url }) .then(() => { - console.log("[External Link] Successfully opened URL in external browser"); + console.log("[External Link] Successfully opened URL with Tauri opener"); }) .catch((err: Error) => { - console.error("[External Link] Failed to open external browser:", err); - alert("Failed to open browser. Please try again."); + console.error("[External Link] Failed to open with Tauri opener:", err); + // Fallback to window.open on desktop (may work), alert on mobile + if (isMobile()) { + alert("Failed to open link. Please try again."); + } else { + window.open(url, "_blank", "noopener,noreferrer"); + } }); } else { - // Default browser opening for non-mobile platforms + // Default browser opening for web platform window.open(url, "_blank", "noopener,noreferrer"); } } catch (error) { @@ -380,11 +388,17 @@ export function AccountMenu() { Terms of Service - - - - Contact Us - + { + e.preventDefault(); + handleOpenExternalUrl("mailto:support@opensecret.cloud"); + }} + onSelect={(e) => { + e.preventDefault(); + }} + > + + Contact Us diff --git a/frontend/src/routes/pricing.tsx b/frontend/src/routes/pricing.tsx index 9f589fc45..cfd562f07 100644 --- a/frontend/src/routes/pricing.tsx +++ b/frontend/src/routes/pricing.tsx @@ -15,7 +15,7 @@ import { Switch } from "@/components/ui/switch"; import { PRICING_PLANS } from "@/config/pricingConfig"; import { VerificationModal } from "@/components/VerificationModal"; import { TeamSeatDialog } from "@/components/TeamSeatDialog"; -import { isIOS, isAndroid, isMobile } from "@/utils/platform"; +import { isIOS, isAndroid, isMobile, isTauri } from "@/utils/platform"; import packageJson from "../../package.json"; // File type constants for upload features @@ -469,7 +469,25 @@ function PricingPage() { // If user is on Zaprite plan, redirect to email if (freshBillingStatus?.payment_provider === "zaprite") { - window.location.href = "mailto:support@opensecret.cloud"; + // Use Tauri opener plugin for all Tauri platforms, fallback to window.location for web + if (isTauri()) { + import("@tauri-apps/api/core") + .then((coreModule) => { + return coreModule.invoke("plugin:opener|open_url", { + url: "mailto:support@opensecret.cloud" + }); + }) + .then(() => { + console.log("[Contact] Successfully opened mailto link with Tauri opener"); + }) + .catch((err) => { + console.error("[Contact] Failed to open mailto link with Tauri opener:", err); + // Fallback for web or if Tauri fails + window.location.href = "mailto:support@opensecret.cloud"; + }); + } else { + window.location.href = "mailto:support@opensecret.cloud"; + } return; } @@ -485,7 +503,25 @@ function PricingPage() { navigate({ to: "/" }); } else { // Other plans: contact support - window.location.href = "mailto:support@opensecret.cloud"; + // Use Tauri opener plugin for all Tauri platforms, fallback to window.location for web + if (isTauri()) { + import("@tauri-apps/api/core") + .then((coreModule) => { + return coreModule.invoke("plugin:opener|open_url", { + url: "mailto:support@opensecret.cloud" + }); + }) + .then(() => { + console.log("[Contact] Successfully opened mailto link with Tauri opener"); + }) + .catch((err) => { + console.error("[Contact] Failed to open mailto link with Tauri opener:", err); + // Fallback for web or if Tauri fails + window.location.href = "mailto:support@opensecret.cloud"; + }); + } else { + window.location.href = "mailto:support@opensecret.cloud"; + } } return; } From b2758e7bf4e7e20474c63be05ed9f04fab66567c Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Fri, 5 Dec 2025 18:39:01 +0000 Subject: [PATCH 2/3] refactor: use synchronous isTauri utility in AccountMenu Replace dynamic import/await of isTauri from @tauri-apps/api/core with the synchronous isTauri() function from @/utils/platform for consistency with the rest of the codebase (pricing.tsx pattern). Co-authored-by: Anthony --- frontend/src/components/AccountMenu.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/AccountMenu.tsx b/frontend/src/components/AccountMenu.tsx index ebad92549..a8bdb3236 100644 --- a/frontend/src/components/AccountMenu.tsx +++ b/frontend/src/components/AccountMenu.tsx @@ -13,7 +13,7 @@ import { FileText, ChevronLeft } from "lucide-react"; -import { isMobile } from "@/utils/platform"; +import { isMobile, isTauri } from "@/utils/platform"; import { Button } from "@/components/ui/button"; import { @@ -176,8 +176,7 @@ export function AccountMenu() { const handleOpenExternalUrl = async (url: string) => { try { // Check if we're on any Tauri platform (mobile or desktop) - const { isTauri } = await import("@tauri-apps/api/core"); - const isInTauri = await isTauri(); + const isInTauri = isTauri(); if (isInTauri) { const { invoke } = await import("@tauri-apps/api/core"); From 5f5fadd3c44591846b8fde4993e80c68157f3493 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Mon, 8 Dec 2025 17:00:30 +0000 Subject: [PATCH 3/3] fix: add mailto URL permissions to Tauri capability files Added mailto:* URL pattern to opener:allow-open-url permissions in all three capability files (mobile-ios, mobile-android, and desktop default). This enables the Tauri opener plugin to properly handle mailto links and open the default email client on all platforms. Also ran cargo fmt to fix formatting issues in Rust source files. Co-authored-by: Anthony --- frontend/src-tauri/capabilities/default.json | 3 +++ .../src-tauri/capabilities/mobile-android.json | 3 +++ frontend/src-tauri/capabilities/mobile-ios.json | 3 +++ frontend/src-tauri/src/lib.rs | 16 +++++++++++----- frontend/src-tauri/src/pdf_extractor.rs | 14 +++++++------- 5 files changed, 27 insertions(+), 12 deletions(-) diff --git a/frontend/src-tauri/capabilities/default.json b/frontend/src-tauri/capabilities/default.json index 3e5456fc6..9e75622f8 100644 --- a/frontend/src-tauri/capabilities/default.json +++ b/frontend/src-tauri/capabilities/default.json @@ -47,6 +47,9 @@ { "identifier": "opener:allow-open-url", "allow": [ + { + "url": "mailto:*" + }, { "url": "http://localhost:5173/*" }, diff --git a/frontend/src-tauri/capabilities/mobile-android.json b/frontend/src-tauri/capabilities/mobile-android.json index f3804bcc3..781a0d490 100644 --- a/frontend/src-tauri/capabilities/mobile-android.json +++ b/frontend/src-tauri/capabilities/mobile-android.json @@ -51,6 +51,9 @@ { "identifier": "opener:allow-open-url", "allow": [ + { + "url": "mailto:*" + }, { "url": "http://localhost:5173/*" }, diff --git a/frontend/src-tauri/capabilities/mobile-ios.json b/frontend/src-tauri/capabilities/mobile-ios.json index 54fe18645..ca7b69be7 100644 --- a/frontend/src-tauri/capabilities/mobile-ios.json +++ b/frontend/src-tauri/capabilities/mobile-ios.json @@ -14,6 +14,9 @@ { "identifier": "opener:allow-open-url", "allow": [ + { + "url": "mailto:*" + }, { "url": "http://localhost:5173/*" }, diff --git a/frontend/src-tauri/src/lib.rs b/frontend/src-tauri/src/lib.rs index 93479e312..5b9c3cfac 100644 --- a/frontend/src-tauri/src/lib.rs +++ b/frontend/src-tauri/src/lib.rs @@ -2,8 +2,8 @@ use tauri::{Emitter, Manager}; use tauri_plugin_deep_link::DeepLinkExt; use tauri_plugin_opener; -mod proxy; mod pdf_extractor; +mod proxy; #[cfg(desktop)] #[tauri::command] @@ -377,12 +377,18 @@ async fn check_for_updates(app_handle: tauri::AppHandle) -> Result<(), String> { version: String, } - if let Err(e) = app_handle.emit("update-ready", UpdateReadyPayload { - version: update.version.clone(), - }) { + if let Err(e) = app_handle.emit( + "update-ready", + UpdateReadyPayload { + version: update.version.clone(), + }, + ) { log::error!("Failed to emit update-ready event: {}", e); } else { - log::info!("Emitted update-ready event for version {}", update.version); + log::info!( + "Emitted update-ready event for version {}", + update.version + ); } } Err(e) => { diff --git a/frontend/src-tauri/src/pdf_extractor.rs b/frontend/src-tauri/src/pdf_extractor.rs index 243e52671..35cc1b2d3 100644 --- a/frontend/src-tauri/src/pdf_extractor.rs +++ b/frontend/src-tauri/src/pdf_extractor.rs @@ -16,31 +16,31 @@ pub struct DocumentResponse { #[tauri::command] pub async fn extract_document_content( - file_base64: String, + file_base64: String, filename: String, - file_type: String + file_type: String, ) -> Result { // Decode base64 file data let file_bytes = BASE64 .decode(&file_base64) .map_err(|e| format!("Failed to decode base64 file: {}", e))?; - + let text_content = match file_type.as_str() { "pdf" | "application/pdf" => { // Extract text from PDF extract_text_from_mem(&file_bytes) .map_err(|e| format!("Failed to extract text from PDF: {}", e))? - }, + } "txt" | "text/plain" | "md" | "text/markdown" => { // For text files, just convert bytes to string String::from_utf8(file_bytes) .map_err(|e| format!("Failed to decode text file: {}", e))? - }, + } _ => { return Err(format!("Unsupported file type: {}", file_type)); } }; - + Ok(DocumentResponse { document: DocumentData { filename, @@ -48,4 +48,4 @@ pub async fn extract_document_content( }, status: "completed".to_string(), }) -} \ No newline at end of file +}