delete messages + fix layout overflow

- DELETE /api/channels/{id}/messages/{msg_id} — soft delete, own only
- Broadcasts WsEvent::Delete to subscribers
- UI: "Del" button on hover for own messages (turns red)
- Fix layout: h-full + overflow-hidden on flex containers
- ScrollArea gets min-h-0 to properly constrain in flex
- Messages no longer push compose past viewport

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-29 21:19:44 +02:00
parent d6b7429925
commit 1830b520a6
5 changed files with 92 additions and 14 deletions

View File

@@ -278,6 +278,47 @@ pub async fn post_message(
Ok((StatusCode::CREATED, Json(message)))
}
pub async fn delete_message(
State(state): State<AppState>,
Path((channel_id, msg_id)): Path<(String, String)>,
Query(user_param): Query<UserParam>,
) -> Result<impl IntoResponse> {
let user_id = resolve_user(&state.db, &user_param).await?;
// Verify message exists and belongs to this user
let row = sqlx::query_as::<_, MessageWithUserRow>(
"SELECT m.*, u.id as u_id, u.username, u.display_name, u.role, u.created_at as u_created_at \
FROM messages m JOIN users u ON m.user_id = u.id WHERE m.id = ? AND m.channel_id = ?",
)
.bind(&msg_id)
.bind(&channel_id)
.fetch_optional(&state.db)
.await?;
let row = match row {
Some(r) => r,
None => return Err(AppError::NotFound("Message not found".into())),
};
if row.user_id != user_id {
return Err(AppError::BadRequest("Can only delete your own messages".into()));
}
// Soft delete
sqlx::query("UPDATE messages SET deleted_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now') WHERE id = ?")
.bind(&msg_id)
.execute(&state.db)
.await?;
// Broadcast delete event
let tx = state.get_sender(&channel_id).await;
let _ = tx.send(WsEvent::Delete {
id: uuid::Uuid::parse_str(&msg_id).unwrap(),
});
Ok(StatusCode::NO_CONTENT)
}
// ── Joined row type for message + user ──
#[derive(Debug, sqlx::FromRow)]