Skip to content

Commit

Permalink
feat(ui): add recent project cards & latest post to home page
Browse files Browse the repository at this point in the history
  • Loading branch information
sawaYch committed Apr 16, 2024
1 parent 23d66b6 commit 6ec5762
Show file tree
Hide file tree
Showing 17 changed files with 3,644 additions and 3,161 deletions.
5 changes: 4 additions & 1 deletion gatsby-browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ export const wrapPageElement = ({ element, props }) => {
>
<Layout
{...props}
style={{ ...mantineCoreStyle, ...mantineSpotLightStyle }}
style={{
...mantineCoreStyle,
...mantineSpotLightStyle,
}}
>
{element}
</Layout>
Expand Down
6,124 changes: 3,329 additions & 2,795 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,10 @@
"classnames": "^2.3.2",
"dayjs": "^1.11.9",
"dotenv": "^16.3.1",
"embla-carousel-react": "^7.1.0",
"file-saver": "^2.0.5",
"framer-motion": "^10.12.18",
"gatsby": "^5.13.4",
"gatsby": "5.13.3",
"gatsby-plugin-image": "^3.11.0",
"gatsby-plugin-manifest": "^5.11.0",
"gatsby-plugin-mdx": "^5.11.0",
Expand Down
7 changes: 5 additions & 2 deletions postcss.config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
// 'postcss-import': {},
// 'tailwindcss/nesting': {},
'postcss-preset-mantine': {},
'postcss-simple-vars': {
variables: {
Expand All @@ -12,5 +12,8 @@ module.exports = {
'mantine-breakpoint-xl': '88em',
},
},
tailwindcss: {},
autoprefixer: {},
// 'postcss-reporter': {}
},
};
115 changes: 115 additions & 0 deletions src/components/latest-post.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { graphql, navigate, useStaticQuery } from 'gatsby';
import { useCallback, useMemo } from 'react';
import { Text, Badge, Card, Group, Image } from '@mantine/core';

import { IconArticle } from '@tabler/icons-react';
import { formatDateMonthName } from '../utils/format-date';
import getImageUrl from '../utils/getImageUrl';
import PaneContainer from './pane-container';
import Placeholder from './placeholder';

interface LatestPostCardProps {
data: Queries.LatestPostQuery['allStrapiArticle']['nodes'][0];
onClick: () => void;
}

const LatestPostCard = ({ data, onClick }: LatestPostCardProps) => {
const sortedTag = useMemo(() => {
const tags = (
data.tags as {
name: string;
color: string;
}[]
).sort((prev, next) => {
if (prev.name === next.name) return 0;
if (prev.name > next.name) return 1;
return -1;
});
return tags;
}, [data.tags]);

return (
<Card
shadow="sm"
padding="lg"
radius="md"
withBorder
onClick={onClick}
className="transition-all border-4 cursor-pointer latest-post-border"
>
<Card.Section>
<Image
src={getImageUrl(data.cover?.formats?.medium?.url ?? '')}
className="brightness-[0.5] h-full w-full bg-clip-text"
/>
</Card.Section>
<Group justify="space-between" mt="md" mb="xs" className="flex flex-col">
<Text fw={900} className="flex flex-row gap-2 text-center">
<IconArticle />
{data.title}
</Text>
<div className="flex flex-wrap gap-1 uppercase">
{sortedTag != null &&
sortedTag.map((t) => (
<Badge key={t!.name} color={t!.color as string} size="xs">
{t!.name as string}
</Badge>
))}
</div>
<div className="flex text-xs uppercase">
{formatDateMonthName(data.publishedAt ?? '')}
</div>
</Group>
</Card>
);
};

const LatestPost = () => {
const data: Queries.LatestPostQuery = useStaticQuery(graphql`
query LatestPost {
allStrapiArticle(sort: { publishedAt: DESC }) {
nodes {
id
title
slug
publishedAt
tags {
name
color
}
cover {
formats {
medium {
url
}
}
}
}
}
}
`);

const latestPost = data.allStrapiArticle.nodes[0];

const handleReadPost = useCallback(() => {
navigate(`/blog/${latestPost.slug}`);
}, [latestPost.slug]);

return (
<>
<PaneContainer className="!bg-transparent flex flex-col !items-center !justify-center !w-3/4 !h-4/5 !border-none">
<div className="self-start my-8 text-xl uppercase">
# Latest Post <span className="animate-ping"></span>
</div>
<LatestPostCard
key={latestPost.id}
data={latestPost}
onClick={handleReadPost}
/>
</PaneContainer>
<Placeholder />
</>
);
};

