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

모먼트 삭제 기능 구현 #91

Merged
merged 5 commits into from
Dec 8, 2022
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
17 changes: 17 additions & 0 deletions data/src/main/java/com/wakeup/data/database/dao/MomentDao.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ package com.wakeup.data.database.dao

import androidx.paging.PagingSource
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Transaction
import com.wakeup.data.database.entity.MomentEntity
import com.wakeup.data.database.entity.MomentWithGlobesAndPictures
import com.wakeup.data.database.entity.MomentWithPictures
import com.wakeup.data.database.entity.PictureEntity
import kotlinx.coroutines.flow.Flow

@Dao
Expand Down Expand Up @@ -62,4 +65,18 @@ interface MomentDao {

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun saveMoment(moment: MomentEntity): Long

@Query(
"""
SELECT picture.picture_id, picture.path
FROM moment_picture
INNER JOIN picture
ON moment_picture.picture_id = picture.picture_id
WHERE moment_picture.moment_id = :momentId
"""
)
suspend fun getMomentPictures(momentId: Long): List<PictureEntity>

@Query("DELETE FROM moment WHERE moment_id = :momentId")
suspend fun deleteMoment(momentId: Long)
}
4 changes: 4 additions & 0 deletions data/src/main/java/com/wakeup/data/database/dao/PictureDao.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.wakeup.data.database.dao

import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
Expand All @@ -13,4 +14,7 @@ interface PictureDao {

@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun savePictures(pictures: List<PictureEntity>): List<Long>

@Query("DELETE FROM picture WHERE picture_id = :pictureId")
suspend fun deletePicture(pictureId: Long)
}
9 changes: 9 additions & 0 deletions data/src/main/java/com/wakeup/data/database/dao/XRefDao.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.wakeup.data.database.entity.MomentGlobeXRef
import com.wakeup.data.database.entity.MomentPictureXRef

