Skip to content

Commit

Permalink
feat: add resume viewer
Browse files Browse the repository at this point in the history
  • Loading branch information
adriancleung committed Sep 25, 2024
1 parent 3abd2a9 commit e874cd4
Show file tree
Hide file tree
Showing 8 changed files with 222 additions and 14 deletions.
4 changes: 4 additions & 0 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,9 @@
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script
type="text/javascript"
src="https://acrobatservices.adobe.com/view-sdk/viewer.js"
></script>
</body>
</html>
29 changes: 29 additions & 0 deletions src/components/common/Button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React, { MouseEventHandler, ReactNode } from 'react';
import styled, { css } from 'styled-components';
import { Styles } from 'styled-components/dist/types';

const ButtonContainer = styled.button<{ styleOverride?: Styles<object> }>`
text-decoration: none;
color: black;
background-color: transparent;
border: unset;
cursor: pointer;
font-size: 1rem;
${props => props.styleOverride && css(props.styleOverride)}
`;

interface ButtonProps {
children: ReactNode;
onClick?: MouseEventHandler<HTMLButtonElement> | undefined;
styleOverride?: Styles<object>;
}

const Button = ({ children, onClick, styleOverride }: ButtonProps) => {
return (
<ButtonContainer onClick={onClick} styleOverride={styleOverride}>
{children}
</ButtonContainer>
);
};

export default Button;
12 changes: 6 additions & 6 deletions src/components/common/FadeIn.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createRef, useEffect, useState } from "react";
import styled from "styled-components";
import { createRef, ReactNode, useEffect, useState } from 'react';
import styled from 'styled-components';

const FadeInContainer = styled.div`
opacity: 0;
Expand All @@ -14,7 +14,7 @@ const FadeInContainer = styled.div`
`;

interface FadeInWrapperProps {
children: React.ReactNode;
children: ReactNode;
}

const FadeInWrapper = ({ children }: FadeInWrapperProps) => {
Expand All @@ -23,8 +23,8 @@ const FadeInWrapper = ({ children }: FadeInWrapperProps) => {

useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => setVisibility(entry.isIntersecting));
entries => {
entries.forEach(entry => setVisibility(entry.isIntersecting));
},
{ threshold: 0.1 }
);
Expand All @@ -40,7 +40,7 @@ const FadeInWrapper = ({ children }: FadeInWrapperProps) => {
}, [ref]);

return (
<FadeInContainer ref={ref} className={isVisible ? "is-visible" : ""}>
<FadeInContainer ref={ref} className={isVisible ? 'is-visible' : ''}>
{children}
</FadeInContainer>
);
Expand Down
10 changes: 5 additions & 5 deletions src/screens/Hero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ const Hero = () => {
firebase.analytics.logEvent('hero_viewed');
}, []);

const handleLinkClicked = useCallback((link: string) => {
const handleLinkClick = useCallback((link: string) => {
firebase.analytics.logEvent('social_link_clicked', {
type: link.toLowerCase(),
});
Expand All @@ -121,26 +121,26 @@ const Hero = () => {
<SocialLink
target='_blank'
rel='noreferrer'
onClick={() => handleLinkClicked('LinkedIn')}
onClick={() => handleLinkClick('LinkedIn')}
href='https://linkedin.com/in/adriancleung'>
<LinkedInIcon fontSize='large' />
</SocialLink>
<SocialLink
target='_blank'
rel='noreferrer'
onClick={() => handleLinkClicked('GitHub')}
onClick={() => handleLinkClick('GitHub')}
href='https://github.com/adriancleung'>
<GitHubIcon fontSize='large' />
</SocialLink>
<SocialLink
target='_blank'
rel='noreferrer'
onClick={() => handleLinkClicked('Instagram')}
onClick={() => handleLinkClick('Instagram')}
href='https://instagram.com/adriancleung'>
<InstagramIcon fontSize='large' />
</SocialLink>
<SocialLink
onClick={() => handleLinkClicked('Email')}
onClick={() => handleLinkClick('Email')}
href='mailto:leung.c.adrian@gmail.com?subject=Inquiry%20-%20Adrian%20L'>
<EmailOutlinedIcon fontSize='large' />
</SocialLink>
Expand Down
4 changes: 2 additions & 2 deletions src/screens/Projects.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ const Projects = () => {
firebase.analytics.logEvent('projects_viewed');
}, []);

const handleProjectLinkClicked = useCallback((repo_name: string) => {
const handleProjectLinkClick = useCallback((repo_name: string) => {
firebase.analytics.logEvent(`project_link_clicked`, { repo_name });
}, []);

Expand Down Expand Up @@ -112,7 +112,7 @@ const Projects = () => {
<a
target='_blank'
rel='noreferrer'
onClick={() => handleProjectLinkClicked(repo.full_name)}
onClick={() => handleProjectLinkClick(repo.full_name)}
href={repo.html_url}
style={{ textDecoration: 'none', color: 'black' }}>
<GitHubIcon />
Expand Down
79 changes: 79 additions & 0 deletions src/screens/Resume.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import React, { useEffect } from 'react';
import styled from 'styled-components';
import CloseIcon from '@mui/icons-material/Close';
import Button from '../components/common/Button';
import { createPortal } from 'react-dom';
import firebase from '../services/firebase';
import PDFViewerClient from '../services/PDFViewerClient';

const ResumeModalContainer = styled.div`
background-color: #fbfcf8;
width: 100vw;
height: 100vh;
position: fixed;
top: 150%;
visibility: hidden;
transition: top 0.7s ease-in-out, visibility 0.7s ease-in-out;
will-change: top, visibility;
z-index: 1;
&.is-visible {
visibility: visible;
top: 0;
}
`;

const ResumeTopBarContainer = styled.div`
display: flex;
justify-content: end;
`;

const ResumeContentContainer = styled.div`
height: 100%;
display: flex;
justify-content: center;
align-items: center;
`;

interface ResumeModalProps {
isVisible: boolean;
onClose: () => void;
}

const ResumeModal = ({ isVisible, onClose }: ResumeModalProps) => {
useEffect(() => {
if (isVisible) {
document.body.style.overflowY = 'hidden';
firebase.analytics.logEvent('resume_viewed');
} else {
document.body.style.overflowY = 'scroll';
}
}, [isVisible]);

useEffect(() => {
const pdfViewerClient = new PDFViewerClient('resume-content-container');
pdfViewerClient.previewFile('https://api.adrianleung.dev/resume', {
embedMode: 'SIZED_CONTAINER',
showAnnotationTools: false,
enableFormFilling: false,
showDownloadPDF: true,
showPrintPDF: false,
showZoomControl: true,
showFullScreenViewButton: false,
showFullScreen: false,
defaultViewMode: 'FIT_PAGE',
});
}, []);

return createPortal(
<ResumeModalContainer className={isVisible ? 'is-visible' : ''}>
<ResumeTopBarContainer>
<Button onClick={onClose}>{<CloseIcon />}</Button>
</ResumeTopBarContainer>
<ResumeContentContainer id='resume-content-container'></ResumeContentContainer>
</ResumeModalContainer>,
document.body
);
};

export default ResumeModal;
30 changes: 29 additions & 1 deletion src/screens/Work.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import React, { useEffect } from 'react';
import React, { useCallback, useEffect, useState } from 'react';
import styled from 'styled-components';
import FadeInWrapper from '../components/common/FadeIn';
import { media } from '../styles/breakpoints';
import { FullScreenLayout } from '../styles/layouts';
import firebase from '../services/firebase';
import Button from '../components/common/Button';
import ResumeModal from './Resume';

const WorkContainer = styled(FullScreenLayout)`
display: flex;
Expand Down Expand Up @@ -39,6 +41,10 @@ const WorkAchievementItem = styled.li`
line-height: 1.7rem;
`;

