Skip to content

Commit

Permalink
chore: responsive styles, biggo clean up (#76)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexieyizhe authored Dec 18, 2020
1 parent 9a3a368 commit 9d208f5
Show file tree
Hide file tree
Showing 20 changed files with 495 additions and 446 deletions.
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
],
"plugins": ["prettier", "@typescript-eslint"],
"rules": {
"@typescript-eslint/explicit-module-boundary-types": "off"
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-explicit-any": "off"
}
}
2 changes: 1 addition & 1 deletion src/components/Bio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const Bio: FC = () => {
<Text>
Wanna chat about <span className="dynamic">{talkingPoint}</span>? Lets
talk. You can reach me at{' '}
<Link href="mailto:[email protected]">[email protected]</Link>.
<Link href="mailto:[email protected]">[email protected]</Link>.
</Text>
</p>
</Container>
Expand Down
166 changes: 67 additions & 99 deletions src/components/DynamicCurrentStatus.tsx
Original file line number Diff line number Diff line change
@@ -1,68 +1,50 @@
import { memo, FC, useEffect, useState, useCallback } from 'react';
import { prominent } from 'color.js';

import { rgbToHsl, useVisibilityChange } from 'services/utils';
import { useSiteContext } from 'services/site/context';
import { TNowPlayingData } from 'services/now-playing';
import { fetchNowPlaying } from 'services/now-playing/fetch';
import TextLoop from 'react-text-loop';
import { styled } from 'goober';

const getBestTextColor = async (coverArt: string) => {
const colors = (await prominent(coverArt, {
amount: 3,
group: 20,
format: 'array',
sample: 10,
})) as number[][];

let [bestH, bestS, bestL] = rgbToHsl(colors[0]);
for (const rgb of colors) {
const [h, s, l] = rgbToHsl(rgb);
if (s > 40) {
[bestH, bestS, bestL] = [h, s, l];
break;
}
import { useVisibilityChange } from 'services/utils';
import { useSiteContext } from 'services/site/context';
import {
TNowPlayingData,
isNowPlayingData,
getNowPlaying,
} from 'services/now-playing';

const CoverArtLink = styled('a')`
position: relative;
display: inline-block;
transition: transform 250ms;
&:hover {
transform: scale(1.1);
}
// upper bound lightness value at 40 to make it readable
return `hsl(${bestH}, ${bestS}%, ${Math.min(bestL, 40)}%)`;
};

const CoverArtLink = ({ href, children }) => {
const [hover, setHover] = useState(false);

return (
<a
href={href}
target="_blank"
rel="noreferrer noopener"
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
style={{
position: 'relative',
display: 'inline-block',
transform: `scale(${hover ? 1.1 : 1})`,
transition: 'transform 250ms',
}}
>
{children}
</a>
);
};
& img {
width: 18px;
height: 18px;
border-radius: 3px;
transform: translateY(1px);
box-shadow: rgba(99, 99, 99, 0.2) 0px 2px 8px 0px;
}
`;

const nowPlayingMarkup = ({
name,
artist,
artistName,
podcastName,
link,
coverArtSrc,
coverArtColor,
}: TNowPlayingData) => {
const isTrack = !!artist;
const action = isTrack ? "jammin' out to" : 'listening to';
const label = `${name}${isTrack ? ` by ${artist}` : ''}`;
const isPodcast = !!podcastName;
const hasArtist = !!artistName;
const action = isPodcast ? 'listening to an episode of' : "jammin' out to";
const label = `${isPodcast ? podcastName : name}${
hasArtist ? ` by ${artistName}` : ''
}`;

return [
...action.split(' ').map((a) => <span style={{ color: '#000' }}>{a}</span>),
...action.split(' ').map((a) => <span>{a}</span>),
...label.split(' ').map((a) => (
<span
className="dynamic"
Expand All @@ -71,85 +53,71 @@ const nowPlayingMarkup = ({
{a}
</span>
)),
<CoverArtLink href={link}>
<img
src={coverArtSrc}
style={{
height: '18px',
borderRadius: '3px',
transform: 'translateY(1px)',
}}
/>
<CoverArtLink href={link} target="_blank" rel="noreferrer noopener">
<img src={coverArtSrc} />
</CoverArtLink>,
];
};

const DynamicCurrentStatus: FC = memo(() => {
const { nowPlaying, activity, spotifyToken } = useSiteContext();
const [np, setNp] = useState<(TNowPlayingData | string)[]>([
nowPlaying ?? `probably ${activity}`,
const { nowPlayingData, activity, spotifyToken } = useSiteContext();
const [statuses, setStatuses] = useState<(TNowPlayingData | string)[]>([
nowPlayingData ?? `probably ${activity}`,
]);
const [shouldFetchNew, setShouldFetchNew] = useState(true);

const refetchNp = useCallback(async () => {
const lastNp = np[np.length - 1];
const lastNowPlayingData = typeof lastNp === 'string' ? null : lastNp;
const updatedNowPlayingData = await fetchNowPlaying(spotifyToken);
console.debug(updatedNowPlayingData, lastNowPlayingData);
const updatedNowPlayingData = await getNowPlaying(spotifyToken);

if (
updatedNowPlayingData &&
updatedNowPlayingData.uri !== lastNowPlayingData?.uri
) {
const lastStatus = statuses[statuses.length - 1];
const lastNowPlayingData = isNowPlayingData(lastStatus) ? lastStatus : null;
const hasNewNowPlayingData =
!!updatedNowPlayingData &&
updatedNowPlayingData.uri !== lastNowPlayingData?.uri;

if (hasNewNowPlayingData) {
console.debug('New now playing data found...', updatedNowPlayingData);
const color = await getBestTextColor(updatedNowPlayingData.coverArtSrc);
setNp((prev) => [
...prev,
{
...updatedNowPlayingData,
coverArtColor: color,
},
]);
setStatuses((prev) => [...prev, updatedNowPlayingData]);
}
}, [np, spotifyToken]);
}, [statuses, spotifyToken]);

/**
*Refetch what's currently playing on Spotify when tab receives focus, and on mount.
*/
useVisibilityChange((isHidden) => {
if (!isHidden) {
console.debug('Received focus, refreshing now playing...');
setShouldFetchNew(true);
refetchNp();
}
});

useEffect(() => {
(async () => {
if (shouldFetchNew) {
await refetchNp();
setShouldFetchNew(false);
}
})();
}, [refetchNp, shouldFetchNew]);
refetchNp();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

const npMarkup = np.map((e) => {
return typeof e === 'string' ? e.split(' ') : nowPlayingMarkup(e);
});
const statusesMarkup = statuses.map((status) =>
isNowPlayingData(status) ? nowPlayingMarkup(status) : status.split(' ')
);

return (
<span>
{[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17].map(
(i) => {
{new Array(Math.max(...statusesMarkup.map((s) => s.length)))
.fill('')
.map((_, wordIdx) => {
return (
<>
<TextLoop
interval={np.map((_, i) => (i === np.length - 1 ? -1 : 2000))} // don't transition from the last data back to the initial data
children={npMarkup.map((e) => e[i] ?? ' ')}
// transition to next status, but don't transition from last back to first
interval={statuses.map((_, i) =>
i === statuses.length - 1 ? -1 : 1000
)}
children={statusesMarkup.map((m) => m[wordIdx] ?? '')}
/>{' '}
</>
);
}
)}
})}
</span>
);
});
// TODO: remove react-motion

export default DynamicCurrentStatus;
21 changes: 21 additions & 0 deletions src/components/DynamicFavicon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useState, FC } from 'react';
import Head from 'next/head';

import { useVisibilityChange } from 'services/utils';

const DynamicFavicon: FC = () => {
const [isAway, setAway] = useState(false);
useVisibilityChange(setAway);

return (
<Head>
<link
rel="shortcut icon"
type="image/png"
href={isAway ? '/favicon-away.png' : '/favicon.png'}
/>
</Head>
);
};

export default DynamicFavicon;
20 changes: 10 additions & 10 deletions src/components/DynamicTime.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,6 @@ type TextGradientInfo = [
gradientTo: string
];

const GradientContainer = styled<{ gradient: TextGradientInfo }>('span')`
color: ${({ gradient }) => gradient[1]};
background: ${({ gradient }) =>
`linear-gradient(${gradient[0]}, ${gradient[1]}, ${gradient[2]})`};
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
`;

const timeHourMarkup = (hour: number) => {
const twelveHourTime = hour % 12 || 12; // 0 or 24 becomes 12am
const timeOfDay = hour < 12 || hour === 24 ? 'AM' : 'PM';
Expand Down Expand Up @@ -50,7 +41,7 @@ const timeToColor = (hour: number, time: string): TextGradientInfo => {
case '2PM':
case '3PM':
case '4PM':
return [`${140 + hour * 3}deg`, '#FFCE32', '#5995B7'];
return [`${140 + hour * 3}deg`, '#FFCE32', '#45B6F7'];

case '5PM': // sunset
return ['120deg', '#5995B7', '#FF8C18'];
Expand All @@ -74,6 +65,15 @@ const timeToColor = (hour: number, time: string): TextGradientInfo => {
}
};

const GradientContainer = styled<{ gradient: TextGradientInfo }>('span')`
color: ${({ gradient }) => gradient[1]};
background: ${({ gradient }) =>
`linear-gradient(${gradient[0]}, ${gradient[1]}, ${gradient[2]})`};
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
`;

const DynamicTime: FC = memo(() => {
const { currentDate } = useSiteContext();
const timeMarkup = timeHourMarkup(currentDate.getHours());
Expand Down
4 changes: 4 additions & 0 deletions src/components/Heading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ const H1 = styled('h1')`
font-size: 48px;
text-align: center;
margin-bottom: 32px;
@media only screen and (max-width: 600px) {
font-size: 32px;
}
`;

const Heading: FC = memo(() => {
Expand Down
2 changes: 1 addition & 1 deletion src/components/Links.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const Container = styled('footer')`
align-items: center;
justify-content: center;
margin: 1em 0 2em 0;
margin: 0 0 2em 0;
& > a {
margin: 0 6px;
Expand Down
17 changes: 13 additions & 4 deletions src/components/MeIllustration.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import { FC, memo, useState } from 'react';
import { styled } from 'goober';

import { useSiteContext } from 'services/site/context';

const Container = styled('svg')`
height: 280px;
@media only screen and (max-width: 600px) {
height: 220px;
}
`;

const WEIRD = (
<g id="Face">
<g fill="none" fillRule="evenodd" stroke="none" strokeWidth="1">
Expand All @@ -17,6 +26,7 @@ const WEIRD = (
</g>
</g>
);

const SURPRISED = (
<g
id="Face/Surprise"
Expand Down Expand Up @@ -65,14 +75,13 @@ const MeIllustration: FC = memo(() => {

const onIllustrationClick = () =>
setNumClicks((prev) => {
if (prev + 1 === 3)
if ((prev + 1) % 3 === 0)
window.open('https://www.youtube.com/watch?v=dQw4w9WgXcQ', '_blank');
return prev + 1;
});

return (
<svg
height="280"
<Container
overflow="visible"
viewBox="233.511 78 682.97 695.5"
onMouseEnter={() => setHovering(true)}
Expand Down Expand Up @@ -266,7 +275,7 @@ const MeIllustration: FC = memo(() => {
</g>
</g>
</g>
</svg>
</Container>
);
});

Expand Down
5 changes: 2 additions & 3 deletions src/pages/_document.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@ import Document, {
} from 'next/document';
import { extractCss } from 'goober';

export default class CustomDocument extends Document<{ css: any }> {
export default class CustomDocument extends Document<{ css: string }> {
static async getInitialProps({ renderPage }: DocumentContext) {
// inline critical css for page render
const page = renderPage();

// extract the css for each page render
const css = extractCss();
return { ...page, css };
}
Expand Down
9 changes: 9 additions & 0 deletions src/pages/api/refresh-now-playing.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { StorageClient } from 'services/_server_';

export default function handler(_, res) {
const client = new StorageClient();
client.getSpotifyCredentials();
res.statusCode = 200;
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ success: true }));
}
Loading

0 comments on commit 9d208f5

Please sign in to comment.