Skip to content

feat(meetings): build the v2 action slot on the resolver - #3313

Merged
nunoeufrasio merged 3 commits into
feat/meeting-details-v2from
feat/issue-1775
Oct 6, 2026
Merged

nunoeufrasio merged 3 commits into
feat/meeting-details-v2from
feat/issue-1775

Conversation

@nunoeufrasio

Copy link
Copy Markdown
Contributor

Closes #1775 · epic #1765 · targets the integration branch feat/meeting-details-v2 (nothing reaches main until release PR #3249)

Summary

E2-01 adds the V2 action slot: the rail card's one control. It is driven by resolveActionSlot (E0-02), so no viewer ever gets V1's silent empty rail.

  • components/action-slot/
    • Always renders exactly one kind and carries it as data-kind, across all nine kinds.
    • none still renders as an empty slot, hidden with empty:hidden, rather than being absent (FR-020, SC-004).
    • join is complete. The other eight kinds get one line of real copy each until the issue that owns them builds the full design (see Decisions below). Visitors get a Sign in button that keeps the page URL, ?password= included, as returnTo.
    • The prototype's "Public meeting. Anyone with this link can join." shows under Join, only when privacy.openToPublic is true.
    • The early-join rule moves here from the time banner (decided on feat(meetings): add the v2 time banner #3297). Before the window, a viewer who will be able to join once it opens sees "You can join up to N minutes before the start time." The banner's before-line is now just "Starts in …".
  • components/join-action/: the Join control (FR-027).
    • States: one button with data-state loading, ready or error. Loading is also the server render, since the URL is fetched in the browser only.
    • Fetch: getPublicMeetingJoinUrl with the viewer's email, as V1 does. It refetches only when the meeting id, password or email changes, not on every lookup emission.
    • Link check: only http(s) links reach the anchor or window.open.
    • Errors: an error offers Try again. NOT_REGISTERED_FOR_MEETING explains that the account's email is not on the invite list; joining with another email is the guest form's job (E2-06).
    • Auto-join: V1's auto-join is kept, and ?zoom_redirect=false turns it off. With noopener, window.open always returns null, so V1's "Popup Blocked" branch could never run. V2 shows one toast that points at Join in case the popup was blocked.
  • components/organizer/: "Organized by" under a divider in the rail card, as in the prototype (FR-014, moved from E1-03).
    • The organizer is the owner, else the creator (resolveMeetingOrganizer).
    • It is absent for anonymous viewers and when nobody resolves.
    • The name falls back to the username, then the email, so a missing name never renders as "undefined". It wraps rather than truncating, so no hover-only tooltip is needed.
  • MeetingDetailsStateService gains privacy, viewerRole, actionSlot, joinsInWindow and signInHref.
    • The header and identity bar now read privacy and signInHref from it instead of computing their own.
    • The sign-in tests moved to the service spec.
  • Shared: MeetingJoinUrlState.
  • Specs:
    • testid-contract.md gets the new testids and a meeting-action-join-button[data-state] section, and drops the rail placeholder.
    • requirements.md records FR-029 and updates FR-012.

Decisions (project owner, 2026-10-06)

  • FR-029: inside the join window the slot is Join only, as in V1. To revisit with the RSVP card (E2-05) if late RSVPs become a need.
  • Interim copy: kinds other than join show one line of copy until their own issues land:
    • register: E2-02
    • invitation-required: E2-03, which already uses its contract testid
    • rsvp: E2-04 / E2-05
    • guest-join: E2-06
    • rsvp-unavailable: N-01
    • tools: E4

Deviation from the issue

The issue asks to keep V1's join-meeting-button-immediate / -error / -loading / -form testids. testid-contract.md, written after the issue, forbids V2 from sharing any V1 value. V2 therefore uses one meeting-action-join-button with data-state, and V1 keeps its own testids. This is noted in the contract.

Validation

  • Tests: V2 page specs 154; all meetings specs 872 before the review fixes; shared 3587. yarn build, check-types, lint:check (0 errors) and format:check are green.
  • Local visual check: run on a worktree dev server with the gate forced on and the meeting and join-URL calls stubbed. I checked join ready, join error (restricted, NOT_REGISTERED_FOR_MEETING), before the window (RSVP plus the early-join hint), and an anonymous visitor in the window (sign in, organizer hidden). All matched the prototype's rail. The temporary preview edits were reverted before any commit.
  • Pre-PR review: one round with the general, repo-code and learnings reviewers, all findings fixed in 59e1a6d:
    • http(s)-only join links
    • a stable fetch key
    • an honest auto-join toast
    • no hover-only tooltip on the organizer name
    • a missing px comment
    • the shared privacy signal
    • a zoom_redirect test that could not fail (both changed tests mutation-checked)
  • /lfx-self-serve-pr-readiness against the integration branch: READY. Its one nit is diff size, +1140, about 590 of which are specs and spec docs.
  • /preflight: passes.

🤖 Generated with Claude Code

Closes #1775

Signed-off-by: Nuno Eufrásio <nmeufrasio@gmail.com>
Refs #1775

Signed-off-by: Nuno Eufrásio <nmeufrasio@gmail.com>
@nunoeufrasio
nunoeufrasio requested a review from a team as a code owner October 6, 2026 10:07
@nunoeufrasio
nunoeufrasio requested review from MRashad26 and dealako and a balanced review from Copilot October 6, 2026 10:08
@coderabbitai

coderabbitai Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Essentials
  • Run ID: 960f48c8-72f7-4894-abd0-6ad2360bd8d7

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@cursor

cursor Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

PR Summary

Medium Risk
Join flow touches meeting URLs, auto-open behavior, and sign-in returnTo (including query params); mistakes could affect access messaging or duplicate meeting tabs, but logic largely mirrors V1 with added validation and tests.

Overview
Adds the V2 meeting details action rail: a single ActionSlotKind from MeetingDetailsStateService (via resolveActionSlot), so viewers always see copy or a control instead of V1’s empty rail.

MeetingActionSlotComponent switches on all nine kinds: full Join via MeetingJoinActionComponent (fetch join URL, loading/ready/error, http(s) validation, V1 auto-join with claimAutoJoin and ?zoom_redirect=false), interim messages and Sign in for visitors, public-meeting explainer, and early-join hint moved out of the time banner. MeetingOrganizerComponent shows “Organized by” under a divider for signed-in users.

MeetingDetailsStateService centralizes privacy, viewerRole, actionSlot, joinsInWindow, signInHref, pastAccessKnown, and past-load ended time state; header and identity bar consume those signals. Page shell wires the new rail pieces and drops the rail placeholder. Specs and testid-contract.md / requirements.md document FR-012, FR-029, and new testids.

Reviewed by Cursor Bugbot for commit 7fff9e8. Bugbot is set up for automated code reviews on this repo. Configure here.

dealako commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

@nunoeufrasio I'm starting review 1 of this pull request now (started 2026-10-06 10:10 UTC). This usually takes a few minutes — I'll update this comment with the summary when I'm done.


Hi @nunoeufrasio, thanks for a well-scoped and well-documented PR.

The action slot is cleanly driven by resolveActionSlot, none renders as an empty slot rather than a missing one, and the Join control handles loading/ready/error, http(s)-only links and a stable fetch key. Moving privacy and signInHref into the state service removes duplicated logic from the header and identity bar. I found no correctness, security, or privacy problems.

⚪ Nit: 1 issue: module-level isHttpUrl helper belongs in @lfx-one/shared/utils

✅ Approved

➡️ This auto-review agent is a preview. Please send feedback to @dealako.

@dealako dealako left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking, security or privacy findings; one nit inline. Approving.


Generated by Claude Code

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Action resolution and auto-join lifecycle behavior still need correction.

Review effort: Balanced
Findings: 1 High severity · 2 Medium severity

Open (3)
What changed in this PR

Adds the resolver-driven action rail to feature-gated meeting details V2 on the integration branch, leaving V1 unchanged.

Changes:

  • Implements Join loading, retry, URL validation, and auto-join behavior.
  • Adds organizer details and shares privacy, sign-in, and action state.
  • Updates component tests, requirements, and test-ID contracts.
File Description
specs/​011-meeting-details-redesign/​testid-contract.md Defines action and organizer test IDs.
specs/​011-meeting-details-redesign/​requirements.md Records early-join placement and Join-only behavior.
packages/​shared/​src/​interfaces/​meeting-view-model.interface.ts Adds join-URL state type.
apps/​lfx-one/​src/​app/​modules/​meetings/​meeting-details-page/​meeting-details-state.service.ts Centralizes action, privacy, and sign-in state.
apps/​lfx-one/​src/​app/​modules/​meetings/​meeting-details-page/​meeting-details-state.service.spec.ts Tests resolver inputs and sign-in links.
apps/​lfx-one/​src/​app/​modules/​meetings/​meeting-details-page/​meeting-details-page.component.ts Imports new rail components.
apps/​lfx-one/​src/​app/​modules/​meetings/​meeting-details-page/​meeting-details-page.component.spec.ts Adds rail component stubs.
apps/​lfx-one/​src/​app/​modules/​meetings/​meeting-details-page/​meeting-details-page.component.html Replaces the rail placeholder.
apps/​lfx-one/​src/​app/​modules/​meetings/​meeting-details-page/​components/​time-banner/​time-banner.component.ts Removes early-join copy from the banner.
apps/​lfx-one/​src/​app/​modules/​meetings/​meeting-details-page/​components/​time-banner/​time-banner.component.spec.ts Updates before-window expectations.
apps/​lfx-one/​src/​app/​modules/​meetings/​meeting-details-page/​components/​organizer/​organizer.component.ts Resolves organizer identity and visibility.
apps/​lfx-one/​src/​app/​modules/​meetings/​meeting-details-page/​components/​organizer/​organizer.component.spec.ts Tests identity fallbacks and hiding.
apps/​lfx-one/​src/​app/​modules/​meetings/​meeting-details-page/​components/​organizer/​organizer.component.html Renders organizer details.
apps/​lfx-one/​src/​app/​modules/​meetings/​meeting-details-page/​components/​join-action/​join-action.component.ts Fetches, validates, and opens join links.
apps/​lfx-one/​src/​app/​modules/​meetings/​meeting-details-page/​components/​join-action/​join-action.component.spec.ts Tests Join states, retries, and auto-join.
apps/​lfx-one/​src/​app/​modules/​meetings/​meeting-details-page/​components/​join-action/​join-action.component.html Renders Join and error controls.
apps/​lfx-one/​src/​app/​modules/​meetings/​meeting-details-page/​components/​identity-bar/​identity-bar.component.ts Uses the shared sign-in link.
apps/​lfx-one/​src/​app/​modules/​meetings/​meeting-details-page/​components/​identity-bar/​identity-bar.component.spec.ts Tests shared-link rendering.
apps/​lfx-one/​src/​app/​modules/​meetings/​meeting-details-page/​components/​header/​header.component.ts Uses shared privacy state.
apps/​lfx-one/​src/​app/​modules/​meetings/​meeting-details-page/​components/​header/​header.component.spec.ts Updates privacy-state fixtures.
apps/​lfx-one/​src/​app/​modules/​meetings/​meeting-details-page/​components/​action-slot/​action-slot.component.ts Connects resolver state and early-join hints.
apps/​lfx-one/​src/​app/​modules/​meetings/​meeting-details-page/​components/​action-slot/​action-slot.component.spec.ts Tests all nine action kinds.
apps/​lfx-one/​src/​app/​modules/​meetings/​meeting-details-page/​components/​action-slot/​action-slot.component.html Renders action kinds and contextual copy.

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Refs #1775

Signed-off-by: Nuno Eufrásio <nmeufrasio@gmail.com>
Copilot AI balanced review requested due to automatic review settings October 6, 2026 11:45

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

The RSVP message incorrectly tells organizers who are not invitees that they are on the invite list.

Review effort: Balanced
Findings: None

Resolved since last review (3)
Previously missed (1)

In code that hasn't changed since last review

Medium severity Incorrectly labels uninvited organizers as invited

apps/​lfx-one/​src/​app/​modules/​meetings/​meeting-details-page/​components/​action-slot/​action-slot.component.html:50

Before the join window, resolveUpcomingActionSlot() returns rsvp for organizers whenever RSVP tracking is enabled, even if meeting.invited is false. This message therefore incorrectly tells those organizers they are on the invite list. Use neutral copy here until the RSVP card lands, or check meeting.invited before claiming the viewer is invited.

@nunoeufrasio
nunoeufrasio merged commit c458d39 into feat/meeting-details-v2 Oct 6, 2026
10 checks passed
@nunoeufrasio
nunoeufrasio deleted the feat/issue-1775 branch October 6, 2026 11:58
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.

3 participants