diff --git a/feature/goal/src/main/kotlin/com/chipichipi/dobedobe/feature/goal/AddGoalScreen.kt b/feature/goal/src/main/kotlin/com/chipichipi/dobedobe/feature/goal/AddGoalScreen.kt index 9ab6328a..cc5f95eb 100644 --- a/feature/goal/src/main/kotlin/com/chipichipi/dobedobe/feature/goal/AddGoalScreen.kt +++ b/feature/goal/src/main/kotlin/com/chipichipi/dobedobe/feature/goal/AddGoalScreen.kt @@ -11,6 +11,7 @@ import androidx.compose.foundation.text.input.rememberTextFieldState import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester @@ -19,9 +20,11 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.LocalLifecycleOwner +import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.flowWithLifecycle import com.chipichipi.dobedobe.core.designsystem.component.DobeDobeBackground import com.chipichipi.dobedobe.core.designsystem.theme.DobeDobeTheme +import com.chipichipi.dobedobe.core.model.CharacterType import com.chipichipi.dobedobe.feature.goal.component.GoalEditor import com.chipichipi.dobedobe.feature.goal.component.GoalTopAppBar import kotlinx.coroutines.flow.launchIn @@ -38,6 +41,7 @@ fun AddGoalRoute( val lifecycle = LocalLifecycleOwner.current.lifecycle val focusManager = LocalFocusManager.current val focusRequester = remember { FocusRequester() } + val characterType: CharacterType by viewModel.characterType.collectAsStateWithLifecycle() val errorMessage = viewModel.goalValidResult.addGoalErrorMessage() ?.let { stringResource(id = it) } @@ -67,6 +71,7 @@ fun AddGoalRoute( } .imePadding(), errorMessage = errorMessage, + characterType = characterType, focusRequester = focusRequester, onShowSnackbar = onShowSnackbar, navigateToBack = navigateToBack, @@ -79,6 +84,7 @@ fun AddGoalRoute( private fun AddGoalScreen( errorMessage: String?, titleState: TextFieldState, + characterType: CharacterType, focusRequester: FocusRequester, onShowSnackbar: suspend (String, String?) -> Boolean, navigateToBack: () -> Unit, @@ -104,6 +110,7 @@ private fun AddGoalScreen( supportMessage = stringResource(id = R.string.feature_detail_goal_editor_support_message), buttonText = stringResource(id = R.string.feature_add_goal_action_button), errorMessage = errorMessage, + characterType = characterType, focusRequester = focusRequester, onDone = onAddGoal, ) @@ -120,6 +127,7 @@ private fun AddGoalScreenPreview() { modifier = Modifier .fillMaxSize(), titleState = textFieldState, + characterType = CharacterType.Rabbit, errorMessage = null, focusRequester = FocusRequester(), onShowSnackbar = { _, _ -> false }, diff --git a/feature/goal/src/main/kotlin/com/chipichipi/dobedobe/feature/goal/AddGoalViewModel.kt b/feature/goal/src/main/kotlin/com/chipichipi/dobedobe/feature/goal/AddGoalViewModel.kt index e6c8f195..90c3f060 100644 --- a/feature/goal/src/main/kotlin/com/chipichipi/dobedobe/feature/goal/AddGoalViewModel.kt +++ b/feature/goal/src/main/kotlin/com/chipichipi/dobedobe/feature/goal/AddGoalViewModel.kt @@ -6,18 +6,33 @@ import androidx.compose.runtime.getValue import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.chipichipi.dobedobe.core.data.repository.GoalRepository +import com.chipichipi.dobedobe.core.data.repository.UserRepository +import com.chipichipi.dobedobe.core.model.CharacterType import com.chipichipi.dobedobe.core.model.Goal import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.receiveAsFlow +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch class AddGoalViewModel( private val goalRepository: GoalRepository, + userRepository: UserRepository, ) : ViewModel() { val goalTitle = TextFieldState() val goalValidResult by derivedStateOf { goalTitle.text.toString().let(Goal::validateTitle) } + val characterType: StateFlow = userRepository.userData.map { + it.characterType + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5_000), + initialValue = CharacterType.Rabbit, + ) + private val _addGoalEvent = Channel(capacity = Channel.BUFFERED) val addGoalEvent: Flow = _addGoalEvent.receiveAsFlow() diff --git a/feature/goal/src/main/kotlin/com/chipichipi/dobedobe/feature/goal/EditGoalScreen.kt b/feature/goal/src/main/kotlin/com/chipichipi/dobedobe/feature/goal/EditGoalScreen.kt index 5ea64fd3..7ce9ef78 100644 --- a/feature/goal/src/main/kotlin/com/chipichipi/dobedobe/feature/goal/EditGoalScreen.kt +++ b/feature/goal/src/main/kotlin/com/chipichipi/dobedobe/feature/goal/EditGoalScreen.kt @@ -27,6 +27,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.flowWithLifecycle import com.chipichipi.dobedobe.core.designsystem.component.ThemePreviews import com.chipichipi.dobedobe.core.designsystem.theme.DobeDobeTheme +import com.chipichipi.dobedobe.core.model.CharacterType import com.chipichipi.dobedobe.core.model.Goal import com.chipichipi.dobedobe.feature.goal.component.GoalEditor import com.chipichipi.dobedobe.feature.goal.component.GoalTopAppBar @@ -124,6 +125,7 @@ private fun EditGoalScreen( .padding(horizontal = 24.dp) .padding(top = 8.dp, bottom = 12.dp), titleState = goalTitleDraft, + characterType = uiState.characterType, supportMessage = stringResource(id = R.string.feature_detail_goal_editor_support_message), buttonText = stringResource(id = R.string.feature_add_goal_action_button), errorMessage = errorMessage, @@ -140,7 +142,10 @@ private fun EditGoalScreen( private fun EditGoalScreenPreview() { DobeDobeTheme { EditGoalScreen( - uiState = EditGoalUiState.Success(Goal.todo("edit")), + uiState = EditGoalUiState.Success( + goal = Goal.todo("edit"), + characterType = CharacterType.Bird, + ), goalTitleDraft = rememberTextFieldState("edit"), errorMessage = null, saveGoalTitle = {}, diff --git a/feature/goal/src/main/kotlin/com/chipichipi/dobedobe/feature/goal/EditGoalViewModel.kt b/feature/goal/src/main/kotlin/com/chipichipi/dobedobe/feature/goal/EditGoalViewModel.kt index a8f3d1aa..e1bf6190 100644 --- a/feature/goal/src/main/kotlin/com/chipichipi/dobedobe/feature/goal/EditGoalViewModel.kt +++ b/feature/goal/src/main/kotlin/com/chipichipi/dobedobe/feature/goal/EditGoalViewModel.kt @@ -10,14 +10,16 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.navigation.toRoute import com.chipichipi.dobedobe.core.data.repository.GoalRepository +import com.chipichipi.dobedobe.core.data.repository.UserRepository +import com.chipichipi.dobedobe.core.model.CharacterType import com.chipichipi.dobedobe.core.model.Goal import com.chipichipi.dobedobe.feature.goal.navigation.GoalRoute import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filterNotNull -import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.stateIn @@ -26,6 +28,7 @@ import kotlinx.coroutines.launch internal class EditGoalViewModel( savedStateHandle: SavedStateHandle, private val goalRepository: GoalRepository, + userRepository: UserRepository, ) : ViewModel() { val goalTitleDraft = TextFieldState("") val goalValidResult by derivedStateOf { @@ -40,7 +43,12 @@ internal class EditGoalViewModel( } val uiState: StateFlow = savedStateHandle.getGoalFlow() - .map(EditGoalUiState::Success) + .combine(userRepository.userData) { goal, user -> + EditGoalUiState.Success( + goal = goal, + characterType = user.characterType, + ) + } .onEach { goalTitleDraft.edit { delete(0, length) @@ -93,6 +101,7 @@ sealed interface EditGoalUiState { data class Success( val goal: Goal, + val characterType: CharacterType, ) : EditGoalUiState data object Error : EditGoalUiState diff --git a/feature/goal/src/main/kotlin/com/chipichipi/dobedobe/feature/goal/component/GoalEditor.kt b/feature/goal/src/main/kotlin/com/chipichipi/dobedobe/feature/goal/component/GoalEditor.kt index a2d6e463..eac7221c 100644 --- a/feature/goal/src/main/kotlin/com/chipichipi/dobedobe/feature/goal/component/GoalEditor.kt +++ b/feature/goal/src/main/kotlin/com/chipichipi/dobedobe/feature/goal/component/GoalEditor.kt @@ -2,7 +2,6 @@ package com.chipichipi.dobedobe.feature.goal.component import androidx.compose.foundation.Image import androidx.compose.foundation.background -import androidx.compose.foundation.border import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -15,14 +14,12 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.requiredSizeIn import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width -import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.text.input.TextFieldState import androidx.compose.foundation.text.input.rememberTextFieldState import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color @@ -38,6 +35,7 @@ import com.chipichipi.dobedobe.core.designsystem.component.DobeDobeTextButton import com.chipichipi.dobedobe.core.designsystem.component.DobeDobeTextField import com.chipichipi.dobedobe.core.designsystem.component.TailPosition import com.chipichipi.dobedobe.core.designsystem.theme.DobeDobeTheme +import com.chipichipi.dobedobe.core.model.CharacterType import com.chipichipi.dobedobe.feature.goal.R @Composable @@ -47,6 +45,7 @@ fun GoalEditor( modifier: Modifier = Modifier, supportMessage: String = "", errorMessage: String? = null, + characterType: CharacterType, focusRequester: FocusRequester, onDone: (() -> Unit)? = null, ) { @@ -55,14 +54,9 @@ fun GoalEditor( horizontalAlignment = Alignment.CenterHorizontally, ) { Image( - painter = painterResource(id = R.drawable.rabit_avatar), + painter = painterResource(id = characterType.toImageRes()), contentDescription = "Avatar", - modifier = Modifier - .size(84.dp) - .border(1.dp, Color(0xFFFFA8E7), CircleShape) - .padding(1.dp) - .clip(CircleShape) - .background(Color(0xFFFFF2FF)), + modifier = Modifier.size(80.dp), contentScale = ContentScale.Crop, ) @@ -115,6 +109,11 @@ fun GoalEditor( } } +private fun CharacterType.toImageRes(): Int = when (this) { + CharacterType.Bird -> R.drawable.bird_avatar + CharacterType.Rabbit -> R.drawable.rabbit_avatar +} + @Preview @Composable private fun GoalDetailEmptyPreview() { @@ -128,6 +127,7 @@ private fun GoalDetailEmptyPreview() { .imePadding(), supportMessage = stringResource(R.string.feature_detail_goal_editor_support_message), titleState = titleState, + characterType = CharacterType.Rabbit, focusRequester = FocusRequester(), buttonText = "Done", ) @@ -148,6 +148,7 @@ private fun GoalDetailPreview() { .imePadding(), supportMessage = stringResource(R.string.feature_detail_goal_editor_support_message), titleState = titleState, + characterType = CharacterType.Bird, focusRequester = FocusRequester(), buttonText = "Done", ) diff --git a/feature/goal/src/main/res/drawable/bird_avatar.png b/feature/goal/src/main/res/drawable/bird_avatar.png new file mode 100644 index 00000000..6bfeae63 Binary files /dev/null and b/feature/goal/src/main/res/drawable/bird_avatar.png differ diff --git a/feature/goal/src/main/res/drawable/rabbit_avatar.png b/feature/goal/src/main/res/drawable/rabbit_avatar.png new file mode 100644 index 00000000..35972b9f Binary files /dev/null and b/feature/goal/src/main/res/drawable/rabbit_avatar.png differ diff --git a/feature/goal/src/main/res/drawable/rabit_avatar.png b/feature/goal/src/main/res/drawable/rabit_avatar.png deleted file mode 100644 index ee625845..00000000 Binary files a/feature/goal/src/main/res/drawable/rabit_avatar.png and /dev/null differ