fix(windows): restauration panel-only des fenêtres détachées suivant le projet en focus (#47)
Partie backend. Les fenêtres/panneaux détachés étaient restaurés avec un project_id figé au moment du détachement, si bien qu'ils restaient collés à un projet mort ou incohérent après redémarrage. Ils sont désormais restaurés en mode panel-only, sans project_id figé, et suivent le projet en focus de la fenêtre principale via un event focused-project exposé par la couche fenêtre. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -10,7 +10,7 @@ use domain::ids::{TabId, WindowId};
|
||||
use std::collections::HashSet;
|
||||
|
||||
use domain::layout::{PersistedWindowKind, PersistedWindowState, WindowStateSnapshot, Workspace};
|
||||
use domain::ports::{IdGenerator, ProjectStore, StoreError, WindowStateStore};
|
||||
use domain::ports::{IdGenerator, ProjectStore, WindowStateStore};
|
||||
|
||||
use crate::error::AppError;
|
||||
|
||||
@ -115,21 +115,24 @@ pub struct RestoreOpenWindowsOutput {
|
||||
/// Loads and filters the latest persisted OS window snapshot.
|
||||
pub struct RestoreOpenWindows {
|
||||
windows: Arc<dyn WindowStateStore>,
|
||||
projects: Arc<dyn ProjectStore>,
|
||||
_projects: Arc<dyn ProjectStore>,
|
||||
}
|
||||
|
||||
impl RestoreOpenWindows {
|
||||
/// Builds the use case from its ports.
|
||||
#[must_use]
|
||||
pub fn new(windows: Arc<dyn WindowStateStore>, projects: Arc<dyn ProjectStore>) -> Self {
|
||||
Self { windows, projects }
|
||||
Self {
|
||||
windows,
|
||||
_projects: projects,
|
||||
}
|
||||
}
|
||||
|
||||
/// Loads restorable windows.
|
||||
///
|
||||
/// Views are ignored if their project no longer exists or if their persisted
|
||||
/// identity is incomplete. Duplicate labels are ignored after the first
|
||||
/// occurrence.
|
||||
/// Views are panel-only: their persisted project id, including legacy
|
||||
/// `view-<panel>-<project>` identities, is ignored. Duplicate labels are ignored
|
||||
/// after the first occurrence.
|
||||
///
|
||||
/// # Errors
|
||||
/// [`AppError::Store`] on snapshot read failure.
|
||||
@ -146,17 +149,10 @@ impl RestoreOpenWindows {
|
||||
match window.kind {
|
||||
PersistedWindowKind::Main => windows.push(window),
|
||||
PersistedWindowKind::View => {
|
||||
let Some(project_id) = window.project_id else {
|
||||
continue;
|
||||
};
|
||||
if window.panel.is_none() || window.url.is_none() {
|
||||
if window.panel.is_none() {
|
||||
continue;
|
||||
}
|
||||
match self.projects.load_project(project_id).await {
|
||||
Ok(_) => windows.push(window),
|
||||
Err(StoreError::NotFound) => {}
|
||||
Err(e) => return Err(e.into()),
|
||||
}
|
||||
windows.push(window);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -232,12 +232,14 @@ async fn snapshot_open_windows_persists_the_supplied_snapshot() {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn restore_open_windows_deduplicates_and_filters_missing_projects() {
|
||||
async fn restore_open_windows_keeps_panel_views_without_reopening_projects() {
|
||||
let existing = ProjectId::from_uuid(Uuid::from_u128(42));
|
||||
let missing = ProjectId::from_uuid(Uuid::from_u128(43));
|
||||
let valid_label = "view-tickets-0000000000000000000000000000002a";
|
||||
let mut incomplete = persisted_view("view-tickets-0000000000000000000000000000002c", existing);
|
||||
incomplete.url = None;
|
||||
let mut url_missing = persisted_view("view-tickets-0000000000000000000000000000002c", existing);
|
||||
url_missing.url = None;
|
||||
let mut no_panel = persisted_view("view-tickets-0000000000000000000000000000002d", existing);
|
||||
no_panel.panel = None;
|
||||
|
||||
let store = FakeWindowStateStore(Arc::new(Mutex::new(WindowStateSnapshot::new(vec![
|
||||
persisted_main(),
|
||||
@ -245,9 +247,10 @@ async fn restore_open_windows_deduplicates_and_filters_missing_projects() {
|
||||
persisted_view(valid_label, existing),
|
||||
persisted_view(valid_label, existing),
|
||||
persisted_view("view-tickets-0000000000000000000000000000002b", missing),
|
||||
incomplete,
|
||||
url_missing,
|
||||
no_panel,
|
||||
]))));
|
||||
let projects = FakeProjectRegistry::new(vec![existing]);
|
||||
let projects = FakeProjectRegistry::new(vec![]);
|
||||
let uc = RestoreOpenWindows::new(Arc::new(store), Arc::new(projects));
|
||||
|
||||
let out = uc.execute().await.unwrap();
|
||||
@ -257,6 +260,11 @@ async fn restore_open_windows_deduplicates_and_filters_missing_projects() {
|
||||
.iter()
|
||||
.map(|w| w.label.as_str())
|
||||
.collect::<Vec<_>>(),
|
||||
vec!["main", valid_label]
|
||||
vec![
|
||||
"main",
|
||||
valid_label,
|
||||
"view-tickets-0000000000000000000000000000002b",
|
||||
"view-tickets-0000000000000000000000000000002c"
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user