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
192 changes: 99 additions & 93 deletions components/BeamCalls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ExternalLink, FileText, ListChecks, Play, Presentation } from 'lucide-r
import { Card, CardContent, CardFooter } from '@/components/ui/primitives';
import { Button } from '@/components/ui/Button';
import Link from 'next/link';
import { BeamCall, beamCallsData } from '@/data/beam-calls';
import { BeamCall, beamCallsData, determineBeamCallStatus } from '@/data/beam-calls';

// Helper function to extract YouTube video ID from URL
function getYoutubeVideoId(url: string): string | null {
Expand Down Expand Up @@ -33,101 +33,107 @@ export function BeamCalls() {
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{beamCallsData.map((call) => (
<Card
key={call.id}
className={`bg-white border-slate-200 overflow-hidden ${call.status === 'unscheduled' ? 'filter brightness-100 opacity-25' : ''}`}
>
<div className="relative">
{call.youtubeUrl !== '#' && getYoutubeVideoId(call.youtubeUrl) ? (
<div className="w-full h-48">
<iframe
src={`https://www.youtube.com/embed/${getYoutubeVideoId(call.youtubeUrl)}`}
title={call.title}
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
className="w-full h-full"
/>
</div>
) : (
<>
<Image
src={call.thumbnail || '/placeholder.svg?height=180&width=320'}
alt={call.title}
width={320}
height={180}
className="w-full h-48 object-cover"
/>
{call.youtubeUrl !== '#' && (
<div className="absolute inset-0 bg-black bg-opacity-30 flex items-center justify-center opacity-0 hover:opacity-100 transition-opacity">
<Link href={call.youtubeUrl} target="_blank">
<Button
size="icon"
variant="secondary"
className="rounded-full bg-white bg-opacity-90 h-12 w-12"
>
<Play className="h-6 w-6 text-red-600" />
</Button>
</Link>
</div>
)}
</>
)}
<BeamCallCard key={call.id} call={call} />
))}
</div>
);
}

