Skip to content

Commit 866b7d4

Browse files
committed
写真の拡大表示にピンチとスワイプ、訪問日の「過去1年」、旅程の天気の良い日探し
写真の拡大表示を PhotoLightbox に切り出し、二本指のピンチで拡大縮小、 1本の横スワイプで前後の写真へ移れるようにした。アプリ全体でページのピンチズームを 切ってある(地図にピンチを取られると元に戻せなくなるため)ので、ブラウザ任せの拡大は できない。二本指の距離を自分で見て拡大率を持ち、touch-action: none で既定の動作を止める。 拡大中は左右スワイプを写真の切り替えに使わない ―― 端を見に行くパンと区別が付かないため。 スワイプで回る範囲は訪問記録1件ぶんで、記録本体の写真のあとに追記の写真が書いた順で続く。 追記は訪問回数を増やさない書き足しなので、写真も1つながりとして扱う。 訪問日の絞り込みに「過去1年」を足した。1年ぶんを選ぶのに、カレンダーだと開始月まで 12回さかのぼって2回タップすることになるため。1年前の日付は Date に年だけ引かせるので 2月29日は3月1日に送られるが、絞り込みの範囲としては1日の差なので月末は特別扱いしない。 訪問予定リストに、予定日の前後1週間から天気の良い日を探す機能を足した。予定を立てた あとに雨予報になったとき、近い日にずらせるかを同じ画面で確かめられるようにするもの。 /api/weather を期間でも引けるようにし、上流も期間をそのまま受け取れるので15日ぶんでも 呼び出しは1回で済む。キャッシュは日ごとなので、既存の1日引きとも共有される。 1日ぶんを1行にたたむときは、いちばん悪い地点に合わせる。旅程は1か所でも降れば雨具が 要るので、平均で均すと全体としては晴れに見えてしまう。予報が出るのは先15日ほどまでで、 遠い予定では前後1週間のほとんどが範囲外になるため、そこは「予報なし」と書いておすすめの 判定からも外す ―― 黙って飛ばすと「候補が無い=悪い日」と読めるため。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent b6c7cc2 commit 866b7d4

10 files changed

Lines changed: 1026 additions & 89 deletions

File tree

CLAUDE.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,8 @@ PATCH `/api/spot-types/[id]`は`series_styles`・`categories`を保存前に検
366366

367367
選び方は**カレンダー**(`components/VisitDateCalendar.tsx`)。絞り込みモーダルの訪問日セクションには**選択中の日(期間)を出すボタン**だけを置き、押すと**カレンダーを別モーダル(z-[60]。絞り込みモーダルのz-50の上)で開く** —— 絞り込みモーダルにカレンダーを直に置くと、他の条件を見るのに毎回その分スクロールすることになるため。**カレンダーは日を選んでも閉じない**(期間は2回のタップで決まるので、1回目で閉じると期間を選べない)。選択はその場で反映されるので、閉じる操作は「閉じる」だけでよい。
368368

