feat: Nachrichten als gelöscht markieren

- DB: deleted_on TEXT Spalte in messages (Migration)
- Backend: ToggleMessageDelete(), includeDeleted-Parameter in allen History-Queries
- Neuer Endpoint: PUT /api/messages/{id}/delete
- Frontend: Trash-Button zwischen Reply- und Mark-Button
- Gelöschte Nachrichten: durchgestrichen + roter Rand (oder versteckt je nach Einstellung)
- Einstellungen: Toggle-Switch 'Gelöschte Nachrichten anzeigen'
- KI-Payload: gelöschte Nachrichten werden nicht mitgeschickt
This commit is contained in:
Meleki committed 2026-03-03 11:45:52 +01:00
1 parent 493dfcda1c
commit a14a991a0f
11 files changed
+248 -39

No files matched your search

+21 -5
View File
@@ -21,6 +21,7 @@ public static class HistoryEndpoint
app.MapGet("/api/archive/{archiveId}", HandleGetArchive);
app.MapGet("/api/attachments/{messageId}", HandleGetAttachments);
app.MapPut("/api/messages/{messageId}/mark", HandleToggleMark);
app.MapPut("/api/messages/{messageId}/delete", HandleToggleDelete);
app.MapGet("/api/chains/{chainId}", HandleGetChain);
}
@@ -44,23 +45,24 @@ public static class HistoryEndpoint
const int messageLimit = 500;
var beforeStr = ctx.Request.Query["beforeId"].FirstOrDefault();
var beforeId = long.TryParse(beforeStr, out var b) ? b : 0;
var includeDeleted = ctx.Request.Query["includeDeleted"].FirstOrDefault() == "true";
var totalCount = await db.GetActiveMessageCount(sessionId);
var totalCount = await db.GetActiveMessageCount(sessionId, includeDeleted);
List<MessageRecord> messages;
if (beforeId > 0)
{
messages = await db.GetActiveMessagesBeforeId(sessionId, beforeId, messageLimit);
messages = await db.GetActiveMessagesBeforeId(sessionId, beforeId, messageLimit, includeDeleted);
}
else
{
messages = await db.GetLatestActiveMessages(sessionId, messageLimit);
messages = await db.GetLatestActiveMessages(sessionId, messageLimit, includeDeleted);
}
// Zähle wie viele Nachrichten es VOR der ältesten geladenen noch gibt
var oldestLoadedId = messages.Count > 0 ? messages[0].Id : 0;
var remainingOlder = oldestLoadedId > 0
? await db.GetActiveMessageCountBeforeId(sessionId, oldestLoadedId)
? await db.GetActiveMessageCountBeforeId(sessionId, oldestLoadedId, includeDeleted)
: 0;
// Nach Chain gruppieren (Reihenfolge beibehalten)
@@ -91,6 +93,7 @@ public static class HistoryEndpoint
var sessionId = ctx.Request.Query["sessionId"].FirstOrDefault();
var afterStr = ctx.Request.Query["afterId"].FirstOrDefault();
var afterId = long.TryParse(afterStr, out var a) ? a : 0;
var includeDeleted = ctx.Request.Query["includeDeleted"].FirstOrDefault() == "true";
if (string.IsNullOrEmpty(sessionId) || !await VerifySessionOwner(ctx, db, sessionId))
{
@@ -98,7 +101,7 @@ public static class HistoryEndpoint
return;
}
var messages = await db.GetActiveMessagesAfterId(sessionId, afterId);
var messages = await db.GetActiveMessagesAfterId(sessionId, afterId, includeDeleted: includeDeleted);
await ctx.Response.WriteAsJsonAsync(messages);
}
@@ -174,6 +177,19 @@ public static class HistoryEndpoint
await ctx.Response.WriteAsJsonAsync(new { messageId, isMarked });
}
private static async Task HandleToggleDelete(HttpContext ctx, DatabaseService db)
{
var messageIdStr = ctx.Request.RouteValues["messageId"]?.ToString();
if (!long.TryParse(messageIdStr, out var messageId))
{
ctx.Response.StatusCode = 400;
return;
}
var deletedOn = await db.ToggleMessageDelete(messageId);
await ctx.Response.WriteAsJsonAsync(new { messageId, deletedOn });
}
private static async Task HandleGetChain(HttpContext ctx, DatabaseService db)
{
var chainId = ctx.Request.RouteValues["chainId"]?.ToString();