diff --git a/src/messaging/channels.rs b/src/messaging/channels.rs index 8dac578..601a38f 100644 --- a/src/messaging/channels.rs +++ b/src/messaging/channels.rs @@ -245,7 +245,9 @@ impl RenderOnce for ChannelList { .iter() .flat_map(|(_, rows)| rows) .any(|(key, _)| key == open); - assert!(listed, "no chat {open}"); + if !listed { + log::error!("channel list: no chat {open}; none open"); + } } let open: OnValue = { let (id, on_open) = (self.id.clone(), self.on_open.clone()); diff --git a/src/messaging/members.rs b/src/messaging/members.rs index 9bb1b5d..3454b9c 100644 --- a/src/messaging/members.rs +++ b/src/messaging/members.rs @@ -124,7 +124,9 @@ impl RenderOnce for MemberList { fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement { if let Some(selected) = &self.selected { let listed = self.members.iter().any(|member| member.key == *selected); - assert!(listed, "no member {selected}"); + if !listed { + log::error!("member list: no member {selected}; none selected"); + } } let theme = cx.theme(); let colors = &theme.colors; diff --git a/src/messaging/tests/people.rs b/src/messaging/tests/people.rs index 98ee061..acc2393 100644 --- a/src/messaging/tests/people.rs +++ b/src/messaging/tests/people.rs @@ -225,3 +225,40 @@ fn an_emoji_is_searched_and_picked_and_focus_comes_back(cx: &mut TestAppContext) "the words follow the emoji field in Tab order" ); } + +/// A chat list and a member list whose open chat and selected member left. +struct Departed; + +impl Render for Departed { + fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { + div() + .w(px(320.0)) + .child( + crate::messaging::ChannelList::new("chats") + .section( + "Channels", + [( + "general", + crate::messaging::ChannelItem::new("general", "general"), + )], + ) + .open("gone"), + ) + .child( + MemberList::new( + "members", + [Member::new("ana", "Ana Lima", Presence::Online)], + ) + .selected("gone"), + ) + } +} + +#[gpui::test] +fn a_chat_or_member_that_left_marks_none(cx: &mut TestAppContext) { + setup(cx); + let (_, cx) = cx.add_window_view(|_, _| Departed); + settle(cx); + assert!(cx.debug_bounds("item-selected-general").is_none()); + assert!(cx.debug_bounds("item-selected-Ana Lima").is_none()); +} diff --git a/tasks/panics.md b/tasks/panics.md index 51da3c8..ccdfc64 100644 --- a/tasks/panics.md +++ b/tasks/panics.md @@ -42,7 +42,7 @@ Each item reads every site in its folders, fixes what the rules name, updates ca - [x] P25 Generative — `src/generative` - [x] P26 Media — `src/media` - [x] P27 Files — `src/files` -- [ ] P28 Messaging — `src/messaging` +- [x] P28 Messaging — `src/messaging` - [ ] P29 Mail — `src/mail` - [ ] P30 Calendar — `src/calendar` - [ ] P31 Project — `src/project` @@ -93,3 +93,4 @@ One line per item: what changed, what stayed and why, review rounds. - P25: a verdict, ratio, style, shot, voice, variation or prompt version gone from its list logs and marks none (no picture, no Vary or Upscale, no diff); embedding points in a gone group stay out; an epoch and GPU memory past their whole log and peg. A seed field holds a seed: each field handed in is fitted, and text that is no seed clears; in `TextInput`, a fit starts a fresh undo history, undo keeps and `set_text` fits against the committed text, never an input method's unfinished one (`committed`), and a lifted mask stroke stays on its picture. Kept: one-value contracts, unique group names, one snapshot's loss lists, two to four sides, the legend's lookup. Review: 5 rounds, the cap; round 5's finding (`set_text` over a composition) is fixed and tested, and P26's review checks it. - P26: a live position past its media logs and sits at the end (`media::played`); loaded time, a trim and chapters past the length peg or stay out; a gone device or track, or a refused trim, leaves none and clears the last; a dropped root path shows whole; subtitle times no clock holds read as none; a rail layout left no width gives no share (`scrubber::along`), so hover, press and drag skip it; waveform buckets count in `u64`, which a 32-bit web `usize` outgrows. Kept: non-zero lengths, one-value contracts, one list's uniqueness and order, lookups by construction. Review: 4 rounds; it also passed P25's last fix. - P27: an owner's pick gone from a grid, an explorer folder or a directory listing logs and is kept as given, so it replaces a local pick and marks none (P13's listing now does the same); the columns mark a path name only while its parent holds it as a folder; moved bytes past a total log, the bar already capped; duplicate marks on no copy, or on every copy of a group, are dropped, so Remove never takes a group's last copy. Kept: one list's uniqueness, a snapshot's path and levels, items and downloads, lookups by construction, `datetime` with a constant pattern. Review: 3 rounds. +- P28: an open chat or selected member no longer listed logs and marks none. Kept: one list's uniqueness and non-emptiness, one message's receipt and replies, constant `datetime` patterns, picks from lists without disabled rows, keys from the same render. Review: 1 round.