export default LatestPost;
2 changes: 1 addition & 1 deletion src/components/oshinoko-character.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ const StatChart = forwardRef<IStatChartHandler, StatChartProps>(
pointLabels: {
font: {
size: 14,
weight: '700',
weight: 700,
},
color: '#ddd',
},
Expand Down
150 changes: 150 additions & 0 deletions src/components/pinned-project.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import { Badge, Card, Group, Indicator, Text } from '@mantine/core';
import { IconGrain } from '@tabler/icons-react';
import { graphql, useStaticQuery } from 'gatsby';
import { GatsbyImage, IGatsbyImageData } from 'gatsby-plugin-image';
import PaneContainer from './pane-container';
import Placeholder from './placeholder';

interface Project {
name: string;
languages: string[];
description: string;
// eslint-disable-next-line react/no-unused-prop-types
isNew?: boolean;
url: string;
image?: IGatsbyImageData;
}

const projectData: Project[] = [
{
name: 'myaPoll',
languages: ['Typescript', 'NextJS'],
description:
'Youtube live stream poll app powered by Next & official data APIv3.',
isNew: true,
url: 'https://github.com/sawaYch/myaPoll',
},
{
name: 'next-youtube-livechat',
languages: ['Typescript', 'NextJS', 'React Hook Library'],
description:
'Fetch YouTube live chat without official API using NextJS. Package available on NPM registry.',
isNew: true,
url: 'https://github.com/sawaYch/next-youtube-livechat',
},
{
name: 'sawaYch.github.io',
languages: ['Typescript', 'Gatsby'],
description: "Nice to meet you.\nCodebase of Sawa's personal site.",
url: 'https://github.com/sawaYch/sawaYch.github.io',
},
{
name: 'mya88',
languages: ['Typescript', 'NextJS'],
description:
'Youtube live stream chat message viewer powered by Next & official data APIv3.',
url: 'https://github.com/sawaYch/mya88',
},
];

const ProjectCard = ({ name, languages, description, url, image }: Project) => (
<a href={url} target="_blank" rel="noopener noreferrer">
<Card
key={name}
withBorder
shadow="sm"
radius="md"
className="bg-[#1b2735]/75 backdrop-md h-fit cursor-pointer"
>
<Card.Section withBorder inheritPadding py="xs">
<Group justify="space-between">
<Text fw={500}>{name}</Text>
<IconGrain size="1rem" color="gray" />
</Group>
</Card.Section>
{image && (
<Card.Section>
<GatsbyImage
className="pointer-events-none select-none"
image={image}
alt={`${name}-image`}
/>
</Card.Section>
)}
<pre className="mt-2 text-sm text-gray-400 whitespace-pre-wrap">
{description}
</pre>
<Card.Section mt="xl" p="sm">
<div className="flex flex-wrap gap-1">
{languages.map((lang) => (
<Badge key={`${name}-${lang}`} color="indigo">
{lang}
</Badge>
))}
</div>
</Card.Section>
</Card>
</a>
);

const ProjectCardWrapper = ({ isNew, ...props }: Project) =>
isNew ? (
<Indicator inline label="✨Latest✨" size={20}>
<ProjectCard key={props.name} {...props} />
</Indicator>
) : (
<ProjectCard key={props.name} {...props} />
);

const PinnedProject = () => {
const data: Queries.ProjectImageFilesQuery = useStaticQuery(graphql`
query ProjectImageFiles {
allFile(
filter: {
extension: { regex: "/(png)/" }
relativeDirectory: { eq: "projects" }
}
) {
edges {
node {
id
name
childImageSharp {
gatsbyImageData(
width: 720
placeholder: BLURRED
formats: [AUTO, WEBP]
)
}
}
}
}
}
`);
return (
<>
<PaneContainer className="!bg-transparent !border-0 flex flex-col !items-center !justify-center !w-3/4 !h-1/2">
<div className="self-start my-8 text-xl uppercase">
# Recent Projects <span className="animate-ping"></span>
</div>
<div className="grid w-full grid-cols-1 gap-x-24 gap-y-24 sm:grid-cols-2">
{projectData.map((d) => {
const imageNode = data.allFile.edges.find((queryData) =>
queryData.node.name.toLowerCase().includes(d.name.toLowerCase())
);
return (
<ProjectCardWrapper
key={d.name}
image={imageNode?.node?.childImageSharp?.gatsbyImageData}
{...d}
/>
);
})}
</div>
</PaneContainer>
<Placeholder />
</>
);
};

export default PinnedProject;
1 change: 1 addition & 0 deletions src/components/powerline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ const Powerline = ({ onAppIconClick, location }: PowerlineProps) => {
<button
onClick={onAppIconClick}
type="button"
aria-label="powerline-button"
className="flex items-center justify-center w-12 bg-dracula-dark"
>
<svg width="0" height="0">
Expand Down
Loading

0 comments on commit 6ec5762

Please sign in to comment.