| 114 |
114 |
|
// rebuilt on device loss (see AudioFilesApp::poll_audio_device).
|
| 115 |
115 |
|
let initial_stream = match audio::start_output_stream(shared.clone()) {
|
| 116 |
116 |
|
Ok((stream, device_rate, device_name)) => {
|
| 117 |
|
- |
shared.device_sample_rate.store(device_rate, std::sync::atomic::Ordering::Relaxed);
|
|
117 |
+ |
shared
|
|
118 |
+ |
.device_sample_rate
|
|
119 |
+ |
.store(device_rate, std::sync::atomic::Ordering::Relaxed);
|
| 118 |
120 |
|
*shared.preview_device_name.lock() = Some(device_name);
|
| 119 |
121 |
|
Some(stream)
|
| 120 |
122 |
|
}
|
| 155 |
157 |
|
Box::new(move |cc| {
|
| 156 |
158 |
|
audiofiles_browser::ui::theme::setup_fonts(&cc.egui_ctx);
|
| 157 |
159 |
|
Ok(Box::new(AudioFilesApp::new(
|
| 158 |
|
- |
config_dir, shared, app_tray, update_checker, prefs, runtime, gtk_ok,
|
|
160 |
+ |
config_dir,
|
|
161 |
+ |
shared,
|
|
162 |
+ |
app_tray,
|
|
163 |
+ |
update_checker,
|
|
164 |
+ |
prefs,
|
|
165 |
+ |
runtime,
|
|
166 |
+ |
gtk_ok,
|
| 159 |
167 |
|
initial_stream,
|
| 160 |
168 |
|
)))
|
| 161 |
169 |
|
}),
|
| 165 |
173 |
|
// ── API key persistence ──
|
| 166 |
174 |
|
|
| 167 |
175 |
|
/// Create a SyncManager from a saved or env-provided API key.
|
| 168 |
|
- |
fn create_sync_manager(
|
| 169 |
|
- |
data_dir: &Path,
|
| 170 |
|
- |
runtime: &tokio::runtime::Handle,
|
| 171 |
|
- |
) -> Option<SyncManager> {
|
|
176 |
+ |
fn create_sync_manager(data_dir: &Path, runtime: &tokio::runtime::Handle) -> Option<SyncManager> {
|
| 172 |
177 |
|
let api_key = api_key::load_api_key(data_dir)?;
|
| 173 |
|
- |
let server_url = std::env::var("AF_SYNC_SERVER_URL")
|
| 174 |
|
- |
.unwrap_or_else(|_| SYNC_SERVER_URL.to_string());
|
|
178 |
+ |
let server_url =
|
|
179 |
+ |
std::env::var("AF_SYNC_SERVER_URL").unwrap_or_else(|_| SYNC_SERVER_URL.to_string());
|
| 175 |
180 |
|
let config = SyncKitConfig {
|
| 176 |
181 |
|
server_url,
|
| 177 |
182 |
|
api_key,
|
| 208 |
213 |
|
license_status: &license::LicenseStatus,
|
| 209 |
214 |
|
has_trial: bool,
|
| 210 |
215 |
|
) -> AppScreen {
|
| 211 |
|
- |
let licensed_or_trial = matches!(license_status, license::LicenseStatus::Licensed(_)) || has_trial;
|
|
216 |
+ |
let licensed_or_trial =
|
|
217 |
+ |
matches!(license_status, license::LicenseStatus::Licensed(_)) || has_trial;
|
| 212 |
218 |
|
match (vault_registry, licensed_or_trial) {
|
| 213 |
219 |
|
(Some(_), true) => AppScreen::Browser,
|
| 214 |
220 |
|
(Some(_), false) => AppScreen::Activation,
|
| 293 |
299 |
|
}
|
| 294 |
300 |
|
};
|
| 295 |
301 |
|
|
| 296 |
|
- |
let has_active_trial = trial_state.as_ref().is_some_and(|t| license::trial_days_remaining(t) > 0);
|
|
302 |
+ |
let has_active_trial = trial_state
|
|
303 |
+ |
.as_ref()
|
|
304 |
+ |
.is_some_and(|t| license::trial_days_remaining(t) > 0);
|
| 297 |
305 |
|
let screen = resolve_initial_screen(&vault_registry, &license_status, has_active_trial);
|
| 298 |
306 |
|
|
| 299 |
|
- |
let licensed_or_trial = matches!(&license_status, license::LicenseStatus::Licensed(_)) || has_active_trial;
|
|
307 |
+ |
let licensed_or_trial =
|
|
308 |
+ |
matches!(&license_status, license::LicenseStatus::Licensed(_)) || has_active_trial;
|
| 300 |
309 |
|
|
| 301 |
310 |
|
let (data_dir, browser, error, sync_manager, license_cache) =
|
| 302 |
311 |
|
match (&vault_registry, &license_status) {
|
| 305 |
314 |
|
let data_dir = reg.active.clone();
|
| 306 |
315 |
|
let _ = std::fs::create_dir_all(&data_dir);
|
| 307 |
316 |
|
let sync_manager = create_sync_manager(&data_dir, runtime.handle());
|
| 308 |
|
- |
let (browser, error) = init_browser(&data_dir, shared.clone(), &vault_setup::vault_name_for_path(reg, &data_dir));
|
|
317 |
+ |
let (browser, error) = init_browser(
|
|
318 |
+ |
&data_dir,
|
|
319 |
+ |
shared.clone(),
|
|
320 |
+ |
&vault_setup::vault_name_for_path(reg, &data_dir),
|
|
321 |
+ |
);
|
| 309 |
322 |
|
(data_dir, browser, error, sync_manager, Some(cache.clone()))
|
| 310 |
323 |
|
}
|
| 311 |
324 |
|
// Registry exists, unlicensed but in trial → open the active vault
|
| 313 |
326 |
|
let data_dir = reg.active.clone();
|
| 314 |
327 |
|
let _ = std::fs::create_dir_all(&data_dir);
|
| 315 |
328 |
|
let sync_manager = create_sync_manager(&data_dir, runtime.handle());
|
| 316 |
|
- |
let (browser, error) = init_browser(&data_dir, shared.clone(), &vault_setup::vault_name_for_path(reg, &data_dir));
|
|
329 |
+ |
let (browser, error) = init_browser(
|
|
330 |
+ |
&data_dir,
|
|
331 |
+ |
shared.clone(),
|
|
332 |
+ |
&vault_setup::vault_name_for_path(reg, &data_dir),
|
|
333 |
+ |
);
|
| 317 |
334 |
|
(data_dir, browser, error, sync_manager, None)
|
| 318 |
335 |
|
}
|
| 319 |
336 |
|
// Registry exists but unlicensed (deactivated and reactivated)
|
| 394 |
411 |
|
self.audio_stream = None;
|
| 395 |
412 |
|
match audio::start_output_stream(self.shared.clone()) {
|
| 396 |
413 |
|
Ok((stream, rate, name)) => {
|
| 397 |
|
- |
self.shared.device_sample_rate.store(rate, Ordering::Relaxed);
|
|
414 |
+ |
self.shared
|
|
415 |
+ |
.device_sample_rate
|
|
416 |
+ |
.store(rate, Ordering::Relaxed);
|
| 398 |
417 |
|
*self.shared.preview_device_name.lock() = Some(name);
|
| 399 |
418 |
|
self.shared.device_lost.store(false, Ordering::Relaxed);
|
| 400 |
419 |
|
self.audio_stream = Some(stream);
|
| 412 |
431 |
|
fn activate_browser(&mut self) {
|
| 413 |
432 |
|
let _ = std::fs::create_dir_all(&self.data_dir);
|
| 414 |
433 |
|
self.sync_manager = create_sync_manager(&self.data_dir, self._runtime.handle());
|
| 415 |
|
- |
let vault_name = self.vault_registry.as_ref()
|
|
434 |
+ |
let vault_name = self
|
|
435 |
+ |
.vault_registry
|
|
436 |
+ |
.as_ref()
|
| 416 |
437 |
|
.map(|r| vault_setup::vault_name_for_path(r, &self.data_dir))
|
| 417 |
438 |
|
.unwrap_or_else(|| "Library".to_string());
|
| 418 |
439 |
|
let (browser, error) = init_browser(&self.data_dir, self.shared.clone(), &vault_name);
|
| 450 |
471 |
|
|
| 451 |
472 |
|
/// Create a BrowserState, returning (Some(browser), None) on success or
|
| 452 |
473 |
|
/// (None, Some(error)) on failure.
|
| 453 |
|
- |
fn init_browser(data_dir: &Path, shared: Arc<SharedState>, vault_name: &str) -> (Option<BrowserState>, Option<String>) {
|
| 454 |
|
- |
let sample_rate = shared.device_sample_rate.load(std::sync::atomic::Ordering::Relaxed) as f32;
|
|
474 |
+ |
fn init_browser(
|
|
475 |
+ |
data_dir: &Path,
|
|
476 |
+ |
shared: Arc<SharedState>,
|
|
477 |
+ |
vault_name: &str,
|
|
478 |
+ |
) -> (Option<BrowserState>, Option<String>) {
|
|
479 |
+ |
let sample_rate = shared
|
|
480 |
+ |
.device_sample_rate
|
|
481 |
+ |
.load(std::sync::atomic::Ordering::Relaxed) as f32;
|
| 455 |
482 |
|
match BrowserState::new(data_dir, shared, sample_rate, vault_name) {
|
| 456 |
483 |
|
Ok(mut browser) => {
|
| 457 |
484 |
|
// CLI args / OS "Open with": route through the background import
|
| 592 |
619 |
|
let ctx = &ctx;
|
| 593 |
620 |
|
// Poll tray menu events
|
| 594 |
621 |
|
if let Some(ref tray) = self.tray
|
| 595 |
|
- |
&& let Some(action) = tray.poll() {
|
| 596 |
|
- |
match action {
|
| 597 |
|
- |
tray::TrayAction::ShowWindow => {
|
| 598 |
|
- |
ctx.send_viewport_cmd(ViewportCommand::Focus);
|
| 599 |
|
- |
}
|
| 600 |
|
- |
tray::TrayAction::TogglePlayback => {
|
| 601 |
|
- |
if let Some(ref mut browser) = self.browser {
|
| 602 |
|
- |
browser.toggle_preview();
|
| 603 |
|
- |
}
|
| 604 |
|
- |
}
|
| 605 |
|
- |
tray::TrayAction::Quit => {
|
| 606 |
|
- |
ctx.send_viewport_cmd(ViewportCommand::Close);
|
|
622 |
+ |
&& let Some(action) = tray.poll()
|
|
623 |
+ |
{
|
|
624 |
+ |
match action {
|
|
625 |
+ |
tray::TrayAction::ShowWindow => {
|
|
626 |
+ |
ctx.send_viewport_cmd(ViewportCommand::Focus);
|
|
627 |
+ |
}
|
|
628 |
+ |
tray::TrayAction::TogglePlayback => {
|
|
629 |
+ |
if let Some(ref mut browser) = self.browser {
|
|
630 |
+ |
browser.toggle_preview();
|
| 607 |
631 |
|
}
|
| 608 |
632 |
|
}
|
|
633 |
+ |
tray::TrayAction::Quit => {
|
|
634 |
+ |
ctx.send_viewport_cmd(ViewportCommand::Close);
|
|
635 |
+ |
}
|
| 609 |
636 |
|
}
|
|
637 |
+ |
}
|
| 610 |
638 |
|
|
| 611 |
639 |
|
// Update tray tooltip based on playback state
|
| 612 |
640 |
|
if let Some(ref tray) = self.tray
|
| 613 |
|
- |
&& let Some(ref browser) = self.browser {
|
| 614 |
|
- |
let playing = browser.shared.preview.lock().playing;
|
| 615 |
|
- |
if playing {
|
| 616 |
|
- |
tray.set_tooltip(&browser.status);
|
| 617 |
|
- |
} else {
|
| 618 |
|
- |
tray.set_tooltip("audiofiles");
|
| 619 |
|
- |
}
|
|
641 |
+ |
&& let Some(ref browser) = self.browser
|
|
642 |
+ |
{
|
|
643 |
+ |
let playing = browser.shared.preview.lock().playing;
|
|
644 |
+ |
if playing {
|
|
645 |
+ |
tray.set_tooltip(&browser.status);
|
|
646 |
+ |
} else {
|
|
647 |
+ |
tray.set_tooltip("audiofiles");
|
| 620 |
648 |
|
}
|
|
649 |
+ |
}
|
| 621 |
650 |
|
|
| 622 |
651 |
|
// Check if sync pulled remote changes → refresh browser contents
|
| 623 |
652 |
|
if let Some(ref sync) = self.sync_manager
|
| 624 |
|
- |
&& sync.status().needs_refresh {
|
| 625 |
|
- |
if let Some(ref mut browser) = self.browser {
|
| 626 |
|
- |
browser.refresh_vfs_list();
|
| 627 |
|
- |
browser.refresh_contents();
|
| 628 |
|
- |
}
|
| 629 |
|
- |
sync.clear_needs_refresh();
|
|
653 |
+ |
&& sync.status().needs_refresh
|
|
654 |
+ |
{
|
|
655 |
+ |
if let Some(ref mut browser) = self.browser {
|
|
656 |
+ |
browser.refresh_vfs_list();
|
|
657 |
+ |
browser.refresh_contents();
|
| 630 |
658 |
|
}
|
|
659 |
+ |
sync.clear_needs_refresh();
|
|
660 |
+ |
}
|
| 631 |
661 |
|
|
| 632 |
662 |
|
// ── Sync setup actions (before draw, so UI sees results this frame) ──
|
| 633 |
663 |
|
// ── Vault actions ──
|
| 634 |
664 |
|
if let Some(ref mut browser) = self.browser
|
| 635 |
|
- |
&& let Some(action) = browser.settings.pending_action.take() {
|
| 636 |
|
- |
use audiofiles_browser::state::VaultAction;
|
| 637 |
|
- |
match action {
|
| 638 |
|
- |
VaultAction::SwitchVault(path) => {
|
| 639 |
|
- |
self.switch_vault(path);
|
| 640 |
|
- |
return;
|
| 641 |
|
- |
}
|
| 642 |
|
- |
VaultAction::CreateVault { name, path, loose_files } => {
|
| 643 |
|
- |
let switch_path = path.clone();
|
| 644 |
|
- |
if self.with_vault_registry(|reg| vault::create_vault(reg, &name, &path)) {
|
| 645 |
|
- |
self.switch_vault(switch_path);
|
| 646 |
|
- |
if loose_files
|
| 647 |
|
- |
&& let Some(ref mut browser) = self.browser {
|
| 648 |
|
- |
let _ = browser.backend.set_config("loose_files", "1");
|
| 649 |
|
- |
browser.settings.is_loose_files = true;
|
| 650 |
|
- |
}
|
| 651 |
|
- |
return;
|
|
665 |
+ |
&& let Some(action) = browser.settings.pending_action.take()
|
|
666 |
+ |
{
|
|
667 |
+ |
use audiofiles_browser::state::VaultAction;
|
|
668 |
+ |
match action {
|
|
669 |
+ |
VaultAction::SwitchVault(path) => {
|
|
670 |
+ |
self.switch_vault(path);
|
|
671 |
+ |
return;
|
|
672 |
+ |
}
|
|
673 |
+ |
VaultAction::CreateVault {
|
|
674 |
+ |
name,
|
|
675 |
+ |
path,
|
|
676 |
+ |
loose_files,
|
|
677 |
+ |
} => {
|
|
678 |
+ |
let switch_path = path.clone();
|
|
679 |
+ |
if self.with_vault_registry(|reg| vault::create_vault(reg, &name, &path)) {
|
|
680 |
+ |
self.switch_vault(switch_path);
|
|
681 |
+ |
if loose_files && let Some(ref mut browser) = self.browser {
|
|
682 |
+ |
let _ = browser.backend.set_config("loose_files", "1");
|
|
683 |
+ |
browser.settings.is_loose_files = true;
|
| 652 |
684 |
|
}
|
| 653 |
|
- |
}
|
| 654 |
|
- |
VaultAction::AddExistingVault { name, path } => {
|
| 655 |
|
- |
self.with_vault_registry(|reg| vault::add_existing_vault(reg, &name, &path));
|
| 656 |
|
- |
}
|
| 657 |
|
- |
VaultAction::RemoveVault(path) => {
|
| 658 |
|
- |
self.with_vault_registry(|reg| vault::remove_vault(reg, &path));
|
| 659 |
|
- |
}
|
| 660 |
|
- |
VaultAction::RenameVault { path, new_name } => {
|
| 661 |
|
- |
self.with_vault_registry(|reg| vault::rename_vault(reg, &path, &new_name));
|
| 662 |
|
- |
}
|
| 663 |
|
- |
VaultAction::RelocateVault { old_path, new_path } => {
|
| 664 |
|
- |
// If we're repointing the active vault, switch to the new
|
| 665 |
|
- |
// path so the open browser picks up the new location.
|
| 666 |
|
- |
let was_active = self
|
| 667 |
|
- |
.vault_registry
|
| 668 |
|
- |
.as_ref()
|
| 669 |
|
- |
.map(|r| r.active == old_path)
|
| 670 |
|
- |
.unwrap_or(false);
|
| 671 |
|
- |
let ok = self.with_vault_registry(|reg| {
|
| 672 |
|
- |
vault::relocate_vault(reg, &old_path, &new_path)
|
| 673 |
|
- |
});
|
| 674 |
|
- |
if ok && was_active {
|
| 675 |
|
- |
self.switch_vault(new_path);
|
| 676 |
|
- |
return;
|
| 677 |
|
- |
}
|
| 678 |
|
- |
}
|
| 679 |
|
- |
VaultAction::ScanStorage => {
|
| 680 |
|
- |
// storage_stats() is a single indexed COUNT/SUM — fast
|
| 681 |
|
- |
// enough to run inline; no in-progress flag needed (the
|
| 682 |
|
- |
// old one was set and cleared in the same frame, so its
|
| 683 |
|
- |
// spinner could never render).
|
| 684 |
|
- |
match browser.backend.storage_stats() {
|
| 685 |
|
- |
Ok(stats) => {
|
| 686 |
|
- |
browser.settings.storage_cache = Some(stats);
|
| 687 |
|
- |
browser.settings.storage_cache_at = Some(
|
| 688 |
|
- |
std::time::SystemTime::now()
|
| 689 |
|
- |
.duration_since(std::time::UNIX_EPOCH)
|
| 690 |
|
- |
.map(|d| d.as_secs() as i64)
|
| 691 |
|
- |
.unwrap_or(0),
|
| 692 |
|
- |
);
|
| 693 |
|
- |
}
|
| 694 |
|
- |
Err(e) => browser.status = format!("Storage scan failed: {e}"),
|
| 695 |
|
- |
}
|
| 696 |
|
- |
}
|
| 697 |
|
- |
VaultAction::DeactivateLicense => {
|
| 698 |
|
- |
self.deactivate();
|
| 699 |
685 |
|
return;
|
| 700 |
686 |
|
}
|
| 701 |
687 |
|
}
|
|
688 |
+ |
VaultAction::AddExistingVault { name, path } => {
|
|
689 |
+ |
self.with_vault_registry(|reg| vault::add_existing_vault(reg, &name, &path));
|
|
690 |
+ |
}
|
|
691 |
+ |
VaultAction::RemoveVault(path) => {
|
|
692 |
+ |
self.with_vault_registry(|reg| vault::remove_vault(reg, &path));
|
|
693 |
+ |
}
|
|
694 |
+ |
VaultAction::RenameVault { path, new_name } => {
|
|
695 |
+ |
self.with_vault_registry(|reg| vault::rename_vault(reg, &path, &new_name));
|
|
696 |
+ |
}
|
|
697 |
+ |
VaultAction::RelocateVault { old_path, new_path } => {
|
|
698 |
+ |
// If we're repointing the active vault, switch to the new
|
|
699 |
+ |
// path so the open browser picks up the new location.
|
|
700 |
+ |
let was_active = self
|
|
701 |
+ |
.vault_registry
|
|
702 |
+ |
.as_ref()
|
|
703 |
+ |
.map(|r| r.active == old_path)
|
|
704 |
+ |
.unwrap_or(false);
|
|
705 |
+ |
let ok = self.with_vault_registry(|reg| {
|
|
706 |
+ |
vault::relocate_vault(reg, &old_path, &new_path)
|
|
707 |
+ |
});
|
|
708 |
+ |
if ok && was_active {
|
|
709 |
+ |
self.switch_vault(new_path);
|
|
710 |
+ |
return;
|
|
711 |
+ |
}
|
|
712 |
+ |
}
|
|
713 |
+ |
VaultAction::ScanStorage => {
|
|
714 |
+ |
// storage_stats() is a single indexed COUNT/SUM — fast
|
|
715 |
+ |
// enough to run inline; no in-progress flag needed (the
|
|
716 |
+ |
// old one was set and cleared in the same frame, so its
|
|
717 |
+ |
// spinner could never render).
|
|
718 |
+ |
match browser.backend.storage_stats() {
|
|
719 |
+ |
Ok(stats) => {
|
|
720 |
+ |
browser.settings.storage_cache = Some(stats);
|
|
721 |
+ |
browser.settings.storage_cache_at = Some(
|
|
722 |
+ |
std::time::SystemTime::now()
|
|
723 |
+ |
.duration_since(std::time::UNIX_EPOCH)
|
|
724 |
+ |
.map(|d| d.as_secs() as i64)
|
|
725 |
+ |
.unwrap_or(0),
|
|
726 |
+ |
);
|
|
727 |
+ |
}
|
|
728 |
+ |
Err(e) => browser.status = format!("Storage scan failed: {e}"),
|
|
729 |
+ |
}
|
|
730 |
+ |
}
|
|
731 |
+ |
VaultAction::DeactivateLicense => {
|
|
732 |
+ |
self.deactivate();
|
|
733 |
+ |
return;
|
|
734 |
+ |
}
|
| 702 |
735 |
|
}
|
|
736 |
+ |
}
|
| 703 |
737 |
|
|
| 704 |
738 |
|
// ── VFS Mirror: sync if dirty ──
|
| 705 |
739 |
|
if let Some(ref mut browser) = self.browser {
|
| 715 |
749 |
|
MidiAction::RefreshPorts => {
|
| 716 |
750 |
|
browser.midi_state.available_ports = midi::list_input_ports();
|
| 717 |
751 |
|
}
|
| 718 |
|
- |
MidiAction::Connect(idx) => {
|
| 719 |
|
- |
match midi::connect(idx, self.shared.clone()) {
|
| 720 |
|
- |
Ok(conn) => {
|
| 721 |
|
- |
let name = browser.midi_state.available_ports
|
| 722 |
|
- |
.get(idx)
|
| 723 |
|
- |
.cloned()
|
| 724 |
|
- |
.unwrap_or_else(|| format!("Port {idx}"));
|
| 725 |
|
- |
browser.midi_state.connected_port = Some(idx);
|
| 726 |
|
- |
browser.midi_state.connected_port_name = Some(name);
|
| 727 |
|
- |
self.midi_connection = Some(conn);
|
| 728 |
|
- |
}
|
| 729 |
|
- |
Err(e) => {
|
| 730 |
|
- |
tracing::error!("MIDI connect failed: {e}");
|
| 731 |
|
- |
browser.midi_state.connected_port = None;
|
| 732 |
|
- |
browser.midi_state.connected_port_name = None;
|
| 733 |
|
- |
}
|
|
752 |
+ |
MidiAction::Connect(idx) => match midi::connect(idx, self.shared.clone()) {
|
|
753 |
+ |
Ok(conn) => {
|
|
754 |
+ |
let name = browser
|
|
755 |
+ |
.midi_state
|
|
756 |
+ |
.available_ports
|
|
757 |
+ |
.get(idx)
|
|
758 |
+ |
.cloned()
|
|
759 |
+ |
.unwrap_or_else(|| format!("Port {idx}"));
|
|
760 |
+ |
browser.midi_state.connected_port = Some(idx);
|
|
761 |
+ |
browser.midi_state.connected_port_name = Some(name);
|
|
762 |
+ |
self.midi_connection = Some(conn);
|
| 734 |
763 |
|
}
|
| 735 |
|
- |
}
|
|
764 |
+ |
Err(e) => {
|
|
765 |
+ |
tracing::error!("MIDI connect failed: {e}");
|
|
766 |
+ |
browser.midi_state.connected_port = None;
|
|
767 |
+ |
browser.midi_state.connected_port_name = None;
|
|
768 |
+ |
}
|
|
769 |
+ |
},
|
| 736 |
770 |
|
MidiAction::Disconnect => {
|
| 737 |
771 |
|
self.midi_connection = None;
|
| 738 |
772 |
|
browser.midi_state.connected_port = None;
|
| 937 |
971 |
|
ui.separator();
|
| 938 |
972 |
|
ui.add_space(8.0);
|
| 939 |
973 |
|
ui.strong("Network");
|
| 940 |
|
- |
ui.checkbox(
|
| 941 |
|
- |
&mut updated_check_pref,
|
| 942 |
|
- |
"Check makenot.work for updates",
|
| 943 |
|
- |
);
|
|
974 |
+ |
ui.checkbox(&mut updated_check_pref, "Check makenot.work for updates");
|
| 944 |
975 |
|
ui.label(
|
| 945 |
976 |
|
egui::RichText::new(
|
| 946 |
977 |
|
"When enabled, the app contacts makenot.work on launch and every 6 hours \
|
| 1037 |
1068 |
|
let dir = tempfile::tempdir().unwrap();
|
| 1038 |
1069 |
|
let reg = Some(make_registry(dir.path()));
|
| 1039 |
1070 |
|
let status = license::LicenseStatus::Licensed(make_license_cache());
|
| 1040 |
|
- |
assert_eq!(resolve_initial_screen(®, &status, false), AppScreen::Browser);
|
|
1071 |
+ |
assert_eq!(
|
|
1072 |
+ |
resolve_initial_screen(®, &status, false),
|
|
1073 |
+ |
AppScreen::Browser
|
|
1074 |
+ |
);
|
| 1041 |
1075 |
|
}
|
| 1042 |
1076 |
|
|
| 1043 |
1077 |
|
#[test]
|
| 1044 |
1078 |
|
fn initial_screen_licensed_without_registry() {
|
| 1045 |
1079 |
|
let status = license::LicenseStatus::Licensed(make_license_cache());
|
| 1046 |
|
- |
assert_eq!(resolve_initial_screen(&None, &status, false), AppScreen::VaultSetup);
|
|
1080 |
+ |
assert_eq!(
|
|
1081 |
+ |
resolve_initial_screen(&None, &status, false),
|
|
1082 |
+ |
AppScreen::VaultSetup
|
|
1083 |
+ |
);
|
| 1047 |
1084 |
|
}
|
| 1048 |
1085 |
|
|
| 1049 |
1086 |
|
#[test]
|
| 1051 |
1088 |
|
let dir = tempfile::tempdir().unwrap();
|
| 1052 |
1089 |
|
let reg = Some(make_registry(dir.path()));
|
| 1053 |
1090 |
|
let status = license::LicenseStatus::Unlicensed;
|
| 1054 |
|
- |
assert_eq!(resolve_initial_screen(®, &status, false), AppScreen::Activation);
|
|
1091 |
+ |
assert_eq!(
|
|
1092 |
+ |
resolve_initial_screen(®, &status, false),
|
|
1093 |
+ |
AppScreen::Activation
|
|
1094 |
+ |
);
|
| 1055 |
1095 |
|
}
|
| 1056 |
1096 |
|
|
| 1057 |
1097 |
|
#[test]
|
| 1058 |
1098 |
|
fn initial_screen_unlicensed_without_registry() {
|
| 1059 |
1099 |
|
let status = license::LicenseStatus::Unlicensed;
|
| 1060 |
|
- |
assert_eq!(resolve_initial_screen(&None, &status, false), AppScreen::Activation);
|
|
1100 |
+ |
assert_eq!(
|
|
1101 |
+ |
resolve_initial_screen(&None, &status, false),
|
|
1102 |
+ |
AppScreen::Activation
|
|
1103 |
+ |
);
|
| 1061 |
1104 |
|
}
|
| 1062 |
1105 |
|
|
| 1063 |
1106 |
|
#[test]
|
| 1065 |
1108 |
|
let dir = tempfile::tempdir().unwrap();
|
| 1066 |
1109 |
|
let reg = Some(make_registry(dir.path()));
|
| 1067 |
1110 |
|
let status = license::LicenseStatus::Unlicensed;
|
| 1068 |
|
- |
assert_eq!(resolve_initial_screen(®, &status, true), AppScreen::Browser);
|
|
1111 |
+ |
assert_eq!(
|
|
1112 |
+ |
resolve_initial_screen(®, &status, true),
|
|
1113 |
+ |
AppScreen::Browser
|
|
1114 |
+ |
);
|
| 1069 |
1115 |
|
}
|
| 1070 |
1116 |
|
|
| 1071 |
1117 |
|
#[test]
|
| 1072 |
1118 |
|
fn initial_screen_trial_without_registry() {
|
| 1073 |
1119 |
|
let status = license::LicenseStatus::Unlicensed;
|
| 1074 |
|
- |
assert_eq!(resolve_initial_screen(&None, &status, true), AppScreen::VaultSetup);
|
|
1120 |
+ |
assert_eq!(
|
|
1121 |
+ |
resolve_initial_screen(&None, &status, true),
|
|
1122 |
+ |
AppScreen::VaultSetup
|
|
1123 |
+ |
);
|
| 1075 |
1124 |
|
}
|
| 1076 |
1125 |
|
|
| 1077 |
1126 |
|
// ── License migration ──
|
| 1080 |
1129 |
|
fn migrate_license_copies_files() {
|
| 1081 |
1130 |
|
let src = tempfile::tempdir().unwrap();
|
| 1082 |
1131 |
|
let dst = tempfile::tempdir().unwrap();
|
| 1083 |
|
- |
std::fs::write(src.path().join("license.json"), r#"{"key_code":"k","machine_id":"m","activated_at":"t"}"#).unwrap();
|
|
1132 |
+ |
std::fs::write(
|
|
1133 |
+ |
src.path().join("license.json"),
|
|
1134 |
+ |
r#"{"key_code":"k","machine_id":"m","activated_at":"t"}"#,
|
|
1135 |
+ |
)
|
|
1136 |
+ |
.unwrap();
|
| 1084 |
1137 |
|
std::fs::write(src.path().join("machine_id"), "mid-123").unwrap();
|
| 1085 |
1138 |
|
|
| 1086 |
1139 |
|
vault_setup::migrate_license_to_config(dst.path(), src.path());
|
| 1154 |
1207 |
|
fn vault_name_for_path_found() {
|
| 1155 |
1208 |
|
let dir = tempfile::tempdir().unwrap();
|
| 1156 |
1209 |
|
let reg = make_registry(dir.path());
|
| 1157 |
|
- |
assert_eq!(vault_setup::vault_name_for_path(®, dir.path()), "Library");
|
|
1210 |
+ |
assert_eq!(
|
|
1211 |
+ |
vault_setup::vault_name_for_path(®, dir.path()),
|
|
1212 |
+ |
"Library"
|
|
1213 |
+ |
);
|
| 1158 |
1214 |
|
}
|
| 1159 |
1215 |
|
|
| 1160 |
1216 |
|
#[test]
|
| 1161 |
1217 |
|
fn vault_name_for_path_not_found() {
|
| 1162 |
1218 |
|
let dir = tempfile::tempdir().unwrap();
|
| 1163 |
1219 |
|
let reg = make_registry(dir.path());
|
| 1164 |
|
- |
assert_eq!(vault_setup::vault_name_for_path(®, Path::new("/nonexistent")), "Library");
|
|
1220 |
+ |
assert_eq!(
|
|
1221 |
+ |
vault_setup::vault_name_for_path(®, Path::new("/nonexistent")),
|
|
1222 |
+ |
"Library"
|
|
1223 |
+ |
);
|
| 1165 |
1224 |
|
}
|
| 1166 |
1225 |
|
}
|