Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
97d3276
feature: 채팅방 목록 리스트 데이터 dto 생성
nueijeel Feb 1, 2025
eb3bc60
feature: 채팅방 목록 리스트 데이터 model 생성
nueijeel Feb 1, 2025
77fdf46
feature: 채팅 목록 불러오는 api 연결
nueijeel Feb 1, 2025
ecc8a19
Merge branch 'develop' into feature/chatting
nueijeel Feb 5, 2025
25353b8
fix: 채팅 목록 불러오는 api 수정
nueijeel Feb 5, 2025
fe82ae1
ui: 채팅 목록 아이템 ui 수정
nueijeel Feb 5, 2025
d286048
feature: 마지막 채팅 수신 시각 표시를 위한 날짜 포맷 함수 구현
nueijeel Feb 5, 2025
7292434
fix: converTeamLogo 함수 접근제한자 제거
nueijeel Feb 5, 2025
7308499
feature: 채팅 홈 목록 불러오는 기능 구현
nueijeel Feb 5, 2025
8ced2ff
fix: 채팅 목록의 항목 dto, model 프로퍼티 추가
nueijeel Feb 17, 2025
002cc04
fix: 채팅 홈 화면에서 채팅방 클릭 시 bundle 통해 chatRoomInfo 객체를 넘기도록 수정
nueijeel Feb 17, 2025
89341b0
fix: ChattingGameInfoView 수정
nueijeel Feb 17, 2025
aea2864
fix: 햄버거 메뉴 헤더 타이틀 typography 변경
nueijeel Feb 17, 2025
bf2c8b1
fix: 마지막 채팅 시간, 내용 표시 처리
nueijeel Feb 17, 2025
128117b
fix: 채팅방 목록 아이템 ui에서 안읽은 메시지 개수 표시하는 뷰 invisible 처리
nueijeel Feb 17, 2025
ab3a8a9
chore: stomp 소켓 통신 구현을 위한 rxjava, rxandroid, stomp 라이브러리 의존성 추가
nueijeel Feb 17, 2025
36fc34b
fix: 마지막 채팅 시간, 내용 표시 처리
nueijeel Feb 17, 2025
cb7b39a
feature: STOMP 프로토콜을 통한 소켓 통신 로직 구현
nueijeel Feb 17, 2025
53958f4
feature: 채팅방 입장 시 websocket연결 및 채팅방 구독, 메시지 전송 기능 구현
nueijeel Feb 17, 2025
8c2e3ad
feature: 채팅 내역 조회 api 연결
nueijeel Feb 18, 2025
9e29ff4
ui: 채팅방 내 date view 제거
nueijeel Feb 18, 2025
c917f7b
ui: 채팅방 내 항목들 margin, padding 조정
nueijeel Feb 18, 2025
084aa36
feature: 채팅방 참여자 목록 조회 api 연결
nueijeel Feb 18, 2025
fe789ee
fix: 불필요한 String.format 제거
nueijeel Feb 18, 2025
ebd9a84
fix: 마지막 채팅 수신 시간 소수점 자릿수 대응
nueijeel Feb 18, 2025
f40d8d2
fix: 채팅 타입 enum에 타입 추가
nueijeel Feb 18, 2025
fab4a07
feature: 채팅 내역 리스트 adapter 생성
nueijeel Feb 18, 2025
4e03818
feature: 채팅방 접속 시 채팅 내역 불러오는 로직 구현
nueijeel Feb 18, 2025
7c2fe66
feature: 채팅방 구독 중 수신된 메시지 표시하는 로직 구현
nueijeel Feb 18, 2025
9720bf2
feature: 채팅방 정보 조회 api 연결
nueijeel Feb 19, 2025
ef9dd45
chore: Manifest에 등록된 fcm default icon 및 color 제거
nueijeel Feb 19, 2025
b1f666a
feature: 채팅방 정보 설정하는 기능 구현
nueijeel Feb 19, 2025
c68ca7c
feature: 푸시 알림 채널 세분화
nueijeel Feb 19, 2025
08c00e5
refactor: trailing comma 추가
nueijeel Feb 19, 2025
2599aed
ui: 채팅방 사이드 시트 ui 수정
nueijeel Feb 19, 2025
e62f941
ui: 채팅 참여자 아이템 레이아웃 ui 수정
nueijeel Feb 19, 2025
d04422e
feature: 채팅방 사이드 시트 데이터 연결
nueijeel Feb 19, 2025
a4d160d
feature: 채팅방 퇴장 api 연결
nueijeel Feb 19, 2025
125c388
rename: DeleteChattingRoomUseCase 클래스명 변경
nueijeel Feb 19, 2025
c62586c
feature: 채팅방 퇴장 기능 구현
nueijeel Feb 19, 2025
40dc553
rename: ChattingRoomSideSheetCrewAdapter 클래스명 변경
nueijeel Feb 19, 2025
94ded0c
feature: 채팅방 설정 화면 이동 시 필요한 데이터 전달하는 로직 구현
nueijeel Feb 19, 2025
cc7b5eb
feature: 채팅방 내에서 통신 중 토큰 만료 시 login 화면으로 이동하는 로직 구현
nueijeel Feb 19, 2025
831dd4e
feature: 채팅방 강제 퇴장 api 연결
nueijeel Feb 19, 2025
848d21c
feature: 채팅방 강퇴 기능 구현
nueijeel Feb 19, 2025
5c24f4d
feature: 채팅방 이미지 수정 api 연결
nueijeel Feb 19, 2025
41ab1ae
fix: bitmap을 multipart로 변환하는 함수 매개변수 수정
nueijeel Feb 19, 2025
963ca4c
feature: 채팅방 이미지 수정 로직 구현
nueijeel Feb 19, 2025
aedd906
fix: 채팅방 목록 표시될 때 변경된 이미지 적용되지 않는 현상 해결
nueijeel Feb 19, 2025
77911a9
refactor: ktlint에 맞게 수정
nueijeel Feb 20, 2025
8506f16
refactor: ktlint에 맞게 수정
nueijeel Feb 20, 2025
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
7 changes: 0 additions & 7 deletions CatchMate/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,6 @@
</intent-filter>
</service>

