messaging: cannot create a group chat from an empty contact list (bootstrap UX) #191
Labels
No labels
prio_critical
prio_low
type_bug
type_contact
type_issue
type_lead
type_question
type_story
type_task
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
lhumina_code/hero_archipelagos#191
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
The New Chat form only switches to group mode (with a Group Name field) when 2+ participants are selected — see
islands/chat_form.rs:87. With 0 or 1 contacts in the system, the user cannot reach group mode at all, so the group-chat code path is unreachable through the UI.This bites in fresh test environments / new installs (we hit it during QA — only one seeded contact, no way to exercise the group flow).
Suggested fix
Allow entering a group title as soon as the form is opened; let the user create a group with 1 selected participant (or 0 — a self-group placeholder) — the title field would be visible from the start. Or expose an explicit 'Create Group' entry alongside '+ New Chat' that doesn't depend on selection count.
Found via QA session 2026-04-29.
Implementation Spec for Issue #191
Objective
Make the group-chat creation path reachable in the New Chat form when the user has 0 or 1 contacts in the system, by decoupling the visibility of the Group Name field and the DM-vs-group decision from the selected-participants count. Today the title field is gated by
selected_contacts.len() > 1(islands/chat_form.rs:87), so a fresh install with zero or one contacts cannot exercise the group code path through the UI.Requirements
services::create_groupalways takes a non-emptyparticipant_keys: Vec<String>).get_or_create_dm.on_createcallback signature must convey the user's mode choice to the archipelago so the existingparticipant_keys.len() == 1heuristic inarchipelago.rs:582no longer silently turns a 1-participant group into a DM.chat_form.rsandarchipelago.rs. No service-layer or SDK changes.cargo check -p hero_archipelagos_messaging.Files to Modify/Create
archipelagos/messaging/src/islands/chat_form.rs— Always render the Group Name field; add a "Create as group" toggle (checkbox); changeon_createto also carry anis_groupbool; updatecan_createso a group needs at least 1 participant + non-empty title; show a contacts-empty hint.archipelagos/messaging/src/archipelago.rs— Updatehandle_create(line ~574) to honor the newis_groupflag instead of inferring mode fromparticipant_keys.len(). Both desktop (~748) and mobile (~806)ChatForm { ... on_create: handle_create }call sites pick up the new signature automatically.Implementation Plan
Step 1: Extend
ChatFormso the Group Name field is always visible and the user picks DM vs Group explicitlyFiles:
archipelagos/messaging/src/islands/chat_form.rsChatFormProps::on_createfromEventHandler<(String, Vec<String>)>toEventHandler<(String, Vec<String>, bool)>. Update its doc comment.is_group = use_signal(|| false)near the existing form signals.if selected_contacts.read().len() > 1 { … }block at lines 87-99 with two always-visible UI elements at the top of.new-chat-view:<input type="checkbox">) bound tois_group. Add a new.new-chat-togglestyle.*is_group.read()instead ofselected_contacts.read().len() > 1. Keep the same.new-chat-field/.new-chat-inputclasses.selected_contacts.read().len() >= 2, forceis_grouptotrue(write only when value actually changes, usingpeek()-then-setpattern from line 59-61, to avoid a re-render loop). Do not auto-demote on shrink — once the user opted in, keep their choice.can_create(line 70):!*is_group.read()): exactly 1 selected participant, not creating.*is_group.read()): at least 1 selected participant AND a non-empty trimmed title, not creating.handle_create(lines 72-80) to callprops.on_create.call((title_val, keys, *is_group.read())).props.contacts.is_empty()—"You have no contacts yet — add a contact to start a chat or group."Add a.new-chat-hintstyle.Dependencies: none.
Step 2: Wire the new
is_groupflag through the archipelago'shandle_createFiles:
archipelagos/messaging/src/archipelago.rshandle_createat line 574 frommove |(title, participant_keys): (String, Vec<String>)|tomove |(title, participant_keys, is_group): (String, Vec<String>, bool)|.is_group:if !is_group && participant_keys.len() == 1→services::get_or_create_dm(...)(unchanged DM path).else if is_group && !participant_keys.is_empty()→services::create_group(&osis_url, &context_name, &name, participant_keys), wherenameistitleif non-empty else"New Chat". This now also fires for 1-participant groups (the bug fix).else→ setcreate_errorto a clear validation message andcreating.set(false). Defensive — the form'sdisabledCreate button already prevents reaching this branch.ChatFormcall sites (desktop ~743-749, mobile ~801-807) pass the samehandle_create, so no further wiring change.Dependencies: Step 1 (the
on_createsignature change must land first or together).Step 3: Verify build and flows
Files: none (validation only).
cargo check -p hero_archipelagos_messagingfrom the repo root and confirm a clean build.testcases/or unit-test directory exists underarchipelagos/messaging, so no test file to extend.Dependencies: Step 2.
Acceptance Criteria
is_group = trueand the archipelago routes toservices::create_group(...)rather thanget_or_create_dm(...).cargo check -p hero_archipelagos_messaging.Notes
Trade-off (option a vs option b): This spec uses option (a) — always-visible Group Name field with an explicit toggle. It is one file's worth of UI logic plus a 3-line signature change in
archipelago.rs, with no new top-level entry point. Option (b) (sibling "Create Group" button next to "+ New Chat") requires a new toolbar action, a newRoute::NewGroupvariant, and either a newGroupFormor amodeprop — materially more code and two near-identical entry points the user has to choose between.The DM-vs-group decision also moves from implicit (count-based, fragile, unreachable in the empty-contacts case) to explicit (user-driven, deterministic), which removes a class of "I chose group but only added one contact and it silently became a DM" surprises.
Dioxus / signal conventions:
chat_form.rsisuse_signalwith closure-captured handles. The newis_groupsignal follows the same pattern.peek()to compare before.set()to avoid an infinite re-render loop, mirroring the pattern at line 59-61.EventHandler<(String, Vec<String>, bool)>change is a breaking change to a private prop — both call sites inarchipelago.rsare the only consumers, so it is safe to land in one pass.Subtleties:
"New Chat"(set atarchipelago.rs:590, mirrored atservices::messaging_service::NEW_CHAT_PLACEHOLDER:267) becomes defensive only — keep for safety.creating.set(false)andcreate_error.set(Some(...))to mirror existing error handling and avoid leaving the Create button stuck in the loading state.services::create_groupaccepts any non-emptyVec<String>. No server-side change required.