Skip to content
Closed
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
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file added public/preview_image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
28 changes: 27 additions & 1 deletion src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,40 @@ import "./globals.css";
const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
metadataBase: new URL('https://www.browseping.com'),

title: "BrowsePing - Socialize Your Browsing Experience",
description:
"Transform your solitary browsing into a vibrant social experience. Connect with friends, share your digital presence, and discover what's capturing everyone's attention across the web.",
keywords: ["BrowsePing", "browser extension", "social browsing", "share browsing", "browser social", "browsing insights", "chrome extension"],
authors: [{ name: "BrowsePing" }],
icons: {
icon: "/browseping.svg",
}
},

openGraph: {
title: "BrowsePing - Socialize Your Browsing Experience",
description: "Transform your solitary browsing into a vibrant social experience. Connect with friends, share your digital presence, and discover what's capturing everyone's attention across the web.",
url: 'https://www.browseping.com',
siteName: 'BrowsePing',
images: [
{
url: '/preview_image.png',
width: 1200,
height: 630,
alt: 'BrowsePing - Open Source Browser Extension',
},
],
type: 'website',
},

// Twitter Card
twitter: {
card: 'summary_large_image',
title: "BrowsePing - Socialize Your Browsing Experience",
description: "Transform your solitary browsing into a vibrant social experience. Connect with friends, share your digital presence, and discover what's capturing everyone's attention across the web.",
images: ['/preview_image.png'],
},
};

export const viewport: Viewport = {
Expand Down
51 changes: 33 additions & 18 deletions src/components/home/FeaturesSection.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import React, { useEffect, useRef, useState } from 'react';
import React, { useEffect, useRef, useState ,useCallback} from 'react';
import Image from 'next/image';
import { FiUsers, FiBarChart, FiShield, FiMessageCircle, FiGithub, FiClock, FiBell, FiWifi, FiCode, FiChevronLeft, FiChevronRight, FiZap } from 'react-icons/fi';
import { FaTrophy } from 'react-icons/fa';
Expand Down Expand Up @@ -102,31 +102,46 @@ const FeaturesSection = () => {
benefits: ['Automatic tracking', 'Digital wellness insights', 'Time optimization']
}
];


useEffect(() => {
intervalRef.current = setInterval(() => {
setCurrentIndex((prev) => (prev + 1) % features.length);
}, 4000);
const startAutoScroll = useCallback(() => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
}

return () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
}
};
}, [features.length]);

const nextSlide = () => {
intervalRef.current = setInterval(() => {
setCurrentIndex((prev) => (prev + 1) % features.length);
}, 10000);
}, [features.length]);


useEffect(() => {
startAutoScroll();

return () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
}
};
}, [startAutoScroll]);


const nextSlide = () => {
setCurrentIndex((prev) => (prev + 1) % features.length);
startAutoScroll();
};


const prevSlide = () => {
setCurrentIndex((prev) => (prev - 1 + features.length) % features.length);
};
setCurrentIndex((prev) => (prev - 1 + features.length) % features.length);
startAutoScroll();
};

const goToSlide = (index: number) => {
setCurrentIndex(index);
};

