mirror of
https://github.com/l0ng-ai/tty7.git
synced 2026-11-06 00:15:06 +00:00
fix(mobile): pairing links without the deep-link plugin, cold launches included
The deep-link plugin's build script rewrites the iOS entitlements while
Xcode builds, which Xcode refuses ("modified during the build"), and a
link that launched the app was lost on the way in any case: UIKit hands
it to the first scene as it connects, and the event loop only passes on
links that come later.
- The tty7pair scheme is declared in Info.ios.plist and the Android
manifest, and links are heard as Tauri's own Opened event.
- The scene delegate's connect is wrapped, before UIKit starts, to keep
a launching link; the page asks for it once it listens, so neither
order of arrival loses it.
This commit is contained in:
Generated
-10
@@ -11,7 +11,6 @@
|
||||
"@tauri-apps/api": "^2.11.1",
|
||||
"@tauri-apps/plugin-barcode-scanner": "^2.5.0",
|
||||
"@tauri-apps/plugin-biometric": "^2.4.0",
|
||||
"@tauri-apps/plugin-deep-link": "^2.6.1",
|
||||
"@tauri-apps/plugin-haptics": "^2.4.1",
|
||||
"@tauri-apps/plugin-opener": "^2.6.0",
|
||||
"@xterm/addon-search": "^0.16.0",
|
||||
@@ -541,15 +540,6 @@
|
||||
"@tauri-apps/api": "^2.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/plugin-deep-link": {
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://mirrors.cloud.tencent.com/npm/@tauri-apps/plugin-deep-link/-/plugin-deep-link-2.6.1.tgz",
|
||||
"integrity": "sha512-yomIOIoiiDwXW3Bgy/V5c4frWo9sfWM9WZQew2s7vr7hXtDdbLDCLzvytIMCjMxbnln9O0oJsTI3XdDd2T9XlA==",
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^2.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/plugin-haptics": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://mirrors.cloud.tencent.com/npm/@tauri-apps/plugin-haptics/-/plugin-haptics-2.4.1.tgz",
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
"@tauri-apps/api": "^2.11.1",
|
||||
"@tauri-apps/plugin-barcode-scanner": "^2.5.0",
|
||||
"@tauri-apps/plugin-biometric": "^2.4.0",
|
||||
"@tauri-apps/plugin-deep-link": "^2.6.1",
|
||||
"@tauri-apps/plugin-haptics": "^2.4.1",
|
||||
"@tauri-apps/plugin-opener": "^2.6.0",
|
||||
"@xterm/addon-search": "^0.16.0",
|
||||
|
||||
Generated
-92
@@ -672,26 +672,6 @@ version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c"
|
||||
|
||||
[[package]]
|
||||
name = "const-random"
|
||||
version = "0.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359"
|
||||
dependencies = [
|
||||
"const-random-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const-random-macro"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e"
|
||||
dependencies = [
|
||||
"getrandom 0.2.17",
|
||||
"once_cell",
|
||||
"tiny-keccak",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "constant_time_eq"
|
||||
version = "0.4.2"
|
||||
@@ -815,12 +795,6 @@ version = "0.8.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a31eee39dddec8330830986fcd7625edb5a24ec90ea038215273bbc3adb08ac6"
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.7"
|
||||
@@ -1155,15 +1129,6 @@ dependencies = [
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dlv-list"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f"
|
||||
dependencies = [
|
||||
"const-random",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dom_query"
|
||||
version = "0.28.0"
|
||||
@@ -1927,12 +1892,6 @@ version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.17.1"
|
||||
@@ -3636,16 +3595,6 @@ version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
||||
|
||||
[[package]]
|
||||
name = "ordered-multimap"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79"
|
||||
dependencies = [
|
||||
"dlv-list",
|
||||
"hashbrown 0.14.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ordered-stream"
|
||||
version = "0.2.0"
|
||||
@@ -4259,16 +4208,6 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-ini"
|
||||
version = "0.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "796e8d2b6696392a43bea58116b667fb4c29727dc5abd27d6acf338bb4f688c7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"ordered-multimap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "2.1.3"
|
||||
@@ -5306,27 +5245,6 @@ dependencies = [
|
||||
"thiserror 2.0.21",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-deep-link"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84d6b3bf88f2ec8b97c852c2fa1464dc13c0ba3fbfb24af4144f23fbd54c0f14"
|
||||
dependencies = [
|
||||
"dunce",
|
||||
"plist",
|
||||
"rust-ini",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"tauri-utils",
|
||||
"thiserror 2.0.21",
|
||||
"tracing",
|
||||
"url",
|
||||
"windows-registry",
|
||||
"windows-result 0.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-haptics"
|
||||
version = "2.4.0"
|
||||
@@ -5565,15 +5483,6 @@ dependencies = [
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiny-keccak"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
|
||||
dependencies = [
|
||||
"crunchy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinystr"
|
||||
version = "0.8.4"
|
||||
@@ -5924,7 +5833,6 @@ dependencies = [
|
||||
"tauri-build",
|
||||
"tauri-plugin-barcode-scanner",
|
||||
"tauri-plugin-biometric",
|
||||
"tauri-plugin-deep-link",
|
||||
"tauri-plugin-haptics",
|
||||
"tauri-plugin-opener",
|
||||
"tokio",
|
||||
|
||||
@@ -40,9 +40,6 @@ tauri-plugin-biometric = "2"
|
||||
# A tap felt under the finger for the keys and answers, as the phone's own
|
||||
# keyboard gives.
|
||||
tauri-plugin-haptics = "2"
|
||||
# The desktop's pairing QR code is a `tty7pair:` link: the phone's own
|
||||
# camera opens it straight into the app, which pairs.
|
||||
tauri-plugin-deep-link = "2"
|
||||
|
||||
# Reading the system bars' size and leaving for the background (`insets`,
|
||||
# `to_background`), which the Tauri API does not reach.
|
||||
|
||||
@@ -24,5 +24,18 @@
|
||||
compliance question on every TestFlight upload. -->
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<!-- The desktop's pairing code is a tty7pair: link, so the phone's Camera
|
||||
opens it in tty7 (lib.rs `opened`, `launch_link`). -->
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>dev.tty7.mobile.pair</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>tty7pair</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
"permissions": [
|
||||
"barcode-scanner:default",
|
||||
"biometric:default",
|
||||
"haptics:default",
|
||||
"deep-link:default"
|
||||
"haptics:default"
|
||||
]
|
||||
}
|
||||
@@ -22,21 +22,13 @@
|
||||
<!-- AndroidTV support -->
|
||||
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
|
||||
</intent-filter>
|
||||
<!-- DEEP LINK PLUGIN. AUTO-GENERATED. DO NOT REMOVE. -->
|
||||
<intent-filter >
|
||||
<!-- The desktop's pairing code is a tty7pair: link (lib.rs `opened`). -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<!-- ChromeOS ARC++ uses a different action for deep links -->
|
||||
<action android:name="org.chromium.arc.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="tty7pair" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</intent-filter>
|
||||
<!-- DEEP LINK PLUGIN. AUTO-GENERATED. DO NOT REMOVE. -->
|
||||
</activity>
|
||||
|
||||
<provider
|
||||
|
||||
+157
-5
@@ -527,6 +527,8 @@ pub fn run() {
|
||||
// unless a process-wide provider is installed first. `ring` is the one
|
||||
// already in the tree. An `Err` means one is installed, which is fine.
|
||||
let _ = rustls::crypto::ring::default_provider().install_default();
|
||||
#[cfg(target_os = "ios")]
|
||||
launch_link::catch();
|
||||
|
||||
// Links tapped in a pane open in the phone's browser.
|
||||
let builder = tauri::Builder::default().plugin(tauri_plugin_opener::init());
|
||||
@@ -536,8 +538,7 @@ pub fn run() {
|
||||
let builder = builder
|
||||
.plugin(tauri_plugin_barcode_scanner::init())
|
||||
.plugin(tauri_plugin_biometric::init())
|
||||
.plugin(tauri_plugin_haptics::init())
|
||||
.plugin(tauri_plugin_deep_link::init());
|
||||
.plugin(tauri_plugin_haptics::init());
|
||||
|
||||
builder
|
||||
.setup(|app| {
|
||||
@@ -575,10 +576,161 @@ pub fn run() {
|
||||
pane_close,
|
||||
appearance,
|
||||
insets,
|
||||
to_background
|
||||
to_background,
|
||||
opened_link
|
||||
])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tty7");
|
||||
.build(tauri::generate_context!())
|
||||
.expect("error while building tty7")
|
||||
.run(|_app, _event| {
|
||||
#[cfg(any(target_os = "ios", target_os = "android"))]
|
||||
if let tauri::RunEvent::Opened { urls } = _event {
|
||||
opened(_app, urls);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// A link the app was opened with that the page has not taken yet: one that
|
||||
/// launched the app arrives before the page is there to hear it.
|
||||
static OPENED: std::sync::Mutex<Option<String>> = std::sync::Mutex::new(None);
|
||||
|
||||
/// The desktop's pairing code is a `tty7pair:` link, so a phone's camera
|
||||
/// pointed at its QR code opens the app with it (`Info.ios.plist`, the
|
||||
/// Android manifest). Kept for the page to ask for, and told to it if it is
|
||||
/// already listening.
|
||||
#[cfg(any(target_os = "ios", target_os = "android"))]
|
||||
fn opened(app: &tauri::AppHandle, urls: Vec<tauri::Url>) {
|
||||
use tauri::Emitter as _;
|
||||
let Some(link) = urls
|
||||
.into_iter()
|
||||
.map(String::from)
|
||||
.find(|u| u.starts_with("tty7pair:"))
|
||||
else {
|
||||
return;
|
||||
};
|
||||
*OPENED.lock().unwrap_or_else(|e| e.into_inner()) = Some(link.clone());
|
||||
let _ = app.emit("opened-link", link);
|
||||
}
|
||||
|
||||
/// A link that launches the app on iOS. UIKit hands it to the app's first
|
||||
/// scene as that connects, in the connection options, and the event loop
|
||||
/// (tao) passes on only the links that come once the scene is up. So its
|
||||
/// scene delegate's connect is wrapped to keep the link on the way. The
|
||||
/// delegate class exists once the app delegate does, which is installed as
|
||||
/// UIKit starts: the wrap goes on then.
|
||||
#[cfg(target_os = "ios")]
|
||||
mod launch_link {
|
||||
use objc2::msg_send;
|
||||
use objc2::runtime::{AnyClass, AnyObject, Imp, Sel};
|
||||
use objc2::sel;
|
||||
use std::ffi::{CStr, c_char};
|
||||
use std::sync::OnceLock;
|
||||
|
||||
static SET_DELEGATE: OnceLock<usize> = OnceLock::new();
|
||||
static CONNECT: OnceLock<usize> = OnceLock::new();
|
||||
|
||||
type SetDelegate = unsafe extern "C-unwind" fn(*mut AnyObject, Sel, *mut AnyObject);
|
||||
type Connect = unsafe extern "C-unwind" fn(
|
||||
*mut AnyObject,
|
||||
Sel,
|
||||
*mut AnyObject,
|
||||
*mut AnyObject,
|
||||
*mut AnyObject,
|
||||
);
|
||||
|
||||
/// Swaps `selector`'s implementation on `class` for `imp`, keeping the
|
||||
/// one it had in `previous`.
|
||||
unsafe fn wrap(class: *const AnyClass, selector: Sel, imp: Imp, previous: &OnceLock<usize>) {
|
||||
unsafe {
|
||||
let method = objc2::ffi::class_getInstanceMethod(class, selector);
|
||||
if method.is_null() || previous.get().is_some() {
|
||||
return;
|
||||
}
|
||||
if let Some(original) = objc2::ffi::method_setImplementation(method as *mut _, imp) {
|
||||
let _ = previous.set(original as usize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn catch() {
|
||||
if let Some(app) = AnyClass::get(c"UIApplication") {
|
||||
unsafe {
|
||||
let imp: Imp = std::mem::transmute(set_delegate as SetDelegate);
|
||||
wrap(app, sel!(setDelegate:), imp, &SET_DELEGATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn set_delegate(
|
||||
this: *mut AnyObject,
|
||||
cmd: Sel,
|
||||
delegate: *mut AnyObject,
|
||||
) {
|
||||
unsafe {
|
||||
if let Some(&previous) = SET_DELEGATE.get() {
|
||||
let previous: SetDelegate = std::mem::transmute(previous);
|
||||
previous(this, cmd, delegate);
|
||||
}
|
||||
if let Some(scene) = AnyClass::get(c"TaoSceneDelegate") {
|
||||
let imp: Imp = std::mem::transmute(connect as Connect);
|
||||
wrap(
|
||||
scene,
|
||||
sel!(scene:willConnectToSession:options:),
|
||||
imp,
|
||||
&CONNECT,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn connect(
|
||||
this: *mut AnyObject,
|
||||
cmd: Sel,
|
||||
scene: *mut AnyObject,
|
||||
session: *mut AnyObject,
|
||||
options: *mut AnyObject,
|
||||
) {
|
||||
unsafe {
|
||||
if !options.is_null() {
|
||||
let contexts: *mut AnyObject = msg_send![options, URLContexts];
|
||||
let context: *mut AnyObject = if contexts.is_null() {
|
||||
std::ptr::null_mut()
|
||||
} else {
|
||||
msg_send![contexts, anyObject]
|
||||
};
|
||||
let url: *mut AnyObject = if context.is_null() {
|
||||
std::ptr::null_mut()
|
||||
} else {
|
||||
msg_send![context, URL]
|
||||
};
|
||||
let text: *mut AnyObject = if url.is_null() {
|
||||
std::ptr::null_mut()
|
||||
} else {
|
||||
msg_send![url, absoluteString]
|
||||
};
|
||||
let utf8: *const c_char = if text.is_null() {
|
||||
std::ptr::null()
|
||||
} else {
|
||||
msg_send![text, UTF8String]
|
||||
};
|
||||
if !utf8.is_null() {
|
||||
let link = CStr::from_ptr(utf8).to_string_lossy().into_owned();
|
||||
if link.starts_with("tty7pair:") {
|
||||
*super::OPENED.lock().unwrap_or_else(|e| e.into_inner()) = Some(link);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(&previous) = CONNECT.get() {
|
||||
let previous: Connect = std::mem::transmute(previous);
|
||||
previous(this, cmd, scene, session, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The link the app was opened with, once.
|
||||
#[tauri::command]
|
||||
fn opened_link() -> Option<String> {
|
||||
OPENED.lock().unwrap_or_else(|e| e.into_inner()).take()
|
||||
}
|
||||
|
||||
/// Light, dark or the system's, for what the page does not draw itself: the
|
||||
|
||||
@@ -22,11 +22,6 @@
|
||||
"csp": "default-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src ipc: http://ipc.localhost"
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
"deep-link": {
|
||||
"mobile": [{ "scheme": ["tty7pair"], "appLink": false }]
|
||||
}
|
||||
},
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"targets": "all",
|
||||
|
||||
@@ -109,6 +109,9 @@ export interface Insets {
|
||||
/** What the system bars cover, in CSS pixels. Android only: zero elsewhere. */
|
||||
export const insets = () => invoke<Insets>("insets");
|
||||
|
||||
/** The `tty7pair:` link the app was opened with and has not handled, once. */
|
||||
export const openedLink = () => invoke<string | null>("opened_link");
|
||||
|
||||
/** Sends the app to the background, as Back from the first screen does. */
|
||||
export const toBackground = () => invoke<void>("to_background");
|
||||
|
||||
|
||||
+39
-18
@@ -10,8 +10,8 @@ import type { ITheme } from "@xterm/xterm";
|
||||
import * as scanner from "@tauri-apps/plugin-barcode-scanner";
|
||||
|
||||
import { getVersion, onBackButtonPress } from "@tauri-apps/api/app";
|
||||
import { listen } from "@tauri-apps/api/event";
|
||||
import { impactFeedback, selectionFeedback } from "@tauri-apps/plugin-haptics";
|
||||
import { getCurrent as linkedWith, onOpenUrl } from "@tauri-apps/plugin-deep-link";
|
||||
|
||||
import * as api from "./api";
|
||||
import { MAX_UPLOAD } from "./api";
|
||||
@@ -1112,7 +1112,13 @@ function hostScreen(host: Host, direction: "push" | "pop" = "pop") {
|
||||
draw();
|
||||
};
|
||||
const draw = () => {
|
||||
if (lastTree) body.replaceChildren(...renderTree(host, lastTree, query, picked, pick));
|
||||
if (lastTree)
|
||||
body.replaceChildren(
|
||||
...renderTree(host, lastTree, query, picked, pick, (key) => {
|
||||
picked = key;
|
||||
remember(`workspace.${host.id}`, key);
|
||||
}),
|
||||
);
|
||||
};
|
||||
const dock = floatingBar("Search tabs", (q) => {
|
||||
query = q;
|
||||
@@ -1344,6 +1350,7 @@ function renderTree(
|
||||
query: string,
|
||||
picked: string | null,
|
||||
pick: (key: string) => void,
|
||||
keep: (key: string) => void,
|
||||
): Node[] {
|
||||
if (query) {
|
||||
const groups = [
|
||||
@@ -1393,6 +1400,9 @@ function renderTree(
|
||||
}),
|
||||
];
|
||||
const on = spaces.find((sp) => sp.key === picked) ?? spaces[0];
|
||||
// What is on screen stays on screen: a workspace the desktop opens or
|
||||
// turns to later does not take its place under the reader's thumb.
|
||||
if (on.key !== picked) keep(on.key);
|
||||
|
||||
const out: Node[] = [];
|
||||
if (spaces.length > 1) {
|
||||
@@ -3585,24 +3595,35 @@ function terminalScreen(host: Host, place: Place, pane: PaneView, title: string,
|
||||
if (prefs.lock) lock();
|
||||
|
||||
// A pairing code opened as a link — the desktop's QR code, read by the
|
||||
// phone's camera — goes straight to pairing.
|
||||
const pairingLink = (urls: string[] | null) => urls?.find((url) => url.startsWith("tty7pair:"));
|
||||
void onOpenUrl((urls) => {
|
||||
const code = pairingLink(urls);
|
||||
if (code) pairScreen(code);
|
||||
}).catch(() => {});
|
||||
// phone's camera — goes straight to pairing. The link is taken once, from
|
||||
// the native side, which holds it until asked: one that launched the app
|
||||
// can arrive before the page, or between the page's first look and its
|
||||
// first screen.
|
||||
let started = false;
|
||||
const takeLink = () =>
|
||||
api.openedLink().then(
|
||||
(code) => code && pairScreen(code),
|
||||
() => {},
|
||||
);
|
||||
const listening = listen("opened-link", () => started && void takeLink()).catch(() => {});
|
||||
|
||||
// Back where it was left: the machine last open, unless it was left for the
|
||||
// list of machines; or pairing, when that is what opened the app.
|
||||
Promise.all([api.hosts(), linkedWith().catch(() => null)]).then(
|
||||
([hosts, urls]) => {
|
||||
const code = pairingLink(urls);
|
||||
const last = hosts.find((host) => host.id === remembered("last.host"));
|
||||
if (code) {
|
||||
void listening.then(() =>
|
||||
Promise.all([api.hosts(), api.openedLink().catch(() => null)]).then(
|
||||
([hosts, code]) => {
|
||||
const last = hosts.find((host) => host.id === remembered("last.host"));
|
||||
if (code) {
|
||||
hostsScreen("push");
|
||||
pairScreen(code);
|
||||
} else if (last) hostScreen(last, "push");
|
||||
else hostsScreen("push");
|
||||
started = true;
|
||||
void takeLink();
|
||||
},
|
||||
() => {
|
||||
hostsScreen("push");
|
||||
pairScreen(code);
|
||||
} else if (last) hostScreen(last, "push");
|
||||
else hostsScreen("push");
|
||||
},
|
||||
() => hostsScreen("push"),
|
||||
started = true;
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user