カレンダーに追加ブロック
ワンタップで「これをカレンダーに追加する」ためのブロックで、タイムゾーンをまたぐ招待の実用的な解決策です。送信されたメールは受信者自身のタイムゾーンを検出できないため(メールにはJavaScriptがありません)、このブロックはイベントの開始と終了を、事前計算されたローカル時刻の文字列ではなく絶対的な時刻として符号化します。これにより、各参加者のカレンダーアプリがそれぞれのローカル時刻で自動的に正しく表示します。
仕組み
このブロックは会場ブロックと同じ純粋なコンテンツです — 回答するものが何もないため、回答ダッシュボードやCSVエクスポートには一切表示されません。レンダリングされる3つのリンクはいずれも、専用のcapabilityではなく、リンクを持つ通常のブロックがすべて使う汎用のクリック追跡スロットに乗ります。
リンクは有効なstartsAtが設定されていることを条件に表示されます: 開始時刻が入力されていないイベントでは、タイトルと説明文だけが表示され、どこにも遷移しない無効なリンクは表示されません。startsAtが設定されていてendsAtが設定されていない場合(またはstartsAtより前の値になっている場合)、ブロックは既定で開始から1時間後を終了時刻とします — 「いつ始まるか」だけを入力するのが一般的なケースとして扱われており、後続の各消費者(.icsファイル、Googleカレンダー、Outlook.com)は何らかの終了時刻の存在を必要とするためです。locationは、会場ブロックやミーティングリンクブロック自身のテキストから手動で入力する、自由記述のプレーンなフィールドです。この最初のバージョンでは3つのブロックは自動的には連動していないため、会場が変わった場合は自分で更新してください。
これらのプロパティから3つのリンクがレンダリングされます。クリック追跡の目的で同じ1つのblockIdを共有します(ヒートマップは個々のリンク単位ではなくブロック単位で集計されます — ソーシャルリンクが複数のアイコンに対して使っているのと同じ規則です):
- .icsをダウンロード — 手作りのRFC 5545(iCalendar)ファイルをストリーミングする、
GET /api/calendarへの署名付きリンクです。背後にnpm依存はありません —.icsファイルはプレーンテキストで、カウントダウンGIFエンコーダーと同じ「完全に一から作る」方法で構築されています。ファイルのDTSTART/DTENDは絶対的なUTC時刻として出力されます(末尾にZが付き、TZID/VTIMEZONEコンポーネントは不要です) — これが実際のタイムゾーン対応の仕組みであり、レンダリング時に計算されるものではなく、そもそもブロックのstartsAt/endsAtがエポックミリ秒として保存されていることの単なる結果です。イベントのUIDはタイトル/説明/場所/開始/終了から決定的に生成されたsha256ハッシュなので、同じ署名付きリンクを再度ダウンロードすると1つのカレンダー項目が更新され、重複が作られることはありません。自由記述のテキストはRFC 5545 §3.3.11に従ってエスケープされ(バックスラッシュ、カンマ、セミコロン、実際の改行)、長い行は仕様が要求する75オクテットの上限で折り返されるため、主催者の長い説明文が技術的に無効なファイルを生成することはありません。URL自体は、カウントダウン/数式/チャートの各エンドポイントと同じ方法で署名されています — イベントペイロード全体がクエリ文字列にシリアライズされ(GETパスでのFirestore読み取りはありません)、HMACの入力に組み込まれた"calendar:"プレフィックスによってアプリ内の他のすべての署名付きURLファミリーとドメイン分離されているため、このエンドポイント用に発行された署名が別のエンドポイントで検証されることは絶対にありません。 - Googleカレンダーに追加 — 出力時に構築される単純な
calendar.google.com/calendar/render?action=TEMPLATE&...ディープリンクで、サーバーとの往復通信はありません。 - Outlook.comに追加 — 同様に出力時に構築される単純な
outlook.live.com/calendar/0/action/compose?...ディープリンクです。
タイトル、説明、場所はそれぞれ、3つのリンクが構築される前に固定の最大長(200/2,000/300文字)で防御的に切り詰められます。そのため、非常に長い自由記述のフィールドが壊れた.icsリンクや、2つのWebリンクで過大なクエリ文字列を生成することはありません。
設定可能なフィールド:
- イベントタイトル。
- 説明(任意)。
- 開始時刻 — リンクが表示されるために必須です。
- 終了時刻(既定では開始の1時間後)。
- 場所(任意、自由記述)。
- カード全体の背景色、パディング、枠線、角丸。
- イベントタイトルの色/サイズ、および3つのリンクで共有される色/背景の上書き。
例
Subject
Save the date: our fall meetup
「イベント」用途のプロジェクトはいずれも、ミーティングリンク、会場、RSVPと並んで、あらかじめカレンダーに追加ブロックが配置された状態で始まります — このブロックが担う、招待からリマインダーまでの一連のパターン全体についてはイベントとウェビナーを参照してください。スポーツクラブは、練習や試合の時間のために同じブロックを毎週再利用するため、どのタイムゾーンを旅行中であっても各家庭のカレンダーには正しいスケジュールが反映されます — スポーツチームとクラブを参照してください。ウェビナーシリーズでは、登録直後に送られる確認メールにこれを組み込むことで、受信者が後で覚えていることに頼るのではなく、登録した瞬間にカレンダーへ追加できます。
静的フォールバックの見え方
<div style="margin:12px 0;background-color:transparent">
<p style="margin:0 0 8px;font-weight:600;font-size:16px;color:#111827">Q3 Product Launch</p>
<a href="https://mailinapp.com/api/calendar?d=eyJ0aXRsZSI6...&s=abc123..."
style="display:inline-block;padding:10px 20px;background-color:#4f46e5;color:#ffffff;
border-radius:6px;text-decoration:none;font-size:14px;font-weight:600;margin:0 8px 8px 0">
Download .ics
</a>
<a href="https://calendar.google.com/calendar/render?action=TEMPLATE&text=Q3+Product+Launch&dates=20260901T170000Z%2F20260901T180000Z"
style="display:inline-block;padding:10px 20px;background-color:#4f46e5;color:#ffffff;
border-radius:6px;text-decoration:none;font-size:14px;font-weight:600;margin:0 8px 8px 0">
Add to Google Calendar
</a>
</div>
これがメール送信されるブロックの全体です — 同じイベント情報から構築された3つの単純なリンクだけで(3つ目のOutlook.comは簡潔にするためここでは省略しています)、画像や条件付きコンテンツは一切関与しません。