diff --git a/core/data/src/main/java/com/teampatch/core/data/di/DataSingletonModule.kt b/core/data/src/main/java/com/teampatch/core/data/di/DataSingletonModule.kt index dd1c1c20..1f922987 100644 --- a/core/data/src/main/java/com/teampatch/core/data/di/DataSingletonModule.kt +++ b/core/data/src/main/java/com/teampatch/core/data/di/DataSingletonModule.kt @@ -3,10 +3,10 @@ package com.teampatch.core.data.di import com.teampatch.core.data.entity.TokenManagerImpl import com.teampatch.core.data.repository.AnswerRepositoryImpl import com.teampatch.core.data.repository.AppManagementRepositoryImpl -import com.teampatch.core.data.repository.MemoryCardRepositoryImpl import com.teampatch.core.data.repository.TodoOfflineRepositoryImpl import com.teampatch.core.data.repository.local.LocalAuthenticationRepositoryImpl import com.teampatch.core.data.repository.local.LocalGroupManagementRepositoryImpl +import com.teampatch.core.data.repository.local.LocalMemoryCardRepositoryImpl import com.teampatch.core.data.repository.local.LocalQuestionRepositoryImpl import com.teampatch.core.data.repository.local.LocalUserRepositoryImpl import com.teampatch.core.domain.entity.TokenManager @@ -34,7 +34,7 @@ internal abstract class DataSingletonModule { @Binds abstract fun bindsMemoryCardRepository( - memoryCardRepositoryImpl: MemoryCardRepositoryImpl, + localMemoryCardRepositoryImpl: LocalMemoryCardRepositoryImpl, ): MemoryCardRepository @Binds diff --git a/core/data/src/main/java/com/teampatch/core/data/mapper/MemoryCardMapper.kt b/core/data/src/main/java/com/teampatch/core/data/mapper/MemoryCardMapper.kt new file mode 100644 index 00000000..21492800 --- /dev/null +++ b/core/data/src/main/java/com/teampatch/core/data/mapper/MemoryCardMapper.kt @@ -0,0 +1,22 @@ +package com.teampatch.core.data.mapper + +import com.harmony.core.database.LOCAL_DB_DATE_TIME_FORMATTER +import com.harmony.core.database.model.MemoryCardEntity +import com.teampatch.core.domain.model.MemoryCard +import java.time.LocalDateTime + +fun MemoryCardEntity.toDomain( + writerName: String, +): MemoryCard = MemoryCard( + id = id.toString(), + writerTitle = "", + writerName = writerName, + text = title, + imageUrl = imageUrl, + dateTime = LocalDateTime.parse( + /* text = */ + modifiedAt, + /* formatter = */ + LOCAL_DB_DATE_TIME_FORMATTER + ) +) \ No newline at end of file diff --git a/core/data/src/main/java/com/teampatch/core/data/repository/MemoryCardRepositoryImpl.kt b/core/data/src/main/java/com/teampatch/core/data/repository/MemoryCardRepositoryImpl.kt deleted file mode 100644 index 833641e4..00000000 --- a/core/data/src/main/java/com/teampatch/core/data/repository/MemoryCardRepositoryImpl.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.teampatch.core.data.repository - -import com.teampatch.core.domain.fake.FakeMemoryCardQuestion -import com.teampatch.core.domain.model.MemoryCardQuestion -import com.teampatch.core.domain.repository.MemoryCardRepository -import java.io.InputStream -import javax.inject.Inject - -internal class MemoryCardRepositoryImpl @Inject constructor() : MemoryCardRepository { - - override suspend fun addCommunication( - memoryCardId: String, - question: String, - audioFile: InputStream, - ) { - } - - override suspend fun getQuestionMessage(memoryCardId: String): MemoryCardQuestion = FakeMemoryCardQuestion().get() -} \ No newline at end of file diff --git a/core/data/src/main/java/com/teampatch/core/data/repository/local/LocalMemoryCardRepositoryImpl.kt b/core/data/src/main/java/com/teampatch/core/data/repository/local/LocalMemoryCardRepositoryImpl.kt new file mode 100644 index 00000000..cd82e394 --- /dev/null +++ b/core/data/src/main/java/com/teampatch/core/data/repository/local/LocalMemoryCardRepositoryImpl.kt @@ -0,0 +1,63 @@ +package com.teampatch.core.data.repository.local + +import android.util.Log +import androidx.paging.PagingData +import com.harmony.core.database.dao.MemoryCardDao +import com.harmony.core.database.dao.UserDao +import com.teampatch.core.data.mapper.toDomain +import com.teampatch.core.domain.fake.FakeMemoryCardQuestion +import com.teampatch.core.domain.model.MemoryCard +import com.teampatch.core.domain.model.MemoryCardQuestion +import com.teampatch.core.domain.repository.MemoryCardRepository +import java.io.InputStream +import javax.inject.Inject +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.map + +internal class LocalMemoryCardRepositoryImpl @Inject constructor( + private val memoryCardDao: MemoryCardDao, + private val userDao: UserDao, +) : MemoryCardRepository { + + override suspend fun addCommunication( + memoryCardId: String, + question: String, + audioFile: InputStream, + ) { + } + + override suspend fun getQuestionMessage(memoryCardId: String): MemoryCardQuestion = FakeMemoryCardQuestion().get() + + override fun getMemoryCards(): Flow> { + return memoryCardDao.getAllMemoryStorage().map { memoryCardEntities -> + memoryCardEntities.mapNotNull { memoryCardEntity -> + val writerUserInfo = userDao.getUserById(memoryCardEntity.writtenUid).firstOrNull() + + if (writerUserInfo == null) { + Log.e(TAG, "function: getMemoryCards(), data: writerUserInfo is null") + return@mapNotNull null + } + + memoryCardEntity.toDomain(writerUserInfo.name) + } + .let { + PagingData.from(it) + } + } + } + + @OptIn(ExperimentalCoroutinesApi::class) + override fun getMemoryCardById(memoryCardId: String): Flow = memoryCardDao.getMemoryStorageById(memoryCardId.toLong()) + .flatMapLatest { memoryCardEntity -> + userDao.getUserById(memoryCardEntity.writtenUid).map { writerUserInfo -> + memoryCardEntity.toDomain(writerUserInfo.name) + } + } + + companion object { + private const val TAG = "MemoryCardRepositoryImpl" + } +} \ No newline at end of file diff --git a/core/database/build.gradle.kts b/core/database/build.gradle.kts index 936eca18..8ebc6928 100644 --- a/core/database/build.gradle.kts +++ b/core/database/build.gradle.kts @@ -23,6 +23,8 @@ dependencies { // optional - Paging 3 Integration implementation(libs.androidx.room.paging) + implementation(libs.squareup.moshi.kotlin) + testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) diff --git a/core/database/src/main/java/com/harmony/core/database/HarmonyDatabase.kt b/core/database/src/main/java/com/harmony/core/database/HarmonyDatabase.kt index 92ecfeaf..bc8294ca 100644 --- a/core/database/src/main/java/com/harmony/core/database/HarmonyDatabase.kt +++ b/core/database/src/main/java/com/harmony/core/database/HarmonyDatabase.kt @@ -2,11 +2,15 @@ package com.harmony.core.database import androidx.room.Database import androidx.room.RoomDatabase +import androidx.room.TypeConverters +import com.harmony.core.database.converters.SetStringTypeTypeConverter import com.harmony.core.database.dao.GroupDao +import com.harmony.core.database.dao.MemoryCardDao import com.harmony.core.database.dao.QuestionDao import com.harmony.core.database.dao.TodoDao import com.harmony.core.database.dao.UserDao import com.harmony.core.database.model.GroupEntity +import com.harmony.core.database.model.MemoryCardEntity import com.harmony.core.database.model.QuestionCommentEntity import com.harmony.core.database.model.QuestionEntity import com.harmony.core.database.model.TodoEntity @@ -18,15 +22,22 @@ import com.harmony.core.database.model.UserEntity UserEntity::class, GroupEntity::class, QuestionEntity::class, - QuestionCommentEntity::class + QuestionCommentEntity::class, + MemoryCardEntity::class ], version = 1 ) +@TypeConverters( + value = [ + SetStringTypeTypeConverter::class + ] +) internal abstract class HarmonyDatabase : RoomDatabase() { abstract fun todoDao(): TodoDao abstract fun userDao(): UserDao abstract fun groupDao(): GroupDao abstract fun questionDao(): QuestionDao + abstract fun memoryCardDao(): MemoryCardDao companion object { internal const val DB_NAME = "harmony.db" diff --git a/core/database/src/main/java/com/harmony/core/database/converters/RoomTypeConverter.kt b/core/database/src/main/java/com/harmony/core/database/converters/RoomTypeConverter.kt new file mode 100644 index 00000000..f74fa342 --- /dev/null +++ b/core/database/src/main/java/com/harmony/core/database/converters/RoomTypeConverter.kt @@ -0,0 +1,6 @@ +package com.harmony.core.database.converters + +internal abstract class RoomTypeConverter { + abstract fun fromObjectToJson(value: T): String + abstract fun fromJsonToObject(value: String): T +} \ No newline at end of file diff --git a/core/database/src/main/java/com/harmony/core/database/converters/SetStringTypeTypeConverter.kt b/core/database/src/main/java/com/harmony/core/database/converters/SetStringTypeTypeConverter.kt new file mode 100644 index 00000000..936d08a5 --- /dev/null +++ b/core/database/src/main/java/com/harmony/core/database/converters/SetStringTypeTypeConverter.kt @@ -0,0 +1,19 @@ +package com.harmony.core.database.converters + +import androidx.room.TypeConverter +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.Moshi + +internal class SetStringTypeTypeConverter : RoomTypeConverter>() { + + @TypeConverter + override fun fromObjectToJson(value: Set): String = adapter.toJson(value) + + @TypeConverter + override fun fromJsonToObject(value: String): Set = adapter.fromJson(value)!! + + companion object { + private val moshi: Moshi = Moshi.Builder().build() + private val adapter: JsonAdapter> = moshi.adapter>(Set::class.java) + } +} \ No newline at end of file diff --git a/core/database/src/main/java/com/harmony/core/database/dao/MemoryCardDao.kt b/core/database/src/main/java/com/harmony/core/database/dao/MemoryCardDao.kt new file mode 100644 index 00000000..2a33fb81 --- /dev/null +++ b/core/database/src/main/java/com/harmony/core/database/dao/MemoryCardDao.kt @@ -0,0 +1,27 @@ +package com.harmony.core.database.dao + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.Query +import androidx.room.Update +import com.harmony.core.database.model.MemoryCardEntity +import kotlinx.coroutines.flow.Flow + +@Dao +interface MemoryCardDao { + + @Query("SELECT * FROM memory_card") + fun getAllMemoryStorage(): Flow> + + @Query("SELECT * FROM memory_card WHERE id = :id") + fun getMemoryStorageById(id: Long): Flow + + @Insert(MemoryCardEntity::class) + fun insertMemoryStorage(memoryCardEntity: MemoryCardEntity) + + @Update(MemoryCardEntity::class) + fun updateMemoryStorage(memoryCardEntity: MemoryCardEntity) + + @Query("DELETE FROM memory_card WHERE id = :id") + fun deleteMemoryStorageById(id: Long) +} \ No newline at end of file diff --git a/core/database/src/main/java/com/harmony/core/database/dao/UserDao.kt b/core/database/src/main/java/com/harmony/core/database/dao/UserDao.kt index 0fec50e7..6f109070 100644 --- a/core/database/src/main/java/com/harmony/core/database/dao/UserDao.kt +++ b/core/database/src/main/java/com/harmony/core/database/dao/UserDao.kt @@ -19,7 +19,7 @@ interface UserDao { @Query("SELECT * FROM user WHERE uid = :uid") fun getUserById(uid: Long): Flow - @Query("SELECT * FROM user WHERE groupId = :id") + @Query("SELECT * FROM user WHERE group_id = :id") fun getUserByGroupId(id: Long): Flow> @Query("SELECT * FROM user WHERE sns_id = :id") diff --git a/core/database/src/main/java/com/harmony/core/database/di/DatabaseModule.kt b/core/database/src/main/java/com/harmony/core/database/di/DatabaseModule.kt index 9c1e9521..1069a14e 100644 --- a/core/database/src/main/java/com/harmony/core/database/di/DatabaseModule.kt +++ b/core/database/src/main/java/com/harmony/core/database/di/DatabaseModule.kt @@ -6,11 +6,14 @@ import androidx.room.RoomDatabase.Callback import androidx.sqlite.db.SupportSQLiteDatabase import com.harmony.core.database.HarmonyDatabase import com.harmony.core.database.dao.GroupDao +import com.harmony.core.database.dao.MemoryCardDao import com.harmony.core.database.dao.QuestionDao import com.harmony.core.database.dao.TodoDao import com.harmony.core.database.dao.UserDao +import com.harmony.core.database.model.preload.MemoryCardPreloadData import com.harmony.core.database.model.preload.QuestionPreloadData import com.harmony.core.database.model.preload.TodoPreloadData +import com.harmony.core.database.model.preload.UserPreloadData import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -32,7 +35,9 @@ internal object DatabaseModule { super.onCreate(db) listOf( QuestionPreloadData(), - TodoPreloadData() + TodoPreloadData(), + UserPreloadData(), + MemoryCardPreloadData() ) .forEach { it.insertPreloadData(db) } } @@ -66,4 +71,9 @@ internal object DatabaseModule { fun providesQuestionDao( harmonyDatabase: HarmonyDatabase, ): QuestionDao = harmonyDatabase.questionDao() + + @Provides + fun providesMemoryCardDao( + harmonyDatabase: HarmonyDatabase, + ): MemoryCardDao = harmonyDatabase.memoryCardDao() } \ No newline at end of file diff --git a/core/database/src/main/java/com/harmony/core/database/model/MemoryCardEntity.kt b/core/database/src/main/java/com/harmony/core/database/model/MemoryCardEntity.kt new file mode 100644 index 00000000..0628d5ec --- /dev/null +++ b/core/database/src/main/java/com/harmony/core/database/model/MemoryCardEntity.kt @@ -0,0 +1,34 @@ +package com.harmony.core.database.model + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.ForeignKey +import androidx.room.PrimaryKey + +@Entity( + tableName = "memory_card", + foreignKeys = [ + ForeignKey( + entity = QuestionEntity::class, + parentColumns = ["id"], + childColumns = ["question_id"] + ), + ForeignKey( + entity = UserEntity::class, + parentColumns = ["uid"], + childColumns = ["written_uid"] + ) + ] +) +data class MemoryCardEntity( + @PrimaryKey(autoGenerate = true) val id: Long? = null, + @ColumnInfo("question_id") val questionId: Long, + @ColumnInfo("written_uid") val writtenUid: Long, + @ColumnInfo("title") val title: String, + @ColumnInfo("content") val content: String, + @ColumnInfo("created_at") val createdAt: String, + @ColumnInfo("modified_at") val modifiedAt: String, + @ColumnInfo("image_uri") val imageUri: String?, + @ColumnInfo("image_url") val imageUrl: String?, + @ColumnInfo("tags") val tags: Set, +) \ No newline at end of file diff --git a/core/database/src/main/java/com/harmony/core/database/model/UserEntity.kt b/core/database/src/main/java/com/harmony/core/database/model/UserEntity.kt index 9037c144..48145c6f 100644 --- a/core/database/src/main/java/com/harmony/core/database/model/UserEntity.kt +++ b/core/database/src/main/java/com/harmony/core/database/model/UserEntity.kt @@ -7,10 +7,10 @@ import androidx.room.PrimaryKey @Entity(tableName = "user") data class UserEntity( @PrimaryKey(autoGenerate = true) val uid: Long? = null, - @ColumnInfo(name = "groupId") val groupId: Long?, + @ColumnInfo(name = "group_id") val groupId: Long?, @ColumnInfo(name = "name") val name: String, @ColumnInfo(name = "relation") val relation: String, - @ColumnInfo(name = "profileImageUri") val profileImageUri: String?, + @ColumnInfo(name = "profile_image_uri") val profileImageUri: String?, @ColumnInfo(name = "role") val role: String, @ColumnInfo(name = "sns_id") val snsId: String, @ColumnInfo(name = "is_me") val isMe: Boolean, diff --git a/core/database/src/main/java/com/harmony/core/database/model/preload/MemoryCardPreloadData.kt b/core/database/src/main/java/com/harmony/core/database/model/preload/MemoryCardPreloadData.kt new file mode 100644 index 00000000..f57f5c4e --- /dev/null +++ b/core/database/src/main/java/com/harmony/core/database/model/preload/MemoryCardPreloadData.kt @@ -0,0 +1,115 @@ +package com.harmony.core.database.model.preload + +import android.content.ContentValues + +internal class MemoryCardPreloadData : DataPreloadHelper() { + + companion object { + private const val TABLE_NAME = "memory_card" + } + + override val tableName: String = TABLE_NAME + + override val preloadData: List = listOf( + ContentValues().apply { + put("question_id", 1) + put("written_uid", 9999) + put("title", "다은이 태어난 날") + put("content", "다은이가 이때부터 참 많이 울었지~ 아주 우렁차게 울어대서 커서 뭐가 되려나 어쩌구 저쩌구 더미 텍스트 블라블라...") + put("created_at", "2024-06-12 14:30:00") + put("modified_at", "2024-06-12 14:45:00") + put("image_url", "https://picsum.photos/400") + put("tags", "[\"추억\",\"가족\"]") + }, + ContentValues().apply { + put("question_id", 2) + put("written_uid", 9999) + put("title", "가족 여행의 추억") + put("content", "여름방학마다 떠났던 가족 여행. 그때마다 설렜던 기억이 나네~ 어쩌구 저쩌구 더미 텍스트 블라블라...") + put("created_at", "2024-06-13 10:15:00") + put("modified_at", "2024-06-13 10:30:00") + put("image_url", "https://picsum.photos/400") + put("tags", "[\"여행\",\"추억\"]") + }, + ContentValues().apply { + put("question_id", 3) + put("written_uid", 9999) + put("title", "학교에서의 첫 발표") + put("content", "엄청 떨렸던 발표 시간, 목소리는 떨렸지만 끝나고 나니 뿌듯했던 기억이 나네... 어쩌구 저쩌구 더미 텍스트 블라블라...") + put("created_at", "2024-06-14 16:45:00") + put("modified_at", "2024-06-14 17:00:00") + put("image_url", "https://picsum.photos/400") + put("tags", "[\"학교\",\"추억\"]") + }, + ContentValues().apply { + put("question_id", 4) + put("written_uid", 9999) + put("title", "어릴 때 가장 좋아했던 음식") + put("content", "김치볶음밥과 떡볶이! 언제 먹어도 맛있는 음식, 어쩌구 저쩌구 더미 텍스트 블라블라...") + put("created_at", "2024-06-15 09:20:00") + put("modified_at", "2024-06-15 09:35:00") + put("image_url", "https://picsum.photos/400") + put("tags", "[\"음식\",\"기억\"]") + }, + ContentValues().apply { + put("question_id", 5) + put("written_uid", 9999) + put("title", "첫 자전거 타기") + put("content", "넘어지고 또 넘어졌지만 결국 혼자서 탈 수 있었던 순간! 어쩌구 저쩌구 더미 텍스트 블라블라...") + put("created_at", "2024-06-16 14:00:00") + put("modified_at", "2024-06-16 14:15:00") + put("image_url", "https://picsum.photos/400") + put("tags", "[\"취미\",\"도전\"]") + }, + ContentValues().apply { + put("question_id", 6) + put("written_uid", 9999) + put("title", "처음 받은 편지") + put("content", "친구가 손으로 정성껏 써준 편지, 아직도 간직하고 있지... 어쩌구 저쩌구 더미 텍스트 블라블라...") + put("created_at", "2024-06-17 20:30:00") + put("modified_at", "2024-06-17 20:45:00") + put("image_url", "https://picsum.photos/400") + put("tags", "[\"우정\",\"추억\"]") + }, + ContentValues().apply { + put("question_id", 7) + put("written_uid", 9999) + put("title", "예전 꿈 이야기") + put("content", "어릴 때는 우주 비행사가 되고 싶었는데, 지금은 전혀 다른 길을 가고 있네... 어쩌구 저쩌구 더미 텍스트 블라블라...") + put("created_at", "2024-06-18 08:00:00") + put("modified_at", "2024-06-18 08:20:00") + put("image_url", "https://picsum.photos/400") + put("tags", "[\"꿈\",\"성장\"]") + }, + ContentValues().apply { + put("question_id", 8) + put("written_uid", 9999) + put("title", "가장 좋아했던 놀이") + put("content", "숨바꼭질! 친구들과 해가 질 때까지 뛰어다녔던 기억이 나네... 어쩌구 저쩌구 더미 텍스트 블라블라...") + put("created_at", "2024-06-19 11:50:00") + put("modified_at", "2024-06-19 12:10:00") + put("image_url", "https://picsum.photos/400") + put("tags", "[\"놀이\",\"추억\"]") + }, + ContentValues().apply { + put("question_id", 9) + put("written_uid", 9999) + put("title", "가장 감동적인 순간") + put("content", "가족이 생일날 깜짝 파티를 해줬을 때, 정말 감동이었지... 어쩌구 저쩌구 더미 텍스트 블라블라...") + put("created_at", "2024-06-20 17:10:00") + put("modified_at", "2024-06-20 17:30:00") + put("image_url", "https://picsum.photos/400") + put("tags", "[\"가족\",\"감동\"]") + }, + ContentValues().apply { + put("question_id", 10) + put("written_uid", 9999) + put("title", "어릴 때 가장 좋아했던 동물") + put("content", "강아지! 항상 같이 놀고 싶었고 강아지랑 산책하는 꿈도 꾸었지... 어쩌구 저쩌구 더미 텍스트 블라블라...") + put("created_at", "2024-06-21 22:40:00") + put("modified_at", "2024-06-21 22:55:00") + put("image_url", "https://picsum.photos/400") + put("tags", "[\"동물\",\"추억\"]") + } + ) +} \ No newline at end of file diff --git a/core/database/src/main/java/com/harmony/core/database/model/preload/UserPreloadData.kt b/core/database/src/main/java/com/harmony/core/database/model/preload/UserPreloadData.kt new file mode 100644 index 00000000..16b30d42 --- /dev/null +++ b/core/database/src/main/java/com/harmony/core/database/model/preload/UserPreloadData.kt @@ -0,0 +1,23 @@ +package com.harmony.core.database.model.preload + +import android.content.ContentValues + +internal class UserPreloadData : DataPreloadHelper() { + + companion object { + private const val TABLE_NAME = "user" + } + + override val tableName: String = TABLE_NAME + + override val preloadData: List = listOf( + ContentValues().apply { + put("uid", 9999) + put("name", "operator") + put("relation", "operator") + put("role", "v") + put("sns_id", "9999999999") + put("is_me", false) + } + ) +} \ No newline at end of file diff --git a/core/domain/src/main/java/com/teampatch/core/domain/model/MemoryCard.kt b/core/domain/src/main/java/com/teampatch/core/domain/model/MemoryCard.kt index f65cae57..9729601e 100644 --- a/core/domain/src/main/java/com/teampatch/core/domain/model/MemoryCard.kt +++ b/core/domain/src/main/java/com/teampatch/core/domain/model/MemoryCard.kt @@ -7,6 +7,6 @@ data class MemoryCard( val writerTitle: String, val writerName: String, val text: String, - val imageUrl: String, + val imageUrl: String?, val dateTime: LocalDateTime, ) \ No newline at end of file diff --git a/core/domain/src/main/java/com/teampatch/core/domain/repository/MemoryCardRepository.kt b/core/domain/src/main/java/com/teampatch/core/domain/repository/MemoryCardRepository.kt index 50ff6d73..817edc36 100644 --- a/core/domain/src/main/java/com/teampatch/core/domain/repository/MemoryCardRepository.kt +++ b/core/domain/src/main/java/com/teampatch/core/domain/repository/MemoryCardRepository.kt @@ -1,7 +1,10 @@ package com.teampatch.core.domain.repository +import androidx.paging.PagingData +import com.teampatch.core.domain.model.MemoryCard import com.teampatch.core.domain.model.MemoryCardQuestion import java.io.InputStream +import kotlinx.coroutines.flow.Flow interface MemoryCardRepository { @@ -22,4 +25,8 @@ interface MemoryCardRepository { */ suspend fun getQuestionMessage(memoryCardId: String): MemoryCardQuestion + + fun getMemoryCards(): Flow> + + fun getMemoryCardById(memoryCardId: String): Flow } \ No newline at end of file diff --git a/core/domain/src/main/java/com/teampatch/core/domain/usecase/memory/GetMemoryCardUseCase.kt b/core/domain/src/main/java/com/teampatch/core/domain/usecase/memory/GetMemoryCardUseCase.kt index 3c31578f..adb9f195 100644 --- a/core/domain/src/main/java/com/teampatch/core/domain/usecase/memory/GetMemoryCardUseCase.kt +++ b/core/domain/src/main/java/com/teampatch/core/domain/usecase/memory/GetMemoryCardUseCase.kt @@ -1,10 +1,13 @@ package com.teampatch.core.domain.usecase.memory -import com.teampatch.core.domain.fake.FakeMemoryCard import com.teampatch.core.domain.model.MemoryCard +import com.teampatch.core.domain.repository.MemoryCardRepository import javax.inject.Inject +import kotlinx.coroutines.flow.first -class GetMemoryCardUseCase @Inject constructor() { +class GetMemoryCardUseCase @Inject constructor( + private val memoryCardRepository: MemoryCardRepository, +) { - suspend operator fun invoke(memoryCardId: String): MemoryCard = FakeMemoryCard().get().first() + suspend operator fun invoke(memoryCardId: String): MemoryCard = memoryCardRepository.getMemoryCardById(memoryCardId).first() } \ No newline at end of file diff --git a/core/domain/src/main/java/com/teampatch/core/domain/usecase/memory/GetMemoryCardsUseCase.kt b/core/domain/src/main/java/com/teampatch/core/domain/usecase/memory/GetMemoryCardsUseCase.kt new file mode 100644 index 00000000..1a650538 --- /dev/null +++ b/core/domain/src/main/java/com/teampatch/core/domain/usecase/memory/GetMemoryCardsUseCase.kt @@ -0,0 +1,15 @@ +package com.teampatch.core.domain.usecase.memory + +import androidx.paging.PagingData +import com.teampatch.core.domain.model.MemoryCard +import com.teampatch.core.domain.repository.MemoryCardRepository +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow + +class GetMemoryCardsUseCase @Inject constructor( + private val memoryCardRepository: MemoryCardRepository, + +) { + + operator fun invoke(): Flow> = memoryCardRepository.getMemoryCards() +} \ No newline at end of file diff --git a/feature/memorystorage/build.gradle.kts b/feature/memorystorage/build.gradle.kts index f225a286..39ce139b 100644 --- a/feature/memorystorage/build.gradle.kts +++ b/feature/memorystorage/build.gradle.kts @@ -15,6 +15,9 @@ dependencies { implementation(project(":core:domain")) implementation(project(":core:designsystem")) + implementation(libs.androidx.paging.runtime) + implementation(libs.androidx.paging.compose) + implementation(libs.coil.compose) testImplementation(libs.junit) diff --git a/feature/memorystorage/src/main/java/com/teampatch/feature/memorystorage/MemoryStorageScreen.kt b/feature/memorystorage/src/main/java/com/teampatch/feature/memorystorage/MemoryStorageScreen.kt index 7426079e..35e32e28 100644 --- a/feature/memorystorage/src/main/java/com/teampatch/feature/memorystorage/MemoryStorageScreen.kt +++ b/feature/memorystorage/src/main/java/com/teampatch/feature/memorystorage/MemoryStorageScreen.kt @@ -40,6 +40,10 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.paging.PagingData +import androidx.paging.compose.LazyPagingItems +import androidx.paging.compose.collectAsLazyPagingItems +import androidx.paging.compose.itemKey import com.teampatch.core.designsystem.R.drawable import com.teampatch.core.designsystem.R.drawable.btn_search import com.teampatch.core.designsystem.component.AppBar @@ -51,6 +55,7 @@ import com.teampatch.core.designsystem.theme.PretendardFontFamily import com.teampatch.core.designsystem.utils.noRippleClickable import com.teampatch.core.domain.model.MemoryCard import java.time.LocalDateTime +import kotlinx.coroutines.flow.flowOf @Composable internal fun MemoryStorageRoute( @@ -59,17 +64,21 @@ internal fun MemoryStorageRoute( ) { val context = LocalContext.current val memoryStorageUiState by memoryStorageViewModel.memoryStorageUiState.collectAsStateWithLifecycle() + val memoryCards: LazyPagingItems = + memoryStorageViewModel.memoryCards.collectAsLazyPagingItems() when (memoryStorageUiState) { is MemoryStorageUiState.Loading -> { // 로딩 화면 표시 } + is MemoryStorageUiState.Success -> { MemoryStorageScreen( onDetailPageRequest = onDetailPageRequest, - memoryStorageUiState = memoryStorageUiState as MemoryStorageUiState.Success + memoryCardsLazyItems = memoryCards ) } + is MemoryStorageUiState.Error -> { LaunchedEffect(Unit) { Toast.makeText( @@ -85,7 +94,7 @@ internal fun MemoryStorageRoute( @Composable internal fun MemoryStorageScreen( onDetailPageRequest: () -> Unit, - memoryStorageUiState: MemoryStorageUiState, + memoryCardsLazyItems: LazyPagingItems, ) { // 상태 관리 var isSearchMode by remember { mutableStateOf(false) } @@ -206,10 +215,6 @@ internal fun MemoryStorageScreen( } } ) { scaffoldPaddingValues -> - val memoryList = when (memoryStorageUiState) { - is MemoryStorageUiState.Success -> memoryStorageUiState.memories.toList() - else -> emptyList() - } LazyVerticalGrid( columns = GridCells.Fixed(2), @@ -219,8 +224,8 @@ internal fun MemoryStorageScreen( .padding(scaffoldPaddingValues) ) { items( - count = memoryList.size, - key = { index -> memoryList[index].first }, // key는 Map의 Key (String) + count = memoryCardsLazyItems.itemCount, + key = memoryCardsLazyItems.itemKey(), span = { index -> if (index == 0) { GridItemSpan(maxLineSpan) @@ -229,11 +234,9 @@ internal fun MemoryStorageScreen( } } ) { index -> - val memory = memoryList[index].second // ID를 사용하지 않으므로 second만 가져옴 - TempMemoryCard( - title = memory.writerTitle, - description = memory.dateTime.toString(), + title = memoryCardsLazyItems[index]?.text ?: "", + description = memoryCardsLazyItems[index]?.dateTime.toString(), painter = painterResource(id = drawable.img_test_memory_card), modifier = Modifier.noRippleClickable { onDetailPageRequest() // ID가 필요 없다면 인자를 제거 @@ -250,42 +253,44 @@ private fun MemoryStorageScreenPreview() { HarmonyTheme { MemoryStorageScreen( onDetailPageRequest = { }, - memoryStorageUiState = MemoryStorageUiState.Success( - memories = mapOf( - "1" to MemoryCard( - id = "1", - writerTitle = "손자", - writerName = "김민준", - text = "title", - imageUrl = "", - dateTime = LocalDateTime.now() - ), - "2" to MemoryCard( - id = "2", - writerTitle = "할머니", - writerName = "이영희", - text = "어릴 적 사진", - imageUrl = "", - dateTime = LocalDateTime.now() - ), - "3" to MemoryCard( - id = "3", - writerTitle = "할머니", - writerName = "이영희", - text = "어릴 적 사진", - imageUrl = "", - dateTime = LocalDateTime.now() - ), - "4" to MemoryCard( - id = "4", - writerTitle = "할머니", - writerName = "이영희", - text = "어릴 적 사진", - imageUrl = "", - dateTime = LocalDateTime.now() + flowOf( + PagingData.from( + listOf( + MemoryCard( + id = "1", + writerTitle = "손자", + writerName = "김민준", + text = "title", + imageUrl = "", + dateTime = LocalDateTime.now() + ), + MemoryCard( + id = "2", + writerTitle = "할머니", + writerName = "이영희", + text = "어릴 적 사진", + imageUrl = "", + dateTime = LocalDateTime.now() + ), + MemoryCard( + id = "3", + writerTitle = "할머니", + writerName = "이영희", + text = "어릴 적 사진", + imageUrl = "", + dateTime = LocalDateTime.now() + ), + MemoryCard( + id = "4", + writerTitle = "할머니", + writerName = "이영희", + text = "어릴 적 사진", + imageUrl = "", + dateTime = LocalDateTime.now() + ) ) ) - ) + ).collectAsLazyPagingItems() ) } } \ No newline at end of file diff --git a/feature/memorystorage/src/main/java/com/teampatch/feature/memorystorage/MemoryStorageUiState.kt b/feature/memorystorage/src/main/java/com/teampatch/feature/memorystorage/MemoryStorageUiState.kt index fc3cd98a..76c949e9 100644 --- a/feature/memorystorage/src/main/java/com/teampatch/feature/memorystorage/MemoryStorageUiState.kt +++ b/feature/memorystorage/src/main/java/com/teampatch/feature/memorystorage/MemoryStorageUiState.kt @@ -1,11 +1,9 @@ package com.teampatch.feature.memorystorage -import com.teampatch.core.domain.model.MemoryCard - sealed interface MemoryStorageUiState { - object Loading : MemoryStorageUiState + data object Loading : MemoryStorageUiState - data class Success(val memories: Map) : MemoryStorageUiState + data object Success : MemoryStorageUiState data class Error(val message: String) : MemoryStorageUiState } \ No newline at end of file diff --git a/feature/memorystorage/src/main/java/com/teampatch/feature/memorystorage/MemoryStorageViewModel.kt b/feature/memorystorage/src/main/java/com/teampatch/feature/memorystorage/MemoryStorageViewModel.kt index 99730b55..e05b83d1 100644 --- a/feature/memorystorage/src/main/java/com/teampatch/feature/memorystorage/MemoryStorageViewModel.kt +++ b/feature/memorystorage/src/main/java/com/teampatch/feature/memorystorage/MemoryStorageViewModel.kt @@ -1,45 +1,27 @@ package com.teampatch.feature.memorystorage import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.teampatch.core.domain.usecase.memory.GetLatestMemoryCardUseCase -import com.teampatch.core.domain.usecase.memory.GetMemoryCardUseCase +import androidx.paging.PagingData +import com.teampatch.core.common.flowErrorCatch +import com.teampatch.core.domain.model.MemoryCard +import com.teampatch.core.domain.usecase.memory.GetMemoryCardsUseCase import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject -import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.receiveAsFlow -import kotlinx.coroutines.launch @HiltViewModel internal class MemoryStorageViewModel @Inject constructor( - private val getLatestMemoryCardUseCase: GetLatestMemoryCardUseCase, - private val getMemoryCardUseCase: GetMemoryCardUseCase, + private val getMemoryCardsUseCase: GetMemoryCardsUseCase, ) : ViewModel() { - private val _event: Channel = Channel() - val event: Flow = _event.receiveAsFlow() - - private val _memoryStorageUiState = MutableStateFlow(MemoryStorageUiState.Loading) + private val _memoryStorageUiState = + MutableStateFlow(MemoryStorageUiState.Loading) val memoryStorageUiState = _memoryStorageUiState.asStateFlow() - init { - loadData() - } - - private fun loadData() = viewModelScope.launch { - _memoryStorageUiState.value = MemoryStorageUiState.Loading - - try { - val memoryCard = getMemoryCardUseCase("someId") // 예제 코드 - _memoryStorageUiState.value = MemoryStorageUiState.Success( - memories = mapOf(memoryCard.id to memoryCard) - ) - } catch (e: Exception) { - _memoryStorageUiState.value = MemoryStorageUiState.Error("데이터 로딩 실패") - _event.send(MemoryStorageEvent.LoadError) - } - } + val memoryCards: Flow> = flowErrorCatch( + block = { getMemoryCardsUseCase() }, + action = { it.printStackTrace() } + ) } \ No newline at end of file