Skip to content

Commit b602bee

Browse files
authored
Merge pull request #149 from YAPP-Github/ui/#148-report-complete-ui
[UI/#148] 제보하기 로딩 및 완료 화면을 구현합니다.
2 parents ba0e1a4 + cf810ca commit b602bee

17 files changed

Lines changed: 618 additions & 167 deletions

File tree

5.31 KB
Loading
3.37 KB
Loading
7.13 KB
Loading
11.1 KB
Loading
15.7 KB
Loading
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="40dp"
3+
android:height="40dp"
4+
android:viewportWidth="40"
5+
android:viewportHeight="40">
6+
<path
7+
android:pathData="M20.136,36.673C19.664,36.673 19.155,37.164 18.682,37.128C18.209,37.091 17.718,36.891 17.264,36.8C16.809,36.709 16.373,36.364 15.918,36.237C15.464,36.11 15.064,35.819 14.609,35.655C14.155,35.491 13.736,35.31 13.3,35.11C12.864,34.91 12.464,34.709 12.046,34.473C11.627,34.237 11.409,33.746 11.027,33.473C10.646,33.2 9.755,33.564 9.391,33.255C9.027,32.946 9.027,32.219 8.7,31.891C8.373,31.564 7.846,31.382 7.536,31.019C7.227,30.655 7.155,30.128 6.882,29.728C6.609,29.328 6.009,29.164 5.773,28.746C5.536,28.328 5.155,27.964 4.955,27.528C4.755,27.091 4.573,26.619 4.409,26.164C4.246,25.709 3.973,25.273 3.846,24.8C3.718,24.328 4.027,23.764 3.955,23.291C3.882,22.819 4.082,22.328 4.046,21.837C4.009,21.346 3.791,20.91 3.791,20.419C3.791,19.928 3.791,19.455 3.827,18.982C3.864,18.51 3.827,18.019 3.9,17.546C3.973,17.073 3.736,16.51 3.864,16.055C3.991,15.6 4.391,15.182 4.555,14.746C4.718,14.309 4.809,13.8 5.009,13.364C5.209,12.928 5.736,12.655 5.973,12.237C6.209,11.819 6.573,11.491 6.846,11.109C7.118,10.728 6.918,9.946 7.227,9.582C7.536,9.219 8.409,9.364 8.736,9.019C9.064,8.673 9.264,8.2 9.627,7.891C9.991,7.582 10.518,7.51 10.918,7.237C11.318,6.964 11.718,6.764 12.118,6.528C12.518,6.291 12.918,6.091 13.355,5.891C13.791,5.691 14.082,5.2 14.536,5.037C14.991,4.873 15.5,5.019 15.973,4.891C16.446,4.764 16.864,4.691 17.337,4.6C17.809,4.51 18.209,4.11 18.7,4.073C19.191,4.037 19.646,3.546 20.136,3.546C20.627,3.546 21.046,4.51 21.518,4.564C21.991,4.619 22.5,4.219 22.973,4.31C23.446,4.4 23.791,4.946 24.246,5.073C24.7,5.2 25.136,5.255 25.591,5.419C26.046,5.582 26.609,5.4 27.046,5.6C27.482,5.8 28.136,5.6 28.537,5.855C28.937,6.11 29.409,6.346 29.791,6.619C30.173,6.891 30.246,7.637 30.609,7.928C30.973,8.219 31.664,8.219 31.991,8.546C32.318,8.873 32.682,9.255 32.991,9.619C33.3,9.982 33.227,10.655 33.5,11.055C33.773,11.455 34.027,11.837 34.264,12.255C34.5,12.673 34.518,13.182 34.736,13.6C34.955,14.019 35.482,14.309 35.646,14.764C35.809,15.219 35.591,15.764 35.718,16.237C35.846,16.709 35.864,17.146 35.937,17.619C36.009,18.091 36.773,18.473 36.809,18.946C36.846,19.419 36.755,19.928 36.755,20.4C36.755,20.873 36.736,21.382 36.7,21.855C36.664,22.328 36.009,22.728 35.918,23.182C35.827,23.637 36.482,24.291 36.373,24.746C36.264,25.2 35.609,25.528 35.446,25.964C35.282,26.4 34.937,26.764 34.736,27.2C34.536,27.637 34.209,27.964 33.973,28.382C33.736,28.8 33.446,29.128 33.173,29.528C32.9,29.928 32.991,30.564 32.7,30.928C32.409,31.291 31.937,31.51 31.591,31.837C31.246,32.164 31.009,32.582 30.646,32.891C30.282,33.2 30.191,33.891 29.809,34.164C29.427,34.437 28.718,34.273 28.318,34.509C27.918,34.746 27.5,34.982 27.064,35.182C26.627,35.382 26.118,35.364 25.682,35.528C25.246,35.691 24.864,36.055 24.409,36.182C23.955,36.31 23.446,36.146 22.973,36.237C22.5,36.328 22.046,36.31 21.573,36.364C21.1,36.419 20.664,36.6 20.173,36.6L20.136,36.673Z"
8+
android:fillColor="#FE7120"/>
9+
<path
10+
android:pathData="M20.136,36.673C19.664,36.673 19.155,37.164 18.682,37.128C18.209,37.091 17.718,36.891 17.264,36.8C16.809,36.709 16.373,36.364 15.918,36.237C15.464,36.11 15.064,35.819 14.609,35.655C14.155,35.491 13.736,35.31 13.3,35.11C12.864,34.91 12.464,34.709 12.046,34.473C11.627,34.237 11.409,33.746 11.027,33.473C10.646,33.2 9.755,33.564 9.391,33.255C9.027,32.946 9.027,32.219 8.7,31.891C8.373,31.564 7.846,31.382 7.536,31.019C7.227,30.655 7.155,30.128 6.882,29.728C6.609,29.328 6.009,29.164 5.773,28.746C5.536,28.328 5.155,27.964 4.955,27.528C4.755,27.091 4.573,26.619 4.409,26.164C4.246,25.709 3.973,25.273 3.846,24.8C3.718,24.328 4.027,23.764 3.955,23.291C3.882,22.819 4.082,22.328 4.046,21.837C4.009,21.346 3.791,20.91 3.791,20.419C3.791,19.928 3.791,19.455 3.827,18.982C3.864,18.51 3.827,18.019 3.9,17.546C3.973,17.073 3.736,16.51 3.864,16.055C3.991,15.6 4.391,15.182 4.555,14.746C4.718,14.309 4.809,13.8 5.009,13.364C5.209,12.928 5.736,12.655 5.973,12.237C6.209,11.819 6.573,11.491 6.846,11.109C7.118,10.728 6.918,9.946 7.227,9.582C7.536,9.219 8.409,9.364 8.736,9.019C9.064,8.673 9.264,8.2 9.627,7.891C9.991,7.582 10.518,7.51 10.918,7.237C11.318,6.964 11.718,6.764 12.118,6.528C12.518,6.291 12.918,6.091 13.355,5.891C13.791,5.691 14.082,5.2 14.536,5.037C14.991,4.873 15.5,5.019 15.973,4.891C16.446,4.764 16.864,4.691 17.337,4.6C17.809,4.51 18.209,4.11 18.7,4.073C19.191,4.037 19.646,3.546 20.136,3.546C20.627,3.546 21.046,4.51 21.518,4.564C21.991,4.619 22.5,4.219 22.973,4.31C23.446,4.4 23.791,4.946 24.246,5.073C24.7,5.2 25.136,5.255 25.591,5.419C26.046,5.582 26.609,5.4 27.046,5.6C27.482,5.8 28.136,5.6 28.537,5.855C28.937,6.11 29.409,6.346 29.791,6.619C30.173,6.891 30.246,7.637 30.609,7.928C30.973,8.219 31.664,8.219 31.991,8.546C32.318,8.873 32.682,9.255 32.991,9.619C33.3,9.982 33.227,10.655 33.5,11.055C33.773,11.455 34.027,11.837 34.264,12.255C34.5,12.673 34.518,13.182 34.736,13.6C34.955,14.019 35.482,14.309 35.646,14.764C35.809,15.219 35.591,15.764 35.718,16.237C35.846,16.709 35.864,17.146 35.937,17.619C36.009,18.091 36.773,18.473 36.809,18.946C36.846,19.419 36.755,19.928 36.755,20.4C36.755,20.873 36.736,21.382 36.7,21.855C36.664,22.328 36.009,22.728 35.918,23.182C35.827,23.637 36.482,24.291 36.373,24.746C36.264,25.2 35.609,25.528 35.446,25.964C35.282,26.4 34.937,26.764 34.736,27.2C34.536,27.637 34.209,27.964 33.973,28.382C33.736,28.8 33.446,29.128 33.173,29.528C32.9,29.928 32.991,30.564 32.7,30.928C32.409,31.291 31.937,31.51 31.591,31.837C31.246,32.164 31.009,32.582 30.646,32.891C30.282,33.2 30.191,33.891 29.809,34.164C29.427,34.437 28.718,34.273 28.318,34.509C27.918,34.746 27.5,34.982 27.064,35.182C26.627,35.382 26.118,35.364 25.682,35.528C25.246,35.691 24.864,36.055 24.409,36.182C23.955,36.31 23.446,36.146 22.973,36.237C22.5,36.328 22.046,36.31 21.573,36.364C21.1,36.419 20.664,36.6 20.173,36.6L20.136,36.673Z"
11+
android:strokeLineJoin="round"
12+
android:strokeWidth="3.63636"
13+
android:fillColor="#00000000"
14+
android:strokeColor="#FE7120"
15+
android:strokeLineCap="round"/>
16+
<path
17+
android:pathData="M11.3,20.51C11.3,21.019 10.918,21.237 10.591,21.564C10.264,21.891 10.045,22.037 9.536,22.037C9.027,22.037 8.755,21.964 8.427,21.619C8.1,21.273 7.646,21.019 7.646,20.51C7.646,20 8.227,19.873 8.555,19.528C8.882,19.182 9.027,18.691 9.536,18.691C10.045,18.691 10.155,19.201 10.5,19.546C10.845,19.891 11.318,19.982 11.318,20.51H11.3Z"
18+
android:fillColor="#ffffff"/>
19+
<path
20+
android:pathData="M11.3,20.51C11.3,21.019 10.918,21.237 10.591,21.564C10.264,21.891 10.045,22.037 9.536,22.037C9.027,22.037 8.755,21.964 8.427,21.619C8.1,21.273 7.646,21.019 7.646,20.51C7.646,20 8.227,19.873 8.555,19.528C8.882,19.182 9.027,18.691 9.536,18.691C10.045,18.691 10.155,19.201 10.5,19.546C10.845,19.891 11.318,19.982 11.318,20.51H11.3Z"
21+
android:strokeWidth="0.454545"
22+
android:fillColor="#00000000"
23+
android:strokeColor="#ffffff"/>
24+
<path
25+
android:pathData="M21.991,20.508C21.991,21.017 21.482,21.144 21.155,21.472C20.827,21.799 20.7,21.908 20.191,21.908C19.682,21.908 19.573,21.817 19.228,21.472C18.882,21.126 17.846,21.017 17.846,20.508C17.846,19.999 18.337,19.326 18.664,18.981C18.991,18.635 19.682,18.962 20.191,18.962C20.7,18.962 21.228,18.817 21.555,19.144C21.882,19.472 22.009,19.999 22.009,20.508H21.991Z"
26+
android:fillColor="#ffffff"/>
27+
<path
28+
android:pathData="M21.991,20.508C21.991,21.017 21.482,21.144 21.155,21.472C20.827,21.799 20.7,21.908 20.191,21.908C19.682,21.908 19.573,21.817 19.228,21.472C18.882,21.126 17.846,21.017 17.846,20.508C17.846,19.999 18.337,19.326 18.664,18.981C18.991,18.635 19.682,18.962 20.191,18.962C20.7,18.962 21.228,18.817 21.555,19.144C21.882,19.472 22.009,19.999 22.009,20.508H21.991Z"
29+
android:strokeWidth="0.454545"
30+
android:fillColor="#00000000"
31+
android:strokeColor="#ffffff"/>
32+
<path
33+
android:pathData="M32.663,20.508C32.663,21.017 32.299,21.308 31.954,21.635C31.609,21.962 31.336,22.58 30.827,22.58C30.318,22.58 29.536,22.471 29.19,22.144C28.845,21.817 28.809,21.017 28.809,20.508C28.809,19.999 29.463,19.817 29.809,19.49C30.154,19.162 30.318,18.217 30.827,18.217C31.336,18.217 31.572,19.108 31.918,19.435C32.263,19.762 32.663,20.017 32.663,20.526V20.508Z"
34+
android:fillColor="#ffffff"/>
35+
<path
36+
android:pathData="M32.663,20.508C32.663,21.017 32.299,21.308 31.954,21.635C31.609,21.962 31.336,22.58 30.827,22.58C30.318,22.58 29.536,22.471 29.19,22.144C28.845,21.817 28.809,21.017 28.809,20.508C28.809,19.999 29.463,19.817 29.809,19.49C30.154,19.162 30.318,18.217 30.827,18.217C31.336,18.217 31.572,19.108 31.918,19.435C32.263,19.762 32.663,20.017 32.663,20.526V20.508Z"
37+
android:strokeWidth="0.454545"
38+
android:fillColor="#00000000"
39+
android:strokeColor="#ffffff"/>
40+
</vector>

presentation/src/main/java/com/threegap/bitnagil/presentation/report/ReportScreen.kt

Lines changed: 96 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ import android.net.Uri
55
import androidx.activity.compose.rememberLauncherForActivityResult
66
import androidx.activity.result.PickVisualMediaRequest
77
import androidx.activity.result.contract.ActivityResultContracts
8+
import androidx.compose.animation.AnimatedContent
9+
import androidx.compose.animation.AnimatedContentTransitionScope
10+
import androidx.compose.animation.SizeTransform
11+
import androidx.compose.animation.core.Spring
12+
import androidx.compose.animation.core.spring
13+
import androidx.compose.animation.togetherWith
814
import androidx.compose.foundation.layout.Arrangement
915
import androidx.compose.foundation.layout.Column
1016
import androidx.compose.foundation.layout.PaddingValues
@@ -20,6 +26,8 @@ import androidx.compose.foundation.layout.windowInsetsPadding
2026
import androidx.compose.foundation.lazy.LazyRow
2127
import androidx.compose.foundation.lazy.items
2228
import androidx.compose.foundation.rememberScrollState
29+
import androidx.compose.foundation.text.KeyboardActions
30+
import androidx.compose.foundation.text.KeyboardOptions
2331
import androidx.compose.foundation.verticalScroll
2432
import androidx.compose.material3.ExperimentalMaterial3Api
2533
import androidx.compose.material3.Text
@@ -30,7 +38,11 @@ import androidx.compose.runtime.remember
3038
import androidx.compose.runtime.setValue
3139
import androidx.compose.ui.Alignment
3240
import androidx.compose.ui.Modifier
41+
import androidx.compose.ui.focus.FocusRequester
42+
import androidx.compose.ui.focus.focusRequester
3343
import androidx.compose.ui.platform.LocalContext
44+
import androidx.compose.ui.platform.LocalFocusManager
45+
import androidx.compose.ui.text.input.ImeAction
3446
import androidx.compose.ui.text.style.TextAlign
3547
import androidx.compose.ui.tooling.preview.Preview
3648
import androidx.compose.ui.unit.dp
@@ -50,6 +62,14 @@ import com.threegap.bitnagil.presentation.report.component.PhotoItem
5062
import com.threegap.bitnagil.presentation.report.component.ReportCategoryBottomSheet
5163
import com.threegap.bitnagil.presentation.report.component.ReportCategorySelector
5264
import com.threegap.bitnagil.presentation.report.component.ReportField
65+
import com.threegap.bitnagil.presentation.report.component.template.CompleteReportContent
66+
import com.threegap.bitnagil.presentation.report.component.template.SubmittingReportContent
67+
import com.threegap.bitnagil.presentation.report.model.ReportSideEffect
68+
import com.threegap.bitnagil.presentation.report.model.ReportState
69+
import com.threegap.bitnagil.presentation.report.model.ReportState.Companion.MAX_IMAGE_COUNT
70+
import com.threegap.bitnagil.presentation.report.model.SubmitState
71+
import com.threegap.bitnagil.presentation.report.model.uiTitle
72+
import kotlinx.coroutines.delay
5373
import org.orbitmvi.orbit.compose.collectAsState
5474
import org.orbitmvi.orbit.compose.collectSideEffect
5575

@@ -61,17 +81,22 @@ fun ReportScreenContainer(
6181
) {
6282
val context = LocalContext.current
6383
val uiState by viewModel.collectAsState()
84+
val contentFocusRequester = remember { FocusRequester() }
6485

6586
viewModel.collectSideEffect { sideEffect ->
6687
when (sideEffect) {
6788
is ReportSideEffect.NavigateToBack -> navigateToBack()
89+
is ReportSideEffect.FocusOnContent -> {
90+
delay(100)
91+
contentFocusRequester.requestFocus()
92+
}
6893
}
6994
}
7095

7196
var pendingCameraPhotoUri by remember { mutableStateOf<Uri?>(null) }
7297

7398
val pickMultipleMediaLauncher = rememberLauncherForActivityResult(
74-
contract = ActivityResultContracts.PickMultipleVisualMedia(ReportViewModel.MAX_IMAGE_COUNT),
99+
contract = ActivityResultContracts.PickMultipleVisualMedia(MAX_IMAGE_COUNT),
75100
onResult = viewModel::addImages,
76101
)
77102

@@ -130,23 +155,56 @@ fun ReportScreenContainer(
130155
)
131156
}
132157

133-
ReportScreen(
134-
uiState = uiState,
135-
onReportTitleChange = viewModel::updateReportTitle,
136-
onReportContentChange = viewModel::updateReportContent,
137-
onShowImageSourceBottomSheet = viewModel::showImageSourceBottomSheet,
138-
onShowReportCategoryBottomSheet = viewModel::showReportCategoryBottomSheet,
139-
onRemoveImage = viewModel::removeImage,
140-
onGetCurrentLocationClick = locationPermissionHandler::requestPermission,
141-
onSubmitClick = viewModel::submitReportWithImages,
142-
onBackClick = viewModel::navigateToBack,
143-
)
158+
AnimatedContent(
159+
modifier = Modifier
160+
.fillMaxSize()
161+
.statusBarsPadding(),
162+
targetState = uiState.submitState,
163+
label = "ReportSlideAnimation",
164+
transitionSpec = {
165+
(
166+
slideIntoContainer(
167+
animationSpec = spring(stiffness = Spring.StiffnessMediumLow),
168+
towards = AnimatedContentTransitionScope.SlideDirection.Start,
169+
) togetherWith slideOutOfContainer(
170+
animationSpec = spring(stiffness = Spring.StiffnessMediumLow),
171+
towards = AnimatedContentTransitionScope.SlideDirection.Start,
172+
)
173+
)
174+
.using(SizeTransform(clip = true))
175+
},
176+
) { submitState ->
177+
when (submitState) {
178+
SubmitState.IDLE -> {
179+
ReportScreen(
180+
uiState = uiState,
181+
contentFocusRequester = contentFocusRequester,
182+
onReportTitleChange = viewModel::updateReportTitle,
183+
onReportContentChange = viewModel::updateReportContent,
184+
onShowImageSourceBottomSheet = viewModel::showImageSourceBottomSheet,
185+
onShowReportCategoryBottomSheet = viewModel::showReportCategoryBottomSheet,
186+
onRemoveImage = viewModel::removeImage,
187+
onGetCurrentLocationClick = locationPermissionHandler::requestPermission,
188+
onSubmitClick = viewModel::submitReportWithImages,
189+
onBackClick = viewModel::navigateToBack,
190+
)
191+
}
192+
SubmitState.SUBMITTING -> SubmittingReportContent()
193+
SubmitState.COMPLETE -> {
194+
CompleteReportContent(
195+
uiState = uiState,
196+
onConfirmClick = viewModel::navigateToBack,
197+
)
198+
}
199+
}
200+
}
144201
}
145202

