New mobile:calendar_feed (/app/calendar/<token>.ics) -- a standard webcal-style subscription feed calendar apps can poll, so a family's schedule shows up in Apple/Google/Outlook Calendar alongside everything else. Token-authenticated rather than session-authenticated (calendar apps can't do interactive login); CalendarFeedToken is deliberately not club-scoped, since the same token works under whichever club subdomain the request is fetched from -- an account active in more than one club needs only the one link. Combined across every managed person (their name goes in the event title when there's more than one), every event they're invited to regardless of RSVP status, not capped to Calendar's own 2-week window -- a synced calendar app is exactly where someone wants the whole season visible. A cancelled event stays in the feed as STATUS:CANCELLED rather than disappearing, so it's removed properly on the subscriber's next refresh instead of just vanishing. New mobile:calendar_feed_settings (linked from Me) shows the webcal:// and https:// links plus a "reset my calendar link" action that immediately invalidates the old one. Uses the icalendar package for RFC 5545 generation rather than hand-rolling escaping/line-folding. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
48 lines
3.1 KiB
HTML
48 lines
3.1 KiB
HTML
{% extends "mobile/base.html" %}
|
|
{% load i18n %}
|
|
|
|
{% comment %}
|
|
M5's "Calendar sync" row -- subscribe an external calendar app (Apple/
|
|
Google/Outlook) to this account's combined feed. Not real-time: calendar
|
|
apps poll a subscribed feed on their own schedule, typically every few
|
|
hours -- the in-app push notifications (M7) are the timely channel, this
|
|
is a convenience layer on top, not a replacement.
|
|
{% endcomment %}
|
|
|
|
{% block content %}
|
|
<div class="-mx-4 -mt-4 flex items-center gap-1 border-b border-line bg-white px-4 py-3">
|
|
<a class="-ml-2.5 flex h-11 w-11 shrink-0 items-center justify-center" href="{% url "mobile:me" %}" aria-label="{% trans "Back" %}">
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#0b1220" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M15 5l-7 7 7 7"/></svg>
|
|
</a>
|
|
<span class="min-w-0 flex-1 truncate font-display text-xl leading-none font-extrabold text-ink uppercase">{% trans "Calendar sync" %}</span>
|
|
</div>
|
|
|
|
<p class="text-sm text-muted">{% trans "Subscribe this link in your phone or computer's calendar app to see everyone you manage's schedule alongside your own. It includes every event they're invited to, not just the ones they've replied to." %}</p>
|
|
|
|
<div class="m-card p-4">
|
|
<a class="btn btn-primary flex w-full items-center justify-center" href="{{ webcal_url }}">{% trans "Add to calendar" %}</a>
|
|
<p class="mt-2 text-center text-xs text-dim">{% trans "Opens directly in Apple/macOS Calendar. On Android or a desktop calendar app, copy the link below and add it as a new calendar by URL." %}</p>
|
|
|
|
<div class="mt-4" x-data="{ copied: false }">
|
|
<div class="flex items-center gap-2 rounded-lg border border-stroke bg-paper px-3 py-2.5">
|
|
<span class="min-w-0 flex-1 truncate font-mono text-xs text-muted">{{ feed_url }}</span>
|
|
<button type="button" class="shrink-0 font-display text-xs font-extrabold tracking-wide text-club uppercase" @click="navigator.clipboard.writeText('{{ feed_url|escapejs }}'); copied = true; setTimeout(() => copied = false, 2000)">
|
|
<span x-show="!copied">{% trans "Copy" %}</span>
|
|
<span x-show="copied" x-cloak>{% trans "Copied" %}</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<p class="text-xs text-dim">{% trans "Updates can take a few hours to appear in your calendar app -- it checks in on its own schedule, RosterChief can't push to it directly." %}</p>
|
|
|
|
<div class="m-card p-4">
|
|
<div class="text-sm font-semibold text-ink">{% trans "Reset link" %}</div>
|
|
<p class="mt-1 text-xs text-muted">{% trans "If you've shared this link and want to take it back, reset it -- the old one stops working immediately and you'll need to re-subscribe with the new one." %}</p>
|
|
<form class="mt-3" method="post" action="{% url "mobile:calendar_feed_settings" %}">
|
|
{% csrf_token %}
|
|
<button type="submit" class="btn btn-secondary w-full">{% trans "Reset my calendar link" %}</button>
|
|
</form>
|
|
</div>
|
|
{% endblock content %}
|