sync-event toasts, image pull ordering, stale banner field diff
- replace chat-bot sync notifications with a dedicated relay_sync_events queue: POST/GET /api/sync-events, drained by /api/relay/poll and surfaced as stacking toasts via caldav.js. Old /api/chat/bot removed, bot=1 rows purged on bootstrap, orange bot rendering dropped from relay UI. Chat thread stays clean. - collab pull: fetch attachments BEFORE applying the body so the moment state.cards reflects the new reference the file is on disk. Fixes the "preview shows broken/missing image, editor shows it" race where the browser cached the 404 between attachment landing and user re-render. - detail stale banner now compares title/body/color/tags in addition to updated_at — robust against second-granularity timestamp collisions and any other case where updated_at doesn't tick but content differs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
26f95ba079
commit
1966f8f4c0
10 files changed
+170
-75
No files matched your search
@@ -588,6 +588,33 @@ function collabSyncCards(PDO $db): array {
|
||||
$fields = collabDecryptPayload($c['payload'] ?? '');
|
||||
if ($fields === null) continue;
|
||||
|
||||
// Fetch referenced attachments BEFORE applying the body so that
|
||||
// the moment state.cards reflects the new body (via _reloadAll),
|
||||
// every `uploads/xxx.png` reference is already on disk. Without
|
||||
// this, a user who opens the card preview between apply and
|
||||
// fetch would get a broken-image render — and the browser's
|
||||
// 404 response can stick around in the page's image cache even
|
||||
// after the file finally lands.
|
||||
$pulledBody = $fields['body'] ?? '';
|
||||
if ($pulledBody && preg_match_all('#uploads/([a-f0-9]+\.\w+)#', $pulledBody, $fm)) {
|
||||
$uploadDir = UPLOAD_DIR;
|
||||
if (!is_dir($uploadDir)) @mkdir($uploadDir, 0755, true);
|
||||
foreach (array_unique($fm[1]) as $fn) {
|
||||
$dest = $uploadDir . '/' . $fn;
|
||||
if (file_exists($dest)) continue;
|
||||
$attRes = collabRelayCall('/api/collab-attachments?bucket_uid=' . urlencode($c['bucket_uid']) . '&filename=' . urlencode($fn));
|
||||
if (!($attRes['ok'] ?? false)) continue;
|
||||
foreach (($attRes['attachments'] ?? []) as $a) {
|
||||
if (($a['filename'] ?? '') !== $fn) continue;
|
||||
$decoded = collabDecryptPayload($a['data'] ?? '');
|
||||
if ($decoded && isset($decoded['raw'])) {
|
||||
$bytes = base64_decode($decoded['raw']);
|
||||
if ($bytes !== false) file_put_contents($dest, $bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($local) {
|
||||
// LWW: only accept remote if it's strictly newer than local
|
||||
if (($local['updated_at'] ?? '') >= ($c['updated_at'] ?? '')) continue;
|
||||
@@ -675,38 +702,17 @@ function collabSyncCards(PDO $db): array {
|
||||
$stats['pulled']++;
|
||||
if ($foreignAuthor) $unreadBuckets[$c['bucket_uid']] = true;
|
||||
|
||||
// Notify the card's author that we synced it (best-effort; bot
|
||||
// chat message, rendered in orange on the author's side). Only
|
||||
// fires for cards authored by someone else.
|
||||
// Notify the card's author that we synced it (best-effort sync
|
||||
// event → toast on the author's side). Only fires for cards
|
||||
// authored by someone else.
|
||||
if ($foreignAuthor && !empty($c['author_username'])) {
|
||||
$cardTitle = trim((string)($fields['title'] ?? '')) ?: '(untitled)';
|
||||
collabRelayCall('/api/chat/bot', 'POST', [
|
||||
collabRelayCall('/api/sync-events', 'POST', [
|
||||
'recipient' => $c['author_username'],
|
||||
'body' => $selfUser . ' synced: ' . $cardTitle,
|
||||
]);
|
||||
}
|
||||
|
||||
// Fetch missing attachments referenced in the body.
|
||||
$pulledBody = $fields['body'] ?? '';
|
||||
if ($pulledBody && preg_match_all('#uploads/([a-f0-9]+\.\w+)#', $pulledBody, $fm)) {
|
||||
$uploadDir = UPLOAD_DIR;
|
||||
if (!is_dir($uploadDir)) @mkdir($uploadDir, 0755, true);
|
||||
foreach (array_unique($fm[1]) as $fn) {
|
||||
$dest = $uploadDir . '/' . $fn;
|
||||
if (file_exists($dest)) continue; // already have it
|
||||
$attRes = collabRelayCall('/api/collab-attachments?bucket_uid=' . urlencode($c['bucket_uid']) . '&filename=' . urlencode($fn));
|
||||
if (!($attRes['ok'] ?? false)) continue;
|
||||
$atts = $attRes['attachments'] ?? [];
|
||||
foreach ($atts as $a) {
|
||||
if (($a['filename'] ?? '') !== $fn) continue;
|
||||
$decoded = collabDecryptPayload($a['data'] ?? '');
|
||||
if ($decoded && isset($decoded['raw'])) {
|
||||
$bytes = base64_decode($decoded['raw']);
|
||||
if ($bytes !== false) file_put_contents($dest, $bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
// Bump `collab_buckets.updated_at` for every bucket that received
|
||||
|
||||
Reference in new issue
Block a user