Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
249 changes: 0 additions & 249 deletions apps/web/src/hooks/useRealtimeSubscription.ts

This file was deleted.

123 changes: 48 additions & 75 deletions apps/web/src/pages/SharedShowModePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@
import { getSharedResource, SharedRunOfShowData } from "../lib/shareUtils";
import Header from "../components/Header";
import Footer from "../components/Footer";
import { Loader, Clock, AlertTriangle, WifiOff, RefreshCw } from "lucide-react";
import { Loader, Clock, AlertTriangle } from "lucide-react";
import { RunOfShowItem, CustomColumnDefinition } from "./RunOfShowEditor"; // Assuming these types are exported
import { useRealtimeSubscription } from "../hooks/useRealtimeSubscription";

// Utility functions (can be moved to a shared util file later)
const parseDurationToSeconds = (durationStr?: string): number | null => {
Expand Down Expand Up @@ -77,7 +76,7 @@
throw new Error("This share link is not for a Run of Show.");
}
setSharedData(resource as SharedRunOfShowData);
} catch (err: any) {

Check failure on line 79 in apps/web/src/pages/SharedShowModePage.tsx

View workflow job for this annotation

GitHub Actions / typescript-checks

Unexpected any. Specify a different type
console.error("Error fetching shared Run of Show:", err);
setError(err.message || "Failed to load shared Run of Show.");
} finally {
Expand All @@ -87,30 +86,47 @@
fetchSharedData();
}, [shareCode]);

// Real-time subscription with automatic reconnection
const {
status: realtimeStatus,
retryCount,
reconnect,
} = useRealtimeSubscription({
table: "run_of_shows",
event: "UPDATE",
filter: sharedData?.id ? `id=eq.${sharedData.id}` : undefined,
enabled: !!sharedData?.id,
onUpdate: (payload) => {
console.log("Real-time update received:", payload);
const updatedRunOfShow = payload.new as SharedRunOfShowData;
setSharedData((prevData) => {
if (!prevData) return null;
return {
...prevData,
live_show_data: updatedRunOfShow.live_show_data,
items: updatedRunOfShow.items || prevData.items,
last_edited: updatedRunOfShow.last_edited || prevData.last_edited,
};
useEffect(() => {
if (!sharedData || !sharedData.id) return;

const channel = supabase
.channel(`public:run_of_shows:id=eq.${sharedData.id}`)
.on(
"postgres_changes",
{
event: "UPDATE",
schema: "public",
table: "run_of_shows",
filter: `id=eq.${sharedData.id}`,
},
(payload) => {
console.log("Real-time update received:", payload);
const updatedRunOfShow = payload.new as SharedRunOfShowData;
setSharedData((prevData) => {
if (!prevData) return null;
return {
...prevData,
live_show_data: updatedRunOfShow.live_show_data,
items: updatedRunOfShow.items || prevData.items,
last_edited: updatedRunOfShow.last_edited || prevData.last_edited,
};
});
},
)
.subscribe((status, err) => {
if (status === "SUBSCRIBED") {
console.log("Subscribed to real-time updates for Run of Show ID:", sharedData.id);
}
if (status === "CHANNEL_ERROR" || status === "TIMED_OUT") {
console.error("Real-time subscription error:", status, err);
setError("Connection lost. Please refresh to see live updates.");
}
});
},
});

return () => {
supabase.removeChannel(channel);
};
}, [sharedData]);

// Auto-scroll to current item
useEffect(() => {
Expand Down Expand Up @@ -258,57 +274,14 @@
</div>
</div>

<div className="my-3 flex justify-between items-center gap-x-4 text-xs text-gray-400 bg-gray-800/50 p-2 rounded-md">
<div className="flex items-center gap-x-4">
<div className="flex items-center">
<span className="h-3 w-3 rounded-full bg-green-500 mr-1.5 border border-green-300 shadow-sm"></span>
<span>Current Cue</span>
</div>
<div className="flex items-center">
<span className="h-3 w-3 rounded-full bg-orange-500 mr-1.5 border border-orange-300 shadow-sm"></span>
<span>Next Cue</span>
</div>
<div className="my-3 flex justify-start items-center gap-x-4 text-xs text-gray-400 bg-gray-800/50 p-2 rounded-md">
<div className="flex items-center">
<span className="h-3 w-3 rounded-full bg-green-500 mr-1.5 border border-green-300 shadow-sm"></span>
<span>Current Cue</span>
</div>

{/* Connection Status Indicator */}
<div className="flex items-center gap-x-2">
{realtimeStatus === "connected" && (
<div className="flex items-center text-green-400">
<span className="h-2 w-2 rounded-full bg-green-500 mr-1.5 animate-pulse"></span>
<span className="text-xs">Live</span>
</div>
)}
{realtimeStatus === "connecting" && (
<div className="flex items-center text-blue-400">
<Loader className="h-3 w-3 mr-1.5 animate-spin" />
<span className="text-xs">Connecting...</span>
</div>
)}
{realtimeStatus === "reconnecting" && (
<div className="flex items-center text-yellow-400">
<RefreshCw className="h-3 w-3 mr-1.5 animate-spin" />
<span className="text-xs">Reconnecting ({retryCount})...</span>
</div>
)}
{realtimeStatus === "disconnected" && (
<div className="flex items-center text-gray-500">
<WifiOff className="h-3 w-3 mr-1.5" />
<span className="text-xs">Offline</span>
</div>
)}
{realtimeStatus === "error" && (
<div className="flex items-center text-red-400">
<AlertTriangle className="h-3 w-3 mr-1.5" />
<span className="text-xs">Connection failed</span>
<button
onClick={reconnect}
className="ml-2 px-2 py-0.5 bg-red-600 hover:bg-red-700 text-white text-xs rounded transition-colors"
title="Retry connection"
>
Retry
</button>
</div>
)}
<div className="flex items-center">
<span className="h-3 w-3 rounded-full bg-orange-500 mr-1.5 border border-orange-300 shadow-sm"></span>
<span>Next Cue</span>
</div>
</div>

Expand Down
Loading