Skip to content

Commit

Permalink
Merge pull request #67 from YAPP-Github/feature/MZ-180-ledger-list-ed…
Browse files Browse the repository at this point in the history
…it-delete

Feature/mz 180 ledger list edit delete, error handling, category config
  • Loading branch information
jinukeu authored Jan 16, 2024
2 parents 63d3777 + 71b5bd2 commit d4fbfe5
Show file tree
Hide file tree
Showing 54 changed files with 1,267 additions and 254 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ internal class FeatureComposeConventionPlugin : Plugin<Project> {
"implementation"(project(":domain"))

"implementation"(libs.findLibrary("kotlinx.immutable").get())
"implementation"(libs.findLibrary("kotlinx.datetime").get())
"implementation"(libs.findLibrary("kotlinx.coroutines.android").get())
"implementation"(libs.findLibrary("kotlinx.coroutines.core").get())

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ fun SusuLimitDatePickerBottomSheet(
InfiniteColumn(
modifier = Modifier.width(100.dp),
items = yearList,
initialItem = stringResource(R.string.word_year_format, criteriaYear),
initialItem = stringResource(R.string.word_year_format, selectedYear),
itemHeight = itemHeight,
numberOfDisplayedItems = numberOfDisplayedItems,
onItemSelected = { _, item ->
Expand All @@ -233,7 +233,7 @@ fun SusuLimitDatePickerBottomSheet(
InfiniteColumn(
modifier = Modifier.width(100.dp),
items = monthRange.map { stringResource(id = R.string.word_month_format, it) },
initialItem = stringResource(R.string.word_month_format, criteriaMonth),
initialItem = stringResource(R.string.word_month_format, selectedMonth),
itemHeight = itemHeight,
numberOfDisplayedItems = numberOfDisplayedItems,
onItemSelected = { _, item ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.res.painterResource
Expand Down Expand Up @@ -185,7 +187,8 @@ fun SusuTextFieldWrapContentButton(
onClickClearIcon: () -> Unit = {},
onClickCloseIcon: () -> Unit = {},
onClickFilledButton: () -> Unit = {},
onClickButton: (isFocused: Boolean) -> Unit = {},
onClickButton: () -> Unit = {},
focusRequester: FocusRequester = remember { FocusRequester() },
) {
val (backgroundColor, textColor) = with(color) {
when {
Expand All @@ -200,7 +203,7 @@ fun SusuTextFieldWrapContentButton(
modifier = modifier
.clip(shape)
.background(backgroundColor)
.susuClickable { onClickButton(isFocused) }
.susuClickable(onClick = onClickButton)
.padding(paddingValues),
horizontalArrangement = Arrangement.spacedBy(iconSpacing),
verticalAlignment = Alignment.CenterVertically,
Expand All @@ -217,7 +220,8 @@ fun SusuTextFieldWrapContentButton(
* see -> https://stackoverflow.com/questions/67719981/resizeable-basictextfield-in-jetpack-compose
*/
.width(IntrinsicSize.Min)
.susuClickable(rippleEnabled = false, runIf = isFocused.not(), onClick = { onClickButton(isFocused) }),
.susuClickable(rippleEnabled = false, runIf = isFocused.not(), onClick = onClickButton)
.focusRequester(focusRequester),
value = text,
enabled = isSaved.not() && isFocused,
onValueChange = onTextChange,
Expand Down
3 changes: 3 additions & 0 deletions core/model/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@

plugins {
alias(libs.plugins.susu.java.library)
alias(libs.plugins.kotlin.serialization)
}

dependencies {
compileOnly(libs.compose.stable.marker)
implementation(libs.kotlinx.serialization.json)
implementation(libs.kotlinx.datetime)
}
11 changes: 8 additions & 3 deletions core/model/src/main/java/com/susu/core/model/Category.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package com.susu.core.model

import androidx.compose.runtime.Stable
import kotlinx.serialization.Serializable

@Stable
@Serializable
data class Category(
val id: Int,
val seq: Int,
val category: String,
val id: Int = 0,
val seq: Int = 0,
val name: String = "",
val customCategory: String? = null,
)
20 changes: 12 additions & 8 deletions core/model/src/main/java/com/susu/core/model/Ledger.kt
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
package com.susu.core.model

import androidx.compose.runtime.Stable
import java.time.LocalDateTime
import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.toKotlinLocalDateTime
import kotlinx.serialization.Serializable

@Stable
@Serializable
data class Ledger(
val id: Int,
val title: String,
val description: String,
val startAt: LocalDateTime,
val endAt: LocalDateTime,
val category: Category,
val totalAmounts: Int,
val id: Int = -1,
val title: String = "",
val description: String = "",
val startAt: LocalDateTime = java.time.LocalDateTime.now().toKotlinLocalDateTime(),
val endAt: LocalDateTime = java.time.LocalDateTime.now().toKotlinLocalDateTime(),
val category: Category = Category(),
val totalAmounts: Int = 0,
val totalCounts: Int = 0,
)
5 changes: 5 additions & 0 deletions core/ui/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@
plugins {
alias(libs.plugins.susu.android.library)
alias(libs.plugins.susu.android.library.compose)
alias(libs.plugins.kotlin.serialization)
}

android {
namespace = "com.susu.core.ui"
}

dependencies {
implementation(libs.kotlinx.serialization.json)
}
13 changes: 13 additions & 0 deletions core/ui/src/main/java/com/susu/core/ui/extension/Json.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.susu.core.ui.extension

import android.net.Uri
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json

inline fun <reified T> Json.encodeToUri(value: T): String {
return Uri.encode(encodeToString(value))
}

inline fun <reified T> Json.decodeFromUri(value: String): T {
return decodeFromString(Uri.decode(value))
}
38 changes: 38 additions & 0 deletions core/ui/src/main/java/com/susu/core/ui/extension/LazyGridState.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.susu.core.ui.extension

import androidx.compose.foundation.lazy.grid.LazyGridState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.snapshotFlow
import kotlinx.coroutines.flow.collectLatest

// https://manavtamboli.medium.com/infinite-list-paged-list-in-jetpack-compose-b10fc7e74768
@Composable
fun LazyGridState.OnBottomReached(
// tells how many items before we reach the bottom of the list
// to call onLoadMore function
buffer: Int = 0,
onLoadMore: () -> Unit,
) {
// Buffer must be positive.
// Or our list will never reach the bottom.
require(buffer >= 0) { "buffer cannot be negative, but was $buffer" }

val shouldLoadMore = remember {
derivedStateOf {
val lastVisibleItem = layoutInfo.visibleItemsInfo.lastOrNull() ?: return@derivedStateOf false

lastVisibleItem.index >= layoutInfo.totalItemsCount - 1 - buffer
}
}
LaunchedEffect(shouldLoadMore) {
snapshotFlow { shouldLoadMore.value }
.collectLatest {
if (it) {
onLoadMore()
}
}
}
}
39 changes: 39 additions & 0 deletions core/ui/src/main/java/com/susu/core/ui/extension/LazyListState.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.susu.core.ui.extension

import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.snapshotFlow

// https://manavtamboli.medium.com/infinite-list-paged-list-in-jetpack-compose-b10fc7e74768
@Composable
fun LazyListState.OnBottomReached(
// tells how many items before we reach the bottom of the list
// to call onLoadMore function
buffer: Int = 3,
onLoadMore: () -> Unit,
) {
// Buffer must be positive.
// Or our list will never reach the bottom.
require(buffer >= 0) { "buffer cannot be negative, but was $buffer" }

val shouldLoadMore = remember {
derivedStateOf {
val lastVisibleItem = layoutInfo.visibleItemsInfo.lastOrNull() ?: return@derivedStateOf false

lastVisibleItem.index >= layoutInfo.totalItemsCount - 1 - buffer
}
}
LaunchedEffect(shouldLoadMore) {
snapshotFlow { shouldLoadMore.value }
.collect {
if (it) {
onLoadMore()
}
}
}
}

fun LazyListState.isScrolledToEnd() = layoutInfo.visibleItemsInfo.lastOrNull()?.index == layoutInfo.totalItemsCount - 1
1 change: 1 addition & 0 deletions core/ui/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<string name="word_edit">편집</string>
<string name="word_save">저장</string>
<string name="word_delete">삭제</string>
<string name="word_cancel">취소</string>
<string name="word_total_money">전체 %s원</string>
<string name="word_total_count">총 %d개</string>
<string name="content_description_see_more_icon">더보기 아이콘</string>
Expand Down
7 changes: 7 additions & 0 deletions data/src/main/java/com/susu/data/data/di/RepositoryModule.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.susu.data.data.di

import com.susu.data.data.repository.CategoryConfigRepositoryImpl
import com.susu.data.data.repository.LedgerRecentSearchRepositoryImpl
import com.susu.data.data.repository.LedgerRepositoryImpl
import com.susu.data.data.repository.LoginRepositoryImpl
import com.susu.data.data.repository.SignUpRepositoryImpl
import com.susu.data.data.repository.TermRepositoryImpl
import com.susu.data.data.repository.TokenRepositoryImpl
import com.susu.domain.repository.CategoryConfigRepository
import com.susu.domain.repository.LedgerRecentSearchRepository
import com.susu.domain.repository.LedgerRepository
import com.susu.domain.repository.LoginRepository
Expand Down Expand Up @@ -50,4 +52,9 @@ abstract class RepositoryModule {
abstract fun bindLedgerRepository(
ledgerRepositoryImpl: LedgerRepositoryImpl,
): LedgerRepository

@Binds
abstract fun bindCategoryConfigRepository(
categoryConfigRepositoryImpl: CategoryConfigRepositoryImpl,
): CategoryConfigRepository
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.susu.data.data.repository

import com.susu.core.android.Dispatcher
import com.susu.core.android.SusuDispatchers
import com.susu.core.model.Category
import com.susu.data.local.dao.CategoryConfigDao
import com.susu.data.local.model.toEntity
import com.susu.data.local.model.toModel
import com.susu.data.remote.api.CategoryService
import com.susu.data.remote.model.response.toModel
import com.susu.domain.repository.CategoryConfigRepository
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
import javax.inject.Inject

class CategoryConfigRepositoryImpl @Inject constructor(
private val dao: CategoryConfigDao,
private val api: CategoryService,
@Dispatcher(SusuDispatchers.IO) private val ioDispatcher: CoroutineDispatcher,
) : CategoryConfigRepository {
override suspend fun getCategoryConfig(): List<Category> = withContext(ioDispatcher) {
val localCategoryConfig = dao.getCategoryConfig().map { it.toModel() }
if (localCategoryConfig.isNotEmpty()) return@withContext localCategoryConfig

val remoteCategoryConfig = api.getCategoryConfig().getOrThrow().map { it.toModel() }
dao.insert(remoteCategoryConfig.map { it.toEntity() })
return@withContext remoteCategoryConfig
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.susu.data.data.repository

import com.susu.core.model.Ledger
import com.susu.data.remote.api.LedgerService
import com.susu.data.remote.model.request.toData
import com.susu.data.remote.model.response.toModel
import com.susu.domain.repository.LedgerRepository
import kotlinx.datetime.toKotlinLocalDateTime
Expand All @@ -18,14 +19,21 @@ class LedgerRepositoryImpl @Inject constructor(
toEndAt: LocalDateTime,
page: Int?,
sort: String?,
): List<Ledger> {
return ledgerService.getLedgerList(
title = title,
categoryId = categoryId,
fromStartAt = fromStartAt.toKotlinLocalDateTime(),
toEndAt = toEndAt.toKotlinLocalDateTime(),
page = page,
sort = sort,
).getOrThrow().toModel()
}
): List<Ledger> = ledgerService.getLedgerList(
title = title,
categoryId = categoryId,
fromStartAt = fromStartAt.toKotlinLocalDateTime(),
toEndAt = toEndAt.toKotlinLocalDateTime(),
page = page,
sort = sort,
).getOrThrow().toModel()

override suspend fun editLedger(ledger: Ledger): Ledger = ledgerService.editLedger(
id = ledger.id,
ledgerRequest = ledger.toData(),
).getOrThrow().toModel()

override suspend fun deleteLedger(id: Int) = ledgerService.deleteLedgerList(
listOf(id),
).getOrThrow()
}
16 changes: 16 additions & 0 deletions data/src/main/java/com/susu/data/local/RoomInMemoryDataBase.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.susu.data.local

import androidx.room.Database
import androidx.room.RoomDatabase
import com.susu.data.local.dao.CategoryConfigDao
import com.susu.data.local.model.CategoryConfigEntity

@Database(
entities = [
CategoryConfigEntity::class,
],
version = 1,
)
abstract class RoomInMemoryDataBase : RoomDatabase() {
abstract fun categoryConfigDao(): CategoryConfigDao
}
16 changes: 16 additions & 0 deletions data/src/main/java/com/susu/data/local/dao/CategoryConfigDao.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.susu.data.local.dao

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import com.susu.data.local.model.CategoryConfigEntity
import com.susu.data.local.model.EntityTable

@Dao
interface CategoryConfigDao {
@Query("SELECT * FROM ${EntityTable.CATEGORY_CONFIG}")
fun getCategoryConfig(): List<CategoryConfigEntity>

@Insert
fun insert(categoryConfig: List<CategoryConfigEntity>)
}
5 changes: 5 additions & 0 deletions data/src/main/java/com/susu/data/local/di/DaoModule.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.susu.data.local.di

import com.susu.data.local.RoomDataBase
import com.susu.data.local.RoomInMemoryDataBase
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
Expand All @@ -14,4 +15,8 @@ object DaoModule {
@Singleton
@Provides
fun provideRecentSearchDao(db: RoomDataBase) = db.ledgerRecentSearchDao()

@Singleton
@Provides
fun provideCategoryConfigDao(db: RoomInMemoryDataBase) = db.categoryConfigDao()
}
Loading

0 comments on commit d4fbfe5

Please sign in to comment.