Temporary chats
A clock in the top-right starts one. It is never listed in the sidebar and is swept a day after the last thing said in it. A real row rather than something held in the browser, because a reload, a crash or a background tab all look identical from here -- "delete when you navigate away" would lose conversations people meant to keep. The flag rides in the URL (/chat?temporary=1) rather than in JavaScript, so it survives a reload and can be bookmarked, and the composer carries it as a hidden field beside model_id. Keep clears the flag. Without a way out, a conversation that turns out to matter is destroyed a day later with no recourse, and people would find that out exactly once. archived was filtered in three places and temporary mirrors all three, plus Folder.visible_chats. It also skips the unread flag in _persist: there is no sidebar row for the dot to land on, and the toast would name a chat nobody can navigate to. The sweep measures age from the newest message, not from the chat row. created_at would destroy a conversation still in use at hour 23, and updated_at does not move when a message is inserted -- onupdate fires on an UPDATE of the chat, and adding a message is not one. It runs at startup beside the existing upload sweep. Deleting a chat cascades its rows but leaves the files on disk; only the orphan sweep unlinks anything, and it looks only at uploads that were never attached. files.remove_files_for_chats() closes that for the new sweep. The same hole in delete_chat is pre-existing and left for its own change, which can now call the same helper. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -435,6 +435,26 @@ def claim(db: DBSession, *, ids: list[str], user_id: str, message_id: str) -> li
|
||||
return pending
|
||||
|
||||
|
||||
def remove_files_for_chats(db: DBSession, chat_ids: list[str]) -> int:
|
||||
"""Unlink the files belonging to these chats' attachments.
|
||||
|
||||
Deleting a Chat cascades to its Message and Attachment *rows* but leaves the
|
||||
files on disk -- only `sweep_orphans` unlinks anything, and it only looks at
|
||||
uploads that were never attached. Anything that deletes chats has to call
|
||||
this first, while the rows still say which files to remove.
|
||||
"""
|
||||
if not chat_ids:
|
||||
return 0
|
||||
|
||||
removed = 0
|
||||
for attachment in db.scalars(select(Attachment).where(Attachment.chat_id.in_(chat_ids))):
|
||||
path = stored_path(attachment.stored_name)
|
||||
if path is not None and path.exists():
|
||||
path.unlink(missing_ok=True)
|
||||
removed += 1
|
||||
return removed
|
||||
|
||||
|
||||
def sweep_orphans(db: DBSession, older_than: timedelta = ORPHAN_AGE) -> int:
|
||||
"""Delete uploads that were never attached to a message.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user