Skip to content

Commit

Permalink
Merge pull request #153 from YAPP-Github/QA/67-keyboard-auto-show
Browse files Browse the repository at this point in the history
[QA-67] 봉투/장부 생성 시 키보드가 요구되는 지면에서 키보드가 자동으로 올라오게 설정
  • Loading branch information
jinukeu authored Feb 16, 2024
2 parents ef8816b + aaa83a8 commit 3edddaa
Show file tree
Hide file tree
Showing 34 changed files with 292 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ class ReceivedEnvelopeAddViewModel @Inject constructor(

copy(
currentStep = prevStep,
buttonEnabled = prevStep == EnvelopeAddStep.MORE,
buttonEnabled = true,
lastPage = false,
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.hilt.navigation.compose.hiltViewModel
Expand All @@ -22,32 +26,46 @@ import com.susu.core.designsystem.theme.Gray40
import com.susu.core.designsystem.theme.SusuTheme
import com.susu.core.ui.extension.collectWithLifecycle
import com.susu.feature.received.R
import kotlinx.coroutines.android.awaitFrame
import kotlinx.coroutines.launch

@Composable
fun MemoContentRoute(
viewModel: MemoViewModel = hiltViewModel(),
updateParentMemo: (String?) -> Unit,
) {
val uiState = viewModel.uiState.collectAsStateWithLifecycle().value
val focusRequester = remember { FocusRequester() }
val scope = rememberCoroutineScope()
viewModel.sideEffect.collectWithLifecycle { sideEffect ->
when (sideEffect) {
is MemoSideEffect.UpdateParentMemo -> updateParentMemo(sideEffect.memo)
MemoSideEffect.ShowKeyboard -> scope.launch {
awaitFrame()
focusRequester.requestFocus()
}
}
}

LaunchedEffect(key1 = Unit) {
viewModel.showKeyboardIfTextEmpty()
}

LaunchedEffect(key1 = Unit) {
viewModel.updateMemo(uiState.memo)
}

MemoContent(
uiState = uiState,
focusRequester = focusRequester,
onTextChangeMemo = viewModel::updateMemo,
)
}

@Composable
fun MemoContent(
uiState: MemoState = MemoState(),
focusRequester: FocusRequester = remember { FocusRequester() },
onTextChangeMemo: (String) -> Unit = {},
) {
Column(
Expand All @@ -72,7 +90,7 @@ fun MemoContent(
onTextChange = onTextChangeMemo,
placeholder = stringResource(R.string.memo_content_placeholder),
placeholderColor = Gray40,
modifier = Modifier.fillMaxWidth(),
modifier = Modifier.fillMaxWidth().focusRequester(focusRequester),
)
Spacer(modifier = Modifier.size(SusuTheme.spacing.spacing_xl))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ data class MemoState(

sealed interface MemoSideEffect : SideEffect {
data class UpdateParentMemo(val memo: String?) : MemoSideEffect
data object ShowKeyboard : MemoSideEffect
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,10 @@ class MemoViewModel @Inject constructor() : BaseViewModel<MemoState, MemoSideEff
postSideEffect(MemoSideEffect.UpdateParentMemo(memo))
copy(memo = memo ?: "")
}

fun showKeyboardIfTextEmpty() {
if (currentState.memo.isEmpty()) {
postSideEffect(MemoSideEffect.ShowKeyboard)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ import androidx.compose.foundation.layout.size
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.hilt.navigation.compose.hiltViewModel
Expand All @@ -28,25 +30,40 @@ import com.susu.core.ui.extension.collectWithLifecycle
import com.susu.core.ui.extension.toMoneyFormat
import com.susu.core.ui.moneyList
import com.susu.feature.received.R
import kotlinx.coroutines.android.awaitFrame
import kotlinx.coroutines.launch

@Composable
fun MoneyContentRoute(
viewModel: MoneyViewModel = hiltViewModel(),
updateParentMoney: (Long) -> Unit,
) {
val uiState = viewModel.uiState.collectAsStateWithLifecycle().value

val focusRequester = remember { FocusRequester() }
val scope = rememberCoroutineScope()

viewModel.sideEffect.collectWithLifecycle { sideEffect ->
when (sideEffect) {
is MoneySideEffect.UpdateParentMoney -> updateParentMoney(sideEffect.money)
MoneySideEffect.ShowKeyboard -> scope.launch {
awaitFrame()
focusRequester.requestFocus()
}
}
}

LaunchedEffect(key1 = Unit) {
viewModel.showKeyboardIfMoneyEmpty()
}

LaunchedEffect(key1 = Unit) {
updateParentMoney(uiState.money.toLongOrNull() ?: 0)
}

MoneyContent(
uiState = uiState,
focusRequester = focusRequester,
onTextChangeMoney = viewModel::updateMoney,
onClickMoneyButton = viewModel::addMoney,
)
Expand All @@ -56,6 +73,7 @@ fun MoneyContentRoute(
@Composable
fun MoneyContent(
uiState: MoneyState = MoneyState(),
focusRequester: FocusRequester = remember { FocusRequester() },
onTextChangeMoney: (String) -> Unit = {},
onClickMoneyButton: (Int) -> Unit = {},
) {
Expand All @@ -77,6 +95,7 @@ fun MoneyContent(
.size(SusuTheme.spacing.spacing_m),
)
SusuPriceTextField(
modifier = Modifier.focusRequester(focusRequester),
text = uiState.money,
onTextChange = onTextChangeMoney,
placeholder = stringResource(R.string.money_content_placeholder),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ data class MoneyState(

sealed interface MoneySideEffect : SideEffect {
data class UpdateParentMoney(val money: Long) : MoneySideEffect

data object ShowKeyboard : MoneySideEffect
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,8 @@ class MoneyViewModel @Inject constructor() : BaseViewModel<MoneyState, MoneySide
money = addedMoney.toString(),
)
}

fun showKeyboardIfMoneyEmpty() {
if (currentState.money.isEmpty()) postSideEffect(MoneySideEffect.ShowKeyboard)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
Expand All @@ -34,7 +33,9 @@ import com.susu.core.ui.extension.collectWithLifecycle
import com.susu.feature.received.R
import com.susu.feature.received.envelopeadd.content.component.FriendListItem
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.android.awaitFrame
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.launch

@OptIn(FlowPreview::class)
@Composable
Expand All @@ -46,14 +47,24 @@ fun NameContentRoute(
val uiState = viewModel.uiState.collectAsStateWithLifecycle().value
val focusRequester = remember { FocusRequester() }
val focusManager = LocalFocusManager.current
val scope = rememberCoroutineScope()

viewModel.sideEffect.collectWithLifecycle { sideEffect ->
when (sideEffect) {
is NameSideEffect.UpdateParentName -> updateParentName(sideEffect.name)
is NameSideEffect.UpdateParentFriendId -> updateParentFriendId(sideEffect.friendId)
NameSideEffect.FocusClear -> focusManager.clearFocus()
NameSideEffect.ShowKeyboard -> scope.launch {
awaitFrame()
focusRequester.requestFocus()
}
}
}

LaunchedEffect(key1 = Unit) {
viewModel.showKeyboardIfTextEmpty()
}

LaunchedEffect(key1 = Unit) {
updateParentName(uiState.name)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ sealed interface NameSideEffect : SideEffect {
data class UpdateParentName(val name: String) : NameSideEffect
data class UpdateParentFriendId(val friendId: Long?) : NameSideEffect
data object FocusClear : NameSideEffect
data object ShowKeyboard : NameSideEffect
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,8 @@ class NameViewModel @Inject constructor(
}
}
}

fun showKeyboardIfTextEmpty() {
if (currentState.name.isEmpty()) postSideEffect(NameSideEffect.ShowKeyboard)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview
Expand All @@ -22,6 +26,8 @@ import com.susu.core.designsystem.theme.SusuTheme
import com.susu.core.ui.extension.collectWithLifecycle
import com.susu.core.ui.util.AnnotatedText
import com.susu.feature.received.R
import kotlinx.coroutines.android.awaitFrame
import kotlinx.coroutines.launch

@Composable
fun PhoneContentRoute(
Expand All @@ -30,9 +36,15 @@ fun PhoneContentRoute(
updateParentPhone: (String?) -> Unit,
) {
val uiState = viewModel.uiState.collectAsStateWithLifecycle().value
val focusRequester = remember { FocusRequester() }
val scope = rememberCoroutineScope()
viewModel.sideEffect.collectWithLifecycle { sideEffect ->
when (sideEffect) {
is PhoneSideEffect.UpdateParentPhone -> updateParentPhone(sideEffect.phone)
PhoneSideEffect.ShowKeyboard -> scope.launch {
awaitFrame()
focusRequester.requestFocus()
}
}
}

Expand All @@ -41,15 +53,21 @@ fun PhoneContentRoute(
viewModel.updatePhone(uiState.phone)
}

LaunchedEffect(key1 = Unit) {
viewModel.showKeyboardIfTextEmpty()
}

PhoneContent(
uiState = uiState,
focusRequester = focusRequester,
onTextChangePhone = viewModel::updatePhone,
)
}

@Composable
fun PhoneContent(
uiState: PhoneState = PhoneState(),
focusRequester: FocusRequester = remember { FocusRequester() },
onTextChangePhone: (String) -> Unit = {},
) {
Column(
Expand All @@ -75,7 +93,7 @@ fun PhoneContent(
onTextChange = onTextChangePhone,
placeholder = stringResource(R.string.phone_content_placeholder),
placeholderColor = Gray40,
modifier = Modifier.fillMaxWidth(),
modifier = Modifier.fillMaxWidth().focusRequester(focusRequester),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
)
Spacer(modifier = Modifier.size(SusuTheme.spacing.spacing_xl))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ data class PhoneState(

sealed interface PhoneSideEffect : SideEffect {
data class UpdateParentPhone(val phone: String?) : PhoneSideEffect
data object ShowKeyboard : PhoneSideEffect
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,10 @@ class PhoneViewModel @Inject constructor() : BaseViewModel<PhoneState, PhoneSide
postSideEffect(PhoneSideEffect.UpdateParentPhone(phone))
copy(phone = phone ?: "")
}

fun showKeyboardIfTextEmpty() {
if (currentState.phone.isEmpty()) {
postSideEffect(PhoneSideEffect.ShowKeyboard)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import androidx.compose.foundation.layout.size
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.hilt.navigation.compose.hiltViewModel
Expand All @@ -22,32 +24,46 @@ import com.susu.core.designsystem.theme.Gray40
import com.susu.core.designsystem.theme.SusuTheme
import com.susu.core.ui.extension.collectWithLifecycle
import com.susu.feature.received.R
import kotlinx.coroutines.android.awaitFrame
import kotlinx.coroutines.launch

@Composable
fun PresentContentRoute(
viewModel: PresentViewModel = hiltViewModel(),
updateParentPresent: (String?) -> Unit,
) {
val uiState = viewModel.uiState.collectAsStateWithLifecycle().value
val focusRequester = remember { FocusRequester() }
val scope = rememberCoroutineScope()
viewModel.sideEffect.collectWithLifecycle { sideEffect ->
when (sideEffect) {
is PresentSideEffect.UpdateParentPresent -> updateParentPresent(sideEffect.present)
PresentSideEffect.ShowKeyboard -> scope.launch {
awaitFrame()
focusRequester.requestFocus()
}
}
}

LaunchedEffect(key1 = Unit) {
viewModel.showKeyboardIfTextEmpty()
}

LaunchedEffect(key1 = Unit) {
viewModel.updatePresent(uiState.present)
}

PresentContent(
uiState = uiState,
focusRequester = focusRequester,
onTextChangeName = viewModel::updatePresent,
)
}

@Composable
fun PresentContent(
uiState: PresentState = PresentState(),
focusRequester: FocusRequester = remember { FocusRequester() },
onTextChangeName: (String) -> Unit = {},
) {
Column(
Expand All @@ -72,7 +88,7 @@ fun PresentContent(
onTextChange = onTextChangeName,
placeholder = stringResource(R.string.present_content_placeholder),
placeholderColor = Gray40,
modifier = Modifier.fillMaxWidth(),
modifier = Modifier.fillMaxWidth().focusRequester(focusRequester),
)
Spacer(modifier = Modifier.size(SusuTheme.spacing.spacing_xl))
}
Expand Down
Loading

0 comments on commit 3edddaa

Please sign in to comment.