146203
@OptIn(ExperimentalMaterial3Api::class)
147204
@Composable
148205
private fun ReportScreen(
149206
uiState: ReportState,
207+
contentFocusRequester: FocusRequester,
150208
onReportTitleChange: (String) -> Unit,
151209
onReportContentChange: (String) -> Unit,
152210
onShowImageSourceBottomSheet: () -> Unit,
@@ -157,11 +215,11 @@ private fun ReportScreen(
157215
onBackClick: () -> Unit,
158216
) {
159217
val scrollState = rememberScrollState()
218+
val focusManager = LocalFocusManager.current
160219

161220
Column(
162221
modifier = Modifier
163222
.fillMaxSize()
164-
.statusBarsPadding()
165223
.windowInsetsPadding(WindowInsets.ime),
166224
) {
167225
BitnagilTopBar(
@@ -185,7 +243,7 @@ private fun ReportScreen(
185243
AddPhotoButton(
186244
onClick = onShowImageSourceBottomSheet,
187245
imageCount = uiState.reportImages.size,
188-
maxImageCount = ReportViewModel.MAX_IMAGE_COUNT,
246+
maxImageCount = MAX_IMAGE_COUNT,
189247
)
190248

191249
LazyRow(
@@ -208,6 +266,12 @@ private fun ReportScreen(
208266
value = uiState.reportTitle,
209267
onValueChange = onReportTitleChange,
210268
singleLine = true,
269+
keyboardActions = KeyboardActions(
270+
onDone = {
271+
focusManager.clearFocus()
272+
onShowReportCategoryBottomSheet()
273+
},
274+
),
211275
placeholder = {
212276
Text(
213277
text = "제보 제목을 작성해주세요.",
@@ -220,16 +284,29 @@ private fun ReportScreen(
220284

221285
ReportField(title = "카테고리") {
222286
ReportCategorySelector(
223-
title = uiState.selectedCategory?.title,
224-
onClick = onShowReportCategoryBottomSheet,
287+
title = uiState.selectedCategory?.uiTitle,
288+
onClick = {
289+
focusManager.clearFocus()
290+
onShowReportCategoryBottomSheet()
291+
},
225292
)
226293
}
227294

228295
ReportField(title = "상세 제보 내용") {
229296
BitnagilTextField(
230297
value = uiState.reportContent,
231298
onValueChange = onReportContentChange,
232-
modifier = Modifier.height(88.dp),
299+
modifier = Modifier
300+
.height(88.dp)
301+
.focusRequester(contentFocusRequester),
302+
keyboardOptions = KeyboardOptions(
303+
imeAction = ImeAction.Done,
304+
),
305+
keyboardActions = KeyboardActions(
306+
onDone = {
307+
focusManager.clearFocus()
308+
},
309+
),
233310
placeholder = {
234311
Text(
235312
text = "어떤 위험인지 간단히 설명해주세요.(100자 내외)",
@@ -248,7 +325,7 @@ private fun ReportScreen(
248325
)
249326
}
250327

251-
ReportField(title = " 신고 위치") {
328+
ReportField(title = "신고 위치") {
252329
CurrentLocationInput(
253330
currentLocation = uiState.currentAddress,
254331
onClick = onGetCurrentLocationClick,
@@ -276,6 +353,7 @@ private fun ReportScreen(
276353
private fun Preview() {
277354
ReportScreen(
278355
uiState = ReportState.Init,
356+
contentFocusRequester = remember { FocusRequester() },
279357
onReportTitleChange = {},
280358
onReportContentChange = {},
281359
onRemoveImage = {},

0 commit comments

Comments
 (0)