Fix stats render and add 'disabled-member' setting
This commit is contained in:
@@ -31,6 +31,7 @@ import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
|||||||
import androidx.compose.material.icons.filled.AccountCircle
|
import androidx.compose.material.icons.filled.AccountCircle
|
||||||
import androidx.compose.material.icons.filled.Archive
|
import androidx.compose.material.icons.filled.Archive
|
||||||
import androidx.compose.material.icons.filled.Brightness2
|
import androidx.compose.material.icons.filled.Brightness2
|
||||||
|
import androidx.compose.material.icons.filled.Group
|
||||||
import androidx.compose.material.icons.filled.Info
|
import androidx.compose.material.icons.filled.Info
|
||||||
import androidx.compose.material.icons.filled.Palette
|
import androidx.compose.material.icons.filled.Palette
|
||||||
import androidx.compose.material.icons.filled.Sync
|
import androidx.compose.material.icons.filled.Sync
|
||||||
@@ -84,6 +85,7 @@ fun SettingsScreen(
|
|||||||
val keyOfflineMode = stringResource(R.string.pref_key_offline_mode)
|
val keyOfflineMode = stringResource(R.string.pref_key_offline_mode)
|
||||||
val keyShowArchived = stringResource(R.string.pref_key_show_archived)
|
val keyShowArchived = stringResource(R.string.pref_key_show_archived)
|
||||||
val keyBetaFeatures = stringResource(R.string.pref_key_beta_features)
|
val keyBetaFeatures = stringResource(R.string.pref_key_beta_features)
|
||||||
|
val keyStatsIncludeDeactivated = stringResource(R.string.pref_key_stats_include_deactivated)
|
||||||
val keyAutoSyncOnOpen = stringResource(R.string.pref_key_auto_sync_on_open)
|
val keyAutoSyncOnOpen = stringResource(R.string.pref_key_auto_sync_on_open)
|
||||||
val keyFillNewBillFromLast = stringResource(R.string.pref_key_fill_new_bill_from_last)
|
val keyFillNewBillFromLast = stringResource(R.string.pref_key_fill_new_bill_from_last)
|
||||||
val keyLastAccountSync = stringResource(R.string.pref_key_last_account_sync_timestamp)
|
val keyLastAccountSync = stringResource(R.string.pref_key_last_account_sync_timestamp)
|
||||||
@@ -132,6 +134,9 @@ fun SettingsScreen(
|
|||||||
var fillNewBillFromLast by remember(keyFillNewBillFromLast) {
|
var fillNewBillFromLast by remember(keyFillNewBillFromLast) {
|
||||||
mutableStateOf(sharedPreferences.getBoolean(keyFillNewBillFromLast, false))
|
mutableStateOf(sharedPreferences.getBoolean(keyFillNewBillFromLast, false))
|
||||||
}
|
}
|
||||||
|
var statsIncludeDeactivated by remember(keyStatsIncludeDeactivated) {
|
||||||
|
mutableStateOf(sharedPreferences.getBoolean(keyStatsIncludeDeactivated, false))
|
||||||
|
}
|
||||||
|
|
||||||
val syncIntervals = SyncSettings.INTERVAL_CHOICES_MINUTES
|
val syncIntervals = SyncSettings.INTERVAL_CHOICES_MINUTES
|
||||||
val syncIntervalLabels = listOf(
|
val syncIntervalLabels = listOf(
|
||||||
@@ -304,6 +309,18 @@ fun SettingsScreen(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
SettingsSwitchPreference(
|
||||||
|
title = stringResource(R.string.settings_stats_include_deactivated),
|
||||||
|
icon = Icons.Default.Group,
|
||||||
|
checked = statsIncludeDeactivated,
|
||||||
|
onCheckedChange = {
|
||||||
|
statsIncludeDeactivated = it
|
||||||
|
sharedPreferences.edit {
|
||||||
|
putBoolean(keyStatsIncludeDeactivated, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
SettingsSliderPreference(
|
SettingsSliderPreference(
|
||||||
title = stringResource(R.string.settings_auto_sync_on_open),
|
title = stringResource(R.string.settings_auto_sync_on_open),
|
||||||
summary = stringResource(R.string.settings_auto_sync_on_open_summary),
|
summary = stringResource(R.string.settings_auto_sync_on_open_summary),
|
||||||
|
|||||||
@@ -121,17 +121,19 @@ fun ProjectSankeyDiagram(
|
|||||||
val catMap = mutableMapOf<Long, Double>()
|
val catMap = mutableMapOf<Long, Double>()
|
||||||
|
|
||||||
activeBills.forEach { bill ->
|
activeBills.forEach { bill ->
|
||||||
val totalWeight = bill.billOwers.sumOf { membersMap[it.memberId]?.weight ?: 1.0 }
|
val totalWeight = bill.billOwers.sumOf { membersMap[it.memberId]?.weight ?: 0.0 }
|
||||||
if (totalWeight > 0) {
|
if (totalWeight > 0) {
|
||||||
if (selectedMemberId == -1L) {
|
if (selectedMemberId == -1L) {
|
||||||
catMap[bill.categoryId] = (catMap[bill.categoryId] ?: 0.0) + bill.amount
|
catMap[bill.categoryId] = (catMap[bill.categoryId] ?: 0.0) + bill.amount
|
||||||
bill.billOwers.forEach { bo ->
|
bill.billOwers.forEach { bo ->
|
||||||
val weight = membersMap[bo.memberId]?.weight ?: 1.0
|
if (membersMap.containsKey(bo.memberId)) {
|
||||||
spentMap[bo.memberId] = (spentMap[bo.memberId] ?: 0.0) + (bill.amount / totalWeight) * weight
|
val weight = membersMap[bo.memberId]?.weight ?: 0.0
|
||||||
|
spentMap[bo.memberId] = (spentMap[bo.memberId] ?: 0.0) + (bill.amount / totalWeight) * weight
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bill.billOwers.find { it.memberId == selectedMemberId }?.let { bo ->
|
bill.billOwers.find { it.memberId == selectedMemberId }?.let { bo ->
|
||||||
val weight = membersMap[bo.memberId]?.weight ?: 1.0
|
val weight = membersMap[bo.memberId]?.weight ?: 0.0
|
||||||
catMap[bill.categoryId] = (catMap[bill.categoryId] ?: 0.0) + (bill.amount / totalWeight) * weight
|
catMap[bill.categoryId] = (catMap[bill.categoryId] ?: 0.0) + (bill.amount / totalWeight) * weight
|
||||||
spentMap[selectedMemberId] = (spentMap[selectedMemberId] ?: 0.0) + (bill.amount / totalWeight) * weight
|
spentMap[selectedMemberId] = (spentMap[selectedMemberId] ?: 0.0) + (bill.amount / totalWeight) * weight
|
||||||
}
|
}
|
||||||
@@ -344,7 +346,9 @@ private fun SankeyContent(
|
|||||||
scope.launch { topFocalIndex.animateTo(index.toFloat(), spring(Spring.DampingRatioLowBouncy, Spring.StiffnessLow)) }
|
scope.launch { topFocalIndex.animateTo(index.toFloat(), spring(Spring.DampingRatioLowBouncy, Spring.StiffnessLow)) }
|
||||||
} else Modifier), contentAlignment = Alignment.Center) {
|
} else Modifier), contentAlignment = Alignment.Center) {
|
||||||
Column(modifier = Modifier.fillMaxSize().background(color = member?.let { Color(it.r ?: 128, it.g ?: 128, it.b ?: 128) } ?: Color.Gray, shape = RoundedCornerShape(10.dp)), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center) {
|
Column(modifier = Modifier.fillMaxSize().background(color = member?.let { Color(it.r ?: 128, it.g ?: 128, it.b ?: 128) } ?: Color.Gray, shape = RoundedCornerShape(10.dp)), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center) {
|
||||||
Text(text = member?.name ?: "???", color = Color.White, fontWeight = FontWeight.Bold, maxLines = 1, overflow = TextOverflow.Ellipsis, textAlign = TextAlign.Center, modifier = Modifier.wrapContentWidth(unbounded = true))
|
if (wDp >= 32.dp) {
|
||||||
|
Text(text = member?.name ?: "???", color = Color.White, fontWeight = FontWeight.Bold, maxLines = 1, overflow = TextOverflow.Ellipsis, textAlign = TextAlign.Center, modifier = Modifier.wrapContentWidth(unbounded = true))
|
||||||
|
}
|
||||||
if (wDp >= 40.dp) Text(text = formatShortValue(amount), fontSize = 12.sp, color = Color.White.copy(alpha = 0.9f), textAlign = TextAlign.Center)
|
if (wDp >= 40.dp) Text(text = formatShortValue(amount), fontSize = 12.sp, color = Color.White.copy(alpha = 0.9f), textAlign = TextAlign.Center)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -382,7 +386,9 @@ private fun SankeyContent(
|
|||||||
scope.launch { bottomFocalIndex.animateTo(index.toFloat(), spring(Spring.DampingRatioLowBouncy, Spring.StiffnessLow)) }
|
scope.launch { bottomFocalIndex.animateTo(index.toFloat(), spring(Spring.DampingRatioLowBouncy, Spring.StiffnessLow)) }
|
||||||
} else Modifier), contentAlignment = Alignment.Center) {
|
} else Modifier), contentAlignment = Alignment.Center) {
|
||||||
Column(modifier = Modifier.fillMaxSize().background(color = category?.color?.let { try { Color(it.toColorInt()) } catch (_: Exception) { Color(0xFF999999) } } ?: Color(0xFF999999), shape = RoundedCornerShape(10.dp)), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center) {
|
Column(modifier = Modifier.fillMaxSize().background(color = category?.color?.let { try { Color(it.toColorInt()) } catch (_: Exception) { Color(0xFF999999) } } ?: Color(0xFF999999), shape = RoundedCornerShape(10.dp)), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center) {
|
||||||
Text(text = category?.icon ?: "❔", fontSize = 20.sp, textAlign = TextAlign.Center, modifier = Modifier.wrapContentWidth(unbounded = true))
|
if (wDp >= 24.dp) {
|
||||||
|
Text(text = category?.icon ?: "❔", fontSize = 20.sp, textAlign = TextAlign.Center, modifier = Modifier.wrapContentWidth(unbounded = true))
|
||||||
|
}
|
||||||
if (wDp >= 40.dp) Text(text = formatShortValue(amount), fontSize = 12.sp, color = Color.White.copy(alpha = 0.9f), textAlign = TextAlign.Center)
|
if (wDp >= 40.dp) Text(text = formatShortValue(amount), fontSize = 12.sp, color = Color.White.copy(alpha = 0.9f), textAlign = TextAlign.Center)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ fun ProjectStatisticsScreen(
|
|||||||
"Sankey"
|
"Sankey"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val keyStatsIncludeDeactivated = stringResource(R.string.pref_key_stats_include_deactivated)
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
@@ -103,7 +105,11 @@ fun ProjectStatisticsScreen(
|
|||||||
val bills = db.getBillsOfProject(proj.id)
|
val bills = db.getBillsOfProject(proj.id)
|
||||||
val categories = db.getCategories(proj.id)
|
val categories = db.getCategories(proj.id)
|
||||||
val paymentModes = db.getPaymentModes(proj.id)
|
val paymentModes = db.getPaymentModes(proj.id)
|
||||||
StatisticsData(members, bills, categories, paymentModes)
|
|
||||||
|
val includeDeactivated = prefs.getBoolean(keyStatsIncludeDeactivated, false)
|
||||||
|
val filteredMembers = if (includeDeactivated) members else members.filter { it.isActivated }
|
||||||
|
|
||||||
|
StatisticsData(filteredMembers, bills, categories, paymentModes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -143,6 +143,7 @@
|
|||||||
<string name="settings_beta_features_summary">Enable experimental features. Use at your own risk.</string>
|
<string name="settings_beta_features_summary">Enable experimental features. Use at your own risk.</string>
|
||||||
<string name="settings_fill_new_bill_from_last">Auto-fill from last bill</string>
|
<string name="settings_fill_new_bill_from_last">Auto-fill from last bill</string>
|
||||||
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
||||||
|
<string name="settings_stats_include_deactivated">Include deactivated members in stats</string>
|
||||||
<string name="settings_auto_sync_on_open">Sync interval</string>
|
<string name="settings_auto_sync_on_open">Sync interval</string>
|
||||||
<string name="settings_auto_sync_on_open_summary">How often to refresh the account and all projects when opening the app.</string>
|
<string name="settings_auto_sync_on_open_summary">How often to refresh the account and all projects when opening the app.</string>
|
||||||
<string name="pref_value_sync_1m">1 minute</string>
|
<string name="pref_value_sync_1m">1 minute</string>
|
||||||
@@ -170,6 +171,7 @@
|
|||||||
<string name="pref_key_offline_mode" translatable="false">offlineMode</string>
|
<string name="pref_key_offline_mode" translatable="false">offlineMode</string>
|
||||||
<string name="pref_key_show_archived" translatable="false">showArchived</string>
|
<string name="pref_key_show_archived" translatable="false">showArchived</string>
|
||||||
<string name="pref_key_beta_features" translatable="false">betaFeatures</string>
|
<string name="pref_key_beta_features" translatable="false">betaFeatures</string>
|
||||||
|
<string name="pref_key_stats_include_deactivated" translatable="false">statsIncludeDeactivated</string>
|
||||||
<string name="pref_key_auto_sync_on_open" translatable="false">autoSyncOnOpen</string>
|
<string name="pref_key_auto_sync_on_open" translatable="false">autoSyncOnOpen</string>
|
||||||
<string name="pref_key_last_account_sync_timestamp" translatable="false">lastAccountSyncTimestamp</string>
|
<string name="pref_key_last_account_sync_timestamp" translatable="false">lastAccountSyncTimestamp</string>
|
||||||
<string name="pref_key_fill_new_bill_from_last" translatable="false">fillNewBillFromLast</string>
|
<string name="pref_key_fill_new_bill_from_last" translatable="false">fillNewBillFromLast</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user