@@ -1132,39 +1132,6 @@ export default function MapView({
11321132 [ filters , setFilters , planLists , planPathSpotById , fitMapToSpots ]
11331133 ) ;
11341134
1135- // 経路表示中のリストに本体種別で解決できないスポット(別種別を重ねて追加したもの)が
1136- // あれば、api.spots.get で座標を補完する(経路線から抜けないように)
1137- useEffect ( ( ) => {
1138- const listId = filters . planListId ;
1139- if ( ! listId ) return ;
1140- const list = planLists . find ( ( l ) => l . id === listId ) ;
1141- if ( ! list ) return ;
1142- const missing = list . spot_ids . filter (
1143- ( id ) => ! spotById . has ( id ) && ! planListResolvedRef . current . has ( id )
1144- ) ;
1145- if ( missing . length === 0 ) return ;
1146- // 二重取得を防ぐため先に予約する。取得結果は id をキーにした追記のみの解決
1147- // キャッシュに足すだけなので、この effect が(リスト変更などで)途中で作り直されても
1148- // 破棄しない。破棄すると予約だけ残って経路からスポットが抜けたままになる
1149- missing . forEach ( ( id ) => planListResolvedRef . current . add ( id ) ) ;
1150- Promise . all ( missing . map ( ( id ) => api . spots . get ( id ) ) ) . then ( ( results ) => {
1151- const fetched = results
1152- . map ( ( r ) => r . data )
1153- . filter ( ( s ) : s is Spot => s != null ) ;
1154- // 取得できなかった id は予約を外し、次に条件が変わったとき再取得できるようにする
1155- const fetchedIds = new Set ( fetched . map ( ( s ) => s . id ) ) ;
1156- for ( const id of missing ) {
1157- if ( ! fetchedIds . has ( id ) ) planListResolvedRef . current . delete ( id ) ;
1158- }
1159- if ( fetched . length === 0 ) return ;
1160- setPlanListExtraSpots ( ( prev ) => {
1161- const next = new Map ( prev ) ;
1162- for ( const s of fetched ) next . set ( s . id , s ) ;
1163- return next ;
1164- } ) ;
1165- } ) ;
1166- } , [ filters . planListId , planLists , spotById ] ) ;
1167-
11681135 // 地図で訪問予定リストを経路表示中に、そのリスト内のスポットへ新しく訪問記録したら、
11691136 // 自動でそのスポットをリストから外す(訪問済みが経路に残り続けないように)。
11701137 // 表示中のリスト(filters.planListId)にそのスポットが含まれるときだけ動く
@@ -1231,6 +1198,9 @@ export default function MapView({
12311198 // ピンのクリックハンドラ(レイヤー作成時に一度だけ束縛される)から現在の作成モードを
12321199 // 参照するためのref。作成モード中はピンタップを詳細表示でなくリスト追加に回す
12331200 const buildModeRef = useRef ( false ) ;
1201+ // 作成モードに入った時点でスポットのある下書き(既存リストの編集など)は、
1202+ // その経路全体が見えるようスポット読み込み後に一度だけfitBoundsする
1203+ const buildFitPendingRef = useRef ( false ) ;
12341204 // 作成中は下タブ(NavBar)を隠して、別タブへ移動して入力中の内容を失うのを防ぐ
12351205 const { setHideNav } = useNavVisibility ( ) ;
12361206 useEffect ( ( ) => {
@@ -1241,13 +1211,52 @@ export default function MapView({
12411211 // マウント時/種別切替時に ?buildList=1 なら下書きを読み込んで作成モードに入る
12421212 useEffect ( ( ) => {
12431213 if ( buildListParam === "1" ) {
1244- setBuildDraft ( loadPlanListDraft ( spotTypeKey ) ) ;
1214+ const draft = loadPlanListDraft ( spotTypeKey ) ;
1215+ setBuildDraft ( draft ) ;
1216+ buildFitPendingRef . current = ( draft ?. spotIds . length ?? 0 ) > 0 ;
12451217 // 経路詳細の「編集」から来た場合、基本情報モーダルは閉じて地図の作成モードに移る
12461218 // (同一ページ遷移のため自動では閉じない。SpotsView からの遷移では unmount で消える)
12471219 setEditingPlanList ( null ) ;
12481220 }
12491221 } , [ buildListParam , spotTypeKey ] ) ;
12501222
1223+ // 経路表示中のリスト・作成モード中の下書きに、本体種別で解決できないスポット
1224+ // (別種別を重ねて追加したもの)があれば、api.spots.get で座標を補完する
1225+ // (経路線から抜けないように)
1226+ useEffect ( ( ) => {
1227+ const list = filters . planListId
1228+ ? planLists . find ( ( l ) => l . id === filters . planListId )
1229+ : undefined ;
1230+ const targetIds = new Set ( [
1231+ ...( list ?. spot_ids ?? [ ] ) ,
1232+ ...( buildDraft ?. spotIds ?? [ ] ) ,
1233+ ] ) ;
1234+ const missing = [ ...targetIds ] . filter (
1235+ ( id ) => ! spotById . has ( id ) && ! planListResolvedRef . current . has ( id )
1236+ ) ;
1237+ if ( missing . length === 0 ) return ;
1238+ // 二重取得を防ぐため先に予約する。取得結果は id をキーにした追記のみの解決
1239+ // キャッシュに足すだけなので、この effect が(リスト変更などで)途中で作り直されても
1240+ // 破棄しない。破棄すると予約だけ残って経路からスポットが抜けたままになる
1241+ missing . forEach ( ( id ) => planListResolvedRef . current . add ( id ) ) ;
1242+ Promise . all ( missing . map ( ( id ) => api . spots . get ( id ) ) ) . then ( ( results ) => {
1243+ const fetched = results
1244+ . map ( ( r ) => r . data )
1245+ . filter ( ( s ) : s is Spot => s != null ) ;
1246+ // 取得できなかった id は予約を外し、次に条件が変わったとき再取得できるようにする
1247+ const fetchedIds = new Set ( fetched . map ( ( s ) => s . id ) ) ;
1248+ for ( const id of missing ) {
1249+ if ( ! fetchedIds . has ( id ) ) planListResolvedRef . current . delete ( id ) ;
1250+ }
1251+ if ( fetched . length === 0 ) return ;
1252+ setPlanListExtraSpots ( ( prev ) => {
1253+ const next = new Map ( prev ) ;
1254+ for ( const s of fetched ) next . set ( s . id , s ) ;
1255+ return next ;
1256+ } ) ;
1257+ } ) ;
1258+ } , [ filters . planListId , planLists , spotById , buildDraft ] ) ;
1259+
12511260 // ピンのタップ: 作成モード中は追加確認へ、それ以外は従来どおり詳細表示へ
12521261 const handleSpotSelect = useCallback ( ( id : string ) => {
12531262 if ( buildModeRef . current ) setAddCandidate ( id ) ;
@@ -1369,6 +1378,33 @@ export default function MapView({
13691378 for ( const [ id , s ] of spotById ) m . set ( id , s ) ;
13701379 return m ;
13711380 } , [ overlaySpotById , spotById ] ) ;
1381+
1382+ // 作成モード中の下書きの経路(選択済みスポットを選んだ順に繋いだもの)。地図に
1383+ // 訪問予定リストと同じ紫の矢印で描き、追加・削除・並び替えに即追従する。
1384+ // スポットは本体+重ね表示+別種別の補完(planListExtraSpots)で解決する
1385+ const buildDraftPath = useMemo ( ( ) => {
1386+ if ( ! buildDraft ) return [ ] ;
1387+ return buildDraft . spotIds
1388+ . map (
1389+ ( id ) =>
1390+ spotById . get ( id ) ?? overlaySpotById . get ( id ) ?? planListExtraSpots . get ( id )
1391+ )
1392+ . filter ( ( s ) : s is Spot => s !== undefined ) ;
1393+ } , [ buildDraft , spotById , overlaySpotById , planListExtraSpots ] ) ;
1394+
1395+ // 作成モードに入った時点でスポットのある下書き(既存リストの編集など)は、
1396+ // 経路が解決でき次第、全体が見えるよう一度だけ地図を移動する
1397+ // (新規作成で最初のスポットを足したときに地図が飛ばないよう、入場時のみ)
1398+ useEffect ( ( ) => {
1399+ if ( ! buildFitPendingRef . current ) return ;
1400+ if ( ! buildDraft ) {
1401+ buildFitPendingRef . current = false ;
1402+ return ;
1403+ }
1404+ if ( buildDraftPath . length === 0 ) return ;
1405+ buildFitPendingRef . current = false ;
1406+ fitMapToSpots ( buildDraftPath ) ;
1407+ } , [ buildDraft , buildDraftPath , fitMapToSpots ] ) ;
13721408 // 重ね表示の絞り込み変更をその種別のlocalStorageへ保存しつつstateへ反映する
13731409 // (overlayFiltersが変わると重ね表示の描画effectが再実行され、地図に即反映される)
13741410 const setOverlayFiltersAndSave = useCallback (
@@ -2024,9 +2060,11 @@ export default function MapView({
20242060 const visitPathIds = new Set (
20252061 buildVisitPath ( visits , filters , spotById ) . map ( ( s ) => s . id )
20262062 ) ;
2027- const planPathIds = new Set (
2028- buildPlanListPath ( planLists , filters , planPathSpotById ) . map ( ( s ) => s . id )
2029- ) ;
2063+ // 作成モード中は、下書きの経由スポットも経路と同様に絞り込みから免除する
2064+ const planPathIds = new Set ( [
2065+ ...buildPlanListPath ( planLists , filters , planPathSpotById ) . map ( ( s ) => s . id ) ,
2066+ ...( buildDraft ?. spotIds ?? [ ] ) ,
2067+ ] ) ;
20302068 // 「これだけを表示」中は、その経路のスポットだけに絞る(他のスポット・ルート・
20312069 // もう一方の経路は隠す)。それ以外は従来どおり絞り込み+経路+ルート経由地で出す
20322070 const isolate = effectiveIsolate ( filters ) ;
@@ -2089,6 +2127,7 @@ export default function MapView({
20892127 planPathSpotById ,
20902128 visits ,
20912129 planLists ,
2130+ buildDraft ,
20922131 visitedIds ,
20932132 filters ,
20942133 runWhenMapReady ,
@@ -2111,8 +2150,11 @@ export default function MapView({
21112150 : [ ] ;
21122151 const visitPath =
21132152 isolate === "plan" ? [ ] : buildVisitPath ( visits , filters , spotById ) ;
2153+ // 作成モード中に編集対象のリスト自身を経路表示していた場合は、更新前の経路が
2154+ // 下書きの経路と古い形のまま二重に残らないよう、保存済み側は描かない
21142155 const planListPath =
2115- isolate === "visit"
2156+ isolate === "visit" ||
2157+ ( buildDraft !== null && filters . planListId === buildDraft . editingId )
21162158 ? [ ]
21172159 : buildPlanListPath ( planLists , filters , planPathSpotById ) ;
21182160
@@ -2131,6 +2173,9 @@ export default function MapView({
21312173 // 現在地(青丸)を表示中は、現在地からリスト先頭のスポットまでも結ぶ
21322174 start : currentLocation ,
21332175 } ,
2176+ // 作成モード中の下書きの経路。kind無し=線のタップで詳細は開かない
2177+ // (作成中のタップはピンの追加操作を優先するため)
2178+ { path : buildDraftPath , color : PLAN_LIST_PATH_COLOR } ,
21342179 ] )
21352180 ) ;
21362181 } ) ;
@@ -2144,6 +2189,8 @@ export default function MapView({
21442189 spotById ,
21452190 planPathSpotById ,
21462191 currentLocation ,
2192+ buildDraft ,
2193+ buildDraftPath ,
21472194 openRouteDetail ,
21482195 openPathDetail ,
21492196 ] ) ;
0 commit comments