Archiving and Label options

This commit is contained in:
soraefir
2026-06-30 20:40:32 +02:00
parent 88469fa96c
commit 678a90ead9
4 changed files with 159 additions and 35 deletions
@@ -65,6 +65,7 @@ import net.helcel.cowspent.android.statistics.ProjectStatisticsActivity
import net.helcel.cowspent.model.DBBill
import net.helcel.cowspent.model.DBMember
import net.helcel.cowspent.model.DBProject
import net.helcel.cowspent.model.ProjectType
import net.helcel.cowspent.model.SectionItem
import net.helcel.cowspent.persistence.CowspentSQLiteOpenHelper
import net.helcel.cowspent.util.IRefreshBillsListCallback
@@ -137,6 +138,10 @@ fun BillsListScreen(
onProjectAction(projectOptionsProjectId, 3)
viewModel.showProjectOptionsDialogByProjectId = null
},
onManageLabels = {
onProjectAction(projectOptionsProjectId, 8)
viewModel.showProjectOptionsDialogByProjectId = null
},
onStatistics = {
onProjectAction(projectOptionsProjectId, 4)
viewModel.showProjectOptionsDialogByProjectId = null
@@ -155,6 +160,7 @@ fun BillsListScreen(
},
onDismiss = { viewModel.showProjectOptionsDialogByProjectId = null },
isArchived = proj?.isArchived == true,
projectType = proj?.type ?: ProjectType.LOCAL,
accessLevel = proj?.myAccessLevel ?: DBProject.ACCESS_LEVEL_ADMIN,
isShareable = proj?.isShareable() ?: true
)
@@ -45,6 +45,7 @@ import net.helcel.cowspent.model.Category
import net.helcel.cowspent.model.DBBill
import net.helcel.cowspent.model.DBProject
import net.helcel.cowspent.model.GroupedBill
import net.helcel.cowspent.model.ProjectType
import net.helcel.cowspent.persistence.CowspentSQLiteOpenHelper
import net.helcel.cowspent.persistence.CowspentServerSyncHelper
import net.helcel.cowspent.theme.ThemeUtils
@@ -254,13 +255,21 @@ class BillsListViewActivity :
onProjectAction = { pid, actionIndex ->
when (actionIndex) {
0 -> onEditProjectClick(pid)
1 -> onRemoveProjectClick(pid)
1 -> {
val proj = db.getProject(pid)
if (proj?.type == ProjectType.COSPEND) {
onArchiveProjectClick(pid)
} else {
onRemoveProjectClick(pid)
}
}
2 -> onManageMembersClick(pid)
3 -> onManageCurrenciesClick(pid)
4 -> onProjectStatisticsClick(pid)
5 -> onSettleProjectClick(pid)
6 -> onShareProjectClick(pid)
7 -> onExportProjectClick(pid)
8 -> { /* Manage Labels - Placeholder for now */ }
}
},
onAccountSwitcherClick = {
@@ -423,6 +432,38 @@ class BillsListViewActivity :
}
}
private fun onArchiveProjectClick(projectId: Long) {
if (projectId == 0L) return
lifecycleScope.launch {
val proj = withContext(Dispatchers.IO) { db.getProject(projectId) } ?: return@launch
val isArchiving = !proj.isArchived
val newArchivedTs = if (isArchiving) System.currentTimeMillis() / 1000 else 0L
withContext(Dispatchers.IO) {
db.updateProject(
projId = projectId,
newName = null,
newEmail = null,
newPassword = null,
newLastPayerId = null,
newLastSyncedTimestamp = null,
newCurrencyName = null,
newDeletionDisabled = null,
newMyAccessLevel = null,
newBearerToken = null,
newArchivedTs = newArchivedTs
)
}
setupDrawerProjects()
refreshLists()
val msgRes = if (isArchiving) R.string.action_archive else R.string.action_unarchive
showToast(this@BillsListViewActivity, getString(msgRes))
}
}
fun onManageMembersClick(projectId: Long) {
if (projectId == 0L) return
lifecycleScope.launch {
@@ -4,6 +4,7 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.Label
import androidx.compose.material.icons.filled.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@@ -15,8 +16,8 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import net.helcel.cowspent.R
import net.helcel.cowspent.model.DBProject
import net.helcel.cowspent.model.ProjectType
@Composable
fun ProjectOptionsDialogContent(
@@ -24,12 +25,14 @@ fun ProjectOptionsDialogContent(
onRemoveProject: () -> Unit,
onManageMembers: () -> Unit,
onManageCurrencies: () -> Unit,
onManageLabels: () -> Unit,
onStatistics: () -> Unit,
onSettle: () -> Unit,
onShareProject: () -> Unit,
onExportProject: () -> Unit,
onDismiss: () -> Unit,
isArchived: Boolean = false,
projectType: ProjectType = ProjectType.LOCAL,
accessLevel: Int = DBProject.ACCESS_LEVEL_ADMIN,
isShareable: Boolean = true
) {
@@ -49,41 +52,63 @@ fun ProjectOptionsDialogContent(
modifier = Modifier.padding(bottom = 8.dp)
)
val options = mutableListOf<ProjectOption>()
val isMaintainer = accessLevel >= DBProject.ACCESS_LEVEL_MAINTAINER || accessLevel == DBProject.ACCESS_LEVEL_UNKNOWN
val isParticipant = accessLevel >= DBProject.ACCESS_LEVEL_PARTICIPANT || accessLevel == DBProject.ACCESS_LEVEL_UNKNOWN
val row1 = mutableListOf<ProjectOption>()
val row2 = mutableListOf<ProjectOption>()
val row3 = mutableListOf<ProjectOption>()
// Row 1: Edit, Share, Remove/Archive
if (!isArchived && isMaintainer) {
options.add(ProjectOption(stringResource(R.string.action_edit_project), Icons.Default.Edit, onEditProject))
}
options.add(ProjectOption(stringResource(R.string.fab_rm_project), Icons.Default.Delete, onRemoveProject))
if (!isArchived && isMaintainer) {
options.add(ProjectOption(stringResource(R.string.fab_manage_members), Icons.Default.Group, onManageMembers))
options.add(ProjectOption(stringResource(R.string.fab_manage_currencies), Icons.Default.MonetizationOn, onManageCurrencies))
}
options.add(ProjectOption(stringResource(R.string.fab_statistics), Icons.Default.BarChart, onStatistics))
if (!isArchived && isParticipant) {
options.add(ProjectOption(stringResource(R.string.fab_settle), Icons.Default.Handshake, onSettle))
row1.add(ProjectOption(stringResource(R.string.action_edit_project), Icons.Default.Edit, onEditProject))
}
if (isShareable && isParticipant) {
options.add(ProjectOption(stringResource(R.string.action_share_project), Icons.Default.Share, onShareProject))
row1.add(ProjectOption(stringResource(R.string.action_share_project), Icons.Default.Share, onShareProject))
}
if (projectType == ProjectType.COSPEND) {
val archiveLabel = if (isArchived) stringResource(R.string.action_unarchive) else stringResource(R.string.action_archive)
val archiveIcon = if (isArchived) Icons.Default.Unarchive else Icons.Default.Archive
row1.add(ProjectOption(archiveLabel, archiveIcon, onRemoveProject))
} else {
row1.add(ProjectOption(stringResource(R.string.fab_rm_project), Icons.Default.Delete, onRemoveProject))
}
options.add(ProjectOption(stringResource(R.string.fab_export_project), Icons.Default.Download, onExportProject))
// Simple 2-column grid using Rows
for (i in options.indices step 2) {
Row(modifier = Modifier.fillMaxWidth()) {
ProjectOptionItem(
option = options[i],
modifier = Modifier.weight(1f)
)
if (i + 1 < options.size) {
// Row 2: Manage Member, Manage Labels, Manage Currencies
if (!isArchived && isMaintainer) {
row2.add(ProjectOption(stringResource(R.string.fab_manage_members), Icons.Default.Group, onManageMembers))
row2.add(ProjectOption(stringResource(R.string.fab_manage_labels), Icons.AutoMirrored.Filled.Label, onManageLabels))
row2.add(ProjectOption(stringResource(R.string.fab_manage_currencies), Icons.Default.MonetizationOn, onManageCurrencies))
}
// Row 3: Statistics, Settle, Export
row3.add(ProjectOption(stringResource(R.string.fab_statistics), Icons.Default.BarChart, onStatistics))
if (!isArchived && isParticipant) {
row3.add(ProjectOption(stringResource(R.string.fab_settle), Icons.Default.Handshake, onSettle))
}
row3.add(ProjectOption(stringResource(R.string.fab_export_project), Icons.Default.Download, onExportProject))
val allRows = listOf(row1, row2, row3).filter { it.isNotEmpty() }
allRows.forEach { rowOptions ->
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
) {
val emptySpace = 3 - rowOptions.size
if (emptySpace > 0) {
Spacer(modifier = Modifier.weight(emptySpace / 2f))
}
rowOptions.forEach { option ->
ProjectOptionItem(
option = options[i + 1],
option = option,
modifier = Modifier.weight(1f)
)
} else {
Spacer(modifier = Modifier.weight(1f))
}
if (emptySpace > 0) {
Spacer(modifier = Modifier.weight(emptySpace / 2f))
}
}
}
@@ -145,10 +170,59 @@ fun ProjectOptionsDialogPreview() {
onManageCurrencies = {},
onStatistics = {},
onSettle = {},
onManageLabels = {},
onShareProject = {},
onExportProject = {},
onDismiss = {},
isArchived = false,
projectType = ProjectType.COSPEND,
accessLevel = DBProject.ACCESS_LEVEL_ADMIN,
isShareable = true
)
}
}
@Preview(showBackground = true)
@Composable
fun ProjectOptionsDialogPreview2() {
MaterialTheme {
ProjectOptionsDialogContent(
onEditProject = {},
onRemoveProject = {},
onManageMembers = {},
onManageCurrencies = {},
onStatistics = {},
onSettle = {},
onManageLabels = {},
onShareProject = {},
onExportProject = {},
onDismiss = {},
isArchived = true,
projectType = ProjectType.COSPEND,
accessLevel = DBProject.ACCESS_LEVEL_ADMIN,
isShareable = true
)
}
}
@Preview(showBackground = true)
@Composable
fun ProjectOptionsDialogPreview3() {
MaterialTheme {
ProjectOptionsDialogContent(
onEditProject = {},
onRemoveProject = {},
onManageMembers = {},
onManageCurrencies = {},
onStatistics = {},
onSettle = {},
onManageLabels = {},
onShareProject = {},
onExportProject = {},
onDismiss = {},
isArchived = false,
projectType = ProjectType.LOCAL,
accessLevel = DBProject.ACCESS_LEVEL_ADMIN,
isShareable = true
)
+12 -9
View File
@@ -8,8 +8,8 @@
<string name="action_scan_qrcode">Scan QRCode from Cospend/Cowspent</string>
<string name="action_add_project">Add project</string>
<string name="action_save_bill">Save bill</string>
<string name="action_edit_project">Edit project</string>
<string name="action_share_project">Share project</string>
<string name="action_edit_project">Edit</string>
<string name="action_share_project">Share</string>
<string name="setting_what">What?</string>
<string name="setting_payer">Who paid?</string>
<string name="setting_all">All</string>
@@ -82,12 +82,15 @@
<string name="simple_share_share">Share Cowspent link</string>
<string name="menu_delete_project_remote">Delete project on server</string>
<string name="menu_save_project">Save project</string>
<string name="fab_add_member">Add a member</string>
<string name="fab_rm_project">Remove Project</string>
<string name="fab_add_member">Add member</string>
<string name="fab_rm_project">Remove</string>
<string name="remove_project_confirmation">Project %1$s has been removed</string>
<string name="choose_project_management_action">Manage project</string>
<string name="fab_manage_members">Manage members</string>
<string name="fab_export_project">Export project</string>
<string name="choose_project_management_action">Project</string>
<string name="fab_manage_members">Members</string>
<string name="fab_manage_labels">Labels</string>
<string name="action_archive">Archive</string>
<string name="action_unarchive">Unarchive</string>
<string name="fab_export_project">Export</string>
<string name="drawer_last_sync_text">Last sync: %1$02d:%2$02d</string>
<string name="scan_qrcode">Scan QR code</string>
<string name="new_project_from_nextcloud_tooltip">Choose from Nextcloud account</string>
@@ -147,7 +150,7 @@
<string name="stats_paid">Paid</string>
<string name="stats_spent">Spent</string>
<string name="stats_balance">Balance</string>
<string name="fab_statistics">Statistics</string>
<string name="fab_statistics">Stats</string>
<string name="fab_settle">Settle</string>
<string name="project_add_success_message">Project %1$s was successfully added</string>
<string name="project_create_success_message">Project %1$s was successfully created</string>
@@ -240,7 +243,7 @@
<!-- QR Code Scanner -->
<string name="error_scanning_bill_qr_code">QR code could not be scanned.</string>
<string name="fab_manage_currencies">Manage currencies</string>
<string name="fab_manage_currencies">Currencies</string>
<string name="setting_show_archived">Show archived projects</string>