Skip to content

Commit

Permalink
✨ Added Link to Headings, ✨ Timestamps to Posts (#96)
Browse files Browse the repository at this point in the history
  • Loading branch information
payapula authored Jun 10, 2024
1 parent b86d629 commit 61785c1
Show file tree
Hide file tree
Showing 23 changed files with 17,778 additions and 16,573 deletions.
3 changes: 2 additions & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ lib
# Refer to comments of this answer = https://stackoverflow.com/a/66260842/5155530
.eslintrc.js
next-sitemap.config.js
next.config.js
next.config.js
scripts/
9 changes: 9 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Check if there are any changes in the "_posts" directory.
if git diff --cached --name-only | grep -q "_posts/"
then
# Run generateTs if changes are detected.
echo "Changes detected in _posts directory. Running the script..."
npm run generateTs
else
echo "No changes in _posts directory. Skipping the script..."
fi
49 changes: 41 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
# Website

Portfolio Website & tech blog of Payapula
Portfolio Website & tech blog of Bharathi Kannan
Live at https://www.bharathikannan.com/

---

## Tech Stack
## 🚀 Tech Stack

- [Next JS](https://nextjs.org/)
- [Chakra UI](https://chakra-ui.com/)
- [Next MDX Remote](https://github.com/hashicorp/next-mdx-remote)

---

## Resources
## 📘 Resources

- [MDX Embed](https://www.mdx-embed.com/?path=/docs/mdx-embed--page)
- [Blog Example with next mdx remote](https://github.com/vercel/next.js/tree/canary/examples/with-mdx-remote)

---

## Inspirations
## 🙏🏽 Inspirations

- [Kent C Dodds Blog](https://github.com/kentcdodds/kentcdodds.com)
- [Leerob Blog](https://github.com/leerob/leerob.io)
Expand All @@ -34,15 +34,15 @@ Please refer to [Projects](https://github.com/payapula/blog/projects)

---

## Installation
## 📍 Installation

`npm install`

`npm run dev`

---

## View Preview Locally on Mobile
## 📲 View Preview Locally on Mobile

Windows:

Expand All @@ -56,7 +56,7 @@ Open that address on the mobile with the same portnumber.

Ex: `192.168.1.9:3001`

## Bundle Analyzer
## 🧵 Bundle Analyzer

`npm run analyze`

Expand All @@ -71,7 +71,7 @@ Make sure you have installed source-map-explorer:

`npm install -g source-map-explorer`

## Sitemap
## 📕 Sitemap

Sitemap file gets updated automatically by this awesome package - [next-sitemap](https://github.com/iamvishnusankar/next-sitemap)

Expand All @@ -80,6 +80,39 @@ Sitemap file gets updated automatically by this awesome package - [next-sitemap]
- `npm run build` would be executed during deployment.
- After build `postbuild` script would be executed, which updates sitemaps.

## 🧪 Playwright Tests

### Screenshots

Its tricky to generate screenshots when tests are running in Github actions.

To update screenshots for blog posts in CI (Github actions), we need to build the
screenshots via docker in local.

👉 Check this [PR for instructions on first time set up](https://github.com/payapula/blog/pull/82).

#### To update Screenshots

##### For local mac

Run `npx playwright test --update-snapshots`, it will generate new screenshot files for `-darwin.png`

##### For CI (linux)

1. On root of the project directory, open a terminal (/blog).
2. Run `docker run --rm --network host -v $(pwd):/work/ -w /work/ -it mcr.microsoft.com/playwright:v1.33.0-jammy /bin/bash`.
- This will sync the local folder with docker folder
3. `rm -rf node_modules` - Deletes node_modules
- This is to do fresh installation of node modules based on linux platform.
4. `npm install`
5. `npx playwright install --with-deps` - Reinstall playwright dependencies
6. `npx playwright test --update-snapshots` - Runs and updates screenshots
- This step will generate new screenshots based on linux platform (`-linux.png`).
7. Commit the same to the repository.

The downside of this approach is, after updating linux screenshots, on local we again need
to remove `node_modules` and do fresh `npm install` for running the app.

## Notes

Eslint, Prettier Configs provided by [Arpit Bharti](https://dev.to/onygami/eslint-and-prettier-for-react-apps-bonus-next-js-and-typescript-3e46)
2 changes: 1 addition & 1 deletion _posts/context-redux-composition.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: 'Context, Redux or Composition?'
excerpt: "Which is the better way to solve prop drilling? Let us talk about it in this post by solving the same problem
in three different ways."
date: '2023-02-16'
description: "Sharing my experience on the recent react interview question and the feedback I received."
description: "Sharing my experience on the recent react interview question and the feedback I had received."
cover:
src: '/assets/blog/context-redux-composition/drilling-cover.jpg'
alt: 'Pump-jack mining crude oil with the sunset'
Expand Down
2 changes: 1 addition & 1 deletion _posts/minimizing-lodash-bundle-size.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ var lodash = import('lodash');
lodash.sum([1, 2, 3, 3])
```

So, with the named exports it is not possible for the bundlers like webpack to treeshake the unwanted code, and we
So, with the named exports it is not possible for the bundlers like webpack to tree shake the unwanted code, and we
end up shipping entire lodash utilities to the user.

You can avoid this by directly importing the utility you need from lodash like this
Expand Down
2 changes: 1 addition & 1 deletion _posts/use-next-link-with-chakra-ui.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ function IndexPage() {
```

At the time of writing this post, Chakra has just included their own next integration for Link component to support Next v13 updates. View their documentatiom on
using the same: [Chakra UI with Next.js Link](https://chakra-ui.com/getting-started/nextjs-guide#chakra-ui-with-nextjs-link). To note this new component integration has some open issues, so please read carefuly before using it. View the issue [ESM Import Error in chakra next link](https://github.com/chakra-ui/chakra-ui/issues/7363)
using the same: [Chakra UI with Next.js Link](https://v2.chakra-ui.com/getting-started/nextjs-app-guide#styling-nextjs-link). To note this new component integration has some open issues, so please read carefully before using it. View the issue [ESM Import Error in chakra next link](https://github.com/chakra-ui/chakra-ui/issues/7363)

## Sandbox Links

Expand Down
28 changes: 26 additions & 2 deletions components/chakra-link.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { forwardRef, Ref } from 'react';
import { Link, LinkProps, useColorModeValue } from '@chakra-ui/react';
import { Heading, HeadingProps, Link, LinkProps, useColorModeValue } from '@chakra-ui/react';

/**
* Refer to sidebar-link.tsx of chakra-ui
Expand Down Expand Up @@ -46,4 +46,28 @@ const ChakraMDXLink = ({ href, ...rest }: LinkProps): ReturnType<typeof Link> =>
);
};

export { ChakraLink, ChakraMDXLink };
type ChakraHeadingLinkProps = {
id: string;
children: React.ReactNode;
} & HeadingProps;

const ChakraHeadingLink = ({ id, as = 'h2', children, ...props }: ChakraHeadingLinkProps) => {
return (
<>
<Heading {...props} id={id} role="group" as={as}>
{children}
<ChakraMDXLink
ml={2}
href={`#${id}`}
aria-current={undefined}
opacity={0}
_focus={{ opacity: 1, boxShadow: 'outline' }}
_groupHover={{ opacity: 1 }}>
#
</ChakraMDXLink>
</Heading>
</>
);
};

export { ChakraLink, ChakraMDXLink, ChakraHeadingLink };
10 changes: 5 additions & 5 deletions components/mdx/components/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Alert, Box, Heading, chakra, Center, Code as ChakraCode } from '@chakra-ui/react';
import { Code, preToCodeBlock } from './code';
import { ChakraMDXLink } from 'components/chakra-link';
import { ChakraHeadingLink, ChakraMDXLink } from 'components/chakra-link';
import { ChakraNextImage } from 'components/chakra-next-image';
import { ReactElement } from 'react';
import { QuizHighlight } from './quiz-highlight';
Expand Down Expand Up @@ -34,10 +34,10 @@ const TData = (props: object): ReactElement => (
const Pre = (props) => <chakra.div my="2em" borderRadius="sm" {...props} />;

const MDXComponents = {
h1: (props): ReactElement => <Heading as="h2" {...props} />,
h2: (props): ReactElement => <Heading {...props} />,
h3: (props): ReactElement => <Heading as="h3" {...props} />,
h4: (props): ReactElement => <Heading as="h4" {...props} />,
h1: (props): ReactElement => <Heading {...props} />,
h2: (props) => <ChakraHeadingLink {...props} />,
h3: (props) => <ChakraHeadingLink as="h3" {...props} />,
h4: (props) => <ChakraHeadingLink as="h4" {...props} />,
strong: (props): ReactElement => <Box as="strong" fontWeight="extrabold" {...props} />,
pre: (preProps) => {
// Refer Kent C Dodds Implementation below
Expand Down
81 changes: 53 additions & 28 deletions components/post/post-footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,61 @@ import siteConfig from 'configs/site-configs';
const responsiveTwitterSize = { base: 3, md: 5 };
const responsiveGithubSize = { base: 3, md: 6 };

function PostFooter({ slug }: { slug: string }): ReactElement {
interface PostFooterProps {
slug: string;
dates: {
createdDate: string;
modifiedDate: string;
};
}

function TimeStamp({ date, text }: { text: string; date: string }): ReactElement {
return (
<Flex mt={5} justify="space-between" h="25px">
<Box textAlign="center">
<ChakraLink
href={`https://twitter.com/search?q=${siteConfig.general.siteUrl}/blog/${slug}`}>
Let&apos;s Discuss On Twitter
</ChakraLink>
<Icon
as={FaLocationArrow}
w={responsiveTwitterSize}
h={responsiveTwitterSize}
ml="2"
cursor="pointer"
title="Twitter"
color="#1ea1f2"
/>
</Box>
<Box ml="auto" textAlign="center">
<Icon
as={FaGithub}
w={responsiveGithubSize}
h={responsiveGithubSize}
cursor="pointer"
title="GitHub"
/>
<ChakraLink ml={3} href={`${siteConfig.general.editUrl}/${slug}.mdx`}>
Edit this Post On GitHub
</ChakraLink>
<Box as="p" textAlign={'center'} fontSize={{ base: 'sm', md: 'md' }}>
{text}
<Box as="span" fontWeight={'bold'} ml={2}>
{date}
</Box>
</Box>
);
}

function PostFooter({ slug, dates: { createdDate, modifiedDate } }: PostFooterProps): ReactElement {
return (
<Flex direction={'column'} mt={5}>
<Flex justify={'space-between'}>
<TimeStamp text="Published:" date={createdDate} />
<TimeStamp text="Updated:" date={modifiedDate} />
</Flex>
<Flex mt={5} justify="space-between" h="25px">
<Box textAlign="center">
<ChakraLink
href={`https://twitter.com/search?q=${siteConfig.general.siteUrl}/blog/${slug}`}>
Let&apos;s Discuss On Twitter
</ChakraLink>
<Icon
as={FaLocationArrow}
w={responsiveTwitterSize}
h={responsiveTwitterSize}
ml="2"
cursor="pointer"
title="Twitter"
color="#1ea1f2"
/>
</Box>
<Box ml="auto" textAlign="center">
<Icon
as={FaGithub}
w={responsiveGithubSize}
h={responsiveGithubSize}
cursor="pointer"
title="GitHub"
/>
<ChakraLink ml={3} href={`${siteConfig.general.editUrl}/${slug}.mdx`}>
Edit this Post On GitHub
</ChakraLink>
</Box>
</Flex>
</Flex>
);
}
Expand Down
47 changes: 47 additions & 0 deletions lib/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,26 @@ import PostType from 'types/post';
import { OverrideProps } from 'types/utils';

const postsDirectory = join(process.cwd(), '_posts');
const timestampsFile = join(process.cwd(), 'timestamps.json');

function getCreatedAndModifiedDateFromGit(realSlug: string) {
let createdDate = '',
modifiedDate = '';
try {
const timestamps = JSON.parse(fs.readFileSync(timestampsFile, 'utf8'));
const currentPostPath = `_posts/${realSlug}.mdx`;
const { created, modified } = timestamps[currentPostPath];
createdDate = unixTimeStampToDate(created);
modifiedDate = unixTimeStampToDate(modified);
} catch (error) {
console.error('Unable to read timestamps file');
}

return {
createdDate,
modifiedDate
};
}

export function getPostSlugs() {
return fs.readdirSync(postsDirectory);
Expand All @@ -15,10 +35,30 @@ type RawPostType = OverrideProps<PostType, { content: string }>;

type PostKey = keyof PostType;

function unixTimeStampToDate(unixTimeStamp: number) {
const dt = new Date(unixTimeStamp * 1000);
return dt.toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
});
}

export function getPostBySlug(slug: string, fields: PostKey[] = []): Partial<RawPostType> {
const realSlug = slug.replace(/\.mdx$/, '');
const fullPath = join(postsDirectory, `${realSlug}.mdx`);
const fileContents = fs.readFileSync(fullPath, 'utf8');

let createdDate = '',
modifiedDate = '';

// Ensure to read timestamps files only when they are needed
if (fields.includes('createdDate') || fields.includes('modifiedDate')) {
const dates = getCreatedAndModifiedDateFromGit(realSlug);
createdDate = dates.createdDate;
modifiedDate = dates.modifiedDate;
}

const { data, content } = matter(fileContents);

// We are just sending requested fields, hence Parital<> is used
Expand All @@ -33,6 +73,13 @@ export function getPostBySlug(slug: string, fields: PostKey[] = []): Partial<Raw
items[field] = content;
}

if (field === 'createdDate') {
items[field] = createdDate;
}
if (field === 'modifiedDate') {
items[field] = modifiedDate;
}

if (data[field]) {
items[field] = data[field];
}
Expand Down
Loading

0 comments on commit 61785c1

Please sign in to comment.