Skip to content

Commit 0f42c80

Browse files
tmmampagent
andauthored
fix(zbugs): open the first issue on launch in zbugs-gpui and zbugs-rune (#190)
As zbugs-swift does since #183: with no saved selection, zbugs-gpui and zbugs-rune opened with no issue selected. Now each selects the list's first issue once the list has rows. A saved selection still comes first, and it happens once per launch, so deleting the selected issue later still leaves none selected. The e2e runs check it: zbugs-gpui's online run starts with nothing saved and waits for the list's first issue to be selected, and e2e.sh launches zbugs-rune with empty settings and compares the selection it saves with the first open issue in Postgres. Amp-Thread-ID: https://ampcode.com/threads/T-01a1d36c-4ffc-7241-95ec-d56d1643dc77 Co-authored-by: Amp <amp@ampcode.com>
1 parent a750fd6 commit 0f42c80

4 files changed

Lines changed: 71 additions & 5 deletions

File tree

‎apps/zbugs-gpui/src/app.rs‎

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,9 @@ pub struct ZbugsApp {
361361
/// Scroll to the selected issue once the list has it: after a launch
362362
/// restores the selection.
363363
reveal_selected: bool,
364+
/// Select the list's first issue once it has rows: a launch with no
365+
/// selection to restore (`select_first`).
366+
select_first: bool,
364367
/// The first rows on screen and where they were, to keep them on
365368
/// screen when rows above them come and go.
366369
anchor: Vec<(String, usize)>,
@@ -437,7 +440,10 @@ impl ZbugsApp {
437440
}),
438441
// On every change, drawn or not: a window that isn't drawing
439442
// still marks the issue it shows viewed.
440-
cx.observe_self(|this, cx| this.mark_viewed(cx)),
443+
cx.observe_self(|this, cx| {
444+
this.select_first(cx);
445+
this.mark_viewed(cx);
446+
}),
441447
// Light or dark, as the system is.
442448
cx.observe_window_appearance(window, |_, window, cx| {
443449
Theme::sync_system_appearance(Some(window), cx)
@@ -498,6 +504,7 @@ impl ZbugsApp {
498504
detail_focus: cx.focus_handle(),
499505
checked: None,
500506
reveal_selected: false,
507+
select_first: true,
501508
anchor: Vec::new(),
502509
search,
503510
comment,
@@ -516,6 +523,7 @@ impl ZbugsApp {
516523
if let Some(id) = saved.and_then(|s| s.selected) {
517524
app.select(id, cx);
518525
app.reveal_selected = true;
526+
app.select_first = false;
519527
}
520528
// Keeps the relative times ("5 minutes ago") current.
521529
app._tasks.push(cx.spawn(async move |this, cx| {
@@ -1041,6 +1049,28 @@ impl ZbugsApp {
10411049
cx.notify();
10421050
}
10431051

1052+
/// With no issue selected at launch, selects the list's first one once
1053+
/// it has rows, as zbugs-swift does, so the issue column shows one.
1054+
/// Only the first time: an issue deleted later leaves none selected.
1055+
fn select_first(&mut self, cx: &mut Context<Self>) {
1056+
if !self.select_first {
1057+
return;
1058+
}
1059+
let first = self
1060+
.list
1061+
.snapshot()
1062+
.value
1063+
.as_ref()
1064+
.and_then(|rows| Some(rows.first()?.id.clone()));
1065+
let Some(id) = first else {
1066+
return;
1067+
};
1068+
self.select_first = false;
1069+
if self.selected.is_none() {
1070+
self.select(id, cx);
1071+
}
1072+
}
1073+
10441074
/// Selects the issue `delta` rows from the selected one, or the first.
10451075
fn move_selection(&mut self, delta: isize, cx: &mut Context<Self>) {
10461076
let next = {

‎apps/zbugs-gpui/src/app/e2e.rs‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
//! a user would through its own controls' handlers, and exits 0 when every
44
//! step shows up, or 1 with the step that didn't.
55
//!
6-
//! - `ZBUGS_E2E=online`: lists issues, creates and deletes one, creates
6+
//! - `ZBUGS_E2E=online`: lists issues and opens the first, creates and deletes one, creates
77
//! two more and deletes them together, creates another, goes to the
88
//! next issue and back with j and k, unsubscribes from it, comments,
99
//! edits and deletes comments, reacts in a skin tone, labels it (with a label it makes
@@ -138,6 +138,11 @@ async fn online(window: AnyWindowHandle, app: &Entity<ZbugsApp>, cx: &mut AsyncA
138138
app.list.is_synced() && app.count.is_synced() && !rows(app).is_empty()
139139
})
140140
.await?;
141+
// With no selection saved, the list's first issue opens.
142+
until(app, cx, "the list's first issue selected", |app| {
143+
app.selected.is_some() && app.selected.as_ref() == rows(app).first().map(|i| &i.id)
144+
})
145+
.await?;
141146
let open = app.read_with(cx, |app, _| app.count.snapshot().value.unwrap_or(0));
142147
shot("1-list");
143148

‎apps/zbugs-rune/e2e.sh‎

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#!/usr/bin/env bash
22
# zbugs-rune against zbugs' backend, end to end: the app runs headless on
3-
# the wall clock (`--realtime`), steps away from an open issue and back with
3+
# the wall clock (`--realtime`), opens at the list's first issue with no
4+
# selection saved, steps away from an open issue and back with
45
# the arrow keys, then opens it, comments on it, reacts
56
# to it, labels it, closes it, edits the comment, creates a label for it,
67
# makes it internal, and unsubscribes from it, pressing its controls as a
@@ -24,7 +25,19 @@ issue=$(sql "select id from issue i where open and not exists
2425
(select 1 from comment c where c.\"issueID\" = i.id)
2526
order by length(description), \"shortID\" limit 1")
2627

27-
# First the arrow keys: down twice, up, then k must land back on the issue,
28+
# With no selection saved, the app opens at the list's first issue (open
29+
# ones in the zero project, last modified first) and saves it.
30+
first_issue=$(sql "select i.id from issue i join project p on p.id = i.\"projectID\"
31+
where i.open and p.\"lowerCaseName\" = 'zero'
32+
order by i.modified desc, i.id collate \"C\" desc limit 1")
33+
mkdir -p "$out/first"
34+
echo '{"events": []}' > "$out/first.json"
35+
ZBUGS_RUNE_DIR="$out/first" cargo run --locked --quiet -- --realtime \
36+
--script "$out/first.json" --at 12 --size 1180x760 >/dev/null
37+
first=$(node -e 'console.log(JSON.parse(require("fs").readFileSync(process.argv[1])).selection)' \
38+
"$out/first/settings.json")
39+
40+
# Then the arrow keys: down twice, up, then k must land back on the issue,
2841
# which the app saves as its selection. Before the view-state reset below,
2942
# as this run marks the issue viewed.
3043
mkdir -p "$out/arrows"
@@ -92,6 +105,7 @@ fail=0
92105
check() {
93106
if [ "$2" = "$3" ]; then echo "ok: $1"; else echo "FAILED: $1: got '$2', want '$3'"; fail=1; fi
94107
}
108+
check "the first issue, with none saved" "$first" "$first_issue"
95109
check "the arrow keys" "$arrows" "$issue"
96110
check "the comment, edited" \
97111
"$(sql "select count(*) from comment where \"issueID\" = '$issue' and body = '$body edited'")" 1

‎apps/zbugs-rune/src/main.rs‎

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,9 @@ pub struct App {
152152
pub checked: Vec<String>,
153153
/// Scroll the list to the selected issue once it's there (a relaunch).
154154
reveal_selected: bool,
155+
/// Select the list's first issue once it has rows: a launch with no
156+
/// selection to restore.
157+
select_first: bool,
155158
pub detail: Option<Issue>,
156159
pub detail_result: ResultType,
157160
pub comments: Vec<Comment>,
@@ -276,6 +279,7 @@ impl App {
276279
select_mode: false,
277280
checked: Vec::new(),
278281
reveal_selected: false,
282+
select_first: true,
279283
detail: None,
280284
detail_result: ResultType::Unknown,
281285
comments: Vec::new(),
@@ -307,9 +311,11 @@ impl App {
307311
saved,
308312
};
309313
app.restart();
310-
// A relaunch opens at the issue it was showing.
314+
// A relaunch opens at the issue it was showing, or else at the
315+
// list's first (`apply_issues`).
311316
if let Some(id) = app.saved.selection.clone() {
312317
app.reveal_selected = true;
318+
app.select_first = false;
313319
app.select(id);
314320
}
315321
app
@@ -1135,6 +1141,17 @@ impl App {
11351141
fn apply_issues(s: &mut App, rows: Vec<Issue>, result_type: ResultType, tx: &mut Tx) {
11361142
s.issues = rows;
11371143
s.issues_result = result_type;
1144+
// With no issue selected at launch, the list's first, as zbugs-swift
1145+
// does, so the issue column shows one. Only the first time: an issue
1146+
// deleted later leaves none selected.
1147+
if s.select_first
1148+
&& let Some(id) = s.issues.first().map(|i| i.id.clone())
1149+
{
1150+
s.select_first = false;
1151+
if s.selected.is_none() {
1152+
s.select(id);
1153+
}
1154+
}
11381155
if s.reveal_selected
11391156
&& let Some(id) = &s.selected
11401157
&& s.issues.iter().any(|i| &i.id == id)

0 commit comments

Comments
 (0)