Skip to content

Commit

Permalink
[AN] feat: 채팅 로컬 저장 구현 (#481)
Browse files Browse the repository at this point in the history
  • Loading branch information
gaeun5744 authored Aug 20, 2024
1 parent fe2c6b1 commit 518a7ed
Show file tree
Hide file tree
Showing 75 changed files with 1,424 additions and 249 deletions.
4 changes: 4 additions & 0 deletions android/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,11 @@ dependencies {
implementation(libs.bundles.datastore)
implementation(libs.bundles.animation)
implementation(libs.bundles.stomp)
implementation(libs.bundles.room)
testImplementation(libs.bundles.test)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.assertj)
androidTestImplementation(libs.androidx.espresso.core)
annotationProcessor(libs.room.compiler)
kapt(libs.room.compiler)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package com.happy.friendogly

import android.content.Context
import androidx.room.Room
import androidx.test.core.app.ApplicationProvider
import com.happy.friendogly.local.model.ChatMemberEntity
import com.happy.friendogly.local.model.ChatMessageEntity
import com.happy.friendogly.local.room.ChatMessageDao
import com.happy.friendogly.local.room.ChatMessageDatabase
import com.happy.friendogly.local.room.ChatRoomDao
import com.happy.friendogly.local.room.MessageTypeEntity
import kotlinx.coroutines.runBlocking
import org.assertj.core.api.Assertions.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Test
import java.time.LocalDateTime

class ChatMessageDatabaseTest {
private lateinit var chatMessageDao: ChatMessageDao
private lateinit var chatRoomDao: ChatRoomDao

private lateinit var db: ChatMessageDatabase

@Before
fun setUp() {
db =
Room.inMemoryDatabaseBuilder(
context = ApplicationProvider.getApplicationContext<Context>(),
klass = ChatMessageDatabase::class.java,
).build()

chatMessageDao = db.chatMessageDao()
chatRoomDao = db.chatRoomDao()
}

@After
fun closeDb() {
db.close()
}

@Test
fun `can_save_chat_message_and_get_the_data`() {
// when
runBlocking {
chatMessageDao.insert(
DUMMY_MY_MESSAGE,
)
}

// then
runBlocking {
val actual: List<ChatMessageEntity> = chatMessageDao.getAll()
assertThat(actual).contains(DUMMY_MY_MESSAGE)
}
}

@Test
fun `can_update_chatRoom_message`() {
// when
runBlocking {
chatRoomDao.addMessageToChatRoom(2, DUMMY_MY_MESSAGE)
chatRoomDao.addMessageToChatRoom(2, DUMMY_CHAT_MESSAGE)
}

runBlocking {
val messages = chatRoomDao.getMessagesByRoomId(2)
assertThat(messages).contains(DUMMY_MY_MESSAGE, DUMMY_CHAT_MESSAGE)
}
}

companion object {
private val DUMMY_MY_MESSAGE =
ChatMessageEntity(
createdAt = LocalDateTime.of(2024, 6, 12, 14, 2),
member =
ChatMemberEntity(
1,
"벼리",
"",
),
content = "",
type = MessageTypeEntity.CHAT,
id = 1,
)

private val DUMMY_CHAT_MESSAGE =
ChatMessageEntity(
createdAt = LocalDateTime.of(2024, 6, 12, 14, 2),
member =
ChatMemberEntity(
2,
"벼리",
"",
),
content = "ZZZZZZZZ",
type = MessageTypeEntity.CHAT,
id = 2,
)
}
}
3 changes: 2 additions & 1 deletion android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
<activity
android:name=".presentation.ui.MainActivity"
android:exported="false"
android:launchMode="singleTask"
android:screenOrientation="portrait" />
<activity
android:name=".presentation.ui.chatlist.chat.ChatActivity"
Expand Down Expand Up @@ -116,4 +117,4 @@
</service>
</application>

</manifest>
</manifest>
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.happy.friendogly.application.di

import android.content.Context
import androidx.room.Room
import com.happy.friendogly.BuildConfig
import com.happy.friendogly.analytics.AnalyticsHelper
import com.happy.friendogly.crashlytics.CrashlyticsHelper
Expand Down Expand Up @@ -45,14 +46,17 @@ import com.happy.friendogly.domain.repository.WebSocketRepository
import com.happy.friendogly.domain.repository.WoofRepository
import com.happy.friendogly.domain.usecase.ConnectWebsocketUseCase
import com.happy.friendogly.domain.usecase.DeleteAddressUseCase
import com.happy.friendogly.domain.usecase.DeleteAlarmSettingUseCase
import com.happy.friendogly.domain.usecase.DeleteChatAlarmUseCase
import com.happy.friendogly.domain.usecase.DeleteClubMemberUseCase
import com.happy.friendogly.domain.usecase.DeleteTokenUseCase
import com.happy.friendogly.domain.usecase.DeleteWoofAlarmUseCase
import com.happy.friendogly.domain.usecase.DisconnectWebsocketUseCase
import com.happy.friendogly.domain.usecase.GetAddressUseCase
import com.happy.friendogly.domain.usecase.GetAlarmSettingUseCase
import com.happy.friendogly.domain.usecase.GetChatAlarmUseCase
import com.happy.friendogly.domain.usecase.GetChatListUseCase
import com.happy.friendogly.domain.usecase.GetChatMemberUseCase
import com.happy.friendogly.domain.usecase.GetChatMessagesUseCase
import com.happy.friendogly.domain.usecase.GetChatRoomClubUseCase
import com.happy.friendogly.domain.usecase.GetClubUseCase
import com.happy.friendogly.domain.usecase.GetFootprintInfoUseCase
import com.happy.friendogly.domain.usecase.GetFootprintMarkBtnInfoUseCase
Expand All @@ -65,6 +69,7 @@ import com.happy.friendogly.domain.usecase.GetNearFootprintsUseCase
import com.happy.friendogly.domain.usecase.GetPetsMineUseCase
import com.happy.friendogly.domain.usecase.GetPetsUseCase
import com.happy.friendogly.domain.usecase.GetSearchingClubsUseCase
import com.happy.friendogly.domain.usecase.GetWoofAlarmUseCase
import com.happy.friendogly.domain.usecase.KakaoLoginUseCase
import com.happy.friendogly.domain.usecase.PatchWalkStatusUseCase
import com.happy.friendogly.domain.usecase.PostClubMemberUseCase
Expand All @@ -78,14 +83,18 @@ import com.happy.friendogly.domain.usecase.PublishLeaveUseCase
import com.happy.friendogly.domain.usecase.PublishSendMessageUseCase
import com.happy.friendogly.domain.usecase.SaveAddressUseCase
import com.happy.friendogly.domain.usecase.SaveAlamTokenUseCase
import com.happy.friendogly.domain.usecase.SaveAlarmSettingUseCase
import com.happy.friendogly.domain.usecase.SaveChatAlarmUseCase
import com.happy.friendogly.domain.usecase.SaveChatMessageUseCase
import com.happy.friendogly.domain.usecase.SaveJwtTokenUseCase
import com.happy.friendogly.domain.usecase.SaveWoofAlarmUseCase
import com.happy.friendogly.domain.usecase.SubScribeMessageUseCase
import com.happy.friendogly.kakao.source.KakaoLoginDataSourceImpl
import com.happy.friendogly.local.di.AddressModule
import com.happy.friendogly.local.di.AlarmModule
import com.happy.friendogly.local.di.AlarmTokenModule
import com.happy.friendogly.local.di.ChatAlarmModule
import com.happy.friendogly.local.di.TokenManager
import com.happy.friendogly.local.di.WoofAlarmModule
import com.happy.friendogly.local.room.ChatMessageDatabase
import com.happy.friendogly.local.source.AddressDataSourceImpl
import com.happy.friendogly.local.source.AlarmSettingDataSourceImpl
import com.happy.friendogly.local.source.TokenDataSourceImpl
Expand Down Expand Up @@ -115,8 +124,12 @@ class AppModule(context: Context) {
private val authenticationListener: AuthenticationListener =
AuthenticationListenerImpl(context, tokenManager)
private val addressModule = AddressModule(context)
private val alarmModule = AlarmModule(context)
private val chatAlarmModule = ChatAlarmModule(context)
private val woofAlarmModule = WoofAlarmModule(context)
private val alarmTokenModule = AlarmTokenModule(context)
private val chatDataBase =
Room.databaseBuilder(context, ChatMessageDatabase::class.java, "chat").build()
private val chatRoomDao = chatDataBase.chatRoomDao()

// service
private val authService =
Expand Down Expand Up @@ -202,7 +215,10 @@ class AppModule(context: Context) {
WebSocketDataSourceImpl(service = webSocketService)
private val chatDataSource: ChatDataSource = ChatDataSourceImpl(service = chatService)
private val alarmSettingDataSource: AlarmSettingDataSource =
AlarmSettingDataSourceImpl(alarmModule = alarmModule)
AlarmSettingDataSourceImpl(
chatAlarmModule = chatAlarmModule,
woofAlarmModule = woofAlarmModule,
)
private val alarmTokenDataSource: AlarmTokenDataSource =
AlamTokenDataSourceImpl(service = alarmTokenService)

Expand All @@ -218,9 +234,10 @@ class AppModule(context: Context) {
private val petRepository: PetRepository = PetRepositoryImpl(source = petDataSource)
private val addressRepository: AddressRepository =
AddressRepositoryImpl(addressDataSource = addressDataSource)
val webSocketRepository: WebSocketRepository =
private val webSocketRepository: WebSocketRepository =
WebSocketRepositoryImpl(source = webSocketDataSource)
private val chatRepository: ChatRepository = ChatRepositoryImpl(source = chatDataSource)
private val chatRepository: ChatRepository =
ChatRepositoryImpl(source = chatDataSource, chatRoomDao = chatRoomDao)
private val alarmSettingRepository: AlarmSettingRepository =
AlarmSettingRepositoryImpl(source = alarmSettingDataSource)
private val alarmTokenRepository: AlarmTokenRepository =
Expand Down Expand Up @@ -270,6 +287,12 @@ class AppModule(context: Context) {
val getChatListUseCase: GetChatListUseCase = GetChatListUseCase(repository = chatRepository)
val getChatMemberUseCase: GetChatMemberUseCase =
GetChatMemberUseCase(repository = chatRepository)
val saveChatMessageUseCase: SaveChatMessageUseCase =
SaveChatMessageUseCase(repository = chatRepository)
val getChatMessagesUseCase: GetChatMessagesUseCase =
GetChatMessagesUseCase(repository = chatRepository)
val getChatRoomClubUseCase: GetChatRoomClubUseCase =
GetChatRoomClubUseCase(repository = chatRepository)
val publishEnterUseCase: PublishEnterUseCase =
PublishEnterUseCase(repository = webSocketRepository)
val publishSendUseCase: PublishSendMessageUseCase =
Expand All @@ -282,12 +305,18 @@ class AppModule(context: Context) {
ConnectWebsocketUseCase(repository = webSocketRepository)
val disconnectWebsocketUseCase: DisconnectWebsocketUseCase =
DisconnectWebsocketUseCase(repository = webSocketRepository)
val deleteAlarmSettingUseCase: DeleteAlarmSettingUseCase =
DeleteAlarmSettingUseCase(repository = alarmSettingRepository)
val saveAlarmSettingUseCase: SaveAlarmSettingUseCase =
SaveAlarmSettingUseCase(repository = alarmSettingRepository)
val getAlarmSettingUseCase: GetAlarmSettingUseCase =
GetAlarmSettingUseCase(repository = alarmSettingRepository)
val deleteChatAlarmUseCase: DeleteChatAlarmUseCase =
DeleteChatAlarmUseCase(repository = alarmSettingRepository)
val saveChatAlarmUseCase: SaveChatAlarmUseCase =
SaveChatAlarmUseCase(repository = alarmSettingRepository)
val saveWoofAlarmUseCase: SaveWoofAlarmUseCase =
SaveWoofAlarmUseCase(repository = alarmSettingRepository)
val getWoofAlarmUseCase: GetWoofAlarmUseCase =
GetWoofAlarmUseCase(repository = alarmSettingRepository)
val deleteWoofAlarmUseCase: DeleteWoofAlarmUseCase =
DeleteWoofAlarmUseCase(repository = alarmSettingRepository)
val getChatAlarmUseCase: GetChatAlarmUseCase =
GetChatAlarmUseCase(repository = alarmSettingRepository)
val saveAlarmTokenUseCase: SaveAlamTokenUseCase =
SaveAlamTokenUseCase(repository = alarmTokenRepository)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import com.happy.friendogly.domain.model.ChatRooms
fun ChatMemberDto.toDomain(): ChatMember =
ChatMember(
isOwner = isOwner,
memberId = memberId,
memberName = memberName,
memberProfileImageUrl = memberProfileImageUrl,
id = memberId,
name = memberName,
profileImageUrl = memberProfileImageUrl,
)

fun ChatRoomListDto.toDomain(): ChatRooms =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.happy.friendogly.data.mapper

import com.happy.friendogly.data.model.ChatRoomClubDto
import com.happy.friendogly.domain.model.ChatRoomClub

fun ChatRoomClubDto.toDomain(): ChatRoomClub =
ChatRoomClub(
clubId = clubId,
allowedGender = allowedGender.map { it.toDomain() },
allowedSize = allowedSize.map { it.toDomain() },
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,51 @@ 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.ChatMember
import com.happy.friendogly.domain.model.Message

fun MessageDto.toOther(): Message.Other =
Message.Other(
memberId = senderMemberId,
name = senderName,
member =
ChatMember(
id = senderMemberId,
name = senderName,
profileImageUrl = profilePictureUrl ?: "",
),
content = content ?: "",
dateTime = createdAt,
profileUrl = profilePictureUrl,
createdAt = createdAt,
)

fun MessageDto.toMine(): Message.Mine =
Message.Mine(
member =
ChatMember(
id = senderMemberId,
name = senderName,
profileImageUrl = profilePictureUrl ?: "",
),
content = content ?: "",
dateTime = createdAt,
createdAt = createdAt,
)

fun MessageDto.toEnter(): ChatComponent.Enter =
ChatComponent.Enter(
name = senderName,
member =
ChatMember(
id = senderMemberId,
name = senderName,
profileImageUrl = profilePictureUrl ?: "",
),
createdAt = createdAt,
)

fun MessageDto.toLeave(): ChatComponent.Leave =
ChatComponent.Leave(
name = senderName,
member =
ChatMember(
id = senderMemberId,
name = senderName,
profileImageUrl = profilePictureUrl ?: "",
),
createdAt = createdAt,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.happy.friendogly.data.model

data class ChatRoomClubDto(
val clubId: Long,
val allowedGender: List<GenderDto>,
val allowedSize: List<SizeTypeDto>,
)
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,17 @@ package com.happy.friendogly.data.repository
import com.happy.friendogly.data.source.AlarmSettingDataSource
import com.happy.friendogly.domain.repository.AlarmSettingRepository

class AlarmSettingRepositoryImpl(private val source: AlarmSettingDataSource) : AlarmSettingRepository {
override suspend fun saveAlarm(isSet: Boolean): Result<Unit> = source.saveAlarm(isSet)
class AlarmSettingRepositoryImpl(private val source: AlarmSettingDataSource) :
AlarmSettingRepository {
override suspend fun saveChatSetting(isSet: Boolean): Result<Unit> = source.saveChatSetting(isSet)

override suspend fun getAlarm(): Result<Boolean> = source.getAlarm()
override suspend fun getChatSetting(): Result<Boolean> = source.getChatSetting()

override suspend fun deleteAlarm(): Result<Unit> = source.deleteAlarm()
override suspend fun deleteChatSetting(): Result<Unit> = source.deleteChatSetting()

override suspend fun saveWoofSetting(isSet: Boolean): Result<Unit> = source.saveWoofSetting(isSet)

override suspend fun getWoofSetting(): Result<Boolean> = source.getWoofSetting()

override suspend fun deleteWoofSetting(): Result<Unit> = source.deleteWoofSetting()
}
Loading

0 comments on commit 518a7ed

Please sign in to comment.