Calendar · open times start over on new days
AvailabilityPicker kept the day chosen in keyed state whatever days the owner passed next, and the slots lookup expected it among them: an owner paging to the next week crashed the app once a day was chosen. The choice is now seeded on the owner's days and pick, so a new run of days starts it over at the picked day or the first open one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
07a577b574
commit
e2eb1cfc41
3 files changed
+38
-4
No files matched your search
@@ -358,3 +358,4 @@ Per-component lines live in `tasks/`. Tags there:
|
|||||||
| T30b | 1 | FAIL | the week and day views froze the line at now and today: the moment read once went down as given and turned the ticker off |
|
| T30b | 1 | FAIL | the week and day views froze the line at now and today: the moment read once went down as given and turned the ticker off |
|
||||||
| T30b | 2 | PASS | |
|
| T30b | 2 | PASS | |
|
||||||
| T30c | 1 | PASS | |
|
| T30c | 1 | PASS | |
|
||||||
|
| T30d | 1 | FAIL | a chosen day outlived the owner's days, and the next render panicked |
|
||||||
@@ -8,6 +8,7 @@ use jiff::{Timestamp, civil::Date, tz::TimeZone};
|
|||||||
use super::hours::moment;
|
use super::hours::moment;
|
||||||
use crate::{
|
use crate::{
|
||||||
forms::{Choice, ChoiceChips},
|
forms::{Choice, ChoiceChips},
|
||||||
|
layout::seeded::use_seeded,
|
||||||
theme::{ActiveTheme, TextSize},
|
theme::{ActiveTheme, TextSize},
|
||||||
typography::format,
|
typography::format,
|
||||||
};
|
};
|
||||||
@@ -41,7 +42,7 @@ pub(crate) fn open_slots(
|
|||||||
starts
|
starts
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Open times to meet over some days: each day with its count of open slots, then the slots of the one chosen. Busy stretches, the working hours and the past close the rest. A pick hands on the slot's start.
|
/// Open times to meet over some days: each day with its count of open slots, then the slots of the one chosen. Busy stretches, the working hours and the past close the rest. A pick hands on the slot's start; new days from the owner start the choice over.
|
||||||
#[derive(IntoElement)]
|
#[derive(IntoElement)]
|
||||||
pub struct AvailabilityPicker {
|
pub struct AvailabilityPicker {
|
||||||
id: ElementId,
|
id: ElementId,
|
||||||
@@ -149,8 +150,9 @@ impl RenderOnce for AvailabilityPicker {
|
|||||||
.iter()
|
.iter()
|
||||||
.find(|(_, slots)| !slots.is_empty())
|
.find(|(_, slots)| !slots.is_empty())
|
||||||
.map(|(day, _)| *day);
|
.map(|(day, _)| *day);
|
||||||
let chosen = window.use_keyed_state((self.id.clone(), "day"), cx, |_, _| None::<Date>);
|
let seed = (self.days.clone(), picked_day.or(first_open));
|
||||||
let shown = chosen.read(cx).or(picked_day).or(first_open);
|
let chosen = use_seeded((self.id.clone(), "day"), seed, window, cx);
|
||||||
|
let shown = chosen.read(cx).value.1;
|
||||||
let theme = cx.theme();
|
let theme = cx.theme();
|
||||||
let quiet = |text: &'static str| {
|
let quiet = |text: &'static str| {
|
||||||
div()
|
div()
|
||||||
@@ -184,7 +186,7 @@ impl RenderOnce for AvailabilityPicker {
|
|||||||
.expect("a day's value is its date");
|
.expect("a day's value is its date");
|
||||||
log::info!("availability picker: {day}");
|
log::info!("availability picker: {day}");
|
||||||
chosen.update(cx, |chosen, cx| {
|
chosen.update(cx, |chosen, cx| {
|
||||||
*chosen = Some(day);
|
chosen.value.1 = Some(day);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -161,3 +161,34 @@ fn a_slot_pick_hands_on_its_start(cx: &mut TestAppContext) {
|
|||||||
"09:00 to 10:00 is taken"
|
"09:00 to 10:00 is taken"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn paged(host: &Planning, owner: Entity<Planning>) -> gpui::AnyElement {
|
||||||
|
let first = host.month;
|
||||||
|
let dawn = date(2026, 8, 31).at(0, 0, 0, 0).to_zoned(TimeZone::UTC);
|
||||||
|
AvailabilityPicker::new("paged", [first, first.tomorrow().expect("a day ahead")])
|
||||||
|
.zone(TimeZone::UTC)
|
||||||
|
.hours(9, 10)
|
||||||
|
.now(dawn.expect("a UTC time").timestamp())
|
||||||
|
.on_pick(move |start, _, cx| note(&owner, format!("pick {start}"), cx))
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
fn new_days_from_the_owner_start_the_choice_over(cx: &mut TestAppContext) {
|
||||||
|
let (host, cx) = planning(paged, cx);
|
||||||
|
tab_to(2, cx);
|
||||||
|
press("space", cx);
|
||||||
|
host.update(cx, |host, cx| {
|
||||||
|
host.month = date(2026, 10, 5);
|
||||||
|
cx.notify();
|
||||||
|
});
|
||||||
|
settle(cx);
|
||||||
|
tab_to(3, cx);
|
||||||
|
press("space", cx);
|
||||||
|
let nine = date(2026, 10, 5).at(9, 0, 0, 0).to_zoned(TimeZone::UTC);
|
||||||
|
assert_eq!(
|
||||||
|
heard(&host, cx),
|
||||||
|
[format!("pick {}", nine.expect("a UTC time").timestamp())],
|
||||||
|
"the chosen day of the old run is gone; the first open day shows"
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in new issue
Block a user