Skip to content

Commit 7053a30

Browse files
authored
[Fix/#124] 등록 뷰 이슈 수정
1 parent 29ce75c commit 7053a30

File tree

8 files changed

+97
-160
lines changed

8 files changed

+97
-160
lines changed

app/src/main/java/com/poti/android/core/designsystem/component/field/PotiBasicField.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ internal fun PotiBasicField(
4747
singleLine: Boolean = true,
4848
trailingIcon: (@Composable () -> Unit)? = null,
4949
enabled: Boolean = true,
50+
readOnly: Boolean = false,
5051
visualTransformation: VisualTransformation? = null,
5152
neverCoverField: Boolean = false,
5253
) {
@@ -83,6 +84,7 @@ internal fun PotiBasicField(
8384
onNext = { onNextAction() },
8485
),
8586
enabled = enabled,
87+
readOnly = readOnly,
8688
textStyle = PotiTheme.typography.body16m.copy(
8789
color = PotiTheme.colors.black,
8890
),

app/src/main/java/com/poti/android/core/designsystem/component/field/PotiShortTextField.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ fun PotiShortTextField(
5252
imeAction: ImeAction = ImeAction.Done,
5353
focusRequester: FocusRequester? = null,
5454
onFocusChanged: ((Boolean) -> Unit)? = null,
55+
readOnly: Boolean = false,
5556
visualTransformation: VisualTransformation? = null,
5657
neverCoverField: Boolean = false,
5758
) {
@@ -95,6 +96,7 @@ fun PotiShortTextField(
9596
isFocused = focused
9697
onFocusChanged?.invoke(focused)
9798
},
99+
readOnly = readOnly,
98100
focusRequester = focusRequester,
99101
singleLine = true,
100102
trailingIcon = trailingIcon,

app/src/main/java/com/poti/android/presentation/party/create/PartyArtistSelectScreen.kt

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.poti.android.presentation.party.create
22

33
import androidx.compose.foundation.layout.Column
4-
import androidx.compose.foundation.layout.Spacer
54
import androidx.compose.foundation.layout.fillMaxSize
65
import androidx.compose.foundation.layout.fillMaxWidth
76
import androidx.compose.foundation.layout.padding
@@ -71,6 +70,18 @@ private fun PartyArtistSelectScreen(
7170
title = stringResource(R.string.create_header_artist_search),
7271
)
7372
},
73+
bottomBar = {
74+
PotiActionButton(
75+
text = stringResource(R.string.action_button_done),
76+
onClick = onConfirmClick,
77+
modifier = Modifier
78+
.fillMaxWidth()
79+
.padding(horizontal = screenWidthDp(16.dp))
80+
.padding(top = 4.dp, bottom = 16.dp),
81+
type = if (uiState.isArtistSelectDoneBtnEnabled) ActionButtonType.SECONDARY_MAIN else ActionButtonType.DEACTIVE_MAIN,
82+
enabled = uiState.isArtistSelectDoneBtnEnabled,
83+
)
84+
},
7485
) { innerPadding ->
7586
Column(
7687
modifier = Modifier
@@ -87,6 +98,7 @@ private fun PartyArtistSelectScreen(
8798
placeholder = stringResource(R.string.create_placeholder_artist_search),
8899
selectedString = uiState.selectedArtist?.name ?: "",
89100
modifier = Modifier.padding(vertical = 12.dp),
101+
showTrailingIcon = true,
90102
)
91103

92104
if (uiState.isArtistSearchResultsEmpty) {
@@ -95,19 +107,6 @@ private fun PartyArtistSelectScreen(
95107
modifier = Modifier.padding(horizontal = screenWidthDp(16.dp)),
96108
)
97109
}
98-
99-
Spacer(Modifier.weight(1f))
100-
101-
PotiActionButton(
102-
text = stringResource(R.string.action_button_done),
103-
onClick = onConfirmClick,
104-
modifier = Modifier
105-
.fillMaxWidth()
106-
.padding(horizontal = screenWidthDp(16.dp))
107-
.padding(top = 4.dp, bottom = 16.dp),
108-
type = if (uiState.isArtistSelectDoneBtnEnabled) ActionButtonType.SECONDARY_MAIN else ActionButtonType.DEACTIVE_MAIN,
109-
enabled = uiState.isArtistSelectDoneBtnEnabled,
110-
)
111110
}
112111
}
113112
}

app/src/main/java/com/poti/android/presentation/party/create/PartyCreateScreen.kt

