diff --git a/app/src/main/java/net/helcel/cowspent/android/main/BillsListScreen.kt b/app/src/main/java/net/helcel/cowspent/android/main/BillsListScreen.kt index f1a9e70..0c0f1a0 100644 --- a/app/src/main/java/net/helcel/cowspent/android/main/BillsListScreen.kt +++ b/app/src/main/java/net/helcel/cowspent/android/main/BillsListScreen.kt @@ -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 ) diff --git a/app/src/main/java/net/helcel/cowspent/android/main/BillsListViewActivity.kt b/app/src/main/java/net/helcel/cowspent/android/main/BillsListViewActivity.kt index 848be87..148786e 100644 --- a/app/src/main/java/net/helcel/cowspent/android/main/BillsListViewActivity.kt +++ b/app/src/main/java/net/helcel/cowspent/android/main/BillsListViewActivity.kt @@ -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 { diff --git a/app/src/main/java/net/helcel/cowspent/android/project/ProjectOptionsDialog.kt b/app/src/main/java/net/helcel/cowspent/android/project/ProjectOptionsDialog.kt index 9d39a8b..178b13f 100644 --- a/app/src/main/java/net/helcel/cowspent/android/project/ProjectOptionsDialog.kt +++ b/app/src/main/java/net/helcel/cowspent/android/project/ProjectOptionsDialog.kt @@ -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() 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() + val row2 = mutableListOf() + val row3 = mutableListOf() + + // 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 ) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b27a701..3f61dc1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -8,8 +8,8 @@ Scan QRCode from Cospend/Cowspent Add project Save bill - Edit project - Share project + Edit + Share What? Who paid? All @@ -82,12 +82,15 @@ Share Cowspent link Delete project on server Save project - Add a member - Remove Project + Add member + Remove Project %1$s has been removed - Manage project - Manage members - Export project + Project + Members + Labels + Archive + Unarchive + Export Last sync: %1$02d:%2$02d Scan QR code Choose from Nextcloud account @@ -147,7 +150,7 @@ Paid Spent Balance - Statistics + Stats Settle Project %1$s was successfully added Project %1$s was successfully created @@ -240,7 +243,7 @@ QR code could not be scanned. - Manage currencies + Currencies Show archived projects