Skip to content

Commit bb1457a

Browse files
committed
delete and ref map
1 parent c0aa86d commit bb1457a

File tree

4 files changed

+133
-18
lines changed

4 files changed

+133
-18
lines changed

frontend/src/navigation/AppNavigator.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,23 @@ import React from 'react';
44
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
55
// 1. react-native-vector-iconsからアイコンコンポーネントをインポートします
66
import Icon from 'react-native-vector-icons/FontAwesome';
7+
import type { RootStackParamList } from '../types/navigation';
78

89
// 各画面コンポーネントをインポート
910
import PhotoGalleryScreen from '../screens/PhotoGalleryScreen';
1011
import MapViewScreen from '../screens/MapViewScreen';
1112
import SharedFeedScreen from '../screens/SharedFeedScreen';
1213
import SettingsScreen from '../screens/SettingsScreen';
1314

14-
const Tab = createBottomTabNavigator();
15+
const Tab = createBottomTabNavigator<RootStackParamList>();
1516

1617
const AppNavigator = () => {
1718
return (
1819
<Tab.Navigator
1920
screenOptions={({ route }) => ({
2021
headerShown: false,
2122
// 2. tabBarIconオプションをここに追加します
22-
tabBarIcon: ({ focused, color, size }) => {
23+
tabBarIcon: ({ color, size }) => {
2324
let iconName = 'image'; // デフォルトのアイコン
2425

2526
// 3. 表示しているルート(画面)の名前に応じて、表示するアイコンを切り替えます

frontend/src/screens/MapViewScreen.tsx

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
// src/screens/MapViewScreen.tsx
22

3-
import React, { useState, useEffect } from 'react';
3+
import React, { useState, useEffect, useRef, useCallback } from 'react';
44
// 1. StyleSheetとSafeAreaViewをインポートします
5-
import { StyleSheet, View, Text, Image, ActivityIndicator } from 'react-native';
5+
import { StyleSheet, View, Text, Image } from 'react-native';
66
import { SafeAreaView } from 'react-native-safe-area-context';
77
// 2. react-native-mapsからMapViewとMarkerをインポートします
88
import MapView, { Marker, Region } from 'react-native-maps';
9+
import { useRoute, RouteProp } from '@react-navigation/native';
10+
import type { RootStackParamList } from '../types/navigation';
911

1012
// 3. APIが完成するまでの「ダミーのピンデータ」を作成します
1113
// 東京の名所の座標をいくつか用意しましょう
@@ -58,21 +60,24 @@ type Nearby_Photos = {
5860
created_at: string;
5961
};
6062

63+
type MapViewScreenRouteProp = RouteProp<RootStackParamList, 'Map'>;
64+
6165
const MapViewScreen = () => {
66+
const route = useRoute<MapViewScreenRouteProp>();
67+
const mapRef = useRef<MapView>(null);
6268

6369
const [markers, setMarkers] = useState<Nearby_Photos[]>([]);
64-
const [loading, setLoading] = useState(true);
6570
const [region, setRegion] = useState<Region>({
66-
latitude: 35.6895,
67-
longitude: 139.6917,
68-
latitudeDelta: 0.1,
69-
longitudeDelta: 0.1,
71+
latitude: route.params?.latitude || 35.6895,
72+
longitude: route.params?.longitude || 139.6917,
73+
latitudeDelta: route.params?.latitude ? 0.01 : 0.1,
74+
longitudeDelta: route.params?.longitude ? 0.01 : 0.1,
7075
});
7176

7277
const BACKEND = 'http://localhost:8000';
73-
const fetchNearbyPhotos = async (lat: number, lng: number) => {
78+
79+
const fetchNearbyPhotos = useCallback(async (lat: number, lng: number) => {
7480
try {
75-
setLoading(true);
7681
const res = await fetch(
7782
`${BACKEND}/photos/nearby/photos?lat=${lat}&lng=${lng}`
7883
);
@@ -81,14 +86,29 @@ const MapViewScreen = () => {
8186
setMarkers(data);
8287
} catch (error) {
8388
console.error('Error fetching markers:', error);
84-
} finally {
85-
setLoading(false);
8689
}
87-
};
90+
}, []);
8891

8992
useEffect(() => {
90-
fetchNearbyPhotos(region.latitude, region.longitude);
91-
}, []);
93+
const initialLat = route.params?.latitude || 35.6895;
94+
const initialLng = route.params?.longitude || 139.6917;
95+
96+
fetchNearbyPhotos(initialLat, initialLng);
97+
98+
// パラメータで位置が指定されている場合、その位置にアニメーション
99+
if (route.params?.latitude && route.params?.longitude && mapRef.current) {
100+
setTimeout(() => {
101+
if (mapRef.current && route.params?.latitude && route.params?.longitude) {
102+
mapRef.current.animateToRegion({
103+
latitude: route.params.latitude,
104+
longitude: route.params.longitude,
105+
latitudeDelta: 0.01,
106+
longitudeDelta: 0.01,
107+
}, 1000);
108+
}
109+
}, 500);
110+
}
111+
}, [route.params, fetchNearbyPhotos]);
92112

93113
const onRegionChangeComplete = async (newRegion: Region) => {
94114
setRegion(newRegion);
@@ -104,6 +124,7 @@ const MapViewScreen = () => {
104124

105125
{/* 5. MapViewコンポーネントを設置します */}
106126
<MapView
127+
ref={mapRef}
107128
style={styles.map}
108129
initialRegion={region}
109130
onRegionChangeComplete={onRegionChangeComplete}

frontend/src/screens/PhotoGalleryScreen.tsx

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,16 @@ import {
1717
} from 'react-native';
1818
import { SafeAreaView } from 'react-native-safe-area-context';
1919
import Icon from 'react-native-vector-icons/FontAwesome';
20+
import { useNavigation } from '@react-navigation/native';
21+
import type { BottomTabNavigationProp } from '@react-navigation/bottom-tabs';
22+
import type { RootStackParamList } from '../types/navigation';
2023

2124
// 3. サービスとフックをインポート
2225
import photoService from '../services/photoService';
2326
import { useImagePicker } from '../hooks/useImagePicker';
2427

28+
type NavigationProp = BottomTabNavigationProp<RootStackParamList, 'Gallery'>;
29+
2530
const { width } = Dimensions.get('window');
2631
const ITEM_SIZE = (width - 20) / 2; // アイテムサイズを計算
2732

@@ -32,6 +37,8 @@ interface Photo {
3237
source?: ReturnType<typeof require>; // require() の戻り値の型(ローカル画像の場合)
3338
uri?: string; // API画像のURL
3439
createdAt: number; // ソート用の仮のタイムスタンプ (追加日時)
40+
lat?: number; // 緯度
41+
lng?: number; // 経度
3542
}
3643

3744
// 型エイリアス(後方互換性のため)
@@ -51,6 +58,9 @@ const SORT_OPTIONS: { key: SortOrder; label: string }[] = [
5158
];
5259

5360
const PhotoGalleryScreen = () => {
61+
// Navigation hookを使用
62+
const navigation = useNavigation<NavigationProp>();
63+
5464
// 状態変数: 表示する写真リスト、ソート順序、ローディング状態
5565
const [photos, setPhotos] = useState<Photo[]>([]);
5666
const [sortOrder, setSortOrder] = useState<SortOrder>('added_desc');
@@ -77,6 +87,8 @@ const PhotoGalleryScreen = () => {
7787
name: photo.title || '無題',
7888
uri: photo.s3_key, // S3のURLを直接使用
7989
createdAt: new Date(photo.created_at).getTime(),
90+
lat: photo.lat,
91+
lng: photo.lng,
8092
};
8193
});
8294
} catch (error) {
@@ -172,13 +184,63 @@ const PhotoGalleryScreen = () => {
172184
}
173185
};
174186

187+
// 写真をクリックしてMap画面に遷移する関数
188+
const handlePhotoPress = (photo: Photo) => {
189+
if (photo.lat !== undefined && photo.lng !== undefined) {
190+
navigation.navigate('Map', {
191+
latitude: photo.lat,
192+
longitude: photo.lng,
193+
photoId: photo.id,
194+
photoTitle: photo.name,
195+
});
196+
} else {
197+
Alert.alert('位置情報なし', 'この写真には位置情報が含まれていません');
198+
}
199+
};
200+
201+
// 写真を削除する関数
202+
const handleDeletePhoto = async (photoId: string, photoName: string) => {
203+
Alert.alert(
204+
'写真を削除',
205+
`「${photoName}」を削除しますか?`,
206+
[
207+
{
208+
text: 'キャンセル',
209+
style: 'cancel',
210+
},
211+
{
212+
text: '削除',
213+
style: 'destructive',
214+
onPress: async () => {
215+
try {
216+
await photoService.deletePhoto(photoId);
217+
// 写真を再読み込み
218+
await fetchAndSortLocalPhotos(sortOrder);
219+
Alert.alert('成功', '写真を削除しました');
220+
} catch (error) {
221+
console.error('削除エラー:', error);
222+
const errorMessage = error instanceof Error ? error.message : '写真の削除に失敗しました';
223+
Alert.alert('エラー', errorMessage);
224+
}
225+
},
226+
},
227+
],
228+
{ cancelable: true }
229+
);
230+
};
231+
175232
// --- FlatList の各写真アイテムをレンダリングする関数 ---
176233
const renderPhotoItem = ({ item }: { item: Photo }) => {
177234
// 画像ソースを決定
178235
const imageSource = item.source ? item.source : { uri: item.uri };
179236

180237
return (
181-
<TouchableOpacity style={styles.photoItem}>
238+
<TouchableOpacity
239+
style={styles.photoItem}
240+
onPress={() => handlePhotoPress(item)}
241+
onLongPress={() => handleDeletePhoto(item.id, item.name)}
242+
delayLongPress={500}
243+
>
182244
<Image
183245
source={imageSource}
184246
style={styles.photoImage}
@@ -191,6 +253,12 @@ const PhotoGalleryScreen = () => {
191253
}}
192254
/>
193255
<Text style={styles.photoName} numberOfLines={1}>{item.name}</Text>
256+
{/* 位置情報があるかどうかを示すアイコン */}
257+
{item.lat !== undefined && item.lng !== undefined && (
258+
<View style={styles.locationBadge}>
259+
<Icon name="map-marker" size={12} color="white" />
260+
</View>
261+
)}
194262
</TouchableOpacity>
195263
);
196264
};
@@ -299,9 +367,21 @@ const styles = StyleSheet.create({
299367
loadingContainer: { flex: 1, justifyContent: 'center', alignItems: 'center' },
300368
loadingText: { marginTop: 10, color: '#666' },
301369
listContent: { paddingHorizontal: 5, paddingBottom: 80 },
302-
photoItem: { width: ITEM_SIZE, margin: 5, alignItems: 'center' },
370+
photoItem: { width: ITEM_SIZE, margin: 5, alignItems: 'center', position: 'relative' },
303371
photoImage: { width: '100%', height: ITEM_SIZE * 1.2, borderRadius: 8, backgroundColor: '#E0E0E0' },
304372
photoName: { marginTop: 5, color: '#333', fontSize: 12, textAlign: 'center' },
373+
locationBadge: {
374+
position: 'absolute',
375+
top: 5,
376+
right: 5,
377+
backgroundColor: 'rgba(200, 181, 111, 0.9)',
378+
borderRadius: 12,
379+
padding: 4,
380+
width: 24,
381+
height: 24,
382+
justifyContent: 'center',
383+
alignItems: 'center',
384+
},
305385
emptyContainer: { flexGrow: 1, justifyContent: 'center', alignItems: 'center', paddingVertical: 50 },
306386
emptyText: { marginTop: 10, fontSize: 16, color: '#666' },
307387
emptySubText: { marginTop: 5, fontSize: 12, color: '#999', textAlign: 'center' },

frontend/src/types/navigation.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// src/types/navigation.ts
2+
3+
export type RootStackParamList = {
4+
Gallery: undefined;
5+
Map: {
6+
latitude?: number;
7+
longitude?: number;
8+
photoId?: string;
9+
photoTitle?: string;
10+
};
11+
Feed: undefined;
12+
Settings: undefined;
13+
};

0 commit comments

Comments
 (0)