Fix: Force pull images in deploy workflow
All checks were successful
Build and Push / build-all (push) Successful in 7m30s

This commit is contained in:
doomtube 2026-01-06 23:56:06 -05:00
parent 7f56f19e94
commit 0bb461498e
7 changed files with 104 additions and 23 deletions

View file

@ -119,8 +119,29 @@ export function clearMessages() {
}
// Set message history
export function setMessageHistory(messageList) {
messages.set(messageList);
// If merge=true, adds new messages to existing ones (for realm switching in global chat)
// If merge=false (default), replaces all messages (for initial connection)
export function setMessageHistory(messageList, merge = false) {
if (merge) {
messages.update((existing) => {
// Create a Map of existing messages by messageId for O(1) lookup
const existingMap = new Map(existing.map((m) => [m.messageId, m]));
// Add new messages that don't already exist
for (const msg of messageList) {
if (!existingMap.has(msg.messageId)) {
existingMap.set(msg.messageId, msg);
}
}
// Convert back to array and sort by timestamp
return Array.from(existingMap.values()).sort(
(a, b) => new Date(a.timestamp) - new Date(b.timestamp)
);
});
} else {
messages.set(messageList);
}
}
// Toggle channel

View file

@ -132,7 +132,11 @@ class ChatWebSocket {
break;
case 'history':
setMessageHistory(data.messages || []);
// Merge history when switching realms to preserve messages from other realms
// Backend includes realmId only for realm-switch history (from handleJoinRealm)
// Initial connection history (handleNewConnection) has no realmId
const shouldMerge = data.realmId !== undefined;
setMessageHistory(data.messages || [], shouldMerge);
break;
case 'new_message':

View file

@ -38,19 +38,39 @@
// Self-destruct countdown
let countdownSeconds = 0;
let countdownInterval = null;
let countdownStartedAt = null;
let initialCountdown = 0;
$: hasSelfDestruct = message.selfDestructAt && message.selfDestructAt > 0;
function updateCountdown() {
if (!message.selfDestructAt) return;
const now = Date.now();
const remaining = Math.max(0, Math.ceil((message.selfDestructAt - now) / 1000));
if (!countdownStartedAt) return;
// Calculate elapsed time since countdown started (local time only, avoids clock skew)
const elapsedSec = Math.floor((Date.now() - countdownStartedAt) / 1000);
const remaining = Math.max(0, initialCountdown - elapsedSec);
countdownSeconds = remaining;
if (remaining <= 0 && countdownInterval) {
clearInterval(countdownInterval);
countdownInterval = null;
}
}
function startCountdown() {
if (!message.selfDestructAt || !message.timestamp) return;
// Calculate the intended duration from server timestamps (no clock skew between them)
const intendedDurationSec = Math.ceil((message.selfDestructAt - message.timestamp) / 1000);
// Record when we started counting down locally
countdownStartedAt = Date.now();
initialCountdown = intendedDurationSec;
countdownSeconds = intendedDurationSec;
countdownInterval = setInterval(updateCountdown, 1000);
}
function formatCountdown(seconds) {
if (seconds >= 60) {
const m = Math.floor(seconds / 60);
@ -69,8 +89,7 @@
onMount(async () => {
// Set up self-destruct countdown if applicable
if (message.selfDestructAt && message.selfDestructAt > 0) {
updateCountdown();
countdownInterval = setInterval(updateCountdown, 1000);
startCountdown();
}
// Ensure stickers are loaded (uses shared store - only fetches once across all components)

View file

@ -106,6 +106,18 @@ function createWatchSyncStore() {
repeatCount: data.repeatCount || 0,
isRepeating: true
}));
} else if (data.event === 'locked_restart') {
// Locked video loop - restart from beginning
update(state => ({
...state,
playbackState: 'playing',
currentTime: 0,
serverTime: data.serverTime || Date.now(),
currentVideo: data.currentVideo !== undefined ? data.currentVideo : state.currentVideo,
leadIn: true,
repeatCount: 0,
isRepeating: true // Video is looping (locked)
}));
} else if (data.event === 'skip') {
// Skip resets repeat state
update(state => ({