Skip to content
This repository has been archived by the owner on Nov 5, 2024. It is now read-only.

Commit

Permalink
Implement Manual CSV import
Browse files Browse the repository at this point in the history
  • Loading branch information
ILIYANGERMANOV committed Apr 15, 2023
1 parent 0b851db commit cc1b02f
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 31 deletions.
1 change: 1 addition & 0 deletions app/src/main/java/com/ivy/wallet/ui/csv/CSVEvent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ sealed interface CSVEvent {
data class MapDescription(val index: Int, val name: String) : CSVEvent

object Continue : CSVEvent
object ResetState : CSVEvent

}
37 changes: 30 additions & 7 deletions app/src/main/java/com/ivy/wallet/ui/csv/CSVScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,36 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import com.google.accompanist.insets.systemBarsPadding
import com.ivy.design.l0_system.UI
import com.ivy.design.l0_system.colorAs
import com.ivy.frp.view.navigation.navigation
import com.ivy.wallet.ui.csvimport.flow.ImportProcessing
import com.ivy.wallet.ui.csvimport.flow.ImportResultUI
import com.ivy.wallet.ui.ivyWalletCtx
import com.ivy.wallet.utils.thenIf
import kotlin.math.abs

@Composable
fun CSVScreen() {
val viewModel: CSVViewModel = viewModel()
UI(state = viewModel.uiState(), onEvent = viewModel::onEvent)
val state = viewModel.uiState()
val nav = navigation()
when (val ui = state.uiState) {
UIState.Idle -> ImportUI(state = state, onEvent = viewModel::onEvent)
is UIState.Processing -> ImportProcessing(progressPercent = ui.percent)
is UIState.Result -> ImportResultUI(
result = ui.importResult,
isManualCsvImport = true,
onTryAgain = {
viewModel.onEvent(CSVEvent.ResetState)
},
onFinish = {
nav.back()
}
)
}
}

