Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
d6a2d6b
fix: response의 answer와 description을 nullable 타입으로 변경했어요.
HamBP Feb 21, 2026
00a5a24
feat: 선물 dto 필드 추가
HamBP Mar 1, 2026
b965c3e
fix: 점심 모드를 변경했어요.
HamBP Mar 3, 2026
a42e040
refactor: 같은 브랜치에 여러 조건을 묶었어요.
HamBP Mar 3, 2026
ab3812d
feat: 사전 질문을 작성하는 화면을 위한 파일을 만들었어요.
HamBP Mar 3, 2026
251ab9e
feat: 등록하기 시 만약 사전 질문이 있다면 질문 작성 화면으로 내비게이션 해요.
HamBP Mar 3, 2026
b3c78b9
feat: git에 누락된 파일을 추가했어요.
HamBP Mar 3, 2026
29aca98
fix: cold start 시 로그인 정보를 가져오지 못 하는 문제를 수정했어요.
HamBP Mar 3, 2026
6c4397b
feat: 공연 정보와 사전 질문 목록을 가져와요
HamBP Mar 4, 2026
a7c013d
feat: 공연 정보를 표시해요
HamBP Mar 4, 2026
74cdac9
feat: 사전 질문 작성 폼을 만들었어요.
HamBP Mar 4, 2026
85375f9
feat: 등록하기 버튼을 추가했어요.
HamBP Mar 4, 2026
a68e790
refactor: 텍스트들을 리소스화 했어요.
HamBP Mar 4, 2026
cf5897f
feat: 페이지 이탈 전 다이얼로그를 띄워줘요.
HamBP Mar 4, 2026
027432f
feat: 등록하기 버튼 클릭 시 필요한 API를 호출해요.
HamBP Mar 4, 2026
85f11b7
feat: 첫 번째 API 실패 시 오류 이벤트를 내보내요.
HamBP Mar 4, 2026
d6cd666
feat: API 호출 성공 여부를 체크하여 메시지를 띄워요
HamBP Mar 5, 2026
f2227a6
feat: 선물 받기 API 호출 순서를 변경했어요.
HamBP Mar 5, 2026
f0f1c5d
feat: 캐시된 값이 있다면 캐시된 값을 사용해요.
HamBP Mar 6, 2026
72a9961
merge
HamBP Mar 6, 2026
8acfb7c
feat: 등록 버튼 enabled 처리했어요.
HamBP Mar 6, 2026
bf8d778
chore: 버전 업
HamBP Mar 6, 2026
7a93107
feat: retry 로직을 넣었어요.
HamBP Mar 6, 2026
275adbf
Merge branch 'develop' into feat/440
HamBP Mar 13, 2026
d782148
fix: 제목 가운데 정렬
HamBP Mar 16, 2026
4bb749f
fix: 취소 시 실패 팝업 제거
HamBP Mar 16, 2026
ff5a353
Merge remote-tracking branch 'origin/develop' into fix/453
HamBP Mar 20, 2026
ae1e373
fix: 더 넓은 범위의 실패를 받을 수 있게 변경했어요.
HamBP Mar 20, 2026
a30c10f
fix: 복사 과정에서 생긴 문제를 수정했어요.
HamBP Mar 20, 2026
f66e245
feat: 선물 등록 실패 시 스낵바 대신 다이얼로그를 띄워요.
HamBP Mar 20, 2026
9e30ddf
fix: 사전 질문이 있을 경우 다이얼로그를 표시하지 않고 질문 화면으로 넘어가요.
HamBP Mar 20, 2026
c2ecb81
feat: 선물 수령 시 티켓 탭으로 이동하도록 수정했어요.
HamBP Mar 20, 2026
2d72fdc
feat: 선물 다이얼로그의 X 버튼을 제거했어요.
HamBP Mar 20, 2026
95e2ba3
fix: 화면 이동 처리 시점을 변경했어요.
HamBP Mar 20, 2026
25d549e
fix: 다이얼로그의 X 버튼을 제거했어요.
HamBP Mar 21, 2026
a16048e
feat: (사전질문 작성) 선물을 받으면 티켓 탭으로 이동해요.
HamBP Mar 22, 2026
93a90fe
feat: 딥 링크를 감지하는 주체를 Main.kt로 변경했어요.
HamBP Mar 22, 2026
c8a1add
fix: 사전질문 작성 화면이 두 번 뜨는 문제를 수정했어요.
HamBP Mar 22, 2026
7b708fc
feat: 선물 등록 실패 시 뒤로가기
HamBP Mar 22, 2026
7640b54
Merge remote-tracking branch 'origin/develop' into fix/453
HamBP Mar 22, 2026
c56565c
fix: 빌드 오류 수정
HamBP Mar 22, 2026
a4d1280
Disable pre-question editing for cancelled reservations
Mar 24, 2026
ef81928
Refactor pre-question edit condition into view model
Mar 24, 2026
ebd1a6d
Hide pre-question section for cancelled reservations
Mar 25, 2026
c54f01d
chore: rename -> copy로 변경
HamBP Mar 26, 2026
875492a
fix: grid의 columns를 2로 고정. 및 하나의 list와 content type을 통한 lazy grid 사용
HamBP Mar 26, 2026
aebb433
Merge pull request #461 from Nexters/fix/453-refactorgrid
HamBP Mar 26, 2026
0d20a00
fix: 타이밍 이슈 해결 (home event 유실 문제)
HamBP Mar 26, 2026
e7f9862
Merge pull request #458 from Nexters/fix/453
HamBP Mar 27, 2026
b980377
Merge pull request #460 from Nexters/feature/Boolti-459
mangbaam Mar 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@
android:name=".presentation.screen.MainActivity"
android:exported="true"
android:theme="@style/Theme.Boolti"
android:windowSoftInputMode="adjustPan">
android:windowSoftInputMode="adjustPan"
android:launchMode="singleTop">

