Skip to content

Commit

Permalink
refactor/#16 : 내용 입력 화면 수정
Browse files Browse the repository at this point in the history
- 이미지 + TextField 의 텍스트 기반 AI 추천
- AI 추천 내용을 TextField 에 이어 붙이는 기능 추가
  • Loading branch information
TaewoongR committed Feb 18, 2025
1 parent d5e697e commit e2807ed
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,17 @@ class EpisodeViewModel @Inject constructor(
copy(
selectedEpisode = "",
selfTypedEpisode = event.selfTypedEpisode,
isEpisodeSelected = currentState.selfTypedEpisode.isNotBlank(),
isEpisodeSelected = event.selfTypedEpisode.isNotBlank(),
)
}
}

EpisodeIntent.OnCompleteInfoPick -> {
sendState { copy(isLoading = true) }
delay(2000L)
sendEffect { EpisodeEffect.ShowToast("에피소드가 업로드 되었습니다.") }
sendEffect { EpisodeEffect.NavigateBackToHomeScreen }
}
}
}
}
Expand Down Expand Up @@ -174,11 +181,12 @@ class EpisodeViewModel @Inject constructor(
}

private fun dealWithLLM() {
viewModelScope.launch(Dispatchers.IO) {
viewModelScope.launch {
sendState {
copy(
selectedEpisode = "",
isLoading = true,
isEpisodeSelected = false,
)
}
if (currentState.imageCaption.isNotBlank()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.content.Context
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
Expand All @@ -21,6 +22,8 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
Expand All @@ -32,10 +35,14 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import coil3.compose.AsyncImage
import coil3.request.ImageRequest
import coil3.request.crossfade
import com.boostcamp.mapisode.designsystem.R
import com.boostcamp.mapisode.designsystem.compose.MapisodeCircularLoadingIndicator
import com.boostcamp.mapisode.designsystem.compose.MapisodeIconButton
Expand All @@ -45,6 +52,7 @@ import com.boostcamp.mapisode.designsystem.compose.button.MapisodeFilledButton
import com.boostcamp.mapisode.designsystem.compose.button.MapisodeOutlinedButton
import com.boostcamp.mapisode.designsystem.theme.MapisodeTheme
import com.boostcamp.mapisode.episode.EpisodeViewModel
import com.boostcamp.mapisode.episode.state.EpisodeEffect
import com.boostcamp.mapisode.episode.state.EpisodeIntent
import com.boostcamp.mapisode.episode.state.EpisodeState

Expand All @@ -57,6 +65,15 @@ fun EpisodeContentRoute(
val uiState = viewModel.state.collectAsStateWithLifecycle().value
val context = LocalContext.current

LaunchedEffect(Unit) {
viewModel.effect.collect {
when (it) {
is EpisodeEffect.NavigateToPreviousScreen -> onBackClick()
is EpisodeEffect.NavigateBackToHomeScreen -> onCompleteContentClick()
}
}
}

EpisodeContentScreen(
uiState = uiState,
context = context,
Expand All @@ -66,8 +83,8 @@ fun EpisodeContentRoute(
viewModel.sendIntent(EpisodeIntent.OnSelectEpisodeClick(generatedEpisode))
},
onSelfTypedEpisodeChange = { viewModel.sendIntent(EpisodeIntent.OnSelfTypedEpisodeChange(it)) },
onCompleteInfoPick = onCompleteContentClick,
onBackClick = onBackClick,
onCompleteInfoPick = { viewModel.sendIntent(EpisodeIntent.OnCompleteInfoPick) },
onBackClick = { viewModel.sendIntent(EpisodeIntent.OnBackClick) },
)

if (uiState.isLoading) {
Expand Down Expand Up @@ -127,7 +144,36 @@ fun EpisodeContentScreen(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Top,
) {
Spacer(modifier = Modifier.height(24.dp))
Box(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.Center,
) {
Spacer(modifier = Modifier.height(16.dp))

Row(
modifier = Modifier
.wrapContentWidth()
.horizontalScroll(rememberScrollState()),
horizontalArrangement = Arrangement.spacedBy(10.dp),
verticalAlignment = Alignment.CenterVertically,
) {
uiState.imageUrls.forEach { imageUrl ->
AsyncImage(
model = ImageRequest.Builder(context)
.data(imageUrl)
.crossfade(true)
.build(),
contentDescription = "애피소드 이미지",
modifier = Modifier
.size(140.dp)
.clip(RoundedCornerShape(16.dp)),
contentScale = ContentScale.Crop,
)
}
}
}

Spacer(modifier = Modifier.height(16.dp))

MapisodeTextField(
value = valueChanged,
Expand All @@ -140,30 +186,6 @@ fun EpisodeContentScreen(

Spacer(modifier = Modifier.height(16.dp))

MapisodeIconButton(
onClick = { onSelfTypedEpisodeChange(valueChanged) },
modifier = Modifier
.width(320.dp)
.height(40.dp)
.clip(
RoundedCornerShape(8.dp),
),
backgroundColor = MapisodeTheme.colorScheme.filledButtonEnableBackground,
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
MapisodeText(
text = "직접 작성하기",
style = MapisodeTheme.typography.labelLarge,
color = MapisodeTheme.colorScheme.filledButtonContent,
)
}
}

Spacer(modifier = Modifier.height(16.dp))

MapisodeIconButton(
onClick = onGenerateLLMClick,
modifier = Modifier
Expand All @@ -181,6 +203,7 @@ fun EpisodeContentScreen(
Image(
painter = painterResource(id = R.drawable.ic_generative_ai_star),
contentDescription = "Generate",
modifier = Modifier.size(16.dp),
)
MapisodeText(
text = "AI 추천받기",
Expand All @@ -192,50 +215,30 @@ fun EpisodeContentScreen(

Spacer(modifier = Modifier.height(16.dp))

MapisodeText(
text = "AI 추천 내용",
modifier = Modifier.fillMaxWidth(),
style = MapisodeTheme.typography.labelLarge,
)

Spacer(modifier = Modifier.height(4.dp))

repeat(maxOf(3, uiState.generatedEpisodes.size)) { index ->
val generatedEpisode = uiState.generatedEpisodes.getOrNull(index) ?: ""
MapisodeOutlinedButton(
text = generatedEpisode,
onClick = {
if (generatedEpisode.isNotBlank()) {
onSelectEpisodeClick(
generatedEpisode,
)
}
},
borderColor = if (generatedEpisode == uiState.selectedEpisode && generatedEpisode.isNotBlank()) MapisodeTheme.colorScheme.chipSelectedStroke else MapisodeTheme.colorScheme.chipUnselectedStroke,
contentColor = if (generatedEpisode == uiState.selectedEpisode && generatedEpisode.isNotBlank()) MapisodeTheme.colorScheme.chipSelectedStroke else MapisodeTheme.colorScheme.chipUnselectedStroke,
if (uiState.generatedEpisodes.isNotEmpty()) {
MapisodeText(
text = "AI 추천 내용",
modifier = Modifier.fillMaxWidth(),
style = MapisodeTheme.typography.labelLarge,
)
Spacer(modifier = Modifier.height(8.dp))
}

Spacer(modifier = Modifier.height(8.dp))

MapisodeText(
text = "작성 내용",
modifier = Modifier.fillMaxWidth(),
style = MapisodeTheme.typography.labelLarge,
)

Spacer(modifier = Modifier.height(4.dp))
Spacer(modifier = Modifier.height(4.dp))

MapisodeOutlinedButton(
text = uiState.selfTypedEpisode,
onClick = {},
enabled = false,
borderColor = if (uiState.selfTypedEpisode.isNotBlank()) MapisodeTheme.colorScheme.chipSelectedStroke else MapisodeTheme.colorScheme.chipUnselectedStroke,
contentColor = if (uiState.selfTypedEpisode.isNotBlank()) MapisodeTheme.colorScheme.chipSelectedStroke else MapisodeTheme.colorScheme.chipUnselectedStroke,
)

Spacer(modifier = Modifier.height(4.dp))
repeat(maxOf(3, uiState.generatedEpisodes.size)) { index ->
val generatedEpisode = uiState.generatedEpisodes.getOrNull(index) ?: ""
MapisodeOutlinedButton(
text = generatedEpisode,
onClick = {
if (generatedEpisode.isNotBlank()) {
valueChanged += " $generatedEpisode"
}
},
borderColor = MapisodeTheme.colorScheme.chipUnselectedStroke,
contentColor = MapisodeTheme.colorScheme.chipUnselectedStroke,
)
Spacer(modifier = Modifier.height(8.dp))
}
}
}

Column(
Expand All @@ -249,7 +252,7 @@ fun EpisodeContentScreen(
onClick = {
onCompleteInfoPick()
},
text = if (uiState.isEpisodeSelected) "완료" else "내용을 선택해주세요",
text = if (uiState.isEpisodeSelected) "완료" else "내용을 입력해주세요",
enabled = uiState.isEpisodeSelected,
showRipple = true,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,7 @@ sealed class EpisodeEffect : SideEffect {

// 그룹 선택
data object NavigateToContentScreen : EpisodeEffect()

// 내용 입력
data object NavigateBackToHomeScreen : EpisodeEffect()
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ sealed class EpisodeIntent : UiIntent {
data object OnGenerateLLMClick : EpisodeIntent()
data class OnSelectEpisodeClick(val generatedEpisode: String) : EpisodeIntent()
data class OnSelfTypedEpisodeChange(val selfTypedEpisode: String) : EpisodeIntent()
data object OnCompleteInfoPick : EpisodeIntent()
}

0 comments on commit e2807ed

Please sign in to comment.