diff --git a/data/src/main/java/com/nexters/boolti/data/datasource/MemberDataSource.kt b/data/src/main/java/com/nexters/boolti/data/datasource/MemberDataSource.kt new file mode 100644 index 00000000..d96546dc --- /dev/null +++ b/data/src/main/java/com/nexters/boolti/data/datasource/MemberDataSource.kt @@ -0,0 +1,11 @@ +package com.nexters.boolti.data.datasource + +import com.nexters.boolti.data.network.api.MemberService +import com.nexters.boolti.data.network.response.MemberResponse +import javax.inject.Inject + +internal class MemberDataSource @Inject constructor( + private val memberService: MemberService, +) { + suspend fun getMember(userCode: String): MemberResponse = memberService.getMember(userCode) +} diff --git a/data/src/main/java/com/nexters/boolti/data/di/NetworkModule.kt b/data/src/main/java/com/nexters/boolti/data/di/NetworkModule.kt index 162982f3..16c70e6b 100644 --- a/data/src/main/java/com/nexters/boolti/data/di/NetworkModule.kt +++ b/data/src/main/java/com/nexters/boolti/data/di/NetworkModule.kt @@ -12,6 +12,7 @@ import com.nexters.boolti.data.network.api.FileService import com.nexters.boolti.data.network.api.GiftService import com.nexters.boolti.data.network.api.HostService import com.nexters.boolti.data.network.api.LoginService +import com.nexters.boolti.data.network.api.MemberService import com.nexters.boolti.data.network.api.ReservationService import com.nexters.boolti.data.network.api.ShowService import com.nexters.boolti.data.network.api.SignUpService @@ -139,6 +140,10 @@ internal object NetworkModule { @Provides fun provideFileService(@Named("non-auth") retrofit: Retrofit): FileService = retrofit.create() + @Singleton + @Provides + fun provideMemberService(@Named("non-auth") retrofit: Retrofit): MemberService = retrofit.create() + @Singleton @Provides @Named("auth") diff --git a/data/src/main/java/com/nexters/boolti/data/di/RepositoryModule.kt b/data/src/main/java/com/nexters/boolti/data/di/RepositoryModule.kt index d25184d9..7b689e87 100644 --- a/data/src/main/java/com/nexters/boolti/data/di/RepositoryModule.kt +++ b/data/src/main/java/com/nexters/boolti/data/di/RepositoryModule.kt @@ -5,6 +5,7 @@ import com.nexters.boolti.data.repository.ConfigRepositoryImpl import com.nexters.boolti.data.repository.FileRepositoryImpl import com.nexters.boolti.data.repository.GiftRepositoryImpl import com.nexters.boolti.data.repository.HostRepositoryImpl +import com.nexters.boolti.data.repository.MemberRepositoryImpl import com.nexters.boolti.data.repository.ReservationRepositoryImpl import com.nexters.boolti.data.repository.ShowRepositoryImpl import com.nexters.boolti.data.repository.TicketRepositoryImpl @@ -14,6 +15,7 @@ import com.nexters.boolti.domain.repository.ConfigRepository import com.nexters.boolti.domain.repository.FileRepository import com.nexters.boolti.domain.repository.GiftRepository import com.nexters.boolti.domain.repository.HostRepository +import com.nexters.boolti.domain.repository.MemberRepository import com.nexters.boolti.domain.repository.ReservationRepository import com.nexters.boolti.domain.repository.ShowRepository import com.nexters.boolti.domain.repository.TicketRepository @@ -52,4 +54,7 @@ internal abstract class RepositoryModule { @Binds abstract fun bindFileRepository(repository: FileRepositoryImpl): FileRepository + + @Binds + abstract fun bindMemberRepository(repository: MemberRepositoryImpl): MemberRepository } diff --git a/data/src/main/java/com/nexters/boolti/data/network/api/MemberService.kt b/data/src/main/java/com/nexters/boolti/data/network/api/MemberService.kt new file mode 100644 index 00000000..1abc00c5 --- /dev/null +++ b/data/src/main/java/com/nexters/boolti/data/network/api/MemberService.kt @@ -0,0 +1,12 @@ +package com.nexters.boolti.data.network.api + +import com.nexters.boolti.data.network.response.MemberResponse +import retrofit2.http.GET +import retrofit2.http.Path + +internal interface MemberService { + @GET("/app/papi/v1/users/{userCode}") + suspend fun getMember( + @Path("userCode") userCode: String, + ): MemberResponse +} diff --git a/data/src/main/java/com/nexters/boolti/data/network/response/MemberResponse.kt b/data/src/main/java/com/nexters/boolti/data/network/response/MemberResponse.kt new file mode 100644 index 00000000..0d332edf --- /dev/null +++ b/data/src/main/java/com/nexters/boolti/data/network/response/MemberResponse.kt @@ -0,0 +1,22 @@ +package com.nexters.boolti.data.network.response + +import com.nexters.boolti.domain.model.User +import com.nexters.boolti.domain.request.EditProfileRequest +import kotlinx.serialization.Serializable + +@Serializable +internal data class MemberResponse( + val nickname: String = "", + val userCode: String = "", + val imgPath: String = "", + val introduction: String = "", + val link: List = emptyList(), +) { + fun toDomain(): User.Others = User.Others( + nickname = nickname, + photo = imgPath, + userCode = userCode, + introduction = introduction, + link = link.map { it.toDomain() }, + ) +} diff --git a/data/src/main/java/com/nexters/boolti/data/network/response/UserResponse.kt b/data/src/main/java/com/nexters/boolti/data/network/response/UserResponse.kt index d309885a..30b9b6d2 100644 --- a/data/src/main/java/com/nexters/boolti/data/network/response/UserResponse.kt +++ b/data/src/main/java/com/nexters/boolti/data/network/response/UserResponse.kt @@ -1,10 +1,8 @@ package com.nexters.boolti.data.network.response -import com.nexters.boolti.domain.model.Link import com.nexters.boolti.domain.model.User import com.nexters.boolti.domain.request.EditProfileRequest import kotlinx.serialization.Serializable -import java.util.UUID @Serializable internal data class UserResponse( @@ -16,15 +14,15 @@ internal data class UserResponse( val introduction: String = "", val link: List = emptyList(), ) { - fun toDomain(): User { - return User( + fun toDomain(): User.My { + return User.My( id = id, nickname = nickname ?: "", email = email ?: "", photo = imgPath, userCode = userCode ?: "", introduction = introduction, - link = link.map { Link(id = UUID.randomUUID().toString(), it.title, it.link) }, + link = link.map { it.toDomain() }, ) } } diff --git a/data/src/main/java/com/nexters/boolti/data/repository/AuthRepositoryImpl.kt b/data/src/main/java/com/nexters/boolti/data/repository/AuthRepositoryImpl.kt index fed7fed3..91c4e569 100644 --- a/data/src/main/java/com/nexters/boolti/data/repository/AuthRepositoryImpl.kt +++ b/data/src/main/java/com/nexters/boolti/data/repository/AuthRepositoryImpl.kt @@ -29,7 +29,7 @@ internal class AuthRepositoryImpl @Inject constructor( override val loggedIn: Flow get() = authDataSource.loggedIn - override val cachedUser: Flow + override val cachedUser: Flow get() = authDataSource.user.map { it?.toDomain() } override suspend fun kakaoLogin(request: LoginRequest): Result { @@ -55,7 +55,7 @@ internal class AuthRepositoryImpl @Inject constructor( authDataSource.localLogout() } - override fun getUserAndCache(): Flow = flow { + override fun getUserAndCache(): Flow = flow { val response = userDateSource.getUser() response?.let { authDataSource.updateUser(it) diff --git a/data/src/main/java/com/nexters/boolti/data/repository/MemberRepositoryImpl.kt b/data/src/main/java/com/nexters/boolti/data/repository/MemberRepositoryImpl.kt new file mode 100644 index 00000000..d47aa0e0 --- /dev/null +++ b/data/src/main/java/com/nexters/boolti/data/repository/MemberRepositoryImpl.kt @@ -0,0 +1,14 @@ +package com.nexters.boolti.data.repository + +import com.nexters.boolti.data.datasource.MemberDataSource +import com.nexters.boolti.domain.model.User +import com.nexters.boolti.domain.repository.MemberRepository +import javax.inject.Inject + +internal class MemberRepositoryImpl @Inject constructor( + private val memberDataSource: MemberDataSource, +) : MemberRepository { + override suspend fun getMember(userCode: String): Result = runCatching { + memberDataSource.getMember(userCode).toDomain() + } +} diff --git a/domain/src/main/java/com/nexters/boolti/domain/model/User.kt b/domain/src/main/java/com/nexters/boolti/domain/model/User.kt index f60fcd31..823eef04 100644 --- a/domain/src/main/java/com/nexters/boolti/domain/model/User.kt +++ b/domain/src/main/java/com/nexters/boolti/domain/model/User.kt @@ -1,11 +1,27 @@ package com.nexters.boolti.domain.model -data class User( - val id: String, - val nickname: String = "", - val email: String = "", - val photo: String? = null, - val userCode: String = "", - val introduction: String = "", - val link: List = emptyList(), -) +sealed interface User { + val nickname: String + val photo: String? + val userCode: String + val introduction: String + val link: List + + class My( + val id: String, + override val nickname: String = "", + val email: String = "", + override val photo: String? = null, + override val userCode: String = "", + override val introduction: String = "", + override val link: List = emptyList(), + ) : User + + class Others( + override val nickname: String = "", + override val photo: String? = null, + override val userCode: String = "", + override val introduction: String = "", + override val link: List = emptyList(), + ) : User +} diff --git a/domain/src/main/java/com/nexters/boolti/domain/repository/AuthRepository.kt b/domain/src/main/java/com/nexters/boolti/domain/repository/AuthRepository.kt index 2b3b7664..4b53886f 100644 --- a/domain/src/main/java/com/nexters/boolti/domain/repository/AuthRepository.kt +++ b/domain/src/main/java/com/nexters/boolti/domain/repository/AuthRepository.kt @@ -21,11 +21,11 @@ interface AuthRepository { suspend fun logout(): Result suspend fun signUp(signUpRequest: SignUpRequest): Result suspend fun signout(request: SignoutRequest): Result - fun getUserAndCache(): Flow + fun getUserAndCache(): Flow suspend fun sendFcmToken(): Result val loggedIn: Flow - val cachedUser: Flow + val cachedUser: Flow suspend fun editProfile(editProfileRequest: EditProfileRequest): Result } diff --git a/domain/src/main/java/com/nexters/boolti/domain/repository/MemberRepository.kt b/domain/src/main/java/com/nexters/boolti/domain/repository/MemberRepository.kt new file mode 100644 index 00000000..c4edd18b --- /dev/null +++ b/domain/src/main/java/com/nexters/boolti/domain/repository/MemberRepository.kt @@ -0,0 +1,7 @@ +package com.nexters.boolti.domain.repository + +import com.nexters.boolti.domain.model.User + +interface MemberRepository { + suspend fun getMember(userCode: String): Result +} diff --git a/domain/src/main/java/com/nexters/boolti/domain/request/EditProfileRequest.kt b/domain/src/main/java/com/nexters/boolti/domain/request/EditProfileRequest.kt index 67dcdddb..953e324f 100644 --- a/domain/src/main/java/com/nexters/boolti/domain/request/EditProfileRequest.kt +++ b/domain/src/main/java/com/nexters/boolti/domain/request/EditProfileRequest.kt @@ -2,6 +2,7 @@ package com.nexters.boolti.domain.request import com.nexters.boolti.domain.model.Link import kotlinx.serialization.Serializable +import java.util.UUID @Serializable data class EditProfileRequest( @@ -14,7 +15,9 @@ data class EditProfileRequest( data class LinkDto( val title: String, val link: String, - ) + ) { + fun toDomain(): Link = Link(id = UUID.randomUUID().toString(), title, link) + } } fun Link.toDto(): EditProfileRequest.LinkDto = EditProfileRequest.LinkDto(name, url) diff --git a/domain/src/main/java/com/nexters/boolti/domain/usecase/GetUserUsecase.kt b/domain/src/main/java/com/nexters/boolti/domain/usecase/GetUserUsecase.kt index fe41e444..d1f19f85 100644 --- a/domain/src/main/java/com/nexters/boolti/domain/usecase/GetUserUsecase.kt +++ b/domain/src/main/java/com/nexters/boolti/domain/usecase/GetUserUsecase.kt @@ -9,5 +9,5 @@ import javax.inject.Inject class GetUserUsecase @Inject constructor( private val authRepository: AuthRepository, ) { - operator fun invoke(): User? = runBlocking { authRepository.getUserAndCache().first() } + operator fun invoke(): User.My? = runBlocking { authRepository.getUserAndCache().first() } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt index bcfb97c2..5d5f6e9e 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt @@ -88,17 +88,17 @@ sealed class MainDestination(val route: String) { data object Login : MainDestination(route = "login") data object Business : MainDestination(route = "business") data object AccountSetting : MainDestination(route = "accountSetting") - data object Profile : MainDestination(route = "profile?id={$userId}") { + data object Profile : MainDestination(route = "profile?userCode={$userCode}") { val arguments = listOf( - navArgument(userId) { + navArgument(userCode) { type = NavType.StringType nullable = true }, ) - fun createRoute(id: String? = null): String = + fun createRoute(userCode: String? = null): String = StringBuilder("profile").apply { - id?.let { append("?id=$id") } + userCode?.let { append("?userCode=$it") } }.toString() } @@ -137,7 +137,7 @@ const val reservationId = "reservationId" const val salesTicketId = "salesTicketId" const val ticketCount = "ticketCount" const val isInviteTicket = "isInviteTicket" -const val userId = "userId" +const val userCode = "userCode" const val linkId = "linkId" const val linkTitle = "linkTitle" const val url = "url" \ No newline at end of file diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyScreen.kt index e8645411..37bb877d 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyScreen.kt @@ -82,7 +82,7 @@ fun MyScreen( @Composable fun MyScreen( modifier: Modifier = Modifier, - user: User? = null, + user: User.My? = null, onClickHeaderButton: () -> Unit = {}, onClickAccountSetting: () -> Unit = {}, onClickReservations: () -> Unit = {}, @@ -176,7 +176,7 @@ fun MyScreen( @Composable private fun MyHeader( modifier: Modifier = Modifier, - user: User? = null, + user: User.My? = null, onClickButton: () -> Unit, ) { val shape = RoundedCornerShape( @@ -255,7 +255,7 @@ private fun MyMenu( @Preview("로그인 한 유저 헤더") @Composable private fun MyHeaderUserPreview() { - val user = User( + val user = User.My( id = "", nickname = "일이삼사오육칠팔구십", email = "boolti@gmail.com", @@ -278,7 +278,7 @@ private fun MyHeaderGuestPreview() { @Preview @Composable private fun MyScreenPreview() { - val user = User( + val user = User.My( id = "", nickname = "불티유저", email = "boolti@gmail.com", @@ -295,7 +295,7 @@ private fun MyScreenPreview() { @Preview(device = "spec:parent=pixel_5,orientation=landscape") @Composable private fun MyScreenLandscapePreview() { - val user = User( + val user = User.My( id = "", nickname = "불티유저", email = "boolti@gmail.com", diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyUiState.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyUiState.kt index f7f1d42d..18e8f4eb 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyUiState.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyUiState.kt @@ -4,6 +4,6 @@ import com.nexters.boolti.domain.model.User sealed interface MyUiState { data object Loading: MyUiState - data class Success(val user: User): MyUiState + data class Success(val user: User.My): MyUiState data object Failure: MyUiState } \ No newline at end of file diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileEvent.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileEvent.kt new file mode 100644 index 00000000..99393502 --- /dev/null +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileEvent.kt @@ -0,0 +1,6 @@ +package com.nexters.boolti.presentation.screen.profile + +sealed interface ProfileEvent { + data object Invalid : ProfileEvent + data object WithdrawUser : ProfileEvent +} diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileScreen.kt index 1b72e79f..acc67f5b 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileScreen.kt @@ -17,7 +17,11 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -33,6 +37,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.nexters.boolti.domain.model.Link import com.nexters.boolti.domain.model.User import com.nexters.boolti.presentation.R +import com.nexters.boolti.presentation.component.BTDialog import com.nexters.boolti.presentation.component.BtAppBarDefaults import com.nexters.boolti.presentation.component.BtBackAppBar import com.nexters.boolti.presentation.component.SmallButton @@ -42,6 +47,8 @@ import com.nexters.boolti.presentation.screen.LocalSnackbarController import com.nexters.boolti.presentation.theme.Grey30 import com.nexters.boolti.presentation.theme.marginHorizontal import com.nexters.boolti.presentation.theme.point3 +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.collectLatest @Composable fun ProfileScreen( @@ -51,11 +58,13 @@ fun ProfileScreen( viewModel: ProfileViewModel = hiltViewModel(), ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() + val event = viewModel.event ProfileScreen( modifier = modifier, user = uiState.user, isMine = uiState.isMine, + event = event, onClickBack = onClickBack, navigateToProfileEdit = navigateToProfileEdit, ) @@ -66,6 +75,7 @@ fun ProfileScreen( modifier: Modifier = Modifier, user: User, isMine: Boolean, + event: Flow, onClickBack: () -> Unit, navigateToProfileEdit: () -> Unit, ) { @@ -75,6 +85,19 @@ fun ProfileScreen( val scrollState = rememberScrollState() + var backDialogMessage by rememberSaveable { mutableStateOf(null) } + val invalidUserMessage = stringResource(R.string.profile_invalid_user_message) + val withdrawUserMessage = stringResource(R.string.profile_withdraw_user_message) + + LaunchedEffect(event) { + event.collectLatest { + when (it) { + ProfileEvent.Invalid -> backDialogMessage = invalidUserMessage + ProfileEvent.WithdrawUser -> backDialogMessage = withdrawUserMessage + } + } + } + Scaffold( modifier = modifier, topBar = { @@ -121,6 +144,28 @@ fun ProfileScreen( } } } + + backDialogMessage?.let { msg -> + BTDialog( + enableDismiss = false, + showCloseButton = false, + positiveButtonLabel = stringResource(R.string.btn_exit), + onClickPositiveButton = { + onClickBack() + backDialogMessage = null + }, + onDismiss = { + onClickBack() + backDialogMessage = null + }, + ) { + Text( + text = msg, + style = MaterialTheme.typography.titleLarge, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileState.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileState.kt index cbbd4cc0..c3e84542 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileState.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileState.kt @@ -4,6 +4,6 @@ import com.nexters.boolti.domain.model.User data class ProfileState( val loading: Boolean = false, - val user: User = User(""), + val user: User = User.My(""), val isMine: Boolean = false, ) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileViewModel.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileViewModel.kt index 87ac240a..05dfca55 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileViewModel.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/profile/ProfileViewModel.kt @@ -4,40 +4,71 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import com.nexters.boolti.domain.model.User import com.nexters.boolti.domain.repository.AuthRepository +import com.nexters.boolti.domain.repository.MemberRepository import com.nexters.boolti.domain.usecase.GetUserUsecase import com.nexters.boolti.presentation.base.BaseViewModel -import com.nexters.boolti.presentation.screen.userId +import com.nexters.boolti.presentation.screen.userCode import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class ProfileViewModel @Inject constructor( savedStateHandle: SavedStateHandle, getUserUsecase: GetUserUsecase, - authRepository: AuthRepository, + private val memberRepository: MemberRepository, + private val authRepository: AuthRepository, ) : BaseViewModel() { - private val id: String? = savedStateHandle[userId] + private val _userCode: String? = savedStateHandle[userCode] + private val myProfile: User.My? = getUserUsecase() private val _uiState = MutableStateFlow( ProfileState( - user = if (id == null) (getUserUsecase() ?: User("")) else User(""), - isMine = id == null, + isMine = _userCode == null || myProfile?.userCode == _userCode, + user = if (_userCode == null) myProfile ?: User.My("") else User.My(""), ) ) val uiState = _uiState.asStateFlow() + private val _event = Channel() + val event = _event.receiveAsFlow() + init { - if (uiState.value.isMine) { - authRepository.cachedUser - .filterNotNull() - .onEach { user -> _uiState.update { it.copy(user = user) } } - .launchIn(viewModelScope) + if (_userCode != null) { + fetchOthersProfile(_userCode) + } else { + collectMyProfile() + } + } + + private fun event(event: ProfileEvent) { + viewModelScope.launch { + _event.send(event) + } + } + + private fun collectMyProfile() { + authRepository.cachedUser + .filterNotNull() + .onEach { user -> _uiState.update { it.copy(user = user) } } + .launchIn(viewModelScope) + } + + private fun fetchOthersProfile(userCode: String) { + viewModelScope.launch(recordExceptionHandler) { + memberRepository.getMember(userCode).onSuccess { user -> + _uiState.update { it.copy(user = user) } + }.onFailure { + event(ProfileEvent.Invalid) + } } } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowViewModel.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowViewModel.kt index db732a8f..675512be 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowViewModel.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/show/ShowViewModel.kt @@ -1,10 +1,8 @@ package com.nexters.boolti.presentation.screen.show import androidx.lifecycle.viewModelScope -import com.nexters.boolti.domain.model.ReservationState import com.nexters.boolti.domain.model.User import com.nexters.boolti.domain.repository.AuthRepository -import com.nexters.boolti.domain.repository.ReservationRepository import com.nexters.boolti.domain.repository.ShowRepository import com.nexters.boolti.presentation.base.BaseViewModel import dagger.hilt.android.lifecycle.HiltViewModel @@ -15,13 +13,9 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -import kotlinx.coroutines.plus import timber.log.Timber import javax.inject.Inject @@ -30,7 +24,7 @@ class ShowViewModel @Inject constructor( private val showRepository: ShowRepository, authRepository: AuthRepository, ) : BaseViewModel() { - val user: StateFlow = authRepository.cachedUser.stateIn( + val user: StateFlow = authRepository.cachedUser.stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(5000), initialValue = null, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt index 8f9d59f0..f1cb7890 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailNavigation.kt @@ -42,7 +42,10 @@ fun NavGraphBuilder.ShowDetailScreen( navigateToReport = { val showId = entry.arguments?.getString("showId") navigateTo("report/$showId") - } + }, + navigateToProfile = { userCode -> + navigateTo(MainDestination.Profile.createRoute(userCode)) + }, ) } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailScreen.kt index 92fffe42..fc209115 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailScreen.kt @@ -86,6 +86,7 @@ import com.nexters.boolti.presentation.util.UrlParser import kotlinx.coroutines.launch import timber.log.Timber import java.time.format.DateTimeFormatter +import kotlin.math.ceil @Composable fun ShowDetailScreen( @@ -106,6 +107,7 @@ fun ShowDetailScreen( ticketCount: Int, ) -> Unit, navigateToReport: () -> Unit, + navigateToProfile: (userCode: String) -> Unit, modifier: Modifier = Modifier, viewModel: ShowDetailViewModel = hiltViewModel(), ) { @@ -193,7 +195,10 @@ fun ShowDetailScreen( onClickContent = onClickContent, ) - 1 -> CastTab(teams = uiState.castTeams) + 1 -> CastTab( + teams = uiState.castTeams, + onClickMember = navigateToProfile, + ) } item { Spacer(modifier = Modifier.size(114.dp)) } @@ -504,6 +509,7 @@ private fun LazyListScope.ShowInfoTab( @Suppress("FunctionName") fun LazyListScope.CastTab( teams: List, + onClickMember: (userCode: String) -> Unit, ) { val paddingModifier = Modifier.padding(horizontal = marginHorizontal) @@ -540,7 +546,7 @@ fun LazyListScope.CastTab( val spacedBySize = 20.dp val memberHeight = 46.dp val spanCount = 2 - val rows = team.members.size / spanCount + val rows = ceil(team.members.size / spanCount.toFloat()) /** * 중첩 Lazy 레이아웃 처리를 위해 높이 고정 필요 @@ -553,7 +559,7 @@ fun LazyListScope.CastTab( horizontalArrangement = Arrangement.spacedBy(16.dp), ) { items(team.members) { member -> - Cast(memberHeight, member) + Cast(memberHeight, member, onClick = { onClickMember(member.userCode) }) } } } @@ -651,7 +657,9 @@ private fun Cast( onClick: () -> Unit = {}, ) { Row( - modifier = Modifier.height(memberHeight).clickable(onClick = onClick), + modifier = Modifier + .height(memberHeight) + .clickable(onClick = onClick), verticalAlignment = Alignment.CenterVertically, ) { UserThumbnail( diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailViewModel.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailViewModel.kt index ce2a7bfd..5c788ce8 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailViewModel.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailViewModel.kt @@ -65,7 +65,9 @@ class ShowDetailViewModel @Inject constructor( viewModelScope.launch { showRepository.requestCastTeams(showId = showId) .onSuccess { newCastTeams -> - _uiState.update { it.copy(castTeams = newCastTeams) } + _uiState.update { + it.copy(castTeams = newCastTeams) + } } .onFailure { Firebase.crashlytics.recordException(it) @@ -75,7 +77,7 @@ class ShowDetailViewModel @Inject constructor( } fun selectTab(index: Int) { - _uiState.update { it.copy(selectedTab = index.coerceIn(0 .. 1)) } + _uiState.update { it.copy(selectedTab = index.coerceIn(0..1)) } } fun preventEvents() { diff --git a/presentation/src/main/res/values/strings.xml b/presentation/src/main/res/values/strings.xml index 1321bc05..fc10dc67 100644 --- a/presentation/src/main/res/values/strings.xml +++ b/presentation/src/main/res/values/strings.xml @@ -57,6 +57,7 @@ 확인 로그인 하러 가기 삭제하기 + 나가기 로그인 알 수 없는 에러가 발생했습니다 복사 @@ -266,6 +267,8 @@ 썸네일 이미지 변경 프로필 편집을 완료했어요 저장하지 않고 이 페이지를 나가면\n작성한 정보가 손실됩니다.\n변경된 정보를 저장할까요? + 탈퇴한 회원의 프로필입니다. + 존재하지 않는 프로필입니다. 닉네임 소개