Skip to content

Commit

Permalink
Add Image Preview Screen.
Browse files Browse the repository at this point in the history
  • Loading branch information
bywindow committed Aug 20, 2024
1 parent 413e9bd commit fee0c4b
Show file tree
Hide file tree
Showing 6 changed files with 280 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import com.goalpanzi.mission_mate.feature.board.screen.BoardFinishRoute
import com.goalpanzi.mission_mate.feature.board.screen.BoardMissionDetailRoute
import com.goalpanzi.mission_mate.feature.board.screen.BoardRoute
import com.goalpanzi.mission_mate.feature.board.screen.UserStoryScreen
import com.goalpanzi.mission_mate.feature.board.screen.VerificationPreviewRoute
import java.net.URLEncoder
import java.nio.charset.StandardCharsets

Expand All @@ -37,7 +38,8 @@ fun NavGraphBuilder.boardNavGraph(
onNavigateDetail : (Long) -> Unit,
onNavigateFinish : (Long) -> Unit,
onNavigateStory: (UserStory) -> Unit,
onClickSetting: () -> Unit
onClickSetting: () -> Unit,
onNavigateToPreview: (Long, String) -> Unit
) {
composable(
"RouteModel.Board/{$missionIdArg}",
Expand All @@ -53,7 +55,8 @@ fun NavGraphBuilder.boardNavGraph(
},
onNavigateFinish = onNavigateFinish,
onClickSetting = onClickSetting,
onClickStory = onNavigateStory
onClickStory = onNavigateStory,
onPreviewImage = onNavigateToPreview
)
}
}
Expand Down Expand Up @@ -148,3 +151,31 @@ fun NavGraphBuilder.userStoryNavGraph(
}
}
}

fun NavController.navigateToVerificationPreview(
missionId: Long,
imageUrl: String
) {
val encodedUrl = URLEncoder.encode(imageUrl, StandardCharsets.UTF_8.toString())
this.navigate("RouteModel.VerificationPreview" + "/${missionId}" +"/${encodedUrl}")
}

