Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/257 선물 완료 후 카카오톡으로 선물 전송하기 #262

Merged
merged 9 commits into from
Jul 24, 2024
3 changes: 3 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@
<data android:scheme="https" />
<data android:host="app.boolti.in" />
<data android:host="preview.boolti.in" />
<data
android:host="kakaolink"
android:scheme="kakao${KAKAO_APP_KEY}" />
</intent-filter>
</activity>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ internal class GiftDataSource @Inject constructor(
suspend fun approveGiftPayment(request: GiftApproveRequest): ApproveGiftPaymentResponse =
service.approveGiftPayment(request)

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

suspend fun getGiftImages(): List<ImageResponse> = service.getGiftImages()
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ internal interface GiftService {
@POST("/app/api/v1/order/gift-approve-payment")
suspend fun approveGiftPayment(@Body request: GiftApproveRequest): ApproveGiftPaymentResponse

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

@GET("/app/api/v1/gift/img-list")
suspend fun getGiftImages(): List<ImageResponse>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package com.nexters.boolti.data.network.request

data class GiftReceiveRequest(
val giftId: String,
val giftUuid: String,
)
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ data class ApproveGiftPaymentResponse(
val orderId: String,
val reservationId: String,
val giftId: String,
val giftUuid: String,
) {
fun toDomain(): ApproveGiftPayment {
return ApproveGiftPayment(
orderId = orderId,
reservationId = reservationId,
giftId = giftId
giftId = giftId,
giftUuid = giftUuid,
)
}
}
Original file line number Diff line number Diff line change
@@ -1,32 +1,39 @@
package com.nexters.boolti.data.network.response

import com.nexters.boolti.data.util.toLocalDate
import com.nexters.boolti.domain.model.Gift
import kotlinx.serialization.Serializable

@Serializable
data class GiftResponse(
val id: String,
val giftUuid: String,
val orderId: String,
val reservationId: String,
val giftImgId: String,
val giftImgPath: String,
val message: String,
val senderName: String,
val senderPhoneNumber: String,
val recipientName: String,
val recipientPhoneNumber: String,
val salesEndTime: String,
val isDone: Boolean,
) {
fun toDomain(): Gift {
return Gift(
id = id,
uuid = giftUuid,
orderId = orderId,
reservationId = reservationId,
giftImgId = giftImgId,
imagePath = giftImgPath,
message = message,
senderName = senderName,
senderPhoneNumber = senderPhoneNumber,
recipientName = recipientName,
recipientPhoneNumber = recipientPhoneNumber,
salesEndTime = salesEndTime.toLocalDate(),
isDone = isDone,
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,16 @@ import javax.inject.Inject
internal class GiftRepositoryImpl @Inject constructor(
private val dataSource: GiftDataSource
) : GiftRepository {
override fun receiveGift(giftId: String): Flow<Boolean> = flow {
emit(dataSource.receiveGift(GiftReceiveRequest(giftId)))
override fun receiveGift(giftUuid: String): Flow<Boolean> = flow {
emit(dataSource.receiveGift(GiftReceiveRequest(giftUuid)))
}

override fun approveGiftPayment(request: GiftApproveRequest): Flow<ApproveGiftPayment> = flow {
emit(dataSource.approveGiftPayment(request).toDomain())
}

override fun getGift(giftId: String): Flow<Gift> = flow {
emit(dataSource.getGift(giftId).toDomain())
override fun getGift(giftUuid: String): Flow<Gift> = flow {
emit(dataSource.getGift(giftUuid).toDomain())
}

override fun getGiftImages(): Flow<List<ImagePair>> = flow {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ data class ApproveGiftPayment(
val orderId: String,
val reservationId: String,
val giftId: String,
val giftUuid: String,
)
5 changes: 5 additions & 0 deletions domain/src/main/java/com/nexters/boolti/domain/model/Gift.kt
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
package com.nexters.boolti.domain.model

import java.time.LocalDate

data class Gift(
val id: String,
val uuid: String,
val orderId: String,
val reservationId: String,
val giftImgId: String,
val imagePath: String,
val message: String,
val senderName: String,
val senderPhoneNumber: String,
val recipientName: String,
val recipientPhoneNumber: String,
val salesEndTime: LocalDate,
val isDone: Boolean,
)
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import com.nexters.boolti.domain.request.GiftApproveRequest
import kotlinx.coroutines.flow.Flow

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

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

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

fun getGiftImages(): Flow<List<ImagePair>>
}
3 changes: 2 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
minSdk = "26"
targetSdk = "34"
versionCode = "12"
versionName = "1.5.3"
versionName = "1.5.4"
packageName = "com.nexters.boolti"
compileSdk = "34"
targetJvm = "17"
Expand Down Expand Up @@ -109,6 +109,7 @@ firebase-dynamic-links = { module = "com.google.firebase:firebase-dynamic-links-
zoomable = { module = "net.engawapg.lib:zoomable", version.ref = "zoomable" }
zxing-android-embedded = { module = "com.journeyapps:zxing-android-embedded", version.ref = "zxing" }
kakao-login = { group = "com.kakao.sdk", name = "v2-user", version.ref = "kakao" }
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" }
Expand Down
1 change: 1 addition & 0 deletions presentation/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ dependencies {
implementation(libs.lottie)
implementation(libs.bundles.coil)
api(libs.kakao.login)
implementation(libs.kakao.share)

implementation(libs.timber)
implementation(libs.zxing.android.embedded)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,15 @@ sealed class MainDestination(val route: String) {
)
}

data object GiftComplete : MainDestination(route = "giftComplete?reservationId={reservationId}&giftId={giftId}") {
data object GiftComplete : MainDestination(route = "giftComplete?reservationId={reservationId}&giftUuid={giftUuid}") {
val arguments = listOf(
navArgument(reservationId) { type = NavType.StringType },
)

fun createRoute(
reservationId: String,
giftId: String,
): String = "giftComplete?reservationId=$reservationId&giftId=$giftId"
giftUuid: String,
): String = "giftComplete?reservationId=$reservationId&giftUuid=$giftUuid"
}

data object TicketDetail : MainDestination(route = "tickets") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ fun NavGraphBuilder.addGiftScreen(
modifier = modifier,
popBackStack = popBackStack,
navigateToBusiness = { navigateTo(MainDestination.Business.route) },
navigateToComplete = { reservationId, giftId ->
navigateTo(MainDestination.GiftComplete.createRoute(reservationId, giftId))
navigateToComplete = { reservationId, giftUuid ->
navigateTo(MainDestination.GiftComplete.createRoute(reservationId, giftUuid))
}
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ import com.nexters.boolti.tosspayments.TossPaymentWidgetActivity.Companion.RESUL
fun GiftScreen(
popBackStack: () -> Unit,
navigateToBusiness: () -> Unit,
navigateToComplete: (reservationId: String, giftId: String) -> Unit,
navigateToComplete: (reservationId: String, giftUuid: String) -> Unit,
modifier: Modifier = Modifier,
viewModel: GiftViewModel = hiltViewModel(),
) {
Expand All @@ -100,10 +100,10 @@ fun GiftScreen(
val reservationId =
intent.getStringExtra("reservationId")
?: return@rememberLauncherForActivityResult
val giftId = intent.getStringExtra("giftId")
val giftUuid = intent.getStringExtra("giftUuid")
?: return@rememberLauncherForActivityResult

navigateToComplete(reservationId, giftId)
navigateToComplete(reservationId, giftUuid)
}

RESULT_SOLD_OUT -> showTicketSoldOutDialog = true
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.nexters.boolti.presentation.screen.giftcomplete

import android.content.Context
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
Expand Down Expand Up @@ -33,6 +34,13 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.kakao.sdk.share.ShareClient
import com.kakao.sdk.template.model.Button
import com.kakao.sdk.template.model.Content
import com.kakao.sdk.template.model.FeedTemplate
import com.kakao.sdk.template.model.Link
import com.nexters.boolti.domain.model.Gift
import com.nexters.boolti.domain.model.PaymentType
import com.nexters.boolti.domain.model.ReservationDetail
import com.nexters.boolti.presentation.R
Expand All @@ -48,6 +56,7 @@ import com.nexters.boolti.presentation.theme.Grey95
import com.nexters.boolti.presentation.theme.KakaoYellow
import com.nexters.boolti.presentation.theme.marginHorizontal
import com.nexters.boolti.presentation.theme.point4
import timber.log.Timber

@Composable
fun GiftCompleteScreen(
Expand All @@ -57,6 +66,7 @@ fun GiftCompleteScreen(
) {
val reservation by viewModel.reservation.collectAsStateWithLifecycle()
val gift by viewModel.gift.collectAsStateWithLifecycle()
val context = LocalContext.current

BackHandler(onBack = onClickClose)

Expand All @@ -70,6 +80,11 @@ fun GiftCompleteScreen(
.padding(innerPadding)
.padding(horizontal = marginHorizontal)
) {
val month = gift?.salesEndTime?.month?.value ?: 0
val day = gift?.salesEndTime?.dayOfMonth ?: 0
val dateText = stringResource(id = R.string.gift_expiration_date, month, day)
val buttonsText = stringResource(id = R.string.gift_check)

Text(
modifier = Modifier.padding(vertical = 20.dp),
text = stringResource(id = R.string.gift_complete_note),
Expand All @@ -93,7 +108,15 @@ fun GiftCompleteScreen(
shape = RoundedCornerShape(4.dp),
colors = ButtonDefaults.outlinedButtonColors(containerColor = KakaoYellow),
contentPadding = PaddingValues(horizontal = 20.dp),
onClick = { TODO() }
onClick = {
if (ShareClient.instance.isKakaoTalkSharingAvailable(context)) {
gift?.let {
sendMessage(context, it, dateText, buttonsText)
}
} else {
// TODO: 카카오톡 미설치 케이스 (아직은 고려 X)
}
}
) {
Box(
modifier = Modifier.fillMaxWidth(),
Expand Down Expand Up @@ -129,6 +152,41 @@ fun GiftCompleteScreen(
}
}

private fun sendMessage(context: Context, gift: Gift, dateText: String, buttonText: String) {
val defaultFeed = FeedTemplate(
content = Content(
title = "To. ${gift.recipientName}",
description = dateText,
imageUrl = gift.imagePath,
link = Link(
webUrl = "https://boolti.in/gift/${gift.uuid}",
mobileWebUrl = "https://boolti.in/gift/${gift.uuid}"
)
),
buttons = listOf(
Button(
buttonText,
Link(
webUrl = "https://boolti.in/gift/${gift.uuid}",
mobileWebUrl = "https://boolti.in/gift/${gift.uuid}"
)
),
)
)

ShareClient.instance.shareDefault(context, defaultFeed) { sharingResult, error ->
if (error != null) {
FirebaseCrashlytics.getInstance().recordException(error)
Timber.e(error)
} else if (sharingResult != null) {
context.startActivity(sharingResult.intent)

Timber.w("Warning Msg: ${sharingResult.warningMsg}")
Timber.w("Argument Msg: ${sharingResult.argumentMsg}")
}
}
}

@Composable
private fun InfoRow(
modifier: Modifier = Modifier,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ class GiftCompleteViewModel @Inject constructor(
private val reservationId: String = requireNotNull(savedStateHandle["reservationId"]) {
"reservationId 가 전달되지 않았습니다."
}
private val giftId: String = requireNotNull(savedStateHandle["giftId"]) {
"giftId 가 전달되지 않았습니다."
private val giftUuid: String = requireNotNull(savedStateHandle["giftUuid"]) {
"giftUuid 가 전달되지 않았습니다."
}

val reservation = ticketingRepository
Expand All @@ -31,7 +31,7 @@ class GiftCompleteViewModel @Inject constructor(
initialValue = null,
)

val gift = giftRepository.getGift(giftId)
val gift = giftRepository.getGift(giftUuid)
.stateIn(
scope = viewModelScope,
started = SharingStarted.Eagerly,
Expand Down
2 changes: 2 additions & 0 deletions presentation/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@
<string name="gift_select_receiver">받는 분 선택하기</string>
<string name="gift_selected_image">선택된 선물 이미지</string>
<string name="gift_image">선물 편지에 포함될 이미지</string>
<string name="gift_expiration_date">%d월 %d일까지 불티앱에서 선물을 등록해주세요.</string>
<string name="gift_check">선물 확인하기</string>
<string-array name="gift_information">
<item>선물 등록 기간은 해당 공연의 티켓 판매 기간까지입니다.</item>
<item>마이 > 결제 내역 > 결제 내역 상세에서 선물을 취소할 수 있습니다.</item>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ sealed interface PaymentEvent {
data class Approved(
val orderId: String,
val reservationId: String,
val giftId: String = "",
val giftUuid: String = "",
) : PaymentEvent

data object TicketSoldOut : PaymentEvent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ class TossPaymentWidgetActivity : AppCompatActivity() {
val intent = Intent().apply {
putExtra("orderId", event.orderId)
putExtra("reservationId", event.reservationId)
putExtra("giftId", event.giftId)
putExtra("giftUuid", event.giftUuid)
}
setResult(RESULT_SUCCESS, intent)
finish()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ class TossPaymentsWidgetViewModel @Inject constructor(
event(PaymentEvent.TicketSoldOut)
}
}.singleOrNull()?.let {
event(PaymentEvent.Approved(it.orderId, it.reservationId, it.giftId))
event(PaymentEvent.Approved(it.orderId, it.reservationId, it.giftUuid))
}
}

Expand Down