8000 feat(ui): POC C — Discover inside the agenda (nearby scope) by MichaelDimmitt · Pull Request #676 · huddlz-hq/huddlz · GitHub
[go: up one dir, main page]

Skip to content

feat(ui): POC C — Discover inside the agenda (nearby scope) - #676

Closed
MichaelDimmitt wants to merge 5 commits into
huddlz-hq:mainfrom
MichaelDimmitt:poc/670-discover-in-agenda
Closed

MichaelDimmitt wants to merge 5 commits into
huddlz-hq:mainfrom
MichaelDimmitt:poc/670-discover-in-agenda

Conversation

@MichaelDimmitt
@MichaelDimmitt MichaelDimmitt commented Oct 9, 2026 •
Copy link
Copy Markdown
Contributor

POC variant C for #67 8000 0: bring Discover's results into /agenda as a third scope beside
RSVPs and Groups, so a person who signs in looking for something to go to does not have to
leave for /discover.

Summary

One new chip, and one structural change behind it. The agenda used to derive everything it
draws inside handle_params/3, which only works when every scope is synchronous:

 handle_params(params, _uri, socket)
   load_entries(user)              # my RSVPs
   load_group_extras(user)         # + what my groups have on
-  all = if scope == :groups, do: everything, else: own
-  grid_entries / agenda_window / week_days / legend_items
-  assign(...)
+  assign(scope, counts, own_entries, everything_entries, nearby_location)
+  request_nearby(scope)           # :nearby only — start_async
+  put_view_state(socket)
+
+handle_async({:nearby, ref}, {:ok, entries})
+  assign(nearby_entries)
+  put_view_state(socket)          # same path, not a second one
+
+put_view_state(socket)
+  all = scope_entries(socket)     # :mine | :groups | :nearby
+  grid_entries / agenda_window / week_days / legend_items

put_view_state/1 is the whole answer to sync-vs-async: the async scope redraws through
exactly the code path the synchronous ones use, so there is no second rendering path and
no duplicated day-grouping logic.

The query is Discover's, borrowed whole rather than written again:

Communities.search_huddlz_in_time_zone(
  nil,                    # no text query
  :upcoming,              # Discover's own default date filter
  nil,                    # no event_type
  home_latitude, home_longitude, 25,
  nil,                    # NO relationship arg -> reaches huddlz that aren't mine
  home_time_zone, ...)
  |> reject(id in my RSVPs + my groups)
  |> map(%{huddl: h, roles: MapSet.new()})   # no roles -> agenda labels it "No RSVP"
  |> put_calendar_time()                      # the agenda's own entry shape

Nothing about day grouping, status logic or list rendering is new — nearby entries enter the
agenda's existing shape and the existing code draws them.

 <div class="chip-group cal-scope">
   <.chip id="calendar-scope-mine"   count={@counts.mine}>RSVPs</.chip>
   <.chip id="calendar-scope-groups" count={@counts.groups}>Groups</.chip>
+  <.chip id="calendar-scope-nearby" count={@counts.nearby}>Nearby</.chip>  # nil -> no count
 </div>
+<p id="calendar-nearby-note">Upcoming huddlz within 25 miles of <b>St. Augustine, FL</b>
+  that you haven't responded to.  [Search all huddlz →]</p>

The two walls the spec flagged, and how they were resolved:

  • Chip counts. scope_counts/4 compares two in-memory lists; a paginated async search
    cannot be counted that way. Resolved by making counts optional per scope:
    counts.nearby is nil, and <.chip> already omits the count span for nil, so no
    component change was needed. Counting after the search landed was rejected — a number
    that appears a beat after the chip row paints reads as a bug. The cost is a permanently
    asymmetric chip row
    ("RSVPs 2 · Groups 3 · Nearby"), not a polish item.
  • Sync vs async. Resolved cleanly by the put_view_state/1 split above, with a stale-ref
    guard so a late reply from a scope the person has already left is dropped. The agenda's own
    two scopes are untouched and still paint in the first render.

Did the agenda stay calm? Yes — but the calm is a restraint, not a property of the
design. Nearby is a fixed query with no knobs: click 1 lands on one list with one added
control, where Discover's console has ~14 (search field, huddlz/groups tabs, Location,
Within, Type, When, Sort). The moment someone adds a "Within" or "Type" control to this
surface, the agenda becomes Discover with a date rail and the variant's claim evaporates.
Nothing in the code prevents that. Full honest write-up, including the measured click count
(2 — the same as the baseline, because Discover already defaults a signed-in person's
location) is in the Findings section of docs/research/post-login-landing-pocs.md.

Nearby is agenda-only: parse_scope/2 takes the view mode and falls back to :mine for the
week and month grid, because a stranger's huddl is not a calendar fact.

Evidence

Screenshot

Variant C

The Nearby filter on the agenda: a huddl from a group the person has not joined, with the scope note naming the distance and home location. The chip carries no count, by design.

The new scenario, before implementation — the nearby filter does not exist:

