Skip to content

予定の参加者(ゲスト)を表示する - #39

Merged
sasagar merged 1 commit into
mainfrom
dev
Jul 30, 2026
Merged

予定の参加者(ゲスト)を表示する#39
sasagar merged 1 commit into
mainfrom
dev

Conversation

@sasagar

@sasagar sasagar commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

概要

このアプリは参加者の概念を持っていなかったOccurrence には自分の responseStatusisOrganizer しか無く、Google は返しているのに DTO 変換で捨てていた。そのため誰が招待されているか分からず、新規作成に参加者欄も入れられなかった(付けたゲストを二度と外せなくなるため)。

表示 → 編集 → 作成 の順で広げる方針の1段目。

編集は入れていない

公式リファレンスに **「参加者から主催者へ返るのは自分の応答状態だけ」**と明記されている。つまり主催者でない予定でゲストを編集しても Google 側で黙って捨てられる

「付けられるが外せない」より「付けたつもりが誰にも届いていない」ほうが悪い。

正しくガードするには guestsCanModify / guestsCanInviteOthers の追加が必要で、それはさらなる世代上げ = 全利用者に再度の全同期を強いる。あわせて sendUpdates の既定が公式に文書化されておらず(none には「予定が同期されない/失われうる」旨の警告、all はゲスト1人追加で全員にメール)、UI 上の明示的な選択が要る ― これは作成と同じ問いなのでまとめて決めるべき。別タスクに登録済み。

調査で避けた罠: maxAttendees

一覧取得時に maxAttendees を指定すると、上限を超えた予定は切り詰めではなく「参加者本人1件だけ」を返す。使っていたら大人数の予定で一覧が丸ごと消えていた。

代わりに DTO 側で 50件に足切りし、超過時は attendeesOmitted を立てて UI が「50人以上」と出す。切り詰めても自分と主催者は必ず残す(一番困る欠け方を防ぐ)。RSVP の書き戻しはサーバーが events.get から read-modify-write するので、この打ち切りは書き込みの正しさに影響しない

表示

  • EventDetailCard に「ゲスト N人 参加x・未定x・不参加x・未返信x」の内訳と一覧
  • 会議室(resource)は人ではないので、人数にも出欠の内訳にも入れず場所と同じ扱いで分離
  • 並びは主催者→自分→元の順(応答状態で並べ替えると返事のたびに行が飛んで探しにくい)
  • 新しい色は1つも足していない — 語彙(参加/未定/不参加/未返信)と形だけで表し、予定カード側の描き分け(不参加=打ち消し線、未定/未返信=淡い)と読み方を揃えた
  • 高さの破綻対策: 畳んだ状態は先頭5人、展開しても一覧に max-height + 内部スクロール。36人展開時もポップオーバーは 329px のまま

⚠️ バックフィル世代 5 → 6(デプロイ時の注意)

同期する項目が増えるため、CURRENT_SYNC_BACKFILL_VERSION(web)と SUPPORTED_SYNC_BACKFILL_VERSION(sync)を両方上げている。

web だけ先に新しくなると「サーバーがまだ返さないフィールドをバックフィル完了として記録」→ 以後 forceFull が走らず永久に欠ける、という事故が過去に実際に起きている(世代4を空振り用に消費した跡がコメントに残っている)。その恒久対策として sync が GET /api/me で対応世代を宣言し、web は min(自分, サーバー) までしか完了記録しない。両方上げないと機能しない。

この世代上げにより、各利用者の次回同期が全同期になる(ゲスト情報を取り込むため)。

検証

  • pnpm --filter web test 1292 passed / pnpm --filter sync test 640 passed
  • 両 typecheck / lint / build
  • demo モードに参加者付きの予定を6件追加(少人数 / 全応答状態 / 会議室込み / 表示名なし / 36人 / 上限超え)し、ライト・ダーク両方で確認

ドキュメント

apps/site/docs/calendar/index.html に「ゲスト(参加者)を見る」節を追加。編集できないことを callout で明記し、理由(非主催者の変更が届かない / 招待メールの扱いを先に決める必要がある)と回避策(Google で開く)を書いた。

🤖 Generated with Claude Code

https://claude.ai/code/session_01HGESE2zBNzoW8MYvtEySBQ

このアプリは参加者の概念を持っておらず、Occurrence には自分の responseStatus と
isOrganizer しか無かった。Google は返しているのに DTO 変換で捨てていたため、
誰が招待されているか分からず、新規作成に参加者欄も入れられなかった。

**表示のみ入れ、編集は入れていない。** 公式リファレンスに「参加者から主催者へ返るのは
自分の応答状態だけ」と明記されており、主催者でない予定でゲストを編集しても Google 側で
黙って捨てられる ―― 「付けられるが外せない」より「付けたつもりが誰にも届いていない」
ほうが悪い。正しくガードするには guestsCanModify 等のフィールド追加とさらなる世代上げ
(= 全利用者に再度の全同期)が要る。あわせて sendUpdates の既定が公式に文書化されて
おらず(none には予定が失われうる旨の警告、all はゲスト1人追加で全員にメール)、
UI 上の明示的な選択が要る ― これは作成と同じ問いなのでまとめて決めるべき。

maxAttendees は使わない。上限を超えると切り詰めではなく**参加者本人1件だけ**を返す
仕様で、大人数の予定で一覧が丸ごと消える。代わりに DTO 側で50件に足切りし、超過時は
attendeesOmitted を立てる。切り詰めても**自分と主催者は必ず残す**(一番困る欠け方を
防ぐ)。RSVP の書き戻しはサーバーが events.get から read-modify-write するので、
この打ち切りは書き込みの正しさに影響しない。

会議室(resource)は人ではないので、人数にも出欠の内訳にも入れず場所と同じ扱いで
分離する。並びは主催者→自分→元の順(応答状態で並べ替えると返事のたびに行が飛ぶ)。
新しい色は足さず、語彙と形だけで応答状態を表して予定カード側の描き分けと揃えた。

同期する項目が増えるのでバックフィル世代を 5 → 6 に上げた(web と sync の2箇所)。
web だけ先に新しくなると「サーバーがまだ返さないフィールドをバックフィル完了として
記録」して永久に欠けるため、sync が対応世代を宣言し web は min(自分, サーバー) までしか
完了記録しない。両方上げないと機能しない。

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HGESE2zBNzoW8MYvtEySBQ
@sasagar
sasagar merged commit 9b8ceb9 into main Jul 30, 2026
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