369+
**よく使う期間はカレンダーを開かずに選べる。** 訪問日セクションとカレンダーの両方に**「今日」「過去1年」「表示しない」**を並べてある(`MapView``visitDateOptions`)。「過去1年」は**1年前の同じ日〜今日**の期間で、カレンダーだと開始月まで12回さかのぼって2回タップすることになるため置いた。**1年前の日付は`Date`に年だけ引かせる**ので、2月29日は3月1日に送られる(閏年でない年に2月29日は無い)——絞り込みの範囲としては1日の差なので、月末を特別扱いはしない。
370+
369371
**訪問記録のある日には日付の下に緑の点**を打つ(`visitDateSet`。どの日に記録があるか分からないまま総当たりで選ぶことになるのを避けるため。**他のスポット種別への訪問も数える** — 経路が種別をまたぐようになったので、その日を落とすと辿れない)。1回目のタップで開始日、2回目で終了日。既に期間が決まっている状態でのタップは新しい開始日として選び直し(範囲を狭めるのにリセットを挟まずに済む)、開始日より前をタップしたときはその日を開始日にして元の開始日を終了日にする(前方向にも伸ばせる)。同じ日を2回タップしても単日のまま。「今日」「表示しない」はよく使うので、カレンダーの中と絞り込みモーダルの両方に置く。既定値は今日。かつてはセレクト(今日 / 表示しない / 訪問のある日の一覧)だったが、期間指定とデータのある日の可視化のためカレンダーに変えた。「表示しない」(`null`)は`saveFilters`が文字列`"none"`で保存し、`loadSavedFilters`は`"none"`のときだけ`null`=表示しないにする。**「今日」(値が`todayKey()`と一致)は具体的な日付ではなく文字列`"today"`で保存し、読み込み時にその日の今日へ解決する**(日付のまま保存すると翌日に開いたとき前日が選ばれた状態で復元されてしまうため)。日付はその日、それ以外(旧仕様の絞り込みだった頃の`null`・キー欠落・不正値)は今日に倒すため、**既存ユーザーも初回から今日の経路が出る**(`todayKey`/`defaultMapFilters`)。終了日(`visitedDateTo`)は**「今日」のような相対表現を持たず具体的な日付でだけ保存する**(終了日だけ動くと期間の長さが日をまたぐたびに変わってしまうため)。カレンダーで日を選んだとき(`handleSelectVisitDate`)は、対象日(期間)をセットしたうえで**その経路全体が画面に収まるよう`fitBounds`する**(1地点だけならmaxZoomまで寄る。経路が0件・「表示しない」のときは地図を動かさない)。開始日を`null`にするときは終了日も一緒に落とす(残っていると次に日を選んだとき意図しない期間になる)。ユーザーが明示的に選んだときだけ移動し、マウント時の既定(今日)の復元では移動しない。日付キーへの変換は`toVisitDateKey`(`visits.visited_on`はtimestamptzでUTC文字列のため、**必ずローカル時刻で日を切る** — UTCのまま切ると日本時間の朝9時前の訪問が前日になる)。
370372

371373
訪問日が選ばれているとき、`MapView`は**その日の訪問記録を訪問時刻の昇順に矢印で結んだ「訪問順の経路」**を描く(`buildVisitPathsByDay`)。**期間を指定したときは日ごとに別の線にし、日をまたいでスポットを結ばない** —— 宿へ帰って翌朝また出る間の移動は実際には辿っていないので、繋ぐと1日の道のりが読めなくなるため。**線・詳細・Google マップの経路検索のいずれも日ごとに別のルートとして扱う**(フィーチャに`pathDate`を持たせ、タップした線の日ぶんだけを詳細に出す)。かつては期間をまたいで1本に繋いでいた。ルートCSVのルートと同じ`spot-routes`ソース・同じ線/矢印レイヤーに載せるだけなので描画コードは共用で、色だけ`VISIT_PATH_COLOR`(緑`#16a34a`=訪問済みピンの塗りと同じ)にしてルートと区別する。同じスポットへの再訪はそのまま複数回経由地として現れる(行って戻る線になる)が、連続する同じスポットへの訪問は長さ0の線分になり矢印の向きが定まらないためまとめる。日時不明の訪問は除外する。**別のスポット種別のスポットも経路に含める**(訪問予定リストと同じ扱い) — 本体種別で解決できないスポットは`api.spots.get`で座標だけ補完する(`pathExtraSpots`/`pathSpotById`。訪問予定リストと共用の仕組み)。かつては表示中の種別の訪問だけを繋いでいたが、同じ日に別の種別のスポットも回っていると経路がそこで途切れていた。**経路上のスポットでもピンは絞り込みに従う**(下記「ピンの表示と線の表示を分ける」)。
@@ -602,6 +604,25 @@ CSVインポートは差分更新で、`AdminView`側が事前読み込み済み
602604
2か所に書くと、片方だけ直したときに保存できる写真が食い違う
603605
- **エクスポート(ZIP)にも入れる**(CSVの「追記」列と、元の記録の写真の後ろに続く写真)。
604606
入れないと、追記に付けた写真だけが持ち出せない
607+
- **拡大表示でも1つながりに扱う**(下記)
608+
609+
**写真の拡大表示は自前のジェスチャで動かす**(`components/PhotoLightbox.tsx`)。
610+
アプリ全体でページのピンチズームを切ってある(`app/layout.tsx``userScalable: false`
611+
地図表示中にピンチが地図へ取られると元に戻せなくなるため)ので、**ブラウザ任せの拡大ができない**
612+
—— 二本指の距離を自分で見て拡大率を持ち、`touch-action: none`で既定の動作を止める。
613+
同じ理由でホイールは**`ctrl`付き(トラックパッドのピンチ)だけ**を拡大に使う。
614+
615+
- **指1本か2本かで操作を分ける。** 2本=ピンチ(**中点を軸にする**ので、つまんだ場所が寄る)、
616+
1本かつ等倍=横スワイプで前後の写真、1本かつ拡大中=パン。
617+
**拡大中に左右スワイプを切り替えに使わない** —— 端を見に行く操作と区別が付かないため
618+
- **渡すのは訪問記録1件ぶんの写真**(`visitPhotoGroup`)。**記録本体の写真のあとに
619+
追記の写真を書いた順**で並べ、画面の並びとそろえる(追記は同じ訪問記録への書き足しなので、
620+
写真も1つながりとして回れる)。サムネイルをタップした位置がそのまま開く位置になる
621+
- **写真をまたぐと拡大は解ける。** 前の写真の拡大率のまま次に移ると、
622+
「なぜか一部しか見えない」状態で始まることになる
623+
- **閉じるのは等倍のタップと×とEsc。** 拡大中のタップでは閉じない(パンの途中で
624+
閉じてしまうため)。タップは**ダブルタップの判定ぶん待ってから**効かせる
625+
(1回目のタップで閉じると拡大に届かない)
605626

