Add local delete of archived projects

This commit is contained in:
2026-09-10 00:34:02 +02:00
parent d6ee50bc34
commit 5bc83e22ba
5 changed files with 91 additions and 67 deletions
@@ -132,6 +132,10 @@ fun BillsListScreen(
onProjectAction(projectOptionsProjectId, 1)
viewModel.showProjectOptionsDialogByProjectId = null
},
onForgetProject = {
onProjectAction(projectOptionsProjectId, 9)
viewModel.showProjectOptionsDialogByProjectId = null
},
onManageMembers = {
onProjectAction(projectOptionsProjectId, 2)
viewModel.showProjectOptionsDialogByProjectId = null
@@ -408,11 +412,9 @@ fun BillsListScreen(
.pullRefresh(pullRefreshState)) {
when {
viewModel.showNoProjects -> EmptyProjectsState(onAccountSwitcherClick, onAddProjectClick)
(viewModel.isRefreshing || viewModel.isLoadingBills) && viewModel.bills.isEmpty() ->
LoadingBillsState()
viewModel.showNoMembers -> EmptyMembersState()
// A large project takes a long time on its first sync. Until that finishes there
// is nothing stored for it yet, and reporting that as "no bills" tells the user
// the project is empty when it is still downloading.
viewModel.isRefreshing && viewModel.bills.isEmpty() -> LoadingBillsState()
viewModel.showNoBills -> EmptyBillsState()
viewModel.bills.isEmpty() -> EmptyState()
else -> {
@@ -270,6 +270,7 @@ class BillsListViewActivity :
8 -> {
startActivity(net.helcel.cowspent.android.label.LabelManagementActivity.createIntent(this@BillsListViewActivity, pid))
}
9 -> onRemoveProjectClick(pid)
}
},
onAccountSwitcherClick = {
@@ -377,6 +378,8 @@ class BillsListViewActivity :
fun onProjectClick(projectId: Long) {
if (viewModel.selectedProjectId != projectId) {
viewModel.selectedMemberId = null
viewModel.bills = emptyList()
viewModel.isLoadingBills = true
}
setSelectedProject(projectId)
navigationSelection = Category(null, null)
@@ -410,6 +413,9 @@ class BillsListViewActivity :
lifecycleScope.launch {
withContext(Dispatchers.IO) {
db.deleteProject(projectId)
// Otherwise the next account sync sees a project the account offers
// with no local row, and creates it again.
CowspentServerSyncHelper.forgetAccountProject(applicationContext, proj)
PreferenceManager.getDefaultSharedPreferences(applicationContext)
.edit { remove(lastProjectSyncKey(projectId)) }
val dbProjects = db.projects
@@ -621,76 +627,80 @@ class BillsListViewActivity :
val selectedProjectId = PreferenceManager.getDefaultSharedPreferences(applicationContext).getLong("selected_project", 0)
lifecycleScope.launch {
val (projId, projName) = withContext(Dispatchers.IO) {
if (selectedProjectId != 0L) {
db.getProject(selectedProjectId)?.let {
it.id to (if (it.name == "null" || it.name.isEmpty()) it.remoteId else it.name)
} ?: (0L to "")
} else {
0L to ""
}
}
val title = if (selectedProjectId != 0L) projName else getString(R.string.app_name)
setSelectedProject(selectedProjectId)
viewModel.title = title
val query = viewModel.searchQuery.ifEmpty { null }
val (ljItems, memberCount) = withContext(Dispatchers.IO) {
val db = CowspentSQLiteOpenHelper.getInstance(applicationContext)
val billList: List<DBBill> = if (projId != 0L) {
db.searchBills(query, projId)
} else {
ArrayList()
try {
val (projId, projName) = withContext(Dispatchers.IO) {
if (selectedProjectId != 0L) {
db.getProject(selectedProjectId)?.let {
it.id to (if (it.name == "null" || it.name.isEmpty()) it.remoteId else it.name)
} ?: (0L to "")
} else {
0L to ""
}
}
val bills = billList.filter {
val mid = viewModel.selectedMemberId
mid == null || mid == it.payerId || it.billOwersIds.contains(mid)
val title = if (selectedProjectId != 0L) projName else getString(R.string.app_name)
setSelectedProject(selectedProjectId)
viewModel.title = title
val query = viewModel.searchQuery.ifEmpty { null }
val (ljItems, memberCount) = withContext(Dispatchers.IO) {
val db = CowspentSQLiteOpenHelper.getInstance(applicationContext)
val billList: List<DBBill> = if (projId != 0L) {
db.searchBills(query, projId)
} else {
ArrayList()
}
val bills = billList.filter {
val mid = viewModel.selectedMemberId
mid == null || mid == it.payerId || it.billOwersIds.contains(mid)
}
viewModel.hasUnlabeledBills = bills.any { it.categoryId == 0L && it.state != DBBill.STATE_DELETED }
val projectMembers = db.getMembersOfProject(projId, null)
val memberMap = projectMembers.associateBy { it.id }
val projectPaymentModes = db.getPaymentModes(projId).associateBy { it.id }
val projectCategories = db.getCategories(projId).associateBy { it.id }
BillFormatter.formatBills(
bills,
memberMap,
projectCategories,
projectPaymentModes
)
val sdf = SimpleDateFormat("yyyy-MM-dd", Locale.ROOT)
val itemList = BillsListUtils.groupAndSectionBills(
bills,
memberMap,
sdf,
applicationContext
)
itemList to projectMembers.size
}
viewModel.hasUnlabeledBills = bills.any { it.categoryId == 0L && it.state != DBBill.STATE_DELETED }
viewModel.showNoProjects = false
viewModel.showNoMembers = false
viewModel.showNoBills = false
val projectMembers = db.getMembersOfProject(projId, null)
val memberMap = projectMembers.associateBy { it.id }
val projectPaymentModes = db.getPaymentModes(projId).associateBy { it.id }
val projectCategories = db.getCategories(projId).associateBy { it.id }
BillFormatter.formatBills(
bills,
memberMap,
projectCategories,
projectPaymentModes
)
val sdf = SimpleDateFormat("yyyy-MM-dd", Locale.ROOT)
val itemList = BillsListUtils.groupAndSectionBills(
bills,
memberMap,
sdf,
applicationContext
)
itemList to projectMembers.size
}
viewModel.showNoProjects = false
viewModel.showNoMembers = false
viewModel.showNoBills = false
when {
memberCount == 0 -> {
viewModel.showNoMembers = true
}
ljItems.isEmpty() -> {
viewModel.showNoBills = true
viewModel.bills = emptyList()
}
else -> {
viewModel.bills = ljItems
when {
memberCount == 0 -> {
viewModel.showNoMembers = true
}
ljItems.isEmpty() -> {
viewModel.showNoBills = true
viewModel.bills = emptyList()
}
else -> {
viewModel.bills = ljItems
}
}
} finally {
viewModel.isLoadingBills = false
}
}
}
@@ -20,6 +20,7 @@ class BillsListViewModel : ViewModel() {
var selectedMemberId by mutableStateOf<Long?>(null)
var bills by mutableStateOf<List<Item>>(emptyList())
var isRefreshing by mutableStateOf(false)
var isLoadingBills by mutableStateOf(false)
var searchQuery by mutableStateOf("")
var title by mutableStateOf("")
var accountName by mutableStateOf("")
@@ -24,6 +24,7 @@ import net.helcel.cowspent.model.ProjectType
fun ProjectOptionsDialogContent(
onEditProject: () -> Unit,
onRemoveProject: () -> Unit,
onForgetProject: () -> Unit = {},
onManageMembers: () -> Unit,
onManageCurrencies: () -> Unit,
onManageLabels: () -> Unit,
@@ -73,6 +74,15 @@ fun ProjectOptionsDialogContent(
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))
if (isArchived) {
row1.add(
ProjectOption(
stringResource(R.string.action_forget),
Icons.Default.Delete,
onForgetProject
)
)
}
} else {
row1.add(ProjectOption(stringResource(R.string.action_delete), Icons.Default.Delete, onRemoveProject))
}
+1
View File
@@ -17,6 +17,7 @@
<string name="simple_back">Back</string>
<string name="action_archive">Archive</string>
<string name="action_unarchive">Unarchive</string>
<string name="action_forget">Delete locally</string>
<string name="action_export">Export</string>
<string name="action_stats">Stats</string>
<string name="action_settle">Settle</string>