From 442548d3a55a68200653b6ee2d2be9d114a7be65 Mon Sep 17 00:00:00 2001 From: gaeulzzang Date: Fri, 8 Nov 2024 17:51:28 +0900 Subject: [PATCH 1/4] =?UTF-8?q?#28=20[FEAT]=20=EC=A2=8B=EC=95=84=EC=9A=94?= =?UTF-8?q?=20=EB=AA=A9=EB=A1=9D=20api=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/di/DataSourceModule.kt | 6 +++ .../app/di/RepositoryModule.kt | 6 +++ .../alddeul_babsang/app/di/ServiceModule.kt | 7 ++++ .../data/datasource/ProfileDataSource.kt | 8 ++++ .../datasourceimpl/ProfileDataSourceImpl.kt | 15 +++++++ .../data/dto/response/ResponseLikesDto.kt | 20 ++++++++++ .../repositoryimpl/ProfileRepositoryImpl.kt | 16 ++++++++ .../data/service/ProfileApiService.kt | 11 +++++ .../domain/repository/ProfileRepository.kt | 7 ++++ .../presentation/profile/screen/LikeItem.kt | 27 +++---------- .../presentation/profile/screen/LikeScreen.kt | 40 +++++++++++++++---- .../profile/screen/LikeViewModel.kt | 27 ++++++++++++- 12 files changed, 160 insertions(+), 30 deletions(-) create mode 100644 app/src/main/java/com/hackathon/alddeul_babsang/data/datasource/ProfileDataSource.kt create mode 100644 app/src/main/java/com/hackathon/alddeul_babsang/data/datasourceimpl/ProfileDataSourceImpl.kt create mode 100644 app/src/main/java/com/hackathon/alddeul_babsang/data/dto/response/ResponseLikesDto.kt create mode 100644 app/src/main/java/com/hackathon/alddeul_babsang/data/repositoryimpl/ProfileRepositoryImpl.kt create mode 100644 app/src/main/java/com/hackathon/alddeul_babsang/data/service/ProfileApiService.kt create mode 100644 app/src/main/java/com/hackathon/alddeul_babsang/domain/repository/ProfileRepository.kt diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/app/di/DataSourceModule.kt b/app/src/main/java/com/hackathon/alddeul_babsang/app/di/DataSourceModule.kt index 1bc750e..e91e6f3 100644 --- a/app/src/main/java/com/hackathon/alddeul_babsang/app/di/DataSourceModule.kt +++ b/app/src/main/java/com/hackathon/alddeul_babsang/app/di/DataSourceModule.kt @@ -2,9 +2,11 @@ package com.hackathon.alddeul_babsang.app.di import com.hackathon.alddeul_babsang.data.datasource.BabsangDataSource import com.hackathon.alddeul_babsang.data.datasource.ExampleDataSource +import com.hackathon.alddeul_babsang.data.datasource.ProfileDataSource import com.hackathon.alddeul_babsang.data.datasource.UserPreferencesDataSource import com.hackathon.alddeul_babsang.data.datasourceimpl.BabsangDataSourceImpl import com.hackathon.alddeul_babsang.data.datasourceimpl.ExampleDataSourceImpl +import com.hackathon.alddeul_babsang.data.datasourceimpl.ProfileDataSourceImpl import com.hackathon.alddeul_babsang.data.datasourceimpl.UserPreferencesDataSourceImpl import dagger.Binds import dagger.Module @@ -27,4 +29,8 @@ abstract class DataSourceModule { @Binds @Singleton abstract fun bindBabsangDataSource(babsangDataSourceImpl: BabsangDataSourceImpl): BabsangDataSource + + @Binds + @Singleton + abstract fun bindProfileDataSource(profileDataSourceImpl: ProfileDataSourceImpl): ProfileDataSource } \ No newline at end of file diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/app/di/RepositoryModule.kt b/app/src/main/java/com/hackathon/alddeul_babsang/app/di/RepositoryModule.kt index 0db5c38..18ad144 100644 --- a/app/src/main/java/com/hackathon/alddeul_babsang/app/di/RepositoryModule.kt +++ b/app/src/main/java/com/hackathon/alddeul_babsang/app/di/RepositoryModule.kt @@ -2,9 +2,11 @@ package com.hackathon.alddeul_babsang.app.di import com.hackathon.alddeul_babsang.data.repositoryimpl.BabsangRepositoryImpl import com.hackathon.alddeul_babsang.data.repositoryimpl.ExampleRepositoryImpl +import com.hackathon.alddeul_babsang.data.repositoryimpl.ProfileRepositoryImpl import com.hackathon.alddeul_babsang.data.repositoryimpl.UserPreferencesRepositoryImpl import com.hackathon.alddeul_babsang.domain.repository.BabsangRepository import com.hackathon.alddeul_babsang.domain.repository.ExampleRepository +import com.hackathon.alddeul_babsang.domain.repository.ProfileRepository import com.hackathon.alddeul_babsang.domain.repository.UserPreferencesRepository import dagger.Binds import dagger.Module @@ -27,4 +29,8 @@ abstract class RepositoryModule { @Binds @Singleton abstract fun bindBabsangRepository(babsangRepositoryImpl: BabsangRepositoryImpl): BabsangRepository + + @Binds + @Singleton + abstract fun bindProfileRepository(profileRepositoryImpl: ProfileRepositoryImpl): ProfileRepository } \ No newline at end of file diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/app/di/ServiceModule.kt b/app/src/main/java/com/hackathon/alddeul_babsang/app/di/ServiceModule.kt index 27583ee..4509be1 100644 --- a/app/src/main/java/com/hackathon/alddeul_babsang/app/di/ServiceModule.kt +++ b/app/src/main/java/com/hackathon/alddeul_babsang/app/di/ServiceModule.kt @@ -1,6 +1,7 @@ package com.hackathon.alddeul_babsang.app.di import com.hackathon.alddeul_babsang.data.service.BabsangApiService +import com.hackathon.alddeul_babsang.data.service.ProfileApiService import com.sopt.data.service.ExampleApiService import dagger.Module import dagger.Provides @@ -24,4 +25,10 @@ object ServiceModule { fun provideBabsangService( @AlddeulRetrofit retrofit: Retrofit ): BabsangApiService = retrofit.create(BabsangApiService::class.java) + + @Provides + @Singleton + fun provideProfileService( + @AlddeulRetrofit retrofit: Retrofit + ): ProfileApiService = retrofit.create(ProfileApiService::class.java) } diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/data/datasource/ProfileDataSource.kt b/app/src/main/java/com/hackathon/alddeul_babsang/data/datasource/ProfileDataSource.kt new file mode 100644 index 0000000..975b431 --- /dev/null +++ b/app/src/main/java/com/hackathon/alddeul_babsang/data/datasource/ProfileDataSource.kt @@ -0,0 +1,8 @@ +package com.hackathon.alddeul_babsang.data.datasource + +import com.hackathon.alddeul_babsang.data.dto.BaseResponse +import com.hackathon.alddeul_babsang.data.dto.response.ResponseLikesDto + +interface ProfileDataSource { + suspend fun getLikes(): BaseResponse +} \ No newline at end of file diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/data/datasourceimpl/ProfileDataSourceImpl.kt b/app/src/main/java/com/hackathon/alddeul_babsang/data/datasourceimpl/ProfileDataSourceImpl.kt new file mode 100644 index 0000000..50fc70f --- /dev/null +++ b/app/src/main/java/com/hackathon/alddeul_babsang/data/datasourceimpl/ProfileDataSourceImpl.kt @@ -0,0 +1,15 @@ +package com.hackathon.alddeul_babsang.data.datasourceimpl + +import com.hackathon.alddeul_babsang.data.datasource.ProfileDataSource +import com.hackathon.alddeul_babsang.data.dto.BaseResponse +import com.hackathon.alddeul_babsang.data.dto.response.ResponseLikesDto +import com.hackathon.alddeul_babsang.data.service.ProfileApiService +import javax.inject.Inject + +class ProfileDataSourceImpl @Inject constructor( + private val profileApiService: ProfileApiService +): ProfileDataSource { + override suspend fun getLikes(): BaseResponse { + return profileApiService.getLikes() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/data/dto/response/ResponseLikesDto.kt b/app/src/main/java/com/hackathon/alddeul_babsang/data/dto/response/ResponseLikesDto.kt new file mode 100644 index 0000000..43e0de2 --- /dev/null +++ b/app/src/main/java/com/hackathon/alddeul_babsang/data/dto/response/ResponseLikesDto.kt @@ -0,0 +1,20 @@ +package com.hackathon.alddeul_babsang.data.dto.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class ResponseLikesDto( + @SerialName("favoriteRestaurants") val favoriteRestaurants: List +) + +@Serializable +data class FavoriteRestaurantDto( + @SerialName("restaurantId") val restaurantId: Long, + @SerialName("name") val name: String, + @SerialName("category") val category: String, + @SerialName("address") val address: String, + @SerialName("contact") val contact: String, + @SerialName("restaurantImageUrl") val restaurantImageUrl: String? = null, + @SerialName("favorite") val favorite: Boolean +) \ No newline at end of file diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/data/repositoryimpl/ProfileRepositoryImpl.kt b/app/src/main/java/com/hackathon/alddeul_babsang/data/repositoryimpl/ProfileRepositoryImpl.kt new file mode 100644 index 0000000..e6b788b --- /dev/null +++ b/app/src/main/java/com/hackathon/alddeul_babsang/data/repositoryimpl/ProfileRepositoryImpl.kt @@ -0,0 +1,16 @@ +package com.hackathon.alddeul_babsang.data.repositoryimpl + +import com.hackathon.alddeul_babsang.data.datasource.ProfileDataSource +import com.hackathon.alddeul_babsang.data.dto.response.FavoriteRestaurantDto +import com.hackathon.alddeul_babsang.domain.repository.ProfileRepository +import javax.inject.Inject + +class ProfileRepositoryImpl @Inject constructor( + private val profileDataSource: ProfileDataSource +) : ProfileRepository { + override suspend fun getLikes(): Result> { + return runCatching { + profileDataSource.getLikes().result?.favoriteRestaurants ?: emptyList() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/data/service/ProfileApiService.kt b/app/src/main/java/com/hackathon/alddeul_babsang/data/service/ProfileApiService.kt new file mode 100644 index 0000000..1b4f3d1 --- /dev/null +++ b/app/src/main/java/com/hackathon/alddeul_babsang/data/service/ProfileApiService.kt @@ -0,0 +1,11 @@ +package com.hackathon.alddeul_babsang.data.service + +import com.hackathon.alddeul_babsang.data.dto.BaseResponse +import com.hackathon.alddeul_babsang.data.dto.response.ResponseLikesDto +import com.sopt.data.service.ApiKeyStorage.STORES +import retrofit2.http.GET + +interface ProfileApiService { + @GET("/$STORES") + suspend fun getLikes(): BaseResponse +} \ No newline at end of file diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/domain/repository/ProfileRepository.kt b/app/src/main/java/com/hackathon/alddeul_babsang/domain/repository/ProfileRepository.kt new file mode 100644 index 0000000..5197015 --- /dev/null +++ b/app/src/main/java/com/hackathon/alddeul_babsang/domain/repository/ProfileRepository.kt @@ -0,0 +1,7 @@ +package com.hackathon.alddeul_babsang.domain.repository + +import com.hackathon.alddeul_babsang.data.dto.response.FavoriteRestaurantDto + +interface ProfileRepository { + suspend fun getLikes(): Result> +} \ No newline at end of file diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/presentation/profile/screen/LikeItem.kt b/app/src/main/java/com/hackathon/alddeul_babsang/presentation/profile/screen/LikeItem.kt index 012fcb1..3057997 100644 --- a/app/src/main/java/com/hackathon/alddeul_babsang/presentation/profile/screen/LikeItem.kt +++ b/app/src/main/java/com/hackathon/alddeul_babsang/presentation/profile/screen/LikeItem.kt @@ -37,12 +37,13 @@ import com.hackathon.alddeul_babsang.core_ui.theme.Orange900 import com.hackathon.alddeul_babsang.core_ui.theme.body2Regular import com.hackathon.alddeul_babsang.core_ui.theme.body4Regular import com.hackathon.alddeul_babsang.core_ui.theme.head4Bold +import com.hackathon.alddeul_babsang.data.dto.response.FavoriteRestaurantDto import com.hackathon.alddeul_babsang.domain.entity.LikesEntity @Composable fun LikeItem( onClick: () -> Unit = {}, - data: LikesEntity + data: FavoriteRestaurantDto ) { var isFavorite by remember { mutableStateOf(data.favorite) } @@ -70,7 +71,7 @@ fun LikeItem( .clip(RoundedCornerShape(topStart = 14.dp, topEnd = 14.dp)) ) { // AsyncImage 로드 - LoadImageWithPlaceholder(data.codeName, data.avatar) + LoadImageWithPlaceholder(data.category, data.restaurantImageUrl) Image( painter = painterResource(heartIconId), contentDescription = null, @@ -93,7 +94,7 @@ fun LikeItem( ) Spacer(modifier = Modifier.width(15.dp)) Text( - text = data.codeName, + text = data.category, style = body2Regular, color = Orange800, modifier = Modifier @@ -110,7 +111,7 @@ fun LikeItem( ) Spacer(modifier = Modifier.height(7.dp)) Text( - text = data.phone, + text = data.contact, style = body4Regular, color = Gray300, modifier = Modifier.padding(start = 20.dp, bottom = 20.dp) @@ -157,22 +158,4 @@ fun LoadImageWithPlaceholder(codeName: String, imageUrl: String?) { ) } } -} - -@Preview(showBackground = true) -@Composable -fun LikeItemPreview() { - AlddeulBabsangTheme { - LikeItem( - data = LikesEntity( - id = 1, - avatar = null, - name = "송이네 밥상", - codeName = "경양식/일식", - address = "서울특별시 용산구 청파동 11", - phone = "02-210-0220", - favorite = true - ) - ) - } } \ No newline at end of file diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/presentation/profile/screen/LikeScreen.kt b/app/src/main/java/com/hackathon/alddeul_babsang/presentation/profile/screen/LikeScreen.kt index b1f9836..fc1f23c 100644 --- a/app/src/main/java/com/hackathon/alddeul_babsang/presentation/profile/screen/LikeScreen.kt +++ b/app/src/main/java/com/hackathon/alddeul_babsang/presentation/profile/screen/LikeScreen.kt @@ -6,7 +6,6 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.rememberScrollState import androidx.compose.material3.CenterAlignedTopAppBar import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon @@ -15,7 +14,9 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.SideEffect +import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.stringResource @@ -26,8 +27,10 @@ import androidx.compose.ui.text.withStyle import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.hackathon.alddeul_babsang.R +import com.hackathon.alddeul_babsang.core_ui.component.LoadingCircleIndicator import com.hackathon.alddeul_babsang.core_ui.theme.AlddeulBabsangTheme import com.hackathon.alddeul_babsang.core_ui.theme.Gray900 import com.hackathon.alddeul_babsang.core_ui.theme.Orange800 @@ -35,6 +38,7 @@ import com.hackathon.alddeul_babsang.core_ui.theme.White import com.hackathon.alddeul_babsang.core_ui.theme.head4Bold import com.hackathon.alddeul_babsang.core_ui.theme.head6Semi import com.hackathon.alddeul_babsang.presentation.profile.navigation.ProfileNavigator +import com.hackathon.alddeul_babsang.util.UiState @Composable fun LikeRoute( @@ -50,6 +54,10 @@ fun LikeRoute( ) } + LaunchedEffect(Unit) { + likeViewModel.getLikes() + } + LikeScreen( onItemClick = { id -> navigator.navigateDetail(id) }, onBackClick = { navigator.navigateBack() }, @@ -64,7 +72,7 @@ fun LikeScreen( onBackClick: () -> Unit = {}, likeViewModel: LikeViewModel ) { - val scrollState = rememberScrollState() + val getLikesState by likeViewModel.getLikesState.collectAsStateWithLifecycle(UiState.Empty) Scaffold( topBar = { @@ -114,11 +122,29 @@ fun LikeScreen( style = head6Semi ) } - items(likeViewModel.mockLikes) { item -> - LikeItem( - onClick = { onItemClick(item.id) }, - data = item - ) + when (getLikesState) { + is UiState.Success -> { + items((getLikesState as UiState.Success).data) { item -> + LikeItem( + onClick = { onItemClick(item.restaurantId) }, + data = item + ) + } + } + + is UiState.Loading -> { + item { + LoadingCircleIndicator() + } + } + + is UiState.Failure -> { + item { + Text(text = (getLikesState as UiState.Failure).msg) + } + } + + else -> {} } } } diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/presentation/profile/screen/LikeViewModel.kt b/app/src/main/java/com/hackathon/alddeul_babsang/presentation/profile/screen/LikeViewModel.kt index 00be6eb..2dedf18 100644 --- a/app/src/main/java/com/hackathon/alddeul_babsang/presentation/profile/screen/LikeViewModel.kt +++ b/app/src/main/java/com/hackathon/alddeul_babsang/presentation/profile/screen/LikeViewModel.kt @@ -2,12 +2,37 @@ package com.hackathon.alddeul_babsang.presentation.profile.screen import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.hackathon.alddeul_babsang.data.dto.response.FavoriteRestaurantDto import com.hackathon.alddeul_babsang.domain.entity.LikesEntity +import com.hackathon.alddeul_babsang.domain.repository.ProfileRepository +import com.hackathon.alddeul_babsang.util.UiState import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel -class LikeViewModel @Inject constructor() : ViewModel() { +class LikeViewModel @Inject constructor( + private val profileRepository: ProfileRepository +) : ViewModel() { + private val _getLikesState = + MutableStateFlow>>(UiState.Empty) + val getLikesState: StateFlow>> = _getLikesState + + fun getLikes() = viewModelScope.launch { + _getLikesState.emit(UiState.Loading) + profileRepository.getLikes().fold( + onSuccess = { + _getLikesState.emit(UiState.Success(it)) + }, + onFailure = { + _getLikesState.emit(UiState.Failure(it.message.toString())) + } + ) + } + val mockLikes = listOf( LikesEntity( id = 1, From 3bf170c3cffbb493b5e7862c557a98b379087ae2 Mon Sep 17 00:00:00 2001 From: gaeulzzang Date: Fri, 8 Nov 2024 18:00:15 +0900 Subject: [PATCH 2/4] =?UTF-8?q?#28=20[MOD]=20=EC=A2=8B=EC=95=84=EC=9A=94?= =?UTF-8?q?=20=EB=AA=A9=EB=A1=9D=20userId=20path=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/datasource/ProfileDataSource.kt | 4 +++- .../data/datasourceimpl/ProfileDataSourceImpl.kt | 8 +++++--- .../data/dto/request/RequestLikesDto.kt | 10 ++++++++++ .../hackathon/alddeul_babsang/data/dto/request/gitkeep | 0 .../data/repositoryimpl/ProfileRepositoryImpl.kt | 9 +++++++-- .../alddeul_babsang/data/service/ApiKeyStorage.kt | 1 + .../alddeul_babsang/data/service/ProfileApiService.kt | 8 ++++++-- .../domain/repository/ProfileRepository.kt | 4 +++- .../presentation/profile/screen/LikeViewModel.kt | 2 +- 9 files changed, 36 insertions(+), 10 deletions(-) create mode 100644 app/src/main/java/com/hackathon/alddeul_babsang/data/dto/request/RequestLikesDto.kt delete mode 100644 app/src/main/java/com/hackathon/alddeul_babsang/data/dto/request/gitkeep diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/data/datasource/ProfileDataSource.kt b/app/src/main/java/com/hackathon/alddeul_babsang/data/datasource/ProfileDataSource.kt index 975b431..4fe5597 100644 --- a/app/src/main/java/com/hackathon/alddeul_babsang/data/datasource/ProfileDataSource.kt +++ b/app/src/main/java/com/hackathon/alddeul_babsang/data/datasource/ProfileDataSource.kt @@ -4,5 +4,7 @@ import com.hackathon.alddeul_babsang.data.dto.BaseResponse import com.hackathon.alddeul_babsang.data.dto.response.ResponseLikesDto interface ProfileDataSource { - suspend fun getLikes(): BaseResponse + suspend fun getLikes( + userId: Long + ): BaseResponse } \ No newline at end of file diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/data/datasourceimpl/ProfileDataSourceImpl.kt b/app/src/main/java/com/hackathon/alddeul_babsang/data/datasourceimpl/ProfileDataSourceImpl.kt index 50fc70f..0e3eca1 100644 --- a/app/src/main/java/com/hackathon/alddeul_babsang/data/datasourceimpl/ProfileDataSourceImpl.kt +++ b/app/src/main/java/com/hackathon/alddeul_babsang/data/datasourceimpl/ProfileDataSourceImpl.kt @@ -8,8 +8,10 @@ import javax.inject.Inject class ProfileDataSourceImpl @Inject constructor( private val profileApiService: ProfileApiService -): ProfileDataSource { - override suspend fun getLikes(): BaseResponse { - return profileApiService.getLikes() +) : ProfileDataSource { + override suspend fun getLikes( + userId: Long + ): BaseResponse { + return profileApiService.getLikes(userId) } } \ No newline at end of file diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/data/dto/request/RequestLikesDto.kt b/app/src/main/java/com/hackathon/alddeul_babsang/data/dto/request/RequestLikesDto.kt new file mode 100644 index 0000000..a33787a --- /dev/null +++ b/app/src/main/java/com/hackathon/alddeul_babsang/data/dto/request/RequestLikesDto.kt @@ -0,0 +1,10 @@ +package com.hackathon.alddeul_babsang.data.dto.request + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class RequestLikesDto ( + @SerialName("userId") val userId: Long, + @SerialName("storeId") val storeId: Long +) \ No newline at end of file diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/data/dto/request/gitkeep b/app/src/main/java/com/hackathon/alddeul_babsang/data/dto/request/gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/data/repositoryimpl/ProfileRepositoryImpl.kt b/app/src/main/java/com/hackathon/alddeul_babsang/data/repositoryimpl/ProfileRepositoryImpl.kt index e6b788b..f7c40dc 100644 --- a/app/src/main/java/com/hackathon/alddeul_babsang/data/repositoryimpl/ProfileRepositoryImpl.kt +++ b/app/src/main/java/com/hackathon/alddeul_babsang/data/repositoryimpl/ProfileRepositoryImpl.kt @@ -1,6 +1,7 @@ package com.hackathon.alddeul_babsang.data.repositoryimpl import com.hackathon.alddeul_babsang.data.datasource.ProfileDataSource +import com.hackathon.alddeul_babsang.data.dto.request.RequestLikesDto import com.hackathon.alddeul_babsang.data.dto.response.FavoriteRestaurantDto import com.hackathon.alddeul_babsang.domain.repository.ProfileRepository import javax.inject.Inject @@ -8,9 +9,13 @@ import javax.inject.Inject class ProfileRepositoryImpl @Inject constructor( private val profileDataSource: ProfileDataSource ) : ProfileRepository { - override suspend fun getLikes(): Result> { + override suspend fun getLikes( + userId: Long, + ): Result> { return runCatching { - profileDataSource.getLikes().result?.favoriteRestaurants ?: emptyList() + profileDataSource.getLikes( + userId = userId + ).result?.favoriteRestaurants ?: emptyList() } } } \ No newline at end of file diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/data/service/ApiKeyStorage.kt b/app/src/main/java/com/hackathon/alddeul_babsang/data/service/ApiKeyStorage.kt index 1e31f95..cf391b3 100644 --- a/app/src/main/java/com/hackathon/alddeul_babsang/data/service/ApiKeyStorage.kt +++ b/app/src/main/java/com/hackathon/alddeul_babsang/data/service/ApiKeyStorage.kt @@ -18,4 +18,5 @@ object ApiKeyStorage { const val DELETE_ACCOUNT = "delete-account" const val MYPAGE = "mypage" const val LOGOUT = "logout" + const val USER_ID = "userId" } \ No newline at end of file diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/data/service/ProfileApiService.kt b/app/src/main/java/com/hackathon/alddeul_babsang/data/service/ProfileApiService.kt index 1b4f3d1..58502fa 100644 --- a/app/src/main/java/com/hackathon/alddeul_babsang/data/service/ProfileApiService.kt +++ b/app/src/main/java/com/hackathon/alddeul_babsang/data/service/ProfileApiService.kt @@ -3,9 +3,13 @@ package com.hackathon.alddeul_babsang.data.service import com.hackathon.alddeul_babsang.data.dto.BaseResponse import com.hackathon.alddeul_babsang.data.dto.response.ResponseLikesDto import com.sopt.data.service.ApiKeyStorage.STORES +import com.sopt.data.service.ApiKeyStorage.USER_ID import retrofit2.http.GET +import retrofit2.http.Path interface ProfileApiService { - @GET("/$STORES") - suspend fun getLikes(): BaseResponse + @GET("/$STORES/{$USER_ID}}") + suspend fun getLikes( + @Path("userId") userId: Long, + ): BaseResponse } \ No newline at end of file diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/domain/repository/ProfileRepository.kt b/app/src/main/java/com/hackathon/alddeul_babsang/domain/repository/ProfileRepository.kt index 5197015..c8b2061 100644 --- a/app/src/main/java/com/hackathon/alddeul_babsang/domain/repository/ProfileRepository.kt +++ b/app/src/main/java/com/hackathon/alddeul_babsang/domain/repository/ProfileRepository.kt @@ -3,5 +3,7 @@ package com.hackathon.alddeul_babsang.domain.repository import com.hackathon.alddeul_babsang.data.dto.response.FavoriteRestaurantDto interface ProfileRepository { - suspend fun getLikes(): Result> + suspend fun getLikes( + userId: Long, + ): Result> } \ No newline at end of file diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/presentation/profile/screen/LikeViewModel.kt b/app/src/main/java/com/hackathon/alddeul_babsang/presentation/profile/screen/LikeViewModel.kt index 2dedf18..528cd0c 100644 --- a/app/src/main/java/com/hackathon/alddeul_babsang/presentation/profile/screen/LikeViewModel.kt +++ b/app/src/main/java/com/hackathon/alddeul_babsang/presentation/profile/screen/LikeViewModel.kt @@ -23,7 +23,7 @@ class LikeViewModel @Inject constructor( fun getLikes() = viewModelScope.launch { _getLikesState.emit(UiState.Loading) - profileRepository.getLikes().fold( + profileRepository.getLikes(userId = 1).fold( onSuccess = { _getLikesState.emit(UiState.Success(it)) }, From 896069546a016a30c788de9aaa966a4577142fa0 Mon Sep 17 00:00:00 2001 From: gaeulzzang Date: Fri, 8 Nov 2024 18:05:44 +0900 Subject: [PATCH 3/4] =?UTF-8?q?#28=20[FEAT]=20=EC=A2=8B=EC=95=84=EC=9A=94?= =?UTF-8?q?=20api=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/datasource/ProfileDataSource.kt | 5 +++++ .../data/datasourceimpl/ProfileDataSourceImpl.kt | 5 +++++ .../data/repositoryimpl/ProfileRepositoryImpl.kt | 11 +++++++++++ .../alddeul_babsang/data/service/ProfileApiService.kt | 11 +++++++++-- .../domain/repository/ProfileRepository.kt | 5 +++++ 5 files changed, 35 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/data/datasource/ProfileDataSource.kt b/app/src/main/java/com/hackathon/alddeul_babsang/data/datasource/ProfileDataSource.kt index 4fe5597..87e2dbb 100644 --- a/app/src/main/java/com/hackathon/alddeul_babsang/data/datasource/ProfileDataSource.kt +++ b/app/src/main/java/com/hackathon/alddeul_babsang/data/datasource/ProfileDataSource.kt @@ -1,10 +1,15 @@ package com.hackathon.alddeul_babsang.data.datasource import com.hackathon.alddeul_babsang.data.dto.BaseResponse +import com.hackathon.alddeul_babsang.data.dto.request.RequestLikesDto import com.hackathon.alddeul_babsang.data.dto.response.ResponseLikesDto interface ProfileDataSource { suspend fun getLikes( userId: Long ): BaseResponse + + suspend fun postLike( + requestLikesDto: RequestLikesDto + ): BaseResponse } \ No newline at end of file diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/data/datasourceimpl/ProfileDataSourceImpl.kt b/app/src/main/java/com/hackathon/alddeul_babsang/data/datasourceimpl/ProfileDataSourceImpl.kt index 0e3eca1..4295b8e 100644 --- a/app/src/main/java/com/hackathon/alddeul_babsang/data/datasourceimpl/ProfileDataSourceImpl.kt +++ b/app/src/main/java/com/hackathon/alddeul_babsang/data/datasourceimpl/ProfileDataSourceImpl.kt @@ -2,6 +2,7 @@ package com.hackathon.alddeul_babsang.data.datasourceimpl import com.hackathon.alddeul_babsang.data.datasource.ProfileDataSource import com.hackathon.alddeul_babsang.data.dto.BaseResponse +import com.hackathon.alddeul_babsang.data.dto.request.RequestLikesDto import com.hackathon.alddeul_babsang.data.dto.response.ResponseLikesDto import com.hackathon.alddeul_babsang.data.service.ProfileApiService import javax.inject.Inject @@ -14,4 +15,8 @@ class ProfileDataSourceImpl @Inject constructor( ): BaseResponse { return profileApiService.getLikes(userId) } + + override suspend fun postLike(requestLikesDto: RequestLikesDto): BaseResponse { + return profileApiService.postLike(requestLikesDto) + } } \ No newline at end of file diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/data/repositoryimpl/ProfileRepositoryImpl.kt b/app/src/main/java/com/hackathon/alddeul_babsang/data/repositoryimpl/ProfileRepositoryImpl.kt index f7c40dc..ca0145c 100644 --- a/app/src/main/java/com/hackathon/alddeul_babsang/data/repositoryimpl/ProfileRepositoryImpl.kt +++ b/app/src/main/java/com/hackathon/alddeul_babsang/data/repositoryimpl/ProfileRepositoryImpl.kt @@ -18,4 +18,15 @@ class ProfileRepositoryImpl @Inject constructor( ).result?.favoriteRestaurants ?: emptyList() } } + + override suspend fun postLike(userId: Long, storeId: Long): Result { + return runCatching { + profileDataSource.postLike( + requestLikesDto = RequestLikesDto( + userId = userId, + storeId = storeId + ) + ).result.toString() + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/data/service/ProfileApiService.kt b/app/src/main/java/com/hackathon/alddeul_babsang/data/service/ProfileApiService.kt index 58502fa..9a3b966 100644 --- a/app/src/main/java/com/hackathon/alddeul_babsang/data/service/ProfileApiService.kt +++ b/app/src/main/java/com/hackathon/alddeul_babsang/data/service/ProfileApiService.kt @@ -1,15 +1,22 @@ package com.hackathon.alddeul_babsang.data.service import com.hackathon.alddeul_babsang.data.dto.BaseResponse +import com.hackathon.alddeul_babsang.data.dto.request.RequestLikesDto import com.hackathon.alddeul_babsang.data.dto.response.ResponseLikesDto -import com.sopt.data.service.ApiKeyStorage.STORES +import com.sopt.data.service.ApiKeyStorage.FAVORITES import com.sopt.data.service.ApiKeyStorage.USER_ID import retrofit2.http.GET +import retrofit2.http.POST import retrofit2.http.Path interface ProfileApiService { - @GET("/$STORES/{$USER_ID}}") + @GET("/$FAVORITES/{$USER_ID}}") suspend fun getLikes( @Path("userId") userId: Long, ): BaseResponse + + @POST("/$FAVORITES") + suspend fun postLike( + requestLikesDto: RequestLikesDto + ): BaseResponse } \ No newline at end of file diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/domain/repository/ProfileRepository.kt b/app/src/main/java/com/hackathon/alddeul_babsang/domain/repository/ProfileRepository.kt index c8b2061..51f5222 100644 --- a/app/src/main/java/com/hackathon/alddeul_babsang/domain/repository/ProfileRepository.kt +++ b/app/src/main/java/com/hackathon/alddeul_babsang/domain/repository/ProfileRepository.kt @@ -6,4 +6,9 @@ interface ProfileRepository { suspend fun getLikes( userId: Long, ): Result> + + suspend fun postLike( + userId: Long, + storeId: Long, + ): Result } \ No newline at end of file From b36351b8c698010a42322bebe31463eecb384f9f Mon Sep 17 00:00:00 2001 From: gaeulzzang Date: Fri, 8 Nov 2024 19:13:48 +0900 Subject: [PATCH 4/4] =?UTF-8?q?#28=20[FEAT]=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=20api=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/di/DataSourceModule.kt | 6 ++ .../app/di/RepositoryModule.kt | 6 ++ .../alddeul_babsang/app/di/ServiceModule.kt | 7 ++ .../data/datasource/DetailDataSource.kt | 14 ++++ .../datasourceimpl/DetailDataSourceImpl.kt | 22 +++++++ .../data/dto/response/ResponseLikesDto.kt | 2 +- .../data/dto/response/ResponseReviewDto.kt | 11 ++++ .../repositoryimpl/DetailRepositoryImpl.kt | 43 ++++++++++++ .../data/service/ApiKeyStorage.kt | 1 + .../data/service/DetailApiService.kt | 23 +++++++ .../data/service/ProfileApiService.kt | 3 +- .../domain/repository/DetailRepository.kt | 13 ++++ .../babsang/screen/BabsangItem.kt | 18 ----- .../detail/screen/ReviewScreen.kt | 65 +++++++++++++++---- .../detail/screen/ReviewViewModel.kt | 38 +++++++++++ 15 files changed, 239 insertions(+), 33 deletions(-) create mode 100644 app/src/main/java/com/hackathon/alddeul_babsang/data/datasource/DetailDataSource.kt create mode 100644 app/src/main/java/com/hackathon/alddeul_babsang/data/datasourceimpl/DetailDataSourceImpl.kt create mode 100644 app/src/main/java/com/hackathon/alddeul_babsang/data/dto/response/ResponseReviewDto.kt create mode 100644 app/src/main/java/com/hackathon/alddeul_babsang/data/repositoryimpl/DetailRepositoryImpl.kt create mode 100644 app/src/main/java/com/hackathon/alddeul_babsang/data/service/DetailApiService.kt create mode 100644 app/src/main/java/com/hackathon/alddeul_babsang/domain/repository/DetailRepository.kt create mode 100644 app/src/main/java/com/hackathon/alddeul_babsang/presentation/detail/screen/ReviewViewModel.kt diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/app/di/DataSourceModule.kt b/app/src/main/java/com/hackathon/alddeul_babsang/app/di/DataSourceModule.kt index e91e6f3..99fa87a 100644 --- a/app/src/main/java/com/hackathon/alddeul_babsang/app/di/DataSourceModule.kt +++ b/app/src/main/java/com/hackathon/alddeul_babsang/app/di/DataSourceModule.kt @@ -1,10 +1,12 @@ package com.hackathon.alddeul_babsang.app.di import com.hackathon.alddeul_babsang.data.datasource.BabsangDataSource +import com.hackathon.alddeul_babsang.data.datasource.DetailDataSource import com.hackathon.alddeul_babsang.data.datasource.ExampleDataSource import com.hackathon.alddeul_babsang.data.datasource.ProfileDataSource import com.hackathon.alddeul_babsang.data.datasource.UserPreferencesDataSource import com.hackathon.alddeul_babsang.data.datasourceimpl.BabsangDataSourceImpl +import com.hackathon.alddeul_babsang.data.datasourceimpl.DetailDataSourceImpl import com.hackathon.alddeul_babsang.data.datasourceimpl.ExampleDataSourceImpl import com.hackathon.alddeul_babsang.data.datasourceimpl.ProfileDataSourceImpl import com.hackathon.alddeul_babsang.data.datasourceimpl.UserPreferencesDataSourceImpl @@ -33,4 +35,8 @@ abstract class DataSourceModule { @Binds @Singleton abstract fun bindProfileDataSource(profileDataSourceImpl: ProfileDataSourceImpl): ProfileDataSource + + @Binds + @Singleton + abstract fun bindDetailDataSource(detailDataSourceImpl: DetailDataSourceImpl): DetailDataSource } \ No newline at end of file diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/app/di/RepositoryModule.kt b/app/src/main/java/com/hackathon/alddeul_babsang/app/di/RepositoryModule.kt index 18ad144..8fd7c27 100644 --- a/app/src/main/java/com/hackathon/alddeul_babsang/app/di/RepositoryModule.kt +++ b/app/src/main/java/com/hackathon/alddeul_babsang/app/di/RepositoryModule.kt @@ -1,10 +1,12 @@ package com.hackathon.alddeul_babsang.app.di import com.hackathon.alddeul_babsang.data.repositoryimpl.BabsangRepositoryImpl +import com.hackathon.alddeul_babsang.data.repositoryimpl.DetailRepositoryImpl import com.hackathon.alddeul_babsang.data.repositoryimpl.ExampleRepositoryImpl import com.hackathon.alddeul_babsang.data.repositoryimpl.ProfileRepositoryImpl import com.hackathon.alddeul_babsang.data.repositoryimpl.UserPreferencesRepositoryImpl import com.hackathon.alddeul_babsang.domain.repository.BabsangRepository +import com.hackathon.alddeul_babsang.domain.repository.DetailRepository import com.hackathon.alddeul_babsang.domain.repository.ExampleRepository import com.hackathon.alddeul_babsang.domain.repository.ProfileRepository import com.hackathon.alddeul_babsang.domain.repository.UserPreferencesRepository @@ -33,4 +35,8 @@ abstract class RepositoryModule { @Binds @Singleton abstract fun bindProfileRepository(profileRepositoryImpl: ProfileRepositoryImpl): ProfileRepository + + @Binds + @Singleton + abstract fun bindDetailRepository(detailRepositoryImpl: DetailRepositoryImpl): DetailRepository } \ No newline at end of file diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/app/di/ServiceModule.kt b/app/src/main/java/com/hackathon/alddeul_babsang/app/di/ServiceModule.kt index 4509be1..e10276a 100644 --- a/app/src/main/java/com/hackathon/alddeul_babsang/app/di/ServiceModule.kt +++ b/app/src/main/java/com/hackathon/alddeul_babsang/app/di/ServiceModule.kt @@ -1,6 +1,7 @@ package com.hackathon.alddeul_babsang.app.di import com.hackathon.alddeul_babsang.data.service.BabsangApiService +import com.hackathon.alddeul_babsang.data.service.DetailApiService import com.hackathon.alddeul_babsang.data.service.ProfileApiService import com.sopt.data.service.ExampleApiService import dagger.Module @@ -31,4 +32,10 @@ object ServiceModule { fun provideProfileService( @AlddeulRetrofit retrofit: Retrofit ): ProfileApiService = retrofit.create(ProfileApiService::class.java) + + @Provides + @Singleton + fun provideDetailService( + @AlddeulRetrofit retrofit: Retrofit + ): DetailApiService = retrofit.create(DetailApiService::class.java) } diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/data/datasource/DetailDataSource.kt b/app/src/main/java/com/hackathon/alddeul_babsang/data/datasource/DetailDataSource.kt new file mode 100644 index 0000000..38646b0 --- /dev/null +++ b/app/src/main/java/com/hackathon/alddeul_babsang/data/datasource/DetailDataSource.kt @@ -0,0 +1,14 @@ +package com.hackathon.alddeul_babsang.data.datasource + +import com.hackathon.alddeul_babsang.data.dto.BaseResponse +import com.hackathon.alddeul_babsang.data.dto.response.ResponseReviewDto +import okhttp3.MultipartBody +import okhttp3.RequestBody + +interface DetailDataSource { + suspend fun postReview( + storeId: Long, + data: Map, // JSON 데이터를 포함하는 Map + reviewImage: MultipartBody.Part? = null // 이미지 파일 + ): BaseResponse +} \ No newline at end of file diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/data/datasourceimpl/DetailDataSourceImpl.kt b/app/src/main/java/com/hackathon/alddeul_babsang/data/datasourceimpl/DetailDataSourceImpl.kt new file mode 100644 index 0000000..74769ba --- /dev/null +++ b/app/src/main/java/com/hackathon/alddeul_babsang/data/datasourceimpl/DetailDataSourceImpl.kt @@ -0,0 +1,22 @@ +package com.hackathon.alddeul_babsang.data.datasourceimpl + +import com.hackathon.alddeul_babsang.data.datasource.DetailDataSource +import com.hackathon.alddeul_babsang.data.dto.BaseResponse +import com.hackathon.alddeul_babsang.data.dto.response.ResponseReviewDto +import com.hackathon.alddeul_babsang.data.service.DetailApiService +import okhttp3.MultipartBody +import okhttp3.RequestBody +import javax.inject.Inject + +class DetailDataSourceImpl @Inject constructor( + private val detailApiService: DetailApiService +) : DetailDataSource { + override suspend fun postReview( + storeId: Long, + data: Map, + reviewImage: MultipartBody.Part? + ): BaseResponse { + return detailApiService.postReview(storeId, data, reviewImage) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/data/dto/response/ResponseLikesDto.kt b/app/src/main/java/com/hackathon/alddeul_babsang/data/dto/response/ResponseLikesDto.kt index 43e0de2..a173239 100644 --- a/app/src/main/java/com/hackathon/alddeul_babsang/data/dto/response/ResponseLikesDto.kt +++ b/app/src/main/java/com/hackathon/alddeul_babsang/data/dto/response/ResponseLikesDto.kt @@ -5,7 +5,7 @@ import kotlinx.serialization.Serializable @Serializable data class ResponseLikesDto( - @SerialName("favoriteRestaurants") val favoriteRestaurants: List + @SerialName("favoriteStoreDetailDtos") val favoriteRestaurants: List ) @Serializable diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/data/dto/response/ResponseReviewDto.kt b/app/src/main/java/com/hackathon/alddeul_babsang/data/dto/response/ResponseReviewDto.kt new file mode 100644 index 0000000..22b73c1 --- /dev/null +++ b/app/src/main/java/com/hackathon/alddeul_babsang/data/dto/response/ResponseReviewDto.kt @@ -0,0 +1,11 @@ +package com.hackathon.alddeul_babsang.data.dto.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class ResponseReviewDto ( + @SerialName("storeId") val storeId: Long, + @SerialName("userId") val userId: Long, + @SerialName("message") val message: String, +) \ No newline at end of file diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/data/repositoryimpl/DetailRepositoryImpl.kt b/app/src/main/java/com/hackathon/alddeul_babsang/data/repositoryimpl/DetailRepositoryImpl.kt new file mode 100644 index 0000000..2ac1915 --- /dev/null +++ b/app/src/main/java/com/hackathon/alddeul_babsang/data/repositoryimpl/DetailRepositoryImpl.kt @@ -0,0 +1,43 @@ +package com.hackathon.alddeul_babsang.data.repositoryimpl + +import com.hackathon.alddeul_babsang.data.datasource.DetailDataSource +import com.hackathon.alddeul_babsang.domain.repository.DetailRepository +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.MultipartBody +import okhttp3.RequestBody.Companion.asRequestBody +import okhttp3.RequestBody.Companion.toRequestBody +import org.json.JSONObject +import java.io.File +import javax.inject.Inject + +class DetailRepositoryImpl @Inject constructor( + private val detailDataSource: DetailDataSource +) : DetailRepository { + override suspend fun postReview( + storeId: Long, + userId: Long, + rating: Double, + content: String, + reviewImage: File + ): Result { + return runCatching { + val dataMap = mapOf( + "userId" to userId.toString().toRequestBody("text/plain".toMediaTypeOrNull()), + "rating" to rating.toString().toRequestBody("text/plain".toMediaTypeOrNull()), + "content" to content.toRequestBody("text/plain".toMediaTypeOrNull()) + ) + + // 이미지 파일 파트 생성 + val filePart = reviewImage?.let { + val requestBody = it.asRequestBody("image/jpeg".toMediaTypeOrNull()) + MultipartBody.Part.createFormData("reviewImage", it.name, requestBody) + } + + detailDataSource.postReview( + storeId = storeId, + data = dataMap, + reviewImage = filePart + ).message + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/data/service/ApiKeyStorage.kt b/app/src/main/java/com/hackathon/alddeul_babsang/data/service/ApiKeyStorage.kt index cf391b3..20eec7a 100644 --- a/app/src/main/java/com/hackathon/alddeul_babsang/data/service/ApiKeyStorage.kt +++ b/app/src/main/java/com/hackathon/alddeul_babsang/data/service/ApiKeyStorage.kt @@ -19,4 +19,5 @@ object ApiKeyStorage { const val MYPAGE = "mypage" const val LOGOUT = "logout" const val USER_ID = "userId" + const val STORE_ID = "storeId" } \ No newline at end of file diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/data/service/DetailApiService.kt b/app/src/main/java/com/hackathon/alddeul_babsang/data/service/DetailApiService.kt new file mode 100644 index 0000000..395383d --- /dev/null +++ b/app/src/main/java/com/hackathon/alddeul_babsang/data/service/DetailApiService.kt @@ -0,0 +1,23 @@ +package com.hackathon.alddeul_babsang.data.service + +import com.hackathon.alddeul_babsang.data.dto.BaseResponse +import com.hackathon.alddeul_babsang.data.dto.response.ResponseReviewDto +import com.sopt.data.service.ApiKeyStorage.REVIEW +import com.sopt.data.service.ApiKeyStorage.STORE_ID +import okhttp3.MultipartBody +import okhttp3.RequestBody +import retrofit2.http.Multipart +import retrofit2.http.POST +import retrofit2.http.Part +import retrofit2.http.PartMap +import retrofit2.http.Path + +interface DetailApiService { + @Multipart + @POST("/$REVIEW/{$STORE_ID}") + suspend fun postReview( + @Path("storeId") storeId: Long, + @PartMap data: Map, + @Part reviewImage: MultipartBody.Part? = null + ): BaseResponse +} \ No newline at end of file diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/data/service/ProfileApiService.kt b/app/src/main/java/com/hackathon/alddeul_babsang/data/service/ProfileApiService.kt index 9a3b966..886aeb3 100644 --- a/app/src/main/java/com/hackathon/alddeul_babsang/data/service/ProfileApiService.kt +++ b/app/src/main/java/com/hackathon/alddeul_babsang/data/service/ProfileApiService.kt @@ -5,6 +5,7 @@ import com.hackathon.alddeul_babsang.data.dto.request.RequestLikesDto import com.hackathon.alddeul_babsang.data.dto.response.ResponseLikesDto import com.sopt.data.service.ApiKeyStorage.FAVORITES import com.sopt.data.service.ApiKeyStorage.USER_ID +import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.POST import retrofit2.http.Path @@ -17,6 +18,6 @@ interface ProfileApiService { @POST("/$FAVORITES") suspend fun postLike( - requestLikesDto: RequestLikesDto + @Body requestLikesDto: RequestLikesDto ): BaseResponse } \ No newline at end of file diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/domain/repository/DetailRepository.kt b/app/src/main/java/com/hackathon/alddeul_babsang/domain/repository/DetailRepository.kt new file mode 100644 index 0000000..ffd6040 --- /dev/null +++ b/app/src/main/java/com/hackathon/alddeul_babsang/domain/repository/DetailRepository.kt @@ -0,0 +1,13 @@ +package com.hackathon.alddeul_babsang.domain.repository + +import java.io.File + +interface DetailRepository { + suspend fun postReview( + storeId: Long, + userId: Long, + rating: Double, + content: String, + reviewImage: File + ): Result +} \ No newline at end of file diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/presentation/babsang/screen/BabsangItem.kt b/app/src/main/java/com/hackathon/alddeul_babsang/presentation/babsang/screen/BabsangItem.kt index fe9e5ff..bac5a52 100644 --- a/app/src/main/java/com/hackathon/alddeul_babsang/presentation/babsang/screen/BabsangItem.kt +++ b/app/src/main/java/com/hackathon/alddeul_babsang/presentation/babsang/screen/BabsangItem.kt @@ -159,22 +159,4 @@ fun ReplaceImage2(codeName: String, imageUrl: String?) { ) } } -} - -@Preview(showBackground = true) -@Composable -fun BabsangListItemPreview() { - AlddeulBabsangTheme { - LikeItem( - data = LikesEntity( - id = 1, - avatar = null, - name = "송이네 밥상", - codeName = "경양식/일식", - address = "서울특별시 용산구 청파동 11", - phone = "02-210-0220", - favorite = true - ) - ) - } } \ No newline at end of file diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/presentation/detail/screen/ReviewScreen.kt b/app/src/main/java/com/hackathon/alddeul_babsang/presentation/detail/screen/ReviewScreen.kt index fb8b0bb..6446db3 100644 --- a/app/src/main/java/com/hackathon/alddeul_babsang/presentation/detail/screen/ReviewScreen.kt +++ b/app/src/main/java/com/hackathon/alddeul_babsang/presentation/detail/screen/ReviewScreen.kt @@ -25,6 +25,7 @@ import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.SideEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue @@ -39,6 +40,8 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import coil.compose.rememberAsyncImagePainter import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.hackathon.alddeul_babsang.R @@ -55,15 +58,36 @@ import com.hackathon.alddeul_babsang.core_ui.theme.Yellow import com.hackathon.alddeul_babsang.core_ui.theme.body4Regular import com.hackathon.alddeul_babsang.core_ui.theme.head4Bold import com.hackathon.alddeul_babsang.presentation.detail.navigation.DetailNavigator +import com.hackathon.alddeul_babsang.util.UiState import com.hackathon.alddeul_babsang.util.toast +import com.hackathon.alddeul_babsang.util.uriToFile +import timber.log.Timber @Composable fun ReviewRoute( navigator: DetailNavigator, id: Long, ) { + val reviewViewModel: ReviewViewModel = hiltViewModel() val keyboardController = LocalSoftwareKeyboardController.current val systemUiController = rememberSystemUiController() + val postReviewState by reviewViewModel.postReviewState.collectAsStateWithLifecycle(UiState.Empty) + + when (postReviewState) { + is UiState.Success -> { + navigator.navigateBack() + keyboardController?.hide() + Timber.d("Review post success") + } + + is UiState.Failure -> { + val message = (postReviewState as UiState.Failure).msg + Timber.e("Review post failed: $message") + LocalContext.current.toast(message) + } + + else -> {} + } SideEffect { systemUiController.setStatusBarColor( @@ -72,19 +96,18 @@ fun ReviewRoute( } ReviewScreen( + id = id, onBackClick = { navigator.navigateBack() }, - onCompleteClick = { - keyboardController?.hide() - navigator.navigateBack() - } + reviewViewModel = reviewViewModel ) } @OptIn(ExperimentalMaterial3Api::class) @Composable fun ReviewScreen( + id: Long = 0, onBackClick: () -> Unit = {}, - onCompleteClick: () -> Unit = {} + reviewViewModel: ReviewViewModel ) { var imageUri by remember { mutableStateOf(null) } val galleryLauncher = rememberLauncherForActivityResult( @@ -95,6 +118,7 @@ fun ReviewScreen( ) var reviewLength by remember { mutableStateOf("") } val context = LocalContext.current + var rating by remember { mutableIntStateOf(0) } Scaffold( topBar = { @@ -158,7 +182,10 @@ fun ReviewScreen( } Spacer(modifier = Modifier.height(30.dp)) AlddeulHeader(text = R.string.tv_review_rating) - StarRating() + StarRating( + rating = rating, + onRatingChange = { rating = it } + ) AlddeulHeader(text = R.string.tv_review_detail) ReviewTextField( value = reviewLength, @@ -177,8 +204,16 @@ fun ReviewScreen( AlddeulButton( text = R.string.btn_review_complete, onClick = { - if (reviewLength.length <= 100) onCompleteClick() - else context.toast(context.getString(R.string.toast_review_length)) + if (reviewLength.length <= 100 && imageUri != null) { + val file = uriToFile(imageUri!!, context) + reviewViewModel.postReview( + storeId = id, + userId = 1, + rating = rating.toDouble(), + content = reviewLength, + reviewImage = file + ) + } else context.toast(context.getString(R.string.toast_review_length)) } ) } @@ -186,21 +221,23 @@ fun ReviewScreen( } @Composable -fun StarRating() { +fun StarRating( + rating: Int, // 현재 선택된 별 개수 + onRatingChange: (Int) -> Unit // 별 개수 변경 시 호출되는 함수 +) { val starCount = 5 - var selectedStars by remember { mutableStateOf(List(starCount) { false }) } LazyRow { items(starCount) { index -> IconButton( onClick = { - selectedStars = List(selectedStars.size) { i -> i <= index } + onRatingChange(index + 1) // 클릭된 별까지의 개수를 전달 } ) { Icon( imageVector = ImageVector.vectorResource(id = R.drawable.ic_review_star), contentDescription = null, - tint = if (selectedStars[index]) Yellow else Gray100 + tint = if (index < rating) Yellow else Gray100 ) } } @@ -211,6 +248,8 @@ fun StarRating() { @Composable fun ReviewScreenPreview() { AlddeulBabsangTheme { - ReviewScreen() + ReviewScreen( + reviewViewModel = hiltViewModel() + ) } } \ No newline at end of file diff --git a/app/src/main/java/com/hackathon/alddeul_babsang/presentation/detail/screen/ReviewViewModel.kt b/app/src/main/java/com/hackathon/alddeul_babsang/presentation/detail/screen/ReviewViewModel.kt new file mode 100644 index 0000000..309897c --- /dev/null +++ b/app/src/main/java/com/hackathon/alddeul_babsang/presentation/detail/screen/ReviewViewModel.kt @@ -0,0 +1,38 @@ +package com.hackathon.alddeul_babsang.presentation.detail.screen + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.hackathon.alddeul_babsang.domain.repository.DetailRepository +import com.hackathon.alddeul_babsang.util.UiState +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch +import java.io.File +import javax.inject.Inject + +@HiltViewModel +class ReviewViewModel @Inject constructor( + private val detailRepository: DetailRepository +) : ViewModel() { + private val _postReviewState = MutableStateFlow>(UiState.Empty) + val postReviewState: StateFlow> = _postReviewState + + fun postReview( + storeId: Long, + userId: Long, + rating: Double, + content: String, + reviewImage: File + ) = viewModelScope.launch { + _postReviewState.emit(UiState.Loading) + detailRepository.postReview(storeId, userId, rating, content, reviewImage).fold( + onSuccess = { + _postReviewState.emit(UiState.Success(it)) + }, + onFailure = { + _postReviewState.emit(UiState.Failure(it.message.toString())) + } + ) + } +} \ No newline at end of file