606627
**訪問記録フォームは「保存ボタンが画面内に収まる」高さを保つ**(モーダルは`max-h-[85dvh]`でスクロールできるが、記録するたびにスクロールさせるのは日常操作として重い)。そのため**説明文は畳んで置く**: 未訪問記録の説明は見出し横の`HelpTip`(`anchored`表示。下記)、口コミは既定で畳んだ入力欄にしてある。**`HelpTip`のボタンは`label`の外に置くこと**(中に入れると押したときにチェックボックスまで切り替わる)。口コミは**畳んでも入力済みの本文はそのまま投稿する**(`AddSpotModal`の「訪問を記録」と違い、開閉は意思表示ではない)ため、畳んだ見出しに「入力済み」と添えて分かるようにしてある。
607628

@@ -716,6 +737,21 @@ CSVインポートは差分更新で、`AdminView`側が事前読み込み済み
716737
250ms以上の間隔**で投げる(無料の公開APIなので自分で間隔を空ける)。
717738
予報の範囲外の日は**投げる前に「予報なし」で返す**(範囲外は上流が400を返し、
718739
同じリクエストに乗せた地点すべてが巻き添えになるため)
740+
- **予定日の前後1週間から天気の良い日を探せる**(`components/PlanWeatherFinder.tsx`)。
741+
予定を立てたあとに雨予報になったとき、近い日にずらせるかを同じ画面で確かめるためのもの。
742+
`/api/weather`**日付1つ(`date`)と期間(`start`/`end`)の2通り**で引けて、
743+
期間のほうは`data.dates``data.byPoint`(地点×日)を返す。**上流も期間をそのまま受け取れる**
744+
ので15日ぶんでも呼び出しは1回で、**キャッシュは日ごと**なのでどちらの引き方でも共有される。
745+
`data`の中に入れて返すのは、api-clientが`data`キーの中身だけを取り出すため(`fetchAndParse`)
746+
- **1日ぶんを1行にたたむときは、いちばん悪い地点に合わせる**(`summarizeDay`)。
747+
旅程は**1か所でも降れば雨具が要る**ので、平均で均すと「全体としては晴れ」に見えてしまう。
748+
良し悪しの3段階(`weatherGrade`)は**降るかどうか**を境目にし、くもり・霧と
749+
「晴れているが降水確率50%以上」は言い切らずに間へ置く
750+
- **予報が無い日を空欄にしない。** 予報が出るのは先15日ほどまでなので、遠い予定では
751+
前後1週間のほとんどが範囲外になる。黙って飛ばすと「候補が無い=悪い日」と読めるため、
752+
「予報なし」と書いて**おすすめの判定からも外す**(`known === 0`)
753+
- **日を選ぶと旅程の長さを保ったままずらす**(3日間の旅程なら3日間のまま動く)。
754+
更新は並び替えと同じくPATCHで、**経由スポットを丸ごと置き換える仕様なので基本情報も送り直す**
719755
- **「このリストだけを地図で表示」**`/[type]/map?planList=<id>`へ遷移し、地図側がそのリストを
720756
経路の対象(`filters.planListId`)に選び「これだけを表示」(`isolate: "plan"`)にして、経路全体が
721757
入るよう移動する。**適用は一度だけ**(`appliedPlanListRef`)で、以後は普通の絞り込みと同じ扱い

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
| 機能 | 説明 |
2222
|---|---|
23-
| **絞り込み** | ランク・シリーズ・訪問状態・カテゴリ・訪問日で絞れる |
23+
| **絞り込み** | ランク・シリーズ・訪問状態・カテゴリ・訪問日で絞れる。訪問日は「今日」「過去1年」をカレンダーを開かずに選べる |
2424
| **見た目の3軸** | **ランク**(A〜E。色と大きさ)・**シリーズ**(1スポットに1つ。ピンの中のアイコンと形)・**カテゴリ**(複数可。絞り込み専用) |
2525
| **訪問予定** | 行きたい場所のブックマーク。訪問を記録すると自動的に外れる |
2626
| **訪問予定リスト** | 複数スポットを順序付きでまとめた旅程。地図に紫の矢印で経路を描き、Googleマップの経路検索へそのまま渡せる。回り終わったリストは**アーカイブ**して一覧から下げられる(中身は残り、スポット画面の「アーカイブ」から読み直せる) |
@@ -31,6 +31,7 @@
3131
| **スポット種別** | 観光地・郵便局・御朱印など。種別ごとに独立したURL・シリーズ・カテゴリ・対象地域を持ち、管理者が自由に追加・削除できる |
3232
| **経路(巡った順の矢印)** | 巡った順に繋いだ線と矢印を地図に出す(CSVで取り込む)。線をタップすると経由地の一覧と区間ごとの説明が開く |
3333
| **訪問日の軌跡** | 訪問記録のある日をカレンダーから選ぶと、その日に回った順に矢印で結んだ軌跡が出る。期間指定もできる |
34+
| **旅程の天気** | 訪問予定リストの各スポットに予定日の予報アイコンが出る。**予定日の前後1週間を並べて天気の良い日を探し**、旅程の長さを保ったまま予定日をずらせる |
3435
| **重ね表示** | 別のスポット種別を半透明で同じ地図に重ねられる |
3536
| **対象地域** | 種別ごとに日本(都道府県)・特定の国(州・県)・世界全体(国)から選べる |
3637
| **AIに聞く** | スポットの詳細から、Claude・ChatGPT・Perplexity・Grok・Geminiに所在地と座標を添えて質問できる(営業時間とアクセスを先に答えるよう頼んである) |
@@ -280,7 +281,9 @@ docker compose pull && docker compose up -d
280281
一覧からは名前と理由だけのテキストにまとめてAIに渡せ、片付いたらまとめて取り消せる
281282
- **口コミ(公開)**: 星評価はなく本文のみ。投稿するたびに増える掲示板方式で、同じスポットに何件でも書ける
282283
- **写真(非公開)**: 自分の訪問記録にのみ添付。ブラウザ側で縮小・圧縮した上で`data/photos/`
283-
フォルダ(Dockerではbindマウント)に保存され、`/api/photos/...`経由(本人のみ)で配信する
284+
フォルダ(Dockerではbindマウント)に保存され、`/api/photos/...`経由(本人のみ)で配信する。
285+
サムネイルをタップすると拡大表示になり、**ピンチ(かダブルタップ)で拡大縮小、
286+
スワイプで同じ訪問記録の前後の写真へ**移れる(追記に付けた写真も同じ並びに続く)
284287
- **訪問記録のエクスポート**: 管理者が`/[type]/admin`で対象ユーザーのメールアドレスを
285288
指定して実行すると、そのユーザーの訪問記録を全スポット種別ぶんまとめたZIPが
286289
バックグラウンドで作られる(`visits-<種別キー>.csv`=訪問のメモ+スポット情報、

0 commit comments

Comments
 (0)