Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add countdown timer #109

Merged
merged 2 commits into from
Dec 3, 2024
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
12 changes: 10 additions & 2 deletions examples/conversational-ai/talk-to-santa/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { AnimatePresence, motion } from "framer-motion";
import { Phone } from "lucide-react";
import { useEffect, useState } from "react";
import Snowfall from "react-snowfall";
import { ChristmasCountdown } from "@/components/christmas-countdown";
import { Logo } from "@/components/logo/animated-logo";

export default function Home() {
const [callState, setCallState] = useState<"idle" | "calling" | "connected">(
Expand Down Expand Up @@ -85,12 +87,18 @@ export default function Home() {

return (
<div className="relative min-h-screen overflow-hidden font-[family-name:var(--font-geist-sans)]">
{/* Header */}
<div className="absolute top-0 left-0 right-0 flex justify-between p-4 z-10">
<Logo />
<ChristmasCountdown />
</div>

<main>
{/* Call Santa Button */}
<div className="flex flex-col items-center justify-center min-h-screen">
<motion.button
className={cn(
"relative flex items-center justify-center text-white rounded-full shadow-lg",
"relative flex items-center justify-center text-white rounded-full shadow-lg opacity-90",
callState === "connected" ? "w-72 h-72" : "w-64 h-16",
callState === "idle"
? "bg-red-700 hover:bg-red-600"
Expand Down Expand Up @@ -132,7 +140,7 @@ export default function Home() {
>
<Phone size={24} />
<span className="text-lg font-semibold">
Call the North Pole
Call Santa
</span>
</motion.div>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"use client";

import { Drawer, DrawerContent, DrawerHeader, DrawerTitle, DrawerDescription, DrawerFooter } from "@/components/ui/drawer";
import { AnimatePresence, motion } from "framer-motion";
import { Button } from "@/components/ui/button"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
"use client";

import { cn } from '@/lib/utils';
import localFont from 'next/font/local';
import { useEffect, useState } from 'react';

const santaFont = localFont({
src: [
{
path: "../app/fonts/SantasSleighFull.woff2",
weight: "400",
style: "normal",
},
{
path: "../app/fonts/SantasSleighFullBold.woff2",
weight: "700",
style: "normal",
},
],
variable: "--font-santa",
});

export const ChristmasCountdown = () => {
const [timeLeft, setTimeLeft] = useState({
days: 0,
hours: 0,
minutes: 0,
seconds: 0
});

useEffect(() => {
const calculateTimeLeft = () => {
const christmas = new Date(new Date().getFullYear(), 11, 25);
const now = new Date();
const difference = christmas.getTime() - now.getTime();

if (difference > 0) {
setTimeLeft({
days: Math.floor(difference / (1000 * 60 * 60 * 24)),
hours: Math.floor((difference / (1000 * 60 * 60)) % 24),
minutes: Math.floor((difference / 1000 / 60) % 60),
seconds: Math.floor((difference / 1000) % 60)
});
}
};

calculateTimeLeft();
const timer = setInterval(calculateTimeLeft, 1000);

return () => clearInterval(timer);
}, []);

return (
<div className="fixed top-6 right-6 z-50">
<div className="bg-white/10 backdrop-blur-md rounded-2xl p-4 shadow-lg border border-white/20">
<div className="flex flex-col items-center space-y-2">
<div className="text-white/90 font-medium flex items-center gap-2">
<span role="img" aria-label="santa">🎅</span>
Days Until Christmas
</div>
<div className="flex gap-3">
{[
{ label: 'Days', value: timeLeft.days },
{ label: 'Hours', value: timeLeft.hours },
{ label: 'Minutes', value: timeLeft.minutes },
{ label: 'Seconds', value: timeLeft.seconds }
].map(({ label, value }) => (
<div key={label} className={cn("flex flex-col items-center", santaFont.className)}>
<div className="bg-red-600/80 backdrop-blur-sm w-12 h-12 rounded-lg flex items-center justify-center text-white font-bold text-xl shadow-inner">
{value.toString().padStart(2, '0')}
</div>
<div className="text-white/80 text-xs mt-1">{label}</div>
</div>
))}
</div>
</div>
</div>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
"use client";

import animationData from "@/components/logo/logo-animation.json";
import type { LottieRefCurrentProps } from "lottie-react";
import { cn } from "@/lib/utils";
import { useEffect, useRef, useState } from "react";
import dynamic from "next/dynamic";

const Lottie = dynamic(() => import("lottie-react"), { ssr: false });

export const Logo = ({
className,
}: {
className?: string;
}) => {
const ref = useRef<LottieRefCurrentProps>(null);
const [isReady, setIsReady] = useState(false);

useEffect(() => {
if (isReady) {
ref.current?.goToAndStop(0);
ref.current?.setSpeed(0.4);
ref.current?.play();
}
}, [isReady]);

return (
<div
className={cn(
"flex h-[32px] w-[160px] items-center lg:h-8 [&_svg]:!h-[32px] lg:[&_svg]:!h-[32px] text-white opacity-65",
className
)}
>
<Lottie
lottieRef={ref}
animationData={animationData}
loop={false}
autoplay={false}
onDOMLoaded={() => setIsReady(true)}
/>
</div>
);
};

/** @knipignore */
export const LogoIcon = ({ className }: { className?: string }) => {
return (
<div
className={cn(
"border-shadow flex h-60 w-60 items-center justify-center rounded-[15px] bg-default text-white",
className
)}
>
<svg
fill="none"
height="20"
viewBox="0 0 12 20"
width="12"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M4 0H0V20H4V0Z" fill="currentColor" />
<path d="M12 0H8V20H12V0Z" fill="currentColor" />
</svg>

<span className="sr-only">ElevenLabs</span>
</div>
);
};
Loading
Loading