Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: 홈 채팅 구현 #243

Merged
merged 20 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions feature/home/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@ android {

dependencies {
implementation(project(":feature:auth"))
implementation(project(":feature:characterchat"))
implementation(libs.retrofit.kotlinx.serialization)
implementation(libs.androidx.appcompat)
implementation(libs.google.accompanist.permissions)
implementation(libs.gson)
implementation(libs.lottie.compose)
implementation(libs.coil.svg)
implementation(libs.eventbus)
implementation(libs.eventbus)
//implementation(libs.androidsvg.aar)
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ fun NavController.navigateToHome(
fun NavGraphBuilder.homeNavGraph(
navigateToBack: () -> Unit,
navigateToGainedCharacter: () -> Unit,
navigateToCharacterChatScreen: (Int, String) -> Unit
) {
composable<MainTabRoute.Home> { backStackEntry ->
val category = backStackEntry.toRoute<MainTabRoute.Home>().category
Expand All @@ -30,6 +31,7 @@ fun NavGraphBuilder.homeNavGraph(
category = category,
completeQuests = completeQuests,
navigateToGainedCharacter = navigateToGainedCharacter,
navigateToCharacterChatScreen = navigateToCharacterChatScreen
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
Expand All @@ -25,14 +24,13 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat
import androidx.hilt.navigation.compose.hiltViewModel
import com.teamoffroad.core.designsystem.component.StaticAnimationWrapper
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.teamoffroad.core.designsystem.component.actionBarPadding
import com.teamoffroad.core.designsystem.theme.HomeGradi1
import com.teamoffroad.core.designsystem.theme.HomeGradi2
Expand Down Expand Up @@ -62,10 +60,12 @@ fun HomeScreen(
category: String?,
completeQuests: List<String> = emptyList(),
navigateToGainedCharacter: () -> Unit = {},
navigateToCharacterChatScreen: (Int, String) -> Unit
) {
val context = LocalContext.current
val viewModel: HomeViewModel = hiltViewModel()
val isCompleteQuestDialogShown = remember { mutableStateOf(false) }
val characterName = viewModel.characterName.collectAsStateWithLifecycle()
val launcher =
rememberLauncherForActivityResult(contract = ActivityResultContracts.RequestPermission()) {}

Expand All @@ -87,38 +87,35 @@ fun HomeScreen(
if (completeQuests.isNotEmpty()) isCompleteQuestDialogShown.value = true
}

StaticAnimationWrapper {
Surface(
modifier = Modifier
.background(homeGradientBackground)
.padding(bottom = 140.dp)
.navigationBarsPadding(),
color = Color.Transparent
) {
StaticAnimationWrapper {
Column(modifier = Modifier.fillMaxWidth()) {
UsersAdventuresInformation(
context = context,
modifier = Modifier
.weight(1f)
.actionBarPadding(),
viewModel = viewModel,
navigateToGainedCharacter = navigateToGainedCharacter,
)
Spacer(modifier = Modifier.padding(top = 12.dp))
UsersQuestInformation(context, viewModel)
}
}
Box(
modifier = Modifier
.background(homeGradientBackground)
.fillMaxSize()
.padding(bottom = 180.dp)
) {
Column(modifier = Modifier.fillMaxSize()) {
UsersAdventuresInformation(
//isChatting = isUserChatting,
context = context,
characterName = characterName.value,
modifier = Modifier
.weight(1f)
.actionBarPadding(),
viewModel = viewModel,
navigateToGainedCharacter = navigateToGainedCharacter,
navigateToCharacterChatScreen = navigateToCharacterChatScreen
)
Spacer(modifier = Modifier.padding(top = 12.dp))
UsersQuestInformation(context, viewModel)

}
}

if (isCompleteQuestDialogShown.value) {
CompleteQuestDialog(
isCompleteQuestDialogShown = isCompleteQuestDialogShown,
completeQuests = completeQuests,
onClickCancel = {
isCompleteQuestDialogShown.value = false
},
onClickCancel = { isCompleteQuestDialogShown.value = false },
)
}
}
Expand All @@ -127,9 +124,11 @@ fun HomeScreen(
@Composable
private fun UsersAdventuresInformation(
context: Context,
characterName: String,
modifier: Modifier = Modifier,
viewModel: HomeViewModel,
navigateToGainedCharacter: () -> Unit,
navigateToCharacterChatScreen: (Int, String) -> Unit
) {
val adventuresInformationState =
viewModel.getUsersAdventuresInformationState.collectAsState(initial = UiState.Loading).value
Expand Down Expand Up @@ -157,7 +156,9 @@ private fun UsersAdventuresInformation(
HomeIcons(
context = context,
imageUrl = imageUrl,
characterName = characterName,
navigateToGainedCharacter = navigateToGainedCharacter,
navigateToCharacterChatScreen = navigateToCharacterChatScreen
)
}

Expand Down Expand Up @@ -238,7 +239,8 @@ fun HomeScreenPreview() {
OffroadTheme {
HomeScreen(
//padding = PaddingValues(),
category = "NONE"
category = "NONE",
navigateToCharacterChatScreen = { _, _ -> }
)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
package com.teamoffroad.feature.home.presentation

import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.teamoffroad.characterchat.domain.model.Chat
import com.teamoffroad.characterchat.domain.repository.CharacterChatRepository
import com.teamoffroad.characterchat.presentation.model.ChatModel
import com.teamoffroad.characterchat.presentation.model.ChatType
import com.teamoffroad.characterchat.presentation.model.TimeType
import com.teamoffroad.core.common.domain.model.NotificationEvent
import com.teamoffroad.core.common.domain.repository.TokenRepository
import com.teamoffroad.core.common.domain.usecase.SetAutoSignInUseCase
import com.teamoffroad.feature.home.domain.model.Emblem
Expand All @@ -11,20 +18,29 @@ import com.teamoffroad.feature.home.domain.repository.UserRepository
import com.teamoffroad.feature.home.domain.usecase.PostFcmTokenUseCase
import com.teamoffroad.feature.home.presentation.component.UiState
import com.teamoffroad.feature.home.presentation.component.getErrorMessage
import com.teamoffroad.feature.home.presentation.model.CharacterChatModel
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import java.time.LocalDateTime
import javax.inject.Inject

@HiltViewModel
class HomeViewModel @Inject constructor(
private val userRepository: UserRepository,
//private val characterChatRepository: CharacterChatRepository,
private val setAutoSignInUseCase: SetAutoSignInUseCase,
private val tokenRepository: TokenRepository,
private val deviceTokenRepository: TokenRepository,
private val fcmTokenUseCase: PostFcmTokenUseCase,
) : ViewModel() {
// private val _getCharacterChat = MutableStateFlow<CharacterChatModel>(CharacterChatModel("", ""))
// val getCharacterChat = _getCharacterChat.asStateFlow()

private val _getUsersAdventuresInformationState =
MutableStateFlow<UiState<UsersAdventuresInformation>>(
Expand Down Expand Up @@ -53,18 +69,69 @@ class HomeViewModel @Inject constructor(
private val _getUserQuestsState = MutableStateFlow<UiState<UserQuests>>(UiState.Loading)
val getUserQuestsState = _getUserQuestsState.asStateFlow()

// private val _sendChatState = MutableStateFlow<UiState<Chat>>(UiState.Loading)
// val sendChatState = _sendChatState.asStateFlow()

private val _circleProgressBar = MutableStateFlow(0f)
val circleProgressBar = _circleProgressBar.asStateFlow()

private val _linearProgressBar = MutableStateFlow(0f)
val linearProgressBar = _linearProgressBar.asStateFlow()

// private val _isCharacterChatting: MutableStateFlow<Boolean> = MutableStateFlow(false)
// val isCharacterChatting: StateFlow<Boolean> = _isCharacterChatting.asStateFlow()

// private val _isCharacterChattingLoading = MutableStateFlow(false)
// val isCharacterChattingLoading = _isCharacterChattingLoading.asStateFlow()
//
// private val _chattingText: MutableStateFlow<String> = MutableStateFlow("")
// val chattingText: StateFlow<String> = _chattingText.asStateFlow()

private val _characterName = MutableStateFlow("")
val characterName = _characterName.asStateFlow()

// var asd = MutableStateFlow("")
// init {
// //아까 CharacterChatBroadcastReceiver에서 게시한 브로드캐스트리시버를 여기서 받습니다.
// EventBus.getDefault().register(this)
// }

// //뷰모델이 삭제될때 이벤트버스도 해제시켜줍니다.
// override fun onCleared() {
// super.onCleared()
// EventBus.getDefault().unregister(this)
// }
//
// //브로드캐스트리시버가 작동할때마다 동작하는 함수(fcm발송 > 앱이 포그라운드에 있고, 타입이 캐릭터채팅이라면 작동)
// //그런데 홈화면이 아니고 다른화면에서 이 함수가 호출되면 ui가 활성되있지 않기 때문에 ui작업을 할 수 없습니다.(함수 실행될때 로그는 찍힘)
// //그래서 데이터스토어 같은 로컬저장소에 데이터와 캐릭터 채팅확인 여부를 저장해두었다가
// //홈화면에 들어와서 채팅확인 여부가 x라면 알림을 보여주고, 알림을 봤다면 다시 채팅확인 여부가 o로 만드는식으로 하면 될거같습니다.
// //그래서 포스트맨으로 fcm쏴보면서 요함수에서 하면 될 것 같습니다.
// @Subscribe(threadMode = ThreadMode.MAIN)
// fun onNotificationEvent(event: NotificationEvent) {
// //Log.d("characterChat data", event.toString())
//
// // 1. 홈 화면에서 캐릭터한테 메시지가 왔을 때
// val characterName = event.characterName
// val characterContent = event.characterContent
// if(characterName != null && characterContent != null) {
// _getCharacterChat.value = CharacterChatModel(_characterName.value, characterContent)
// updateCharacterChatting(true)
// }
//
// }

// fun updateCharacterChatting(state: Boolean) {
// _isCharacterChatting.value = state
// }

fun getUsersAdventuresInformation(category: String) {
viewModelScope.launch {
runCatching {
userRepository.getUsersAdventuresInformation(category)
}.onSuccess { state ->
_getUsersAdventuresInformationState.emit(UiState.Success(state))
_characterName.value = state.characterName
updateSelectedEmblem(state.emblemName)
updateCharacterImage(state.baseImageUrl)
updateMotionImageUrl(state.motionImageUrl)
Expand Down Expand Up @@ -145,9 +212,45 @@ class HomeViewModel @Inject constructor(
}
}

// fun updateChattingText(text: String) {
// _chattingText.value = text
// }

// fun sendChat() {
// val chattingText = chattingText.value
// _isCharacterChattingLoading.value = true
//
// viewModelScope.launch {
// runCatching {
// val now = LocalDateTime.now()
// val userChat = ChatModel(
// chatType = ChatType.USER,
// text = chattingText,
// date = now.toLocalDate(),
// time = Triple(TimeType.toTimeType(now.hour), now.hour, now.minute),
// )
// characterChatRepository.saveChat(1, chattingText)
// }.onSuccess { chat ->
// // 보낸 채팅 내용 홈에 보여주어야 함
// _sendChatState.emit(UiState.Success(chat))
// _isCharacterChattingLoading.value = false
//
// val characterContent = chat.content
// if (characterContent != null) {
// _getCharacterChat.value = CharacterChatModel(_characterName.value, characterContent)
// updateCharacterChatting(true)
// }
//
// }.onFailure { t ->
// val errorMessage = getErrorMessage(t)
// _sendChatState.emit(UiState.Failure(errorMessage))
// }
// }
// }

fun updateFcmToken() {
viewModelScope.launch {
val deviceToken = tokenRepository.getDeviceToken().first()
val deviceToken = deviceTokenRepository.getDeviceToken().first()
if (deviceToken.isBlank()) return@launch
runCatching {
fcmTokenUseCase.invoke(deviceToken)
Expand Down
Loading