diff --git a/android/app/src/main/java/com/happy/friendogly/application/di/NetworkModule.kt b/android/app/src/main/java/com/happy/friendogly/application/di/NetworkModule.kt index a0c417118..b1ed04596 100644 --- a/android/app/src/main/java/com/happy/friendogly/application/di/NetworkModule.kt +++ b/android/app/src/main/java/com/happy/friendogly/application/di/NetworkModule.kt @@ -67,14 +67,20 @@ object NetworkModule { @Singleton fun provideAuthService( @Base baseUrl: BaseUrl, + authenticatorInterceptor: AuthorizationInterceptor, converterFactory: Factory, ): AuthService = - Retrofit.Builder().baseUrl(baseUrl.url).client( - createOkHttpClient { - addInterceptor(ErrorResponseInterceptor()) - addInterceptor(logging) - }, - ).addConverterFactory(converterFactory).build() + Retrofit + .Builder() + .baseUrl(baseUrl.url) + .client( + createOkHttpClient { + addInterceptor(authenticatorInterceptor) + addInterceptor(ErrorResponseInterceptor()) + addInterceptor(logging) + }, + ).addConverterFactory(converterFactory) + .build() .create(AuthService::class.java) @Singleton @@ -84,24 +90,27 @@ object NetworkModule { authenticator: Authenticator, authenticatorInterceptor: AuthorizationInterceptor, converterFactory: Factory, - ): Retrofit { - return Retrofit.Builder().baseUrl(baseUrl.url).client( - createOkHttpClient { - addInterceptor(authenticatorInterceptor) - authenticator(authenticator) - addInterceptor(ErrorResponseInterceptor()) - addInterceptor(logging) - }, - ).addConverterFactory(converterFactory).build() - } + ): Retrofit = + Retrofit + .Builder() + .baseUrl(baseUrl.url) + .client( + createOkHttpClient { + addInterceptor(authenticatorInterceptor) + authenticator(authenticator) + addInterceptor(ErrorResponseInterceptor()) + addInterceptor(logging) + }, + ).addConverterFactory(converterFactory) + .build() @Singleton @Provides fun provideStumpClient( authenticator: Authenticator, authenticatorInterceptor: AuthorizationInterceptor, - ): StompClient { - return createOkHttpClient { + ): StompClient = + createOkHttpClient { addInterceptor(authenticatorInterceptor) authenticator(authenticator) addInterceptor(ErrorResponseInterceptor()) @@ -109,9 +118,12 @@ object NetworkModule { }.let(::OkHttpWebSocketClient).let( ::StompClient, ) - } private fun createOkHttpClient(interceptors: OkHttpClient.Builder.() -> Unit = { }): OkHttpClient = - OkHttpClient.Builder().callTimeout(Duration.ofMinutes(TIME_OUT_MINUTE)) - .pingInterval(Duration.ofSeconds(PING_OUT_SECOND)).apply(interceptors).build() + OkHttpClient + .Builder() + .callTimeout(Duration.ofMinutes(TIME_OUT_MINUTE)) + .pingInterval(Duration.ofSeconds(PING_OUT_SECOND)) + .apply(interceptors) + .build() } diff --git a/android/app/src/main/java/com/happy/friendogly/data/mapper/ChatMemberMapper.kt b/android/app/src/main/java/com/happy/friendogly/data/mapper/ChatMemberMapper.kt index 33cda6b83..3f32a773d 100644 --- a/android/app/src/main/java/com/happy/friendogly/data/mapper/ChatMemberMapper.kt +++ b/android/app/src/main/java/com/happy/friendogly/data/mapper/ChatMemberMapper.kt @@ -24,6 +24,8 @@ fun ChatRoomListDto.toDomain(): ChatRooms = clubName = it.clubName, memberCount = it.memberCount, clubImageUrl = it.clubImageUrl, + recentMessage = it.recentMessage, + recentMessageCreatedAt = it.recentMessageCreatedAt, ) }, ) diff --git a/android/app/src/main/java/com/happy/friendogly/data/model/ChatRoomDto.kt b/android/app/src/main/java/com/happy/friendogly/data/model/ChatRoomDto.kt index 6e68fb4cd..f4208b8ce 100644 --- a/android/app/src/main/java/com/happy/friendogly/data/model/ChatRoomDto.kt +++ b/android/app/src/main/java/com/happy/friendogly/data/model/ChatRoomDto.kt @@ -1,8 +1,12 @@ package com.happy.friendogly.data.model +import java.time.LocalDateTime + data class ChatRoomDto( val chatRoomId: Long, val clubName: String, val memberCount: Int, val clubImageUrl: String? = null, + val recentMessage: String?, + val recentMessageCreatedAt: LocalDateTime?, ) diff --git a/android/app/src/main/java/com/happy/friendogly/domain/model/ChatRoom.kt b/android/app/src/main/java/com/happy/friendogly/domain/model/ChatRoom.kt index 35a0f63eb..7ccc2d68f 100644 --- a/android/app/src/main/java/com/happy/friendogly/domain/model/ChatRoom.kt +++ b/android/app/src/main/java/com/happy/friendogly/domain/model/ChatRoom.kt @@ -1,8 +1,12 @@ package com.happy.friendogly.domain.model +import java.time.LocalDateTime + data class ChatRoom( val chatRoomId: Long, val clubName: String, val memberCount: Int, val clubImageUrl: String? = null, + val recentMessage: String?, + val recentMessageCreatedAt: LocalDateTime?, ) diff --git a/android/app/src/main/java/com/happy/friendogly/presentation/ui/chatlist/ChatBindingAdapter.kt b/android/app/src/main/java/com/happy/friendogly/presentation/ui/chatlist/ChatBindingAdapter.kt index 80021efc1..30a9540fb 100644 --- a/android/app/src/main/java/com/happy/friendogly/presentation/ui/chatlist/ChatBindingAdapter.kt +++ b/android/app/src/main/java/com/happy/friendogly/presentation/ui/chatlist/ChatBindingAdapter.kt @@ -1,5 +1,6 @@ package com.happy.friendogly.presentation.ui.chatlist +import android.view.View import android.widget.TextView import androidx.core.view.isVisible import androidx.databinding.BindingAdapter @@ -10,7 +11,11 @@ import java.time.format.DateTimeFormatter private const val MAX_MESSAGE_COUNT = 999 @BindingAdapter("chatDateTime") -fun TextView.dateTimeString(dateTime: ChatDateTime) { +fun TextView.dateTimeString(dateTime: ChatDateTime?) { + if (dateTime == null) { + this.visibility = View.GONE + return + } val timeFormatter = DateTimeFormatter.ofPattern(context.getString(R.string.chat_time)) val dateFormatter = DateTimeFormatter.ofPattern(context.getString(R.string.chat_date)) diff --git a/android/app/src/main/java/com/happy/friendogly/presentation/ui/chatlist/ChatListFragment.kt b/android/app/src/main/java/com/happy/friendogly/presentation/ui/chatlist/ChatListFragment.kt index d7e649d80..58cbe3dc1 100644 --- a/android/app/src/main/java/com/happy/friendogly/presentation/ui/chatlist/ChatListFragment.kt +++ b/android/app/src/main/java/com/happy/friendogly/presentation/ui/chatlist/ChatListFragment.kt @@ -26,15 +26,19 @@ class ChatListFragment : binding.vm = viewModel } - override fun onStart() { - super.onStart() - viewModel.getChats() + override fun onHiddenChanged(hidden: Boolean) { + super.onHiddenChanged(hidden) + if (hidden) { + viewModel.cancelPolling() + } else { + viewModel.getPollingChats() + } } private fun initAdapter() { adapter = ChatListAdapter(this) binding.rcvChatList.adapter = adapter - viewModel.getChats() + viewModel.getPollingChats() viewModel.chats.observe(viewLifecycleOwner) { chats -> adapter.submitList(chats) } @@ -43,7 +47,7 @@ class ChatListFragment : private fun swipeEvent() { binding.swipelayoutChatListRefresh.setOnRefreshListener { - viewModel.getChats() + viewModel.getPollingChats() binding.swipelayoutChatListRefresh.isRefreshing = false } } @@ -59,7 +63,7 @@ class ChatListFragment : ) { result -> if (result.resultCode == LEAVE_CHAT_CODE) { - viewModel.getChats() + viewModel.getPollingChats() } } } diff --git a/android/app/src/main/java/com/happy/friendogly/presentation/ui/chatlist/ChatListMapper.kt b/android/app/src/main/java/com/happy/friendogly/presentation/ui/chatlist/ChatListMapper.kt index 92e08cbca..68632ea2b 100644 --- a/android/app/src/main/java/com/happy/friendogly/presentation/ui/chatlist/ChatListMapper.kt +++ b/android/app/src/main/java/com/happy/friendogly/presentation/ui/chatlist/ChatListMapper.kt @@ -9,10 +9,10 @@ import java.time.LocalDateTime fun ChatRoom.toUiModel(): ChatListUiModel = ChatListUiModel( clubName, - "", + recentMessage, memberCount, 0, - LocalDateTime.of(2024, 6, 7, 13, 6).classifyChatDateTime(), + recentMessageCreatedAt?.classifyChatDateTime(), clubImageUrl, chatRoomId = chatRoomId, ) diff --git a/android/app/src/main/java/com/happy/friendogly/presentation/ui/chatlist/ChatListViewModel.kt b/android/app/src/main/java/com/happy/friendogly/presentation/ui/chatlist/ChatListViewModel.kt index 14fbd61a1..78d59590c 100644 --- a/android/app/src/main/java/com/happy/friendogly/presentation/ui/chatlist/ChatListViewModel.kt +++ b/android/app/src/main/java/com/happy/friendogly/presentation/ui/chatlist/ChatListViewModel.kt @@ -8,6 +8,9 @@ import com.happy.friendogly.domain.usecase.GetChatListUseCase import com.happy.friendogly.presentation.base.BaseViewModel import com.happy.friendogly.presentation.ui.chatlist.uimodel.ChatListUiModel import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import javax.inject.Inject @@ -23,6 +26,8 @@ class ChatListViewModel var memberId: Long = 0L private set + private var pollJob: Job? = null + val isChatEmpty = MediatorLiveData().apply { addSource(_chats) { @@ -30,12 +35,21 @@ class ChatListViewModel } } - fun getChats() { - viewModelScope.launch { - getChatListUseCase.invoke().onSuccess { room -> - _chats.value = room.chatRooms.map { it.toUiModel() } - memberId = room.myMemberId + fun getPollingChats() { + pollJob?.cancel() + pollJob = + viewModelScope.launch { + while (this.isActive) { + getChatListUseCase.invoke().onSuccess { room -> + _chats.value = room.chatRooms.map { it.toUiModel() } + memberId = room.myMemberId + } + delay(1000) + } } - } + } + + fun cancelPolling() { + pollJob?.cancel() } } diff --git a/android/app/src/main/java/com/happy/friendogly/presentation/ui/chatlist/uimodel/ChatListUiModel.kt b/android/app/src/main/java/com/happy/friendogly/presentation/ui/chatlist/uimodel/ChatListUiModel.kt index af111a141..ece3667b3 100644 --- a/android/app/src/main/java/com/happy/friendogly/presentation/ui/chatlist/uimodel/ChatListUiModel.kt +++ b/android/app/src/main/java/com/happy/friendogly/presentation/ui/chatlist/uimodel/ChatListUiModel.kt @@ -4,10 +4,10 @@ import java.time.LocalDateTime data class ChatListUiModel( val title: String, - val body: String, + val body: String?, val numberOfPeople: Int, val unreadMessageCount: Int, - val dateTime: ChatDateTime, + val dateTime: ChatDateTime?, val imageUrl: String? = null, val chatRoomId: Long = 0, ) { diff --git a/android/app/src/main/java/com/happy/friendogly/remote/api/ApiClient.kt b/android/app/src/main/java/com/happy/friendogly/remote/api/ApiClient.kt index ca30f8d0a..50fb15b5d 100644 --- a/android/app/src/main/java/com/happy/friendogly/remote/api/ApiClient.kt +++ b/android/app/src/main/java/com/happy/friendogly/remote/api/ApiClient.kt @@ -57,7 +57,7 @@ class ApiClient { object ChatRoom { private const val BASE_URL = "/api/chat-rooms" - const val CHAT_LIST = "$BASE_URL/mine" + const val CHAT_LIST = "$BASE_URL/mine/v2" const val MEMBERS = "$BASE_URL/{chatRoomId}" const val CLUB = "$BASE_URL/{chatRoomId}/club" const val LEAVE = "$BASE_URL/leave/{chatRoomId}" diff --git a/android/app/src/main/java/com/happy/friendogly/remote/mapper/ChatRoomMapper.kt b/android/app/src/main/java/com/happy/friendogly/remote/mapper/ChatRoomMapper.kt index 4e37dfd9e..2f00f87fe 100644 --- a/android/app/src/main/java/com/happy/friendogly/remote/mapper/ChatRoomMapper.kt +++ b/android/app/src/main/java/com/happy/friendogly/remote/mapper/ChatRoomMapper.kt @@ -11,9 +11,11 @@ fun ChatRoomListResponse.toData(): ChatRoomListDto = chatRooms.map { ChatRoomDto( chatRoomId = it.chatRoomId, - clubName = it.clubName, + clubName = it.title, memberCount = it.memberCount, - clubImageUrl = it.clubImageUrl, + clubImageUrl = it.imageUrl, + recentMessage = it.recentMessage, + recentMessageCreatedAt = it.recentMessageCreatedAt, ) }, ) diff --git a/android/app/src/main/java/com/happy/friendogly/remote/model/response/ChatRoomResponse.kt b/android/app/src/main/java/com/happy/friendogly/remote/model/response/ChatRoomResponse.kt index 73946c96d..45689d478 100644 --- a/android/app/src/main/java/com/happy/friendogly/remote/model/response/ChatRoomResponse.kt +++ b/android/app/src/main/java/com/happy/friendogly/remote/model/response/ChatRoomResponse.kt @@ -1,11 +1,16 @@ package com.happy.friendogly.remote.model.response +import com.happy.friendogly.remote.util.JavaLocalDateTimeSerializer import kotlinx.serialization.Serializable +import java.time.LocalDateTime @Serializable data class ChatRoomResponse( val chatRoomId: Long, - val clubName: String, val memberCount: Int, - val clubImageUrl: String? = null, + val title: String, + val imageUrl: String? = null, + val recentMessage: String?, + @Serializable(with = JavaLocalDateTimeSerializer::class) + val recentMessageCreatedAt: LocalDateTime?, ) diff --git a/android/app/src/main/res/layout/item_chat_list.xml b/android/app/src/main/res/layout/item_chat_list.xml index d0f845916..8eb69ba3f 100644 --- a/android/app/src/main/res/layout/item_chat_list.xml +++ b/android/app/src/main/res/layout/item_chat_list.xml @@ -41,6 +41,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="12dp" + android:layout_marginBottom="7dp" app:layout_constraintStart_toEndOf="@id/iv_chat_group" app:layout_constraintBottom_toBottomOf="@id/iv_chat_group" app:layout_constraintTop_toTopOf="@id/iv_chat_group" @@ -59,7 +60,6 @@