Skip to content

Commit

Permalink
Merge pull request #267 from Nexters/feature/259
Browse files Browse the repository at this point in the history
[feature/259] 선물하기 무료 케이스 및 다양한 케이스 처리
  • Loading branch information
HamBP authored Jul 27, 2024
2 parents e594969 + 15bb972 commit 98fc7a0
Show file tree
Hide file tree
Showing 17 changed files with 406 additions and 256 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.nexters.boolti.data.network.request.GiftReceiveRequest
import com.nexters.boolti.data.network.response.ApproveGiftPaymentResponse
import com.nexters.boolti.data.network.response.GiftResponse
import com.nexters.boolti.data.network.response.ImageResponse
import com.nexters.boolti.domain.request.FreeGiftRequest
import com.nexters.boolti.domain.request.GiftApproveRequest
import javax.inject.Inject

Expand All @@ -16,6 +17,9 @@ internal class GiftDataSource @Inject constructor(
suspend fun approveGiftPayment(request: GiftApproveRequest): ApproveGiftPaymentResponse =
service.approveGiftPayment(request)

suspend fun createFreeGift(request: FreeGiftRequest): ApproveGiftPaymentResponse =
service.createFreeGift(request)

suspend fun getGift(giftUuid: String): GiftResponse = service.getGift(giftUuid)

suspend fun getGiftImages(): List<ImageResponse> = service.getGiftImages()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.nexters.boolti.data.network.request.GiftReceiveRequest
import com.nexters.boolti.data.network.response.ApproveGiftPaymentResponse
import com.nexters.boolti.data.network.response.GiftResponse
import com.nexters.boolti.data.network.response.ImageResponse
import com.nexters.boolti.domain.request.FreeGiftRequest
import com.nexters.boolti.domain.request.GiftApproveRequest
import retrofit2.http.Body
import retrofit2.http.GET
Expand All @@ -17,6 +18,9 @@ internal interface GiftService {
@POST("/app/api/v1/order/gift-approve-payment")
suspend fun approveGiftPayment(@Body request: GiftApproveRequest): ApproveGiftPaymentResponse

@POST("/app/api/v1/order/free-gift-reservation")
suspend fun createFreeGift(@Body request: FreeGiftRequest): ApproveGiftPaymentResponse

@GET("/app/api/v1/gift/{giftUuid}")
suspend fun getGift(@Path("giftUuid") giftUuid: String): GiftResponse

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import kotlinx.serialization.Serializable

@Serializable
data class ApproveGiftPaymentResponse(
val orderId: String,
val orderId: String = "-1", // 무료 티켓일 경우 orderId가 null
val reservationId: String,
val giftId: String,
val giftUuid: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import kotlinx.serialization.Serializable
data class GiftResponse(
val id: String,
val giftUuid: String,
val orderId: String,
val orderId: String?,
val reservationId: String,
val giftImgId: String,
val giftImgPath: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import com.nexters.boolti.domain.model.ApproveGiftPayment
import com.nexters.boolti.domain.model.Gift
import com.nexters.boolti.domain.model.ImagePair
import com.nexters.boolti.domain.repository.GiftRepository
import com.nexters.boolti.domain.request.FreeGiftRequest
import com.nexters.boolti.domain.request.GiftApproveRequest
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
Expand All @@ -23,6 +24,10 @@ internal class GiftRepositoryImpl @Inject constructor(
emit(dataSource.approveGiftPayment(request).toDomain())
}

override fun sendFreeGift(request: FreeGiftRequest): Flow<ApproveGiftPayment> = flow {
emit(dataSource.createFreeGift(request).toDomain())
}

override fun getGift(giftUuid: String): Flow<Gift> = flow {
emit(dataSource.getGift(giftUuid).toDomain())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import java.time.LocalDate
data class Gift(
val id: String,
val uuid: String,
val orderId: String,
val orderId: String?,
val reservationId: String,
val giftImgId: String,
val imagePath: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@ package com.nexters.boolti.domain.repository
import com.nexters.boolti.domain.model.ApproveGiftPayment
import com.nexters.boolti.domain.model.Gift
import com.nexters.boolti.domain.model.ImagePair
import com.nexters.boolti.domain.request.FreeGiftRequest
import com.nexters.boolti.domain.request.GiftApproveRequest
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow

interface GiftRepository {
fun receiveGift(giftUuid: String): Flow<Boolean>

fun approveGiftPayment(request: GiftApproveRequest): Flow<ApproveGiftPayment>

fun sendFreeGift(request: FreeGiftRequest): Flow<ApproveGiftPayment>

fun getGift(giftUuid: String): Flow<Gift>

fun getGiftImages(): Flow<List<ImagePair>>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.nexters.boolti.domain.request

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class FreeGiftRequest(
val amount: Int,
val showId: String,
val salesTicketTypeId: String,
val ticketCount: Int,
@SerialName("giftImgId") val giftImageId: String,
val message: String,
val senderName: String,
val senderPhoneNumber: String,
val recipientName: String,
val recipientPhoneNumber: String,
)
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ kakao = "2.19.0"
timber = "5.0.1"
mockk = "1.13.8"
tosspayments = "0.1.15"
immutable = "0.3.7"

[libraries]
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activity-ktx" }
Expand Down Expand Up @@ -113,6 +114,7 @@ kakao-share = { group = "com.kakao.sdk", name = "v2-share", version.ref = "kakao
retrofit2-kotlinx-serialization-converter = { module = "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter", version.ref = "serializationConverter" }
timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" }
mockk = { group = "io.mockk", name = "mockk", version.ref = "mockk" }
immutable = { group = "org.jetbrains.kotlinx", name = "kotlinx-collections-immutable", version.ref = "immutable" }

[plugins]
android-application = { id = "com.android.application", version.ref = "android" }
Expand Down
1 change: 1 addition & 0 deletions presentation/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ dependencies {
implementation(libs.material)
implementation(libs.bundles.lifecycle)
implementation(libs.bundles.compose)
implementation(libs.immutable)
implementation(platform(libs.andoridx.compose.compose.bom))
implementation(libs.bundles.coroutines)
implementation(libs.bundles.firebase)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
package com.nexters.boolti.presentation.screen.gift

import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import com.nexters.boolti.domain.model.ImagePair
import com.nexters.boolti.presentation.R
import com.nexters.boolti.presentation.theme.BooltiTheme
import com.nexters.boolti.presentation.theme.Grey10
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toPersistentList


@Composable
fun CardSelection(
message: String,
onMessageChanged: (String) -> Unit,
images: ImmutableList<ImagePair>,
selectedImage: ImagePair?,
onImageSelected: (ImagePair) -> Unit,
) {
Column(
modifier = Modifier.padding(top = 24.dp, bottom = 48.dp),
) {
Column(
modifier = Modifier
.padding(horizontal = 32.dp)
.clip(RoundedCornerShape(8.dp))
.background(
brush = Brush.verticalGradient(
colors = listOf(
Color(0xFFFF5A14),
Color(0xFFFFA883),
)
)
)
.border(
width = 1.dp,
color = Color(0xFFFFA883),
shape = RoundedCornerShape(8.dp)
)
.padding(horizontal = 20.dp, vertical = 32.dp)
.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
val maximumLength = 40
val messageLengthUnit = stringResource(id = R.string.gift_message_length_unit)

BasicTextField(
modifier = Modifier
.fillMaxWidth()
.height(80.dp),
value = message,
onValueChange = onMessageChanged,
textStyle = MaterialTheme.typography.titleLarge.copy(
color = Color.White,
textAlign = TextAlign.Center
),
cursorBrush = SolidColor(Color.White)
)
Text(
modifier = Modifier.padding(top = 12.dp),
text = "${message.length}/${maximumLength}${messageLengthUnit}",
style = MaterialTheme.typography.labelMedium.copy(color = Grey10),
)

AsyncImage(
model = selectedImage?.originImage,
contentDescription = stringResource(id = R.string.gift_selected_image),
modifier = Modifier
.padding(top = 28.dp)
.fillMaxWidth()
.aspectRatio(3 / 2f)
.background(Color.White),
contentScale = ContentScale.Crop,
)
}

// TODO: 현재 선택 가능한 카드가 1개, 이후 카드 개수가 추가되면 주석 풀기!
// CardCarousel(
// modifier = Modifier
// .padding(top = 44.dp)
// .fillMaxWidth(),
// images = images,
// selectedImage = selectedImage,
// onImageSelected = onImageSelected,
// )
}
}

@Composable
private fun CardCarousel(
images: ImmutableList<ImagePair>,
selectedImage: ImagePair?,
onImageSelected: (ImagePair) -> Unit,
modifier: Modifier = Modifier,
) {
LazyRow(
modifier = modifier,
contentPadding = PaddingValues(start = 32.dp),
horizontalArrangement = Arrangement.spacedBy(12.dp),
) {
items(images) { image ->
val cardModifier = if (image == selectedImage) {
Modifier.border(
width = 1.dp,
color = MaterialTheme.colorScheme.primary,
shape = RoundedCornerShape(4.dp)
)
} else {
Modifier
}

AsyncImage(
model = image.thumbnailImage,
contentDescription = stringResource(id = R.string.gift_image),
modifier = cardModifier
.size(52.dp)
.clip(RoundedCornerShape(4.dp))
.clickable {
onImageSelected(image)
},
contentScale = ContentScale.Crop,
)
}
}
}

@Preview
@Composable
private fun CardSelectionPreview() {
BooltiTheme {
CardSelection(
message = "공연에 초대합니다.",
onMessageChanged = {},
images = (1..10).map {
ImagePair(
it.toString(),
"https://picsum.photos/200",
"https://picsum.photos/200"
)
}.toPersistentList(),
selectedImage = ImagePair("", "", ""),
onImageSelected = {}
)
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.nexters.boolti.presentation.screen.gift

sealed interface GiftEvent {
data class GiftSuccess(val reservationId: String, val showId: String) : GiftEvent
data class GiftSuccess(val reservationId: String, val giftUuid: String) : GiftEvent
data class ProgressPayment(val userId: String, val orderId: String) : GiftEvent
data object NoRemainingQuantity : GiftEvent
}
Loading

0 comments on commit 98fc7a0

Please sign in to comment.