const goToSlide = (index: number) => {
setCurrentIndex(index);
startAutoScroll();
};
return (
<section ref={sectionRef} className="py-32 bg-gradient-to-br from-gray-900 via-gray-800 to-gray-900 relative overflow-hidden">
{/* Background effects */}
Expand Down
118 changes: 58 additions & 60 deletions src/components/leaderboard/PublicLeaderboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,8 @@ const PublicLeaderboard = () => {
try {
setLoading(true);
setError(null);

const apiUrl = process.env.NEXT_PUBLIC_API_URL;
const url = `${apiUrl}/api/leaderboard/public-top`;

const response = await fetch(url, {
method: 'GET',
headers: {
Expand All @@ -44,7 +42,7 @@ const PublicLeaderboard = () => {
}

const result = await response.json();

if (result.success) {
setData(result.data);
} else {
Expand Down Expand Up @@ -167,69 +165,69 @@ const PublicLeaderboard = () => {
</div>
) : (
<>
{/* Header */}
<div className="bg-gray-800/50 px-6 py-4 border-b border-gray-700">
<div className="grid grid-cols-10 gap-4 text-gray-400 text-sm font-medium">
<div className="col-span-1">Rank</div>
<div className="col-span-5">User</div>
<div className="col-span-2">Monthly Time</div>
<div className="col-span-2">Total Time</div>
</div>
</div>

{/* Leaderboard entries */}
<div className="divide-y divide-gray-700">
{data.leaderboard.map((user) => (
<div
key={user.userId}
className="px-6 py-4 hover:bg-gray-800/30 transition-colors group"
>
<div className="grid grid-cols-10 gap-4 items-center">
{/* Rank */}
<div className="col-span-1">
<div className={`inline-flex items-center justify-center w-8 h-8 rounded-full bg-gradient-to-r ${getRankColor(user.rank)} text-white font-bold text-sm`}>
{user.rank <= 3 ? getRankIcon(user.rank) : user.rank}
</div>
</div>

{/* User */}
<div className="col-span-5">
<div className="flex items-center space-x-3">
<div className="w-10 h-10 bg-gradient-to-br from-blue-500 to-purple-600 rounded-full flex items-center justify-center">
<FiUser className="text-white" size={16} />
{/* Scrollable Leaderboard Container */}
<div className="overflow-x-auto">
<div className="min-w-[600px]">
{/* Header */}
<div className="bg-gray-800/50 px-6 py-4 border-b border-gray-700">
<div className="grid grid-cols-10 gap-4 text-gray-400 text-sm font-medium">
<div className="col-span-1">Rank</div>
<div className="col-span-5">User</div>
<div className="col-span-2">Monthly Time</div>
<div className="col-span-2">Total Time</div>
</div>
</div>
{/* Leaderboard entries */}
<div className="divide-y divide-gray-700">
{data.leaderboard.map((user) => (
<div
key={user.userId}
className="px-6 py-4 hover:bg-gray-800/30 transition-colors group"
>
<div className="grid grid-cols-10 gap-4 items-center">
{/* Rank */}
<div className="col-span-1">
<div className={`inline-flex items-center justify-center w-8 h-8 rounded-full bg-gradient-to-r ${getRankColor(user.rank)} text-white font-bold text-sm`}>
{user.rank <= 3 ? getRankIcon(user.rank) : user.rank}
</div>
</div>
<div>
<div className="text-white font-semibold group-hover:text-blue-400 transition-colors">
{user.displayName || user.username}
{/* User */}
<div className="col-span-5">
<div className="flex items-center space-x-3">
<div className="w-10 h-10 bg-gradient-to-br from-blue-500 to-purple-600 rounded-full flex items-center justify-center">
<FiUser className="text-white" size={16} />
</div>
<div>
<div className="text-white font-semibold group-hover:text-blue-400 transition-colors">
{user.displayName || user.username}
</div>
{user.displayName && (
<div className="text-gray-400 text-sm">@{user.username}</div>
)}
</div>
</div>
{user.displayName && (
<div className="text-gray-400 text-sm">@{user.username}</div>
)}
</div>
{/* Monthly Time */}
<div className="col-span-2">
<div className="flex items-center space-x-2">
<FiClock className="text-blue-400" size={16} />
<span className="text-white font-medium">
{formatHours(user.monthlyHours)}
</span>
</div>
</div>
{/* Total Time */}
<div className="col-span-2">
<span className="text-gray-300">
{formatHours(user.totalOnlineHours)}
</span>
</div>
</div>
</div>

{/* Monthly Time */}
<div className="col-span-2">
<div className="flex items-center space-x-2">
<FiClock className="text-blue-400" size={16} />
<span className="text-white font-medium">
{formatHours(user.monthlyHours)}
</span>
</div>
</div>

{/* Total Time */}
<div className="col-span-2">
<span className="text-gray-300">
{formatHours(user.totalOnlineHours)}
</span>
</div>
</div>
))}
</div>
))}
</div>
</div>

{/* Footer */}
{data.totalUsers > 0 && (
<div className="bg-gray-800/30 px-6 py-4 border-t border-gray-700">
Expand All @@ -246,4 +244,4 @@ const PublicLeaderboard = () => {
);
};

export default PublicLeaderboard;
export default PublicLeaderboard;