From d53c961ac22d2c5360d9bb6120b401e39bcdaa8e Mon Sep 17 00:00:00 2001 From: eshc123 <> Date: Tue, 29 Oct 2024 22:31:27 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20memberId=20=ED=95=84=EB=93=9C?= =?UTF-8?q?=EA=B0=92=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../goalpanzi/mission_mate/core/board/mapper/MissionMapper.kt | 1 + .../core/domain/mission/model/MissionBoardMembers.kt | 1 + .../core/network/model/response/MissionBoardMembersResponse.kt | 2 +- .../goalpanzi/mission_mate/feature/board/model/BoardPiece.kt | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/core/data/mission/src/main/java/com/goalpanzi/mission_mate/core/board/mapper/MissionMapper.kt b/core/data/mission/src/main/java/com/goalpanzi/mission_mate/core/board/mapper/MissionMapper.kt index 832cfb1a..31dacee6 100644 --- a/core/data/mission/src/main/java/com/goalpanzi/mission_mate/core/board/mapper/MissionMapper.kt +++ b/core/data/mission/src/main/java/com/goalpanzi/mission_mate/core/board/mapper/MissionMapper.kt @@ -43,6 +43,7 @@ fun MissionBoardResponse.toModel() : MissionBoard { fun MissionBoardMembersResponse.toModel() : MissionBoardMembers { return MissionBoardMembers( + memberId = memberId, nickname = nickname, characterType = characterType.toModel() ) diff --git a/core/domain/mission/src/main/java/com/goalpanzi/mission_mate/core/domain/mission/model/MissionBoardMembers.kt b/core/domain/mission/src/main/java/com/goalpanzi/mission_mate/core/domain/mission/model/MissionBoardMembers.kt index 4883a4cd..399e22fb 100644 --- a/core/domain/mission/src/main/java/com/goalpanzi/mission_mate/core/domain/mission/model/MissionBoardMembers.kt +++ b/core/domain/mission/src/main/java/com/goalpanzi/mission_mate/core/domain/mission/model/MissionBoardMembers.kt @@ -3,6 +3,7 @@ package com.goalpanzi.mission_mate.core.domain.mission.model import com.goalpanzi.mission_mate.core.domain.common.model.user.CharacterType data class MissionBoardMembers( + val memberId : Long, val nickname : String, val characterType : CharacterType = CharacterType.RABBIT ) diff --git a/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/model/response/MissionBoardMembersResponse.kt b/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/model/response/MissionBoardMembersResponse.kt index 6183efe5..a673c1af 100644 --- a/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/model/response/MissionBoardMembersResponse.kt +++ b/core/network/src/main/java/com/goalpanzi/mission_mate/core/network/model/response/MissionBoardMembersResponse.kt @@ -4,7 +4,7 @@ import kotlinx.serialization.Serializable @Serializable data class MissionBoardMembersResponse( - //val memberId : Long, + val memberId : Long, val nickname : String, val characterType : CharacterTypeResponse = CharacterTypeResponse.RABBIT ) diff --git a/feature/board/src/main/java/com/goalpanzi/mission_mate/feature/board/model/BoardPiece.kt b/feature/board/src/main/java/com/goalpanzi/mission_mate/feature/board/model/BoardPiece.kt index 2c5baddf..f47f3b7e 100644 --- a/feature/board/src/main/java/com/goalpanzi/mission_mate/feature/board/model/BoardPiece.kt +++ b/feature/board/src/main/java/com/goalpanzi/mission_mate/feature/board/model/BoardPiece.kt @@ -1,6 +1,7 @@ package com.goalpanzi.mission_mate.feature.board.model data class BoardPiece( + val memberId : Long, val index : Int, val count : Int, val nickname : String, From d2ffe26ed61a9c14619ba55155a3695e18ce6d3c Mon Sep 17 00:00:00 2001 From: eshc123 <> Date: Tue, 29 Oct 2024 22:37:53 +0900 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20PieceManager=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feature/board/util/PieceManager.kt | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 feature/board/src/main/java/com/goalpanzi/mission_mate/feature/board/util/PieceManager.kt diff --git a/feature/board/src/main/java/com/goalpanzi/mission_mate/feature/board/util/PieceManager.kt b/feature/board/src/main/java/com/goalpanzi/mission_mate/feature/board/util/PieceManager.kt new file mode 100644 index 00000000..51cc191a --- /dev/null +++ b/feature/board/src/main/java/com/goalpanzi/mission_mate/feature/board/util/PieceManager.kt @@ -0,0 +1,56 @@ +package com.goalpanzi.mission_mate.feature.board.util + +import com.goalpanzi.mission_mate.feature.board.model.BoardPiece +import com.goalpanzi.mission_mate.feature.board.model.BoardPieceType +import com.goalpanzi.mission_mate.feature.board.model.MotionType + +object PieceManager { + fun getBoardPieces( + prevBoardPieces: List, + newBoardPieces: List + ): List { + val result = newBoardPieces.map { new -> + val prevBoardPiece = prevBoardPieces.find { it.memberId == new.memberId } ?: new + val sameIndex = prevBoardPiece.index == new.index + + val motionType = when { + sameIndex -> when (prevBoardPiece.boardPieceType to new.boardPieceType) { + BoardPieceType.INITIAL to BoardPieceType.INITIAL -> MotionType.STATIC + BoardPieceType.INITIAL to BoardPieceType.HIDDEN -> MotionType.FADE_OUT + BoardPieceType.HIDDEN to BoardPieceType.INITIAL -> MotionType.FADE_IN + BoardPieceType.HIDDEN to BoardPieceType.HIDDEN -> MotionType.HIDE + else -> new.motionType + } + else -> when (prevBoardPiece.boardPieceType to new.boardPieceType) { + BoardPieceType.INITIAL to BoardPieceType.INITIAL -> MotionType.MOVE + BoardPieceType.INITIAL to BoardPieceType.HIDDEN -> MotionType.MOVE_FADE_OUT + BoardPieceType.HIDDEN to BoardPieceType.INITIAL -> MotionType.MOVE_FADE_IN + BoardPieceType.HIDDEN to BoardPieceType.HIDDEN -> MotionType.FADE_IN_MOVE_FADE_OUT + else -> new.motionType + } + } + new.copy(motionType = motionType) + } + + return result.map { item -> + if (item.motionType != MotionType.FADE_IN_MOVE_FADE_OUT) return@map item + val list = item.piecesWithAnimation(result) + item.copy( + motionType = if (list.isEmpty() || list.lastOrNull()?.memberId == item.memberId) { + item.motionType + } else { + MotionType.HIDE + } + ) + } + } + + private fun BoardPiece.piecesWithAnimation(targetList: List): List { + return targetList.filter { + it.index == this.index && it.motionType in setOf( + MotionType.FADE_IN_MOVE_FADE_OUT, + MotionType.MOVE + ) + } + } +} From feffe16ac925ca17f4010e4ba1099dfe96c7ede7 Mon Sep 17 00:00:00 2001 From: eshc123 <> Date: Tue, 29 Oct 2024 22:38:22 +0900 Subject: [PATCH 3/3] =?UTF-8?q?feat:=20Board=20&=20Piece=20=EC=95=A0?= =?UTF-8?q?=EB=8B=88=EB=A9=94=EC=9D=B4=EC=85=98=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feature/board/component/Board.kt | 66 ++++--- .../feature/board/component/Piece.kt | 171 +++++++++++++----- .../feature/board/model/BoardPiece.kt | 23 ++- .../board/model/MissionBoardsUiModel.kt | 28 +-- .../feature/board/screen/BoardScreen.kt | 6 - .../feature/board/screen/BoardViewModel.kt | 125 ++++--------- 6 files changed, 231 insertions(+), 188 deletions(-) diff --git a/feature/board/src/main/java/com/goalpanzi/mission_mate/feature/board/component/Board.kt b/feature/board/src/main/java/com/goalpanzi/mission_mate/feature/board/component/Board.kt index 610e734b..acb3cf22 100644 --- a/feature/board/src/main/java/com/goalpanzi/mission_mate/feature/board/component/Board.kt +++ b/feature/board/src/main/java/com/goalpanzi/mission_mate/feature/board/component/Board.kt @@ -2,8 +2,8 @@ package com.goalpanzi.mission_mate.feature.board.component import android.annotation.SuppressLint import androidx.compose.animation.core.animateDpAsState -import androidx.compose.animation.core.tween import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.LocalOverscrollConfiguration import androidx.compose.foundation.ScrollState import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxWithConstraints @@ -19,7 +19,6 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.statusBars import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.verticalScroll @@ -29,6 +28,7 @@ import androidx.compose.material.pullrefresh.PullRefreshState import androidx.compose.material.pullrefresh.pullRefresh import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue @@ -102,7 +102,7 @@ fun Board( LaunchedEffect(myIndex) { scrollState.animateScrollTo( getPositionScrollToMyIndex( - myIndex = myIndex, + myIndex = if(myIndex in 3 .. 5) myIndex - 3 else myIndex, numberOfColumns = numberOfColumns, blockSize = (configuration.screenWidthDp - 48) / numberOfColumns, localDensity = localDensity @@ -176,33 +176,39 @@ fun Board( modifier ) } + if (isVisiblePieces) { - Column( - modifier = modifier.modifierWithClipRect( - scrollState = scrollState, - isVisiblePieces = isVisiblePieces, - innerModifier = Modifier - .drawWithContent { - clipRect(top = (size.height + navigationBarHeight.toPx() - bottomViewHeight.toPx() + 1.dp.toPx())) { - this@drawWithContent.drawContent() - } - } - .blur(10.dp, 10.dp) - ) - ) { - Spacer(modifier = Modifier.height(refreshingSpacerSize)) - BoardContent( - missionBoards, - missionDetail, - numberOfColumns, - boardPieces, - profile, - missionState, - isVisiblePieces = isVisiblePieces, - onClickPassedBlock = onClickPassedBlock, - modifier - ) - } + CompositionLocalProvider( + LocalOverscrollConfiguration provides null, + content = { + Column( + modifier = modifier.modifierWithClipRect( + scrollState = scrollState, + isVisiblePieces = isVisiblePieces, + innerModifier = Modifier + .drawWithContent { + clipRect(top = (size.height + navigationBarHeight.toPx() - bottomViewHeight.toPx() + 1.dp.toPx())) { + this@drawWithContent.drawContent() + } + } + .blur(10.dp, 10.dp) + ) + ) { + Spacer(modifier = Modifier.height(refreshingSpacerSize)) + BoardContent( + missionBoards, + missionDetail, + numberOfColumns, + boardPieces, + profile, + missionState, + isVisiblePieces = isVisiblePieces, + onClickPassedBlock = onClickPassedBlock, + modifier + ) + } + } + ) } PullRefreshIndicator( refreshing = isRefreshLoading, @@ -285,7 +291,7 @@ fun ColumnScope.BoardContent( } if (isVisiblePieces) { boardPieces.forEach { piece -> - key(piece.nickname) { + key(piece.memberId,piece.needToDraw) { Piece( boardPiece = piece, sizePerBlock = width / numberOfColumns, diff --git a/feature/board/src/main/java/com/goalpanzi/mission_mate/feature/board/component/Piece.kt b/feature/board/src/main/java/com/goalpanzi/mission_mate/feature/board/component/Piece.kt index f06ad432..d37bf6f6 100644 --- a/feature/board/src/main/java/com/goalpanzi/mission_mate/feature/board/component/Piece.kt +++ b/feature/board/src/main/java/com/goalpanzi/mission_mate/feature/board/component/Piece.kt @@ -1,8 +1,14 @@ package com.goalpanzi.mission_mate.feature.board.component +import android.util.Log import androidx.annotation.DrawableRes +import androidx.compose.animation.core.Animatable +import androidx.compose.animation.core.AnimationSpec +import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.LinearOutSlowInEasing import androidx.compose.animation.core.animateDpAsState +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.keyframes import androidx.compose.animation.core.tween import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement @@ -12,18 +18,23 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.absoluteOffset import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue +import androidx.compose.runtime.produceState import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip +import androidx.compose.ui.geometry.Offset import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.Dp @@ -37,6 +48,8 @@ import com.goalpanzi.mission_mate.feature.board.model.BoardPieceType import com.goalpanzi.mission_mate.feature.board.util.PieceGenerator import com.goalpanzi.mission_mate.core.designsystem.component.OutlinedChip import com.goalpanzi.mission_mate.core.designsystem.component.StableImage +import com.goalpanzi.mission_mate.feature.board.model.MotionType +import kotlinx.coroutines.delay @Composable fun BoxScope.Piece( @@ -45,28 +58,38 @@ fun BoxScope.Piece( sizePerBlock: Dp, modifier: Modifier = Modifier ) { - val isAnimated by remember(boardPiece) { - derivedStateOf { boardPiece.boardPieceType == BoardPieceType.MOVED } + val isAnimated by remember { + derivedStateOf { + boardPiece.motionType in setOf( + MotionType.MOVE_FADE_IN, + MotionType.MOVE, + MotionType.MOVE_FADE_OUT, + MotionType.FADE_IN_MOVE_FADE_OUT + ) + } } + + val alpha = animatedFloatByMotionType(boardPiece.motionType, 600) + val x = animateDpAsState( targetValue = if (isAnimated) PieceGenerator.getXOffset( - boardPiece.index + 1, + boardPiece.index, numberOfColumn, sizePerBlock - ) else PieceGenerator.getXOffset(boardPiece.index , numberOfColumn, sizePerBlock), + ) else PieceGenerator.getXOffset(boardPiece.index, numberOfColumn, sizePerBlock), animationSpec = tween( - durationMillis = 500, + durationMillis = 650, easing = LinearOutSlowInEasing ) ) val y = animateDpAsState( targetValue = if (isAnimated) PieceGenerator.getYOffset( - boardPiece.index + 1, + boardPiece.index, numberOfColumn, sizePerBlock - ) else PieceGenerator.getYOffset(boardPiece.index , numberOfColumn, sizePerBlock), + ) else PieceGenerator.getYOffset(boardPiece.index, numberOfColumn, sizePerBlock), animationSpec = tween( - durationMillis = 500, + durationMillis = 650, easing = LinearOutSlowInEasing ) ) @@ -82,45 +105,108 @@ fun BoxScope.Piece( sizePerBlock ) ) { - if(boardPiece.boardPieceType != BoardPieceType.HIDDEN){ - StableImage( - modifier = Modifier - .padding(top = 4.dp) - .fillMaxWidth(88f / 114f) - .aspectRatio(1f) - .align(Alignment.TopCenter), - drawableResId = boardPiece.drawableRes, + if (boardPiece.boardPieceType == BoardPieceType.HIDDEN && boardPiece.motionType in setOf(MotionType.HIDE,MotionType.STATIC)) return + StableImage( + modifier = Modifier + .padding(top = 4.dp) + .fillMaxWidth(88f / 114f) + .alpha(alpha) + .aspectRatio(1f) + .align(Alignment.TopCenter), + drawableResId = boardPiece.drawableRes, + ) + if (boardPiece.count > 1 && boardPiece.boardPieceType != BoardPieceType.HIDDEN) { + PieceCountChip( + modifier = Modifier.align(Alignment.TopEnd), + count = boardPiece.count, + imageId = boardPiece.drawableRes ) - if(boardPiece.count > 1){ - PieceCountChip( - modifier = Modifier.align(Alignment.TopEnd), - count = boardPiece.count, - imageId = boardPiece.drawableRes + } + if(boardPiece.boardPieceType != BoardPieceType.HIDDEN) + PieceNameChip( + modifier = Modifier + .align( + Alignment.BottomCenter ) + .padding(bottom = 7.dp), + name = boardPiece.nickname, + isMe = boardPiece.isMe + ) + } +} + + +@Composable +fun animatedFloatByMotionType( + motionType: MotionType, + durationMillis: Int +): Float { + val animatable = remember { Animatable(1f) } + + LaunchedEffect(motionType) { + when (motionType) { + MotionType.STATIC, MotionType.MOVE, MotionType.HIDE -> { + animatable.snapTo(if (motionType == MotionType.HIDE) 0f else 1f) } - PieceNameChip( - modifier = Modifier - .align( - Alignment.BottomCenter + MotionType.FADE_IN, MotionType.MOVE_FADE_IN -> { + animatable.animateTo( + targetValue = 1f, + animationSpec = tween( + durationMillis = durationMillis * 1 / 5, + easing = LinearEasing ) - .padding(bottom = 7.dp), - name = boardPiece.nickname, - isMe = boardPiece.isMe - ) + ) + animatable.animateTo( + targetValue = 1f, + animationSpec = tween(durationMillis = durationMillis * 4 / 5) + ) + } + MotionType.FADE_OUT, MotionType.MOVE_FADE_OUT -> { + animatable.animateTo( + targetValue = 1f, + animationSpec = tween(durationMillis = durationMillis * 3 / 5) + ) + animatable.animateTo( + targetValue = 0f, + animationSpec = tween( + durationMillis = durationMillis * 2 / 5, + easing = LinearEasing + ) + ) + } + MotionType.FADE_IN_MOVE_FADE_OUT -> { + animatable.animateTo( + targetValue = 1f, + animationSpec = tween( + durationMillis = durationMillis * 1 / 5, + easing = LinearEasing + ) + ) + animatable.animateTo( + targetValue = 1f, + animationSpec = tween(durationMillis = durationMillis * 3 / 5) + ) + animatable.animateTo( + targetValue = 0f, + animationSpec = tween( + durationMillis = durationMillis * 1 / 5, + easing = LinearEasing + ) + ) + } } - } - + return animatable.value } @Composable fun PieceNameChip( - name : String, + name: String, modifier: Modifier = Modifier, - isMe : Boolean = false, - textStyle : TextStyle = MissionMateTypography.body_md_bold -){ + isMe: Boolean = false, + textStyle: TextStyle = MissionMateTypography.body_md_bold +) { Text( modifier = modifier .padding(horizontal = 12.dp) @@ -130,11 +216,10 @@ fun PieceNameChip( .background( if (isMe) ColorOrange_FFFF5732 else ColorWhite_FFFFFFFF ) - .padding(horizontal = 8.5.dp, 0.85.dp) - , + .padding(horizontal = 8.5.dp, 0.85.dp), text = name, style = textStyle, - color = if(isMe) ColorWhite_FFFFFFFF else ColorGray1_FF404249, + color = if (isMe) ColorWhite_FFFFFFFF else ColorGray1_FF404249, textAlign = TextAlign.Center ) } @@ -142,17 +227,17 @@ fun PieceNameChip( @Composable fun PieceCountChip( @DrawableRes imageId: Int, - count : Int, + count: Int, modifier: Modifier = Modifier, - textStyle : TextStyle = MissionMateTypography.body_md_bold -){ + textStyle: TextStyle = MissionMateTypography.body_md_bold +) { OutlinedChip( modifier = modifier - ){ + ) { Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(5.dp) - ){ + ) { StableImage( modifier = Modifier.size(22.dp), drawableResId = imageId, diff --git a/feature/board/src/main/java/com/goalpanzi/mission_mate/feature/board/model/BoardPiece.kt b/feature/board/src/main/java/com/goalpanzi/mission_mate/feature/board/model/BoardPiece.kt index f47f3b7e..9f78676c 100644 --- a/feature/board/src/main/java/com/goalpanzi/mission_mate/feature/board/model/BoardPiece.kt +++ b/feature/board/src/main/java/com/goalpanzi/mission_mate/feature/board/model/BoardPiece.kt @@ -7,12 +7,25 @@ data class BoardPiece( val nickname : String, val isMe : Boolean, val drawableRes: Int, - val boardPieceType: BoardPieceType = BoardPieceType.INITIAL -) + val boardPieceType: BoardPieceType = BoardPieceType.INITIAL, + val motionType : MotionType = MotionType.STATIC, + val order : Int = 0 +){ + val needToDraw = boardPieceType == BoardPieceType.HIDDEN && motionType in setOf(MotionType.STATIC,MotionType.HIDE,MotionType.FADE_OUT) +} enum class BoardPieceType { INITIAL, - MOVED, - NOT_CHANGED, HIDDEN -} \ No newline at end of file +} + +enum class MotionType { + STATIC, + MOVE, + HIDE, + FADE_IN, + FADE_OUT, + MOVE_FADE_IN, + MOVE_FADE_OUT, + FADE_IN_MOVE_FADE_OUT +} diff --git a/feature/board/src/main/java/com/goalpanzi/mission_mate/feature/board/model/MissionBoardsUiModel.kt b/feature/board/src/main/java/com/goalpanzi/mission_mate/feature/board/model/MissionBoardsUiModel.kt index 7a29f89f..542face8 100644 --- a/feature/board/src/main/java/com/goalpanzi/mission_mate/feature/board/model/MissionBoardsUiModel.kt +++ b/feature/board/src/main/java/com/goalpanzi/mission_mate/feature/board/model/MissionBoardsUiModel.kt @@ -29,17 +29,19 @@ fun MissionBoards.toUiModel() : MissionBoardsUiModel { fun MissionBoardsUiModel.toBoardPieces( profile: UserProfile? ) : List { - return missionBoardList.filter { block -> - block.missionBoardMembers.isNotEmpty() - }.map { block -> - BoardPiece( - index = block.number, - count = block.missionBoardMembers.size, - nickname = if(block.isMyPosition && profile != null) profile.nickname - else block.missionBoardMembers.first().nickname, - isMe = block.isMyPosition, - drawableRes = if(block.isMyPosition && profile != null) profile.characterType.toCharacterUiModel().imageId - else block.missionBoardMembers.first().characterType.toCharacterUiModel().imageId - ) - } + return missionBoardList.map { block -> + block.missionBoardMembers.mapIndexed { index, member -> + BoardPiece( + memberId = member.memberId, + index = block.number, + count = block.missionBoardMembers.size, + nickname = member.nickname, + isMe = block.isMyPosition, + drawableRes = member.characterType.toCharacterUiModel().imageId, + boardPieceType = if(block.missionBoardMembers.firstOrNull()?.memberId == member.memberId) BoardPieceType.INITIAL + else BoardPieceType.HIDDEN, + order = index + ) + }.reversed() + }.flatten() } diff --git a/feature/board/src/main/java/com/goalpanzi/mission_mate/feature/board/screen/BoardScreen.kt b/feature/board/src/main/java/com/goalpanzi/mission_mate/feature/board/screen/BoardScreen.kt index 6c004bf1..e212dc85 100644 --- a/feature/board/src/main/java/com/goalpanzi/mission_mate/feature/board/screen/BoardScreen.kt +++ b/feature/board/src/main/java/com/goalpanzi/mission_mate/feature/board/screen/BoardScreen.kt @@ -141,12 +141,6 @@ fun BoardRoute( } } - LaunchedEffect(key1 = isUploadSuccess) { - if (isUploadSuccess) { - viewModel.onVerifySuccess() - } - } - if (isShownDeleteMissionDialog) { DeleteMissionDialog( onDismissRequest = { diff --git a/feature/board/src/main/java/com/goalpanzi/mission_mate/feature/board/screen/BoardViewModel.kt b/feature/board/src/main/java/com/goalpanzi/mission_mate/feature/board/screen/BoardViewModel.kt index 1fe5b27f..0e76380c 100644 --- a/feature/board/src/main/java/com/goalpanzi/mission_mate/feature/board/screen/BoardViewModel.kt +++ b/feature/board/src/main/java/com/goalpanzi/mission_mate/feature/board/screen/BoardViewModel.kt @@ -16,7 +16,6 @@ import com.goalpanzi.mission_mate.core.domain.setting.usecase.SetViewedTooltipUs import com.goalpanzi.mission_mate.core.domain.user.usecase.GetCachedMemberIdUseCase import com.goalpanzi.mission_mate.core.domain.user.usecase.ProfileUseCase import com.goalpanzi.mission_mate.feature.board.model.BoardPiece -import com.goalpanzi.mission_mate.feature.board.model.BoardPieceType import com.goalpanzi.mission_mate.feature.board.model.MissionError import com.goalpanzi.mission_mate.feature.board.model.MissionState import com.goalpanzi.mission_mate.feature.board.model.MissionState.Companion.getMissionState @@ -27,6 +26,7 @@ import com.goalpanzi.mission_mate.feature.board.model.toUiModel import com.goalpanzi.mission_mate.feature.board.model.uimodel.MissionBoardUiModel import com.goalpanzi.mission_mate.feature.board.model.uimodel.MissionUiModel import com.goalpanzi.mission_mate.feature.board.model.uimodel.MissionVerificationUiModel +import com.goalpanzi.mission_mate.feature.board.util.PieceManager import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableSharedFlow @@ -57,7 +57,7 @@ class BoardViewModel @Inject constructor( private val profileUseCase: ProfileUseCase, private val setViewedTooltipUseCase: SetViewedTooltipUseCase, private val verifyMissionUseCase: VerifyMissionUseCase, - private val getMyMissionVerificationUseCase : GetMyMissionVerificationUseCase, + private val getMyMissionVerificationUseCase: GetMyMissionVerificationUseCase, ) : ViewModel() { val missionId: Long = savedStateHandle.get("missionId")!! @@ -69,10 +69,10 @@ class BoardViewModel @Inject constructor( ) private val _missionError = MutableStateFlow(null) - val missionError : StateFlow =_missionError.asStateFlow() + val missionError: StateFlow = _missionError.asStateFlow() private val _myMissionVerification = MutableSharedFlow() - val myMissionVerification : SharedFlow = _myMissionVerification.asSharedFlow() + val myMissionVerification: SharedFlow = _myMissionVerification.asSharedFlow() private val _deleteMissionResultEvent = MutableSharedFlow() val deleteMissionResultEvent: SharedFlow = _deleteMissionResultEvent.asSharedFlow() @@ -91,7 +91,7 @@ class BoardViewModel @Inject constructor( _missionVerificationUiModel.asStateFlow() private val _isRefreshLoading = MutableStateFlow(false) - val isRefreshLoading : StateFlow = _isRefreshLoading.asStateFlow() + val isRefreshLoading: StateFlow = _isRefreshLoading.asStateFlow() val isHost: StateFlow = combine( @@ -124,7 +124,7 @@ class BoardViewModel @Inject constructor( private val _boardPieces = MutableStateFlow>(emptyList()) val boardPieces: StateFlow> = _boardPieces.asStateFlow() - fun fetchMissionData(){ + fun fetchMissionData() { viewModelScope.launch { getMissionBoards() getMission() @@ -132,7 +132,7 @@ class BoardViewModel @Inject constructor( } } - fun refreshMissionData(){ + fun refreshMissionData() { viewModelScope.launch { _isRefreshLoading.emit(true) joinAll( @@ -151,16 +151,31 @@ class BoardViewModel @Inject constructor( }.collect { when (it) { is DomainResult.Success -> { - _missionBoardUiModel.emit( - MissionBoardUiModel.Success( - it.data.toUiModel() + _missionBoardUiModel.emit(MissionBoardUiModel.Success(it.data.toUiModel())) + + val boardPieceList = + it.data.toUiModel().toBoardPieces(profileUseCase.getProfile()) + + if (boardPieces.value.isEmpty()) { + _boardPieces.emit(boardPieceList) + } else { + val isMoved = + boardPieces.value.find { it.isMe }?.index?.plus(1) == boardPieceList.find { it.isMe }?.index + + val newList = PieceManager.getBoardPieces( + boardPieces.value, + boardPieceList ) - ) - _boardPieces.emit( - it.data.toUiModel().toBoardPieces( - profileUseCase.getProfile() + _boardPieces.emit( + newList ) - ) + if(isMoved){ + delay(550) + _boardRewardEvent.emit( + it.data.missionBoards.find { it.isMyPosition }?.reward + ) + } + } } else -> { @@ -222,77 +237,9 @@ class BoardViewModel @Inject constructor( } } - fun onVerifySuccess() { - if(missionState.value != MissionState.IN_PROGRESS_MISSION_DAY_BEFORE_CONFIRM) return - viewModelScope.launch { - // 내 캐릭터 - val myBoardPiece = boardPieces.value.find { it.isMe } - val missionBoard = missionBoardUiModel.value - if (myBoardPiece != null && missionBoard is MissionBoardUiModel.Success) { - val missionBoardList = missionBoard.missionBoards.missionBoardList - // 내 캐릭터보다 한칸 앞션 캐릭터 - val nextBoardPiece = missionBoardList.filter { block -> - block.missionBoardMembers.isNotEmpty() - }.find { - it.number == myBoardPiece.index + 1 - } - - // 내 캐릭터와 같이 있던 캐릭터 - val prevBoardPiece = missionBoardList.filter { block -> - block.missionBoardMembers.size >= 2 - }.find { - it.number == myBoardPiece.index - } - _boardPieces.emit( - buildList { - addAll( - boardPieces.value.map { - if (it.isMe) it.copy( - boardPieceType = BoardPieceType.MOVED, - count = if (nextBoardPiece != null) nextBoardPiece.missionBoardMembers.size + 1 else 1 - ) else if (nextBoardPiece != null && it.index == nextBoardPiece.number) { - it.copy(boardPieceType = BoardPieceType.HIDDEN) - } else it - } - ) - if (prevBoardPiece != null) { - val target = prevBoardPiece.missionBoardMembers.first { - it.nickname != myBoardPiece.nickname - } - add( - BoardPiece( - count = prevBoardPiece.missionBoardMembers.size - 1, - nickname = target.nickname, - drawableRes = target.characterType.toCharacterUiModel().imageId, - index = prevBoardPiece.number, - isMe = false - ) - ) - } - } - ) - - _missionBoardUiModel.emit( - missionBoard.copy( - missionBoards = missionBoard.missionBoards.copy( - passedCountByMe = missionBoard.missionBoards.passedCountByMe + 1 - ) - ) - ) - delay(400) - _boardRewardEvent.emit( - missionBoardList.find { - myBoardPiece.index + 1 == it.number - }?.reward - ) - } - fetchMissionData() - } - } - fun getMyMissionVerification( - number : Int - ){ + number: Int + ) { viewModelScope.launch { getMyMissionVerificationUseCase( missionId, @@ -300,7 +247,7 @@ class BoardViewModel @Inject constructor( ).catch { }.collect { - when(it){ + when (it) { is DomainResult.Success -> { _myMissionVerification.emit( UserStory( @@ -312,16 +259,12 @@ class BoardViewModel @Inject constructor( ) ) } + else -> { } } - - - } - } } - }