<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ data class GiftResponse(
val recipientPhoneNumber: String,
val salesEndTime: String,
val isDone: Boolean,
val showId: String,
val showName: String,
val showImg: String,
val showDate: String,
val salesTicketName: String,
val ticketCount: Int,
) {
fun toDomain(): Gift {
return Gift(
Expand All @@ -38,6 +44,12 @@ data class GiftResponse(
recipientPhoneNumber = recipientPhoneNumber,
salesEndTime = salesEndTime.toLocalDate(),
isDone = isDone,
showId = showId,
showName = showName,
showImage = showImg,
showDate = showDate.toLocalDate(),
salesTicketName = salesTicketName,
ticketCount = ticketCount,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ internal data class PreQuestionAnswerDetailResponse(
internal data class PreQuestionAnswerItemResponse(
val preQuestionId: Long,
val question: String,
val description: String,
val description: String?,
val isRequired: Boolean,
val answer: String,
val answer: String?,
val createdAt: String?,
val modifiedAt: String?,
)
Expand Down
6 changes: 6 additions & 0 deletions domain/src/main/java/com/nexters/boolti/domain/model/Gift.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,10 @@ data class Gift(
val recipientPhoneNumber: String,
val salesEndTime: LocalDate,
val isDone: Boolean,
val showId: String,
val showName: String,
val showImage: String,
val showDate: LocalDate,
val salesTicketName: String,
val ticketCount: Int,
)
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.nexters.boolti.domain.model
data class PreQuestionAnswer(
val preQuestionId: Long,
val question: String,
val description: String,
val description: String?,
val isRequired: Boolean,
val answer: String,
val answer: String?,
)
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import com.nexters.boolti.presentation.component.ToastSnackbarHost
import com.nexters.boolti.presentation.screen.accountsetting.accountSettingScreen
import com.nexters.boolti.presentation.screen.business.businessScreen
import com.nexters.boolti.presentation.screen.gift.giftScreen
import com.nexters.boolti.presentation.screen.giftprequestion.giftPreQuestionScreen
import com.nexters.boolti.presentation.screen.giftcomplete.giftCompleteScreen
import com.nexters.boolti.presentation.screen.home.homeScreen
import com.nexters.boolti.presentation.screen.link.linkListScreen
Expand Down Expand Up @@ -185,6 +186,7 @@ fun MainNavigation(
}

giftScreen()
giftPreQuestionScreen()

hostedShowScreen(
onClickShow = onClickQrScan,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.nexters.boolti.presentation.screen.giftprequestion

sealed interface GiftPreQuestionEvent {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.nexters.boolti.presentation.screen.giftprequestion

import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.composable
import androidx.navigation.toRoute
import com.nexters.boolti.presentation.screen.LocalNavController
import com.nexters.boolti.presentation.screen.navigation.MainRoute

fun NavGraphBuilder.giftPreQuestionScreen() {
composable<MainRoute.GiftPreQuestion> { backStackEntry ->
val navController = LocalNavController.current

GiftPreQuestionScreen(
onBackPressed = navController::navigateUp,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package com.nexters.boolti.presentation.screen.giftprequestion

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.nexters.boolti.domain.model.Gift
import com.nexters.boolti.domain.model.PreQuestion
import com.nexters.boolti.presentation.R
import com.nexters.boolti.presentation.component.BtCloseableAppBar
import com.nexters.boolti.presentation.component.ShowItemV2
import com.nexters.boolti.presentation.theme.Grey05
import com.nexters.boolti.presentation.theme.marginHorizontal
import com.nexters.boolti.presentation.theme.point4
import kotlinx.collections.immutable.ImmutableList

@Composable
fun GiftPreQuestionScreen(
onBackPressed: () -> Unit,
viewModel: GiftPreQuestionViewModel = hiltViewModel(),
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()

Scaffold(
topBar = {
BtCloseableAppBar(
onClickClose = {
TODO()
onBackPressed()
}
)
}
) { innerPadding ->
Box(modifier = Modifier.padding(innerPadding)) {
val state = uiState

if (state is GiftPreQuestionUiState.Success) {
GiftPreQuestionScreen(
gift = state.gift,
preQuestions = state.preQuestions,
)
}
}
}
}

@Composable
fun GiftPreQuestionScreen(
gift: Gift,
preQuestions: ImmutableList<PreQuestion>
) {
Column() {
Text(
modifier = Modifier
.padding(top = 16.dp)
.padding(horizontal = marginHorizontal),
text = stringResource(R.string.gift_pre_question_title),
style = point4,
color = Grey05,
)

Column {
ShowItemV2(
modifier = Modifier
.fillMaxWidth()
.padding(top = 12.dp)
.padding(horizontal = marginHorizontal),
poster = gift.showImage,
title = gift.showName,
description = stringResource(
id = R.string.reservation_ticket_info_format,
gift.salesTicketName,
gift.ticketCount,
),
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.nexters.boolti.presentation.screen.giftprequestion

import com.nexters.boolti.domain.model.Gift
import com.nexters.boolti.domain.model.PreQuestion
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.ImmutableMap
import kotlinx.collections.immutable.ImmutableSet
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.persistentMapOf
import kotlinx.collections.immutable.persistentSetOf

sealed interface GiftPreQuestionUiState {
data object Loading : GiftPreQuestionUiState

data class Success(
val gift: Gift,
val preQuestions: ImmutableList<PreQuestion> = persistentListOf(),
) : GiftPreQuestionUiState

companion object {
const val MAX_ANSWER_LENGTH = 100
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.nexters.boolti.presentation.screen.giftprequestion

import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
import com.nexters.boolti.domain.repository.GiftRepository
import com.nexters.boolti.domain.repository.TicketingRepository
import com.nexters.boolti.presentation.base.BaseViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class GiftPreQuestionViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
private val giftRepository: GiftRepository, // TODO: 캐시
private val ticketingRepository: TicketingRepository,
) : BaseViewModel() {

private val giftUuid: String = checkNotNull(savedStateHandle["giftUuid"]) {
"giftUuid가 전달되어야 합니다."
}
private val showId: String = checkNotNull(savedStateHandle["showId"]) {
"showId가 전달되어야 합니다."
}

private val _uiState = MutableStateFlow<GiftPreQuestionUiState>(GiftPreQuestionUiState.Loading)
val uiState: StateFlow<GiftPreQuestionUiState> = _uiState.asStateFlow()

init {
fetchGiftAndPreQuestions()
}

private fun fetchGiftAndPreQuestions() {
viewModelScope.launch(recordExceptionHandler) {
_uiState.update { GiftPreQuestionUiState.Loading }

val gift = async {
giftRepository.getGift(giftUuid).first()
}
val preQuestions = async {
ticketingRepository.getPreQuestions(showId).first()
}

_uiState.update {
GiftPreQuestionUiState.Success(
gift = gift.await(),
preQuestions = preQuestions.await().toImmutableList(),
)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,14 @@ fun GiftDialog(
}

val action: () -> Unit = when (status) {
GiftStatus.SELF -> {
GiftStatus.SELF, GiftStatus.CAN_REGISTER -> {
{
receiveGift()
onDismiss()
}
}

GiftStatus.NEED_LOGIN -> requireLogin
GiftStatus.CAN_REGISTER -> {
{
receiveGift()
onDismiss()
}
}

GiftStatus.FAILED -> onDismiss
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,9 @@ sealed interface HomeEvent {
) : HomeEvent

data object GiftRegistered : HomeEvent

data class NavigateToGiftPreQuestion(
val giftUuid: String,
val showId: String,
) : HomeEvent
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import com.nexters.boolti.presentation.screen.navigation.MainRoute
import com.nexters.boolti.presentation.screen.navigation.SearchRoute
import com.nexters.boolti.presentation.screen.navigation.ShowRoute
import com.nexters.boolti.presentation.screen.navigation.TicketRoute

fun NavGraphBuilder.homeScreen(
modifier: Modifier = Modifier,
) {
Expand Down Expand Up @@ -47,6 +46,9 @@ fun NavGraphBuilder.homeScreen(
navController.navigate(MainRoute.Login)
},
navigateToLogin = { navController.navigate(MainRoute.Login) },
navigateToGiftPreQuestion = { giftUuid, showId ->
navController.navigate(MainRoute.GiftPreQuestion(giftUuid = giftUuid, showId = showId))
},
)
}
}
Loading