Skip to content

Commit

Permalink
Merge branch 'feat/#179-map-location-search' of https://github.com/SO…
Browse files Browse the repository at this point in the history
…PT-all/35-APPJAM-ANDROID-SPOONY into feat/#179-map-location-search
  • Loading branch information
Hyobeen-Park committed Jan 24, 2025
2 parents 9354059 + 439ff61 commit 7cd2dd1
Show file tree
Hide file tree
Showing 21 changed files with 252 additions and 34 deletions.
4 changes: 4 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ dependencies {

// Naver Map
implementation(libs.bundles.naverMap)

// Room
implementation(libs.bundles.room)
ksp(libs.room.compiler)
}

ktlint {
Expand Down
48 changes: 48 additions & 0 deletions app/src/main/java/com/spoony/spoony/core/database/SearchDao.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.spoony.spoony.core.database

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Transaction
import com.spoony.spoony.core.database.entity.SearchEntity

@Dao
interface SearchDao {
// 검색어 추가
@Insert
suspend fun insertSearch(searchEntity: SearchEntity)

// 특정 검색어 삭제
@Query("DELETE FROM search WHERE text = :searchText")
suspend fun deleteSearchByText(searchText: String)

// 전체 검색어 삭제
@Query("DELETE FROM search")
suspend fun deleteAllSearches()

// 최신순으로 최대 6개의 검색어 가져오기
@Query("SELECT * FROM search ORDER BY id DESC LIMIT 6")
suspend fun getRecentSearches(): List<SearchEntity>

// 검색어 추가 시, 최근 6개만 유지하도록 처리
@Transaction
suspend fun addSearchWithLimit(searchEntity: SearchEntity) {
// 검색어 추가
insertSearch(searchEntity)

// 총 검색어 수가 6개를 초과하면 오래된 검색어 삭제
val allSearches = getAllSearches() // 모든 검색어 가져오기
if (allSearches.size > 6) {
val excessSearches = allSearches.drop(6) // 초과된 검색어
excessSearches.forEach { deleteSearchById(it.id) }
}
}

// 특정 ID 검색어 삭제
@Query("DELETE FROM search WHERE id = :id")
suspend fun deleteSearchById(id: Int)

// 전체 검색어 가져오기
@Query("SELECT * FROM search ORDER BY id DESC")
suspend fun getAllSearches(): List<SearchEntity>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.spoony.spoony.core.database

import androidx.room.Database
import androidx.room.RoomDatabase
import com.spoony.spoony.core.database.entity.SearchEntity

@Database(
entities = [SearchEntity::class],
version = 1
)
abstract class SearchDatabase : RoomDatabase() {
abstract fun SearchDao(): SearchDao
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.spoony.spoony.core.database.di

import android.content.Context
import androidx.room.Room
import com.spoony.spoony.core.database.SearchDatabase
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object DBModule {
@Singleton
@Provides
fun providesDataBase(
@ApplicationContext context: Context
): SearchDatabase =
Room.databaseBuilder(context, SearchDatabase::class.java, "search-database").build()

@Singleton
@Provides
fun providesDao(
searchDatabase: SearchDatabase
) = searchDatabase.SearchDao()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.spoony.spoony.core.database.entity

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(
tableName = "search"
)
data class SearchEntity(
@PrimaryKey(autoGenerate = true)
val id: Int = 0,
val text: String
)
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,11 @@ fun SpoonyBasicTextField(
imeAction: ImeAction = ImeAction.Done,
onDoneAction: () -> Unit = {},
onSearchAction: () -> Unit = {},
focusRequester: FocusRequester = FocusRequester(),
singleLine: Boolean = true,
leadingIcon: @Composable () -> Unit = {},
trailingIcon: @Composable () -> Unit = {}
) {
val focusRequester = remember { FocusRequester() }

BasicTextField(
value = value,
onValueChange = { newValue ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
Expand All @@ -28,6 +29,7 @@ fun SpoonySearchTextField(
placeholder: String,
modifier: Modifier = Modifier,
maxLength: Int = Int.MAX_VALUE,
focusRequester: FocusRequester = FocusRequester(),
onSearchAction: () -> Unit
) {
var isFocused by remember { mutableStateOf(false) }
Expand Down Expand Up @@ -74,6 +76,7 @@ fun SpoonySearchTextField(
}
},
imeAction = ImeAction.Search,
focusRequester = focusRequester,
onSearchAction = {
onSearchAction()
keyboardController?.hide()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.spoony.spoony.data.repositoryimpl

import com.spoony.spoony.core.database.SearchDao
import com.spoony.spoony.core.database.entity.SearchEntity
import com.spoony.spoony.data.datasource.MapRemoteDataSource
import com.spoony.spoony.data.datasource.PostRemoteDataSource
import com.spoony.spoony.data.mapper.toDomain
Expand All @@ -11,7 +13,8 @@ import javax.inject.Inject

class MapRepositoryImpl @Inject constructor(
private val mapRemoteDataSource: MapRemoteDataSource,
private val postRemoteDataSource: PostRemoteDataSource
private val postRemoteDataSource: PostRemoteDataSource,
private val searchDao: SearchDao,
) : MapRepository {
override suspend fun searchLocation(query: String): Result<List<LocationEntity>> =
runCatching {
Expand All @@ -30,4 +33,24 @@ class MapRepositoryImpl @Inject constructor(
postRemoteDataSource.getZzimByLocation(userId, locationId)
.data!!.zzimCardResponses.map { it.toDomain() }
}

override suspend fun getRecentSearches(): Result<List<String>> =
runCatching {
searchDao.getRecentSearches().map { it.text }
}

override suspend fun deleteSearchByText(searchText: String): Result<Unit> =
runCatching {
searchDao.deleteSearchByText(searchText)
}

override suspend fun deleteAllSearches(): Result<Unit> =
runCatching {
searchDao.deleteAllSearches()
}

override suspend fun addSearch(searchText: String) =
runCatching {
searchDao.addSearchWithLimit(SearchEntity(text = searchText))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,8 @@ interface MapRepository {
suspend fun searchLocation(query: String): Result<List<LocationEntity>>
suspend fun getAddedPlaceListByLocation(userId: Int, locationId: Int): Result<List<AddedPlaceEntity>>
suspend fun getAddedPlaceList(userId: Int): Result<AddedPlaceListEntity>
suspend fun getRecentSearches(): Result<List<String>>
suspend fun deleteSearchByText(searchText: String): Result<Unit>
suspend fun deleteAllSearches(): Result<Unit>
suspend fun addSearch(searchText: String): Result<Unit>
}
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@ private fun ExploreScreen(

if (isLocationBottomSheetVisible) {
ExploreLocationBottomSheet(
selectedCity = selectedCity,
onDismiss = { isLocationBottomSheetVisible = false },
onClick = onLocationSortingButtonClick
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ class ExploreViewModel @Inject constructor(

init {
getCategoryList()
getFeedList()
}

private fun getCategoryList() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ val CITY_LIST = persistentListOf(
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ExploreLocationBottomSheet(
selectedCity: String,
onDismiss: () -> Unit,
onClick: (String) -> Unit
) {
Expand All @@ -84,7 +83,7 @@ fun ExploreLocationBottomSheet(
skipPartiallyExpanded = true
)

var currentCity by remember { mutableStateOf(selectedCity) }
var currentCity by remember { mutableStateOf("") }
var columnHeight by remember { mutableIntStateOf(0) }

GetColumnHeight(
Expand Down Expand Up @@ -144,6 +143,7 @@ fun ExploreLocationBottomSheet(
onClick(currentCity)
onDismiss()
},
enabled = currentCity.isNotEmpty(),
modifier = Modifier
.fillMaxWidth()
.padding(
Expand Down
13 changes: 13 additions & 0 deletions app/src/main/java/com/spoony/spoony/presentation/map/MapScreen.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.spoony.spoony.presentation.map

import android.view.Gravity
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOut
Expand All @@ -18,6 +19,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.shape.RoundedCornerShape
Expand Down Expand Up @@ -47,6 +49,7 @@ import com.naver.maps.map.CameraPosition
import com.naver.maps.map.CameraUpdate
import com.naver.maps.map.compose.CameraPositionState
import com.naver.maps.map.compose.ExperimentalNaverMapApi
import com.naver.maps.map.compose.MapUiSettings
import com.naver.maps.map.compose.Marker
import com.naver.maps.map.compose.MarkerState
import com.naver.maps.map.compose.NaverMap
Expand Down Expand Up @@ -86,6 +89,11 @@ fun MapRoute(
) {
val state by viewModel.state.collectAsStateWithLifecycle()

LaunchedEffect(Unit) {
viewModel.getAddedPlaceList()
viewModel.getSpoonCount()
}

val userName = when (state.userName) {
is UiState.Success -> {
(state.userName as UiState.Success<String>).data
Expand Down Expand Up @@ -160,6 +168,7 @@ fun MapScreen(
confirmValueChange = { true }
)
val scaffoldState = rememberBottomSheetScaffoldState(sheetState)
val lazyListState = rememberLazyListState()

var isSelected by remember { mutableStateOf(false) }
var selectedMarkerId by remember { mutableIntStateOf(-1) }
Expand All @@ -170,6 +179,9 @@ fun MapScreen(
) {
NaverMap(
cameraPositionState = cameraPositionState,
uiSettings = MapUiSettings(
isZoomControlEnabled = false
),
onMapClick = { _, _ ->
if (isSelected) {
selectedMarkerId = -1
Expand Down Expand Up @@ -324,6 +336,7 @@ fun MapScreen(
} else {
LazyColumn(
contentPadding = PaddingValues(bottom = paddingValues.calculateBottomPadding()),
state = lazyListState,
modifier = Modifier
.fillMaxSize()
.padding(bottom = paddingValues.calculateBottomPadding())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,14 @@ import timber.log.Timber
class MapViewModel @Inject constructor(
private val postRepository: PostRepository,
private val mapRepository: MapRepository,
private val authRepository: AuthRepository,
private val authRepository: AuthRepository
) : ViewModel() {
private var _state: MutableStateFlow<MapState> = MutableStateFlow(MapState())
val state: StateFlow<MapState>
get() = _state.asStateFlow()

init {
getUserInfo()
getSpoonCount()

getAddedPlaceList()
}

fun getPlaceInfo(placeId: Int) {
Expand All @@ -52,7 +49,7 @@ class MapViewModel @Inject constructor(
}
}

private fun getAddedPlaceList() {
fun getAddedPlaceList() {
viewModelScope.launch {
mapRepository.getAddedPlaceList(USER_ID)
.onSuccess { response ->
Expand Down Expand Up @@ -114,7 +111,7 @@ class MapViewModel @Inject constructor(
}
}

private fun getSpoonCount() {
fun getSpoonCount() {
viewModelScope.launch {
authRepository.getSpoonCount(USER_ID)
.onSuccess { response ->
Expand All @@ -134,4 +131,14 @@ class MapViewModel @Inject constructor(
)
}
}

fun resetSelectedPlace() {
_state.update {
it.copy(
locationModel = it.locationModel.copy(
placeId = null
)
)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.spoony.spoony.presentation.map
package com.spoony.spoony.presentation.map.locationMap

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.slideInVertically
Expand Down Expand Up @@ -55,7 +55,6 @@ import com.spoony.spoony.presentation.map.component.MapPlaceDetailCard
import com.spoony.spoony.presentation.map.component.bottomsheet.MapBottomSheetDragHandle
import com.spoony.spoony.presentation.map.component.bottomsheet.MapEmptyBottomSheetContent
import com.spoony.spoony.presentation.map.component.bottomsheet.MapListItem
import com.spoony.spoony.presentation.map.locationMap.LocationMapViewModel
import com.spoony.spoony.presentation.map.model.LocationModel
import io.morfly.compose.bottomsheet.material3.rememberBottomSheetScaffoldState
import io.morfly.compose.bottomsheet.material3.rememberBottomSheetState
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavOptions
import androidx.navigation.compose.composable
import com.spoony.spoony.core.navigation.Route
import com.spoony.spoony.presentation.map.LocationMapRoute
import com.spoony.spoony.presentation.map.locationMap.LocationMapRoute
import kotlinx.serialization.Serializable

fun NavController.navigateToLocationMap(
Expand Down
Loading

0 comments on commit 7cd2dd1

Please sign in to comment.