Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src-tauri/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ fn main() {
"cancel_sign_in",
"sign_out",
"delete_account",
"get_settings_sync",
"sync_settings_now",
"resolve_settings_sync",
]);
let mut attributes = tauri_build::Attributes::new().app_manifest(app_manifest);
if std::env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("windows") {
Expand Down
5 changes: 4 additions & 1 deletion src-tauri/capabilities/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@
"allow-verify-sign-in-code",
"allow-cancel-sign-in",
"allow-sign-out",
"allow-delete-account"
"allow-delete-account",
"allow-get-settings-sync",
"allow-sync-settings-now",
"allow-resolve-settings-sync"
]
}
11 changes: 11 additions & 0 deletions src-tauri/permissions/autogenerated/get_settings_sync.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!

[[permission]]
identifier = "allow-get-settings-sync"
description = "Enables the get_settings_sync command without any pre-configured scope."
commands.allow = ["get_settings_sync"]

[[permission]]
identifier = "deny-get-settings-sync"
description = "Denies the get_settings_sync command without any pre-configured scope."
commands.deny = ["get_settings_sync"]
11 changes: 11 additions & 0 deletions src-tauri/permissions/autogenerated/resolve_settings_sync.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!

[[permission]]
identifier = "allow-resolve-settings-sync"
description = "Enables the resolve_settings_sync command without any pre-configured scope."
commands.allow = ["resolve_settings_sync"]

[[permission]]
identifier = "deny-resolve-settings-sync"
description = "Denies the resolve_settings_sync command without any pre-configured scope."
commands.deny = ["resolve_settings_sync"]
11 changes: 11 additions & 0 deletions src-tauri/permissions/autogenerated/sync_settings_now.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!

[[permission]]
identifier = "allow-sync-settings-now"
description = "Enables the sync_settings_now command without any pre-configured scope."
commands.allow = ["sync_settings_now"]