Lines changed: 13 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ import androidx.compose.ui.res.stringResource
2424
import androidx.compose.ui.res.vectorResource
2525
import androidx.compose.ui.text.input.ImeAction
2626
import androidx.compose.ui.text.input.KeyboardType
27-
import androidx.compose.ui.tooling.preview.Preview
2827
import androidx.compose.ui.unit.dp
2928
import androidx.lifecycle.compose.collectAsStateWithLifecycle
29+
import androidx.lifecycle.viewmodel.compose.viewModel
3030
import com.poti.android.R
3131
import com.poti.android.core.common.extension.getSuccessDataOrNull
3232
import com.poti.android.core.common.extension.noRippleClickable
@@ -43,7 +43,6 @@ import com.poti.android.core.designsystem.component.navigation.PotiBottomButton
4343
import com.poti.android.core.designsystem.component.navigation.PotiHeaderPage
4444
import com.poti.android.core.designsystem.theme.PotiTheme
4545
import com.poti.android.domain.model.artist.MemberPriceOption
46-
import com.poti.android.domain.model.delivery.DeliveryOption
4746
import com.poti.android.presentation.party.create.component.CreateDeliverySetting
4847
import com.poti.android.presentation.party.create.component.CreateDropdownField
4948
import com.poti.android.presentation.party.create.component.CreateMemberSetting
@@ -53,11 +52,8 @@ import com.poti.android.presentation.party.create.component.ViewType
5352
import com.poti.android.presentation.party.create.model.CreateUiEffect
5453
import com.poti.android.presentation.party.create.model.CreateUiIntent
5554
import com.poti.android.presentation.party.create.model.CreateUiState
56-
import com.poti.android.presentation.party.create.model.FieldError
57-
import com.poti.android.presentation.party.create.model.MemberSettingStatus
5855
import com.poti.android.presentation.party.create.util.DateTransformation
5956
import com.poti.android.presentation.party.create.util.toImageInfosForPresigned
60-
import kotlinx.collections.immutable.persistentListOf
6157

