* fix(watchtogether): harden realtime sync, room lifecycle, and lobby UX Remediates all findings from a deep review of the Watch Together feature. Server: - Serialize every websocket write (pong/error replies bypassed the write mutex, racing broadcasts on the same gorilla conn) - Send room_closed with a reason on terminal connect failures so clients stop reconnecting to dead rooms - Persist room state outside the service-wide mutex via a shared generation-CAS helper; drop ~450 lines of dead duplicated methods - Measure transport latency from server-side ping/pong RTT (was one-way client-clock delta, poisoned by clock skew) and clamp the lead time - Re-evaluate readiness when a waiting participant disconnects and add a 30s waiting deadline that skips stragglers (activates ignoreWait) - Guard the host-disconnect close timer against reconnect races - Clamp buffering-report anchor moves; clear stale member sessions on selection change - Janitor: evict empty live rooms and close rooms idle >24h - Snapshot gains an additive members list with profile display names Web: - Surface terminal room errors (REST 404/410/403 and WS error codes) as closedReason instead of reconnecting forever on "Connecting..." - Memoize the playback-sync hook and narrow VideoPlayer's video-listener effect deps to stop re-subscribing 13 listeners on every render - Preserve invite-link destination through login/profile guards - Lobby: terminal ended/missing-token states with CTAs, End-room confirm dialog, toast feedback via shared action helpers (dedup with player), participant list with guest Leave, mobile-visible connection status, document title, a11y labels/focus reveal, unified status dot component - Delete dead useWatchTogetherRoom hook (345 lines, zero importers) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(watchtogether): guard indexed access in join-page keyboard nav for noUncheckedIndexedAccess Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(watchtogether): reconcile CAS conflicts without regressing newer state; roll back unpersisted waiting-resume Addresses Codex review on PR #273: - persistRoomChangeLocked now undoes the failed writer's optimistic generation increment and only adopts the refreshed database row when it is at least as new as the local copy, so a stale conflict refresh can no longer overwrite a concurrent writer's newer in-memory state (and a failed write can no longer leave a phantom generation) - maybeResumeFromWaitingLocked restores the waiting state and re-arms the deadline when the resume transition fails to persist, instead of broadcasting a resume the database never recorded Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
43 lines
1.1 KiB
Go
43 lines
1.1 KiB
Go
package watchtogether
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
|
|
"github.com/Silo-Server/silo-server/internal/userstore"
|
|
)
|
|
|
|
const fallbackMemberName = "Guest"
|
|
|
|
// ProfileNameResolver resolves a profile's display name for room member lists.
|
|
type ProfileNameResolver interface {
|
|
ProfileDisplayName(ctx context.Context, userID int, profileID string) string
|
|
}
|
|
|
|
type userStoreProfileNames struct {
|
|
provider userstore.UserStoreProvider
|
|
}
|
|
|
|
// NewProfileNameResolver adapts a UserStoreProvider into a ProfileNameResolver.
|
|
func NewProfileNameResolver(provider userstore.UserStoreProvider) ProfileNameResolver {
|
|
if provider == nil {
|
|
return nil
|
|
}
|
|
return &userStoreProfileNames{provider: provider}
|
|
}
|
|
|
|
func (r *userStoreProfileNames) ProfileDisplayName(ctx context.Context, userID int, profileID string) string {
|
|
if r == nil || r.provider == nil {
|
|
return fallbackMemberName
|
|
}
|
|
store, err := r.provider.ForUser(ctx, userID)
|
|
if err != nil || store == nil {
|
|
return fallbackMemberName
|
|
}
|
|
profile, err := store.GetProfile(ctx, profileID)
|
|
if err != nil || profile == nil || strings.TrimSpace(profile.Name) == "" {
|
|
return fallbackMemberName
|
|
}
|
|
return profile.Name
|
|
}
|