Skip to content

Org emails, rich links, and My Events calendar with iCal/Google Calendar export - #56

Merged
DIodide merged 7 commits into
stagingfrom
release/emails-calendar
Sep 28, 2026
Merged

DIodide merged 7 commits into
stagingfrom
release/emails-calendar

Conversation

@DIodide

@DIodide DIodide commented Sep 28, 2026 •

Copy link
Copy Markdown
Member
  • Org pages show the organization's recent campus emails (via InboxEngine) with a full-email reader and a 'Browse all in TigerInbox' link (TigerInbox /org/, TigerMail chore(deps): bump pytest from 9.0.2 to 9.0.3 in /backends/fastapi #4)
  • Event/org/email text renders links, lists and paragraphs (no more raw [https://…])
  • My Events gains a calendar view with Going / Saved / per-org calendars, each exportable via Add to Google Calendar, webcal subscribe, copy link or .ics download (secret-token feeds, resettable)

Report (with real-data screenshots): https://claude.ai/artifact/EhAQm1uNu8aG1pSbKQHG7D

lib/rich-text.ts parses html-to-text output into blocks and inline runs:
paragraphs and line breaks, '* '/'- ' list items, '[https://…]' as a
'host ↗' link, '[text](url)' markdown links and bare URLs. Only
http/https/mailto hrefs are produced; links open in a new tab with
noopener noreferrer; no dangerouslySetInnerHTML. Used for event
descriptions and org About; card previews use the same plainPreview so
bracketed URLs don't eat the clamped lines. Unit tests included.
- env: optional server vars INBOX_ENGINE_URL / INBOX_ENGINE_TOKEN
  (env.ts, runtimeEnv, both .env examples)
- lib/inbox-engine.ts: server-side fetch with Bearer auth and a 5s
  timeout; lists cached 5 min, single emails per request; missing
  config, timeouts, non-2xx and malformed payloads resolve to empty/null
  so the org page never fails; payloads are validated field by field
- actions/inbox.ts: getInboxEmail for the reader (signed-in, rate
  limited, never throws)
- getOrg exposes externalId (MyPrincetonU 'mpu:<id>')
- OrgEmails: compact rows (subject, sender, NY-time date, list badges,
  preview), total count and 'Browse all N emails in TigerInbox'; a row
  opens a Sheet that lazily loads the email and renders it with
  RichText, with Open in TigerInbox / listserv archive links
- Placement: emails lead (and events collapse to one line) when the org
  has no upcoming events; otherwise they follow Upcoming events
Nullable, unique varchar(64); created on first use and rotatable.
Migration 0005_user_calendar_token.
VCALENDAR with PRODID, VERSION 2.0, X-WR-CALNAME and X-WR-TIMEZONE
America/New_York; VEVENTs with stable UIDs (<id>@forum.tigerapps.org),
DTSTAMP, UTC DTSTART/DTEND (1h default), SUMMARY, LOCATION (venue +
room), DESCRIPTION and URL. TEXT escaping, 75-octet UTF-8-safe folding
and CRLF line endings, with unit tests. buildGCalUrl now uses the same
end-time default and 'Venue, Room' location.
- lib/calendar-feeds.ts: token create/rotate/lookup; Going, Saved,
  per-org and combined 'all' feeds; RSVP/saved events filtered by
  eventVisibleTo, org events by eventDiscoverableBy (published + public
  unless you manage the org); 60 days of history, capped at 1000
- GET /cal/<token>/<going|saved|all|org-<id>>.ics: public in proxy.ts,
  authorized only by the token (unknown token and unknown feed both
  404), text/calendar; charset=utf-8, noindex/no-referrer headers,
  ?download=1 for an attachment; /cal/ disallowed in robots.txt
- actions/calendar.ts: getMyCalendars (calendars with feed URLs — the
  token is created on first use — and their events, each tagged with
  every calendar it belongs to; unfollowed orgs of your RSVP'd/saved
  events are offered as suggestions) and resetCalendarLink
InboxEngine may fetch an email's body from LISTSERV on demand (~10s on
first open), so single-email fetches get 15s while list fetches stay at
5s. The reader shows a loading state over the dimmed preview, and when
the engine only has the RSS preview (complete: false) it says 'Only a
preview is archived. Open in TigerInbox for the original.'
- Cards / List / Calendar toggle (remembered); the calendar shows Going,
  Saved and every followed org's events, color-coded by calendar and
  bucketed by Princeton day: a month grid on desktop, a week strip +
  day agenda on phones
- Calendars panel: toggle visibility per calendar (remembered per
  browser); orgs behind your RSVPs/saves that you don't follow are
  offered, hidden by default
- Export per calendar and for 'All my Forum events': Add to Google
  Calendar (calendar.google.com/calendar/r?cid=<webcal URL>), webcal
  subscribe, copy link, download .ics; 'Reset link' rotates the secret
  after a confirmation
@DIodide
DIodide merged commit 6b4e5fd into staging Sep 28, 2026
1 check 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