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

TOP-63 채팅 모듈 아키텍처 수정 #124

Merged
merged 2 commits into from
Apr 30, 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
2 changes: 1 addition & 1 deletion app/src/main/java/com/tht/tht/HomeActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import tht.core.ui.base.BaseActivity
import tht.core.ui.base.FragmentNavigator
import tht.core.ui.delegate.viewBinding
import tht.core.ui.extension.hideSoftInput
import tht.feature.chat.ChatFragment
import tht.feature.chat.chat.fragment.ChatFragment
import tht.feature.like.like.LikeFragment
import tht.feature.setting.MyPageFragment
import tht.feature.tohot.tohot.fragment.ToHotFragment
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package tht.core.navigation

import androidx.fragment.app.FragmentManager

interface ChatNavigation {
fun navigateChat(
fragmentManager: FragmentManager,
fragmentContainerResourceId: Int
)
}
9 changes: 2 additions & 7 deletions feature/chat/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest>

<application>
<activity
android:name=".screen.detail.ChatDetailActivity"
android:exported="false" />
</application>

</manifest>
</manifest>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package tht.feature.chat
package tht.feature.chat.chat.fragment

import android.os.Bundle
import android.view.LayoutInflater
Expand All @@ -8,8 +8,7 @@ import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.fragment.app.Fragment
import dagger.hilt.android.AndroidEntryPoint
import tht.feature.chat.screen.ChatScreen
import tht.feature.chat.screen.detail.ChatDetailActivity
import tht.feature.chat.navigation.ChatNavigation

