mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2026-10-19 09:22:12 +00:00
open links in browser
This commit is contained in:
1 parent
8d6885d58b
commit
358253b7a9
9 files changed
+90
No files matched your search
@@ -25,4 +25,5 @@ notify = "8.2.0"
|
||||
notify-debouncer-mini = "0.7.0"
|
||||
tauri-plugin-dialog = "2.8.2"
|
||||
tauri-plugin-single-instance = "2.5.3"
|
||||
tauri-plugin-opener = "2"
|
||||
|
||||
@@ -12,3 +12,4 @@ pub mod queries;
|
||||
pub mod selection;
|
||||
pub mod statistics;
|
||||
pub mod storage;
|
||||
pub mod system;
|
||||
@@ -0,0 +1,18 @@
|
||||
//! What the app asks of the system, outside of the files.
|
||||
|
||||
use tauri::{AppHandle, Runtime};
|
||||
use tauri_plugin_opener::OpenerExt;
|
||||
|
||||
/// Opens a web page (or an email to write) with the app of the system for it: the links of the
|
||||
/// page are not followed in the window of the app. Only `http`, `https` and `mailto` addresses are
|
||||
/// opened.
|
||||
#[tauri::command(async)]
|
||||
pub fn open_url<R: Runtime>(app: AppHandle<R>, url: String) -> Result<(), String> {
|
||||
let allowed = ["http://", "https://", "mailto:"];
|
||||
if !allowed.iter().any(|scheme| url.starts_with(scheme)) {
|
||||
return Err(format!("{url} is not a web address"));
|
||||
}
|
||||
app.opener()
|
||||
.open_url(url, None::<&str>)
|
||||
.map_err(|error| error.to_string())
|
||||
}
|
||||
@@ -50,6 +50,7 @@ pub fn run() {
|
||||
));
|
||||
register_commands(builder)
|
||||
.plugin(tauri_plugin_dialog::init())
|
||||
.plugin(tauri_plugin_opener::init())
|
||||
.setup(|app| {
|
||||
let handle = app.handle().clone();
|
||||
let emit: state::Emit = std::sync::Arc::new(move |event, payload| {
|
||||
@@ -193,6 +194,8 @@ fn register_commands<R: tauri::Runtime>(builder: tauri::Builder<R>) -> tauri::Bu
|
||||
disk::reload_files,
|
||||
disk::export_files,
|
||||
disk::list_folder,
|
||||
// the system
|
||||
system::open_url,
|
||||
])
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import { engine } from '$lib/engine';
|
||||
|
||||
/** Where the pages of the website are, for the links of the desktop app to them. */
|
||||
const website = 'https://gpx.studio';
|
||||
|
||||
let installed = false;
|
||||
|
||||
/** The address to open in the browser for a link: the pages of the website are on the website. */
|
||||
function browserURL(href: string): string {
|
||||
const url = new URL(href, location.href);
|
||||
return url.origin === location.origin
|
||||
? `${website}${url.pathname}${url.search}${url.hash}`
|
||||
: url.href;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the links that leave the app in the browser of the system, as the window of the desktop app
|
||||
* does not follow them: the external links, the links that open a new tab (the pages of the
|
||||
* website, like the help), and what `window.open` is given. The other links are pages of the app.
|
||||
* It is done once, for as long as the window is open.
|
||||
*/
|
||||
export function openLinksInBrowser() {
|
||||
if (installed) {
|
||||
return;
|
||||
}
|
||||
installed = true;
|
||||
const onClick = (e: MouseEvent) => {
|
||||
// the main button, or the middle one which opens a new tab
|
||||
if (e.button > 1) {
|
||||
return;
|
||||
}
|
||||
const link = (e.target as Element | null)?.closest?.('a[href]');
|
||||
if (!(link instanceof HTMLAnchorElement)) {
|
||||
return;
|
||||
}
|
||||
const url = new URL(link.href, location.href);
|
||||
if (url.origin === location.origin && link.target !== '_blank' && e.button === 0) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
engine.openUrl(browserURL(link.href));
|
||||
};
|
||||
window.open = (url?: string | URL) => {
|
||||
if (url !== undefined) {
|
||||
engine.openUrl(browserURL(String(url)));
|
||||
}
|
||||
return null;
|
||||
};
|
||||
// before the router of the app, which would follow the link
|
||||
document.addEventListener('click', onClick, true);
|
||||
document.addEventListener('auxclick', onClick, true);
|
||||
}
|
||||
@@ -221,6 +221,7 @@ export async function createTauriBackend(): Promise<Backend> {
|
||||
exportFiles: (fileIds, options) =>
|
||||
invoke<boolean>('export_files', { fileIds, options }),
|
||||
listFolder: (path) => invoke<FolderEntry[]>('list_folder', { path }),
|
||||
openUrl: (url) => invoke('open_url', { url }),
|
||||
listen: async (onEngineChanged, onWorkspaceChanged, onFilesOpened, onFolderChanged) => {
|
||||
await listen<Outcome>('files-opened', (event) => onFilesOpened(event.payload));
|
||||
await listen<Outcome>('engine-changed', (event) => onEngineChanged(event.payload));
|
||||
|
||||
@@ -89,6 +89,8 @@ export interface DesktopFiles {
|
||||
exportFiles(fileIds: string[], options: ExportOptions): Promise<boolean>;
|
||||
/** The folders and GPX files in a folder of the open folder (the open folder by default). */
|
||||
listFolder(path?: string): Promise<FolderEntry[]>;
|
||||
/** Opens a web page (or an email to write) in the app of the system for it. */
|
||||
openUrl(url: string): Promise<void>;
|
||||
/**
|
||||
* Tells when the engine changed without being asked, because the files on the disk did (the
|
||||
* outcome to apply), when the status of the files changed, when files were opened with the
|
||||
|
||||
@@ -617,6 +617,16 @@ class Engine {
|
||||
await this.enqueue(async () => this.refreshWorkspace(this.backend!));
|
||||
}
|
||||
|
||||
/** Opens a web page in the browser of the system (the desktop app). */
|
||||
async openUrl(url: string) {
|
||||
await this.ready;
|
||||
try {
|
||||
await this.backend!.desktop?.openUrl(url);
|
||||
} catch (error) {
|
||||
console.error('The page could not be opened', error);
|
||||
}
|
||||
}
|
||||
|
||||
/** The folders and GPX files in a folder of the open folder (the open folder by default). */
|
||||
async listFolder(path?: string): Promise<FolderEntry[]> {
|
||||
await this.ready;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
import { get } from 'svelte/store';
|
||||
import { getURLForLanguage } from '$lib/utils';
|
||||
import { inDesktopApp } from '$lib/engine/backend';
|
||||
import { openLinksInBrowser } from '$lib/desktop/links';
|
||||
import { hoveredPoint, slicedStatistics } from '$lib/state/selection-statistics';
|
||||
import { engine } from '$lib/engine';
|
||||
import { boundsManager } from '$lib/state/bounds';
|
||||
@@ -72,6 +73,7 @@
|
||||
settings.connect(await engine.openStorage());
|
||||
|
||||
if (inDesktopApp()) {
|
||||
openLinksInBrowser();
|
||||
// the window of the desktop app always opens the same page, in English: it goes to the
|
||||
// language that was chosen last
|
||||
const saved = get(settings.language);
|
||||
|
||||
Reference in new issue
Block a user