Skip to content

ECHO-972 feat(sharing): invite non-members from the project sharing modal - #1060

Merged
ussaama merged 2 commits into
mainfrom
feat/project-sharing-inline-invites
Sep 11, 2026
Merged

ECHO-972 feat(sharing): invite non-members from the project sharing modal#1060
ussaama merged 2 commits into
mainfrom
feat/project-sharing-inline-invites

Conversation

@ussaama

@ussaama ussaama commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Sharing a private project with an email that isn't on the workspace used to dead-end in a 404 toast. The modal now offers a workspace invite that carries the project, and the share is granted when the person joins.

  • workspace_invite.project_id records which private project to share on accept. Every accept path grants it: by id, by hash, the by-hash heal, onboarding auto-accept, the org multi-consume sweep, and the stale-invite sweep on re-inviting an active member
  • POST /v2/projects/:id/members returns a structured not_a_member code so the modal can pivot into an invite instead of matching on message text
  • GET /v2/projects/:id/invites lists the project's pending invites, shown on the Access tab and in the modal, with revoke. It returns empty below the innovator tier so a lapsed workspace doesn't 403 on every open
  • a share now only unlocks the project. Permissions are the person's workspace summary.sent, the ids silently changed to positional form, so they no longer matched the translated entries. Every non-English locale would have fallen back to English. Nothing had re-run extract and compile after that edit, so the catalogs still held the old named ids.

Sharing a private project with an email that isn't on the workspace used to
dead-end in a 404 toast. The modal now offers a workspace invite that carries
the project, and the share is granted when the person joins.

- workspace_invite.project_id records which private project to share on
  accept. Every accept path grants it: by id, by hash, the by-hash heal,
  onboarding auto-accept, the org multi-consume sweep, and the stale-invite
  sweep on re-inviting an active member
- POST /v2/projects/:id/members returns a structured not_a_member code so the
  modal can pivot into an invite instead of matching on message text
- GET /v2/projects/:id/invites lists the project's pending invites, shown on
  the Access tab and in the modal, with revoke. It returns empty below the
  innovator tier so a lapsed workspace doesn't 403 on every open
- a share now only unlocks the project. Permissions are the person's workspace
summary.sent, the ids silently changed to positional form, so they no longer matched the translated entries. Every
non-English locale would have fallen back to English. Nothing had re-run extract and compile after that edit, so the
catalogs still held the old named ids.
@linear

linear Bot commented Sep 10, 2026

Copy link
Copy Markdown

ECHO-972

Enriching each share row fetched the person's app_user and their avatar
separately, so a project shared with ten people cost twenty round trips.
The listing now reads all app_user rows and all avatars in one query each,
giving three queries for the whole list regardless of its length. The
single-row helper stays for add_project_share, which handles exactly one
member by definition.

Covered by a test that counts Directus calls across five shares and fails
on any per-row fetch. Redaction, ordering, and the skip for shares whose
account or workspace membership is gone are unchanged.
@ussaama
ussaama merged commit 1e4a64a into main Sep 11, 2026
11 checks passed
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