const ResumeButtonContainer = styled.div`
margin: 0 auto;
`;

interface WorkInfoProps {
title: string;
company: string;
Expand Down Expand Up @@ -86,12 +92,23 @@ const WorkInfo = ({
};

const Work = () => {
const [showResumeModal, setShowResumeModal] = useState(false);
const workExperiences: WorkInfoProps[] = firebase.config.get('work');

useEffect(() => {
firebase.analytics.logEvent('work_viewed');
});

const handleResumeButtonClick = useCallback(() => {
setShowResumeModal(true);
firebase.analytics.logEvent('resume_link_clicked');
}, []);

const handleResumeModalClose = useCallback(() => {
setShowResumeModal(false);
firebase.analytics.logEvent('resume_modal_closed');
}, []);

return (
<FadeInWrapper>
<WorkContainer>
Expand All @@ -102,7 +119,18 @@ const Work = () => {
{...workExperience}
/>
))}
<ResumeButtonContainer>
<Button
onClick={handleResumeButtonClick}
styleOverride={{ textDecorationLine: 'underline' }}>
View resume
</Button>
</ResumeButtonContainer>
</WorkContainer>
<ResumeModal
isVisible={showResumeModal}
onClose={handleResumeModalClose}
/>
</FadeInWrapper>
);
};
Expand Down
68 changes: 68 additions & 0 deletions src/services/PDFViewerClient.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
interface ViewConfig {
embedMode?: 'FULL_WINDOW' | 'IN_LINE' | 'LIGHT_BOX' | 'SIZED_CONTAINER';
showAnnotationTools?: boolean;
enableFormFilling?: boolean;
showDownloadPDF?: boolean;
showPrintPDF?: boolean;
showZoomControl?: boolean;
showFullScreenViewButton?: boolean;
showFullScreen?: boolean;
defaultViewMode?:
| 'FIT_PAGE'
| 'FIT_WIDTH'
| 'TWO_COLUMN'
| 'TWO_COLUMN_FIT_PAGE';
}

interface FileContent {
content: {
location: {
url: string;
};
};
metaData: {
fileName: string;
};
}

interface AdobeDCView {
previewFile: (fileContent: FileContent, viewConfig: ViewConfig) => void;
}

class PDFViewerClient {
config: object = {
clientId: 'e4e1feeff1db47a1a9a218a37a77d502',
};

adobeDCView: AdobeDCView | undefined = undefined;

constructor(divId?: string) {
this.config = { ...this.config, divId };
if ((window as any).AdobeDC) {
this.adobeDCView = new (window as any).AdobeDC.View(this.config);
} else {
document.addEventListener('adobe_dc_view_sdk.ready', () => {
this.adobeDCView = new (window as any).AdobeDC.View(this.config);
});
}
}

previewFile(url: string, viewConfig: ViewConfig) {
if (this.adobeDCView) {
this.adobeDCView.previewFile(
{
content: {
location: {
url,
},
},
metaData: {
fileName: 'Resume_AdrianLeung.pdf',
},
},
viewConfig
);
}
}
}
export default PDFViewerClient;

0 comments on commit e874cd4

Please sign in to comment.