Skip to content

Commit

Permalink
feat: truncate html, hide images, add mask effect to post cards
Browse files Browse the repository at this point in the history
  • Loading branch information
leojuriolli7 committed Mar 31, 2023
1 parent cda6c50 commit 2ce73b4
Show file tree
Hide file tree
Showing 12 changed files with 87 additions and 67 deletions.
1 change: 1 addition & 0 deletions custom.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
declare module "markdown-it-task-lists";
declare module "react-syntax-highlighter";
declare module "react-syntax-highlighter/dist/cjs/styles/prism";
declare module "rehype-truncate";
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
"rehype-highlight": "^6.0.0",
"rehype-rewrite": "^3.0.6",
"rehype-stringify": "^9.0.3",
"rehype-truncate": "^1.2.2",
"remark": "^14.0.2",
"remark-gfm": "^3.0.1",
"remark-parse": "^10.0.1",
Expand Down
21 changes: 6 additions & 15 deletions src/components/Comment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,26 +77,17 @@ const Comment: React.FC<CommentProps> = ({ comment }) => {
>
<div className="flex w-full justify-between gap-10 sm:gap-0">
<div className="flex gap-1 items-center">
<h3 className="font-medium">
{getUserDisplayName(comment?.user)}{" "}
<ShouldRender if={comment.userId === session?.user.id}>
<span className=" text-emerald-500"> (You)</span>
</ShouldRender>
</h3>
<Link
href={`/users/${comment.userId}`}
className="font-medium hover:underline"
title="Visit user profile"
target="_blank"
rel="noreferrer"
>
<FiExternalLink
size={15}
className="text-emerald-500 mb-1 cursor-pointer hover:opacity-60"
aria-label="Click to go to this user's page"
title="Click to go to this user's page"
role="link"
/>
{getUserDisplayName(comment?.user)}{" "}
</Link>

<ShouldRender if={comment.userId === session?.user.id}>
<span className=" text-emerald-500"> (You)</span>
</ShouldRender>
</div>

<p className="cursor-pointer select-none" onClick={toggleDateType}>
Expand Down
2 changes: 1 addition & 1 deletion src/components/CompactCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const CompactCard: React.FC<Props> = ({ slide, post, loading }) => {
<HTMLBody
loading={loading}
lines={3}
className={`text-md line-clamp-2 text-ellipsis max-h-14`}
className={`text-md line-clamp-2 text-ellipsis max-h-14 content-mask`}
>
{post?.body}
</HTMLBody>
Expand Down
2 changes: 1 addition & 1 deletion src/components/PostCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const PostCard: React.FC<Props> = ({ post, loading }) => {
<HTMLBody
loading={loading}
lines={3}
className={`prose-sm prose-code:text-xs prose-pre:p-0 line-clamp-4 overflow-hidden text-ellipsis prose-headings:text-base max-h-56`}
className={`prose prose-code:text-xs prose-pre:p-0 line-clamp-4 text-ellipsis prose-headings:text-base max-h-56 overflow-hidden content-mask`}
>
{post?.body}
</HTMLBody>
Expand Down
3 changes: 2 additions & 1 deletion src/server/router/comment.router.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createRouter } from "@server/createRouter";
import { deleteChildComments } from "@server/utils/deleteChildComments";
import { isLoggedInMiddleware } from "@server/utils/isLoggedInMiddleware";
import { markdownToHtml } from "@server/utils/markdownToHtml";
import * as trpc from "@trpc/server";
import { isStringEmpty } from "@utils/checkEmpty";
import {
Expand All @@ -9,7 +10,6 @@ import {
getCommentsSchema,
updateCommentSchema,
} from "@schema/comment.schema";
import { markdownToHtml } from "@server/utils";

export const commentRouter = createRouter()
.query("all-comments", {
Expand All @@ -36,6 +36,7 @@ export const commentRouter = createRouter()
comments.map(async (comment) => {
const formattedBody = await markdownToHtml(
comment?.body || "",
false,
false
);

Expand Down
2 changes: 1 addition & 1 deletion src/server/router/post.router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ export const postRouter = createRouter()
}
: null;

const htmlBody = await markdownToHtml(post?.body || "", false);
const htmlBody = await markdownToHtml(post?.body || "", false, false);

return {
...postWithLikes,
Expand Down
48 changes: 1 addition & 47 deletions src/server/utils/formatPosts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,7 @@ import {
Tag,
User,
} from "@prisma/client";
import { remark } from "remark";
import remarkGfm from "remark-gfm";
import remarkParse from "remark-parse";
import remarkRehype from "remark-rehype";
import rehypeStringify from "rehype-stringify";
import rehypeCode from "rehype-highlight";
import rehypeRewrite from "rehype-rewrite";
import { Root, RootContent } from "hast";
import { markdownToHtml } from "./markdownToHtml";

type PostType =
| (Post & {
Expand All @@ -33,45 +26,6 @@ type PostType =
})
| null;

// Parse post body (markdown) to HTML.
export async function markdownToHtml(markdown: string, rewriteLinks = true) {
const result = await remark()
.use(remarkParse)
.use(remarkGfm)
.use(remarkRehype)
.use(rehypeCode)
.use(
rehypeRewrite,
rewriteLinks
? {
rewrite: (node) => {
// Rewrites any `<a>` to `<p>` to avoid any hydration or
// validate DOM nesting errors.
if (
node.type === "element" &&
node.tagName === "a" &&
rewriteLinks
) {
node.tagName = "p";
}

// Add aria-label to `<input type='checkbox'>`
if (
node.type === "element" &&
node.tagName === "input" &&
node?.properties?.type === "checkbox"
) {
node.properties["aria-label"] = "Checkbox from checklist";
}
},
}
: false
)
.use(rehypeStringify)
.process(markdown);
return result.toString();
}

// Filter post for likes and dislikes, and liked/dislikedByMe
export const getPostWithLikes = (post: PostType, session?: Session | null) => {
const likedByMe =
Expand Down
3 changes: 2 additions & 1 deletion src/server/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { deleteChildComments } from "./deleteChildComments";
import { getFiltersByInput } from "./getFiltersByInput";
import { isLoggedInMiddleware } from "./isLoggedInMiddleware";
import { formatPosts, getPostWithLikes, markdownToHtml } from "./formatPosts";
import { markdownToHtml } from "./markdownToHtml";
import { formatPosts, getPostWithLikes } from "./formatPosts";

export {
deleteChildComments,
Expand Down
61 changes: 61 additions & 0 deletions src/server/utils/markdownToHtml.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { remark } from "remark";
import remarkGfm from "remark-gfm";
import remarkParse from "remark-parse";
import remarkRehype from "remark-rehype";
import rehypeStringify from "rehype-stringify";
import rehypeTruncate from "rehype-truncate";
import rehypeCode from "rehype-highlight";
import rehypeRewrite from "rehype-rewrite";

// Parse markdown to HTML. (Post/Comment body)
export async function markdownToHtml(
markdown: string,
rewriteLinks = true,
truncate = true
) {
const result = await remark()
.use(remarkParse)
.use(remarkGfm)
.use(remarkRehype)
.use(rehypeCode)
.use(
// Truncate the HTML to reduce the size of the DOM and avoid
// sending unnecessary HTML to the client.
rehypeTruncate,
truncate ? { maxChars: 200, ignoreTags: ["ul", "code", "pre"] } : false
)
.use(rehypeRewrite, {
rewrite: (node) => {
if (rewriteLinks) {
// Rewrites any `<a>` to `<p>` to avoid any hydration or
// validate DOM nesting errors.
if (node.type === "element" && node.tagName === "a" && rewriteLinks) {
node.tagName = "p";
}

// Hide any images.
if (
node.type === "element" &&
node.tagName === "img" &&
node.properties
) {
node.properties["width"] = "0";
node.properties["height"] = "0";
node.properties["style"] = "display: none; visibility: hidden;";
}
}

// Add aria-label to `<input type='checkbox'>` for better SEO.
if (
node.type === "element" &&
node.tagName === "input" &&
node?.properties?.type === "checkbox"
) {
node.properties["aria-label"] = "Checkbox from checklist";
}
},
})
.use(rehypeStringify)
.process(markdown);
return result.toString();
}
5 changes: 5 additions & 0 deletions src/styles/globals.scss
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ button:disabled {

// Create new tailwind class for MD editor dark-mode styles.
@layer utilities {
.content-mask {
-webkit-mask-image: linear-gradient(180deg, #000 71%, transparent);
mask-image: linear-gradient(180deg, #000 71%, transparent);
}

.md-dark-mode {
@apply bg-neutral-900;

Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4713,6 +4713,11 @@ rehype-stringify@^9.0.3:
hast-util-to-html "^8.0.0"
unified "^10.0.0"

rehype-truncate@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/rehype-truncate/-/rehype-truncate-1.2.2.tgz#fbb75bad4ad13d89d7dab2cf88c205fe48a4aa27"
integrity sha512-zj2FxxC3rm8bg6loesMdS/+BuGpp89mu+12VsJrBzN5kKgOxKwtawscQXElyt2BnxU2pG3hkoyb3Euc3Q7F38A==

remark-gfm@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/remark-gfm/-/remark-gfm-3.0.1.tgz#0b180f095e3036545e9dddac0e8df3fa5cfee54f"
Expand Down

1 comment on commit 2ce73b4

@vercel
Copy link

@vercel vercel bot commented on 2ce73b4 Mar 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

t3-blog – ./

t3-blog-leojuriolli.vercel.app
t3-blog-pi.vercel.app
t3-blog-git-main-leojuriolli.vercel.app

Please sign in to comment.