@Composable
private fun UI(
private fun ImportUI(
state: CSVState,
onEvent: (CSVEvent) -> Unit,
) {
Expand Down Expand Up @@ -301,6 +320,7 @@ private fun AmountMetadata(
multiplier: Int,
onMetaChange: (Int) -> Unit,
) {
Text(text = "Multiplier", style = UI.typo.nB2)
Row(verticalAlignment = Alignment.CenterVertically) {
Button(onClick = {
onMetaChange(
Expand All @@ -316,8 +336,8 @@ private fun AmountMetadata(
Spacer8(horizontal = true)
Text(
text = when {
multiplier < 0 -> "/$multiplier"
multiplier > 1 -> "*$multiplier"
multiplier < 0 -> "/${abs(multiplier)}"
multiplier > 1 -> "*${abs(multiplier)}"
else -> "None"
},
style = UI.typo.nB2,
Expand Down Expand Up @@ -458,9 +478,12 @@ fun LazyListScope.transferFields(
status = transferFields.toAmountStatus,
onMapTo = { index, name -> onEvent(CSVEvent.MapToAmount(index, name)) },
metadataContent = { multiplier ->
AmountMetadata(multiplier = multiplier, onMetaChange = {
onEvent(CSVEvent.ToAmountMetaChange(it))
})
AmountMetadata(
multiplier = multiplier,
onMetaChange = {
onEvent(CSVEvent.ToAmountMetaChange(it))
}
)
}
)
}
Expand Down
9 changes: 9 additions & 0 deletions app/src/main/java/com/ivy/wallet/ui/csv/CSVState.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.ivy.wallet.ui.csv

import com.ivy.wallet.domain.deprecated.logic.csv.model.ImportResult

data class CSVState(
val uiState: UIState,
val columns: CSVRow?,
val csv: List<CSVRow>?,
val important: ImportantFields?,
Expand All @@ -9,6 +12,12 @@ data class CSVState(
val continueEnabled: Boolean,
)

sealed interface UIState {
object Idle : UIState
data class Processing(val percent: Int) : UIState
data class Result(val importResult: ImportResult) : UIState
}

data class ImportantFields(
val amount: ColumnMapping<Int>,
val amountStatus: MappingStatus,
Expand Down
64 changes: 58 additions & 6 deletions app/src/main/java/com/ivy/wallet/ui/csv/CSVViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.ivy.wallet.domain.deprecated.logic.csv.IvyFileReader
import com.ivy.wallet.ui.csv.domain.*
import com.ivy.wallet.utils.uiThread
import com.opencsv.CSVReaderBuilder
import com.opencsv.validators.LineValidator
import com.opencsv.validators.RowValidator
Expand All @@ -17,11 +18,12 @@ import kotlinx.coroutines.withContext
import java.io.StringReader
import java.nio.charset.Charset
import javax.inject.Inject
import kotlin.math.roundToInt

@HiltViewModel
class CSVViewModel @Inject constructor(
private val fileReader: IvyFileReader,
private val csvImporterV2: CSVImporterV2,
private val csvImporter: CSVImporterV2,
) : ViewModel() {

private var columns by mutableStateOf<CSVRow?>(null)
Expand Down Expand Up @@ -179,16 +181,19 @@ class CSVViewModel @Inject constructor(
)
// endregion

private var uiState by mutableStateOf<UIState>(UIState.Idle)


@Composable
fun uiState(): CSVState {
val sampleCSV = remember(csv) {
// drop the header
csv?.drop(1)?.shuffled()?.take(SAMPLE_SIZE)
csv?.drop(1)?.take(SAMPLE_SIZE)
}

val important = importantFields(sampleCSV)
return CSVState(
uiState = uiState,
columns = columns,
csv = csv,
important = important,
Expand Down Expand Up @@ -239,7 +244,7 @@ class CSVViewModel @Inject constructor(
private fun transferFields(sampleCSV: List<CSVRow>?): TransferFields? {
return produceState<TransferFields?>(
initialValue = null,
sampleCSV, toAccount, toAccountCurrency,
sampleCSV, toAccount, toAccountCurrency, toAmount,
) {
val result = withContext(Dispatchers.Default) {
if (sampleCSV != null) {
Expand Down Expand Up @@ -373,6 +378,9 @@ class CSVViewModel @Inject constructor(
metadata = event.multiplier
)
}
CSVEvent.ResetState -> {
uiState = UIState.Idle
}
}
}

Expand All @@ -382,7 +390,7 @@ class CSVViewModel @Inject constructor(
columns = csv?.firstOrNull()
}

private suspend fun processFile(
private fun processFile(
uri: Uri,
charset: Charset = Charsets.UTF_8
): List<CSVRow>? {
Expand All @@ -397,7 +405,7 @@ class CSVViewModel @Inject constructor(
}
}

private suspend fun parseCSV(csv: String): List<CSVRow> {
private fun parseCSV(csv: String): List<CSVRow> {
val csvReader = CSVReaderBuilder(StringReader(csv))
.withLineValidator(object : LineValidator {
override fun isValid(line: String?): Boolean {
Expand Down Expand Up @@ -425,8 +433,52 @@ class CSVViewModel @Inject constructor(
}
// endregion

suspend private fun handleContinue() {
private suspend fun handleContinue() {
val csv = this.csv ?: return
val emptyStatus = MappingStatus(emptyList(), false)

withContext(Dispatchers.IO) {

val result = csvImporter.import(
csv = csv,
importantFields = ImportantFields(
amount = amount,
date = date,
type = type,
account = account,
accountCurrency = account,
amountStatus = emptyStatus,
dateStatus = emptyStatus,
accountStatus = emptyStatus,
typeStatus = emptyStatus,
accountCurrencyStatus = emptyStatus,
),
transferFields = TransferFields(
toAccount = toAccount,
toAccountCurrency = toAccountCurrency,
toAmount = toAmount,
toAmountStatus = emptyStatus,
toAccountStatus = emptyStatus,
toAccountCurrencyStatus = emptyStatus,
),
optionalFields = OptionalFields(
category = category,
title = title,
description = description,
categoryStatus = emptyStatus,
titleStatus = emptyStatus,
descriptionStatus = emptyStatus,
),
onProgress = { progressPercent ->
uiThread {
uiState = UIState.Processing(
(progressPercent * 100).roundToInt()
)
}
}
)
uiState = UIState.Result(result)
}
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ import com.ivy.wallet.utils.format
@Composable
fun ImportResultUI(
result: ImportResult,
isManualCsvImport: Boolean = false,

onTryAgain: (() -> Unit)? = null,
onFinish: () -> Unit
) {
Column(
Expand Down Expand Up @@ -153,25 +155,27 @@ fun ImportResultUI(

//TODO: Implement "See failed imports"

Spacer(modifier = Modifier.height(16.dp))
Text(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
text = "If this didn't work, Try manual CSV import.",
color = UI.colors.pureInverse,
)
Spacer(modifier = Modifier.height(8.dp))
Button(
modifier = Modifier
.fillMaxWidth()
.height(52.dp)
.padding(horizontal = 16.dp),
onClick = {
nav.navigateTo(CSVScreen)
if (!isManualCsvImport) {
Spacer(modifier = Modifier.height(16.dp))
Text(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
text = "If this didn't work, Try manual CSV import.",
color = UI.colors.pureInverse,
)
Spacer(modifier = Modifier.height(8.dp))
Button(
modifier = Modifier
.fillMaxWidth()
.height(52.dp)
.padding(horizontal = 16.dp),
onClick = {
nav.navigateTo(CSVScreen)
}
) {
Text(text = "Manual CSV import")
}
) {
Text(text = "Manual CSV import")
}

Spacer(Modifier.weight(1f))
Expand All @@ -189,6 +193,20 @@ fun ImportResultUI(
onFinish()
}

if (onTryAgain != null) {
Spacer(Modifier.height(12.dp))

Button(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 24.dp),
onClick = onTryAgain,
enabled = true
) {
Text(text = "Try again")
}
}

Spacer(Modifier.height(16.dp))
}
}
Expand Down

0 comments on commit cc1b02f

Please sign in to comment.