diff --git a/src/Kontext.Presentation/GitChangeRow.cs b/src/Kontext.Presentation/GitChangeRow.cs
index 5e1a591..4afc0cf 100644
--- a/src/Kontext.Presentation/GitChangeRow.cs
+++ b/src/Kontext.Presentation/GitChangeRow.cs
@@ -39,17 +39,23 @@ public bool InitiallyChecked
/// Told when the user checks or unchecks the row.
public Action? Toggled { get; init; }
- public string StatusLetter => Change?.Status switch
+ public string StatusGlyph => Change?.Status switch
{
- FileChangeStatus.Modified => "M",
// A new file is "+", staged or not.
FileChangeStatus.Added or FileChangeStatus.Untracked => "+",
- FileChangeStatus.Deleted => "D",
- FileChangeStatus.Renamed => "R",
+ FileChangeStatus.Modified or FileChangeStatus.Renamed => "●",
+ FileChangeStatus.Deleted => "−",
FileChangeStatus.Conflicted => "!",
_ => string.Empty,
};
+ public bool IsAddedGlyph => Change?.Status is FileChangeStatus.Added or FileChangeStatus.Untracked;
+
+ public bool IsChangedGlyph => Change?.Status is FileChangeStatus.Modified or FileChangeStatus.Renamed;
+
+ /// Drawn in the critical colour: deleted files, and conflicted ones since they need attention.
+ public bool IsRemovedGlyph => Change?.Status is FileChangeStatus.Deleted or FileChangeStatus.Conflicted;
+
public string FileName => Change is null ? string.Empty : Change.Path[(Change.Path.LastIndexOf('/') + 1)..];
public string Folder => Change is null ? string.Empty : Change.Path[..Math.Max(Change.Path.LastIndexOf('/'), 0)];
diff --git a/src/Kontext.Presentation/IssueSidebarViewModel.cs b/src/Kontext.Presentation/IssueSidebarViewModel.cs
index c4653da..8c080b0 100644
--- a/src/Kontext.Presentation/IssueSidebarViewModel.cs
+++ b/src/Kontext.Presentation/IssueSidebarViewModel.cs
@@ -373,8 +373,33 @@ private void OnGroupExpandedChanged(RepositoryGroupViewModel group)
_collapsedRepositories.Save(_collapsed);
}
+ private void ForgetClearedWorkspaces(IReadOnlyList workspaces)
+ {
+ var existing = workspaces.Select(workspace => workspace.Issue).ToHashSet();
+
+ foreach (var item in _all.ToList())
+ {
+ if (existing.Contains(item.Key))
+ {
+ continue;
+ }
+
+ var state = _tracker.StateOf(item.Key);
+ if (state.Status is IssueWorkStatus.Ready or IssueWorkStatus.Done && state.WorktreePath is not null)
+ {
+ _tracker.Report(item.Key, IssueWorkState.NotStarted);
+ }
+ else if (state.Status == IssueWorkStatus.NotStarted)
+ {
+ item.HasSavedSession = false;
+ }
+ }
+ }
+
private void MarkExistingWorkspaces(IReadOnlyList workspaces)
{
+ ForgetClearedWorkspaces(workspaces);
+
foreach (var workspace in workspaces)
{
if (!_itemsByKey.TryGetValue(workspace.Issue, out var item))
diff --git a/src/Kontext/Views/GitChangesView.xaml b/src/Kontext/Views/GitChangesView.xaml
index 66ead98..93e0016 100644
--- a/src/Kontext/Views/GitChangesView.xaml
+++ b/src/Kontext/Views/GitChangesView.xaml
@@ -102,12 +102,26 @@
AutomationProperties.AutomationId="GitFileCheck"
AutomationProperties.Name="{x:Bind Text}"
IsChecked="{x:Bind IsChecked, Mode=TwoWay}" />
-
+
+
+
+
+
new(path, new FileChange(path, null, status, IsStaged: false, additions, deletions, IsBinary: false));
- [TestCase(FileChangeStatus.Modified, "M")]
- [TestCase(FileChangeStatus.Added, "+")]
- [TestCase(FileChangeStatus.Deleted, "D")]
- [TestCase(FileChangeStatus.Renamed, "R")]
- [TestCase(FileChangeStatus.Untracked, "+")]
- [TestCase(FileChangeStatus.Conflicted, "!")]
- public void ShowsTheStatusLetterForEachStatus(FileChangeStatus status, string letter)
+ [TestCase(FileChangeStatus.Added, "+", true, false, false)]
+ [TestCase(FileChangeStatus.Untracked, "+", true, false, false)]
+ [TestCase(FileChangeStatus.Modified, "●", false, true, false)]
+ [TestCase(FileChangeStatus.Renamed, "●", false, true, false)]
+ [TestCase(FileChangeStatus.Deleted, "−", false, false, true)]
+ [TestCase(FileChangeStatus.Conflicted, "!", false, false, true)]
+ public void ShowsAColouredGlyphForEachStatus(FileChangeStatus status, string glyph, bool added, bool changed, bool removed)
{
- Assert.That(File(status: status).StatusLetter, Is.EqualTo(letter));
+ var row = File(status: status);
+
+ Assert.That(row.StatusGlyph, Is.EqualTo(glyph));
+ Assert.That(row.IsAddedGlyph, Is.EqualTo(added));
+ Assert.That(row.IsChangedGlyph, Is.EqualTo(changed));
+ Assert.That(row.IsRemovedGlyph, Is.EqualTo(removed));
}
[Test]
@@ -54,7 +59,8 @@ public void AHeaderRowHasNoFile()
Assert.That(header.IsHeader, Is.True);
Assert.That(header.Text, Is.EqualTo("Staged (1)"));
- Assert.That(header.StatusLetter, Is.Empty);
+ Assert.That(header.StatusGlyph, Is.Empty);
+ Assert.That(header.IsAddedGlyph || header.IsChangedGlyph || header.IsRemovedGlyph, Is.False);
Assert.That(header.HasCounts, Is.False);
Assert.That(File().IsHeader, Is.False);
}
diff --git a/tests/Kontext.Presentation.UnitTests/IssueSidebarAutoRefreshTests.cs b/tests/Kontext.Presentation.UnitTests/IssueSidebarAutoRefreshTests.cs
index db0a3d3..81f5f32 100644
--- a/tests/Kontext.Presentation.UnitTests/IssueSidebarAutoRefreshTests.cs
+++ b/tests/Kontext.Presentation.UnitTests/IssueSidebarAutoRefreshTests.cs
@@ -12,6 +12,7 @@ public class IssueSidebarAutoRefreshTests
private FakeTimeProvider _clock = null!;
private InMemoryIssueWorkTracker _tracker = null!;
private FakeGitHubService _gitHub = null!;
+ private FakeExistingWorkspaceFinder _workspaces = null!;
private IssueSidebarViewModel? _sidebar;
[SetUp]
@@ -20,6 +21,7 @@ public void Setup()
_clock = new FakeTimeProvider();
_tracker = new InMemoryIssueWorkTracker();
_gitHub = new FakeGitHubService { Issues = [Issues.Create(4, "Add notifications", "acme/memento")] };
+ _workspaces = new FakeExistingWorkspaceFinder();
}
[TearDown]
@@ -29,7 +31,7 @@ private IssueSidebarViewModel CreateSidebar(TimeSpan interval) =>
_sidebar = new(
_gitHub,
_tracker,
- new FakeExistingWorkspaceFinder(),
+ _workspaces,
new WorkspaceIssues(_gitHub),
new InMemoryHiddenRepositoryStore(),
new InMemoryCollapsedRepositoryStore(),
@@ -97,6 +99,21 @@ public async Task DoesNotRefreshWhenTheIntervalIsZero()
Assert.That(IssueCount(sidebar), Is.EqualTo(1));
}
+ [Test]
+ public async Task NoticesWhenASessionIsClearedElsewhere()
+ {
+ var issue = _gitHub.Issues[0];
+ _workspaces.Found.Add(new ExistingWorkspace(issue.Repository, issue.Number, new WorktreeInfo(@"C:\wt\4", "kontext/4-add", "abc", IsMain: false)));
+ var sidebar = CreateSidebar(Interval);
+ await sidebar.RefreshCommand.ExecuteAsync(null);
+ Assert.That(_tracker.StateOf(issue.Key).Status, Is.EqualTo(IssueWorkStatus.Ready));
+
+ _workspaces.Found.Clear();
+ _clock.Advance(Interval);
+
+ await AsyncAssert.EventuallyAsync(() => _tracker.StateOf(issue.Key).Status == IssueWorkStatus.NotStarted, "the cleared session to be noticed");
+ }
+
[Test]
public async Task StopsRefreshingAfterSignOut()
{