@AndroidEntryPoint
class ChatFragment : Fragment() {
Expand All @@ -25,9 +24,7 @@ class ChatFragment : Fragment() {
return ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
ChatScreen {
startActivity(ChatDetailActivity.newIntent(requireContext()))
}
ChatNavigation()
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package tht.feature.chat.screen.detail.screen
package tht.feature.chat.chat.screen

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
Expand All @@ -11,11 +11,11 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.hilt.navigation.compose.hiltViewModel
import com.example.compose_ui.common.viewmodel.collectAsState
import tht.feature.chat.chat.state.ChatDetailState
import tht.feature.chat.chat.viewmodel.ChatDetailViewModel
import tht.feature.chat.component.detail.ChatDetailList
import tht.feature.chat.component.detail.ChatDetailTopAppBar
import tht.feature.chat.component.detail.ChatEditTextContainer
import tht.feature.chat.viewmodel.detail.ChatDetailViewModel
import tht.feature.chat.viewmodel.detail.state.ChatDetailState

@Composable
internal fun ChatDetailScreen(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package tht.feature.chat.chat.screen

import androidx.compose.foundation.Image
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
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.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.example.compose_ui.component.button.ThtButton
import com.example.compose_ui.component.text.headline.ThtHeadline4
import com.example.compose_ui.component.text.headline.ThtHeadline5
import com.example.compose_ui.component.text.p.ThtP1
import tht.feature.chat.R

@Composable
internal fun ChatEmptyScreen(
onClickChangeTitle: () -> Unit
) {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Image(
painter = painterResource(id = R.drawable.ic_empty),
contentDescription = null
)
Spacer(modifier = Modifier.height(16.dp))
ThtHeadline4(
text = "아직 매칭된 무디가 없어요",
fontWeight = FontWeight.W600,
color = Color.White
)
Spacer(modifier = Modifier.height(6.dp))
ThtP1(
text = "대화가 잘 통하는 무디를 찾아볼까요?",
fontWeight = FontWeight.W400,
color = Color(0xFF8D8D8D)
)
}
Spacer(modifier = Modifier.height(64.dp))
ThtButton(
modifier = Modifier
.clip(RoundedCornerShape(16.dp))
.border(1.dp, Color.White, RoundedCornerShape(16.dp))
.padding(horizontal = 70.dp),
backgroundColor = Color.Transparent,
contentColor = Color.White,
onClick = onClickChangeTitle,
content = {
ThtHeadline5(
text = "무디들 만나러 가기",
fontWeight = FontWeight.W700,
color = Color.White
)
}
)
Spacer(modifier = Modifier.height(56.dp))
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package tht.feature.chat.screen.chatlist
package tht.feature.chat.chat.screen

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import tht.feature.chat.component.LazyColumnChatItem
import tht.feature.chat.viewmodel.state.ChatState
import tht.feature.chat.chat.state.ChatState

@Composable
internal fun ChatListScreen(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package tht.feature.chat.screen
package tht.feature.chat.chat.screen

import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.tween
Expand All @@ -15,17 +15,16 @@ import androidx.hilt.navigation.compose.hiltViewModel
import com.example.compose_ui.R
import com.example.compose_ui.common.viewmodel.collectAsState
import tht.feature.chat.component.ChatTopAppBar
import tht.feature.chat.screen.chatlist.ChatListScreen
import tht.feature.chat.viewmodel.chatlist.ChatViewModel
import tht.feature.chat.viewmodel.state.ChatState
import tht.feature.chat.chat.viewmodel.ChatViewModel
import tht.feature.chat.chat.state.ChatState

@Composable
internal fun ChatScreen(
viewModel: ChatViewModel = hiltViewModel(),
navigateChatDetail: () -> Unit = { }
) {
LaunchedEffect(key1 = Unit) {
viewModel.getChatList()
viewModel.getFakeChatList()
}

val state = viewModel.collectAsState().value
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package tht.feature.chat.chat.screen

import android.os.Build
import androidx.annotation.RequiresApi
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleOwner
import java.time.Instant
import java.time.LocalDateTime
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter

@Composable
fun OnLifecycleEvent(
onEvent: (
owner: LifecycleOwner,
event: Lifecycle.Event
) -> Unit
) {
val eventHandler = rememberUpdatedState(onEvent)
val lifecycleOwner = rememberUpdatedState(LocalLifecycleOwner.current)

DisposableEffect(lifecycleOwner.value) {
val lifecycle = lifecycleOwner.value.lifecycle
val observer = LifecycleEventObserver { owner, event ->
eventHandler.value(owner, event)
}

lifecycle.addObserver(observer)
onDispose {
lifecycle.removeObserver(observer)
}
}
}

@RequiresApi(Build.VERSION_CODES.O)
fun format(milliseconds: Long): String {
return LocalDateTime.ofInstant(
Instant.ofEpochMilli(milliseconds),
ZoneOffset.systemDefault()
).format(DateTimeFormatter.ISO_DATE)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package tht.feature.chat.chat.state

sealed class ChatDetailSideEffect
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package tht.feature.chat.viewmodel.detail.state
package tht.feature.chat.chat.state

import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package tht.feature.chat.chat.state

sealed class ChatSideEffect
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package tht.feature.chat.viewmodel.state
package tht.feature.chat.chat.state

import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package tht.feature.chat.viewmodel.detail
package tht.feature.chat.chat.viewmodel

import androidx.lifecycle.ViewModel
import com.example.compose_ui.common.viewmodel.Container
Expand All @@ -10,21 +10,19 @@ import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import tht.feature.chat.chat.state.ChatDetailSideEffect
import tht.feature.chat.chat.state.ChatDetailState
import tht.feature.chat.model.ChatListUiModel
import tht.feature.chat.viewmodel.detail.sideeffect.ChatDetailSideEffect
import tht.feature.chat.viewmodel.detail.state.ChatDetailState
import tht.feature.chat.viewmodel.state.skeletonChatList
import javax.inject.Inject

@HiltViewModel
internal class ChatDetailViewModel @Inject constructor() :
ViewModel(),
Container<ChatDetailState, ChatDetailSideEffect> {
ViewModel(), Container<ChatDetailState, ChatDetailSideEffect> {
override val store: Store<ChatDetailState, ChatDetailSideEffect> =
store(
initialState = ChatDetailState.ChatList(
isLoading = true,
chatList = skeletonChatList
chatList = persistentListOf()
)
)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package tht.feature.chat.chat.viewmodel

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.compose_ui.common.viewmodel.Container
import com.example.compose_ui.common.viewmodel.Store
import com.example.compose_ui.common.viewmodel.intent
import com.example.compose_ui.common.viewmodel.store
import com.tht.tht.domain.chat.model.ChatListModel
import com.tht.tht.domain.chat.usecase.GetChatListUseCase
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.launch
import tht.feature.chat.chat.state.ChatSideEffect
import tht.feature.chat.chat.state.ChatState
import tht.feature.chat.mapper.toModel
import javax.inject.Inject

@HiltViewModel
internal class ChatViewModel @Inject constructor(
private val getChatListUseCase: GetChatListUseCase
) : ViewModel(), Container<ChatState, ChatSideEffect> {
override val store: Store<ChatState, ChatSideEffect> =
store(initialState = ChatState.ChatList(isLoading = true, chatList = persistentListOf()))

fun getChatList() {
viewModelScope.launch {
val chatList = getChatListUseCase().getOrNull() ?: listOf()
intent {
reduce {
if (chatList.isEmpty()) {
ChatState.Empty
} else {
ChatState.ChatList(
isLoading = false,
chatList = chatList.map { it.toModel() }.toImmutableList()
)
}
}
}
}
}

fun getFakeChatList() {
viewModelScope.launch {
intent {
reduce {
ChatState.ChatList(
isLoading = false,
chatList = listOf(
ChatListModel(
chatRoomIdx = 1L,
partnerName = "최웅재",
partnerProfileUrl = "",
currentMessage = "안녕",
messageTime = "2020.08.08"
),
ChatListModel(
chatRoomIdx = 2L,
partnerName = "최웅재",
partnerProfileUrl = "",
currentMessage = "안녕",
messageTime = "2020.08.08"
),
ChatListModel(
chatRoomIdx = 3L,
partnerName = "최웅재",
partnerProfileUrl = "",
currentMessage = "안녕",
messageTime = "2020.08.08"
),
ChatListModel(
chatRoomIdx = 4L,
partnerName = "최웅재",
partnerProfileUrl = "",
currentMessage = "안녕",
messageTime = "2020.08.08"
)
).map { it.toModel() }.toImmutableList()
)
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package tht.feature.chat.navigation

interface ChatDestination {
val route: String
}

object Chat : ChatDestination {
override val route: String = "chat"
}

object ChatDetail : ChatDestination {
override val route: String = "chat-detail"
}
Loading
Loading