fun NavGraphBuilder.verificationPreviewNavGraph(
onClickClose: () -> Unit
) {
composable(
route = "RouteModel.VerificationPreview/{$missionIdArg}/{$imageUrlArg}",
arguments = listOf(
navArgument(missionIdArg) {
type = NavType.LongType
},
navArgument(imageUrlArg) {
type = NavType.StringType
}
)
) {
VerificationPreviewRoute(
onClickClose = onClickClose
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ fun BoardRoute(
onNavigateFinish : (Long) -> Unit,
onClickSetting: () -> Unit,
onClickStory: (UserStory) -> Unit,
onPreviewImage: (Long, String) -> Unit,
modifier: Modifier = Modifier,
viewModel: BoardViewModel = hiltViewModel()
) {
Expand All @@ -79,9 +80,10 @@ fun BoardRoute(
contract = ActivityResultContracts.PickVisualMedia(),
onResult = { imageUri ->
imageUri?.let { original ->
ImageCompressor.getCompressedImage(context, original).let { compressed ->
viewModel.verify(compressed)
}
onPreviewImage(viewModel.missionId, original.toString())
// ImageCompressor.getCompressedImage(context, original).let { compressed ->
// viewModel.verify(compressed)
// }
}
}
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
package com.goalpanzi.mission_mate.feature.board.screen

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.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.paint
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import coil.compose.AsyncImage
import coil.request.ImageRequest
import com.goalpanzi.mission_mate.core.designsystem.theme.ColorBlack_FF000000
import com.goalpanzi.mission_mate.core.designsystem.theme.ColorWhite_FFFFFFFF
import com.goalpanzi.mission_mate.core.designsystem.theme.MissionMateTypography
import java.net.URLDecoder
import java.nio.charset.StandardCharsets
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter

@Composable
fun VerificationPreviewRoute(
onClickClose: () -> Unit,
viewModel: VerificationPreviewViewModel = hiltViewModel()
) {

val uiState by viewModel.uiState.collectAsStateWithLifecycle()

VerificationPreviewScreen(
onClickClose = onClickClose,
uiState = uiState
)
}

@Composable
fun VerificationPreviewScreen(
onClickClose: () -> Unit,
uiState: VerificationPreviewUiState
) {
val dateTime = LocalDateTime.now()
val formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd")

Box(
modifier = Modifier
.fillMaxSize()
.background(ColorWhite_FFFFFFFF)
.navigationBarsPadding()
) {
Box(
modifier = Modifier
.fillMaxSize()
.statusBarsPadding()
) {
when (uiState) {
VerificationPreviewUiState.Loading -> VerificationPreviewLoading()
is VerificationPreviewUiState.Success -> {
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
.data(URLDecoder.decode(uiState.imageUrl, StandardCharsets.UTF_8.toString()))
.build(),
contentDescription = null,
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.FillBounds,
filterQuality = FilterQuality.None
)

Row(
modifier = Modifier
.fillMaxWidth()
.background(
brush = Brush.verticalGradient(
colors = listOf(
ColorBlack_FF000000.copy(alpha = 0.4f),
Color.Transparent
)
)
)
.height(93.dp)
.padding(horizontal = 24.dp, vertical = 14.dp)
) {
Image(
modifier = Modifier
.padding(top = 6.dp)
.size(28.dp)
.border(1.dp, ColorWhite_FFFFFFFF, CircleShape)
.paint(
painter = painterResource(uiState.character.backgroundId),
contentScale = ContentScale.FillWidth
)
.padding(5.dp),
painter = painterResource(uiState.character.imageId),
contentDescription = ""
)
Text(
text = uiState.nickname,
style = MissionMateTypography.body_xl_bold,
color = ColorWhite_FFFFFFFF,
modifier = Modifier
.padding(start = 8.dp)
.wrapContentWidth()
.padding(top = 6.dp)
)

Text(
text = dateTime.format(formatter),
style = MissionMateTypography.body_xl_regular,
color = ColorWhite_FFFFFFFF,
modifier = Modifier
.padding(start = 8.dp)
.weight(1f)
.padding(top = 6.dp)
)

IconButton(
onClick = onClickClose,
modifier = Modifier.wrapContentSize()
) {
Icon(
imageVector = ImageVector.vectorResource(id = com.goalpanzi.mission_mate.core.designsystem.R.drawable.ic_close),
contentDescription = "",
tint = ColorWhite_FFFFFFFF
)
}
}
}
}
}
}
}

@Composable
fun VerificationPreviewLoading() {
Box(
modifier = Modifier
.background(ColorWhite_FFFFFFFF)
.statusBarsPadding()
.navigationBarsPadding()
) {
CircularProgressIndicator(
modifier = Modifier.align(Alignment.Center)
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.goalpanzi.mission_mate.feature.board.screen

import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.goalpanzi.mission_mate.core.domain.usecase.ProfileUseCase
import com.goalpanzi.mission_mate.core.domain.usecase.VerifyMissionUseCase
import com.goalpanzi.mission_mate.feature.board.imageUrlArg
import com.goalpanzi.mission_mate.feature.board.model.Character
import com.luckyoct.core.model.UserProfile
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import javax.inject.Inject

@HiltViewModel
class VerificationPreviewViewModel @Inject constructor(
private val verifyMissionUseCase: VerifyMissionUseCase,
private val profileUseCase: ProfileUseCase,
savedStateHandle: SavedStateHandle
): ViewModel() {

private val _errorFlow = MutableSharedFlow<Throwable>()
val errorFlow = _errorFlow.asSharedFlow()

val uiState: StateFlow<VerificationPreviewUiState> = flow {
val profile = profileUseCase.getProfile() as UserProfile
emit(profile)
}.catch {
_errorFlow.emit(it)
}.map {
VerificationPreviewUiState.Success(
nickname = it.nickname,
character = Character.valueOf(it.characterType.name),
imageUrl = savedStateHandle.get<String>(imageUrlArg) ?: ""
)
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = VerificationPreviewUiState.Loading
)

}

sealed interface VerificationPreviewUiState {
data object Loading: VerificationPreviewUiState
data class Success(
val nickname: String,
val character: Character,
val imageUrl: String
): VerificationPreviewUiState
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import com.goalpanzi.mission_mate.feature.board.boardDetailNavGraph
import com.goalpanzi.mission_mate.feature.board.boardFinishNavGraph
import com.goalpanzi.mission_mate.feature.board.boardNavGraph
import com.goalpanzi.mission_mate.feature.board.userStoryNavGraph
import com.goalpanzi.mission_mate.feature.board.verificationPreviewNavGraph
import com.goalpanzi.mission_mate.feature.login.loginNavGraph
import com.goalpanzi.mission_mate.feature.onboarding.boardSetupNavGraph
import com.goalpanzi.mission_mate.feature.onboarding.boardSetupSuccessNavGraph
Expand Down Expand Up @@ -103,6 +104,9 @@ internal fun MainNavHost(
},
onNavigateStory = { userStory ->
navigator.navigationToUserStory(userStory)
},
onNavigateToPreview = { missionId, imageUrl ->
navigator.navigationToVerificationPreview(missionId, imageUrl)
}
)
boardDetailNavGraph(
Expand All @@ -126,6 +130,11 @@ internal fun MainNavHost(
navigator.popBackStack()
}
)
verificationPreviewNavGraph(
onClickClose = {
navigator.popBackStack()
}
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.goalpanzi.mission_mate.feature.board.navigateToBoard
import com.goalpanzi.mission_mate.feature.board.navigateToBoardDetail
import com.goalpanzi.mission_mate.feature.board.navigateToBoardFinish
import com.goalpanzi.mission_mate.feature.board.navigateToUserStory
import com.goalpanzi.mission_mate.feature.board.navigateToVerificationPreview
import com.goalpanzi.mission_mate.feature.login.navigateToLogin
import com.goalpanzi.mission_mate.feature.onboarding.navigateToBoardSetup
import com.goalpanzi.mission_mate.feature.onboarding.navigateToBoardSetupSuccess
Expand Down Expand Up @@ -86,6 +87,10 @@ class MainNavigator(
fun navigationToUserStory(userStory: UserStory) {
navController.navigateToUserStory(userStory)
}

fun navigationToVerificationPreview(missionId: Long, imageUrl : String) {
navController.navigateToVerificationPreview(missionId, imageUrl)
}
}

@Composable
Expand Down

0 comments on commit fee0c4b

Please sign in to comment.