mix test test/features_test.exs --only agenda_nearby

** (Cucumber.StepError) Step failed:
     And I switch to huddlz near me
  in scenario "The nearby filter shows an upcoming huddl from a group I do not belong to"
  selector "#calendar-scope-nearby" did not return any element within:

   [passed] Given my home search location is set
   [passed] And a group I have not joined has scheduled "Saturday trail run" near my home location
   [passed] When I open the agenda
   [failed] And I switch to huddlz near me

After:

mix test test/features_test.exs --only agenda_nearby

   [passed] Given my home search location is set
   [passed] And a group I have not joined has scheduled "Saturday trail run" near my home location
   [passed] When I open the agenda
   [passed] And I switch to huddlz near me
   [passed] Then the agenda lists "Saturday trail run" without an RSVP status

Five scenarios cover: a nearby huddl from a group I don't belong to; nearby excluding huddlz
I already responded to; nearby excluding huddlz out of range (PostGIS ST_DWithin); the
RSVPs and Groups filters still behaving as before; and choosing nearby with no home search
location on file.

mix compile --warnings-as-errors is clean.

Merge Danger

Door: two-way.

Additive. No migration, no new resource, no change to an existing action. Reverting is
deleting a scope — the two existing scopes' code paths are unchanged and still synchronous.
The one non-additive edit is the handle_params/3 → put_view_state/1 split inside
CalendarLive, which is a pure refactor of that module's own private functions.

Blast Radius: CalendarLive.

/agenda, /calendar/week and /calendar/month all render through the refactored
derivation step, so a mistake there would affect all three views rather than only the new
scope — which is why the existing calendar and agenda scenarios were run alongside the new
ones. scope_param/2 now drops ?scope=nearby from links leaving the agenda, so a
hand-typed /calendar/week?scope=nearby falls back to the person's own RSVPs rather than
erroring. New CSS is confined to .cal-scope-note and .cal-agenda-skeleton; no existing
selector was changed.

Part of #670

This is a POC for comparison, not intended to merge as-is. Verified by
scenario (failing -> passing) and a clean --warnings-as-errors compile
only; mix precommit is a deliberate follow-up after review.

Model: Claude Opus 5.5 (claude-opus-5-5)

🤖 Generated with Claude Code

Issue huddlz-hq#670 asks for several competing proofs of concept, each as its own
draft PR, so one can be chosen and the rest declined. This is the shared
brief those PRs are built from: the problem, what the code already does,
one section per variant with its own task list, and the rubric they are
all judged against.

Records three findings that shape the variants: the return_to plumbing is
already complete and safe but only the huddl page's RSVP button produces
such a link; the agenda already loads both scopes every render to feed its
chip counts; and the agenda has no query for huddlz outside a person's
groups.

Part of huddlz-hq#670
The POC variants are compared partly on whether people want to use them,
so a variant that looks unfinished loses Desirable and Delightful for
reasons unrelated to its idea. Ask each one to polish the surface it
changes, and keep the scope discipline pointed at everything else.

Part of huddlz-hq#670
The agenda only ever showed huddlz the person already has a relationship
to, so somebody who signs in looking for something to go to has to leave
for /discover. This adds a third scope beside RSVPs and Groups, showing
upcoming huddlz near their home search location that they have not
responded to, with no filters to configure.

Nearby reuses Discover's query whole rather than adding a second search
path: the same :search action, the same upcoming date filter and the same
PostGIS distance filter, with no relationship argument so it reaches
huddlz the person has nothing to do with. Results are put into the
agenda's own entry shape, so the existing day grouping, status logic and
list rendering draw them unchanged — a stranger's huddl reads as
"No RSVP".

- parse_scope/2 now takes the view mode: nearby is agenda-only, since a
  stranger's huddl is not a calendar fact and does not belong in a month
  cell. scope_param/2 drops the param from links leaving the agenda.
- handle_params/3 is split: it loads the two synchronous scopes and sets
  URL state, and put_view_state/1 derives everything the views draw from
  whichever scope is selected. handle_async/3 calls put_view_state/1
  again, so the async scope redraws through exactly the same path as the
  synchronous ones. A stale-ref guard drops a late reply.
- The Nearby chip carries no count. RSVPs and Groups are counted by
  comparing two in-memory lists; a paginated search cannot be counted the
  same way, and a number arriving a beat late reads as a bug.
- Three states the other scopes do not have, each answered in place: no
  saved location points at /profile, a search in flight draws placeholder
  rows in the agenda's shape, and nothing in range offers Discover.
- The window is the agenda's existing @agenda_days, so the scopes stay
  comparable; the search overfills it, so Nearby never paginates.

Part of huddlz-hq#670
Fill in the Discover-inside-the-agenda section: the measured click count
(2, the same as the baseline — the win is controls to ignore, not clicks),
how the chip-count and sync-vs-async walls were resolved, where the design
strained, and an honest answer on whether the agenda stayed calm.

Part of huddlz-hq#670
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

0