目的・役割

選択肢の中から1つを選んで投票する画面。再投票時はポイント消費(revote1: 3pt, revote2: 7pt)。

UI 構成

要素種類説明
RankingHeaderView(Xスタイル)VStack 画面上部の共通ヘッダ。以下7段構成。
1段目: 作成者アバター + 表示名 + フォローボタン(Xスタイル) + 右上に大きなランクバッジ(44px円、Sは虹グラデ)
2段目: 投票タイプバッジ(シングル/TOP3) + クイズ通過バッジ(hasQuiz=true のみ)
3段目: 大タグ
4段目: 小タグ
5段目: 投票期間 + 残り時間
6段目: タイトル(選択肢直前に再表示)
7段目: 説明文(存在する場合のみ)
選択肢グリッドLazyVGrid各 Choice をカード表示(label + image)
選択ハイライトBorder選択中の Choice を視覚化
投票ボタンButton選択完了でアクティブ化
消費ポイント表示Text「投票には Npt を消費します」
バナー広告共通配置MainTabView の safeAreaInset(.bottom) でタブバー直上に共通配置(プレミアム時は非表示)

レイアウト変更(v1.1)

状態

状態表示内容
未選択投票ボタン無効
選択済投票ボタン有効
投票中ProgressView、ボタン無効
ポイント不足「ポイントが足りません」アラート
既投票検出「すでに投票済みです」 → ResultView へ

遷移

操作遷移先
選択 + 投票ボタン押下VoteService.castVote() → ResultView
キャンセルRankingDetailView へ戻る

使用するデータモデル

Vote {
    let rankingId: UUID
    let userId: UUID
    let choiceId: UUID
    let points: Int       // 0 (初投票) / 3 (revote1) / 7 (revote2)
    let voteNumber: Int   // 1, 2, 3
    let ipHash: String?
    let deviceHash: String?
}

実装メモ

関連

変更履歴

バージョン日付変更内容
1.02026-05-09初版作成
1.12026-05-10RankingHeaderView を Xスタイル化(7段構成)/VoteView の重複タイトル表示を削除/バナー広告を MainTabView 共通配置へ統一