- Fixed the actual race:
Sent messages wait for polling to catch up before rendering. Missing interleaved messages cannot appear above later ones. Messages sort by server timestamp, then ID. Timestamps are assigned atomically during SQLite insertion. Client updated to 20260609.5.
This commit is contained in:
+21
-9
@@ -38,15 +38,18 @@ function sendTextMessage(): never {
|
||||
if ($message === '') jsonResponse(['error' => 'Empty message'], 400);
|
||||
if (strlen($message) > $max) jsonResponse(['error' => "Message too long (max $max chars)"], 400);
|
||||
|
||||
$created = time();
|
||||
$db = getDB();
|
||||
$db->prepare("INSERT INTO messages (user_id,username,color,message,created_at,day_key)
|
||||
VALUES (?,?,?,?,?,?)")->execute([
|
||||
$user['id'], $user['username'], $user['color'], $message, $created, dayKey(),
|
||||
VALUES (?,?,?,?,CAST(strftime('%s','now') AS INTEGER),?)")->execute([
|
||||
$user['id'], $user['username'], $user['color'], $message, dayKey(),
|
||||
]);
|
||||
$messageId = (int)$db->lastInsertId();
|
||||
$createdStmt = $db->prepare("SELECT created_at FROM messages WHERE id=?");
|
||||
$createdStmt->execute([$messageId]);
|
||||
$created = (int)$createdStmt->fetchColumn();
|
||||
trackEvent('message_text', '/api/messages.php', (int)$user['id']);
|
||||
jsonResponse(['success' => true, 'message' => messagePayload([
|
||||
'id' => $db->lastInsertId(),
|
||||
'id' => $messageId,
|
||||
'user_id' => $user['id'],
|
||||
'username' => $user['username'],
|
||||
'color' => $user['color'],
|
||||
@@ -99,25 +102,28 @@ function sendVoiceMessage(): never {
|
||||
jsonResponse(['error' => 'Could not store voice clip'], 500);
|
||||
}
|
||||
|
||||
$created = time();
|
||||
$storedMime = $mime === 'video/webm' ? 'audio/webm' : $mime;
|
||||
$db = getDB();
|
||||
try {
|
||||
$db->prepare("INSERT INTO messages
|
||||
(user_id,username,color,message,message_type,voice_file,voice_mime,voice_duration,created_at,day_key)
|
||||
VALUES (?,?,?,?,'voice',?,?,?,?,?)")->execute([
|
||||
VALUES (?,?,?,?,'voice',?,?,?,CAST(strftime('%s','now') AS INTEGER),?)")->execute([
|
||||
$user['id'], $user['username'], $user['color'], '[Voice clip]',
|
||||
$filename, $storedMime, $duration, $created, dayKey(),
|
||||
$filename, $storedMime, $duration, dayKey(),
|
||||
]);
|
||||
} catch (Throwable $e) {
|
||||
deleteVoiceFile($filename);
|
||||
throw $e;
|
||||
}
|
||||
$messageId = (int)$db->lastInsertId();
|
||||
$db->prepare("DELETE FROM recording_status WHERE channel_key='public' AND user_id=?")
|
||||
->execute([$user['id']]);
|
||||
$createdStmt = $db->prepare("SELECT created_at FROM messages WHERE id=?");
|
||||
$createdStmt->execute([$messageId]);
|
||||
$created = (int)$createdStmt->fetchColumn();
|
||||
trackEvent('message_voice', '/api/messages.php', (int)$user['id']);
|
||||
jsonResponse(['success' => true, 'message' => messagePayload([
|
||||
'id' => $db->lastInsertId(),
|
||||
'id' => $messageId,
|
||||
'user_id' => $user['id'],
|
||||
'username' => $user['username'],
|
||||
'color' => $user['color'],
|
||||
@@ -159,7 +165,8 @@ function pollMessages(): never {
|
||||
$stmt = $db->prepare("SELECT * FROM messages
|
||||
WHERE day_key=? ORDER BY id DESC LIMIT ?");
|
||||
$stmt->execute([dayKey(), $limit]);
|
||||
$rows = array_reverse($stmt->fetchAll());
|
||||
$rows = $stmt->fetchAll();
|
||||
usort($rows, 'compareMessagesChronologically');
|
||||
$hasMore = false;
|
||||
} else {
|
||||
$stmt = $db->prepare("SELECT * FROM messages
|
||||
@@ -197,6 +204,11 @@ function pollMessages(): never {
|
||||
]);
|
||||
}
|
||||
|
||||
function compareMessagesChronologically(array $a, array $b): int {
|
||||
return ((int)$a['created_at'] <=> (int)$b['created_at'])
|
||||
?: ((int)$a['id'] <=> (int)$b['id']);
|
||||
}
|
||||
|
||||
function archiveDays(): never {
|
||||
$user = authRequired();
|
||||
$rows = personalArchiveRows($user, '', 1000);
|
||||
|
||||
Reference in New Issue
Block a user