mirror of
https://github.com/nesquena/hermes-webui.git
synced 2026-05-26 19:50:15 +00:00
Repaint sidebar after session archive or delete
This commit is contained in:
@@ -3,6 +3,10 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Session sidebar Archive/Delete menu actions now repaint from local sidebar state immediately after the server confirms the mutation, instead of waiting for the full `/api/sessions` refresh before the row disappears.
|
||||
|
||||
## [v0.51.134] — 2026-05-25 — Release DF (stage-batch16 — single-PR Windows path defaults)
|
||||
|
||||
### Fixed
|
||||
|
||||
+24
-3
@@ -1544,6 +1544,24 @@ function _sessionArchiveToast(response, session){
|
||||
function _sessionDeleteDescription(session){
|
||||
return session&&session.worktree_path?t('session_delete_worktree_desc'):t('session_delete_desc');
|
||||
}
|
||||
function _optimisticallyArchiveSessionInList(sid, archived){
|
||||
if(!sid||!Array.isArray(_allSessions)) return;
|
||||
let changed=false;
|
||||
_allSessions=_allSessions.map(s=>{
|
||||
if(!s||s.session_id!==sid) return s;
|
||||
changed=true;
|
||||
return {...s,archived:!!archived};
|
||||
});
|
||||
if(changed) renderSessionListFromCache();
|
||||
}
|
||||
function _optimisticallyRemoveSessionFromList(sid){
|
||||
if(!sid||!Array.isArray(_allSessions)) return;
|
||||
const before=_allSessions.length;
|
||||
_allSessions=_allSessions.filter(s=>!s||s.session_id!==sid);
|
||||
if(_selectedSessions&&_selectedSessions.has(sid)) _selectedSessions.delete(sid);
|
||||
if(typeof _dropStaleOptimisticSessionRow==='function') _dropStaleOptimisticSessionRow(sid);
|
||||
if(_allSessions.length!==before) renderSessionListFromCache();
|
||||
}
|
||||
|
||||
function _sessionIdFromLocation(){
|
||||
if(typeof window==='undefined'||!window.location) return null;
|
||||
@@ -1866,9 +1884,10 @@ function _openSessionActionMenu(session, anchorEl){
|
||||
closeSessionActionMenu();
|
||||
try{
|
||||
const response=await api('/api/session/archive',{method:'POST',body:JSON.stringify({session_id:session.session_id,archived:!session.archived})});
|
||||
_optimisticallyArchiveSessionInList(session.session_id,!session.archived);
|
||||
session.archived=!session.archived;
|
||||
if(S.session&&S.session.session_id===session.session_id) S.session.archived=session.archived;
|
||||
await renderSessionList();
|
||||
void renderSessionList();
|
||||
showToast(session.archived?_sessionArchiveToast(response,session):t('session_restored'));
|
||||
}catch(err){showToast(t('session_archive_failed')+err.message);}
|
||||
}
|
||||
@@ -1882,9 +1901,10 @@ function _openSessionActionMenu(session, anchorEl){
|
||||
closeSessionActionMenu();
|
||||
try{
|
||||
await api('/api/session/archive',{method:'POST',body:JSON.stringify({session_id:session.session_id,archived:true})});
|
||||
_optimisticallyArchiveSessionInList(session.session_id,true);
|
||||
session.archived=true;
|
||||
if(S.session&&S.session.session_id===session.session_id) S.session.archived=true;
|
||||
await renderSessionList();
|
||||
void renderSessionList();
|
||||
showToast(t('session_hidden'));
|
||||
}catch(err){showToast(t('session_archive_failed')+err.message);}
|
||||
}
|
||||
@@ -3874,6 +3894,7 @@ async function deleteSession(sid){
|
||||
let response=null;
|
||||
try{
|
||||
response=await api('/api/session/delete',{method:'POST',body:JSON.stringify({session_id:sid})});
|
||||
_optimisticallyRemoveSessionFromList(sid);
|
||||
_clearHandoffStorageForSession(sid);
|
||||
}catch(e){setStatus(`Delete failed: ${e.message}`);return;}
|
||||
if(S.session&&S.session.session_id===sid){
|
||||
@@ -3894,7 +3915,7 @@ async function deleteSession(sid){
|
||||
}
|
||||
}
|
||||
showToast(_sessionResponseRetainsWorktree(response,session)?t('session_deleted_worktree'):t('session_deleted'));
|
||||
await renderSessionList();
|
||||
void renderSessionList();
|
||||
}
|
||||
|
||||
// ── Project helpers ─────────────────────────────────────────────────────
|
||||
|
||||
@@ -29,3 +29,29 @@ def test_session_list_refresh_does_not_close_open_conversation_actions():
|
||||
assert "if(_renamingSid) return;" in body
|
||||
assert "if(_sessionActionMenu) return;" in body
|
||||
assert body.index("if(_sessionActionMenu) return;") < body.index("closeSessionActionMenu();")
|
||||
|
||||
|
||||
def test_archive_action_repaints_sidebar_before_full_refresh():
|
||||
"""Archive should hide the row from cached sidebar state before /api/sessions returns."""
|
||||
body = _function_block(SESSIONS_JS, "_openSessionActionMenu")
|
||||
|
||||
api_call = "const response=await api('/api/session/archive'"
|
||||
optimistic = "_optimisticallyArchiveSessionInList(session.session_id,!session.archived);"
|
||||
full_refresh = "void renderSessionList();"
|
||||
|
||||
assert optimistic in body
|
||||
assert body.index(api_call) < body.index(optimistic) < body.index(full_refresh)
|
||||
|
||||
|
||||
def test_delete_action_repaints_sidebar_before_loading_remaining_sessions():
|
||||
"""Delete should remove the row locally before loading replacement session data."""
|
||||
body = _function_block(SESSIONS_JS, "deleteSession")
|
||||
|
||||
api_call = "response=await api('/api/session/delete'"
|
||||
optimistic = "_optimisticallyRemoveSessionFromList(sid);"
|
||||
remaining_fetch = "const remaining=await api('/api/sessions');"
|
||||
full_refresh = "void renderSessionList();"
|
||||
|
||||
assert optimistic in body
|
||||
assert body.index(api_call) < body.index(optimistic) < body.index(full_refresh)
|
||||
assert body.index(optimistic) < body.index(remaining_fetch)
|
||||
|
||||
Reference in New Issue
Block a user