From 8710dd3691166cacab8c59c5ef8c08834196667b Mon Sep 17 00:00:00 2001 From: jakehsj Date: Thu, 9 Nov 2023 18:43:05 +0900 Subject: [PATCH 1/9] fix: make photo fit the grid / only write reviews to restaurant, bar, cafe --- .../fooriend/core/graph/HomeNavGraph.kt | 5 ++-- .../fooriend/ui/component/ProfileSection.kt | 17 ++++++++++---- .../fooriend/ui/component/ReviewLazyGrid.kt | 3 +++ .../fooriend/ui/screen/FooriendScreen.kt | 17 ++++++++++++-- .../fooriend/ui/screen/PostingScreen.kt | 2 ++ .../ui/screen/RestaurantDetailScreen.kt | 8 ++++--- .../fooriend/ui/screen/home/HomeScreen.kt | 2 +- .../fooriend/ui/screen/mypage/MyPageScreen.kt | 18 +++++++++++++-- .../com/team13/fooriend/ui/util/ApiService.kt | 23 +++++++++++++++++-- 9 files changed, 79 insertions(+), 16 deletions(-) diff --git a/frontend/app/src/main/java/com/team13/fooriend/core/graph/HomeNavGraph.kt b/frontend/app/src/main/java/com/team13/fooriend/core/graph/HomeNavGraph.kt index 623ff08..247d409 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/core/graph/HomeNavGraph.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/core/graph/HomeNavGraph.kt @@ -56,7 +56,7 @@ fun HomeNavGraph( RestaurantDetailScreen( restaurantPlaceId = it.arguments?.getString("restaurantId")?:"", onBackClick = { navController.navigateUp() }, // 뒤로가기 버튼을 누른 경우 - onWriteReviewClick = { navController.navigate("writeReview") }, // 리뷰 작성 버튼을 누른 경우 + onWriteReviewClick = { navController.navigate("writeReview/${it}") }, // 리뷰 작성 버튼을 누른 경우 onWriterClick = { navController.navigate("fooriend/${it}") }, // 리뷰에 있는 작성자 프로필 이미지를 누른 경우 ) } @@ -76,8 +76,9 @@ fun HomeNavGraph( onReviewClick = { navController.navigate("reviewDetail/${it}") }, // 리뷰 이미지를 클릭한 경우 ) } - composable(route = "writeReview"){ + composable(route = "writeReview/{restaurantPlaceId}"){ PostingScreen( + restaurantPlaceId = it.arguments?.getString("restaurantPlaceId")?:"", onCloseClick = { navController.navigateUp() }, onPostClick = { navController.navigateUp() }, // TODO : Post버튼을 누르면 review가 저장되어야 함 ) diff --git a/frontend/app/src/main/java/com/team13/fooriend/ui/component/ProfileSection.kt b/frontend/app/src/main/java/com/team13/fooriend/ui/component/ProfileSection.kt index 2068301..e763542 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/ui/component/ProfileSection.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/ui/component/ProfileSection.kt @@ -32,15 +32,16 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import coil.compose.rememberImagePainter import com.team13.fooriend.R @Composable fun ProfileSection( // default value는 추후에 모두 지우자 - username : String = "ozeeeno", + username : String = "", followersCount : Int = 100, followingCount : Int = 50, - userProfileImageUrl : Int = R.drawable.profile_cat, + userProfileImageUrl : String, isFooried : Boolean = false, onFollowClick : () -> Unit = {}, isMyPage : Boolean = false, @@ -59,7 +60,15 @@ fun ProfileSection( .border(2.dp, MaterialTheme.colorScheme.primary, CircleShape) ) { Image( - painter = painterResource(id = userProfileImageUrl), + painter = rememberImagePainter( + data = userProfileImageUrl, + builder = { + crossfade(true) + // 이곳에 더 많은 Coil 설정을 추가할 수 있습니다. + // 예: placeholder(R.drawable.placeholder), error(R.drawable.error) + }, + + ), contentDescription = null, modifier = Modifier.fillMaxSize(), contentScale = ContentScale.Crop @@ -104,6 +113,6 @@ fun ProfileSection( @Composable @Preview(showSystemUi = true, showBackground = true) fun ProfileSectionPreview(){ - ProfileSection("조용찬", 10, 20, R.drawable.profile_cat, + ProfileSection("조용찬", 10, 20, "", isFooried = false, onFollowClick = {}, isMyPage = false) } \ No newline at end of file diff --git a/frontend/app/src/main/java/com/team13/fooriend/ui/component/ReviewLazyGrid.kt b/frontend/app/src/main/java/com/team13/fooriend/ui/component/ReviewLazyGrid.kt index abde217..92a590a 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/ui/component/ReviewLazyGrid.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/ui/component/ReviewLazyGrid.kt @@ -3,6 +3,7 @@ package com.team13.fooriend.ui.component import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding @@ -67,6 +68,8 @@ fun ReviewCard( }, ), contentDescription = "Loaded image", + modifier = Modifier + .fillMaxSize(), contentScale = ContentScale.Crop ) } diff --git a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/FooriendScreen.kt b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/FooriendScreen.kt index 858650a..6309576 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/FooriendScreen.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/FooriendScreen.kt @@ -41,12 +41,21 @@ fun FooriendScreen( var reviews by remember { mutableStateOf>(emptyList()) } var isLoading by remember { mutableStateOf(true) } + var username by remember { mutableStateOf("")} + var followerCount by remember { mutableStateOf(0) } + var followingCount by remember { mutableStateOf(0) } + var userProfileImageUrl by remember { mutableStateOf("") } LaunchedEffect(Unit) { try { // API 호출하여 데이터 가져오기 - val response = apiService.getUserDetail(userId = userId) + val response = apiService.getUserReviews(userId = userId) + val response2 = apiService.getUserDetail(userId = userId) reviews = response.reviewList + username = response2.name + followerCount = response2.followerCount + followingCount = response2.followingCount + userProfileImageUrl = response2.profileImage } catch (e: Exception) { Log.d("RestaurantDetailScreen", "error: $e") } @@ -64,7 +73,11 @@ fun FooriendScreen( if(isLoading) { Text(text = "Loading...") } else { - ProfileSection(username = reviews[0].user.name, onFollowClick = onFollowClick) + ProfileSection(username = username, + followersCount = followerCount, + followingCount = followingCount, + userProfileImageUrl = userProfileImageUrl, + onFollowClick = onFollowClick) } // review lazy grid ReviewLazyGrid(reviews = reviews, onReviewClick = onReviewClick) diff --git a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/PostingScreen.kt b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/PostingScreen.kt index b6e8a2d..a0a2eb7 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/PostingScreen.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/PostingScreen.kt @@ -56,9 +56,11 @@ import com.team13.fooriend.ui.component.ImageCard @OptIn(ExperimentalMaterial3Api::class) @Composable fun PostingScreen( + restaurantPlaceId: String = "", onCloseClick: () -> Unit = {}, onPostClick: () -> Unit = {}, ){ + val state = rememberScrollState() val (content, contentValue) = remember { mutableStateOf("") } var selectImages by remember { mutableStateOf(listOf()) } diff --git a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/RestaurantDetailScreen.kt b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/RestaurantDetailScreen.kt index 12f0f2f..9f056dd 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/RestaurantDetailScreen.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/RestaurantDetailScreen.kt @@ -56,7 +56,7 @@ import retrofit2.converter.gson.GsonConverterFactory fun RestaurantDetailScreen( restaurantPlaceId: String, onBackClick: () -> Unit, - onWriteReviewClick: () -> Unit, + onWriteReviewClick: (String) -> Unit, onWriterClick: (Int) -> Unit, ) { val scope = rememberCoroutineScope() @@ -98,6 +98,7 @@ fun RestaurantDetailScreen( TopRestaurantBar( onCloseClick = onBackClick, onWriteReviewClick = onWriteReviewClick, + restaurantPlaceId = restaurantPlaceId, restaurantName = restaurantName, restaurantGood = restaurantGood, restaurantBad = restaurantBad, @@ -187,7 +188,8 @@ fun LoadImageFromUrl(url: String) { @Composable fun TopRestaurantBar( onCloseClick: () -> Unit, - onWriteReviewClick: () -> Unit, + onWriteReviewClick: (String) -> Unit, + restaurantPlaceId: String, restaurantName: String, restaurantGood: Int, restaurantBad: Int, @@ -223,7 +225,7 @@ fun TopRestaurantBar( Text(text = "싫어요 $restaurantBad") } Spacer(modifier = Modifier.width(12.dp)) - Button(onClick = onWriteReviewClick) { + Button(onClick = { onWriteReviewClick(restaurantPlaceId) }) { Text(text = "리뷰 작성") } } diff --git a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/home/HomeScreen.kt b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/home/HomeScreen.kt index a3864e7..975c083 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/home/HomeScreen.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/home/HomeScreen.kt @@ -218,7 +218,7 @@ fun HomeScreen( val marker = map.addMarker( MarkerOptions().position(latLng).title(poi.name.lines()[0]) ) - if(response.result.types.contains("food") || response.result.types.contains("bar")){ + if(response.result.types.contains("restaurant") || response.result.types.contains("bar") || response.result.types.contains("cafe")){ map.setOnInfoWindowClickListener { clickedMarker -> if (clickedMarker.id == marker?.id) { onReviewClick("000${poi.name.lines()[0]}") // onReviewClick(poi.placeId) diff --git a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/mypage/MyPageScreen.kt b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/mypage/MyPageScreen.kt index a4e7451..0f889cf 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/mypage/MyPageScreen.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/mypage/MyPageScreen.kt @@ -64,15 +64,25 @@ fun MyPageScreen( var reviews by remember { mutableStateOf>(emptyList()) } var isLoading by remember { mutableStateOf(true) } + var username by remember { mutableStateOf("")} + var followerCount by remember { mutableStateOf(0) } + var followingCount by remember { mutableStateOf(0) } + var userProfileImageUrl by remember { mutableStateOf("") } LaunchedEffect(Unit) { try { // API 호출하여 데이터 가져오기 - val response = apiService.getUserDetail(userId = 1) + val response = apiService.getMyReviews() + val response2 = apiService.getMyInfo() reviews = response.reviewList + username = response2.name + followerCount = response2.followerCount + followingCount = response2.followingCount + userProfileImageUrl = response2.profileImage } catch (e: Exception) { Log.d("RestaurantDetailScreen", "error: $e") } + username = reviews[0].user.name isLoading = false } Column( @@ -92,7 +102,11 @@ fun MyPageScreen( } } // 프로필 섹션 - ProfileSection(isMyPage = true) + ProfileSection(username = username, + followersCount = followerCount, + followingCount = followingCount, + userProfileImageUrl = userProfileImageUrl, + isMyPage = true) Spacer(modifier = Modifier.height(16.dp)) diff --git a/frontend/app/src/main/java/com/team13/fooriend/ui/util/ApiService.kt b/frontend/app/src/main/java/com/team13/fooriend/ui/util/ApiService.kt index 46c4bd7..17788a6 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/ui/util/ApiService.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/ui/util/ApiService.kt @@ -13,10 +13,15 @@ interface ApiService { ): RestaurantDetailResponse @GET("/reviews/users/{userId}") - suspend fun getUserDetail( + suspend fun getUserReviews( @Path("userId") userId: Int ): UserDetailResponse + @GET("/user/{userId}") + suspend fun getUserDetail( + @Path("userId") userId: Int + ): User + @GET("/reviews/{reviewId}") suspend fun getReviewDetail( @Path("reviewId") reviewId: Int @@ -24,6 +29,12 @@ interface ApiService { @GET("/reviews/random") suspend fun getRandomReviews(): RandomReviews + + @GET("/reviews/my") + suspend fun getMyReviews(): MyReviews + + @GET("/user/me") + suspend fun getMyInfo(): User } data class RestaurantsResponse( @@ -61,7 +72,11 @@ data class Image( data class User( val id: Int, - val name: String + val profileImage: String, + val name: String, + val username: String, + val followerCount: Int, + val followingCount: Int, ) data class UserDetailResponse( @@ -70,4 +85,8 @@ data class UserDetailResponse( data class RandomReviews( val reviewList: List +) + +data class MyReviews( + val reviewList: List ) \ No newline at end of file From 7eb03173857327d1f103fbde23124549a1f6b285 Mon Sep 17 00:00:00 2001 From: jakehsj Date: Thu, 9 Nov 2023 22:30:26 +0900 Subject: [PATCH 2/9] feat: review upload available --- frontend/app/build.gradle.kts | 1 + frontend/app/src/main/AndroidManifest.xml | 1 + .../fooriend/core/graph/HomeNavGraph.kt | 1 + .../fooriend/ui/screen/PostingScreen.kt | 113 +++++++++++++++++- .../ui/screen/RestaurantDetailScreen.kt | 23 ++-- .../fooriend/ui/screen/home/HomeScreen.kt | 10 +- .../com/team13/fooriend/ui/util/ApiService.kt | 34 ++++++ .../team13/fooriend/ui/util/LoginSuccess.kt | 2 +- 8 files changed, 161 insertions(+), 24 deletions(-) diff --git a/frontend/app/build.gradle.kts b/frontend/app/build.gradle.kts index 3bbb0f2..72360ef 100644 --- a/frontend/app/build.gradle.kts +++ b/frontend/app/build.gradle.kts @@ -107,6 +107,7 @@ dependencies { // Retrofit implementation("com.squareup.retrofit2:retrofit:2.9.0") implementation("com.squareup.retrofit2:converter-gson:2.9.0") + implementation ("com.squareup.okhttp3:okhttp:4.9.0") // //Lifecycle implementation ("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1") diff --git a/frontend/app/src/main/AndroidManifest.xml b/frontend/app/src/main/AndroidManifest.xml index 31757b0..b3ff4f2 100644 --- a/frontend/app/src/main/AndroidManifest.xml +++ b/frontend/app/src/main/AndroidManifest.xml @@ -4,6 +4,7 @@ + Unit = {}, onPostClick: () -> Unit = {}, @@ -73,6 +92,22 @@ fun PostingScreen( rememberLauncherForActivityResult(contract = ActivityResultContracts.GetMultipleContents()){ selectReceipt = it } + val retrofit = Retrofit.Builder() + .baseUrl("http://ec2-54-180-101-207.ap-northeast-2.compute.amazonaws.com") + .addConverterFactory(GsonConverterFactory.create()) + .build() + + val apiService = retrofit.create(ApiService::class.java) + + val retrofit2 = Retrofit.Builder() + .baseUrl("https://maps.googleapis.com/") + .addConverterFactory(GsonConverterFactory.create()) + .build() + + val placesApi = retrofit2.create(PlacesApiService::class.java) + + val coroutineScope = rememberCoroutineScope() + Column( modifier = Modifier .padding(16.dp) @@ -90,8 +125,7 @@ fun PostingScreen( ) } } - // 음식점 이름 - Text(text = "용찬 반점") + Text(text = "리뷰 작성") // 리뷰 작성 textfield (최대 140자) TextField( modifier = Modifier @@ -142,7 +176,74 @@ fun PostingScreen( } // 리뷰 등록 Spacer(modifier = Modifier.height(20.dp)) - Button(onClick = { onPostClick() }, modifier = Modifier + Button(onClick = { + Log.d("PostingScreen", "restaurantPlaceId: $restaurantPlaceId") + if(content == ""){ + Toast.makeText(context, "리뷰 내용을 입력해주세요.", Toast.LENGTH_SHORT).show() + return@Button + } else if(selectImages.size == 0){ + Toast.makeText(context, "리뷰 사진을 등록해주세요.", Toast.LENGTH_SHORT).show() + return@Button + } + coroutineScope.launch { + Log.d("PostingScreen", "restaurantPlaceId: $restaurantPlaceId") + val response = placesApi.getPlaceDetails(placeId = restaurantPlaceId, apiKey = "AIzaSyDV4YwwZmJp1PHNO4DSp_BdgY4qCDQzKH0") + Log.d("PostingScreen", "restaurant: $response") + val restaurant = RestaurantInfo( + googleMapPlaceId = restaurantPlaceId, + name = response.result.name, + latitude = response.result.geometry.location["lat"]!!, + longitude = response.result.geometry.location["lng"]!! + ) + var imageIds = mutableListOf() + for(uri in selectImages){ + val inputStream = context.contentResolver.openInputStream(uri) + inputStream?.let { stream -> + val requestBody = stream.readBytes().toRequestBody(MultipartBody.FORM) + val multipartBody = MultipartBody.Part.createFormData("file", "filename.jpg", requestBody) + + // API 호출 + val response = apiService.uploadImage(multipartBody) + imageIds.add(response.id) + } +// val file = File(uri.path) +// Log.d("PostingScreen", "file: $file") +// val requestbody = file.asRequestBody("multipart/form-data".toMediaTypeOrNull()) +// val body = MultipartBody.Part.createFormData("file",file.name, requestbody) +// val imageresponse = apiService.uploadImage(body) + } + Log.d("PostingScreen", "imageIds: $imageIds") + val receiptImageIds = mutableListOf() + for(uri in selectReceipt){ + val inputStream = context.contentResolver.openInputStream(uri) + inputStream?.let { stream -> + val requestBody = stream.readBytes().toRequestBody(MultipartBody.FORM) + val multipartBody = MultipartBody.Part.createFormData("file", "filename.jpg", requestBody) + + // API 호출 + val response = apiService.uploadImage(multipartBody) + imageIds.add(response.id) + } + } + Log.d("PostingScreen", "receiptImageIds: $receiptImageIds") + var receiptImageId = 0 + if(receiptImageIds.size > 0) { + receiptImageId = receiptImageIds[0] + } + + apiService.postReview( + ReviewPostBody( + content = content, + imageIds = imageIds, + receiptImageId = receiptImageId, + restaurant = restaurant + ) + ) + Toast.makeText(context, "리뷰가 등록되었습니다.", Toast.LENGTH_SHORT).show() + onPostClick() + } + }, + modifier = Modifier .fillMaxWidth() .align(Alignment.CenterHorizontally)){ Text(text = "리뷰 등록") @@ -150,9 +251,13 @@ fun PostingScreen( } } +// place api 로 Restaurant 객체 가져와서 RestaurantInfo 객체 만들기 +suspend fun onPost(){ + +} @Composable @Preview(showSystemUi = true, showBackground = true) fun PostingScreenPreview(){ - PostingScreen() +// PostingScreen() } \ No newline at end of file diff --git a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/RestaurantDetailScreen.kt b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/RestaurantDetailScreen.kt index 9f056dd..4d13f4f 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/RestaurantDetailScreen.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/RestaurantDetailScreen.kt @@ -70,24 +70,19 @@ fun RestaurantDetailScreen( .build() val apiService = retrofit.create(ApiService::class.java) - var restaurantName by remember { mutableStateOf("") } + var resPlaceId = restaurantPlaceId.substring(0,27) + var restaurantName = restaurantPlaceId.substring(27) var restaurantGood by remember { mutableStateOf(0) } var restaurantBad by remember { mutableStateOf(0) } // LaunchedEffect를 사용하여 Composable이 처음 구성될 때 데이터 로드 LaunchedEffect(Unit) { - // if prefix 0~2 of restaurantPlaceId == "000" - if(restaurantPlaceId.substring(0, 3) == "000"){ - restaurantName = restaurantPlaceId.substring(3) - }else { - try { - // API 호출하여 데이터 가져오기 - val response = apiService.getRestaurantDetail(restaurantPlaceId = restaurantPlaceId) - reviews = response.reviewList - } catch (e: Exception) { - Log.d("RestaurantDetailScreen", "error: $e") - } - restaurantName = reviews[0].restaurant.name + try { + // API 호출하여 데이터 가져오기 + val response = apiService.getRestaurantDetail(restaurantPlaceId = resPlaceId) + reviews = response.reviewList + } catch (e: Exception) { + Log.d("RestaurantDetailScreen", "error: $e") } isLoading = false } @@ -98,7 +93,7 @@ fun RestaurantDetailScreen( TopRestaurantBar( onCloseClick = onBackClick, onWriteReviewClick = onWriteReviewClick, - restaurantPlaceId = restaurantPlaceId, + restaurantPlaceId = resPlaceId, restaurantName = restaurantName, restaurantGood = restaurantGood, restaurantBad = restaurantBad, diff --git a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/home/HomeScreen.kt b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/home/HomeScreen.kt index 975c083..43edae7 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/home/HomeScreen.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/home/HomeScreen.kt @@ -146,13 +146,13 @@ fun HomeScreen( map.setOnPoiClickListener { poi -> val matchingItem = markers.find { it.placeId == poi.placeId } - // 일치하는 MyItem이 있으면 로그를 찍거나 원하는 다른 작업을 수행 + // 일치하는 MyItem이 있으면 투명 마커를 추가하고 카메라를 이동시킴 matchingItem?.let { // MyItem을 처리하는 코드, 예를 들어 로그 출력 Log.d("MyMap", "Matching MyItem found: ${it.title}") lastAddedMarker.value?.remove() // 마지막에 추가된 마커 삭제 val marker = map.addMarker( - MarkerOptions().position(it.position).title(it.title).icon( + MarkerOptions().position(it.position).title(it.title!!.lines()[0]).icon( getMarkerIconFromDrawable( context, R.drawable.transparent, @@ -169,7 +169,7 @@ fun HomeScreen( map.cameraPosition.bearing ) map.setOnInfoWindowClickListener { clickedMarker -> - onReviewClick(poi.placeId) // onReviewClick(clickedMarker.placeId) + onReviewClick(poi.placeId + poi.name.lines()[0]) // onReviewClick(clickedMarker.placeId) } lastMarkerUpdated = true return@setOnPoiClickListener // MyItem을 찾았으므로 여기서 리스너 작업을 종료 @@ -221,7 +221,7 @@ fun HomeScreen( if(response.result.types.contains("restaurant") || response.result.types.contains("bar") || response.result.types.contains("cafe")){ map.setOnInfoWindowClickListener { clickedMarker -> if (clickedMarker.id == marker?.id) { - onReviewClick("000${poi.name.lines()[0]}") // onReviewClick(poi.placeId) + onReviewClick(poi.placeId + poi.name.lines()[0]) // onReviewClick(poi.placeId) } true } @@ -277,7 +277,7 @@ fun HomeScreen( defaultClusterRenderer.setOnClusterItemInfoWindowClickListener { item -> map.setOnInfoWindowClickListener(clusterManager) cameraPositionState.position = map.cameraPosition - onReviewClick(item.placeId) // onReviewClick(item.placeId) + onReviewClick(item.placeId + item.title) // onReviewClick(item.placeId) } Log.d( "MyMap", diff --git a/frontend/app/src/main/java/com/team13/fooriend/ui/util/ApiService.kt b/frontend/app/src/main/java/com/team13/fooriend/ui/util/ApiService.kt index 17788a6..fdf3de3 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/ui/util/ApiService.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/ui/util/ApiService.kt @@ -1,6 +1,13 @@ package com.team13.fooriend.ui.util +import okhttp3.MultipartBody +import okhttp3.ResponseBody +import retrofit2.Response +import retrofit2.http.Body import retrofit2.http.GET +import retrofit2.http.Multipart +import retrofit2.http.POST +import retrofit2.http.Part import retrofit2.http.Path interface ApiService { @@ -35,8 +42,35 @@ interface ApiService { @GET("/user/me") suspend fun getMyInfo(): User + + @POST("/reviews") + suspend fun postReview(@Body reviewPostBody: ReviewPostBody): Response + + @Multipart + @POST("/reviews/images") + suspend fun uploadImage(@Part file: MultipartBody.Part): ImageResponse } +data class ImageResponse( + val id: Int, + val url: String, + val isReceiptVerified: Boolean +) + +data class ReviewPostBody( + val content: String, + val imageIds: List, + val receiptImageId: Int, + val restaurant: RestaurantInfo +) + +data class RestaurantInfo( + val googleMapPlaceId: String, + val name: String, + val latitude: Double, + val longitude: Double +) + data class RestaurantsResponse( val restaurantList: List ) diff --git a/frontend/app/src/main/java/com/team13/fooriend/ui/util/LoginSuccess.kt b/frontend/app/src/main/java/com/team13/fooriend/ui/util/LoginSuccess.kt index af29dfb..3dd412f 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/ui/util/LoginSuccess.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/ui/util/LoginSuccess.kt @@ -20,7 +20,7 @@ fun LoginSuccess(context: Context, navController: NavHostController = rememberNa "restaurant/{restaurantId}" -> false "reviewDetail/{reviewId}" -> false "fooriend/{userId}" -> false - "writeReview" -> false + "writeReview/{restaurantPlaceId}" -> false "changePwd" -> false "myInfo" -> false else -> true // in all other cases show the bottom bar From bc9fd7e92d933e213070600b0db4b7ae92514c7f Mon Sep 17 00:00:00 2001 From: jakehsj Date: Tue, 14 Nov 2023 17:10:49 +0900 Subject: [PATCH 3/9] feat: add loading effect at homescreen --- .../team13/fooriend/ui/screen/PostingScreen.kt | 18 +++++++++++++++--- .../fooriend/ui/screen/home/HomeScreen.kt | 14 ++++++++++---- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/PostingScreen.kt b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/PostingScreen.kt index 298c871..e8e808a 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/PostingScreen.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/PostingScreen.kt @@ -53,6 +53,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.rememberCoroutineScope import androidx.core.net.toFile @@ -108,6 +109,8 @@ fun PostingScreen( val coroutineScope = rememberCoroutineScope() + var isLoading by remember { mutableStateOf(false) } + Column( modifier = Modifier .padding(16.dp) @@ -185,6 +188,7 @@ fun PostingScreen( Toast.makeText(context, "리뷰 사진을 등록해주세요.", Toast.LENGTH_SHORT).show() return@Button } + isLoading = true coroutineScope.launch { Log.d("PostingScreen", "restaurantPlaceId: $restaurantPlaceId") val response = placesApi.getPlaceDetails(placeId = restaurantPlaceId, apiKey = "AIzaSyDV4YwwZmJp1PHNO4DSp_BdgY4qCDQzKH0") @@ -250,10 +254,18 @@ fun PostingScreen( } } + if (isLoading) { + LoadingScreen() + } } -// place api 로 Restaurant 객체 가져와서 RestaurantInfo 객체 만들기 -suspend fun onPost(){ - +@Composable +fun LoadingScreen() { + Box( + contentAlignment = Alignment.Center, + modifier = Modifier.fillMaxSize() + ) { + CircularProgressIndicator() + } } @Composable diff --git a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/home/HomeScreen.kt b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/home/HomeScreen.kt index 43edae7..1b1eee8 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/home/HomeScreen.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/home/HomeScreen.kt @@ -37,6 +37,7 @@ import com.google.maps.android.clustering.view.DefaultClusterRenderer import com.google.maps.android.compose.CameraPositionState import com.google.maps.android.compose.rememberCameraPositionState import com.team13.fooriend.R +import com.team13.fooriend.ui.screen.LoadingScreen import com.team13.fooriend.ui.util.ApiService import com.team13.fooriend.ui.util.getMarkerIconFromDrawable //import com.team13.fooriend.ui.util.restaurants @@ -90,7 +91,8 @@ fun HomeScreen( val lastAddedMarker = remember { mutableStateOf(null) } var lastMarkerUpdated by remember { mutableStateOf(false) } // 초기 마커들이 추가되었는지 여부 var markers by remember { mutableStateOf>(emptyList()) } - var isLoading by remember { mutableStateOf(true) } + var isFirstLoad by remember { mutableStateOf(true) } + var isLoading by remember { mutableStateOf(false) } LaunchedEffect(Unit){ markers = apiService.getFriendsRestaurants().restaurantList.map { restaurant -> MyItem( @@ -100,9 +102,9 @@ fun HomeScreen( id = restaurant.id ) } - isLoading = false + isFirstLoad = false } - if(!isLoading) { + if(!isFirstLoad) { Box(modifier = Modifier.fillMaxSize()) { AndroidView( factory = { innerContext -> @@ -145,7 +147,6 @@ fun HomeScreen( map.setOnPoiClickListener { poi -> val matchingItem = markers.find { it.placeId == poi.placeId } - // 일치하는 MyItem이 있으면 투명 마커를 추가하고 카메라를 이동시킴 matchingItem?.let { // MyItem을 처리하는 코드, 예를 들어 로그 출력 @@ -174,6 +175,7 @@ fun HomeScreen( lastMarkerUpdated = true return@setOnPoiClickListener // MyItem을 찾았으므로 여기서 리스너 작업을 종료 } + isLoading = true coroutineScope.launch { Log.d("MyMap", "Poi clicked: ${poi.placeId}") val response = placesApi.getPlaceDetails( @@ -241,6 +243,7 @@ fun HomeScreen( lastAddedMarker.value?.remove() // 마지막에 추가된 마커 삭제 lastAddedMarker.value = marker lastMarkerUpdated = true + isLoading = false } } map.setOnMapClickListener { clickedLatLng -> @@ -322,6 +325,9 @@ fun HomeScreen( ) } } + if (isLoading) { + LoadingScreen() + } Log.d("MyMap", "Reload") } From 80f388d479c1568e8105a152c176a30ea4f1ec95 Mon Sep 17 00:00:00 2001 From: mechanicjo Date: Thu, 16 Nov 2023 03:05:29 +0900 Subject: [PATCH 4/9] =?UTF-8?q?test:=20login=20screen=EC=9C=BC=EB=A1=9C?= =?UTF-8?q?=EB=B6=80=ED=84=B0=20=EC=8B=9C=EC=9E=91=ED=95=98=EB=8A=94=20nav?= =?UTF-8?q?igation=20testing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit login screen으로부터 이동할 수 있는 navigation testing code 들입니다. -처음 앱 시작시 login screen인지 확인 -SIGN UP 버튼 클릭 sign up screen으로 이동하는지 확인 -ID/PWD 입력 하지 않고 LOGIN 버튼 클릭시 화면 전환 없는지 확인 -잘못된 ID/PWD 입력하고 LOGIN 버튼 클릭시 화면 전환 없는지 확인 -올바른 ID/PWD 입력하고 LOGIN 버튼 클릭시 HOME route로 이동하는지 확인 --- frontend/app/build.gradle.kts | 13 ++- .../com/team13/fooriend/NavigationTest.kt | 89 +++++++++++++++++++ 2 files changed, 94 insertions(+), 8 deletions(-) create mode 100644 frontend/app/src/androidTest/java/com/team13/fooriend/NavigationTest.kt diff --git a/frontend/app/build.gradle.kts b/frontend/app/build.gradle.kts index 72360ef..a11b7f7 100644 --- a/frontend/app/build.gradle.kts +++ b/frontend/app/build.gradle.kts @@ -94,13 +94,13 @@ dependencies { androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") androidTestImplementation(platform("androidx.compose:compose-bom:2023.03.00")) - androidTestImplementation("androidx.compose.ui:ui-test-junit4") - debugImplementation("androidx.compose.ui:ui-tooling") - debugImplementation("androidx.compose.ui:ui-test-manifest") + androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.3.3") + debugImplementation("androidx.compose.ui:ui-tooling:1.3.3") + debugImplementation("androidx.compose.ui:ui-test-manifest:1.3.3") // Navigation - implementation("androidx.navigation:navigation-compose:2.4.0-alpha10") - + implementation("androidx.navigation:navigation-compose:2.5.3") + androidTestImplementation("androidx.navigation:navigation-testing:2.5.3") // Gogle Places implementation("com.google.android.libraries.places:places:3.1.0") @@ -113,14 +113,11 @@ dependencies { implementation ("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1") implementation ("androidx.lifecycle:lifecycle-runtime-compose:2.6.1") - // Testing Navigation - androidTestImplementation ("androidx.navigation:navigation-testing:2.4.0-alpha10") // JUnit testImplementation ("junit:junit:4.+") // MockK (Kotlin-friendly mocking library) testImplementation ("io.mockk:mockk:1.12.0") - androidTestImplementation ("androidx.compose.ui:ui-test-junit4:1.7.0") // coil: image upload implementation ("io.coil-kt:coil-compose:1.4.0") diff --git a/frontend/app/src/androidTest/java/com/team13/fooriend/NavigationTest.kt b/frontend/app/src/androidTest/java/com/team13/fooriend/NavigationTest.kt new file mode 100644 index 0000000..8bfb244 --- /dev/null +++ b/frontend/app/src/androidTest/java/com/team13/fooriend/NavigationTest.kt @@ -0,0 +1,89 @@ +package com.team13.fooriend + +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.performTextInput +import androidx.navigation.compose.ComposeNavigator +import androidx.navigation.testing.TestNavHostController +import com.team13.fooriend.core.graph.AuthScreen +import com.team13.fooriend.core.graph.Graph +import com.team13.fooriend.core.graph.RootNavigationGraph +import com.team13.fooriend.ui.screen.login.LogInScreen +import org.junit.Assert +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +class NavigationTest { + + @get:Rule + val composeTestRule = createComposeRule() + lateinit var navController: TestNavHostController + + @Before + fun setUpNavHost(){ + composeTestRule.setContent { + navController = TestNavHostController(LocalContext.current) + navController.navigatorProvider.addNavigator(ComposeNavigator()) + RootNavigationGraph(navController = navController, context = LocalContext.current) + } + } + + @Test + fun verify_StartDestinationIsLoginScreen(){ + composeTestRule + .onNodeWithText("LOGIN") + .assertIsDisplayed() + } + + @Test + fun performClick_OnSignUpButton_navigatesToSignUpScreen(){ + composeTestRule + .onNodeWithText("SIGN UP") + .performClick() + val route = navController.currentBackStackEntry?.destination?.route + Assert.assertEquals(route, AuthScreen.SignUp.route) + } + + @Test + fun performClick_OnLogInButton_withEmptyIDandPWD(){ + composeTestRule + .onNodeWithText("LOGIN") + .performClick() + val route = navController.currentBackStackEntry?.destination?.route + Assert.assertEquals(route, AuthScreen.Login.route) + } + + @Test + fun performClick_OnLogInButton_withWrongIDorPWD(){ + composeTestRule + .onNodeWithText("ID") + .performTextInput("admin") + composeTestRule + .onNodeWithText("PASSWORD") + .performTextInput("admin") // please change wrong pwd + composeTestRule + .onNodeWithText("LOGIN") + .performClick() + val route = navController.currentBackStackEntry?.destination?.route + Assert.assertEquals(route, AuthScreen.Login.route) + } + + @Test + fun performClick_OnLogInButton_withCorrectIDandPWD(){ + composeTestRule + .onNodeWithText("ID") + .performTextInput("admin") + composeTestRule + .onNodeWithText("PASSWORD") + .performTextInput("admin") + composeTestRule + .onNodeWithText("LOGIN") + .performClick() + val route = navController.currentBackStackEntry?.destination?.route + Assert.assertEquals(route, Graph.HOME) + } +} \ No newline at end of file From ed3f00ceb93726318ec2a538d0b65a1a35d27ae1 Mon Sep 17 00:00:00 2001 From: jakehsj Date: Thu, 16 Nov 2023 14:31:25 +0900 Subject: [PATCH 5/9] feat: get access token when login --- .../fooriend/core/graph/AuthNavGraph.kt | 3 ++ .../fooriend/core/graph/HomeNavGraph.kt | 7 ++++ .../fooriend/core/graph/RootNavGraph.kt | 2 +- .../fooriend/ui/screen/FooriendScreen.kt | 10 ++--- .../fooriend/ui/screen/PostingScreen.kt | 7 +--- .../ui/screen/RestaurantDetailScreen.kt | 8 ++-- .../fooriend/ui/screen/ReviewDetailScreen.kt | 9 +++-- .../fooriend/ui/screen/home/HomeScreen.kt | 7 +--- .../fooriend/ui/screen/login/LogInScreen.kt | 28 +++++++++++++- .../ui/screen/mypage/ChangePwdScreen.kt | 3 ++ .../ui/screen/mypage/MyInformationScreen.kt | 3 ++ .../fooriend/ui/screen/mypage/MyPageScreen.kt | 11 +++--- .../fooriend/ui/screen/social/SocialScreen.kt | 12 +++--- .../team13/fooriend/ui/util/AccessToken.kt | 38 +++++++++++++++++++ .../com/team13/fooriend/ui/util/ApiService.kt | 12 ++++++ 15 files changed, 121 insertions(+), 39 deletions(-) create mode 100644 frontend/app/src/main/java/com/team13/fooriend/ui/util/AccessToken.kt diff --git a/frontend/app/src/main/java/com/team13/fooriend/core/graph/AuthNavGraph.kt b/frontend/app/src/main/java/com/team13/fooriend/core/graph/AuthNavGraph.kt index c8f4b42..5a41fe6 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/core/graph/AuthNavGraph.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/core/graph/AuthNavGraph.kt @@ -1,5 +1,6 @@ package com.team13.fooriend.core.graph +import android.content.Context import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable @@ -13,6 +14,7 @@ sealed class AuthScreen(val route: String) { object Forgot : AuthScreen(route = "FORGOT") } fun NavGraphBuilder.authNavGraph( + context : Context, navController: NavHostController ){ navigation( @@ -21,6 +23,7 @@ fun NavGraphBuilder.authNavGraph( ){ composable(route = AuthScreen.Login.route){// LogInScreen LogInScreen( + context = context, onClick = { navController.popBackStack() navController.navigate(Graph.HOME) diff --git a/frontend/app/src/main/java/com/team13/fooriend/core/graph/HomeNavGraph.kt b/frontend/app/src/main/java/com/team13/fooriend/core/graph/HomeNavGraph.kt index a9f37d1..e5894dd 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/core/graph/HomeNavGraph.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/core/graph/HomeNavGraph.kt @@ -41,6 +41,7 @@ fun HomeNavGraph( } composable(route = BottomNavItem.Social.route) { SocialScreen( + context = context, onReviewClick = { navController.navigate("reviewDetail/${it}") }, // 리뷰 이미지를 클릭한 경우 onUserClick = { navController.navigate("fooriend/${it}") }, // search bar에서 검색한 유저를 클릭한 경우 @@ -48,12 +49,14 @@ fun HomeNavGraph( } composable(route = BottomNavItem.MyPage.route) { MyPageScreen( + context = context, onMyInfoClick = { navController.navigate("myInfo") }, onReviewClick = { navController.navigate("reviewDetail/${it}") }, // 리뷰 이미지를 클릭한 경우 ) } composable(route = "restaurant/{restaurantId}"){ RestaurantDetailScreen( + context = context, restaurantPlaceId = it.arguments?.getString("restaurantId")?:"", onBackClick = { navController.navigateUp() }, // 뒤로가기 버튼을 누른 경우 onWriteReviewClick = { navController.navigate("writeReview/${it}") }, // 리뷰 작성 버튼을 누른 경우 @@ -62,6 +65,7 @@ fun HomeNavGraph( } composable("reviewDetail/{reviewId}"){backStackEntry -> ReviewDetailScreen( + context = context, onBackClick = { navController.navigateUp() }, onWriterClick = { navController.navigate("fooriend/${it}") }, // 작성자를 클릭한 경우 onRestaurantClick = { navController.navigate("restaurant/${it}") }, // 식당을 클릭한 경우 @@ -70,6 +74,7 @@ fun HomeNavGraph( } composable(route = "fooriend/{userId}"){ FooriendScreen( + context = context, onBackClick = { navController.navigateUp() }, onFollowClick = { }, //TODO: Follow 버튼을 누르면 팔로우가 되도록 구현해야 함 userId = it.arguments?.getString("userId")?.toInt() ?: 0, @@ -86,12 +91,14 @@ fun HomeNavGraph( } composable(route = "myInfo"){ MyInformationScreen( + context = context, onBackClick = { navController.navigateUp() }, onChangePwd = { navController.navigate("changePwd") }, ) } composable(route = "changePwd"){ ChangePwdScreen( + context = context, onConfirmClick = { navController.navigateUp() }, ) } diff --git a/frontend/app/src/main/java/com/team13/fooriend/core/graph/RootNavGraph.kt b/frontend/app/src/main/java/com/team13/fooriend/core/graph/RootNavGraph.kt index a779649..8255230 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/core/graph/RootNavGraph.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/core/graph/RootNavGraph.kt @@ -14,7 +14,7 @@ fun RootNavigationGraph(navController: NavHostController, context: Context) { route = Graph.ROOT, startDestination = Graph.AUTHENTICATION // 앱이 시작하면 authNavGraph 먼저 시작 ) { - authNavGraph(navController = navController) + authNavGraph(context = context, navController = navController) composable(route = Graph.HOME) { LoginSuccess(context) // 로그인 성공하면 Graph.Home 라우터 호출 -> LoginSuccess로 이동 (util package) } diff --git a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/FooriendScreen.kt b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/FooriendScreen.kt index 6309576..347d4fb 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/FooriendScreen.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/FooriendScreen.kt @@ -1,5 +1,6 @@ package com.team13.fooriend.ui.screen +import android.content.Context import android.util.Log import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding @@ -22,20 +23,19 @@ import com.team13.fooriend.ui.component.ProfileSection import com.team13.fooriend.ui.component.ReviewLazyGrid import com.team13.fooriend.ui.util.ApiService import com.team13.fooriend.ui.util.Review +import com.team13.fooriend.ui.util.createRetrofit import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory @Composable fun FooriendScreen( + context : Context, onBackClick : () -> Unit = {}, onFollowClick : () -> Unit = {}, userId : Int = 0, onReviewClick : (Int) -> Unit = {}, ){ - val retrofit = Retrofit.Builder() - .baseUrl("http://ec2-54-180-101-207.ap-northeast-2.compute.amazonaws.com") - .addConverterFactory(GsonConverterFactory.create()) - .build() + val retrofit = createRetrofit(context) val apiService = retrofit.create(ApiService::class.java) @@ -87,6 +87,6 @@ fun FooriendScreen( @Composable @Preview(showSystemUi = true, showBackground = true) fun FooriendScreenPreview(){ - FooriendScreen() + FooriendScreen(TODO()) } diff --git a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/PostingScreen.kt b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/PostingScreen.kt index e8e808a..82240c1 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/PostingScreen.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/PostingScreen.kt @@ -62,6 +62,7 @@ import com.team13.fooriend.ui.screen.home.PlacesApiService import com.team13.fooriend.ui.util.ApiService import com.team13.fooriend.ui.util.RestaurantInfo import com.team13.fooriend.ui.util.ReviewPostBody +import com.team13.fooriend.ui.util.createRetrofit import kotlinx.coroutines.launch import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MultipartBody @@ -93,11 +94,7 @@ fun PostingScreen( rememberLauncherForActivityResult(contract = ActivityResultContracts.GetMultipleContents()){ selectReceipt = it } - val retrofit = Retrofit.Builder() - .baseUrl("http://ec2-54-180-101-207.ap-northeast-2.compute.amazonaws.com") - .addConverterFactory(GsonConverterFactory.create()) - .build() - + val retrofit = createRetrofit(context) val apiService = retrofit.create(ApiService::class.java) val retrofit2 = Retrofit.Builder() diff --git a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/RestaurantDetailScreen.kt b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/RestaurantDetailScreen.kt index 4d13f4f..6b06a0a 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/RestaurantDetailScreen.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/RestaurantDetailScreen.kt @@ -1,5 +1,6 @@ package com.team13.fooriend.ui.screen +import android.content.Context import android.util.Log import androidx.compose.foundation.Image import androidx.compose.foundation.background @@ -47,6 +48,7 @@ import com.team13.fooriend.data.Restaurant import com.team13.fooriend.ui.util.Review import com.team13.fooriend.ui.util.ApiService import com.team13.fooriend.ui.screen.home.MyItem +import com.team13.fooriend.ui.util.createRetrofit import org.intellij.lang.annotations.JdkConstants.HorizontalAlignment import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory @@ -54,6 +56,7 @@ import retrofit2.converter.gson.GsonConverterFactory @OptIn(ExperimentalMaterial3Api::class) @Composable fun RestaurantDetailScreen( + context: Context, restaurantPlaceId: String, onBackClick: () -> Unit, onWriteReviewClick: (String) -> Unit, @@ -64,10 +67,7 @@ fun RestaurantDetailScreen( var isLoading by remember { mutableStateOf(true) } // Retrofit 설정 - val retrofit = Retrofit.Builder() - .baseUrl("http://ec2-54-180-101-207.ap-northeast-2.compute.amazonaws.com") - .addConverterFactory(GsonConverterFactory.create()) - .build() + val retrofit = createRetrofit(context) val apiService = retrofit.create(ApiService::class.java) var resPlaceId = restaurantPlaceId.substring(0,27) diff --git a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/ReviewDetailScreen.kt b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/ReviewDetailScreen.kt index 81e3844..794e65b 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/ReviewDetailScreen.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/ReviewDetailScreen.kt @@ -1,5 +1,6 @@ package com.team13.fooriend.ui.screen +import android.content.Context import android.util.Log import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement @@ -41,20 +42,19 @@ import com.team13.fooriend.R import com.team13.fooriend.data.Restaurant import com.team13.fooriend.ui.util.ApiService import com.team13.fooriend.ui.util.Review +import com.team13.fooriend.ui.util.createRetrofit import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory @Composable fun ReviewDetailScreen( + context: Context, reviewId: Int, onBackClick: () -> Unit, onWriterClick: (Int) -> Unit, onRestaurantClick: (String) -> Unit, ) { - val retrofit = Retrofit.Builder() - .baseUrl("http://ec2-54-180-101-207.ap-northeast-2.compute.amazonaws.com") - .addConverterFactory(GsonConverterFactory.create()) - .build() + val retrofit = createRetrofit(context) val apiService = retrofit.create(ApiService::class.java) //add review variable @@ -148,6 +148,7 @@ fun ReviewDetailScreen( @Preview(showSystemUi = true, showBackground = true) fun ReviewDetailScreenPreview() { ReviewDetailScreen( + context = TODO(), reviewId = 0, onBackClick = {}, onWriterClick = {}, diff --git a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/home/HomeScreen.kt b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/home/HomeScreen.kt index 1b1eee8..b589a3a 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/home/HomeScreen.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/home/HomeScreen.kt @@ -39,6 +39,7 @@ import com.google.maps.android.compose.rememberCameraPositionState import com.team13.fooriend.R import com.team13.fooriend.ui.screen.LoadingScreen import com.team13.fooriend.ui.util.ApiService +import com.team13.fooriend.ui.util.createRetrofit import com.team13.fooriend.ui.util.getMarkerIconFromDrawable //import com.team13.fooriend.ui.util.restaurants import kotlinx.coroutines.launch @@ -65,11 +66,7 @@ fun HomeScreen( val placesApi = retrofit.create(PlacesApiService::class.java) - val retrofit2 = Retrofit.Builder() - .baseUrl("http://ec2-54-180-101-207.ap-northeast-2.compute.amazonaws.com") // 기본 URL 설정 - .addConverterFactory(GsonConverterFactory.create()) // Gson 변환기 사용 - .build() - + val retrofit2 = createRetrofit(context) val apiService = retrofit2.create(ApiService::class.java) val coroutineScope = rememberCoroutineScope() diff --git a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/login/LogInScreen.kt b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/login/LogInScreen.kt index ee1a02e..434dcb7 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/login/LogInScreen.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/login/LogInScreen.kt @@ -1,5 +1,7 @@ package com.team13.fooriend.ui.screen.login +import android.content.Context +import android.widget.Toast import androidx.compose.foundation.background import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.collectIsPressedAsState @@ -19,9 +21,11 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -33,11 +37,19 @@ import com.team13.fooriend.ui.theme.CIvory import com.team13.fooriend.ui.theme.CLightGreen import com.team13.fooriend.ui.theme.CMidGreen import com.team13.fooriend.ui.theme.CRed +import com.team13.fooriend.ui.util.ApiService +import com.team13.fooriend.ui.util.LoginBody +import com.team13.fooriend.ui.util.createRetrofit +import com.team13.fooriend.ui.util.saveAccessToken +import kotlinx.coroutines.launch +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory @OptIn(ExperimentalMaterial3Api::class) @Composable fun LogInScreen( + context : Context, onClick : () -> Unit = {}, onSignUpClick : () -> Unit = {}, onForgotClick : () -> Unit = {} @@ -48,6 +60,10 @@ fun LogInScreen( val interactionSource = remember { MutableInteractionSource() } val isPressed by interactionSource.collectIsPressedAsState() val color = if (isPressed) CRed else Color.Black//CDarkGreen + val coroutineScope = rememberCoroutineScope() + val retrofit = createRetrofit(context) + + val apiService = retrofit.create(ApiService::class.java) Column( modifier = Modifier @@ -90,7 +106,15 @@ fun LogInScreen( Spacer(modifier = Modifier.height(20.dp)) Button( onClick = { if(id.isNotEmpty() && password.isNotEmpty()){ - onClick() + coroutineScope.launch{ + val response = apiService.login(LoginBody(id, password)) + if(response.accessToken == null){ + Toast.makeText(context, "아이디 또는 비밀번호가 일치하지 않습니다.", Toast.LENGTH_SHORT).show() + return@launch + } + saveAccessToken(context = context, token = response.accessToken) + onClick() + } } }, interactionSource = interactionSource, colors = ButtonDefaults.buttonColors( @@ -123,5 +147,5 @@ fun LogInScreen( @Preview(showSystemUi = true, showBackground = true) @Composable fun LogInScreenPreview(){ - LogInScreen() + LogInScreen( TODO()) } \ No newline at end of file diff --git a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/mypage/ChangePwdScreen.kt b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/mypage/ChangePwdScreen.kt index 2f9344f..3b16a4b 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/mypage/ChangePwdScreen.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/mypage/ChangePwdScreen.kt @@ -1,5 +1,6 @@ package com.team13.fooriend.ui.screen.mypage +import android.content.Context import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize @@ -12,6 +13,7 @@ import androidx.compose.ui.tooling.preview.Preview @Composable fun ChangePwdScreen( + context : Context, onConfirmClick : () -> Unit = {} ){ Column( @@ -34,6 +36,7 @@ fun ChangePwdScreen( @Preview(showSystemUi = true, showBackground = true) fun ChangePwdScreenPreview(){ ChangePwdScreen( + context = TODO(), onConfirmClick = {} ) } \ No newline at end of file diff --git a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/mypage/MyInformationScreen.kt b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/mypage/MyInformationScreen.kt index a484ffd..e57c564 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/mypage/MyInformationScreen.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/mypage/MyInformationScreen.kt @@ -1,5 +1,6 @@ package com.team13.fooriend.ui.screen.mypage +import android.content.Context import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize @@ -21,6 +22,7 @@ import com.team13.fooriend.data.User @Composable fun MyInformationScreen( + context: Context, onBackClick: () -> Unit, onChangePwd: () -> Unit, ) { @@ -62,6 +64,7 @@ fun MyInformationScreen( @Preview(showSystemUi = true, showBackground = true) fun MyInformationScreenPreview() { MyInformationScreen( + context = TODO(), onBackClick = {}, onChangePwd = {} ) diff --git a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/mypage/MyPageScreen.kt b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/mypage/MyPageScreen.kt index 0f889cf..a7f1031 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/mypage/MyPageScreen.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/mypage/MyPageScreen.kt @@ -1,5 +1,6 @@ package com.team13.fooriend.ui.screen.mypage +import android.content.Context import android.util.Log import androidx.compose.foundation.Image import androidx.compose.foundation.border @@ -46,20 +47,18 @@ import com.team13.fooriend.ui.component.ProfileSection import com.team13.fooriend.ui.component.ReviewLazyGrid import com.team13.fooriend.ui.util.ApiService import com.team13.fooriend.ui.util.Review +import com.team13.fooriend.ui.util.createRetrofit import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory @Composable fun MyPageScreen( + context : Context, onMyInfoClick: () -> Unit, onReviewClick: (Int) -> Unit ){ - val retrofit = Retrofit.Builder() - .baseUrl("http://ec2-54-180-101-207.ap-northeast-2.compute.amazonaws.com") - .addConverterFactory(GsonConverterFactory.create()) - .build() - + val retrofit = createRetrofit(context) val apiService = retrofit.create(ApiService::class.java) var reviews by remember { mutableStateOf>(emptyList()) } @@ -119,5 +118,5 @@ fun MyPageScreen( @Preview(showSystemUi = true, showBackground = true) @Composable fun MyPageScreenPreview(){ - MyPageScreen( onMyInfoClick = { }, onReviewClick = { }) + MyPageScreen( context = TODO(), onMyInfoClick = { }, onReviewClick = { }) } \ No newline at end of file diff --git a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/social/SocialScreen.kt b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/social/SocialScreen.kt index 77fedad..9c6b94a 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/social/SocialScreen.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/social/SocialScreen.kt @@ -1,5 +1,6 @@ package com.team13.fooriend.ui.screen.social +import android.content.Context import android.util.Log import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement @@ -38,6 +39,7 @@ import com.team13.fooriend.data.User import com.team13.fooriend.ui.component.ReviewLazyGrid import com.team13.fooriend.ui.util.ApiService import com.team13.fooriend.ui.util.Review +import com.team13.fooriend.ui.util.createRetrofit import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory @@ -45,15 +47,11 @@ import retrofit2.converter.gson.GsonConverterFactory @OptIn(ExperimentalMaterial3Api::class) @Composable fun SocialScreen( - // reviews: List 파라미터로 받아야 하나? + context: Context, onReviewClick : (Int) -> Unit, // 리뷰 이미지를 클릭한 경우 onUserClick : (Int) -> Unit, // search bar에서 검색한 유저를 클릭한 경우 ){ - // 임시로 id=1 호출 - val retrofit = Retrofit.Builder() - .baseUrl("http://ec2-54-180-101-207.ap-northeast-2.compute.amazonaws.com") - .addConverterFactory(GsonConverterFactory.create()) - .build() + val retrofit = createRetrofit(context) val apiService = retrofit.create(ApiService::class.java) @@ -202,5 +200,5 @@ fun SocialSearchBar( @Composable @Preview(showSystemUi = true, showBackground = true) fun SocialScreenPreview(){ - SocialScreen(onReviewClick = {}, onUserClick = {}) + SocialScreen(context = TODO(),onReviewClick = {}, onUserClick = {}) } \ No newline at end of file diff --git a/frontend/app/src/main/java/com/team13/fooriend/ui/util/AccessToken.kt b/frontend/app/src/main/java/com/team13/fooriend/ui/util/AccessToken.kt new file mode 100644 index 0000000..e0a5aa1 --- /dev/null +++ b/frontend/app/src/main/java/com/team13/fooriend/ui/util/AccessToken.kt @@ -0,0 +1,38 @@ +package com.team13.fooriend.ui.util + +import android.content.Context +import okhttp3.OkHttpClient +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory + +fun saveAccessToken(context: Context, token: String) { + val sharedPreferences = context.getSharedPreferences("MyAppPrefs", Context.MODE_PRIVATE) + sharedPreferences.edit().putString("AccessToken", token).apply() +} + +fun getAccessToken(context: Context): String? { + val sharedPreferences = context.getSharedPreferences("MyAppPrefs", Context.MODE_PRIVATE) + return sharedPreferences.getString("AccessToken", null) +} + +fun createRetrofit(context: Context): Retrofit { + val okHttpClient = OkHttpClient.Builder() + .addInterceptor { chain -> + val original = chain.request() + val accessToken = getAccessToken(context) + + val requestBuilder = original.newBuilder() + .header("Authorization", "Bearer $accessToken") + .method(original.method, original.body) + + val request = requestBuilder.build() + chain.proceed(request) + } + .build() + + return Retrofit.Builder() + .baseUrl("http://ec2-54-180-101-207.ap-northeast-2.compute.amazonaws.com") + .addConverterFactory(GsonConverterFactory.create()) + .client(okHttpClient) + .build() +} diff --git a/frontend/app/src/main/java/com/team13/fooriend/ui/util/ApiService.kt b/frontend/app/src/main/java/com/team13/fooriend/ui/util/ApiService.kt index fdf3de3..d91ce0e 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/ui/util/ApiService.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/ui/util/ApiService.kt @@ -49,7 +49,19 @@ interface ApiService { @Multipart @POST("/reviews/images") suspend fun uploadImage(@Part file: MultipartBody.Part): ImageResponse + + @POST("/auth/login") + suspend fun login(@Body loginBody: LoginBody): LoginResponse } +data class LoginBody( + val username: String, + val password: String +) + +data class LoginResponse( + val accessToken: String, + val refreshToken: String +) data class ImageResponse( val id: Int, From 5a2c3a1473940e2464f43299c96b7da8516b339c Mon Sep 17 00:00:00 2001 From: jakehsj Date: Thu, 16 Nov 2023 14:40:32 +0900 Subject: [PATCH 6/9] feat: signup available and error messages added --- .../fooriend/core/graph/AuthNavGraph.kt | 1 + .../fooriend/ui/screen/login/LogInScreen.kt | 5 +++- .../fooriend/ui/screen/signup/SignUpScreen.kt | 27 +++++++++++++++++-- .../com/team13/fooriend/ui/util/ApiService.kt | 8 ++++++ 4 files changed, 38 insertions(+), 3 deletions(-) diff --git a/frontend/app/src/main/java/com/team13/fooriend/core/graph/AuthNavGraph.kt b/frontend/app/src/main/java/com/team13/fooriend/core/graph/AuthNavGraph.kt index 5a41fe6..b1feb01 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/core/graph/AuthNavGraph.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/core/graph/AuthNavGraph.kt @@ -35,6 +35,7 @@ fun NavGraphBuilder.authNavGraph( } composable(route = AuthScreen.SignUp.route){// SignUpScreen SignUpScreen( + context = context, onSignUpClick = { navController.navigate(AuthScreen.Login.route) } diff --git a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/login/LogInScreen.kt b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/login/LogInScreen.kt index 434dcb7..465a410 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/login/LogInScreen.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/login/LogInScreen.kt @@ -61,7 +61,10 @@ fun LogInScreen( val isPressed by interactionSource.collectIsPressedAsState() val color = if (isPressed) CRed else Color.Black//CDarkGreen val coroutineScope = rememberCoroutineScope() - val retrofit = createRetrofit(context) + val retrofit = Retrofit.Builder() + .baseUrl("http://ec2-54-180-101-207.ap-northeast-2.compute.amazonaws.com") + .addConverterFactory(GsonConverterFactory.create()) + .build() val apiService = retrofit.create(ApiService::class.java) diff --git a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/signup/SignUpScreen.kt b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/signup/SignUpScreen.kt index fef6e3f..2e2c507 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/signup/SignUpScreen.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/signup/SignUpScreen.kt @@ -1,5 +1,7 @@ package com.team13.fooriend.ui.screen.signup +import android.content.Context +import android.widget.Toast import androidx.compose.animation.expandVertically import androidx.compose.foundation.background import androidx.compose.foundation.interaction.MutableInteractionSource @@ -26,6 +28,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -42,12 +45,18 @@ import com.team13.fooriend.ui.theme.CIvory import com.team13.fooriend.ui.theme.CLightGreen import com.team13.fooriend.ui.theme.CMidGreen import com.team13.fooriend.ui.theme.CRed +import com.team13.fooriend.ui.util.ApiService +import com.team13.fooriend.ui.util.RegisterBody +import kotlinx.coroutines.launch +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory import java.time.format.TextStyle @OptIn(ExperimentalMaterial3Api::class) @Composable fun SignUpScreen( + context : Context, onSignUpClick : () -> Unit = {} ){ val (id, idValue) = remember { mutableStateOf("") } @@ -59,7 +68,13 @@ fun SignUpScreen( val color = if (isPressed) CRed else Color.Black//CDarkGreen // val (nickname, nicknameValue) = remember { mutableStateOf("") } // val (phoneNumber, phoneNumberValue) = remember { mutableStateOf("") } + val retrofit = Retrofit.Builder() + .baseUrl("http://ec2-54-180-101-207.ap-northeast-2.compute.amazonaws.com") + .addConverterFactory(GsonConverterFactory.create()) + .build() + val apiService = retrofit.create(ApiService::class.java) + val coroutineScope = rememberCoroutineScope() Column( modifier = Modifier .fillMaxSize() @@ -119,7 +134,15 @@ fun SignUpScreen( // TextField(value = nickname, onValueChange = nicknameValue) // TextField(value = phoneNumber, onValueChange = phoneNumberValue) Button( - onClick = { onSignUpClick() }, + onClick = { + coroutineScope.launch { + if(password != passwordCheck){ + Toast.makeText(context, "비밀번호가 일치하지 않습니다.", Toast.LENGTH_SHORT).show() + return@launch + } + val response = apiService.register(RegisterBody("username",id, password)) + onSignUpClick() + } }, interactionSource = interactionSource, colors = ButtonDefaults.buttonColors( Color.Transparent,//CMidGreen, @@ -136,5 +159,5 @@ fun SignUpScreen( @Preview(showSystemUi = true, showBackground = true) @Composable fun SignUpScreenPreview(){ - SignUpScreen() + SignUpScreen(TODO()) } \ No newline at end of file diff --git a/frontend/app/src/main/java/com/team13/fooriend/ui/util/ApiService.kt b/frontend/app/src/main/java/com/team13/fooriend/ui/util/ApiService.kt index d91ce0e..be02be6 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/ui/util/ApiService.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/ui/util/ApiService.kt @@ -52,7 +52,15 @@ interface ApiService { @POST("/auth/login") suspend fun login(@Body loginBody: LoginBody): LoginResponse + + @POST("/user") + suspend fun register(@Body registerBody: RegisterBody): Response } +data class RegisterBody( + val name: String, + val username: String, + val password: String +) data class LoginBody( val username: String, val password: String From 623adb0a767ea55b1d297f8755a433c7751ea274 Mon Sep 17 00:00:00 2001 From: mechanicjo Date: Thu, 16 Nov 2023 15:21:59 +0900 Subject: [PATCH 7/9] =?UTF-8?q?test:=20bottomBar=20testing=20=EC=9D=BC?= =?UTF-8?q?=EB=B6=80=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 일부 구현했는데 디바이스가 없어서 모두 구현은 아직 못했습니다. --- .../java/com/team13/fooriend/BottomBarTest.kt | 76 +++++++++++++++++++ .../{NavigationTest.kt => LogInScreenTest.kt} | 3 +- .../com/team13/fooriend/MyPageScreenTest.kt | 45 +++++++++++ .../team13/fooriend/ui/component/BottomBar.kt | 1 + .../fooriend/ui/screen/mypage/MyPageScreen.kt | 2 +- 5 files changed, 124 insertions(+), 3 deletions(-) create mode 100644 frontend/app/src/androidTest/java/com/team13/fooriend/BottomBarTest.kt rename frontend/app/src/androidTest/java/com/team13/fooriend/{NavigationTest.kt => LogInScreenTest.kt} (97%) create mode 100644 frontend/app/src/androidTest/java/com/team13/fooriend/MyPageScreenTest.kt diff --git a/frontend/app/src/androidTest/java/com/team13/fooriend/BottomBarTest.kt b/frontend/app/src/androidTest/java/com/team13/fooriend/BottomBarTest.kt new file mode 100644 index 0000000..cbd9ffb --- /dev/null +++ b/frontend/app/src/androidTest/java/com/team13/fooriend/BottomBarTest.kt @@ -0,0 +1,76 @@ +package com.team13.fooriend + +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.test.assert +import androidx.compose.ui.test.assertIsFocused +import androidx.compose.ui.test.assertIsNotSelected +import androidx.compose.ui.test.assertIsOn +import androidx.compose.ui.test.assertIsSelected +import androidx.compose.ui.test.assertIsToggleable +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onNodeWithContentDescription +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import androidx.navigation.compose.ComposeNavigator +import androidx.navigation.testing.TestNavHostController +import com.team13.fooriend.core.graph.AuthScreen +import com.team13.fooriend.core.graph.RootNavigationGraph +import com.team13.fooriend.ui.component.BottomBar +import com.team13.fooriend.ui.component.BottomNavigation +import com.team13.fooriend.ui.navigation.BottomNavItem +import org.junit.Assert +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +class BottomBarTest { + @get:Rule + val composeTestRule = createComposeRule() + lateinit var navController: TestNavHostController + + @Before + fun setUpNavHost(){ + composeTestRule.setContent { + navController = TestNavHostController(LocalContext.current) + navController.navigatorProvider.addNavigator(ComposeNavigator()) + BottomBar(navController = navController, context = LocalContext.current, showBottomBar = true) + } + } + + @Test + fun verify_StartDestinationIsHomeScreen(){ + composeTestRule + .onNodeWithText("Home") + .assertIsSelected() + composeTestRule + .onNodeWithText("Social") + .assertIsNotSelected() + composeTestRule + .onNodeWithText("MyPage") + .assertIsNotSelected() + } + // 이후부터는 스마트폰 필요 + @Test + fun performClick_OnSocialIcon_navigatesToSocialScreen(){ + composeTestRule + .onNodeWithText("Social") + .performClick() + } + + @Test + fun performClick_OnMyPageIcon_navigatesToMyPageScreen(){ + composeTestRule + .onNodeWithText("MyPage") + .assertIsNotSelected() + } + + @Test + fun test3(){ + composeTestRule + .onNodeWithText("MyPage") + .assertIsNotSelected() + } + + + +} \ No newline at end of file diff --git a/frontend/app/src/androidTest/java/com/team13/fooriend/NavigationTest.kt b/frontend/app/src/androidTest/java/com/team13/fooriend/LogInScreenTest.kt similarity index 97% rename from frontend/app/src/androidTest/java/com/team13/fooriend/NavigationTest.kt rename to frontend/app/src/androidTest/java/com/team13/fooriend/LogInScreenTest.kt index 8bfb244..9754672 100644 --- a/frontend/app/src/androidTest/java/com/team13/fooriend/NavigationTest.kt +++ b/frontend/app/src/androidTest/java/com/team13/fooriend/LogInScreenTest.kt @@ -11,13 +11,12 @@ import androidx.navigation.testing.TestNavHostController import com.team13.fooriend.core.graph.AuthScreen import com.team13.fooriend.core.graph.Graph import com.team13.fooriend.core.graph.RootNavigationGraph -import com.team13.fooriend.ui.screen.login.LogInScreen import org.junit.Assert import org.junit.Before import org.junit.Rule import org.junit.Test -class NavigationTest { +class LogInScreenTest { @get:Rule val composeTestRule = createComposeRule() diff --git a/frontend/app/src/androidTest/java/com/team13/fooriend/MyPageScreenTest.kt b/frontend/app/src/androidTest/java/com/team13/fooriend/MyPageScreenTest.kt new file mode 100644 index 0000000..23c2f1d --- /dev/null +++ b/frontend/app/src/androidTest/java/com/team13/fooriend/MyPageScreenTest.kt @@ -0,0 +1,45 @@ +package com.team13.fooriend + +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.test.assert +import androidx.compose.ui.test.hasParent +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onNodeWithContentDescription +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.onRoot +import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.printToLog +import androidx.navigation.compose.ComposeNavigator +import androidx.navigation.testing.TestNavHostController +import com.team13.fooriend.core.graph.AuthScreen +import com.team13.fooriend.core.graph.HomeNavGraph +import com.team13.fooriend.core.graph.RootNavigationGraph +import org.junit.Assert +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +class MyPageScreenTest { + @get:Rule + val composeTestRule = createComposeRule() + lateinit var navController: TestNavHostController + + @Before + fun setUpNavHost(){ + composeTestRule.setContent { + navController = TestNavHostController(LocalContext.current) + navController.navigatorProvider.addNavigator(ComposeNavigator()) + HomeNavGraph(navController = navController, context = LocalContext.current) + + } + } + + @Test + fun performClick_OnSettingButton_navigatesToMyInformationScreen(){ + composeTestRule + .onNodeWithContentDescription("My Info") + + composeTestRule.onRoot().printToLog("test1234") + } + +} \ No newline at end of file diff --git a/frontend/app/src/main/java/com/team13/fooriend/ui/component/BottomBar.kt b/frontend/app/src/main/java/com/team13/fooriend/ui/component/BottomBar.kt index 12bff72..3462f5f 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/ui/component/BottomBar.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/ui/component/BottomBar.kt @@ -46,6 +46,7 @@ fun BottomNavigation(navController: NavHostController){ items.forEach { item -> val selected = item.route == navBackStackEntry.value?.destination?.route NavigationBarItem( + icon = { Icon(imageVector = item.icon, contentDescription = item.title) }, label = { Text(text = item.title, fontWeight = FontWeight.Bold) }, selected = selected, diff --git a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/mypage/MyPageScreen.kt b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/mypage/MyPageScreen.kt index 0f889cf..5640603 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/mypage/MyPageScreen.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/mypage/MyPageScreen.kt @@ -96,7 +96,7 @@ fun MyPageScreen( IconButton(onClick = onMyInfoClick){ Icon( imageVector = Icons.Default.Settings, - contentDescription = "Back", + contentDescription = "My Info", tint = Color.Black, ) } From 3dedfd741135cac2dbf91cb7de4deb9b8048848a Mon Sep 17 00:00:00 2001 From: jakehsj Date: Thu, 16 Nov 2023 18:30:48 +0900 Subject: [PATCH 8/9] feat: logout --- .../fooriend/core/graph/AuthNavGraph.kt | 13 +++++++- .../fooriend/core/graph/HomeNavGraph.kt | 11 ++++--- .../fooriend/core/graph/RootNavGraph.kt | 10 +++++- .../fooriend/ui/screen/login/LogInScreen.kt | 7 ++-- .../ui/screen/mypage/MyInformationScreen.kt | 33 ++++++++++++------- 5 files changed, 54 insertions(+), 20 deletions(-) diff --git a/frontend/app/src/main/java/com/team13/fooriend/core/graph/AuthNavGraph.kt b/frontend/app/src/main/java/com/team13/fooriend/core/graph/AuthNavGraph.kt index b1feb01..9d88e10 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/core/graph/AuthNavGraph.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/core/graph/AuthNavGraph.kt @@ -7,6 +7,7 @@ import androidx.navigation.compose.composable import androidx.navigation.navigation import com.team13.fooriend.ui.screen.login.LogInScreen import com.team13.fooriend.ui.screen.signup.SignUpScreen +import com.team13.fooriend.ui.util.getAccessToken sealed class AuthScreen(val route: String) { object Login : AuthScreen(route = "LOGIN") @@ -21,7 +22,12 @@ fun NavGraphBuilder.authNavGraph( route = Graph.AUTHENTICATION, startDestination = AuthScreen.Login.route // default screen ){ - composable(route = AuthScreen.Login.route){// LogInScreen + composable(route = AuthScreen.Login.route){ +// if(isLoggedIn(context)){ +// navController.popBackStack() +// navController.navigate(Graph.HOME) +// } + // LogInScreen LogInScreen( context = context, onClick = { @@ -42,4 +48,9 @@ fun NavGraphBuilder.authNavGraph( ) } } +} + +private fun isLoggedIn(context: Context): Boolean { + val accesstoken = getAccessToken(context) + return accesstoken != "" } \ No newline at end of file diff --git a/frontend/app/src/main/java/com/team13/fooriend/core/graph/HomeNavGraph.kt b/frontend/app/src/main/java/com/team13/fooriend/core/graph/HomeNavGraph.kt index e5894dd..072aab0 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/core/graph/HomeNavGraph.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/core/graph/HomeNavGraph.kt @@ -10,6 +10,7 @@ import androidx.navigation.compose.composable import com.team13.fooriend.R import com.team13.fooriend.data.Restaurant import com.team13.fooriend.data.Review +import com.team13.fooriend.ui.FooriendApp import com.team13.fooriend.ui.navigation.BottomNavItem import com.team13.fooriend.ui.screen.FooriendScreen import com.team13.fooriend.ui.screen.PostingScreen @@ -21,6 +22,7 @@ import com.team13.fooriend.ui.screen.mypage.ChangePwdScreenPreview import com.team13.fooriend.ui.screen.mypage.MyInformationScreen import com.team13.fooriend.ui.screen.mypage.MyPageScreen import com.team13.fooriend.ui.screen.social.SocialScreen +import com.team13.fooriend.ui.util.saveAccessToken @Composable fun HomeNavGraph( @@ -93,14 +95,13 @@ fun HomeNavGraph( MyInformationScreen( context = context, onBackClick = { navController.navigateUp() }, - onChangePwd = { navController.navigate("changePwd") }, + onChangePwd = { + saveAccessToken(context, "") + navController.navigate("changePwd") }, ) } composable(route = "changePwd"){ - ChangePwdScreen( - context = context, - onConfirmClick = { navController.navigateUp() }, - ) + FooriendApp(context = context) } } } \ No newline at end of file diff --git a/frontend/app/src/main/java/com/team13/fooriend/core/graph/RootNavGraph.kt b/frontend/app/src/main/java/com/team13/fooriend/core/graph/RootNavGraph.kt index 8255230..d5fd1cd 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/core/graph/RootNavGraph.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/core/graph/RootNavGraph.kt @@ -6,13 +6,16 @@ import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import com.team13.fooriend.ui.util.LoginSuccess +import com.team13.fooriend.ui.util.getAccessToken @Composable fun RootNavigationGraph(navController: NavHostController, context: Context) { NavHost( navController = navController, route = Graph.ROOT, - startDestination = Graph.AUTHENTICATION // 앱이 시작하면 authNavGraph 먼저 시작 + startDestination = + if(!isLoggedIn(context)) Graph.AUTHENTICATION else Graph.HOME + // 앱이 시작하면 authNavGraph 먼저 시작 ) { authNavGraph(context = context, navController = navController) composable(route = Graph.HOME) { @@ -21,6 +24,11 @@ fun RootNavigationGraph(navController: NavHostController, context: Context) { } } +private fun isLoggedIn(context: Context): Boolean { + val accesstoken = getAccessToken(context) + return accesstoken != "" +} + object Graph { const val ROOT = "root_graph" const val AUTHENTICATION = "auth_graph" diff --git a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/login/LogInScreen.kt b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/login/LogInScreen.kt index 465a410..ab60112 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/login/LogInScreen.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/login/LogInScreen.kt @@ -39,6 +39,7 @@ import com.team13.fooriend.ui.theme.CMidGreen import com.team13.fooriend.ui.theme.CRed import com.team13.fooriend.ui.util.ApiService import com.team13.fooriend.ui.util.LoginBody +import com.team13.fooriend.ui.util.LoginResponse import com.team13.fooriend.ui.util.createRetrofit import com.team13.fooriend.ui.util.saveAccessToken import kotlinx.coroutines.launch @@ -110,8 +111,10 @@ fun LogInScreen( Button( onClick = { if(id.isNotEmpty() && password.isNotEmpty()){ coroutineScope.launch{ - val response = apiService.login(LoginBody(id, password)) - if(response.accessToken == null){ + var response: LoginResponse? = null + try{ + response = apiService.login(LoginBody(id, password)) + } catch(e: Exception){ Toast.makeText(context, "아이디 또는 비밀번호가 일치하지 않습니다.", Toast.LENGTH_SHORT).show() return@launch } diff --git a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/mypage/MyInformationScreen.kt b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/mypage/MyInformationScreen.kt index e57c564..44c8fa8 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/mypage/MyInformationScreen.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/mypage/MyInformationScreen.kt @@ -13,12 +13,19 @@ import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.team13.fooriend.data.User +import com.team13.fooriend.ui.util.ApiService +import com.team13.fooriend.ui.util.createRetrofit @Composable fun MyInformationScreen( @@ -27,12 +34,16 @@ fun MyInformationScreen( onChangePwd: () -> Unit, ) { // 실제로는 user data 저장한 내용에서 가져와야 함 - val user = User( - id = 202114671, - name = "조용찬", - email = "jych1109@gmail.com", - password = "******", - ) + val retrofit = createRetrofit(context) + val apiService = retrofit.create(ApiService::class.java) + var name by remember { mutableStateOf("") } + var username by remember { mutableStateOf("") } + LaunchedEffect(Unit){ + val user = apiService.getMyInfo() + name = user.name + username = user.username + } + Column( modifier = Modifier .fillMaxSize() @@ -48,12 +59,12 @@ fun MyInformationScreen( tint = Color.Black ) } - Text(text = "Name : ${user.name}") - Text(text = "ID : ${user.id}") - Text(text = "Email : ${user.email}") - Text(text = "Password : ${user.password}") + Text(text = "Name : ${name}") + Text(text = "ID : ${username}") +// Text(text = "Email : ${user.email}") +// Text(text = "Password : ${user.password}") Button(onClick = onChangePwd) { - Text(text = "change pwd") + Text(text = "logout") } } From 02b71908ccea046b67a1a2270e50155f8ec3be34 Mon Sep 17 00:00:00 2001 From: jakehsj Date: Thu, 16 Nov 2023 18:44:35 +0900 Subject: [PATCH 9/9] feat: delete review post --- .../fooriend/ui/component/ReviewLazyGrid.kt | 2 +- .../fooriend/ui/screen/ReviewDetailScreen.kt | 36 +++++++++++++++---- .../com/team13/fooriend/ui/util/ApiService.kt | 6 ++++ 3 files changed, 37 insertions(+), 7 deletions(-) diff --git a/frontend/app/src/main/java/com/team13/fooriend/ui/component/ReviewLazyGrid.kt b/frontend/app/src/main/java/com/team13/fooriend/ui/component/ReviewLazyGrid.kt index 92a590a..0398992 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/ui/component/ReviewLazyGrid.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/ui/component/ReviewLazyGrid.kt @@ -62,7 +62,7 @@ fun ReviewCard( ) { Image( painter = rememberImagePainter( - data = review.images[0].url, + data = review.images[review.images.size - 1].url, builder = { crossfade(true) }, diff --git a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/ReviewDetailScreen.kt b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/ReviewDetailScreen.kt index 794e65b..4256ea0 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/ui/screen/ReviewDetailScreen.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/ui/screen/ReviewDetailScreen.kt @@ -2,6 +2,7 @@ package com.team13.fooriend.ui.screen import android.content.Context import android.util.Log +import android.widget.Toast import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -19,6 +20,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.filled.Delete import androidx.compose.material3.Button import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -28,6 +30,7 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -43,6 +46,7 @@ import com.team13.fooriend.data.Restaurant import com.team13.fooriend.ui.util.ApiService import com.team13.fooriend.ui.util.Review import com.team13.fooriend.ui.util.createRetrofit +import kotlinx.coroutines.launch import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory @@ -60,6 +64,7 @@ fun ReviewDetailScreen( //add review variable var review by remember { mutableStateOf(null) } var isLoading by remember { mutableStateOf(true) } + val coroutineScope = rememberCoroutineScope() LaunchedEffect(Unit) { try { @@ -82,12 +87,31 @@ fun ReviewDetailScreen( verticalArrangement = Arrangement.Top, horizontalAlignment = Alignment.Start, ) { - IconButton(onClick = onBackClick) { - Icon( - imageVector = Icons.Default.ArrowBack, - contentDescription = "Back", - tint = Color.Black - ) + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + IconButton(onClick = onBackClick) { + Icon( + imageVector = Icons.Default.ArrowBack, + contentDescription = "Back", + tint = Color.Black + ) + } + + IconButton(onClick = { + coroutineScope.launch{ + apiService.deleteReview(reviewId) + Toast.makeText(context, "리뷰가 삭제되었습니다.", Toast.LENGTH_SHORT).show() + onBackClick() + } + }) { + Icon( + imageVector = Icons.Default.Delete, + contentDescription = "Delete", + tint = Color.Black + ) + } } Spacer(modifier = Modifier.height(20.dp)) Row( diff --git a/frontend/app/src/main/java/com/team13/fooriend/ui/util/ApiService.kt b/frontend/app/src/main/java/com/team13/fooriend/ui/util/ApiService.kt index be02be6..fece7fa 100644 --- a/frontend/app/src/main/java/com/team13/fooriend/ui/util/ApiService.kt +++ b/frontend/app/src/main/java/com/team13/fooriend/ui/util/ApiService.kt @@ -4,6 +4,7 @@ import okhttp3.MultipartBody import okhttp3.ResponseBody import retrofit2.Response import retrofit2.http.Body +import retrofit2.http.DELETE import retrofit2.http.GET import retrofit2.http.Multipart import retrofit2.http.POST @@ -55,6 +56,11 @@ interface ApiService { @POST("/user") suspend fun register(@Body registerBody: RegisterBody): Response + + @DELETE("/reviews/{reviewId}") + suspend fun deleteReview( + @Path("reviewId") reviewId: Int + ): Response } data class RegisterBody( val name: String,