137 lines
4.0 KiB
TypeScript
137 lines
4.0 KiB
TypeScript
'use client';
|
||
|
||
import { useState, useCallback } from 'react';
|
||
import { useSessionLive, type LiveEvent } from '@/hooks/useSessionLive';
|
||
import { LiveIndicator } from './LiveIndicator';
|
||
import { ShareModal } from './ShareModal';
|
||
import { Button } from '@/components/ui/Button';
|
||
import type { ShareRole } from '@prisma/client';
|
||
|
||
interface ShareUser {
|
||
id: string;
|
||
name: string | null;
|
||
email: string;
|
||
}
|
||
|
||
interface Share {
|
||
id: string;
|
||
role: ShareRole;
|
||
user: ShareUser;
|
||
createdAt: Date;
|
||
}
|
||
|
||
interface SessionLiveWrapperProps {
|
||
sessionId: string;
|
||
sessionTitle: string;
|
||
shares: Share[];
|
||
isOwner: boolean;
|
||
canEdit: boolean;
|
||
children: React.ReactNode;
|
||
}
|
||
|
||
export function SessionLiveWrapper({
|
||
sessionId,
|
||
sessionTitle,
|
||
shares,
|
||
isOwner,
|
||
canEdit,
|
||
children,
|
||
}: SessionLiveWrapperProps) {
|
||
const [shareModalOpen, setShareModalOpen] = useState(false);
|
||
const [lastEventUser, setLastEventUser] = useState<string | null>(null);
|
||
|
||
const handleEvent = useCallback((event: LiveEvent) => {
|
||
// Show who made the last change
|
||
if (event.user?.name || event.user?.email) {
|
||
setLastEventUser(event.user.name || event.user.email);
|
||
// Clear after 3 seconds
|
||
setTimeout(() => setLastEventUser(null), 3000);
|
||
}
|
||
}, []);
|
||
|
||
const { isConnected, error } = useSessionLive({
|
||
sessionId,
|
||
onEvent: handleEvent,
|
||
});
|
||
|
||
return (
|
||
<>
|
||
{/* Header toolbar */}
|
||
<div className="mb-4 flex items-center justify-between rounded-lg border border-border bg-card p-3">
|
||
<div className="flex items-center gap-4">
|
||
<LiveIndicator isConnected={isConnected} error={error} />
|
||
|
||
{lastEventUser && (
|
||
<div className="flex items-center gap-2 text-sm text-muted animate-pulse">
|
||
<span>✏️</span>
|
||
<span>{lastEventUser} édite...</span>
|
||
</div>
|
||
)}
|
||
|
||
{!canEdit && (
|
||
<div className="flex items-center gap-2 rounded-full bg-yellow/10 px-3 py-1.5 text-sm text-yellow">
|
||
<span>👁️</span>
|
||
<span>Mode lecture</span>
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
<div className="flex items-center gap-2">
|
||
{/* Collaborators avatars */}
|
||
{shares.length > 0 && (
|
||
<div className="flex -space-x-2">
|
||
{shares.slice(0, 3).map((share) => (
|
||
<div
|
||
key={share.id}
|
||
className="flex h-8 w-8 items-center justify-center rounded-full border-2 border-card bg-primary/10 text-xs font-medium text-primary"
|
||
title={share.user.name || share.user.email}
|
||
>
|
||
{share.user.name?.[0]?.toUpperCase() || share.user.email[0].toUpperCase()}
|
||
</div>
|
||
))}
|
||
{shares.length > 3 && (
|
||
<div className="flex h-8 w-8 items-center justify-center rounded-full border-2 border-card bg-muted/20 text-xs font-medium text-muted">
|
||
+{shares.length - 3}
|
||
</div>
|
||
)}
|
||
</div>
|
||
)}
|
||
|
||
<Button
|
||
variant="outline"
|
||
size="sm"
|
||
onClick={() => setShareModalOpen(true)}
|
||
>
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
viewBox="0 0 20 20"
|
||
fill="currentColor"
|
||
className="mr-2 h-4 w-4"
|
||
>
|
||
<path d="M13 4.5a2.5 2.5 0 11.702 1.737L6.97 9.604a2.518 2.518 0 010 .792l6.733 3.367a2.5 2.5 0 11-.671 1.341l-6.733-3.367a2.5 2.5 0 110-3.475l6.733-3.366A2.52 2.52 0 0113 4.5z" />
|
||
</svg>
|
||
Partager
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Content */}
|
||
<div className={!canEdit ? 'pointer-events-none opacity-90' : ''}>
|
||
{children}
|
||
</div>
|
||
|
||
{/* Share Modal */}
|
||
<ShareModal
|
||
isOpen={shareModalOpen}
|
||
onClose={() => setShareModalOpen(false)}
|
||
sessionId={sessionId}
|
||
sessionTitle={sessionTitle}
|
||
shares={shares}
|
||
isOwner={isOwner}
|
||
/>
|
||
</>
|
||
);
|
||
}
|
||
|
||
|