Expand All @@ -18,4 +19,12 @@ interface XRefDao {

@Delete
suspend fun deleteMomentGlobeXRef(momentGlobe: MomentGlobeXRef)

@Query(
"""
SELECT (CASE WHEN count(*) > 1 THEN 0 ELSE 1 END) FROM moment_picture
WHERE picture_id = :pictureId
"""
)
suspend fun isOnlyOnePicture(pictureId: Long): Boolean
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.wakeup.data.database.entity

import androidx.room.Embedded
import androidx.room.Junction
import androidx.room.Relation

data class MomentWithPictures(
@Embedded val moment: MomentEntity,
@Relation(
parentColumn = "moment_id",
entity = PictureEntity::class,
entityColumn = "picture_id",
associateBy = Junction(MomentPictureXRef::class)
)
val pictures: List<PictureEntity>,
)
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,8 @@ class MomentRepositoryImpl @Inject constructor(
override suspend fun saveMoment(moment: Moment): Long {
return momentLocalDataSource.saveMoment(moment.toEntity())
}

override suspend fun deleteMoment(momentId: Long) {
momentLocalDataSource.deleteMoment(momentId)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@ interface MomentLocalDataSource {
suspend fun getMoment(id: Long): MomentWithGlobesAndPictures

suspend fun saveMoment(moment: MomentEntity): Long

suspend fun deleteMoment(momentId: Long)
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,21 @@ import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.wakeup.data.database.dao.MomentDao
import com.wakeup.data.database.dao.PictureDao
import com.wakeup.data.database.dao.XRefDao
import com.wakeup.data.database.entity.LocationEntity
import com.wakeup.data.database.entity.MomentEntity
import com.wakeup.data.database.entity.MomentWithGlobesAndPictures
import com.wakeup.data.util.InternalFileUtil
import com.wakeup.domain.model.SortType
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject

class MomentLocalDataSourceImpl @Inject constructor(
private val momentDao: MomentDao,
private val pictureDao: PictureDao,
private val xRefDao: XRefDao,
private val util: InternalFileUtil
) : MomentLocalDataSource {
override fun getMoments(
sortType: SortType,
Expand Down Expand Up @@ -49,6 +55,18 @@ class MomentLocalDataSourceImpl @Inject constructor(
return momentDao.saveMoment(moment)
}

override suspend fun deleteMoment(momentId: Long) {
val pictures = momentDao.getMomentPictures(momentId)
pictures.forEach { picture ->
val isDelete = xRefDao.isOnlyOnePicture(picture.id)
if (isDelete) {
util.deletePictureInInternalStorage(picture.path)
pictureDao.deletePicture(picture.id)
}
}
momentDao.deleteMoment(momentId)
}

companion object {
const val PREFETCH_PAGE = 2
const val ITEMS_PER_PAGE = 10
Expand Down
42 changes: 30 additions & 12 deletions data/src/main/java/com/wakeup/data/util/InternalFileUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,27 @@ class InternalFileUtil @Inject constructor(
Glide.with(context)
.asBitmap()
.load(picture.path.toUri())
.listener(PictureSaveRequestListener(picture, context))
.listener(PictureSaveRequestListener(picture, context, ::catchException))
.override(1000, 1000)
.fitCenter()
.submit()
}

fun deletePictureInInternalStorage(fileName: String) {
runCatching {
val filePath = "${context.filesDir.path}/$DIR_NAME/$fileName"
val file = File(filePath)
if (file.exists()) {
Timber.d("사진 삭제: $fileName")
file.delete()
}
}.onFailure { catchException(it) }
}

class PictureSaveRequestListener(
private val picture: Picture,
private val context: Context,
private val catchException: (Throwable) -> Unit
) : RequestListener<Bitmap> {
override fun onLoadFailed(
e: GlideException?,
Expand All @@ -56,25 +68,31 @@ class InternalFileUtil @Inject constructor(
): Boolean {
CoroutineScope(Dispatchers.IO).launch {
runCatching {
val dirPath = File(context.filesDir, "images").apply { mkdirs() }
val dirPath = File(context.filesDir, DIR_NAME).apply { mkdirs() }
val filePath = File("${dirPath}/${picture.path.substringAfterLast("/")}")
Timber.d("${dirPath}/${picture.path.substringAfterLast("/")}")
FileOutputStream(filePath).use { out ->
resource?.compress(Bitmap.CompressFormat.JPEG, 100, out)
}
}.onFailure { exception ->
when (exception) {
is FileNotFoundException ->
Timber.e("FileNotFoundException : " + exception.message)
is IOException ->
Timber.e("IOException : " + exception.message)
else ->
Timber.e("AnotherException : " + exception.message)
}
}
}.onFailure { catchException(it) }
}
return false
}
}

private fun catchException(exception: Throwable) {
when (exception) {
is FileNotFoundException ->
Timber.e("FileNotFoundException : " + exception.message)
is IOException ->
Timber.e("IOException : " + exception.message)
else ->
Timber.e("AnotherException : " + exception.message)
}
}

companion object {
private const val DIR_NAME = "images"
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,6 @@ interface MomentRepository {
suspend fun getMoment(id: Long): Moment

suspend fun saveMoment(moment: Moment): Long

suspend fun deleteMoment(momentId: Long)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.wakeup.domain.usecase

import com.wakeup.domain.repository.MomentRepository
import javax.inject.Inject

class DeleteMomentUseCase @Inject constructor(
private val momentRepository: MomentRepository
) {

suspend operator fun invoke(momentId: Long) {
momentRepository.deleteMoment(momentId)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ import com.wakeup.presentation.mapper.toPresentation
import com.wakeup.presentation.model.LocationModel
import com.wakeup.presentation.model.MomentModel
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
Expand All @@ -26,11 +28,16 @@ class MainViewModel @Inject constructor(
private val _isReady = MutableStateFlow(false)
val isReady = _isReady.asStateFlow()

val allMoments: Flow<List<MomentModel>?> = getAllMomentListUseCase("").map { moments ->
var allMoments: StateFlow<List<MomentModel>>? = getAllMomentListUseCase("").map { moments ->
moments.map { moment ->
moment.toPresentation()
}
}.apply { _isReady.value = true }
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = emptyList()
)
.apply { _isReady.value = true }
Comment on lines +39 to +40
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.

악!


fun testGetWeather() {
viewModelScope.launch {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,6 @@ class AddMomentFragment : Fragment() {

if (state is UiState.Success) {
Toast.makeText(context, "모먼트를 기록하였습니다.", Toast.LENGTH_LONG).show()
val intent = Intent(UPDATE_MOMENTS_KEY)
requireContext().sendBroadcast(intent)
findNavController().popBackStack()
}
}
Expand Down
Loading