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

Commit

Permalink
WIP: Manual CSV mapping
Browse files Browse the repository at this point in the history
  • Loading branch information
ILIYANGERMANOV committed Apr 15, 2023
1 parent 5a1caa2 commit ed602f2
Show file tree
Hide file tree
Showing 4 changed files with 332 additions and 0 deletions.
8 changes: 8 additions & 0 deletions app/src/main/java/com/ivy/wallet/ui/csv/CSVEvent.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.ivy.wallet.ui.csv

import android.net.Uri

sealed interface CSVEvent {
data class FilePicked(val uri: Uri) : CSVEvent

}
222 changes: 222 additions & 0 deletions app/src/main/java/com/ivy/wallet/ui/csv/CSVScreen.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
package com.ivy.wallet.ui.csv

import android.net.Uri
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.rememberScrollState
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
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.wallet.ui.ivyWalletCtx
import com.ivy.wallet.utils.thenIf

@Composable
fun CSVScreen() {
val viewModel: CSVViewModel = viewModel()
UI(state = viewModel.uiState(), onEvent = viewModel::onEvent)
}

@Composable
private fun UI(
state: CSVState,
onEvent: (CSVEvent) -> Unit,
) {
LazyColumn(
modifier = Modifier
.fillMaxSize()
.systemBarsPadding(),
contentPadding = PaddingValues(
horizontal = 8.dp,
vertical = 16.dp,
)
) {
item(key = "import_btn") {
ImportButton(
onFilePicked = {
onEvent(CSVEvent.FilePicked(it))
}
)
}
if (state.csv != null) {
spacer8()
csvTable(state.csv)
}

}
}

@Composable
private fun ImportButton(
onFilePicked: (Uri) -> Unit,
) {
val ivyContext = ivyWalletCtx()
Button(
onClick = {
ivyContext.openFile {
onFilePicked(it)
}
}
) {
Text(text = "Import CSV")
}
}

fun LazyListScope.spacer8() {
item {
Spacer8()
}
}

@Composable
fun Spacer8() {
Spacer(modifier = Modifier.height(8.dp))
}

private fun LazyListScope.csvTable(
csv: List<CSVRow>
) {
itemsIndexed(items = csv) { index, row ->
CSVRow(row = row, header = index == 0, even = index % 2 == 0)
}
}

@Composable
private fun CSVRow(
row: CSVRow,
header: Boolean,
even: Boolean,
) {
Row(
modifier = Modifier
.fillMaxWidth()
.horizontalScroll(rememberScrollState())
) {
row.values.forEach { value ->
CSVCell(text = value, header = header, even = even)
}
}
}

@Composable
private fun CSVCell(
text: String,
header: Boolean,
even: Boolean
) {
Text(
modifier = Modifier
.width(120.dp)
.border(1.dp, UI.colors.pureInverse)
.thenIf(even) {
this.background(UI.colors.medium)
}
.padding(all = 4.dp),
text = text,
style = UI.typo.nB1,
fontWeight = if (header) FontWeight.ExtraBold else FontWeight.Normal,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
)
}


private fun <M> LazyListScope.mappingRow(
columns: CSVRow,
mapping: ColumnMapping<M>,
onMapTo: (Int, String) -> Unit,
metadataContent: (@Composable (M) -> Unit)? = null,
) {
item {
Spacer8()
Column(
modifier = Modifier
.fillMaxWidth()
.border(
width = 2.dp,
color = when {
mapping.required && !mapping.success -> UI.colors.red
mapping.success -> UI.colors.green
else -> UI.colors.medium
}
)
.padding(vertical = 8.dp, horizontal = 4.dp)
) {
Text(
text = mapping.ivyColumn,
style = UI.typo.b1.colorAs(UI.colors.primary),
)
Spacer8()
Row(
verticalAlignment = Alignment.CenterVertically
) {
columns.values.forEachIndexed { index, column ->
Button(
colors = if (column == mapping.name) {
ButtonDefaults.buttonColors()
} else {
ButtonDefaults.outlinedButtonColors()
},
onClick = {
onMapTo(index, column)
}
) {
Text(text = column)
}
Spacer(Modifier.width(8.dp))
}
}

if (metadataContent != null) {
Spacer8()
metadataContent(mapping.metadata)
}

if (mapping.sampleValues.isNotEmpty()) {
Spacer8()
CSVRow(row = CSVRow(mapping.sampleValues), header = false, even = true)
}
}
Spacer8()
}
}

fun LazyListScope.sectionDivider(text: String) {
item {
Spacer8()
Text(text = text, style = UI.typo.h2)
Spacer8()
}
}

fun LazyListScope.important(
columns: CSVRow,
importantFields: ImportantFields,
onEvent: (CSVEvent) -> Unit
) {
sectionDivider("Important")
mappingRow(
columns = columns,
mapping = importantFields.amount,
onMapTo = { index, name ->

},
metadataContent = {

}
)
}
57 changes: 57 additions & 0 deletions app/src/main/java/com/ivy/wallet/ui/csv/CSVState.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.ivy.wallet.ui.csv

import com.ivy.wallet.domain.data.core.Transaction

data class CSVState(
val columns: CSVRow,
val csv: List<CSVRow>?,
val important: ImportantFields?,
val transfer: TransferFields?,
val optional: OptionalFields?,
val transactions: List<Transaction>?
)

data class ImportantFields(
val amount: ColumnMapping<AmountMetadata>,
val type: ColumnMapping<TrnTypeMetadata>,
val date: ColumnMapping<String>,
val account: ColumnMapping<Unit>,
val accountCurrency: ColumnMapping<Unit>,
)

sealed interface AmountMetadata {
data class Multiple(val multiplier: Int) : AmountMetadata
data class Divider(val divider: Int) : AmountMetadata
}

data class TrnTypeMetadata(
val income: String,
val expense: String,
val transfer: String?,
)

data class TransferFields(
val toAccount: ColumnMapping<Unit>,
val toAccountCurrency: ColumnMapping<Unit>,
)

data class OptionalFields(
val category: ColumnMapping<Unit>,
val title: ColumnMapping<Unit>,
val description: ColumnMapping<Unit>,
)

data class ColumnMapping<M>(
val ivyColumn: String,
val helpInfo: String,
val name: String,
val index: Int,
val sampleValues: List<String>,
val metadata: M,
val required: Boolean,
val success: Boolean,
)

data class CSVRow(
val values: List<String>
)
45 changes: 45 additions & 0 deletions app/src/main/java/com/ivy/wallet/ui/csv/CSVViewModel.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.ivy.wallet.ui.csv

import androidx.compose.runtime.Composable
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class CSVViewModel @Inject constructor(

) : ViewModel() {

@Composable
fun uiState(): CSVState = TODO()


private suspend fun handleEvent(event: CSVEvent) {

}


// region Boiler-plate
private val events = MutableSharedFlow<CSVEvent>(replay = 0)

init {
viewModelScope.launch {
events.collect {
handleEvent(it)
}
}
}

fun onEvent(event: CSVEvent) {
viewModelScope.launch {
events.emit(event)
}
}
// endregion
}

0 comments on commit ed602f2

Please sign in to comment.