@@ -14,9 +14,9 @@ import androidx.compose.runtime.mutableStateOf
1414import androidx.compose.runtime.remember
1515import androidx.compose.runtime.rememberCoroutineScope
1616import androidx.compose.runtime.setValue
17+ import androidx.compose.runtime.snapshotFlow
1718import androidx.compose.ui.Alignment
1819import androidx.compose.ui.Modifier
19- import androidx.compose.ui.graphics.ImageBitmap
2020import androidx.compose.ui.platform.LocalContext
2121import androidx.compose.ui.unit.dp
2222import androidx.compose.ui.window.DialogProperties
@@ -51,7 +51,6 @@ import com.neki.android.feature.map.impl.component.PhotoBoothMarker
5151import com.neki.android.feature.map.impl.component.ToMapChip
5252import com.neki.android.feature.map.impl.const.MapConst
5353import com.neki.android.feature.map.impl.util.DirectionHelper
54- import com.neki.android.feature.map.impl.util.rememberCachedBrandImage
5554import kotlinx.coroutines.launch
5655
5756@OptIn(ExperimentalNaverMapApi ::class )
@@ -72,21 +71,17 @@ fun MapRoute(
7271 MapConst .DEFAULT_ZOOM_LEVEL ,
7372 )
7473 }
75- val brandImageCache = rememberCachedBrandImage(uiState.brands)
7674 var isNavigatedToSettings by remember { mutableStateOf(false ) }
77- var isFirstLocationLoaded by remember { mutableStateOf(false ) }
7875
79- fun setFollowMode () {
80- if (locationTrackingMode != LocationTrackingMode .Follow ) {
81- cameraPositionState.move(CameraUpdate .zoomTo(MapConst .DEFAULT_ZOOM_LEVEL ))
82- locationTrackingMode = LocationTrackingMode .Follow
83- }
84- }
76+ val locationPermissionLauncher = rememberLauncherForActivityResult(
77+ contract = ActivityResultContracts .RequestMultiplePermissions (),
78+ ) { permissions ->
79+ val isGranted = permissions.values.any { it }
8580
86- LaunchedEffect (cameraPositionState.cameraUpdateReason, cameraPositionState.isMoving ) {
87- if ( ! cameraPositionState.isMoving && cameraPositionState.cameraUpdateReason == CameraUpdateReason . LOCATION && ! isFirstLocationLoaded) {
88- isFirstLocationLoaded = true
89- // 권한 있고, 최초 요청 시
81+ if (isGranted ) {
82+ locationTrackingMode = LocationTrackingMode . NoFollow
83+ viewModel.store.onIntent( MapIntent . GrantedLocationPermission )
84+ } else {
9085 cameraPositionState.contentBounds?.let { bounds ->
9186 viewModel.store.onIntent(
9287 MapIntent .LoadPhotoBoothsByBounds (
@@ -99,28 +94,28 @@ fun MapRoute(
9994 ),
10095 )
10196 }
102- }
103- }
10497
105- val locationPermissionLauncher = rememberLauncherForActivityResult(
106- contract = ActivityResultContracts .RequestMultiplePermissions (),
107- ) { permissions ->
108- val isGranted = permissions.values.any { it }
109-
110- if (isGranted) {
111- setFollowMode()
112- } else {
113- // 2회 이상 거부
98+ // 영구 거부
11499 if (! LocationPermissionManager .shouldShowLocationRationale(activity)) {
115100 viewModel.store.onIntent(MapIntent .ShowLocationPermissionDialog )
116101 }
117102 }
118103 }
119104
105+ LaunchedEffect (Unit ) {
106+ snapshotFlow { cameraPositionState.isMoving to cameraPositionState.cameraUpdateReason }
107+ .collect { (isMoving, reason) ->
108+ if (isMoving && reason == CameraUpdateReason .GESTURE ) {
109+ viewModel.store.onIntent(MapIntent .GestureOnMap )
110+ }
111+ }
112+ }
113+
120114 LifecycleResumeEffect (Unit ) {
121115 if (isNavigatedToSettings) {
122116 if (LocationPermissionManager .isGrantedLocationPermission(context)) {
123- setFollowMode()
117+ locationTrackingMode = LocationTrackingMode .NoFollow
118+ viewModel.store.onIntent(MapIntent .GrantedLocationPermission )
124119 }
125120 isNavigatedToSettings = false
126121 }
@@ -129,37 +124,8 @@ fun MapRoute(
129124
130125 viewModel.store.sideEffects.collectWithLifecycle { sideEffect ->
131126 when (sideEffect) {
132- is MapEffect .LoadInitialPhotoBooths -> {
133- cameraPositionState.contentBounds?.let { bounds ->
134- if (! LocationPermissionManager .isGrantedLocationPermission(context)) {
135- viewModel.store.onIntent(
136- MapIntent .LoadPhotoBoothsByBounds (
137- MapBounds (
138- southWest = LocLatLng (bounds.southWest.latitude, bounds.southWest.longitude),
139- northWest = LocLatLng (bounds.northWest.latitude, bounds.northWest.longitude),
140- northEast = LocLatLng (bounds.northEast.latitude, bounds.northEast.longitude),
141- southEast = LocLatLng (bounds.southEast.latitude, bounds.southEast.longitude),
142- ),
143- ),
144- )
145- }
146- }
147- }
148-
149- is MapEffect .TrackingFollowMode -> {
150- if (LocationPermissionManager .isGrantedLocationPermission(context)) {
151- setFollowMode()
152- } else {
153- viewModel.store.onIntent(MapIntent .RequestLocationPermission )
154- }
155- }
156-
157127 is MapEffect .OpenDirectionBottomSheet -> {
158- if (LocationPermissionManager .isGrantedLocationPermission(context)) {
159- viewModel.store.onIntent(MapIntent .OpenDirectionBottomSheet )
160- } else {
161- viewModel.store.onIntent(MapIntent .RequestLocationPermission )
162- }
128+ viewModel.store.onIntent(MapIntent .OpenDirectionBottomSheet )
163129 }
164130
165131 is MapEffect .MoveCameraToPosition -> {
@@ -172,6 +138,22 @@ fun MapRoute(
172138 animation = CameraAnimation .Easing ,
173139 durationMs = MapConst .DEFAULT_CAMERA_ANIMATION_DURATIONS_MS ,
174140 )
141+
142+ if (sideEffect.isRequiredLoadPhotoBooths) {
143+ locationTrackingMode = LocationTrackingMode .NoFollow
144+ cameraPositionState.contentBounds?.let { bounds ->
145+ viewModel.store.onIntent(
146+ MapIntent .LoadPhotoBoothsByBounds (
147+ MapBounds (
148+ southWest = LocLatLng (bounds.southWest.latitude, bounds.southWest.longitude),
149+ northWest = LocLatLng (bounds.northWest.latitude, bounds.northWest.longitude),
150+ northEast = LocLatLng (bounds.northEast.latitude, bounds.northEast.longitude),
151+ southEast = LocLatLng (bounds.southEast.latitude, bounds.southEast.longitude),
152+ ),
153+ ),
154+ )
155+ }
156+ }
175157 }
176158 }
177159
@@ -203,17 +185,7 @@ fun MapRoute(
203185 uiState = uiState,
204186 onIntent = viewModel.store::onIntent,
205187 locationTrackingMode = locationTrackingMode,
206- onLocationTrackingModeChange = { locationTrackingMode = it },
207188 cameraPositionState = cameraPositionState,
208- brandImageCache = brandImageCache,
209- onMapLoaded = {
210- if (LocationPermissionManager .isGrantedLocationPermission(context)) {
211- setFollowMode()
212- } else {
213- viewModel.store.onIntent(MapIntent .NaverMapLoaded )
214- viewModel.store.onIntent(MapIntent .RequestLocationPermission )
215- }
216- },
217189 )
218190}
219191
@@ -223,12 +195,9 @@ fun MapScreen(
223195 uiState : MapState = MapState (),
224196 onIntent : (MapIntent ) -> Unit = {},
225197 locationTrackingMode : LocationTrackingMode = LocationTrackingMode .None ,
226- onLocationTrackingModeChange : (LocationTrackingMode ) -> Unit = {},
227198 cameraPositionState : CameraPositionState = rememberCameraPositionState {
228199 position = CameraPosition (LatLng (MapConst .DEFAULT_LATITUDE , MapConst .DEFAULT_LONGITUDE ), MapConst .DEFAULT_ZOOM_LEVEL )
229200 },
230- brandImageCache : Map <String , ImageBitmap > = emptyMap(),
231- onMapLoaded : () -> Unit = {},
232201) {
233202 val mapProperties = remember(locationTrackingMode) {
234203 MapProperties (
@@ -251,22 +220,15 @@ fun MapScreen(
251220 locationSource = rememberFusedLocationSource(),
252221 properties = mapProperties,
253222 uiSettings = mapUiSettings,
254- onMapLoaded = onMapLoaded,
255- onOptionChange = {
256- cameraPositionState.locationTrackingMode?.let {
257- onLocationTrackingModeChange(it)
258- }
259- },
223+ onMapLoaded = { onIntent(MapIntent .RequestLocationPermission ) },
260224 onLocationChange = { location ->
261225 onIntent(MapIntent .UpdateCurrentLocation (LocLatLng (location.latitude, location.longitude)))
262226 },
263227 ) {
264228 uiState.mapMarkers.filter { it.isCheckedBrand }.forEach { photoBooth ->
265- val cachedBitmap = brandImageCache[photoBooth.imageUrl]
266229 PhotoBoothMarker (
267230 photoBooth = photoBooth,
268- isFocused = photoBooth.isFocused,
269- cachedBitmap = cachedBitmap,
231+ cachedBitmap = uiState.brandImageCache[photoBooth.imageUrl],
270232 onClick = {
271233 onIntent(MapIntent .ClickPhotoBoothMarker (LocLatLng (photoBooth.latitude, photoBooth.longitude)))
272234 },
@@ -279,14 +241,14 @@ fun MapScreen(
279241 nearbyPhotoBooths = uiState.nearbyPhotoBooths,
280242 dragLevel = uiState.dragLevel,
281243 onDragLevelChanged = { onIntent(MapIntent .ChangeDragLevel (it)) },
244+ isCurrentLocation = uiState.isCameraOnCurrentLocation,
282245 onClickInfoIcon = { onIntent(MapIntent .ClickInfoIcon ) },
283- isCurrentLocation = locationTrackingMode == LocationTrackingMode .Follow ,
284246 onClickCurrentLocation = { onIntent(MapIntent .ClickCurrentLocationIcon ) },
285247 onClickBrand = { onIntent(MapIntent .ClickVerticalBrand (it)) },
286248 onClickNearPhotoBooth = { onIntent(MapIntent .ClickNearPhotoBooth (it)) },
287249 )
288250
289- if ((uiState.dragLevel == DragLevel .FIRST || uiState.dragLevel == DragLevel .SECOND ) && locationTrackingMode != LocationTrackingMode . Follow ) {
251+ if ((uiState.dragLevel == DragLevel .FIRST || uiState.dragLevel == DragLevel .SECOND ) && uiState.isVisibleRefreshButton ) {
290252 MapRefreshChip (
291253 modifier = Modifier
292254 .align(Alignment .TopCenter )
@@ -321,7 +283,7 @@ fun MapScreen(
321283 PhotoBoothDetailContent (
322284 photoBooth = focusedPhotoBooth,
323285 modifier = Modifier .align(Alignment .BottomCenter ),
324- isCurrentLocation = locationTrackingMode == LocationTrackingMode . Follow ,
286+ isCurrentLocation = uiState.isCameraOnCurrentLocation ,
325287 onClickCurrentLocation = { onIntent(MapIntent .ClickCurrentLocationIcon ) },
326288 onClickCloseCard = { onIntent(MapIntent .ClickClosePhotoBoothCard ) },
327289 onClickCard = {
@@ -359,9 +321,7 @@ fun MapScreen(
359321 )
360322 }
361323
362- val isLoadingLocation = locationTrackingMode == LocationTrackingMode .Follow && uiState.currentLocLatLng == null
363-
364- if (isLoadingLocation || uiState.isLoading) {
324+ if (uiState.isLoading) {
365325 LoadingDialog (
366326 properties = DialogProperties (
367327 dismissOnBackPress = false ,
0 commit comments