Order sessions by recent use
This commit is contained in:
@@ -1481,6 +1481,10 @@ impl App {
|
||||
});
|
||||
match loaded {
|
||||
Ok((messages, a2ui)) => {
|
||||
if let Err(error) = database.touch_session(session_id) {
|
||||
self.error = Some(format!("Could not update the session: {error}"));
|
||||
return Task::none();
|
||||
}
|
||||
self.conversation = messages.into_iter().map(ChatMessage::from).collect();
|
||||
generation::promote_legacy_turn_summaries(&mut self.conversation);
|
||||
self.context_notice = None;
|
||||
@@ -1514,6 +1518,7 @@ impl App {
|
||||
};
|
||||
self.tokens_per_second = tokens_per_second;
|
||||
self.error = restore_error;
|
||||
self.reload_projects();
|
||||
return Task::batch([scroll_chat_to_end(), self.load_next_a2ui_image()]);
|
||||
}
|
||||
Err(error) => {
|
||||
|
||||
@@ -3,6 +3,7 @@ use diesel_migrations::{EmbeddedMigrations, MigrationHarness, embed_migrations};
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
use crate::schema::{a2ui_messages, messages, projects, sessions};
|
||||
|
||||
@@ -40,6 +41,7 @@ pub struct Session {
|
||||
/// Raw column value; read it through [`Session::state`].
|
||||
state: String,
|
||||
pub compacted_summary: Option<String>,
|
||||
last_used: i64,
|
||||
}
|
||||
|
||||
impl Session {
|
||||
@@ -58,6 +60,7 @@ impl Session {
|
||||
last_tokens_per_second: None,
|
||||
state: state.as_id().to_owned(),
|
||||
compacted_summary: None,
|
||||
last_used: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -105,6 +108,7 @@ impl SessionState {
|
||||
struct NewSession<'a> {
|
||||
project_id: i32,
|
||||
title: &'a str,
|
||||
last_used: i64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Identifiable, Queryable, Selectable)]
|
||||
@@ -207,10 +211,15 @@ impl Database {
|
||||
.or_default()
|
||||
.push(session);
|
||||
}
|
||||
// Pinned first, then ordinary, then archived; creation order within each
|
||||
// group. A stable sort keeps the id ordering the query established.
|
||||
// Preserve sidebar groups while matching DS4's newest-used-first order.
|
||||
for sessions in sessions_by_project.values_mut() {
|
||||
sessions.sort_by_key(|session| session.state().rank());
|
||||
sessions.sort_by(|left, right| {
|
||||
left.state()
|
||||
.rank()
|
||||
.cmp(&right.state().rank())
|
||||
.then_with(|| right.last_used.cmp(&left.last_used))
|
||||
.then_with(|| right.id.cmp(&left.id))
|
||||
});
|
||||
}
|
||||
|
||||
Ok(project_rows
|
||||
@@ -275,12 +284,20 @@ impl Database {
|
||||
|
||||
pub fn create_session(&mut self, project_id: i32, title: &str) -> Result<Session, String> {
|
||||
diesel::insert_into(sessions::table)
|
||||
.values(NewSession { project_id, title })
|
||||
.values(NewSession {
|
||||
project_id,
|
||||
title,
|
||||
last_used: OffsetDateTime::now_utc().unix_timestamp(),
|
||||
})
|
||||
.returning(Session::as_returning())
|
||||
.get_result(&mut self.connection)
|
||||
.map_err(|error| error.to_string())
|
||||
}
|
||||
|
||||
pub fn touch_session(&mut self, session_id: i32) -> Result<(), String> {
|
||||
touch_session(&mut self.connection, session_id).map_err(|error| error.to_string())
|
||||
}
|
||||
|
||||
pub fn rename_session(&mut self, session_id: i32, title: &str) -> Result<(), String> {
|
||||
let title = title.trim();
|
||||
if title.is_empty() {
|
||||
@@ -431,6 +448,7 @@ impl Database {
|
||||
) -> Result<Vec<StoredMessage>, String> {
|
||||
self.connection
|
||||
.transaction(|connection| {
|
||||
touch_session(connection, session_id)?;
|
||||
let mut stored = Vec::with_capacity(system_messages.len() + 2);
|
||||
for content in system_messages {
|
||||
stored.push(
|
||||
@@ -497,6 +515,7 @@ impl Database {
|
||||
) -> Result<Vec<StoredMessage>, String> {
|
||||
self.connection
|
||||
.transaction(|connection| {
|
||||
touch_session(connection, session_id)?;
|
||||
let mut stored = Vec::with_capacity(system_messages.len() + 3);
|
||||
stored.push(
|
||||
diesel::insert_into(messages::table)
|
||||
@@ -686,6 +705,16 @@ impl Database {
|
||||
}
|
||||
}
|
||||
|
||||
fn touch_session(
|
||||
connection: &mut SqliteConnection,
|
||||
session_id: i32,
|
||||
) -> Result<(), diesel::result::Error> {
|
||||
diesel::update(sessions::table.find(session_id))
|
||||
.set(sessions::last_used.eq(OffsetDateTime::now_utc().unix_timestamp()))
|
||||
.execute(connection)
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -758,6 +787,46 @@ mod tests {
|
||||
fs::remove_file(path).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sessions_are_ordered_by_recent_use_within_sidebar_groups() {
|
||||
let id = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let path = std::env::temp_dir().join(format!("ds4-session-order-{id}.sqlite3"));
|
||||
let mut database = Database::open(&path).unwrap();
|
||||
let project = database
|
||||
.create_project("DS4", "/tmp/ds4-session-order")
|
||||
.unwrap();
|
||||
let older = database.create_session(project.id, "Older").unwrap();
|
||||
let newer = database.create_session(project.id, "Newer").unwrap();
|
||||
let pinned = database.create_session(project.id, "Pinned").unwrap();
|
||||
database
|
||||
.set_session_state(pinned.id, SessionState::Pinned)
|
||||
.unwrap();
|
||||
diesel::update(sessions::table.find(older.id))
|
||||
.set(sessions::last_used.eq(100_i64))
|
||||
.execute(&mut database.connection)
|
||||
.unwrap();
|
||||
diesel::update(sessions::table.find(newer.id))
|
||||
.set(sessions::last_used.eq(200_i64))
|
||||
.execute(&mut database.connection)
|
||||
.unwrap();
|
||||
|
||||
let loaded = database.load_projects().unwrap();
|
||||
assert_eq!(
|
||||
loaded[0]
|
||||
.sessions
|
||||
.iter()
|
||||
.map(|session| session.id)
|
||||
.collect::<Vec<_>>(),
|
||||
[pinned.id, newer.id, older.id]
|
||||
);
|
||||
|
||||
drop(database);
|
||||
fs::remove_file(path).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a2ui_dismissal_persists_a_fresh_surface_boundary() {
|
||||
let id = SystemTime::now()
|
||||
|
||||
@@ -47,6 +47,7 @@ diesel::table! {
|
||||
last_tokens_per_second -> Nullable<Float>,
|
||||
state -> Text,
|
||||
compacted_summary -> Nullable<Text>,
|
||||
last_used -> BigInt,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user