6258
@Composable
6359
fun PartyCreateRoute(
@@ -150,9 +146,11 @@ fun PartyCreateRoute(
150146

151147
PartyCreateScreen(
152148
uiState = uiState,
149+
onScrollComplete = { viewModel.processIntent(CreateUiIntent.OnScrollComplete) },
153150
onBackClick = { viewModel.processIntent(CreateUiIntent.OnBackClick) },
154151
onImageChanged = { viewModel.processIntent(CreateUiIntent.OnImagesChanged(it)) },
155152
onSearchArtist = { viewModel.processIntent(CreateUiIntent.OnSearchClick) },
153+
onProductFocusChanged = { viewModel.processIntent(CreateUiIntent.OnProductFocus(it)) },
156154
onProductChanged = { viewModel.processIntent(CreateUiIntent.OnProductChange(it)) },
157155
onProductSearchItemClick = { viewModel.processIntent(CreateUiIntent.OnProductSelect(it)) },
158156
onDeadlineChanged = { viewModel.processIntent(CreateUiIntent.OnDeadlineChange(it)) },
@@ -170,9 +168,11 @@ fun PartyCreateRoute(
170168
@Composable
171169
private fun PartyCreateScreen(
172170
uiState: CreateUiState,
171+
onScrollComplete: () -> Unit,
173172
onBackClick: () -> Unit,
174173
onImageChanged: (List<Uri>) -> Unit,
175174
onSearchArtist: () -> Unit,
175+
onProductFocusChanged: (Boolean) -> Unit,
176176
onProductChanged: (String) -> Unit,
177177
onProductSearchItemClick: (String) -> Unit,
178178
onDeadlineChanged: (String) -> Unit,
@@ -188,31 +188,12 @@ private fun PartyCreateScreen(
188188
val listState = rememberLazyListState()
189189
val dateTransformation = remember { DateTransformation() }
190190

191-
LaunchedEffect(
192-
uiState.imageError,
193-
uiState.artistError,
194-
uiState.productError,
195-
uiState.deadlineError,
196-
uiState.descriptionError,
197-
uiState.accountNumberError,
198-
uiState.bankError,
199-
uiState.memberSettingStatus,
200-
) {
201-
val firstErrorFieldIndex = when {
202-
uiState.imageError != null -> 0
203-
uiState.artistError != null -> 1
204-
uiState.productError != null -> 2
205-
uiState.deadlineError != null -> 3
206-
uiState.descriptionError != null -> 4
207-
uiState.accountNumberError != null -> 5
208-
uiState.bankError != null -> 6
209-
uiState.memberSettingStatus == MemberSettingStatus.ERROR_NO_PRICE || uiState.memberSettingStatus == MemberSettingStatus.ERROR_NO_MEMBER -> 7
210-
else -> null
211-
}
212-
213-
firstErrorFieldIndex?.let { index ->
191+
LaunchedEffect(uiState.errorIndexToScroll) {
192+
val index = uiState.errorIndexToScroll
193+
index?.let {
214194
listState.animateScrollToItem(index)
215195
}
196+
onScrollComplete()
216197
}
217198

218199
Scaffold(
@@ -274,8 +255,8 @@ private fun PartyCreateScreen(
274255
Icon(
275256
imageVector = ImageVector.vectorResource(R.drawable.ic_search),
276257
contentDescription = null,
277-
tint = PotiTheme.colors.black,
278-
modifier = Modifier.size(18.dp),
258+
tint = PotiTheme.colors.gray700,
259+
modifier = Modifier.size(24.dp),
279260
)
280261
},
281262
)
@@ -295,6 +276,8 @@ private fun PartyCreateScreen(
295276
.padding(bottom = 28.dp),
296277
fieldErrorMsg = uiState.productError?.let { stringResource(it.message) } ?: "",
297278
selectedString = uiState.selectedProductName,
279+
readOnly = uiState.isProductFieldReadOnly,
280+
onFocusChanged = onProductFocusChanged,
298281
)
299282
}
300283

@@ -407,101 +390,3 @@ private fun PartyCreateScreen(
407390
}
408391
}
409392
}
410-
411-
@Preview
412-
@Composable
413-
private fun PartyCreateScreenDefaultPreview() {
414-
val deliveryOptions =
415-
persistentListOf(
416-
DeliveryOption(deliveryId = 1, name = "일반택배", price = 4000),
417-
DeliveryOption(deliveryId = 2, name = "준등기", price = 1800),
418-
)
419-
val selectedDeliveryIds = setOf(1.toLong())
420-
421-
PotiTheme {
422-
PartyCreateScreen(
423-
uiState = CreateUiState(
424-
editableDeliveryOptions = deliveryOptions,
425-
selectedDeliveryIds = selectedDeliveryIds,
426-
),
427-
onBackClick = {},
428-
onImageChanged = {},
429-
onSearchArtist = {},
430-
onProductChanged = {},
431-
onProductSearchItemClick = {},
432-
onDeadlineChanged = {},
433-
onDescriptionChanged = {},
434-
onAccountNumberChanged = {},
435-
onBankChanged = {},
436-
onMemberPriceChanged = {},
437-
onMemberEditBtnClick = {},
438-
onDeliveryRadioBtnClick = {},
439-
onCreateBtnClick = {},
440-
)
441-
}
442-
}
443-
444-
@Preview
445-
@Composable
446-
private fun PartyCreateScreenAccountNumberErrorPreview() {
447-
val deliveryOptions =
448-
persistentListOf(
449-
DeliveryOption(deliveryId = 1, name = "일반택배", price = 4000),
450-
DeliveryOption(deliveryId = 2, name = "준등기", price = 1800),
451-
)
452-
var accountNumberError by remember { mutableStateOf<FieldError?>(null) }
453-
454-
PotiTheme {
455-
PartyCreateScreen(
456-
uiState = CreateUiState(
457-
editableDeliveryOptions = deliveryOptions,
458-
accountNumberError = accountNumberError,
459-
),
460-
onBackClick = {},
461-
onImageChanged = {},
462-
onSearchArtist = {},
463-
onProductChanged = {},
464-
onProductSearchItemClick = {},
465-
onDeadlineChanged = {},
466-
onDescriptionChanged = {},
467-
onAccountNumberChanged = {},
468-
onBankChanged = {},
469-
onMemberPriceChanged = {},
470-
onMemberEditBtnClick = {},
471-
onDeliveryRadioBtnClick = {},
472-
onCreateBtnClick = { accountNumberError = FieldError.ACCOUNT_NUMBER_ERROR },
473-
)
474-
}
475-
}
476-
477-
@Preview
478-
@Composable
479-
private fun PartyCreateMemberPreview() {
480-
val deliveryOptions =
481-
persistentListOf(
482-
DeliveryOption(deliveryId = 1, name = "일반택배", price = 4000),
483-
DeliveryOption(deliveryId = 2, name = "준등기", price = 1800),
484-
)
485-
486-
PotiTheme {
487-
PartyCreateScreen(
488-
uiState = CreateUiState(
489-
editableDeliveryOptions = deliveryOptions,
490-
memberSettingStatus = MemberSettingStatus.ERROR_NO_MEMBER,
491-
),
492-
onBackClick = {},
493-
onImageChanged = {},
494-
onSearchArtist = {},
495-
onProductChanged = {},
496-
onProductSearchItemClick = {},
497-
onDeadlineChanged = {},
498-
onDescriptionChanged = {},
499-
onAccountNumberChanged = {},
500-
onBankChanged = {},
501-
onMemberPriceChanged = {},
502-
onMemberEditBtnClick = {},
503-
onDeliveryRadioBtnClick = {},
504-
onCreateBtnClick = {},
505-
)
506-
}
507-
}

app/src/main/java/com/poti/android/presentation/party/create/PartyCreateViewModel.kt

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ class PartyCreateViewModel @Inject constructor(
7575

7676
is CreateUiIntent.OnBackToCreate -> sendEffect(CreateUiEffect.NavigateToBack)
7777

78+
CreateUiIntent.OnScrollComplete -> updateState { copy(errorIndexToScroll = null) }
79+
7880
is CreateUiIntent.OnImagesChanged -> {
7981
updateState {
8082
copy(
@@ -113,6 +115,12 @@ class PartyCreateViewModel @Inject constructor(
113115
handleMemberPriceChange(newOption = intent.option)
114116
}
115117

118+
is CreateUiIntent.OnProductFocus -> {
119+
if (uiState.value.isProductFieldReadOnly && intent.focused) {
120+
updateState { copy(productError = FieldError.ARTIST_EMPTY_ERROR, isDirty = true) }
121+
}
122+
}
123+
116124
is CreateUiIntent.OnProductChange -> {
117125
handleProductChange(newValue = intent.value)
118126
}
@@ -315,19 +323,16 @@ class PartyCreateViewModel @Inject constructor(
315323
}
316324

317325
private fun handleProductChange(newValue: String) {
318-
if (uiState.value.selectedArtist == null) {
319-
updateState { copy(productError = FieldError.ARTIST_EMPTY_ERROR) }
320-
} else {
321-
updateState {
322-
copy(
323-
isDirty = true,
324-
productName = newValue,
325-
productError = if (newValue.isNotBlank()) null else this.productError,
326-
selectedProductName = "",
327-
)
328-
}
329-
productSearchKeywordForDebounce.value = newValue
326+
updateState {
327+
copy(
328+
isDirty = true,
329+
productName = newValue,
330+
productError = if (newValue.isNotBlank()) null else this.productError,
331+
selectedProductName = "",
332+
)
330333
}
334+
335+
productSearchKeywordForDebounce.value = newValue
331336
}
332337

333338
private suspend fun searchProdut(keyword: String) {
@@ -538,6 +543,7 @@ class PartyCreateViewModel @Inject constructor(
538543
if (hasError) {
539544
updateState {
540545
copy(
546+
isDirty = true,
541547
imageError = imageError,
542548
artistError = artistError,
543549
productError = productError,
@@ -549,11 +555,30 @@ class PartyCreateViewModel @Inject constructor(
549555
neverShowHint = if (hasMemberOptionError) true else this.neverShowHint,
550556
)
551557
}
558+
getScrollIndex()?.let {
559+
updateState { copy(errorIndexToScroll = it) }
560+
}
552561
}
553562

554563
return hasError
555564
}
556565

566+
private fun getScrollIndex(): Int? {
567+
val firstErrorFieldIndex = when {
568+
uiState.value.imageError != null -> 0
569+
uiState.value.artistError != null -> 1
570+
uiState.value.productError != null -> 2
571+
uiState.value.deadlineError != null -> 3
572+
uiState.value.descriptionError != null -> 4
573+
uiState.value.accountNumberError != null -> 5
574+
uiState.value.bankError != null -> 6
575+
uiState.value.memberSettingStatus == MemberSettingStatus.ERROR_NO_PRICE || uiState.value.memberSettingStatus == MemberSettingStatus.ERROR_NO_MEMBER -> 7
576+
else -> null
577+
}
578+
579+
return firstErrorFieldIndex
580+
}
581+
557582
private suspend fun uploadPartyInfo(
558583
urls: List<String>,
559584
): Result<Long> =

0 commit comments

Comments
 (0)