Skip to content

Commit

Permalink
Merge pull request #262 from Nexters/feature/257
Browse files Browse the repository at this point in the history
Feature/257 선물 완료 후 카카오톡으로 선물 전송하기
  • Loading branch information
HamBP authored Jul 24, 2024
2 parents 7e1028f + 33fc6d5 commit 3065572
Show file tree
Hide file tree
Showing 21 changed files with 111 additions and 26 deletions.
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>>
}
1 change: 1 addition & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
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
2 changes: 2 additions & 0 deletions presentation/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ android {
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
buildConfigField("String", "PACKAGE_NAME", "\"${libs.versions.packageName.get()}\"")
buildConfigField("String", "DEV_SUBDOMAIN", getLocalProperty("DEV_SUBDOMAIN"))
}

buildTypes {
Expand Down Expand Up @@ -81,6 +82,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,8 +34,16 @@ 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.BuildConfig
import com.nexters.boolti.presentation.R
import com.nexters.boolti.presentation.extension.cardCodeToCompanyName
import com.nexters.boolti.presentation.screen.payment.PaymentToolbar
Expand All @@ -48,6 +57,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 +67,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 +81,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 +109,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 +153,44 @@ fun GiftCompleteScreen(
}
}

private fun sendMessage(context: Context, gift: Gift, dateText: String, buttonText: String) {
val subDomain = if (BuildConfig.DEBUG) BuildConfig.DEV_SUBDOMAIN else ""
val giftUrl = "https://${subDomain}boolti.in/gift/${gift.uuid}"

val defaultFeed = FeedTemplate(
content = Content(
title = "To. ${gift.recipientName}",
description = dateText,
imageUrl = gift.imagePath,
link = Link(
webUrl = giftUrl,
mobileWebUrl = giftUrl
)
),
buttons = listOf(
Button(
buttonText,
Link(
webUrl = giftUrl,
mobileWebUrl = giftUrl
)
),
)
)

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
Loading

0 comments on commit 3065572

Please sign in to comment.