<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/ic_notification" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="@color/brand500"/>

<provider
android:authorities="com.catchmate.android"
android:name="androidx.core.content.FileProvider"
Expand Down
6 changes: 6 additions & 0 deletions CatchMate/data/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ android {

val googleWebClientId = properties["google_web_client_id"] as? String ?: ""
val serverDomain = properties["server_domain"] as? String ?: ""
val serverSocketUrl = properties["server_socket_url"] as? String ?: ""

defaultConfig {
buildConfigField("String", "GOOGLE_WEB_CLIENT_ID", googleWebClientId)
buildConfigField("String", "SERVER_DOMAIN", serverDomain)
buildConfigField("String", "SERVER_SOCKET_URL", serverSocketUrl)
}

buildFeatures {
Expand Down Expand Up @@ -49,4 +51,8 @@ dependencies {
implementation(libs.logging.interceptor)

implementation(libs.androidx.security.crypto.ktx)

implementation(libs.rxjava)
implementation(libs.rxandroid)
implementation(libs.stomp)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.catchmate.data.datasource.remote

import com.catchmate.data.BuildConfig
import com.gmail.bishoybasily.stomp.lib.StompClient
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import javax.inject.Inject

class ChatWebSocketClient
@Inject
constructor() {
private val intervalMillis = 1000L

private val loggingInterceptor =
HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}

private val okHttpClient =
OkHttpClient
.Builder()
.addInterceptor(loggingInterceptor)
.build()

val stompClient =
StompClient(okHttpClient, intervalMillis).apply {
url = BuildConfig.SERVER_SOCKET_URL
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.catchmate.data.datasource.remote

import com.catchmate.data.dto.chatting.ChatRoomInfoDTO
import com.catchmate.data.dto.chatting.DeleteChattingCrewKickOutResponseDTO
import com.catchmate.data.dto.chatting.DeleteChattingRoomResponseDTO
import com.catchmate.data.dto.chatting.GetChattingCrewListResponseDTO
import com.catchmate.data.dto.chatting.GetChattingHistoryResponseDTO
import com.catchmate.data.dto.chatting.GetChattingRoomListResponseDTO
import com.catchmate.data.dto.chatting.PatchChattingRoomImageResponseDTO
import okhttp3.MultipartBody
import retrofit2.Response
import retrofit2.http.DELETE
import retrofit2.http.GET
import retrofit2.http.Multipart
import retrofit2.http.PATCH
import retrofit2.http.Part
import retrofit2.http.Path
import retrofit2.http.Query

interface ChattingService {
@GET("chat-rooms/list")
suspend fun getChattingRoomList(
@Query("page") page: Int,
): Response<GetChattingRoomListResponseDTO?>

@GET("chat-rooms/{chatRoomId}/user-list")
suspend fun getChattingCrewList(
@Path("chatRoomId") chatRoomId: Long,
): Response<GetChattingCrewListResponseDTO?>

@GET("chat-rooms/{chatRoomId}")
suspend fun getChattingRoomInfo(
@Path("chatRoomId") chatRoomId: Long,
): Response<ChatRoomInfoDTO?>

@Multipart
@PATCH("chat-rooms/{chatRoomId}/image")
suspend fun patchChattingRoomImage(
@Path("chatRoomId") chatRoomId: Long,
@Part chatRoomImage: MultipartBody.Part,
): Response<PatchChattingRoomImageResponseDTO?>

@DELETE("chat-rooms/{chatRoomId}")
suspend fun deleteChattingRoom(
@Path("chatRoomId") chatRoomId: Long,
): Response<DeleteChattingRoomResponseDTO?>

@DELETE("chat-rooms/{chatRoomId}/users/{userId}")
suspend fun deleteChattingCrewKickOut(
@Path("chatRoomId") chatRoomId: Long,
@Path("userId") userId: Long,
): Response<DeleteChattingCrewKickOutResponseDTO?>

@GET("chats/{chatRoomId}")
suspend fun getChattingHistory(
@Path("chatRoomId") chatRoomId: Long,
@Query("page") page: Int,
@Query("size") size: Int?, // default = 20
): Response<GetChattingHistoryResponseDTO?>
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ class FCMTokenService : FirebaseMessagingService() {

val data = message.data

Log.e("MSG", "${data["boardId"]} / ${data["acceptStatus"]} / $title / $body")
// 채팅 알림 - data = {chatRoomId}
// 직관 신청 알림 - data = {boardId, acceptStatus}
Log.e("MSG", "$data / $title / $body")
if (title.isNotEmpty() && body.isNotEmpty()) {
showNotification(data, title, body)
}
Expand Down Expand Up @@ -92,15 +94,28 @@ class FCMTokenService : FirebaseMessagingService() {
title: String,
body: String,
) {
val notificationBuilder = getNotificationBuilder(PUST_NOTIFICATION_CHANNEL_ID)
var channelId: String = ""
var channelName: String = ""
if (data.containsKey("acceptStatus")) { // 직관 신청 알림
channelId = ENROLL_CHANNEL_ID
channelName = ENROLL_CHANNEL_NAME
} else { // 채팅 알림
channelId = CHATTING_CHANNEL_ID
channelName = CHATTING_CHANNEL_NAME
}
val notificationBuilder = getNotificationBuilder(channelId)
val builder = notificationHandler.createNotificationBuilder(data, title, body, notificationBuilder)
createNotificationChannel(PUST_NOTIFICATION_CHANNEL_ID, PUST_NOTIFICATION_CHANNEL_NAME, builder)
createNotificationChannel(channelId, channelName, builder)
}

suspend fun getToken(): String = FirebaseMessaging.getInstance().token.await()

companion object {
const val PUST_NOTIFICATION_CHANNEL_ID = "CatchmateNotificationChannel"
const val PUST_NOTIFICATION_CHANNEL_NAME = "CatchmateNotificationChannel"
const val ENROLL_CHANNEL_ID = "EnrollChannel"
const val ENROLL_CHANNEL_NAME = "직관 신청 알림"
const val CHATTING_CHANNEL_ID = "ChattingChannel"
const val CHATTING_CHANNEL_NAME = "채팅 알림"
const val EVENT_CHANNEL_ID = "EventChannel"
const val EVENT_CHANNEL_NAME = "이벤트 알림"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.catchmate.data.di

import com.catchmate.data.datasource.remote.ChatWebSocketClient
import com.catchmate.data.repository.ChatWebSocketRepositoryImpl
import com.catchmate.domain.repository.ChatWebSocketRepository
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object ChatWebSocketModule {
@Provides
fun provideChatWebSocketRepository(chatWebSocketRepositoryImpl: ChatWebSocketRepositoryImpl): ChatWebSocketRepository =
chatWebSocketRepositoryImpl

@Provides
@Singleton
fun provideChatWebSocketClient(): ChatWebSocketClient = ChatWebSocketClient()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.catchmate.data.di

import com.catchmate.data.repository.ChattingRepositoryImpl
import com.catchmate.domain.repository.ChattingRepository
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent

@Module
@InstallIn(SingletonComponent::class)
object ChattingModule {
@Provides
fun provideChattingRepository(chattingRepositoryImpl: ChattingRepositoryImpl): ChattingRepository = chattingRepositoryImpl
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.catchmate.data.dto.chatting

data class ChatMessageIdDTO(
val timestamp: Long? = null,
val date: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.catchmate.data.dto.chatting

data class ChatMessageInfoDTO(
val id: ChatMessageIdDTO,
val roomId: Long? = null,
val content: String,
val senderId: Long,
val messageType: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.catchmate.data.dto.chatting

import com.catchmate.data.dto.board.BoardDTO

data class ChatRoomInfoDTO(
val chatRoomId: Long,
val boardInfo: BoardDTO,
val participantCount: Int,
val lastMessageAt: String?,
val lastMessageContent: String?,
val chatRoomImage: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.catchmate.data.dto.chatting

data class DeleteChattingCrewKickOutResponseDTO(
val state: Boolean,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.catchmate.data.dto.chatting

data class DeleteChattingRoomResponseDTO(
val state: Boolean,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.catchmate.data.dto.chatting

import com.catchmate.data.dto.user.GetUserProfileResponseDTO

data class GetChattingCrewListResponseDTO(
val userInfoList: List<GetUserProfileResponseDTO>,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.catchmate.data.dto.chatting

data class GetChattingHistoryResponseDTO(
val chatMessageInfoList: List<ChatMessageInfoDTO>,
val totalPages: Int,
val totalElements: Int,
val isFirst: Boolean,
val isLast: Boolean,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.catchmate.data.dto.chatting

data class GetChattingRoomListResponseDTO(
val chatRoomInfoList: List<ChatRoomInfoDTO>,
val totalPages: Int,
val totalElements: Int,
val isFirst: Boolean,
val isLast: Boolean,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.catchmate.data.dto.chatting

data class PatchChattingRoomImageResponseDTO(
val state: Boolean,
)
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ object BoardMapper {
isLast = responseDTO.isLast,
)

private fun toBoard(dto: BoardDTO): Board =
fun toBoard(dto: BoardDTO): Board =
Board(
boardId = dto.boardId,
title = dto.title,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package com.catchmate.data.mapper

import com.catchmate.data.dto.chatting.ChatMessageIdDTO
import com.catchmate.data.dto.chatting.ChatMessageInfoDTO
import com.catchmate.data.dto.chatting.ChatRoomInfoDTO
import com.catchmate.data.dto.chatting.DeleteChattingCrewKickOutResponseDTO
import com.catchmate.data.dto.chatting.DeleteChattingRoomResponseDTO
import com.catchmate.data.dto.chatting.GetChattingCrewListResponseDTO
import com.catchmate.data.dto.chatting.GetChattingHistoryResponseDTO
import com.catchmate.data.dto.chatting.GetChattingRoomListResponseDTO
import com.catchmate.data.dto.chatting.PatchChattingRoomImageResponseDTO
import com.catchmate.data.mapper.BoardMapper.toBoard
import com.catchmate.data.mapper.UserMapper.toGetUserProfileResponse
import com.catchmate.domain.model.chatting.ChatMessageId
import com.catchmate.domain.model.chatting.ChatMessageInfo
import com.catchmate.domain.model.chatting.ChatRoomInfo
import com.catchmate.domain.model.chatting.DeleteChattingCrewKickOutResponse
import com.catchmate.domain.model.chatting.DeleteChattingRoomResponse
import com.catchmate.domain.model.chatting.GetChattingCrewListResponse
import com.catchmate.domain.model.chatting.GetChattingHistoryResponse
import com.catchmate.domain.model.chatting.GetChattingRoomListResponse
import com.catchmate.domain.model.chatting.PatchChattingRoomImageResponse

object ChattingMapper {
fun toGetChattingRoomListResponse(dto: GetChattingRoomListResponseDTO): GetChattingRoomListResponse =
GetChattingRoomListResponse(
chatRoomInfoList = dto.chatRoomInfoList.map { toChatRoomInfo(it) },
totalPages = dto.totalPages,
totalElements = dto.totalElements,
isFirst = dto.isFirst,
isLast = dto.isLast,
)

fun toChatRoomInfo(dto: ChatRoomInfoDTO): ChatRoomInfo =
ChatRoomInfo(
chatRoomId = dto.chatRoomId,
boardInfo = toBoard(dto.boardInfo),
participantCount = dto.participantCount,
lastMessageAt = dto.lastMessageAt,
lastMessageContent = dto.lastMessageContent,
chatRoomImage = dto.chatRoomImage,
)

fun toGetChattingHistoryResponse(dto: GetChattingHistoryResponseDTO): GetChattingHistoryResponse =
GetChattingHistoryResponse(
chatMessageInfoList = dto.chatMessageInfoList.map { toChatMessageInfo(it) },
totalPages = dto.totalPages,
totalElements = dto.totalElements,
isFirst = dto.isFirst,
isLast = dto.isLast,
)

private fun toChatMessageInfo(dto: ChatMessageInfoDTO): ChatMessageInfo =
ChatMessageInfo(
id = toChatMessageId(dto.id),
roomId = dto.roomId,
content = dto.content,
senderId = dto.senderId,
messageType = dto.messageType,
)

private fun toChatMessageId(dto: ChatMessageIdDTO): ChatMessageId =
ChatMessageId(
timestamp = dto.timestamp,
date = dto.date,
)

fun toGetChattingCrewListResponse(dto: GetChattingCrewListResponseDTO): GetChattingCrewListResponse =
GetChattingCrewListResponse(
userInfoList = dto.userInfoList.map { toGetUserProfileResponse(it) },
)

fun toDeleteChattingRoomResponse(dto: DeleteChattingRoomResponseDTO): DeleteChattingRoomResponse =
DeleteChattingRoomResponse(
state = dto.state,
)

fun toDeleteChattingCrewKickOutResponse(dto: DeleteChattingCrewKickOutResponseDTO): DeleteChattingCrewKickOutResponse =
DeleteChattingCrewKickOutResponse(
state = dto.state,
)

fun toPatchChattingRoomImageResponse(dto: PatchChattingRoomImageResponseDTO): PatchChattingRoomImageResponse =
PatchChattingRoomImageResponse(
state = dto.state,
)
}
Loading