From e12a23befe04bff88e78ab6eaff4329ea1c7c0ec Mon Sep 17 00:00:00 2001 From: M57 <41645758+Hy4ri@users.noreply.github.com> Date: Tue, 21 Jul 2026 12:43:06 +0300 Subject: [PATCH] fix: correct bulk session delete endpoint + surface deleted count (#653) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: correct bulk session delete endpoint + surface deleted count - Fix Retrofit @POST path from 'api/sessions/delete' to 'api/sessions/bulk-delete' - Change response type from Response to Response - Add BulkDeleteResponse model (ok, deleted) matching backend contract - Surface backend 'deleted' count in toast message instead of using client-side count Backend contract (web_server.py:10750): POST /api/sessions/bulk-delete Body: { "ids": [...], "delete_all": false } Response: { "ok": true, "deleted": } Fixes #651 * fix: replace client-side session list filter with full refresh after bulk delete - Call loadSessions() + loadStats() after successful bulk delete instead of filtering sessions/searchResults locally via 'ids' — fixes state desync when backend deletes fewer sessions than requested (partial success) and is correct for delete_all=true where ids may be empty - Show 'No sessions were deleted' toast when deletedCount == 0 - Extract toastMsg to separate val to satisfy ktlint lint rules Addresses review findings: UI state desync, delete_all handling, hardcoded pluralizing, and zero-deletion UX. --------- Co-authored-by: M57 --- .../data/model/SessionListResponse.kt | 6 ++++++ .../hermescontrol/data/remote/HermesApiService.kt | 5 +++-- .../hermescontrol/ui/sessions/SessionsViewModel.kt | 14 ++++++++++---- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/m57/hermescontrol/data/model/SessionListResponse.kt b/app/src/main/java/com/m57/hermescontrol/data/model/SessionListResponse.kt index d73de9cc..24fd6f48 100644 --- a/app/src/main/java/com/m57/hermescontrol/data/model/SessionListResponse.kt +++ b/app/src/main/java/com/m57/hermescontrol/data/model/SessionListResponse.kt @@ -41,6 +41,12 @@ data class BulkDeleteRequest( val delete_all: Boolean = false, ) +@Serializable +data class BulkDeleteResponse( + val ok: Boolean = false, + val deleted: Int = 0, +) + @Serializable data class PruneRequest( val days: Int, diff --git a/app/src/main/java/com/m57/hermescontrol/data/remote/HermesApiService.kt b/app/src/main/java/com/m57/hermescontrol/data/remote/HermesApiService.kt index 2d2a0d29..94c9637e 100644 --- a/app/src/main/java/com/m57/hermescontrol/data/remote/HermesApiService.kt +++ b/app/src/main/java/com/m57/hermescontrol/data/remote/HermesApiService.kt @@ -9,6 +9,7 @@ import com.m57.hermescontrol.data.model.AgentPluginInstallBody import com.m57.hermescontrol.data.model.AnalyticsResponse import com.m57.hermescontrol.data.model.AuxiliaryModelsResponse import com.m57.hermescontrol.data.model.BulkDeleteRequest +import com.m57.hermescontrol.data.model.BulkDeleteResponse import com.m57.hermescontrol.data.model.CheckpointsResponse import com.m57.hermescontrol.data.model.CloneProfileRequest import com.m57.hermescontrol.data.model.ConfigSchemaResponse @@ -156,10 +157,10 @@ interface HermesApiService { @Body body: SessionRenameRequest, ): Response - @POST("api/sessions/delete") + @POST("api/sessions/bulk-delete") suspend fun bulkDeleteSessions( @Body body: BulkDeleteRequest, - ): Response + ): Response @DELETE("api/sessions/{id}") suspend fun deleteSession( diff --git a/app/src/main/java/com/m57/hermescontrol/ui/sessions/SessionsViewModel.kt b/app/src/main/java/com/m57/hermescontrol/ui/sessions/SessionsViewModel.kt index f85b87d8..58ee02cd 100644 --- a/app/src/main/java/com/m57/hermescontrol/ui/sessions/SessionsViewModel.kt +++ b/app/src/main/java/com/m57/hermescontrol/ui/sessions/SessionsViewModel.kt @@ -401,17 +401,23 @@ class SessionsViewModel : ViewModel(), ToastHost { } when (result) { is NetworkResult.Success -> { + val deletedCount = result.data.deleted + val toastMsg = + if (deletedCount > 0) { + "$deletedCount session(s) deleted" + } else { + "No sessions were deleted" + } _uiState.update { it.copy( isDeletingBulk = false, isSelecting = false, selectedIds = emptySet(), - sessions = it.sessions.filter { s -> s.id !in ids }, - searchResults = it.searchResults.filter { it.session_id !in ids }, - total = (it.total - ids.size).coerceAtLeast(0), - toastMessage = "${ids.size} session(s) deleted", + toastMessage = toastMsg, ) } + loadSessions() + loadStats() } is NetworkResult.Failure -> {