diff --git a/.github/workflows/lint-tests.yml b/.github/workflows/lint-tests.yml new file mode 100644 index 0000000..1a4cc81 --- /dev/null +++ b/.github/workflows/lint-tests.yml @@ -0,0 +1,19 @@ +name: Lint Test + +on: [push, pull_request] + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Install dependencies + run: | + npm install + + - name: Run Linting + run: | + npm run lint diff --git a/app/page.js b/app/page.js index c18f3ec..ececfd3 100644 --- a/app/page.js +++ b/app/page.js @@ -3,9 +3,7 @@ import projects from "../data/projects.yaml"; import { Link } from "@nextui-org/link"; import { fontGabarito } from "@/config/fonts"; import clsx from "clsx"; -import { TypeAnimation } from 'react-type-animation'; import Image from 'next/image'; - import { Navbar } from "@/components/Navbar"; import ProjectCarousel from "@/components/ProjectCarousel"; import Timeline from "@/components/Timeline"; @@ -16,6 +14,7 @@ import { GithubIcon, LinkedinIcon } from "@/components/Icons"; +import TypingEffect from "@/components/TypingEffect"; export default function Home() { return ( @@ -32,20 +31,6 @@ export default function Home() { />
- {/* */}
-

Trust me, I’m not a robot… but I speak their language.

-

Pursuing Software Engineering & Commerce at UNSW.

-

Always up for a challenge.

+ +

Pursuing Software Engineering & Commerce at UNSW.

+

Always up for a challenge.

diff --git a/components/BrandChip.js b/components/BrandChip.js index 8c85440..b45d366 100644 --- a/components/BrandChip.js +++ b/components/BrandChip.js @@ -39,6 +39,7 @@ import { SiJest, SiPytest, SiGradle, + SiGooglecloud, } from "react-icons/si"; import { FaJava } from "react-icons/fa"; import { @@ -195,6 +196,10 @@ const BrandChip = ({ brand, ...props }) => { color: "hover:bg-[#02303A]", icon: , }, + 'Google Cloud': { + color: "hover:bg-[#4285F4]", + icon: , + }, }; let { color, icon } = brands[brand] || { diff --git a/components/Carousel.js b/components/Carousel.js index 4b61734..dee2b43 100644 --- a/components/Carousel.js +++ b/components/Carousel.js @@ -1,13 +1,27 @@ -import React, { useEffect, useRef } from 'react'; +import React, { useEffect, useRef, useCallback } from 'react'; import Flickity from 'react-flickity-component'; import BrandChip from "./BrandChip"; import "flickity/css/flickity.css"; const Carousel = ({ technologies }) => { const flickityRef = useRef(null); - let requestId; + const requestId = useRef(null); + + const play = useCallback(() => { + const mainTicker = flickityRef.current && flickityRef.current.flkty; + if (mainTicker) { + mainTicker.x -= 0.1; + mainTicker.settle(mainTicker.x); + requestId.current = window.requestAnimationFrame(play); + } + }, []); useEffect(() => { + const pause = () => { + window.cancelAnimationFrame(requestId.current); + requestId.current = undefined; + }; + // Delay our logic to ensure Flickity has initialized setTimeout(() => { const mainTicker = flickityRef.current && flickityRef.current.flkty; @@ -20,39 +34,11 @@ const Carousel = ({ technologies }) => { }, 200); return () => { - if (requestId) { - window.cancelAnimationFrame(requestId); + if (requestId.current) { + window.cancelAnimationFrame(requestId.current); } }; - }, [flickityRef]); - - useEffect(() => { - const mainTicker = flickityRef.current && flickityRef.current.flkty; - if (mainTicker) { - console.log(flickityRef.current.flkty); - // display the flickity container - // flickityRef.current.style.display = 'block'; - } else { - // hide the flickity container - // flickityRef.current.style.display = 'none'; - } - }, [flickityRef]); - - - - function play() { - const mainTicker = flickityRef.current && flickityRef.current.flkty; - if (mainTicker) { - mainTicker.x -= 0.1; - mainTicker.settle(mainTicker.x); - requestId = window.requestAnimationFrame(play); - } - } - - function pause() { - window.cancelAnimationFrame(requestId); - requestId = undefined; - } + }, [flickityRef, play]); function wrapAroundList(list) { const wrapLength = 20; diff --git a/components/Footer.js b/components/Footer.js index 675586e..6d060d6 100644 --- a/components/Footer.js +++ b/components/Footer.js @@ -18,7 +18,7 @@ export const Footer = () => {
diff --git a/components/ProfileCard.js b/components/ProfileCard.js index 4c85cc8..c9b0261 100644 --- a/components/ProfileCard.js +++ b/components/ProfileCard.js @@ -25,7 +25,7 @@ const ProfileCard = () => {

Software Engineering Student at UNSW

Sydney, New South Wales, Australia

-

323 connections

+

338 connections

diff --git a/components/ProjectButton.js b/components/ProjectButton.js new file mode 100644 index 0000000..d75eb61 --- /dev/null +++ b/components/ProjectButton.js @@ -0,0 +1,21 @@ +import React from 'react'; +import { Tooltip, Button } from "@nextui-org/react"; + +const ProjectButton = ({ tooltip, className, onPress, shortText, longText, endIcon, ...props }) => { + return ( + + + + ) +} + +export default ProjectButton \ No newline at end of file diff --git a/components/ProjectCard.js b/components/ProjectCard.js index ce6bc05..8471e68 100644 --- a/components/ProjectCard.js +++ b/components/ProjectCard.js @@ -1,12 +1,35 @@ -import React from "react"; +"use client"; +import React, { useEffect, useRef } from 'react'; import ProjectDetails from "./ProjectDetails"; import ProjectImage from "./ProjectImage"; +import { motion, useInView, useAnimation } from "framer-motion"; const ProjectCard = ({ project, index, openModal }) => { const isOdd = index % 2 == 0; + const ref = useRef(null); + const isInView = useInView(ref, { once: false }); + + const mainControls = useAnimation(); + + useEffect(() => { + if (isInView) { + mainControls.start("visible"); + } + }, [isInView, mainControls]); + return ( -
+
{ )}
-
+ ); } diff --git a/components/ProjectDetails.js b/components/ProjectDetails.js index c0fc1dc..4b7be33 100644 --- a/components/ProjectDetails.js +++ b/components/ProjectDetails.js @@ -4,10 +4,10 @@ import { TbBrandGithub, TbBrandFigma, TbLockCode, - TbExternalLink + TbExternalLink, } from "react-icons/tb"; -import { Tooltip, Button } from "@nextui-org/react"; - +import { FaApple } from "react-icons/fa"; +import ProjectButton from "./ProjectButton"; const ProjectDetails = ({ project, openModal }) => { return ( @@ -21,58 +21,57 @@ const ProjectDetails = ({ project, openModal }) => {
-
+
{project.githubRepo && (!project.isPrivate ? ( - - - + window.open("https://github.com/jeremytraini/" + project.githubRepo)} + shortText="GitHub" + longText="View on GitHub" + endIcon={} + /> ) : ( - - - + openModal(project)} + shortText="GitHub" + longText="Request Access" + endIcon={} + variant="ghost" + /> )) } {project.figmaLink && - + shortText="Figma" + longText="View on Figma" + endIcon={} + /> + } + {project.appStoreUrl && + window.open(project.appStoreUrl)} + shortText="App Store" + longText="View on the App Store" + endIcon={} + /> } {project.liveLink && - - - + window.open(project.liveLink)} + shortText="Live" + longText="View Live" + endIcon={} + color="primary" + /> }
diff --git a/components/ProjectDisplayCard.js b/components/ProjectDisplayCard.js index 8b966ec..afba24c 100644 --- a/components/ProjectDisplayCard.js +++ b/components/ProjectDisplayCard.js @@ -1,9 +1,7 @@ "use client"; - import React, { useEffect } from "react"; import NextImage from "next/image"; - const ProjectCard = ({ project, ...props }) => { useEffect(() => { document.addEventListener("DOMContentLoaded", function () { diff --git a/components/ProjectImage.js b/components/ProjectImage.js index e072939..50ba9e6 100644 --- a/components/ProjectImage.js +++ b/components/ProjectImage.js @@ -1,12 +1,10 @@ import React from "react"; import NextImage from "next/image"; -const ProjectImage = ({ title, src, height, width }) => { +const ProjectImage = ({ title, src }) => { return (
-
+
({ - hidden: { - x: direction === 'left' ? 100 : direction === 'right' ? -100 : 0, - y: direction === 'up' ? 100 : direction === 'down' ? -100 : 0, - opacity: 0, - }, - show: { - x: 0, - y: 0, - opacity: 1, - transition: { - type, - delay, - duration, - ease: 'easeOut', - }, - }, -}); - -const staggeredContainer = (staggerChildren, delayChildren) => ({ - hidden: {}, - show: { - transition: { - staggerChildren, - delayChildren, - }, - }, -}); - - - const Projects = ({ projects }) => { const {isOpen, onOpen, onOpenChange} = useDisclosure(); const [currentProject, setCurrentProject] = useState(null); @@ -48,19 +15,11 @@ const Projects = ({ projects }) => { return (
-
+
{projects .filter((project) => !project.hidden) .map((project, index) => ( -
+
))} diff --git a/components/Timeline.js b/components/Timeline.js index 60f5576..e1a58f4 100644 --- a/components/Timeline.js +++ b/components/Timeline.js @@ -1,83 +1,43 @@ -import React from 'react'; -import Image from 'next/image'; -import { HiRocketLaunch } from "react-icons/hi2"; -import BarkerLogo from '../public/images/timeline/barker_logo.png'; -import UnswLogo from '../public/images/timeline/unsw_logo.png'; -import InfotrackLogo from '../public/images/timeline/infotrack_logo.png'; +"use client"; +import React, { useEffect, useRef } from 'react'; +import TimelineItem from './TimelineItem'; +import { motion, useInView, useAnimation } from "framer-motion"; +import experiences from "../data/experiences.yaml"; -const experiences = [ - { - type: 'High School', - name: 'Barker College', - duration: '2013 - 2020', - image: BarkerLogo, - backgroundColor: '#e02725', - scale: 0.8 - }, - { - type: 'University', - name: 'UNSW Sydney', - duration: '2021 - Present', - image: UnswLogo, - backgroundColor: '#fee600', - scale: 0.7 - }, - { - type: 'Internship', - name: 'InfoTrack', - duration: '2022 - 2023', - image: InfotrackLogo, - backgroundColor: '#ffffff', - scale: 0.8 - }, - { - type: 'Next', - name: 'Your Company?', - duration: '2023', - icon: , - backgroundColor: '#ffa500', - scale: 0.8 - } -]; +function Timeline() { + const ref = useRef(null); + const isInView = useInView(ref, { once: false }); -function TimelineItem({ data }) { - return ( -
-
-
- {data.image && ( - {"Logo - )} - {data.icon} -
-
-
-

{data.type}

-

{data.name}

-

{data.duration}

-

{data.description}

-
-
-
- ); -} + const itemControls = useAnimation(); + + useEffect(() => { + if (isInView) { + itemControls.start("visible"); + } + }, [isInView, itemControls]); -function Timeline() { return ( -
+
- {experiences.slice(0,2).map((experience, index) => ( - - ))} + + {experiences.slice(0,2).map((experience, index) => ( + + + + ))} +
@@ -85,11 +45,24 @@ function Timeline() {
- {experiences.slice(2).map((experience, index) => ( - - ))} +
+ {experiences.slice(2).map((experience, index) => ( + + + + ))} +
-
+ ); } diff --git a/components/TimelineItem.js b/components/TimelineItem.js new file mode 100644 index 0000000..52aadd6 --- /dev/null +++ b/components/TimelineItem.js @@ -0,0 +1,37 @@ +import React from 'react'; +import { HiRocketLaunch } from "react-icons/hi2"; +import BarkerLogo from '../public/images/timeline/barker_logo.png'; +import UnswLogo from '../public/images/timeline/unsw_logo.png'; +import InfotrackLogo from '../public/images/timeline/infotrack_logo.png'; +import TimelineLogo from './TimelineLogo'; + +const icons = { + "barker": , + "unsw": , + "infotrack": , + "rocket": +}; + +const TimelineItem = ({ data }) => { + return ( +
+
+
+ {icons[data.icon]} +
+
+
+

{data.type}

+

{data.name}

+

{data.duration}

+

{data.description}

+
+
+
+ ); +} + +export default TimelineItem \ No newline at end of file diff --git a/components/TimelineLogo.js b/components/TimelineLogo.js new file mode 100644 index 0000000..7dfebda --- /dev/null +++ b/components/TimelineLogo.js @@ -0,0 +1,16 @@ +import React from 'react'; +import Image from 'next/image'; + +const TimelineLogo = ({ src, organisation, scale }) => { + return ( + {organisation + ) +} + +export default TimelineLogo \ No newline at end of file diff --git a/components/TypingEffect.js b/components/TypingEffect.js new file mode 100644 index 0000000..8a9443c --- /dev/null +++ b/components/TypingEffect.js @@ -0,0 +1,16 @@ +"use client"; +import React from 'react'; +import { TypeAnimation } from 'react-type-animation'; + +const TypingEffect = ({ className, sequence }) => { + return ( + + ) +} + +export default TypingEffect \ No newline at end of file diff --git a/data/experiences.yaml b/data/experiences.yaml new file mode 100644 index 0000000..30eca69 --- /dev/null +++ b/data/experiences.yaml @@ -0,0 +1,23 @@ +- type: High School + name: Barker College + duration: 2013 - 2020 + backgroundColor: "#e02725" + icon: barker + +- type: University + name: UNSW Sydney + duration: 2021 - Present + backgroundColor: "#fee600" + icon: unsw + +- type: Internship + name: InfoTrack + duration: 2022 - 2023 + backgroundColor: "#ffffff" + icon: infotrack + +- type: Next + name: Your Company? + duration: '2024' + backgroundColor: "#ffa500" + icon: rocket diff --git a/data/projects.yaml b/data/projects.yaml index a26fad6..b9d48f7 100644 --- a/data/projects.yaml +++ b/data/projects.yaml @@ -14,7 +14,9 @@ - React Native - Express.js - Firebase + - Google Cloud - Axios + - Jest imageUrl: /images/thumbnails/tekkerz.png imageHeight: 1920 imageWidth: 3840 diff --git a/package.json b/package.json index b9d1303..7b5d5e3 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "@nextui-org/react": "^2.1.13", "axios": "^1.5.1", "flickity": "2.3.0", - "framer-motion": "^10.16.4", + "framer-motion": "^10.17.9", "js-yaml": "^4.1.0", "next": "latest", "next-themes": "^0.2.1", diff --git a/yarn.lock b/yarn.lock index 87aaa01..3b80a52 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3137,10 +3137,10 @@ fraction.js@^4.3.6: resolved "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.6.tgz" integrity sha512-n2aZ9tNfYDwaHhvFTkhFErqOMIb8uyzSQ+vGJBjZyanAKZVbGUQ1sngfk9FdkBw7G26O7AgNjLcecLffD1c7eg== -framer-motion@^10.16.4: - version "10.16.4" - resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-10.16.4.tgz#30279ef5499b8d85db3a298ee25c83429933e9f8" - integrity sha512-p9V9nGomS3m6/CALXqv6nFGMuFOxbWsmaOrdmhyQimMIlLl3LC7h7l86wge/Js/8cRu5ktutS/zlzgR7eBOtFA== +framer-motion@^10.17.9: + version "10.17.9" + resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-10.17.9.tgz#d3bb15039e8bd6ba5d044a3fa181332a09cd2692" + integrity sha512-z2NpP8r+XuALoPA7ZVZHm/OoTnwkQNJFBu91sC86o/FYvJ4x7ar3eQnixgwYWFK7kEqOtQ6whtNM37tn1KrOOA== dependencies: tslib "^2.4.0" optionalDependencies: