Skip to content

Commit

Permalink
[AN] 웹소켓 연결 (#336)
Browse files Browse the repository at this point in the history
  • Loading branch information
gaeun5744 authored and takoyakimchi committed Oct 23, 2024
1 parent c806f25 commit f5db1c9
Show file tree
Hide file tree
Showing 46 changed files with 775 additions and 62 deletions.
3 changes: 3 additions & 0 deletions android/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ val kakaoNativeAppKey = localProperties.getProperty("KAKAO_NATIVE_APP_KEY") ?: "
val kakaoOauthHost = localProperties.getProperty("KAKAO_OAUTH_HOST") ?: ""
val naverClientId = localProperties.getProperty("NAVER_CLIEND_ID") ?: ""
val baseUrl = localProperties.getProperty("base_url") ?: ""
val websocketUrl = localProperties.getProperty("websocket_url") ?: ""

android {
namespace = "com.happy.friendogly"
Expand All @@ -39,6 +40,7 @@ android {
resValue("string", "KAKAO_OAUTH_HOST", kakaoOauthHost)
buildConfigField("String", "NAVER_CLIEND_ID", naverClientId)
buildConfigField("String", "base_url", baseUrl)
buildConfigField("String", "websocket_url", websocketUrl)
}

buildTypes {
Expand Down Expand Up @@ -82,6 +84,7 @@ dependencies {
implementation(libs.bundles.network)
implementation(libs.bundles.datastore)
implementation(libs.bundles.animation)
implementation(libs.bundles.stomp)
testImplementation(libs.bundles.test)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import com.happy.friendogly.analytics.AnalyticsHelper
import com.happy.friendogly.crashlytics.CrashlyticsHelper
import com.happy.friendogly.data.repository.AddressRepositoryImpl
import com.happy.friendogly.data.repository.AuthRepositoryImpl
import com.happy.friendogly.data.repository.ChatRepositoryImpl
import com.happy.friendogly.data.repository.ClubRepositoryImpl
import com.happy.friendogly.data.repository.KakaoLoginRepositoryImpl
import com.happy.friendogly.data.repository.MemberRepositoryImpl
import com.happy.friendogly.data.repository.PetRepositoryImpl
import com.happy.friendogly.data.repository.TokenRepositoryImpl
import com.happy.friendogly.data.repository.WebSocketRepositoryImpl
import com.happy.friendogly.data.repository.WoofRepositoryImpl
import com.happy.friendogly.data.source.AddressDataSource
import com.happy.friendogly.data.source.AuthDataSource
Expand All @@ -19,14 +21,17 @@ import com.happy.friendogly.data.source.KakaoLoginDataSource
import com.happy.friendogly.data.source.MemberDataSource
import com.happy.friendogly.data.source.PetDataSource
import com.happy.friendogly.data.source.TokenDataSource
import com.happy.friendogly.data.source.WebSocketDataSource
import com.happy.friendogly.data.source.WoofDataSource
import com.happy.friendogly.domain.repository.AddressRepository
import com.happy.friendogly.domain.repository.AuthRepository
import com.happy.friendogly.domain.repository.ChatRepository
import com.happy.friendogly.domain.repository.ClubRepository
import com.happy.friendogly.domain.repository.KakaoLoginRepository
import com.happy.friendogly.domain.repository.MemberRepository
import com.happy.friendogly.domain.repository.PetRepository
import com.happy.friendogly.domain.repository.TokenRepository
import com.happy.friendogly.domain.repository.WebSocketRepository
import com.happy.friendogly.domain.repository.WoofRepository
import com.happy.friendogly.domain.usecase.DeleteAddressUseCase
import com.happy.friendogly.domain.usecase.DeleteClubMemberUseCase
Expand Down Expand Up @@ -59,11 +64,13 @@ import com.happy.friendogly.local.source.AddressDataSourceImpl
import com.happy.friendogly.local.source.TokenDataSourceImpl
import com.happy.friendogly.remote.api.AuthenticationListener
import com.happy.friendogly.remote.api.BaseUrl
import com.happy.friendogly.remote.api.WebSocketService
import com.happy.friendogly.remote.di.RemoteModule
import com.happy.friendogly.remote.source.AuthDataSourceImpl
import com.happy.friendogly.remote.source.ClubDataSourceImpl
import com.happy.friendogly.remote.source.MemberDataSourceImpl
import com.happy.friendogly.remote.source.PetDataSourceImpl
import com.happy.friendogly.remote.source.WebSocketDataSourceImpl
import com.happy.friendogly.remote.source.WoofDataSourceImpl

class AppModule(context: Context) {
Expand All @@ -72,6 +79,7 @@ class AppModule(context: Context) {
val crashlyticsHelper = CrashlyticsHelper()

private val baseUrl = BaseUrl(BuildConfig.base_url)
private val websocketUrl = BaseUrl(BuildConfig.websocket_url)

private val tokenManager = TokenManager(context)
private val authenticationListener: AuthenticationListener =
Expand Down Expand Up @@ -113,6 +121,25 @@ class AppModule(context: Context) {
authenticationListener = authenticationListener,
)

private val webSocketService =
WebSocketService(
client =
RemoteModule.createStumpClient(
baseUrl = baseUrl,
tokenManager = tokenManager,
authenticationListener = authenticationListener,
),
tokenManager = tokenManager,
baseUrl = websocketUrl,
)

private val chatService =
RemoteModule.createChatService(
baseUrl = baseUrl,
tokenManager = tokenManager,
authenticationListener = authenticationListener,
)

// data source
private val authDataSource: AuthDataSource = AuthDataSourceImpl(service = authService)
private val clubDataSource: ClubDataSource = ClubDataSourceImpl(service = clubService)
Expand All @@ -123,6 +150,8 @@ class AppModule(context: Context) {
private val woofDataSource: WoofDataSource = WoofDataSourceImpl(service = woofService)
private val memberDataSource: MemberDataSource = MemberDataSourceImpl(service = memberService)
private val petDataSource: PetDataSource = PetDataSourceImpl(service = petService)
private val webSocketDataSource: WebSocketDataSource =
WebSocketDataSourceImpl(service = webSocketService)

// repository
private val authRepository: AuthRepository = AuthRepositoryImpl(source = authDataSource)
Expand All @@ -135,6 +164,9 @@ class AppModule(context: Context) {
private val petRepository: PetRepository = PetRepositoryImpl(source = petDataSource)
private val addressRepository: AddressRepository =
AddressRepositoryImpl(addressDataSource = addressDataSource)
val webSocketRepository: WebSocketRepository =
WebSocketRepositoryImpl(source = webSocketDataSource)
val chatRepository: ChatRepository = ChatRepositoryImpl(service = chatService)

// use case
val postKakaoLoginUseCase: PostKakaoLoginUseCase =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.happy.friendogly.data.mapper

import com.happy.friendogly.data.model.ChatMemberDto
import com.happy.friendogly.data.model.ChatRoomListDto
import com.happy.friendogly.domain.model.ChatMember
import com.happy.friendogly.domain.model.ChatRoom
import com.happy.friendogly.domain.model.ChatRooms

fun ChatMemberDto.toDomain(): ChatMember =
ChatMember(
isOwner = isOwner,
memberId = memberId,
memberName = memberName,
memberProfileImageUrl = memberProfileImageUrl,
)

fun ChatRoomListDto.toDomain(): ChatRooms =
ChatRooms(
myMemberId = myMemberId,
chatRooms =
chatRooms.map {
ChatRoom(
chatRoomId = it.chatRoomId,
clubName = it.clubName,
memberCount = it.memberCount,
clubImageUrl = it.clubImageUrl,
)
},
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.happy.friendogly.data.mapper

import com.happy.friendogly.data.model.MessageDto
import com.happy.friendogly.domain.model.ChatComponent
import com.happy.friendogly.domain.model.Message

fun MessageDto.toOther(): Message.Other =
Message.Other(
memberId = senderMemberId,
name = senderName,
content = content!!,
dateTime = createdAt,
profileUrl = profilePictureUrl,
)

fun MessageDto.toMine(): Message.Mine =
Message.Mine(
content = content!!,
dateTime = createdAt,
)

fun MessageDto.toEnter(): ChatComponent.Enter =
ChatComponent.Enter(
name = senderName,
)

fun MessageDto.toLeave(): ChatComponent.Leave =
ChatComponent.Leave(
name = senderName,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.happy.friendogly.data.model

data class ChatMemberDto(
val isOwner: Boolean,
val memberId: Long,
val memberName: String,
val memberProfileImageUrl: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.happy.friendogly.data.model

data class ChatRoomDto(
val chatRoomId: Long,
val clubName: String,
val memberCount: Int,
val clubImageUrl: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.happy.friendogly.data.model

data class ChatRoomListDto(
val myMemberId: Long,
val chatRooms: List<ChatRoomDto>,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.happy.friendogly.data.model

import java.time.LocalDateTime

data class MessageDto(
val messageType: MessageTypeDto,
val senderMemberId: Long,
val senderName: String,
val content: String?,
val createdAt: LocalDateTime,
val profilePictureUrl: String?,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.happy.friendogly.data.model

enum class MessageTypeDto {
ENTER,
CHAT,
LEAVE,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.happy.friendogly.data.repository

import com.happy.friendogly.data.mapper.toDomain
import com.happy.friendogly.domain.model.ChatMember
import com.happy.friendogly.domain.model.ChatRooms
import com.happy.friendogly.domain.repository.ChatRepository
import com.happy.friendogly.remote.api.ChatService
import com.happy.friendogly.remote.mapper.toData

class ChatRepositoryImpl(private val service: ChatService) : ChatRepository {
override suspend fun getChatList(): Result<ChatRooms> =
runCatching {
service.getChatList().data.toData().toDomain()
}

override suspend fun getMembers(chatRoomId: Long): Result<List<ChatMember>> =
runCatching {
service.getChatMembers(chatRoomId).data.map { it.toData() }.map { it.toDomain() }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.happy.friendogly.data.repository

import com.happy.friendogly.data.mapper.toEnter
import com.happy.friendogly.data.mapper.toLeave
import com.happy.friendogly.data.mapper.toOther
import com.happy.friendogly.data.model.MessageTypeDto
import com.happy.friendogly.data.source.WebSocketDataSource
import com.happy.friendogly.domain.model.ChatComponent
import com.happy.friendogly.domain.repository.WebSocketRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map

class WebSocketRepositoryImpl(private val source: WebSocketDataSource) : WebSocketRepository {
override suspend fun publishInvite(memberId: Long) = source.publishInvite(memberId)

override suspend fun publishSend(
chatRoomId: Long,
content: String,
) = source.publishSend(chatRoomId, content)

override suspend fun publishLeave(chatRoomId: Long) = source.publishLeave(chatRoomId)

override suspend fun subscribeMessage(chatRoomId: Long): Flow<ChatComponent> =
source.subscribeMessage(chatRoomId).map {
when (it.messageType) {
MessageTypeDto.ENTER -> it.toEnter()
MessageTypeDto.LEAVE -> it.toLeave()
MessageTypeDto.CHAT -> it.toOther()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.happy.friendogly.data.source

import com.happy.friendogly.data.model.MessageDto
import kotlinx.coroutines.flow.Flow

interface WebSocketDataSource {
suspend fun publishInvite(memberId: Long)

suspend fun publishSend(
chatRoomId: Long,
content: String,
)

suspend fun publishLeave(chatRoomId: Long)

suspend fun subscribeMessage(chatRoomId: Long): Flow<MessageDto>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.happy.friendogly.domain.model

import java.time.LocalDate

sealed interface ChatComponent {
data class Date(val created: LocalDate) : ChatComponent

data class Enter(val name: String) : ChatComponent

data class Leave(val name: String) : ChatComponent
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.happy.friendogly.domain.model

data class ChatMember(
val isOwner: Boolean,
val memberId: Long,
val memberName: String,
val memberProfileImageUrl: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.happy.friendogly.domain.model

data class ChatRoom(
val chatRoomId: Long,
val clubName: String,
val memberCount: Int,
val clubImageUrl: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.happy.friendogly.domain.model

data class ChatRooms(
val myMemberId: Long,
val chatRooms: List<ChatRoom>,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.happy.friendogly.domain.model

import java.time.LocalDateTime

sealed interface Message : ChatComponent {
val content: String

data class Mine(
override val content: String,
val dateTime: LocalDateTime,
) : Message

data class Other(
val memberId: Long,
val name: String,
override val content: String,
val dateTime: LocalDateTime,
val profileUrl: String?,
) : Message
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.happy.friendogly.domain.repository

import com.happy.friendogly.domain.model.ChatMember
import com.happy.friendogly.domain.model.ChatRooms

interface ChatRepository {
suspend fun getChatList(): Result<ChatRooms>

suspend fun getMembers(chatRoomId: Long): Result<List<ChatMember>>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.happy.friendogly.domain.repository

import com.happy.friendogly.domain.model.ChatComponent
import kotlinx.coroutines.flow.Flow

interface WebSocketRepository {
suspend fun publishInvite(chatRoomId: Long)

suspend fun publishSend(
chatRoomId: Long,
content: String,
)

suspend fun publishLeave(chatRoomId: Long)

suspend fun subscribeMessage(chatRoomId: Long): Flow<ChatComponent>
}
Loading

0 comments on commit f5db1c9

Please sign in to comment.