Skip to content

Events

Events are scheduled, location-anchored gatherings. The UX covers creating, finding, joining, leaving, and hosting — plus the chat room that opens to participants once they're in.

Entry points

From Action
Discover map Tap an event marker, or long-press an empty location to create one.
Discover list Tap an event card.
Floating "+" on the map Opens the event-create modal.
Horizon → Events The user's personal events list (mine + hosted + pending).
Share link tomoda.life/events/:id opens the public share page; "Open in app" hands off to the in-app detail.
Notification deep link (Future: approval / start-soon notifications open the event detail.)

Primary flow — discover and join

  1. Discover an event. The user taps a marker or list card. A detail sheet opens with title, host avatar, date/time, location, capacity, description, and a participant strip.
  2. Inspect. Scrolling reveals the full description, the location map, the participant list, and the host's other upcoming events. The sheet header pins to the top.
  3. Tap "Join". If the event is public and auto_accept, the user is added immediately as approved and the action bar flips to "Leave" + "Open chat". If auto_accept is off, the user becomes pending and the action bar flips to "Awaiting approval".
  4. Open chat. Approved participants get a "Chat" affordance that opens the event's embedded group chat room. The chat is unavailable while pending.
  5. Pre-event reminder. (Stub — no notification service exists yet.) The user is expected to surface event start time via Horizon → Events; the dedicated "starts in N minutes" reminder is not yet wired.

Primary flow — create

  1. Open the modal. The "+" on the map or a "Create" action on Horizon opens the create-event sheet.
  2. Pick a location. Either tap-and-hold on the map (location prefilled), or use the location search. The selected place name + address appear.
  3. Set time. Pick a start and (optionally) end time. The timezone is resolved from the location's coordinates and shown next to the time.
  4. Configure. Title, description, category, capacity, visibility (public / friends / invite_only), auto-accept toggle, optional access code, waitlist toggle.
  5. Publish. The event appears on the map within seconds. The creator is auto-added as the host (admin in the chat room).
  6. Manage. The host sees a different action bar on the detail sheet: pending requests show a count badge; tapping reveals an approval list.

Sub-flows

Sub-flow Behavior
RSVP'd vs not Action bar varies: not joined → "Join". Pending → "Awaiting approval" + "Withdraw". Approved → "Leave" + "Chat". Host → "Manage" + "Chat".
Host approval Host opens the participant list. Pending requests have ✓ and ✗ actions inline. Approve adds the user to the chat room; reject removes them.
Access-code events "Join" prompts for the code first. Wrong code → an inline error appears under the input. Correct code → the join proceeds.
Waitlist When capacity is full and waitlist_enabled is on, "Join" places the user on the waitlist. If a participant leaves, the next waitlisted user is auto-promoted (and they see a state change next time they open the event).
Required attestations If the event needs an attestation the user doesn't have (verified email, complete profile, etc.), the "Join" action is disabled with a tooltip explaining which attestation is missing.
Past events After end time, the action bar collapses to "View chat" (read-only after retention window). Detail sheet shows a "Completed" badge in place of the join button.
Cancellation The host has a destructive "Cancel event" action in the manage menu. A confirmation modal lists the participant count. Confirming flips the event to cancelled and broadcasts a system message to the chat.
Transfer host "Transfer host" picker lists current participants. Selecting one makes them host; the previous host is demoted to participant. The chat room follows.
Leave Participants tap "Leave" → confirmation modal. Leaving removes them from the chat room. If the host leaves and there are no other participants, the event is deleted entirely; if others remain, the event flips to cancelled so the chat history survives.
Public share page /events/:id is reachable without sign-in. Visitors see the event card with a sign-up CTA. Authenticated visitors see the same in-app detail.

States

State What the user sees
Loading detail Skeleton avatar + skeleton lines. Action bar is a single shimmering pill.
Approved + chat ready Action bar: "Leave" + "Chat". Chat icon shows an unread badge if there are new messages.
Pending Action bar: "Awaiting approval". A muted line: "The host hasn't approved you yet."
Host (own event) "Manage" bar with counts of pending / approved / waitlisted participants.
Capacity full, waitlist off "Join" is disabled, replaced by "Event is full".
Capacity full, waitlist on "Join waitlist" with a position indicator after the first join.
Past / completed "Completed" badge on the header. Action bar replaced with "View chat" (or removed if retention has elapsed).
Cancelled A red "Cancelled" banner spans the header. The chat is read-only.
Invite-only and not invited The user reaches the page (via a link) but the detail sheet shows "This event is invite-only" — no action bar, no participant list.

Edge cases & gotchas

  • Join eligibility is checked in order. Access code → capacity → required attestations → visibility (friends-only requires friendship with the host) → auto-accept. The error message reflects the first failure, not all of them. A user who is missing an attestation and is not a friend will see only the attestation error.
  • Chat room opens immediately on auto-accept. If the event is public + auto-accept, the user is in the chat room the moment they tap Join, with no approval delay.
  • Friends-only check is at the host level. "Friends" visibility means friends of the host, not friends of anyone already in the room.
  • Withdraw a pending join. The user can withdraw before approval — it cancels the request. The host's pending list updates within a few seconds.
  • Approval after capacity reached. If the host delays approving a pending user past the point where the event is now full, approval still proceeds — pending users aren't subject to capacity, only fresh joins are.
  • Leaving the chat room. Leaving the event removes the user from the chat room as a side effect. There is no separate "leave chat" for an event chat.
  • Past-event messages are purged. Once end_time + retention_days is past, the event's chat history is hard-deleted by a background job. The user sees the chat go empty next time they open it.
  • Event cancellation broadcast. Participants get a system message in the chat ("Event cancelled by host") and the event flips to a cancelled banner on the detail sheet — both happen on the same broadcast.
  • Backgrounding mid-join. If the app is backgrounded between the tap and the response, the join completes server-side; on resume, the action bar reflects the new state without re-tap.
  • Profile incomplete. A user with no username / DOB cannot reach the create flow — the auth gate redirects them to onboarding first.

See also