mirror of
https://github.com/zhom/donutbrowser.git
synced 2026-08-09 04:36:15 +02:00
fix: properly handle urls added by system events
This commit is contained in:
Vendored
+1
@@ -69,6 +69,7 @@
|
|||||||
"Torbrowser",
|
"Torbrowser",
|
||||||
"turbopack",
|
"turbopack",
|
||||||
"unlisten",
|
"unlisten",
|
||||||
|
"unminimize",
|
||||||
"unrs",
|
"unrs",
|
||||||
"urlencoding",
|
"urlencoding",
|
||||||
"vercel",
|
"vercel",
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "^22.15.30",
|
"@types/node": "^22.15.31",
|
||||||
"@yao-pkg/pkg": "^6.5.1",
|
"@yao-pkg/pkg": "^6.5.1",
|
||||||
"commander": "^14.0.0",
|
"commander": "^14.0.0",
|
||||||
"dotenv": "^16.5.0",
|
"dotenv": "^16.5.0",
|
||||||
@@ -30,6 +30,6 @@
|
|||||||
"proxy-chain": "^2.5.9",
|
"proxy-chain": "^2.5.9",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"typescript-eslint": "^8.33.1"
|
"typescript-eslint": "^8.34.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-11
@@ -35,6 +35,7 @@
|
|||||||
"@radix-ui/react-tooltip": "^1.2.7",
|
"@radix-ui/react-tooltip": "^1.2.7",
|
||||||
"@tanstack/react-table": "^8.21.3",
|
"@tanstack/react-table": "^8.21.3",
|
||||||
"@tauri-apps/api": "^2.5.0",
|
"@tauri-apps/api": "^2.5.0",
|
||||||
|
"@tauri-apps/plugin-deep-link": "^2.3.0",
|
||||||
"@tauri-apps/plugin-dialog": "^2.2.2",
|
"@tauri-apps/plugin-dialog": "^2.2.2",
|
||||||
"@tauri-apps/plugin-fs": "~2.3.0",
|
"@tauri-apps/plugin-fs": "~2.3.0",
|
||||||
"@tauri-apps/plugin-opener": "^2.2.7",
|
"@tauri-apps/plugin-opener": "^2.2.7",
|
||||||
@@ -48,31 +49,31 @@
|
|||||||
"react-dom": "^19.1.0",
|
"react-dom": "^19.1.0",
|
||||||
"react-icons": "^5.5.0",
|
"react-icons": "^5.5.0",
|
||||||
"sonner": "^2.0.5",
|
"sonner": "^2.0.5",
|
||||||
"tailwind-merge": "^3.3.0",
|
"tailwind-merge": "^3.3.1",
|
||||||
"tauri-plugin-macos-permissions-api": "^2.3.0"
|
"tauri-plugin-macos-permissions-api": "^2.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "1.9.4",
|
"@biomejs/biome": "1.9.4",
|
||||||
"@eslint/eslintrc": "^3.3.1",
|
"@eslint/eslintrc": "^3.3.1",
|
||||||
"@eslint/js": "^9.28.0",
|
"@eslint/js": "^9.29.0",
|
||||||
"@next/eslint-plugin-next": "^15.3.3",
|
"@next/eslint-plugin-next": "^15.3.3",
|
||||||
"@tailwindcss/postcss": "^4.1.8",
|
"@tailwindcss/postcss": "^4.1.10",
|
||||||
"@tauri-apps/cli": "^2.5.0",
|
"@tauri-apps/cli": "^2.5.0",
|
||||||
"@types/node": "^22.15.30",
|
"@types/node": "^22.15.31",
|
||||||
"@types/react": "^19.1.6",
|
"@types/react": "^19.1.8",
|
||||||
"@types/react-dom": "^19.1.6",
|
"@types/react-dom": "^19.1.6",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.33.1",
|
"@typescript-eslint/eslint-plugin": "^8.34.0",
|
||||||
"@typescript-eslint/parser": "^8.33.1",
|
"@typescript-eslint/parser": "^8.34.0",
|
||||||
"@vitejs/plugin-react": "^4.5.1",
|
"@vitejs/plugin-react": "^4.5.2",
|
||||||
"eslint": "^9.28.0",
|
"eslint": "^9.29.0",
|
||||||
"eslint-config-next": "^15.3.3",
|
"eslint-config-next": "^15.3.3",
|
||||||
"eslint-plugin-react-hooks": "^5.2.0",
|
"eslint-plugin-react-hooks": "^5.2.0",
|
||||||
"husky": "^9.1.7",
|
"husky": "^9.1.7",
|
||||||
"lint-staged": "^16.1.0",
|
"lint-staged": "^16.1.0",
|
||||||
"tailwindcss": "^4.1.8",
|
"tailwindcss": "^4.1.10",
|
||||||
"tw-animate-css": "^1.3.4",
|
"tw-animate-css": "^1.3.4",
|
||||||
"typescript": "~5.8.3",
|
"typescript": "~5.8.3",
|
||||||
"typescript-eslint": "^8.33.1"
|
"typescript-eslint": "^8.34.0"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.11.1",
|
"packageManager": "pnpm@10.11.1",
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
|
|||||||
Generated
+762
-712
File diff suppressed because it is too large
Load Diff
@@ -26,5 +26,33 @@
|
|||||||
<string>public.app-category.productivity</string>
|
<string>public.app-category.productivity</string>
|
||||||
<key>NSHumanReadableCopyright</key>
|
<key>NSHumanReadableCopyright</key>
|
||||||
<string>Copyright © 2025 Donut Browser</string>
|
<string>Copyright © 2025 Donut Browser</string>
|
||||||
|
<key>CFBundleDocumentTypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleTypeName</key>
|
||||||
|
<string>HTML document</string>
|
||||||
|
<key>CFBundleTypeRole</key>
|
||||||
|
<string>Viewer</string>
|
||||||
|
<key>LSHandlerRank</key>
|
||||||
|
<string>Default</string>
|
||||||
|
<key>LSItemContentTypes</key>
|
||||||
|
<array>
|
||||||
|
<string>public.html</string>
|
||||||
|
<string>public.xhtml</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>CFBundleURLTypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleURLName</key>
|
||||||
|
<string>Web site URL</string>
|
||||||
|
<key>CFBundleURLSchemes</key>
|
||||||
|
<array>
|
||||||
|
<string>http</string>
|
||||||
|
<string>https</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
@@ -20,6 +20,9 @@
|
|||||||
"shell:allow-stdin-write",
|
"shell:allow-stdin-write",
|
||||||
"deep-link:default",
|
"deep-link:default",
|
||||||
"deep-link:allow-register",
|
"deep-link:allow-register",
|
||||||
|
"deep-link:allow-unregister",
|
||||||
|
"deep-link:allow-is-registered",
|
||||||
|
"deep-link:allow-get-current",
|
||||||
"dialog:default",
|
"dialog:default",
|
||||||
"dialog:allow-open",
|
"dialog:allow-open",
|
||||||
"macos-permissions:default",
|
"macos-permissions:default",
|
||||||
|
|||||||
+67
-20
@@ -1,4 +1,5 @@
|
|||||||
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
|
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
|
||||||
|
use std::env;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use tauri::{Emitter, Manager, Runtime, WebviewUrl, WebviewWindow, WebviewWindowBuilder};
|
use tauri::{Emitter, Manager, Runtime, WebviewUrl, WebviewWindow, WebviewWindowBuilder};
|
||||||
use tauri_plugin_deep_link::DeepLinkExt;
|
use tauri_plugin_deep_link::DeepLinkExt;
|
||||||
@@ -111,20 +112,16 @@ async fn handle_url_open(app: tauri::AppHandle, url: String) -> Result<(), Strin
|
|||||||
|
|
||||||
// Check if the main window exists and is ready
|
// Check if the main window exists and is ready
|
||||||
if let Some(window) = app.get_webview_window("main") {
|
if let Some(window) = app.get_webview_window("main") {
|
||||||
if window.is_visible().unwrap_or(false) {
|
println!("Main window exists");
|
||||||
// Window is visible, emit event directly
|
|
||||||
println!("Main window is visible, emitting show-profile-selector event");
|
// Try to show and focus the window first
|
||||||
app
|
let _ = window.show();
|
||||||
.emit("show-profile-selector", url.clone())
|
let _ = window.set_focus();
|
||||||
.map_err(|e| format!("Failed to emit URL open event: {e}"))?;
|
let _ = window.unminimize();
|
||||||
let _ = window.show();
|
|
||||||
let _ = window.set_focus();
|
app
|
||||||
} else {
|
.emit("show-profile-selector", url.clone())
|
||||||
// Window not visible yet - add to pending URLs
|
.map_err(|e| format!("Failed to emit URL open event: {e}"))?;
|
||||||
println!("Main window not visible, adding URL to pending list");
|
|
||||||
let mut pending = PENDING_URLS.lock().unwrap();
|
|
||||||
pending.push(url);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Window doesn't exist yet - add to pending URLs
|
// Window doesn't exist yet - add to pending URLs
|
||||||
println!("Main window doesn't exist, adding URL to pending list");
|
println!("Main window doesn't exist, adding URL to pending list");
|
||||||
@@ -137,6 +134,8 @@ async fn handle_url_open(app: tauri::AppHandle, url: String) -> Result<(), Strin
|
|||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
async fn check_and_handle_startup_url(app_handle: tauri::AppHandle) -> Result<bool, String> {
|
async fn check_and_handle_startup_url(app_handle: tauri::AppHandle) -> Result<bool, String> {
|
||||||
|
println!("check_and_handle_startup_url called");
|
||||||
|
|
||||||
let pending_urls = {
|
let pending_urls = {
|
||||||
let mut pending = PENDING_URLS.lock().unwrap();
|
let mut pending = PENDING_URLS.lock().unwrap();
|
||||||
let urls = pending.clone();
|
let urls = pending.clone();
|
||||||
@@ -144,12 +143,24 @@ async fn check_and_handle_startup_url(app_handle: tauri::AppHandle) -> Result<bo
|
|||||||
urls
|
urls
|
||||||
};
|
};
|
||||||
|
|
||||||
|
println!("Found {} pending URLs", pending_urls.len());
|
||||||
|
|
||||||
if !pending_urls.is_empty() {
|
if !pending_urls.is_empty() {
|
||||||
println!(
|
println!(
|
||||||
"Handling {} pending URLs from frontend request",
|
"Handling {} pending URLs from frontend request",
|
||||||
pending_urls.len()
|
pending_urls.len()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Ensure the main window is visible and focused
|
||||||
|
if let Some(window) = app_handle.get_webview_window("main") {
|
||||||
|
let _ = window.show();
|
||||||
|
let _ = window.set_focus();
|
||||||
|
let _ = window.unminimize();
|
||||||
|
|
||||||
|
// Give the window a moment to become visible
|
||||||
|
tokio::time::sleep(tokio::time::Duration::from_millis(200)).await;
|
||||||
|
}
|
||||||
|
|
||||||
for url in pending_urls {
|
for url in pending_urls {
|
||||||
println!("Emitting show-profile-selector event for URL: {url}");
|
println!("Emitting show-profile-selector event for URL: {url}");
|
||||||
if let Err(e) = app_handle.emit("show-profile-selector", url.clone()) {
|
if let Err(e) = app_handle.emit("show-profile-selector", url.clone()) {
|
||||||
@@ -166,11 +177,23 @@ async fn check_and_handle_startup_url(app_handle: tauri::AppHandle) -> Result<bo
|
|||||||
|
|
||||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||||
pub fn run() {
|
pub fn run() {
|
||||||
|
let args: Vec<String> = env::args().collect();
|
||||||
|
let startup_url = args.iter().find(|arg| arg.starts_with("http")).cloned();
|
||||||
|
|
||||||
|
if let Some(url) = startup_url.clone() {
|
||||||
|
println!("Found startup URL in command line: {url}");
|
||||||
|
let mut pending = PENDING_URLS.lock().unwrap();
|
||||||
|
pending.push(url.clone());
|
||||||
|
}
|
||||||
|
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
|
.plugin(tauri_plugin_single_instance::init(|_, args, _cwd| {
|
||||||
|
println!("Single instance triggered with args: {args:?}");
|
||||||
|
}))
|
||||||
|
.plugin(tauri_plugin_deep_link::init())
|
||||||
.plugin(tauri_plugin_fs::init())
|
.plugin(tauri_plugin_fs::init())
|
||||||
.plugin(tauri_plugin_opener::init())
|
.plugin(tauri_plugin_opener::init())
|
||||||
.plugin(tauri_plugin_shell::init())
|
.plugin(tauri_plugin_shell::init())
|
||||||
.plugin(tauri_plugin_deep_link::init())
|
|
||||||
.plugin(tauri_plugin_dialog::init())
|
.plugin(tauri_plugin_dialog::init())
|
||||||
.plugin(tauri_plugin_macos_permissions::init())
|
.plugin(tauri_plugin_macos_permissions::init())
|
||||||
.setup(|app| {
|
.setup(|app| {
|
||||||
@@ -180,7 +203,10 @@ pub fn run() {
|
|||||||
.title("Donut Browser")
|
.title("Donut Browser")
|
||||||
.inner_size(900.0, 600.0)
|
.inner_size(900.0, 600.0)
|
||||||
.resizable(false)
|
.resizable(false)
|
||||||
.fullscreen(false);
|
.fullscreen(false)
|
||||||
|
.center()
|
||||||
|
.focused(true)
|
||||||
|
.visible(true);
|
||||||
|
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
let window = win_builder.build().unwrap();
|
let window = win_builder.build().unwrap();
|
||||||
@@ -199,16 +225,27 @@ pub fn run() {
|
|||||||
#[cfg(any(windows, target_os = "linux"))]
|
#[cfg(any(windows, target_os = "linux"))]
|
||||||
{
|
{
|
||||||
// For Windows and Linux, register all deep links at runtime for development
|
// For Windows and Linux, register all deep links at runtime for development
|
||||||
app.deep_link().register_all()?;
|
if let Err(e) = app.deep_link().register_all() {
|
||||||
|
eprintln!("Failed to register deep links: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
{
|
||||||
|
// On macOS, try to register deep links for development builds
|
||||||
|
if let Err(e) = app.deep_link().register_all() {
|
||||||
|
eprintln!(
|
||||||
|
"Note: Deep link registration failed on macOS (this is normal for production): {e}"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle deep links - this works for both scenarios:
|
|
||||||
// 1. App is running and URL is opened
|
|
||||||
// 2. App is not running and URL causes app to launch
|
|
||||||
app.deep_link().on_open_url({
|
app.deep_link().on_open_url({
|
||||||
let handle = handle.clone();
|
let handle = handle.clone();
|
||||||
move |event| {
|
move |event| {
|
||||||
let urls = event.urls();
|
let urls = event.urls();
|
||||||
|
println!("Deep link event received with {} URLs", urls.len());
|
||||||
|
|
||||||
for url in urls {
|
for url in urls {
|
||||||
let url_string = url.to_string();
|
let url_string = url.to_string();
|
||||||
println!("Deep link received: {url_string}");
|
println!("Deep link received: {url_string}");
|
||||||
@@ -226,6 +263,16 @@ pub fn run() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if let Some(startup_url) = startup_url {
|
||||||
|
let handle_clone = handle.clone();
|
||||||
|
tauri::async_runtime::spawn(async move {
|
||||||
|
println!("Processing startup URL from command line: {startup_url}");
|
||||||
|
if let Err(e) = handle_url_open(handle_clone, startup_url.clone()).await {
|
||||||
|
eprintln!("Failed to handle startup URL: {e}");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize and start background version updater
|
// Initialize and start background version updater
|
||||||
let app_handle = app.handle().clone();
|
let app_handle = app.handle().clone();
|
||||||
tauri::async_runtime::spawn(async move {
|
tauri::async_runtime::spawn(async move {
|
||||||
|
|||||||
@@ -61,8 +61,9 @@
|
|||||||
},
|
},
|
||||||
"plugins": {
|
"plugins": {
|
||||||
"deep-link": {
|
"deep-link": {
|
||||||
"schemes": ["http", "https"],
|
"desktop": {
|
||||||
"domains": []
|
"schemes": ["http", "https"]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-1
@@ -29,6 +29,7 @@ import { showErrorToast } from "@/lib/toast-utils";
|
|||||||
import type { BrowserProfile, ProxySettings } from "@/types";
|
import type { BrowserProfile, ProxySettings } from "@/types";
|
||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import { listen } from "@tauri-apps/api/event";
|
import { listen } from "@tauri-apps/api/event";
|
||||||
|
import { getCurrent } from "@tauri-apps/plugin-deep-link";
|
||||||
import { useCallback, useEffect, useRef, useState } from "react";
|
import { useCallback, useEffect, useRef, useState } from "react";
|
||||||
import { FaDownload } from "react-icons/fa";
|
import { FaDownload } from "react-icons/fa";
|
||||||
import { GoGear, GoKebabHorizontal, GoPlus } from "react-icons/go";
|
import { GoGear, GoKebabHorizontal, GoPlus } from "react-icons/go";
|
||||||
@@ -102,6 +103,18 @@ export default function Home() {
|
|||||||
|
|
||||||
useAppUpdateNotifications();
|
useAppUpdateNotifications();
|
||||||
|
|
||||||
|
// For some reason, app.deep_link().get_current() is not working properly
|
||||||
|
const checkCurrentUrl = useCallback(async () => {
|
||||||
|
try {
|
||||||
|
const currentUrl = await getCurrent();
|
||||||
|
if (currentUrl && currentUrl.length > 0) {
|
||||||
|
void handleUrlOpen(currentUrl[0]);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to check current URL:", error);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
void loadProfilesWithUpdateCheck();
|
void loadProfilesWithUpdateCheck();
|
||||||
|
|
||||||
@@ -113,6 +126,7 @@ export default function Home() {
|
|||||||
|
|
||||||
// Check for startup URLs (when app was launched as default browser)
|
// Check for startup URLs (when app was launched as default browser)
|
||||||
void checkStartupUrls();
|
void checkStartupUrls();
|
||||||
|
void checkCurrentUrl();
|
||||||
|
|
||||||
// Set up periodic update checks (every 30 minutes)
|
// Set up periodic update checks (every 30 minutes)
|
||||||
const updateInterval = setInterval(
|
const updateInterval = setInterval(
|
||||||
@@ -125,7 +139,7 @@ export default function Home() {
|
|||||||
return () => {
|
return () => {
|
||||||
clearInterval(updateInterval);
|
clearInterval(updateInterval);
|
||||||
};
|
};
|
||||||
}, [loadProfilesWithUpdateCheck, checkForUpdates]);
|
}, [loadProfilesWithUpdateCheck, checkForUpdates, checkCurrentUrl]);
|
||||||
|
|
||||||
// Check permissions when they are initialized
|
// Check permissions when they are initialized
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user