From d2ed18bec73e6a4860ce588beebe1794366f8c9f Mon Sep 17 00:00:00 2001 From: Janne Mareike Koschinski <mail@justjanne.de> Date: Tue, 10 Jun 2025 05:19:05 +0200 Subject: [PATCH] refactor: history --- .../de/chaosdorf/meteroid/MeteroidRouter.kt | 1 + .../de/chaosdorf/meteroid/ui/HistoryRoute.kt | 150 ------------------ .../meteroid/ui/history/HistoryRoute.kt | 53 +++++++ .../ui/history/TransactionHistoryItem.kt | 85 ++++++++++ .../meteroid/ui/history/TransactionIcon.kt | 86 ++++++++++ 5 files changed, 225 insertions(+), 150 deletions(-) delete mode 100644 app/src/main/kotlin/de/chaosdorf/meteroid/ui/HistoryRoute.kt create mode 100644 app/src/main/kotlin/de/chaosdorf/meteroid/ui/history/HistoryRoute.kt create mode 100644 app/src/main/kotlin/de/chaosdorf/meteroid/ui/history/TransactionHistoryItem.kt create mode 100644 app/src/main/kotlin/de/chaosdorf/meteroid/ui/history/TransactionIcon.kt diff --git a/app/src/main/kotlin/de/chaosdorf/meteroid/MeteroidRouter.kt b/app/src/main/kotlin/de/chaosdorf/meteroid/MeteroidRouter.kt index f4890d2..3deba3f 100644 --- a/app/src/main/kotlin/de/chaosdorf/meteroid/MeteroidRouter.kt +++ b/app/src/main/kotlin/de/chaosdorf/meteroid/MeteroidRouter.kt @@ -40,6 +40,7 @@ import androidx.navigation3.runtime.rememberSavedStateNavEntryDecorator import androidx.navigation3.ui.NavDisplay import androidx.navigation3.ui.rememberSceneSetupNavEntryDecorator import de.chaosdorf.meteroid.ui.* +import de.chaosdorf.meteroid.ui.history.HistoryRoute import de.chaosdorf.meteroid.ui.purchase.PurchaseRoute import de.chaosdorf.meteroid.viewmodel.* import kotlinx.coroutines.flow.update diff --git a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/HistoryRoute.kt b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/HistoryRoute.kt deleted file mode 100644 index 0e95ec6..0000000 --- a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/HistoryRoute.kt +++ /dev/null @@ -1,150 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2013-2025 Chaosdorf e.V. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package de.chaosdorf.meteroid.ui - -import androidx.compose.foundation.Image -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.AttachMoney -import androidx.compose.material.icons.filled.QuestionMark -import androidx.compose.material3.Icon -import androidx.compose.material3.ListItem -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.unit.dp -import coil3.compose.rememberAsyncImagePainter -import de.chaosdorf.meteroid.model.Drink -import de.chaosdorf.meteroid.model.Transaction -import de.chaosdorf.meteroid.theme.secondaryGradient -import de.chaosdorf.meteroid.ui.common.PriceBadge -import de.chaosdorf.meteroid.viewmodel.HistoryViewModel -import de.chaosdorf.meteroid.viewmodel.Navigator -import kotlinx.datetime.TimeZone -import kotlinx.datetime.toJavaLocalDateTime -import kotlinx.datetime.toLocalDateTime -import java.math.BigDecimal -import java.time.format.DateTimeFormatter -import java.time.format.FormatStyle - -@Composable -fun HistoryRoute( - viewModel: HistoryViewModel, - navigator: Navigator, - contentPadding: PaddingValues, -) { - val transactions by viewModel.transactions.collectAsState() - - LazyColumn(contentPadding = contentPadding) { - items( - transactions, - key = { "transaction-${it.transaction.serverId}-${it.transaction.transactionId}" }, - ) { (transaction, drink) -> - TransactionHistoryItem(transaction, drink) - } - } -} - -@Composable -fun TransactionHistoryItem( - transaction: Transaction, - drink: Drink?, - modifier: Modifier = Modifier -) { - val timestamp = transaction.timestamp.toLocalDateTime(TimeZone.currentSystemDefault()) - val formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT) - - ListItem( - headlineContent = { - val label = when { - drink != null -> drink.name - transaction.difference > BigDecimal.ZERO -> "Deposit" - else -> "Unknown" - } - Text(label) - }, - supportingContent = { - Text(formatter.format(timestamp.toJavaLocalDateTime())) - }, - leadingContent = { - Box( - modifier = Modifier - .size(48.dp) - .clip(CircleShape) - .aspectRatio(1.0f) - .background(MaterialTheme.colorScheme.secondaryGradient.verticalGradient()) - ) { - if (drink != null) { - val thumbPainter = rememberAsyncImagePainter( - drink.logoUrl - ) - val originalPainter = rememberAsyncImagePainter( - drink.originalLogoUrl, - error = thumbPainter - ) - - Image( - painter = originalPainter, - contentDescription = null, - contentScale = ContentScale.Fit, - modifier = Modifier - .align(Alignment.Center) - .fillMaxSize() - ) - } else if (transaction.difference > BigDecimal.ZERO) { - Icon( - Icons.Default.AttachMoney, - contentDescription = null, - modifier = Modifier.align(Alignment.Center) - ) - } else { - Icon( - Icons.Default.QuestionMark, - contentDescription = null, - modifier = Modifier.align(Alignment.Center) - ) - } - } - }, - trailingContent = { - PriceBadge( - transaction.difference, - modifier = Modifier.padding(horizontal = 8.dp) - ) - }, - modifier = modifier, - ) -} - diff --git a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/history/HistoryRoute.kt b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/history/HistoryRoute.kt new file mode 100644 index 0000000..450c6d1 --- /dev/null +++ b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/history/HistoryRoute.kt @@ -0,0 +1,53 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2013-2025 Chaosdorf e.V. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package de.chaosdorf.meteroid.ui.history + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import de.chaosdorf.meteroid.viewmodel.HistoryViewModel +import de.chaosdorf.meteroid.viewmodel.Navigator + +@Composable +fun HistoryRoute( + viewModel: HistoryViewModel, + navigator: Navigator, + contentPadding: PaddingValues, +) { + val transactions by viewModel.transactions.collectAsState() + + LazyColumn(contentPadding = contentPadding) { + items( + transactions, + key = { "transaction-${it.transaction.serverId}-${it.transaction.transactionId}" }, + contentType = { if (it.drink != null) "transaction-drink" else "transaction-generic" } + ) { (transaction, drink) -> + TransactionHistoryItem(transaction, drink) + } + } +} diff --git a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/history/TransactionHistoryItem.kt b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/history/TransactionHistoryItem.kt new file mode 100644 index 0000000..935011d --- /dev/null +++ b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/history/TransactionHistoryItem.kt @@ -0,0 +1,85 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2013-2025 Chaosdorf e.V. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package de.chaosdorf.meteroid.ui.history + +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.ListItem +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import de.chaosdorf.meteroid.model.Drink +import de.chaosdorf.meteroid.model.Transaction +import de.chaosdorf.meteroid.ui.common.PriceBadge +import kotlinx.datetime.TimeZone +import kotlinx.datetime.toJavaLocalDateTime +import kotlinx.datetime.toLocalDateTime +import java.math.BigDecimal +import java.time.format.DateTimeFormatter +import java.time.format.FormatStyle + +@Composable +fun TransactionHistoryItem( + transaction: Transaction, + drink: Drink?, + modifier: Modifier = Modifier, +) { + val timestamp = remember(transaction.timestamp) { + val timestamp = transaction.timestamp.toLocalDateTime(TimeZone.Companion.currentSystemDefault()) + val formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT) + formatter.format(timestamp.toJavaLocalDateTime()) + } + val deposit = remember(transaction.difference) { + transaction.difference > BigDecimal.ZERO + } + + val label = remember(drink, deposit) { + when { + drink != null -> drink.name + deposit -> "Deposit" + else -> "Unknown" + } + } + + ListItem( + headlineContent = { + Text(label) + }, + supportingContent = { + Text(timestamp) + }, + leadingContent = { + TransactionIcon(drink, deposit) + }, + trailingContent = { + PriceBadge( + transaction.difference, + modifier = Modifier.padding(horizontal = 8.dp) + ) + }, + modifier = modifier, + ) +} diff --git a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/history/TransactionIcon.kt b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/history/TransactionIcon.kt new file mode 100644 index 0000000..9c2c371 --- /dev/null +++ b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/history/TransactionIcon.kt @@ -0,0 +1,86 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2013-2025 Chaosdorf e.V. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package de.chaosdorf.meteroid.ui.history + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.AttachMoney +import androidx.compose.material.icons.filled.QuestionMark +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.unit.dp +import coil3.compose.rememberAsyncImagePainter +import de.chaosdorf.meteroid.model.Drink +import de.chaosdorf.meteroid.theme.secondaryGradient + +@Composable +fun TransactionIcon( + drink: Drink?, + deposit: Boolean, +) { + Box( + modifier = Modifier + .size(48.dp) + .clip(CircleShape) + .aspectRatio(1.0f) + .background(MaterialTheme.colorScheme.secondaryGradient.verticalGradient()) + ) { + if (drink != null) { + val thumbPainter = rememberAsyncImagePainter( + drink.logoUrl, + ) + val originalPainter = rememberAsyncImagePainter( + drink.originalLogoUrl, + error = thumbPainter, + ) + + Image( + painter = originalPainter, + contentDescription = null, + contentScale = ContentScale.Companion.Fit, + modifier = Modifier + .align(Alignment.Companion.Center) + .fillMaxSize() + ) + } else { + Icon( + if (deposit) Icons.Default.AttachMoney else Icons.Default.QuestionMark, + contentDescription = null, + modifier = Modifier.align(Alignment.Companion.Center) + ) + } + } +} -- GitLab