mirror of
https://github.com/storytold/figcraft.git
synced 2026-11-03 00:39:44 -06:00
Merge fix/712
This commit is contained in:
@@ -14,7 +14,16 @@
|
||||
//! join from other machines.
|
||||
//!
|
||||
//! URL query flag: `?webgl` forces the WebGL2 backend instead of WebGPU.
|
||||
//!
|
||||
//! URL fragment: `#join=<invite link or join code>` joins that live session once the page has
|
||||
//! loaded, so a bookmark opens a shared file. `join` takes the rest of the fragment: put it last.
|
||||
//! The link carries the session's secret; a fragment is never sent to the web host, but the address
|
||||
//! bar and the browser history keep it, so the URL is as private as the link itself.
|
||||
|
||||
#[cfg(any(target_arch = "wasm32", test))]
|
||||
mod query;
|
||||
#[cfg(test)]
|
||||
mod query_tests;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
mod web;
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
//! The page's URL: the invite link its fragment names.
|
||||
|
||||
/// The invite link in `#join=<link>` (`fragment` is `location.hash`, with or without its `#`), still
|
||||
/// escaped. The fragment, unlike the query, never leaves the browser, so the link's secret stays out
|
||||
/// of the web host's logs and `Referer` headers. `join` takes the rest of the fragment, so a pasted
|
||||
/// `figcraft://join?host=…&file=…&token=…` keeps its own `&`s. Anything longer than an escaped invite
|
||||
/// link can be is ignored; what is left is checked by `collab.join` like a link the desktop app is
|
||||
/// opened with.
|
||||
pub fn join_link(fragment: &str) -> Option<&str> {
|
||||
// An invite link is at most 2 KiB; every byte of it escaped is three.
|
||||
const MAX: usize = 3 * 2048;
|
||||
let fragment = fragment.strip_prefix('#').unwrap_or(fragment);
|
||||
let start = if fragment.starts_with("join=") { 0 } else { fragment.find("&join=")? + 1 };
|
||||
fragment.get(start + "join=".len()..).filter(|link| !link.is_empty() && link.len() <= MAX)
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
//! `#join=` in the page's URL.
|
||||
|
||||
use crate::query::join_link;
|
||||
|
||||
#[test]
|
||||
fn join_takes_the_rest_of_the_fragment() {
|
||||
let link = "figcraft://join?host=192.168.1.20:47900&file=abc&token=AbCdEfGhIjKlMnOpQrStUv";
|
||||
assert_eq!(join_link(&format!("#join={link}")), Some(link), "a pasted link keeps its own &s");
|
||||
assert_eq!(join_link(&format!("#x=1&join={link}")), Some(link), "after other entries");
|
||||
assert_eq!(join_link("#join=figcraft%3A%2F%2Fjoin%3Fhost%3Dh"), Some("figcraft%3A%2F%2Fjoin%3Fhost%3Dh"), "escaped, decoded later");
|
||||
assert_eq!(join_link("join=K7M-2QX-9TD"), Some("K7M-2QX-9TD"), "a join code, no leading #");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_join_without_the_entry() {
|
||||
assert_eq!(join_link(""), None);
|
||||
assert_eq!(join_link("#"), None);
|
||||
assert_eq!(join_link("#join="), None);
|
||||
assert_eq!(join_link("#rejoin=x"), None, "only the whole entry name");
|
||||
assert_eq!(join_link("#xjoin=x&a=1"), None);
|
||||
assert_eq!(join_link("?join=x"), None, "the query isn't read: it goes to the web host");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_overlong_link_is_ignored() {
|
||||
let long = format!("#join=figcraft://join?host=h&file=f&token={}", "a".repeat(7000));
|
||||
assert_eq!(join_link(&long), None);
|
||||
assert_eq!(join_link(&format!("#join={}", "é".repeat(4000))), None, "length counts bytes");
|
||||
}
|
||||
@@ -40,7 +40,13 @@ pub fn start() {
|
||||
// on the socket wakes the page for a frame.
|
||||
let ctx = cc.egui_ctx.clone();
|
||||
session.live.install(Box::new(figcraft_collab_net::NetLink::new(std::sync::Arc::new(move || ctx.request_repaint()))));
|
||||
let fig = FigApp::new(session, Box::new(WebServices { reduce_motion, ctx: cc.egui_ctx.clone() }));
|
||||
let mut fig = FigApp::new(session, Box::new(WebServices { reduce_motion, ctx: cc.egui_ctx.clone() }));
|
||||
// `#join=<link>`: join that session, as opening an invite link does on the desktop.
|
||||
let hash = web_sys::window().and_then(|w| w.location().hash().ok()).unwrap_or_default();
|
||||
if let Some(link) = crate::query::join_link(&hash) {
|
||||
let link = js_sys::decode_uri_component(link).ok().and_then(|s| s.as_string()).unwrap_or_else(|| link.to_string());
|
||||
figcraft_ui_egui::live::open_invite(&mut fig, &link);
|
||||
}
|
||||
Ok(Box::new(WebShell { fig }))
|
||||
}),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user