Fix todo deletion not refreshing milestone widgets and file watcher race condition
- Add _load_milestones() call after _load_todos() in _on_todo_deleted and _on_todo_edited to refresh milestone widgets showing linked todos - Replace boolean _ignoring_file_change flag with timestamp-based ignore window (0.5s) to handle multiple file system events from a single save - Add _is_within_save_window() helper method for cleaner event filtering Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
b60af09922
commit
20818956b3
|
|
@ -1,5 +1,6 @@
|
|||
"""Data storage and file management for the dashboard."""
|
||||
|
||||
import time
|
||||
from datetime import date
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
|
@ -78,7 +79,9 @@ class DashboardDataStore(QObject):
|
|||
self._ideas_list: list[Goal] | None = None
|
||||
|
||||
# File watching
|
||||
self._ignoring_file_change = False
|
||||
# Use timestamp to ignore file events shortly after saving (handles multiple events)
|
||||
self._last_save_time: float = 0
|
||||
self._save_ignore_window = 0.5 # seconds to ignore file events after save
|
||||
self._file_watcher = QFileSystemWatcher(self)
|
||||
self._file_watcher.fileChanged.connect(self._on_file_changed)
|
||||
self._file_watcher.directoryChanged.connect(self._on_directory_changed)
|
||||
|
|
@ -262,14 +265,14 @@ class DashboardDataStore(QObject):
|
|||
def save_todos(self) -> None:
|
||||
"""Save todos with file watcher temporarily disabled."""
|
||||
if self._todos_parser and self._todo_list:
|
||||
self._ignoring_file_change = True
|
||||
self._last_save_time = time.time()
|
||||
self._todos_parser.save(self._todo_list)
|
||||
self._ensure_file_watched()
|
||||
|
||||
def save_goals(self) -> None:
|
||||
"""Save goals with file watcher temporarily disabled."""
|
||||
if self._goals_parser and self._goal_list:
|
||||
self._ignoring_file_change = True
|
||||
self._last_save_time = time.time()
|
||||
saver = GoalsSaver(self._goals_path, self._goals_parser.frontmatter)
|
||||
saver.save(self._goal_list)
|
||||
self._ensure_file_watched()
|
||||
|
|
@ -277,7 +280,7 @@ class DashboardDataStore(QObject):
|
|||
def save_milestones(self) -> None:
|
||||
"""Save milestones with file watcher temporarily disabled."""
|
||||
if self._milestones_parser and self._milestones_list:
|
||||
self._ignoring_file_change = True
|
||||
self._last_save_time = time.time()
|
||||
self._milestones_parser.save(self._milestones_list)
|
||||
self._ensure_file_watched()
|
||||
|
||||
|
|
@ -286,7 +289,7 @@ class DashboardDataStore(QObject):
|
|||
if self._is_global or not self._ideas_path:
|
||||
return
|
||||
|
||||
self._ignoring_file_change = True
|
||||
self._last_save_time = time.time()
|
||||
|
||||
lines = []
|
||||
lines.append("---")
|
||||
|
|
@ -864,10 +867,13 @@ class DashboardDataStore(QObject):
|
|||
if file_path.parent.exists() and parent not in watched_dirs:
|
||||
self._file_watcher.addPath(parent)
|
||||
|
||||
def _is_within_save_window(self) -> bool:
|
||||
"""Check if we're within the ignore window after a save."""
|
||||
return (time.time() - self._last_save_time) < self._save_ignore_window
|
||||
|
||||
def _on_file_changed(self, path: str) -> None:
|
||||
"""Handle file modification events."""
|
||||
if self._ignoring_file_change:
|
||||
self._ignoring_file_change = False
|
||||
if self._is_within_save_window():
|
||||
self._ensure_file_watched()
|
||||
return
|
||||
|
||||
|
|
@ -885,8 +891,7 @@ class DashboardDataStore(QObject):
|
|||
|
||||
def _on_directory_changed(self, path: str) -> None:
|
||||
"""Handle directory changes."""
|
||||
if self._ignoring_file_change:
|
||||
self._ignoring_file_change = False
|
||||
if self._is_within_save_window():
|
||||
self._ensure_file_watched()
|
||||
return
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
"""Project dashboard view."""
|
||||
|
||||
import shutil
|
||||
import subprocess
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
from PySide6.QtCore import Qt, Signal, QTimer, QThread
|
||||
|
|
@ -32,6 +34,7 @@ from development_hub.views.dashboard.undo_manager import UndoAction, UndoManager
|
|||
|
||||
from development_hub.services.git_service import GitService
|
||||
from development_hub.models.health import HealthStatus
|
||||
from development_hub.parsers.base import atomic_write
|
||||
from development_hub.parsers.todos_parser import TodosParser
|
||||
from development_hub.models.todo import Todo, TodoList
|
||||
from development_hub.parsers.goals_parser import MilestonesParser, GoalsParser
|
||||
|
|
@ -2079,6 +2082,7 @@ class ProjectDashboard(QWidget):
|
|||
|
||||
# Refresh UI
|
||||
self._load_todos()
|
||||
self._load_milestones()
|
||||
|
||||
# Show toast
|
||||
self.toast.show_message(
|
||||
|
|
@ -2105,6 +2109,7 @@ class ProjectDashboard(QWidget):
|
|||
|
||||
# Refresh UI
|
||||
self._load_todos()
|
||||
self._load_milestones()
|
||||
|
||||
# Show toast
|
||||
self.toast.show_message(
|
||||
|
|
@ -2672,13 +2677,15 @@ class ProjectDashboard(QWidget):
|
|||
if confirm != QMessageBox.StandardButton.Ok:
|
||||
return
|
||||
|
||||
# Determine project key for the audit tool
|
||||
# Determine paths for the audit tool
|
||||
if self.is_global:
|
||||
project_key = "global"
|
||||
project_root = None
|
||||
project_name = "Global Goals"
|
||||
milestones_path = self._docs_root / "goals" / "milestones.md"
|
||||
project_dir = None
|
||||
else:
|
||||
project_key = self.project.key
|
||||
project_root = Path(self.project.path) if self.project.path else None
|
||||
project_name = self.project.key
|
||||
milestones_path = self._docs_root / "projects" / self.project.key / "milestones.md"
|
||||
project_dir = Path(self.project.path) if self.project.path else None
|
||||
|
||||
# Create progress dialog
|
||||
self._audit_dialog = QDialog(self)
|
||||
|
|
@ -2720,7 +2727,12 @@ class ProjectDashboard(QWidget):
|
|||
|
||||
# Create worker and thread
|
||||
self._audit_thread = QThread()
|
||||
self._audit_worker = AuditWorker(project_key, project_root)
|
||||
self._audit_worker = AuditWorker(
|
||||
project_name=project_name,
|
||||
goals_path=goals_path,
|
||||
milestones_path=milestones_path if milestones_path.exists() else None,
|
||||
project_dir=project_dir,
|
||||
)
|
||||
self._audit_worker.moveToThread(self._audit_thread)
|
||||
|
||||
# Connect signals
|
||||
|
|
|
|||
Loading…
Reference in New Issue