Skip to content

Commit 935811b

Browse files
committed
feat/#13 : AiEpisodeScreen.kt 구현
1 parent aee2f33 commit 935811b

File tree

1 file changed

+267
-0
lines changed

1 file changed

+267
-0
lines changed
Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
package com.boostcamp.mapisode.episode.aiEpisode
2+
3+
import androidx.compose.foundation.BorderStroke
4+
import androidx.compose.foundation.border
5+
import androidx.compose.foundation.clickable
6+
import androidx.compose.foundation.layout.Arrangement
7+
import androidx.compose.foundation.layout.Box
8+
import androidx.compose.foundation.layout.Column
9+
import androidx.compose.foundation.layout.PaddingValues
10+
import androidx.compose.foundation.layout.Spacer
11+
import androidx.compose.foundation.layout.aspectRatio
12+
import androidx.compose.foundation.layout.fillMaxSize
13+
import androidx.compose.foundation.layout.fillMaxWidth
14+
import androidx.compose.foundation.layout.height
15+
import androidx.compose.foundation.layout.padding
16+
import androidx.compose.foundation.layout.size
17+
import androidx.compose.foundation.layout.width
18+
import androidx.compose.foundation.layout.wrapContentSize
19+
import androidx.compose.foundation.lazy.LazyRow
20+
import androidx.compose.foundation.lazy.items
21+
import androidx.compose.foundation.shape.RoundedCornerShape
22+
import androidx.compose.runtime.Composable
23+
import androidx.compose.runtime.LaunchedEffect
24+
import androidx.compose.runtime.getValue
25+
import androidx.compose.runtime.mutableIntStateOf
26+
import androidx.compose.runtime.saveable.rememberSaveable
27+
import androidx.compose.runtime.setValue
28+
import androidx.compose.ui.Alignment
29+
import androidx.compose.ui.Modifier
30+
import androidx.compose.ui.draw.clip
31+
import androidx.compose.ui.layout.ContentScale
32+
import androidx.compose.ui.platform.LocalConfiguration
33+
import androidx.compose.ui.unit.dp
34+
import androidx.hilt.navigation.compose.hiltViewModel
35+
import androidx.lifecycle.compose.collectAsStateWithLifecycle
36+
import coil3.compose.AsyncImage
37+
import com.boostcamp.mapisode.designsystem.compose.MapisodeDivider
38+
import com.boostcamp.mapisode.designsystem.compose.MapisodeScaffold
39+
import com.boostcamp.mapisode.designsystem.compose.MapisodeText
40+
import com.boostcamp.mapisode.designsystem.compose.MapisodeTextField
41+
import com.boostcamp.mapisode.designsystem.compose.TextAlignment
42+
import com.boostcamp.mapisode.designsystem.compose.Thickness
43+
import com.boostcamp.mapisode.designsystem.compose.button.MapisodeFilledButton
44+
import com.boostcamp.mapisode.designsystem.theme.MapisodeTheme
45+
import com.boostcamp.mapisode.ui.photopicker.MapisodePhotoPicker
46+
47+
@Composable
48+
fun AiEpisodeRoute(
49+
navigateToMain: () -> Unit,
50+
viewModel: AiEpisodeViewModel = hiltViewModel(),
51+
) {
52+
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
53+
54+
LaunchedEffect(Unit) {
55+
viewModel.onIntent(AiEpisodeIntent.LoadMyGroups)
56+
}
57+
58+
LaunchedEffect(Unit) {
59+
viewModel.sideEffect.collect { sideEffect ->
60+
when (sideEffect) {
61+
is AiEpisodeSideEffect.NavigateToHome -> navigateToMain()
62+
is AiEpisodeSideEffect.ShowToast -> Unit
63+
}
64+
}
65+
}
66+
67+
if (uiState.showPhotoPicker) {
68+
MapisodePhotoPicker(
69+
numOfPhoto = 4,
70+
onPhotoSelected = { photos ->
71+
val uris = photos.map { it.uri }
72+
viewModel.onIntent(AiEpisodeIntent.SetImages(uris))
73+
viewModel.onIntent(AiEpisodeIntent.HidePhotoPicker)
74+
},
75+
onBackPressed = {
76+
viewModel.onIntent(AiEpisodeIntent.BackToHome)
77+
},
78+
onPermissionDenied = {
79+
80+
viewModel.onIntent(AiEpisodeIntent.BackToHome)
81+
},
82+
)
83+
} else {
84+
AiEpisodeScreen(
85+
myGroups = uiState.myGroups,
86+
selectedGroups = uiState.selectedGroups,
87+
addGroup = { viewModel.onIntent(AiEpisodeIntent.AddGroup(it)) },
88+
subtractGroup = { viewModel.onIntent(AiEpisodeIntent.SubtractGroup(it)) },
89+
addAllGroup = { viewModel.onIntent(AiEpisodeIntent.AddAllGroup) },
90+
clearGroup = { viewModel.onIntent(AiEpisodeIntent.ClearGroup) },
91+
aiText = uiState.aiText,
92+
onAiTextChange = { viewModel.onIntent(AiEpisodeIntent.SetAiText(it)) },
93+
onBackClicked = { viewModel.onIntent(AiEpisodeIntent.ShowPhotoPicker) },
94+
onBackToHome = { viewModel.onIntent(AiEpisodeIntent.BackToHome) },
95+
submitAiEpisode = { viewModel.onIntent(AiEpisodeIntent.SubmitAiEpisode) },
96+
)
97+
}
98+
}
99+
100+
@Composable
101+
internal fun AiEpisodeScreen(
102+
myGroups: List<GroupInfo>,
103+
selectedGroups: List<GroupInfo>,
104+
addGroup: (GroupInfo) -> Unit,
105+
subtractGroup: (GroupInfo) -> Unit,
106+
addAllGroup: () -> Unit,
107+
clearGroup: () -> Unit,
108+
aiText: String,
109+
onAiTextChange: (String) -> Unit,
110+
onBackClicked: () -> Unit,
111+
onBackToHome: () -> Unit,
112+
submitAiEpisode: () -> Unit,
113+
) {
114+
MapisodeScaffold(
115+
isNavigationBarPaddingExist = true,
116+
topBar = {
117+
AiEpisodeTopBar(
118+
onClickBack = onBackClicked,
119+
onClickClear = onBackToHome,
120+
)
121+
},
122+
) {
123+
Box(
124+
modifier = Modifier
125+
.fillMaxSize()
126+
.padding(it),
127+
contentAlignment = Alignment.Center,
128+
) {
129+
Column(
130+
modifier = Modifier.fillMaxWidth(0.9f),
131+
horizontalAlignment = Alignment.CenterHorizontally,
132+
) {
133+
Spacer(modifier = Modifier.weight(3f))
134+
135+
MapisodeText(
136+
text = "어디에 공유하시겠습니까?",
137+
style = MapisodeTheme.typography.titleLarge,
138+
)
139+
140+
Spacer(modifier = Modifier.height(28.dp))
141+
142+
LazyRow(
143+
modifier = Modifier
144+
.fillMaxWidth()
145+
.clip(RoundedCornerShape(8.dp))
146+
.border(
147+
border = BorderStroke(
148+
width = 4.dp,
149+
color = MapisodeTheme.colorScheme.chipSelectedStroke,
150+
),
151+
shape = RoundedCornerShape(8.dp),
152+
),
153+
contentPadding = PaddingValues(20.dp),
154+
horizontalArrangement = Arrangement.spacedBy(
155+
12.dp,
156+
Alignment.CenterHorizontally,
157+
),
158+
) {
159+
val mine = myGroups.first()
160+
val others = myGroups.drop(1)
161+
item() {
162+
GroupCard(
163+
group = mine,
164+
isSelected = selectedGroups.contains(mine),
165+
addGroup = {
166+
clearGroup()
167+
addGroup(it)
168+
},
169+
subtractGroup = subtractGroup,
170+
)
171+
}
172+
173+
items(others) { group ->
174+
GroupCard(
175+
group = group,
176+
isSelected = selectedGroups.contains(group),
177+
addGroup = {
178+
addGroup(it)
179+
subtractGroup(mine)
180+
},
181+
subtractGroup = subtractGroup,
182+
)
183+
}
184+
}
185+
186+
Spacer(modifier = Modifier.weight(2f))
187+
188+
MapisodeDivider(thickness = Thickness.Thin)
189+
190+
Spacer(modifier = Modifier.weight(2f))
191+
192+
MapisodeText(
193+
text = "무슨 일이 있었나요?",
194+
style = MapisodeTheme.typography.titleLarge,
195+
)
196+
197+
Spacer(modifier = Modifier.height(28.dp))
198+
199+
MapisodeTextField(
200+
value = aiText,
201+
onValueChange = onAiTextChange,
202+
)
203+
204+
Spacer(modifier = Modifier.weight(3f))
205+
206+
MapisodeFilledButton(
207+
text = "완료",
208+
onClick = submitAiEpisode,
209+
)
210+
211+
Spacer(modifier = Modifier.weight(1f))
212+
}
213+
}
214+
}
215+
}
216+
217+
@Composable
218+
fun GroupCard(
219+
group: GroupInfo,
220+
isSelected: Boolean,
221+
addGroup: (GroupInfo) -> Unit,
222+
subtractGroup: (GroupInfo) -> Unit,
223+
) {
224+
var border by rememberSaveable { mutableIntStateOf(-1) }
225+
226+
border = if (isSelected) 5 else 0
227+
val size = LocalConfiguration.current.screenWidthDp.dp / 4
228+
229+
Column(
230+
modifier = Modifier
231+
.clickable {
232+
if (isSelected) {
233+
subtractGroup(group)
234+
} else {
235+
addGroup(group)
236+
}
237+
}
238+
.wrapContentSize(),
239+
horizontalAlignment = Alignment.CenterHorizontally,
240+
) {
241+
AsyncImage(
242+
model = group.imageUri,
243+
contentDescription = group.name,
244+
modifier = Modifier
245+
.size(size)
246+
.aspectRatio(1f)
247+
.clip(RoundedCornerShape(8.dp))
248+
.border(
249+
border = BorderStroke(
250+
width = border.dp,
251+
color = MapisodeTheme.colorScheme.chipSelectedStroke,
252+
),
253+
shape = RoundedCornerShape(border.dp),
254+
),
255+
contentScale = ContentScale.Crop,
256+
)
257+
258+
Spacer(modifier = Modifier.height(8.dp))
259+
260+
MapisodeText(
261+
text = group.name,
262+
modifier = Modifier.width(size),
263+
textAlignment = TextAlignment.Center,
264+
maxLines = 2,
265+
)
266+
}
267+
}

0 commit comments

Comments
 (0)