Fix potential race condition and update lists faster

This commit is contained in:
Luna712 2024-07-17 16:01:51 -06:00 committed by GitHub
parent b42337c605
commit ca469093f8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 40 additions and 26 deletions

View file

@ -133,9 +133,7 @@ class DownloadChildFragment : Fragment() {
{ click -> { click ->
if (click.action == DOWNLOAD_ACTION_DELETE_FILE) { if (click.action == DOWNLOAD_ACTION_DELETE_FILE) {
context?.let { ctx -> context?.let { ctx ->
downloadsViewModel.handleSingleDelete(ctx, click.data.id) { downloadsViewModel.handleSingleDelete(ctx, click.data.id)
downloadsViewModel.updateChildList(ctx, folder)
}
} }
} else handleDownloadClick(click) } else handleDownloadClick(click)
}, },
@ -170,10 +168,7 @@ class DownloadChildFragment : Fragment() {
binding?.btnDelete?.setOnClickListener { binding?.btnDelete?.setOnClickListener {
context?.let { ctx -> context?.let { ctx ->
downloadsViewModel.handleMultiDelete(ctx) { downloadsViewModel.handleMultiDelete(ctx)
arguments?.getString("folder")
?.let { folder -> downloadsViewModel.updateChildList(ctx, folder) }
}
} }
} }

View file

@ -180,9 +180,7 @@ class DownloadFragment : Fragment() {
{ click -> { click ->
if (click.action == DOWNLOAD_ACTION_DELETE_FILE) { if (click.action == DOWNLOAD_ACTION_DELETE_FILE) {
context?.let { ctx -> context?.let { ctx ->
downloadsViewModel.handleSingleDelete(ctx, click.data.id) { downloadsViewModel.handleSingleDelete(ctx, click.data.id)
downloadsViewModel.updateList(ctx)
}
} }
} else handleDownloadClick(click) } else handleDownloadClick(click)
}, },
@ -253,9 +251,7 @@ class DownloadFragment : Fragment() {
binding?.btnDelete?.setOnClickListener { binding?.btnDelete?.setOnClickListener {
context?.let { ctx -> context?.let { ctx ->
downloadsViewModel.handleMultiDelete(ctx) { downloadsViewModel.handleMultiDelete(ctx)
downloadsViewModel.updateList(ctx)
}
} }
} }

View file

@ -251,6 +251,17 @@ class DownloadViewModel : ViewModel() {
} }
} }
private fun removeItems(idsToRemove: List<Int>) = viewModelScope.launchSafe {
val currentHeaders = headerCards.value ?: emptyList()
val currentChildren = childCards.value ?: emptyList()
val updatedHeaders = currentHeaders.filter { !idsToRemove.contains(it.data.id) }
val updatedChildren = currentChildren.filter { !idsToRemove.contains(it.data.id) }
_headerCards.postValue(updatedHeaders)
_childCards.postValue(updatedChildren)
}
private fun updateStorageStats(visual: List<VisualDownloadCached.Header>) { private fun updateStorageStats(visual: List<VisualDownloadCached.Header>) {
try { try {
val stat = StatFs(Environment.getExternalStorageDirectory().path) val stat = StatFs(Environment.getExternalStorageDirectory().path)
@ -267,25 +278,21 @@ class DownloadViewModel : ViewModel() {
} }
} }
fun handleMultiDelete( fun handleMultiDelete(context: Context) = viewModelScope.launchSafe {
context: Context,
onDeleteConfirm: () -> Unit
) = viewModelScope.launchSafe {
val selectedItemsList = getSelectedItemsData() ?: emptyList() val selectedItemsList = getSelectedItemsData() ?: emptyList()
val deleteData = processSelectedItems(context, selectedItemsList) val deleteData = processSelectedItems(context, selectedItemsList)
val message = buildDeleteMessage(context, deleteData) val message = buildDeleteMessage(context, deleteData)
showDeleteConfirmationDialog(context, message, deleteData.ids, onDeleteConfirm) showDeleteConfirmationDialog(context, message, deleteData.ids, deleteData.parentIds)
} }
fun handleSingleDelete( fun handleSingleDelete(
context: Context, context: Context,
itemId: Int, itemId: Int
onDeleteConfirm: () -> Unit
) = viewModelScope.launchSafe { ) = viewModelScope.launchSafe {
val itemData = getItemDataFromId(itemId) val itemData = getItemDataFromId(itemId)
val deleteData = processSelectedItems(context, itemData) val deleteData = processSelectedItems(context, itemData)
val message = buildDeleteMessage(context, deleteData) val message = buildDeleteMessage(context, deleteData)
showDeleteConfirmationDialog(context, message, deleteData.ids, onDeleteConfirm) showDeleteConfirmationDialog(context, message, deleteData.ids, deleteData.parentIds)
} }
private fun getSelectedItemsData(): List<VisualDownloadCached>? { private fun getSelectedItemsData(): List<VisualDownloadCached>? {
@ -312,6 +319,7 @@ class DownloadViewModel : ViewModel() {
selectedItemsList: List<VisualDownloadCached> selectedItemsList: List<VisualDownloadCached>
): DeleteData { ): DeleteData {
val ids = mutableListOf<Int>() val ids = mutableListOf<Int>()
val parentIds = mutableListOf<Int>()
val seriesNames = mutableListOf<String>() val seriesNames = mutableListOf<String>()
val names = mutableListOf<String>() val names = mutableListOf<String>()
var parentName: String? = null var parentName: String? = null
@ -329,6 +337,7 @@ class DownloadViewModel : ViewModel() {
.filter { it.parentId == item.data.id } .filter { it.parentId == item.data.id }
.map { it.id } .map { it.id }
ids.addAll(episodes) ids.addAll(episodes)
parentIds.add(item.data.id)
val episodeInfo = "${item.data.name} (${item.totalDownloads} ${ val episodeInfo = "${item.data.name} (${item.totalDownloads} ${
context.resources.getQuantityString( context.resources.getQuantityString(
@ -361,7 +370,7 @@ class DownloadViewModel : ViewModel() {
} }
} }
return DeleteData(ids, seriesNames, names, parentName) return DeleteData(ids, parentIds, seriesNames, names, parentName)
} }
private fun buildDeleteMessage( private fun buildDeleteMessage(
@ -402,7 +411,7 @@ class DownloadViewModel : ViewModel() {
context: Context, context: Context,
message: String, message: String,
ids: List<Int>, ids: List<Int>,
onDeleteConfirm: () -> Unit parentIds: List<Int>
) { ) {
val builder = AlertDialog.Builder(context) val builder = AlertDialog.Builder(context)
val dialogClickListener = val dialogClickListener =
@ -410,9 +419,13 @@ class DownloadViewModel : ViewModel() {
when (which) { when (which) {
DialogInterface.BUTTON_POSITIVE -> { DialogInterface.BUTTON_POSITIVE -> {
viewModelScope.launchSafe { viewModelScope.launchSafe {
deleteFilesAndUpdateSettings(context, ids, this)
setIsMultiDeleteState(false) setIsMultiDeleteState(false)
onDeleteConfirm.invoke() deleteFilesAndUpdateSettings(context, ids, this) { successfulIds ->
// We always remove parent because if we are deleting from here
// and we have it as non-empty, it was triggered on
// parent header card
removeItems(successfulIds + parentIds)
}
} }
} }
@ -438,6 +451,7 @@ class DownloadViewModel : ViewModel() {
private data class DeleteData( private data class DeleteData(
val ids: List<Int>, val ids: List<Int>,
val parentIds: List<Int>,
val seriesNames: List<String>, val seriesNames: List<String>,
val names: List<String>, val names: List<String>,
val parentName: String? val parentName: String?

View file

@ -1710,7 +1710,12 @@ object VideoDownloadManager {
} }
} }
fun deleteFilesAndUpdateSettings(context: Context, ids: List<Int>, scope: CoroutineScope) { fun deleteFilesAndUpdateSettings(
context: Context,
ids: List<Int>,
scope: CoroutineScope,
onComplete: (List<Int>) -> Unit = {}
) {
scope.launchSafe(Dispatchers.IO) { scope.launchSafe(Dispatchers.IO) {
val deleteJobs = ids.map { id -> val deleteJobs = ids.map { id ->
async { async {
@ -1719,7 +1724,9 @@ object VideoDownloadManager {
} }
val results = deleteJobs.awaitAll() val results = deleteJobs.awaitAll()
val successfulDeletes = results.filter { it.second }.map { it.first }
val failedDeletes = results.filterNot { it.second } val failedDeletes = results.filterNot { it.second }
if (failedDeletes.isNotEmpty()) { if (failedDeletes.isNotEmpty()) {
failedDeletes.forEach { (id, _) -> failedDeletes.forEach { (id, _) ->
// TODO show a toast if some failed? // TODO show a toast if some failed?
@ -1728,6 +1735,8 @@ object VideoDownloadManager {
} else { } else {
Log.i("FileDeletion", "All files deleted successfully") Log.i("FileDeletion", "All files deleted successfully")
} }
onComplete.invoke(successfulDeletes)
} }
} }