function BeamCallCard({ call }: { call: BeamCall }) {
const status = determineBeamCallStatus(call.date);

return (
<Card
key={call.id}
className={`bg-white border-slate-200 overflow-hidden ${status === 'unscheduled' ? 'filter brightness-100 opacity-25' : ''}`}
>
<div className="relative">
{call.youtubeUrl !== '#' && getYoutubeVideoId(call.youtubeUrl) ? (
<div className="w-full h-48">
<iframe
src={`https://www.youtube.com/embed/${getYoutubeVideoId(call.youtubeUrl)}`}
title={call.title}
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
className="w-full h-full"
/>
</div>
<CardContent className="pt-4">
{call.youtubeUrl !== '#' ? (
<Link
href={call.youtubeUrl}
target="_blank"
className="font-semibold text-lg mb-1 text-slate-900 hover:text-blue-600"
>
{call.title}
</Link>
) : (
<div className="flex items-center">
<p className="font-semibold text-lg mb-1 text-slate-900">{call.title}</p>
{call.status === 'upcoming' && (
<span className="ml-2 text-xs bg-red-500 text-white rounded-md px-2 py-1">
Soon!
</span>
)}
</div>
)}
<p className="text-xs text-slate-500 mb-2">{call.date}</p>
<p className="text-sm text-slate-600">{call.summary}</p>
{call.resources && call.resources.length > 0 && (
<div className="mt-4 space-y-2">
<p className="text-sm font-medium text-slate-700">Resources:</p>
<div className="flex flex-wrap gap-2">
{call.resources.map((resource, index) => (
<Link
key={index}
href={resource.url}
target="_blank"
className="inline-flex items-center gap-1.5 px-2.5 py-1 text-xs font-medium text-slate-700 bg-slate-100 rounded-full hover:bg-slate-200"
>
{getResourceIcon(resource.type)}
{resource.title}
</Link>
))}
</div>
) : (
<>
<Image
src={call.thumbnail || '/placeholder.svg?height=180&width=320'}
alt={call.title}
width={320}
height={180}
className="w-full h-48 object-cover"
/>
{call.youtubeUrl !== '#' && (
<div className="absolute inset-0 bg-black bg-opacity-30 flex items-center justify-center opacity-0 hover:opacity-100 transition-opacity">
<Link href={call.youtubeUrl} target="_blank">
<Button
size="icon"
variant="secondary"
className="rounded-full bg-white bg-opacity-90 h-12 w-12"
>
<Play className="h-6 w-6 text-red-600" />
</Button>
</Link>
</div>
)}
</CardContent>
<CardFooter className="pt-0">
{call.youtubeUrl !== '#' ? (
<Link
href={call.youtubeUrl}
className="text-sm font-medium text-slate-700 hover:text-slate-900 flex items-center gap-1"
target="_blank"
>
Watch on YouTube <ExternalLink className="h-3.5 w-3.5" />
</Link>
) : (
<p className="text-sm text-slate-400">Recording not yet available.</p>
</>
)}
</div>
<CardContent className="pt-4">
{call.youtubeUrl !== '#' ? (
<Link
href={call.youtubeUrl}
target="_blank"
className="font-semibold text-lg mb-1 text-slate-900 hover:text-blue-600"
>
{call.title}
</Link>
) : (
<div className="flex items-center">
<p className="font-semibold text-lg mb-1 text-slate-900">{call.title}</p>
{status === 'upcoming' && (
<span className="ml-2 text-xs bg-red-500 text-white rounded-md px-2 py-1">Soon!</span>
)}
</CardFooter>
</Card>
))}
</div>
</div>
)}
<p className="text-xs text-slate-500 mb-2">{call.date}</p>
<p className="text-sm text-slate-600">{call.summary}</p>
{call.resources && call.resources.length > 0 && (
<div className="mt-4 space-y-2">
<p className="text-sm font-medium text-slate-700">Resources:</p>
<div className="flex flex-wrap gap-2">
{call.resources.map((resource, index) => (
<Link
key={index}
href={resource.url}
target="_blank"
className="inline-flex items-center gap-1.5 px-2.5 py-1 text-xs font-medium text-slate-700 bg-slate-100 rounded-full hover:bg-slate-200"
>
{getResourceIcon(resource.type)}
{resource.title}
</Link>
))}
</div>
</div>
)}
</CardContent>
<CardFooter className="pt-0">
{call.youtubeUrl !== '#' ? (
<Link
href={call.youtubeUrl}
className="text-sm font-medium text-slate-700 hover:text-slate-900 flex items-center gap-1"
target="_blank"
>
Watch on YouTube <ExternalLink className="h-3.5 w-3.5" />
</Link>
) : (
<p className="text-sm text-slate-400">Recording not yet available.</p>
)}
</CardFooter>
</Card>
);
}
31 changes: 16 additions & 15 deletions data/beam-calls.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
export type BeamCallStatus = 'completed' | 'upcoming' | 'scheduled' | 'unscheduled';

export interface BeamCall {
id: string;
title: string;
status: 'completed' | 'upcoming' | 'unscheduled';
date: string;
summary: string;
thumbnail: string;
thumbnail: string | null;
youtubeUrl: string;
resources?: {
title: string;
Expand All @@ -13,11 +14,23 @@ export interface BeamCall {
}[];
}

export function determineBeamCallStatus(date: string): BeamCallStatus {
if (date === 'TBD') return 'unscheduled';

const callDate = new Date(date);
const today = new Date();
const oneMonthFromNow = new Date();
oneMonthFromNow.setMonth(today.getMonth() + 1);

if (callDate < today) return 'completed';
if (callDate <= oneMonthFromNow) return 'upcoming';
return 'scheduled';
}

export const beamCallsData: BeamCall[] = [
{
id: 'call-1',
title: 'Beam Call #1: Social Layer Updates',
status: 'completed',
date: 'February 14, 2025',
summary:
'The kickoff call covering social layer progress, funding structures, legal updates, introductions of research specialists and client teams, and coordinator presentations',
Expand All @@ -39,7 +52,6 @@ export const beamCallsData: BeamCall[] = [
{
id: 'call-2',
title: 'Beam Call #2: Post-Quantum Security',
status: 'completed',
date: 'February 28, 2025',
summary:
'Technical updates on post-quantum cryptography solutions, featuring technical presentations from researchers on signature schemes, hash functions, minimal zkVMs, and formal verification.',
Expand Down Expand Up @@ -81,7 +93,6 @@ export const beamCallsData: BeamCall[] = [
{
id: 'call-3',
title: 'Beam Call #3: P2P Networking',
status: 'completed',
date: 'April 4, 2025',
summary: 'Discussion on the latest developments in peer-to-peer networking.',
thumbnail: null,
Expand Down Expand Up @@ -142,7 +153,6 @@ export const beamCallsData: BeamCall[] = [
{
id: 'call-4',
title: 'Beam Call #4: Exit Queue',
status: 'completed',
date: 'April 18, 2025',
summary:
'Discussion of Ethereum validator exit mechanisms and proposals to improve exit queue flexibility while maintaining protocol security.',
Expand All @@ -169,7 +179,6 @@ export const beamCallsData: BeamCall[] = [
{
id: 'call-5',
title: 'Beam Call #5: APS (Attester-Proposer Separation)',
status: 'completed',
date: 'May 2, 2025',
summary:
'Exploration of the Attester-Proposer Separation (APS) concept, its benefits, and potential applications for the Beam Chain.',
Expand All @@ -191,7 +200,6 @@ export const beamCallsData: BeamCall[] = [
{
id: 'call-6',
title: 'Beam Call #6: 3SF (Finality)',
status: 'upcoming',
date: 'May 16, 2025',
summary:
'Discussion on the finality of the 3SF protocol, including its implementation and implications on the network.',
Expand All @@ -213,7 +221,6 @@ export const beamCallsData: BeamCall[] = [
{
id: 'call-7',
title: 'Beam Call #7: Rainbow Staking',
status: 'upcoming',
date: 'May 30, 2025',
summary:
'Introduction to Rainbow Staking, a novel staking mechanism designed to enhance network security and decentralization.',
Expand All @@ -223,7 +230,6 @@ export const beamCallsData: BeamCall[] = [
{
id: 'call-8',
title: 'Beam Call #8: PQ Sub-Spec',
status: 'unscheduled',
date: 'TBD',
summary:
'In-depth discussion on the Post-Quantum (PQ) sub-specification, focusing on its cryptographic aspects and integration into the Beam Chain.',
Expand All @@ -233,7 +239,6 @@ export const beamCallsData: BeamCall[] = [
{
id: 'call-9',
title: 'Beam Call #9: P2P Sub-Spec',
status: 'unscheduled',
date: 'TBD',
summary:
'Examination of the Peer-to-Peer (P2P) sub-specification, covering its role in facilitating decentralized communication within the Beam Chain.',
Expand All @@ -243,7 +248,6 @@ export const beamCallsData: BeamCall[] = [
{
id: 'call-10',
title: 'Beam Call #10: APS Sub-Spec',
status: 'unscheduled',
date: 'TBD',
summary:
'Exploring the Attester-Proposer Separation (APS) sub-specification, its benefits, and potential applications in the Beam Chain.',
Expand All @@ -253,7 +257,6 @@ export const beamCallsData: BeamCall[] = [
{
id: 'call-11',
title: 'Beam Call #11: 3SF Sub-Spec',
status: 'unscheduled',
date: 'TBD',
summary:
"Delving into the 3SF sub-specification, including its technical details and implications for the network's finality.",
Expand All @@ -263,7 +266,6 @@ export const beamCallsData: BeamCall[] = [
{
id: 'call-12',
title: 'Beam Call #12: Beam Spec (Part 1)',
status: 'unscheduled',
date: 'TBD',
summary:
'First part of the Beam specification discussion, covering the overall architecture and core components of the Beam Chain.',
Expand All @@ -273,7 +275,6 @@ export const beamCallsData: BeamCall[] = [
{
id: 'call-13',
title: 'Beam Call #13: Beam Spec (Part 2)',
status: 'upcoming',
date: 'December 26, 2025',
summary:
"Second part of the Beam specification discussion, focusing on the protocol's advanced features, security considerations, and future development plans.",
Expand Down