feat(invitations): card-style invite email with kind-0 team profile#33
Merged
alejandro-runner merged 1 commit intosynvya-stagingfrom Apr 22, 2026
Merged
Conversation
Rework the team-invitation email so it mirrors the accept-invite page: centred Synvya wordmark, "Team Invitation" header, white card with the team's avatar and display name, inviter + recipient + expiry, and a prominent Accept Invitation button. - New api/src/nostr_profile.rs fetches kind-0 metadata (display_name, picture) from BUNKER_RELAYS with a 3s timeout; returns None on any failure so the email still sends with the DB handle as fallback. - Prefer inviter email over the pubkey-prefix fallback when the admin has no display_name/username, so recipients see "staging@…" instead of "f7ebfcf1…". - Team display name + avatar in the email now match what the accept page shows, because both derive from the same kind-0 record. - HTML-escape all interpolated values and restrict avatar URLs to http/https. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Rework the team-invitation email so it mirrors the accept-invite page recipients see when they click through: centred Synvya wordmark, Team Invitation heading, white card with the team's avatar + display name, inviter / recipient / expiry line, and a prominent Accept Invitation button.
Solves two issues with the old email:
f7ebfcf1…) instead of the admin's identity when the admin had nodisplay_name/usernameset. Now falls back to the admin's email.elcandado) with no avatar. Now fetches kind-0 for the team's stored key fromBUNKER_RELAYSand uses the samedisplay_name+picturethe accept page renders, so the two views match.Changes
api/src/nostr_profile.rs—fetch_profile_metadata(pubkey, relays)with a 3s timeout. ReturnsNoneon any failure (bad pubkey, unreachable relay, timeout, no kind-0, malformed JSON) so the email still sends with a sensible fallback.api/src/email_service.rs— extendedEmailSender::send_team_invite_emailto acceptteam_picture: Option<&str>,expires_at, and a renamedinviter_label; rewroteteam_invite_html/team_invite_textas the card layout. HTML-escapes all interpolated values; avatar URL restricted to http/https.api/src/api/http/teams.rs— in the invite handler, resolve inviter email, load team key pubkey, call kind-0 fetch, pass real display name / picture / expiry / inviter label. ExistingInvitationListItemresponse still uses the previousresolve_display_namebehaviour.api/src/lib.rs— exports the new module.Fallback behaviour
display_namesetpubkey[..8]…(unchanged)display_nameteam.namehandle (unchanged)picturepicturemissing or non-httpOut of scope
Visual consistency pass for
verification/password_reset/claimemails — tracked separately. Those don't have an entity to present, so the card pattern doesn't transfer; a lighter shared header/footer/button treatment can follow.Test plan
cargo check --workspacecleancargo check -p keycast_api --features awscleancargo clippy -p keycast_api --all-targets -- -D warningscleancargo test -p keycast_api --lib— 62 passed (5 new for the kind-0 parser)BUNKER_RELAYSis briefly unreachable (fallback to DB handle, no avatar)🤖 Generated with Claude Code