From e57394dd07f85e8ccb9e735fd04e5c5ff334a2de Mon Sep 17 00:00:00 2001 From: Claudio Sciotto Date: Tue, 24 Dec 2024 16:58:04 -0500 Subject: [PATCH 1/6] Testing playwright instead of puppeteer --- package-lock.json | 42 ++++++++++++++++++++++++++++ package.json | 1 + src/app/api/instagram-count/route.ts | 27 ++++-------------- 3 files changed, 49 insertions(+), 21 deletions(-) diff --git a/package-lock.json b/package-lock.json index 39a5bc7..9977e37 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "firebase": "^10.14.1", "lucide-react": "^0.436.0", "next": "14.2.6", + "playwright": "^1.49.1", "puppeteer": "^23.11.1", "react": "^18", "react-dom": "^18", @@ -4794,6 +4795,47 @@ "node": ">= 6" } }, + "node_modules/playwright": { + "version": "1.49.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.1.tgz", + "integrity": "sha512-VYL8zLoNTBxVOrJBbDuRgDWa3i+mfQgDTrL8Ah9QXZ7ax4Dsj0MSq5bYgytRnDVVe+njoKnfsYkH3HzqVj5UZA==", + "dependencies": { + "playwright-core": "1.49.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.49.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.1.tgz", + "integrity": "sha512-BzmpVcs4kE2CH15rWfzpjzVGhWERJfmnXmniSyKeRZUs9Ws65m+RGIi7mjJK/euCegfn3i7jvqWeWyHe9y3Vgg==", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/possible-typed-array-names": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", diff --git a/package.json b/package.json index 46e29c6..0ec9757 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "firebase": "^10.14.1", "lucide-react": "^0.436.0", "next": "14.2.6", + "playwright": "^1.49.1", "puppeteer": "^23.11.1", "react": "^18", "react-dom": "^18", diff --git a/src/app/api/instagram-count/route.ts b/src/app/api/instagram-count/route.ts index 9865d5c..d687394 100644 --- a/src/app/api/instagram-count/route.ts +++ b/src/app/api/instagram-count/route.ts @@ -1,46 +1,31 @@ import { NextResponse } from "next/server"; -import puppeteer from "puppeteer"; +import { chromium } from "playwright"; export async function GET() { let browser; try { - browser = await puppeteer.launch({ + browser = await chromium.launch({ headless: true, - args: ["--no-sandbox", "--disable-setuid-sandbox"], }); - const page = await browser.newPage(); + const context = await browser.newContext(); + const page = await context.newPage(); - // Set a user agent to appear more like a regular browser - await page.setUserAgent( - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36", - ); - - // Navigate to the Instagram profile await page.goto("https://www.instagram.com/pcbuildinguf/", { - waitUntil: "networkidle0", + waitUntil: "networkidle", timeout: 30000, }); - // Wait for the followers count to be visible - await page.waitForSelector('meta[property="og:description"]', { - timeout: 5000, - }); - - // Get the meta tag content const metaContent = await page.$eval( 'meta[property="og:description"]', - (element) => element.content, + (element: HTMLMetaElement) => element.content, ); - // Extract follower count from meta content const followerMatch = metaContent.match(/([0-9,.]+)\s*Followers/i); const followerCount = followerMatch ? parseInt(followerMatch[1].replace(/,/g, "")) : null; - console.log("Fetched content:", metaContent); // For debugging - if (!followerCount) { throw new Error("Could not extract follower count"); } From 806b92734323494f5975b2c0f491fab4a3ca486a Mon Sep 17 00:00:00 2001 From: Claudio Sciotto Date: Tue, 24 Dec 2024 17:01:41 -0500 Subject: [PATCH 2/6] Revert "Testing playwright instead of puppeteer" This reverts commit e57394dd07f85e8ccb9e735fd04e5c5ff334a2de. --- package-lock.json | 42 ---------------------------- package.json | 1 - src/app/api/instagram-count/route.ts | 27 ++++++++++++++---- 3 files changed, 21 insertions(+), 49 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9977e37..39a5bc7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,6 @@ "firebase": "^10.14.1", "lucide-react": "^0.436.0", "next": "14.2.6", - "playwright": "^1.49.1", "puppeteer": "^23.11.1", "react": "^18", "react-dom": "^18", @@ -4795,47 +4794,6 @@ "node": ">= 6" } }, - "node_modules/playwright": { - "version": "1.49.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.1.tgz", - "integrity": "sha512-VYL8zLoNTBxVOrJBbDuRgDWa3i+mfQgDTrL8Ah9QXZ7ax4Dsj0MSq5bYgytRnDVVe+njoKnfsYkH3HzqVj5UZA==", - "dependencies": { - "playwright-core": "1.49.1" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "fsevents": "2.3.2" - } - }, - "node_modules/playwright-core": { - "version": "1.49.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.1.tgz", - "integrity": "sha512-BzmpVcs4kE2CH15rWfzpjzVGhWERJfmnXmniSyKeRZUs9Ws65m+RGIi7mjJK/euCegfn3i7jvqWeWyHe9y3Vgg==", - "bin": { - "playwright-core": "cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/playwright/node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/possible-typed-array-names": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", diff --git a/package.json b/package.json index 0ec9757..46e29c6 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,6 @@ "firebase": "^10.14.1", "lucide-react": "^0.436.0", "next": "14.2.6", - "playwright": "^1.49.1", "puppeteer": "^23.11.1", "react": "^18", "react-dom": "^18", diff --git a/src/app/api/instagram-count/route.ts b/src/app/api/instagram-count/route.ts index d687394..9865d5c 100644 --- a/src/app/api/instagram-count/route.ts +++ b/src/app/api/instagram-count/route.ts @@ -1,31 +1,46 @@ import { NextResponse } from "next/server"; -import { chromium } from "playwright"; +import puppeteer from "puppeteer"; export async function GET() { let browser; try { - browser = await chromium.launch({ + browser = await puppeteer.launch({ headless: true, + args: ["--no-sandbox", "--disable-setuid-sandbox"], }); - const context = await browser.newContext(); - const page = await context.newPage(); + const page = await browser.newPage(); + // Set a user agent to appear more like a regular browser + await page.setUserAgent( + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36", + ); + + // Navigate to the Instagram profile await page.goto("https://www.instagram.com/pcbuildinguf/", { - waitUntil: "networkidle", + waitUntil: "networkidle0", timeout: 30000, }); + // Wait for the followers count to be visible + await page.waitForSelector('meta[property="og:description"]', { + timeout: 5000, + }); + + // Get the meta tag content const metaContent = await page.$eval( 'meta[property="og:description"]', - (element: HTMLMetaElement) => element.content, + (element) => element.content, ); + // Extract follower count from meta content const followerMatch = metaContent.match(/([0-9,.]+)\s*Followers/i); const followerCount = followerMatch ? parseInt(followerMatch[1].replace(/,/g, "")) : null; + console.log("Fetched content:", metaContent); // For debugging + if (!followerCount) { throw new Error("Could not extract follower count"); } From 1e8c225c70ca1db2630ca2aa5355742d4c5e0ccc Mon Sep 17 00:00:00 2001 From: Claudio Sciotto Date: Tue, 24 Dec 2024 17:16:40 -0500 Subject: [PATCH 3/6] testing instagram fetch --- src/app/api/instagram-count/route.ts | 75 ++++++++++++---------------- 1 file changed, 31 insertions(+), 44 deletions(-) diff --git a/src/app/api/instagram-count/route.ts b/src/app/api/instagram-count/route.ts index 9865d5c..981ef93 100644 --- a/src/app/api/instagram-count/route.ts +++ b/src/app/api/instagram-count/route.ts @@ -1,60 +1,47 @@ import { NextResponse } from "next/server"; -import puppeteer from "puppeteer"; export async function GET() { - let browser; try { - browser = await puppeteer.launch({ - headless: true, - args: ["--no-sandbox", "--disable-setuid-sandbox"], + const response = await fetch("https://www.instagram.com/pcbuildinguf/", { + headers: { + "User-Agent": + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36", + Accept: + "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "Accept-Language": "en-US,en;q=0.5", + "Cache-Control": "no-cache", + Pragma: "no-cache", + }, }); - const page = await browser.newPage(); - - // Set a user agent to appear more like a regular browser - await page.setUserAgent( - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36", - ); + if (!response.ok) { + throw new Error("Failed to fetch Instagram page"); + } - // Navigate to the Instagram profile - await page.goto("https://www.instagram.com/pcbuildinguf/", { - waitUntil: "networkidle0", - timeout: 30000, - }); + const html = await response.text(); - // Wait for the followers count to be visible - await page.waitForSelector('meta[property="og:description"]', { - timeout: 5000, - }); + // Try to find the followers count in the JSON data that Instagram embeds + const jsonMatch = html.match(/"edge_followed_by":{"count":(\d+)}/); + if (jsonMatch && jsonMatch[1]) { + const followerCount = parseInt(jsonMatch[1]); + return NextResponse.json({ followerCount }); + } - // Get the meta tag content - const metaContent = await page.$eval( - 'meta[property="og:description"]', - (element) => element.content, + // Fallback: try to find it in meta tags + const metaMatch = html.match( + /]*?og:description[^>]*?content="[^"]*?(\d+)\s+Followers/i, ); - - // Extract follower count from meta content - const followerMatch = metaContent.match(/([0-9,.]+)\s*Followers/i); - const followerCount = followerMatch - ? parseInt(followerMatch[1].replace(/,/g, "")) - : null; - - console.log("Fetched content:", metaContent); // For debugging - - if (!followerCount) { - throw new Error("Could not extract follower count"); + if (metaMatch && metaMatch[1]) { + const followerCount = parseInt(metaMatch[1]); + return NextResponse.json({ followerCount }); } - return NextResponse.json({ followerCount }); + throw new Error("Could not find follower count"); } catch (error) { console.error("Error fetching Instagram count:", error); - return NextResponse.json( - { error: "Failed to fetch follower count" }, - { status: 500 }, - ); - } finally { - if (browser) { - await browser.close(); - } + + // Return a fallback count if scraping fails + // You can store this in an environment variable or database + return NextResponse.json({ followerCount: 909 }); } } From 93a0362d87903327e0ea7d852c8542b2823526c1 Mon Sep 17 00:00:00 2001 From: Claudio Sciotto Date: Tue, 24 Dec 2024 17:22:37 -0500 Subject: [PATCH 4/6] testing instagram v2 --- src/app/api/instagram-count/route.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/api/instagram-count/route.ts b/src/app/api/instagram-count/route.ts index 981ef93..e46d83f 100644 --- a/src/app/api/instagram-count/route.ts +++ b/src/app/api/instagram-count/route.ts @@ -42,6 +42,6 @@ export async function GET() { // Return a fallback count if scraping fails // You can store this in an environment variable or database - return NextResponse.json({ followerCount: 909 }); + return NextResponse.json({ followerCount: 908 }); } } From 2bece18a5d39ea1d7aaf28350524c5c145985453 Mon Sep 17 00:00:00 2001 From: Claudio Sciotto Date: Thu, 9 Jan 2025 14:26:46 -0500 Subject: [PATCH 5/6] Updated Animations in Landing Page + more * Added typing animation on landing page * Returned discord member count * Added coming soon to About and Projects page Updated minor ui elements --- src/app/about/page.tsx | 37 ++++++++- src/app/projects/page.tsx | 38 ++++++++- src/components/MemberCounts/SocialStats.tsx | 8 +- src/components/landing/Hero.tsx | 90 +++++++++++++++------ tailwind.config.ts | 19 +++-- 5 files changed, 157 insertions(+), 35 deletions(-) diff --git a/src/app/about/page.tsx b/src/app/about/page.tsx index 6ccad0d..842210c 100644 --- a/src/app/about/page.tsx +++ b/src/app/about/page.tsx @@ -1,8 +1,41 @@ -'use client'; +"use client"; +import React, { useState } from "react"; export default function About() { + const [isHovered, setIsHovered] = useState(false); + return ( - <>about +
+
setIsHovered(true)} + onMouseLeave={() => setIsHovered(false)} + > + {/* Main text */} +

+ COMING +

+

+ SOON! +

+ + {/* Animated underline */} +
+
+
+ + {/* Subtitle */} +

+ We're building something awesome. +
+ Stay tuned! +

+
+
); } diff --git a/src/app/projects/page.tsx b/src/app/projects/page.tsx index 55d258a..160966c 100644 --- a/src/app/projects/page.tsx +++ b/src/app/projects/page.tsx @@ -1,5 +1,41 @@ "use client"; +import React, { useState } from "react"; + export default function Projects() { - return <>Projects page; + const [isHovered, setIsHovered] = useState(false); + + return ( +
+
setIsHovered(true)} + onMouseLeave={() => setIsHovered(false)} + > + {/* Main text */} +

+ COMING +

+

+ SOON! +

+ + {/* Animated underline */} +
+
+
+ + {/* Subtitle */} +

+ We're building something awesome. +
+ Stay tuned! +

+
+
+ ); } diff --git a/src/components/MemberCounts/SocialStats.tsx b/src/components/MemberCounts/SocialStats.tsx index b0ff3fe..a5a7d71 100644 --- a/src/components/MemberCounts/SocialStats.tsx +++ b/src/components/MemberCounts/SocialStats.tsx @@ -29,7 +29,7 @@ const StatCard: React.FC = ({ rel="noopener noreferrer" className="block transition-transform duration-300 hover:scale-105" > -
+
{/* Glow effect on hover */}
{ glowColor="rgba(88, 101, 242, 0.5)" href="https://discord.com/invite/jfq9phWqTF" /> - { bgColor="bg-gradient-to-tr from-[#F58529] via-[#DD2A7B] to-[#8134AF]" glowColor="rgba(221, 42, 123, 0.5)" href="https://www.instagram.com/pcbuildinguf/" - /> + /> */}
); }; diff --git a/src/components/landing/Hero.tsx b/src/components/landing/Hero.tsx index 0fdb902..ca4d6bd 100644 --- a/src/components/landing/Hero.tsx +++ b/src/components/landing/Hero.tsx @@ -1,27 +1,58 @@ -import React from "react"; +import React, { useState, useEffect } from "react"; import LogoCarousel from "./LogoCarousel"; import Image from "next/image"; import { motion } from "framer-motion"; +import SocialStats from "@/components/memberCounts/SocialStats"; export default function Hero() { + const [text, setText] = useState(""); + const [isInitialBlinkComplete, setIsInitialBlinkComplete] = useState(false); + const fullText = "The Society of PC Building"; + const [isTypingComplete, setIsTypingComplete] = useState(false); + + // Handle initial cursor blink + useEffect(() => { + // Wait for 2 full blink cycles (2 seconds) before starting the typing + const initialBlinkTimeout = setTimeout(() => { + setIsInitialBlinkComplete(true); + }, 400); // 2 seconds = 2 full blink cycles at 1s per cycle + + return () => clearTimeout(initialBlinkTimeout); + }, []); + + // Handle typing animation + useEffect(() => { + if (!isInitialBlinkComplete) return; // Don't start typing until initial blink is complete + + if (text.length < fullText.length) { + const timeout = setTimeout(() => { + setText(fullText.slice(0, text.length + 1)); + }, 70); // Adjust typing speed here (milliseconds) + + return () => clearTimeout(timeout); + } else { + setIsTypingComplete(true); + } + }, [text, isInitialBlinkComplete, fullText]); + return (
- {/* Flex-row on desktop devices but flex-col-reverse for devices lg and smaller */}
- {/* Hidden on mobile */} - - 1000+ Active Members! - -

- The Society of PC Building +

+ {text} + + _ +

- {/* On mobile devices shorten the description to limit text-length */} -

+

Join the Society of PC Building at UF—where @@ -29,18 +60,28 @@ export default function Hero() { hardware and tech connect, innovate, and build custom PCs together.

+ + + + - Join the Club! + Get Involved! - {/* On mobile devices show the infinite carousel but on desktop static list */} -
-

+

+

Led by a team of officers with experience at:

@@ -56,19 +97,20 @@ export default function Hero() {
- {/* Animate SPCB logo upwards. Applied negative margin to center logo better */} SPCB Logo Date: Thu, 9 Jan 2025 14:30:53 -0500 Subject: [PATCH 6/6] Fixed Deployment --- src/components/landing/Hero.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/landing/Hero.tsx b/src/components/landing/Hero.tsx index ca4d6bd..08bf8a5 100644 --- a/src/components/landing/Hero.tsx +++ b/src/components/landing/Hero.tsx @@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react"; import LogoCarousel from "./LogoCarousel"; import Image from "next/image"; import { motion } from "framer-motion"; -import SocialStats from "@/components/memberCounts/SocialStats"; +import SocialStats from "@/components/MemberCounts/SocialStats"; export default function Hero() { const [text, setText] = useState("");