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

[AN] feat: 채팅 폴링 구현 #764

Merged
merged 3 commits into from
Dec 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
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -84,34 +90,40 @@ 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())
addInterceptor(logging)
}.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()
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ fun ChatRoomListDto.toDomain(): ChatRooms =
clubName = it.clubName,
memberCount = it.memberCount,
clubImageUrl = it.clubImageUrl,
recentMessage = it.recentMessage,
recentMessageCreatedAt = it.recentMessageCreatedAt,
)
},
)
Original file line number Diff line number Diff line change
@@ -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?,
Comment on lines +10 to +11
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

채팅방 입실/퇴실일 경우 null이 들어옵니다!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

)
Original file line number Diff line number Diff line change
@@ -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?,
)
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,19 @@ class ChatListFragment :
binding.vm = viewModel
}

override fun onStart() {
super.onStart()
viewModel.getChats()
override fun onHiddenChanged(hidden: Boolean) {
super.onHiddenChanged(hidden)
Comment on lines +29 to +30
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 메서드는 어떤 역할을 하고, 로직도 설명해주면 좋을 것 같아요

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

불필요한 업데이트를 방지하고자, MainActivity에서 show를 할 때만 폴링을 진행하는 로직입니다!

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)
}
Expand All @@ -43,7 +47,7 @@ class ChatListFragment :

private fun swipeEvent() {
binding.swipelayoutChatListRefresh.setOnRefreshListener {
viewModel.getChats()
viewModel.getPollingChats()
binding.swipelayoutChatListRefresh.isRefreshing = false
}
}
Expand All @@ -59,7 +63,7 @@ class ChatListFragment :
) { result ->

if (result.resultCode == LEAVE_CHAT_CODE) {
viewModel.getChats()
viewModel.getPollingChats()
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -23,19 +26,30 @@ class ChatListViewModel
var memberId: Long = 0L
private set

private var pollJob: Job? = null

val isChatEmpty =
MediatorLiveData<Boolean>().apply {
addSource(_chats) {
value = it.isEmpty()
}
}

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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

delay를 작성한 이유가 무엇일까요?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

폴링 주기를 생각했을 때, 1초가 너무 느리지 않은 적당한 업데이트 주기라고 판단했습니다.

}
}
}
}

fun cancelPolling() {
pollJob?.cancel()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
},
)
Original file line number Diff line number Diff line change
@@ -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?,
)
3 changes: 1 addition & 2 deletions android/app/src/main/res/layout/item_chat_list.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -59,7 +60,6 @@

<TextView
android:id="@+id/tv_chat_body"
android:visibility="gone"
style="@style/Theme.AppCompat.TextView.Regular.Gray07.Size12"
android:layout_width="0dp"
android:layout_height="wrap_content"
Expand All @@ -72,7 +72,6 @@
tools:text="다들 언제 모이시는게 편하세요?" />

<TextView
android:visibility="gone"
android:id="@+id/tv_chat_date_time"
style="@style/Theme.AppCompat.TextView.Regular.Gray07.Size10"
chatDateTime="@{dateTime}"
Expand Down
12 changes: 5 additions & 7 deletions android/app/src/main/res/layout/item_chat_other.xml
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,14 @@
<TextView
android:id="@+id/tv_chat_other_message"
style="@style/Theme.AppCompat.TextView.Regular.Size16"
android:layout_width="0dp"
android:layout_width="wrap_content"
app:layout_constrainedWidth="true"
android:layout_height="wrap_content"
android:layout_marginEnd="100dp"
android:layout_weight="0"
android:background="@drawable/rect_gray03_fill_16"
android:paddingHorizontal="16dp"
android:paddingVertical="11dp"
android:textColor="@color/black"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintEnd_toStartOf="@id/tv_chat_other_time"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
Expand All @@ -70,16 +69,15 @@
<TextView
android:id="@+id/tv_chat_other_time"
style="@style/Theme.AppCompat.TextView.Regular.Gray07.Size10"
android:layout_width="0dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginStart="5dp"
android:layout_marginBottom="2dp"
android:layout_weight="1"
android:minWidth="120dp"
app:layout_constraintBottom_toBottomOf="@id/tv_chat_other_message"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toEndOf="@id/tv_chat_other_message"
app:layout_constraintEnd_toEndOf="parent"
tools:text="오후 2:23" />

</androidx.constraintlayout.widget.ConstraintLayout>
Expand Down
Loading