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

Feature/mz 180 ledger list edit delete, error handling, category config #67

Merged
merged 18 commits into from
Jan 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
bb2028f
feat: CategoryConfig, UseCase ์ถ”๊ฐ€
jinukeu Jan 15, 2024
40ad55c
fix: MainNavigator saveState, RestoreState ๋™์ž‘ํ•˜์ง€ ์•Š๋˜ ๋ฌธ์ œ ํ•ด๊ฒฐ
jinukeu Jan 15, 2024
b9ede9e
feat: ์žฅ๋ถ€ ๋ชฉ๋ก ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ๊ตฌํ˜„
jinukeu Jan 15, 2024
b30905e
feat: encodeToUri, decodeFromUri ํ™•์žฅ ํ•จ์ˆ˜ ์ถ”๊ฐ€
jinukeu Jan 15, 2024
1309a2e
feat: ์žฅ๋ถ€ ์ƒ์„ธ ์ด๋™ ์‹œ id๊ฐ€ ์•„๋‹Œ Ledger ๊ฐ์ฒด๋ฅผ ๋„˜๊ธฐ๊ฒŒ ์ˆ˜์ •
jinukeu Jan 15, 2024
7bcf4af
feat: ์žฅ๋ถ€ ํŽธ์ง‘ ๋™์ž‘ ๊ตฌํ˜„
jinukeu Jan 15, 2024
58046f4
refactor: ์žฅ๋ถ€ ํŽธ์ง‘ ๋ฆฌํŒฉํ† ๋ง
jinukeu Jan 15, 2024
5ed8288
feat: ์žฅ๋ถ€ ํŽธ์ง‘ api ์—ฐ๋™
jinukeu Jan 15, 2024
7c6b4a7
feat: ์žฅ๋ถ€ ํŽธ์ง‘ ๊ธฐ๋Šฅ ๊ตฌํ˜„
jinukeu Jan 15, 2024
c15c5e3
Merge branch 'feature/datepicker-limit' into feature/MZ-180-ledger-liโ€ฆ
jinukeu Jan 16, 2024
1a9f2db
fix: SusuDatePickerBottomSheet ๋ฌด์กฐ๊ฑด ๊ธฐ์ค€ ๋‚ ์งœ๋กœ ๋ณด์ด๋Š” ํ˜„์ƒ ์ˆ˜์ •
jinukeu Jan 16, 2024
4a6cb61
feat: SusuLimitDatePicker ์ ์šฉ
jinukeu Jan 16, 2024
3889fef
feat: ์žฅ๋ถ€ ํŽธ์ง‘ ๊ธฐ๋Šฅ ๊ตฌํ˜„
jinukeu Jan 16, 2024
537238f
feat: DeleteLedgerUseCase ์ถ”๊ฐ€
jinukeu Jan 16, 2024
24a2b96
feat: ์žฅ๋ถ€ ์‚ญ์ œ ๊ธฐ๋Šฅ ๊ตฌํ˜„
jinukeu Jan 16, 2024
38fc742
fix: ์‹œ์Šคํ…œ ๋’ค๋กœ๊ฐ€๊ธฐ ํด๋ฆญ ์‹œ ์žฅ๋ถ€ ์—…๋ฐ์ดํŠธ๊ฐ€ ๋˜์ง€ ์•Š๋Š” ๋ฌธ์ œ ํ•ด๊ฒฐ
jinukeu Jan 16, 2024
2a4da61
feat: ์žฅ๋ถ€ ์‚ญ์ œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ
jinukeu Jan 16, 2024
71b5bd2
feat: ktlint
jinukeu Jan 16, 2024
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
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),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

์—๋Ÿฌ ์ˆ˜์ • ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค,,

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()
Comment on lines +18 to +21
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

category ์บ์‹ฑ์„ ์œ„ํ•ด ๋„์ž…ํ–ˆ๋‹ค๊ณ  ์ดํ•ดํ•˜๋ฉด ๋ ๊นŒ์šฉ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

๋„ค ๋งž์Šต๋‹ˆ๋‹ค!

}
Loading