[[permission]]
identifier = "deny-sync-settings-now"
description = "Denies the sync_settings_now command without any pre-configured scope."
commands.deny = ["sync_settings_now"]
10 changes: 8 additions & 2 deletions src-tauri/src/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,6 @@ pub struct AccountView {
/// A usable access token for the signed-in user.
#[derive(Clone)]
pub struct Authorized {
// Read by the sync engine (#986).
#[allow(dead_code)]
pub user_id: String,
pub access_token: String,
}
Expand Down Expand Up @@ -289,6 +287,14 @@ impl Account {
let _ = self.store.delete();
}

/// The project URL and publishable key, for the database requests made by
/// settings sync with an `Authorized` token.
pub fn endpoint(&self) -> Option<(String, String)> {
self.config
.as_ref()
.map(|config| (config.url.clone(), config.key.clone()))
}

fn config(&self) -> Result<&Config, String> {
self.config
.as_ref()
Expand Down
61 changes: 58 additions & 3 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ mod scan_tile;
mod scan_tree;
mod scanning;
mod scanning_runtime;
mod settings_sync;
mod state;
mod storage;
mod switch_gestures;
Expand Down Expand Up @@ -826,8 +827,20 @@ fn delete_switch_profile(
}

#[tauri::command]
async fn get_account(account: State<'_, account::Account>) -> Result<account::AccountView, String> {
Ok(account.view().await)
async fn get_account(
app: AppHandle,
account: State<'_, account::Account>,
) -> Result<account::AccountView, String> {
let view = account.view().await;
// Opening the tab can find a session a locked keychain hid at startup.
if view.signed_in {
if let Some(sync) = sync_engine(&app) {
if sync.view().status == settings_sync::Status::Off {
sync.wake();
}
}
}
Ok(view)
}

#[tauri::command]
Expand All @@ -851,6 +864,11 @@ async fn verify_sign_in_code(
let result = account.verify_code(&code).await;
// Emitted on failure too: a rejected session signs out mid-action.
account::emit(&app, &account.current_view().await);
if result.is_ok() {
if let Some(sync) = app.try_state::<std::sync::Arc<settings_sync::Engine>>() {
sync.wake();
}
}
result
}

Expand All @@ -872,6 +890,9 @@ async fn sign_out(
let result = account.sign_out().await;
// Emitted on failure too: a rejected session signs out mid-action.
account::emit(&app, &account.current_view().await);
if let Some(sync) = app.try_state::<std::sync::Arc<settings_sync::Engine>>() {
sync.wake();
}
result
}

Expand All @@ -883,9 +904,39 @@ async fn delete_account(
let result = account.delete_account().await;
// Emitted on failure too: a rejected session signs out mid-action.
account::emit(&app, &account.current_view().await);
if result.is_ok() {
if let Some(sync) = sync_engine(&app) {
sync.forget().await;
}
}
result
}

fn sync_engine(app: &AppHandle) -> Option<std::sync::Arc<settings_sync::Engine>> {
app.try_state::<std::sync::Arc<settings_sync::Engine>>()
.map(|engine| engine.inner().clone())
}

#[tauri::command]
fn get_settings_sync(app: AppHandle) -> settings_sync::SyncView {
sync_engine(&app).map_or_else(settings_sync::SyncView::unavailable, |engine| engine.view())
}

#[tauri::command]
async fn sync_settings_now(app: AppHandle) -> Result<settings_sync::SyncView, String> {
let engine = sync_engine(&app).ok_or("Settings sync is unavailable in this build.")?;
Ok(engine.sync(None).await)
}

#[tauri::command]
async fn resolve_settings_sync(
app: AppHandle,
choice: settings_sync::Choice,
) -> Result<settings_sync::SyncView, String> {
let engine = sync_engine(&app).ok_or("Settings sync is unavailable in this build.")?;
Ok(engine.sync(Some(choice)).await)
}

fn publish_update(app: &AppHandle, model: &AppModel, update: UpdateView) -> AppState {
model
.shared
Expand Down Expand Up @@ -1588,6 +1639,7 @@ pub fn run() {
switch_runtime::install(app.handle());
remote_scan::install(app.handle());
point_scan_runtime::install(app.handle());
settings_sync::install(app.handle());
install_tray(app)?;
if updater_is_configured(app.config().plugins.0.get("updater")) {
let model = app.state::<AppModel>();
Expand Down Expand Up @@ -1719,7 +1771,10 @@ pub fn run() {
verify_sign_in_code,
cancel_sign_in,
sign_out,
delete_account
delete_account,
get_settings_sync,
sync_settings_now,
resolve_settings_sync
])
.run(tauri::generate_context!())
.expect("error while running Switchify PC");
Expand Down
13 changes: 8 additions & 5 deletions src-tauri/src/portable_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
//! registration never leave this install. Incoming documents are fully
//! validated before anything is applied, and each section is applied through
//! the same path as a local save so runtime state and overlays stay in step.
// Wired up by the sync engine (#986).
#![allow(dead_code)]

use crate::{
point_scan, remote_scan,
Expand Down Expand Up @@ -224,6 +222,7 @@ pub struct Plan {
}

impl Plan {
#[cfg(test)]
pub fn is_empty(&self) -> bool {
*self == Self::default()
}
Expand Down Expand Up @@ -268,8 +267,9 @@ pub fn plan(local: &Local, incoming: Document) -> Result<Plan, String> {

/// Replaces the custom profiles with the incoming set, keeping built-ins.
/// A changed profile gets a version above both copies so connected phones
/// notice the new bindings. Returns `None` when nothing would change.
fn merge_profiles(
/// notice the new bindings; an unchanged one keeps the higher of the two.
/// Returns `None` when nothing would change.
pub(crate) fn merge_profiles(
local: &[SwitchProfile],
mut incoming: Vec<SwitchProfile>,
) -> Result<Option<Vec<SwitchProfile>>, String> {
Expand Down Expand Up @@ -299,8 +299,11 @@ fn merge_profiles(
.iter()
.find(|candidate| candidate.id == profile.id && !candidate.built_in)
{
// Same content: never lower the version, so computers that synced
// the same profile converge on one version instead of trading
// their own back and forth.
profile.version = if same_profile(existing, &profile) {
existing.version
existing.version.max(profile.version)
} else {
profile.version.max(existing.version.saturating_add(1))
};
Expand Down
Loading
Loading