refactor getting html for files and previews

This commit is contained in:
Max Leiter 2022-11-12 17:11:05 -08:00
parent c41cf7c5ef
commit ecd4521403
13 changed files with 70 additions and 1088 deletions

View file

@ -1,7 +1,7 @@
import { memo, useEffect, useState } from "react" import { memo, useEffect, useState } from "react"
import styles from "./preview.module.css" import styles from "./preview.module.css"
import "@styles/markdown.css" import "@styles/markdown.css"
import "./marked.css" import "@styles/syntax.css"
type Props = { type Props = {
height?: number | string height?: number | string
@ -20,35 +20,34 @@ const MarkdownPreview = ({
const [isLoading, setIsLoading] = useState<boolean>(true) const [isLoading, setIsLoading] = useState<boolean>(true)
useEffect(() => { useEffect(() => {
async function fetchPost() { async function fetchPost() {
if (fileId) { // POST to avoid query string length limit
const resp = await fetch(`/api/file/html/${fileId}`, { const method = fileId ? "GET" : "POST"
method: "GET" const path = fileId ? `/api/file/html/${fileId}` : "/api/file/get-html"
}) const body = fileId
if (resp.ok) { ? undefined
const res = await resp.text() : JSON.stringify({
setPreview(res) title: title || "",
setIsLoading(false) content: initial
} })
} else if (content) {
const urlQuery = new URLSearchParams({
title: title || "",
content
})
const resp = await fetch(`/api/file/get-html?${urlQuery}`, { const resp = await fetch(path, {
method: "GET" method: method,
}) headers: {
"Content-Type": "application/json"
},
body
})
if (resp.ok) { if (resp.ok) {
const res = await resp.text() const res = await resp.text()
setPreview(res) setPreview(res)
setIsLoading(false)
}
} }
setIsLoading(false) setIsLoading(false)
} }
fetchPost() fetchPost()
}, [content, fileId, title]) }, [initial, fileId, title])
return ( return (
<> <>
{isLoading ? ( {isLoading ? (
@ -60,7 +59,7 @@ const MarkdownPreview = ({
) )
} }
export default MarkdownPreview export default memo(MarkdownPreview)
export const StaticPreview = ({ export const StaticPreview = ({
content, content,

File diff suppressed because it is too large Load diff

View file

@ -18,7 +18,6 @@ const getPost = async (id: string) => {
const post = await getPostById(id, true) const post = await getPostById(id, true)
const user = await getCurrentUser() const user = await getCurrentUser()
console.log("post is", post)
if (!post) { if (!post) {
return notFound() return notFound()
} }

View file

@ -1,4 +1,3 @@
@import "./syntax.css";
@import "./inter.css"; @import "./inter.css";
:root { :root {
@ -71,7 +70,7 @@
--article-color: #212121; --article-color: #212121;
--header-bg: rgba(255, 255, 255, 0.8); --header-bg: rgba(255, 255, 255, 0.8);
--gray-alpha: rgba(19, 20, 21, 0.5); --gray-alpha: rgba(19, 20, 21, 0.5);
--selection: rgba(0, 0, 0, 0.99); --selection: var(0, 0, 0, .6);
} }
* { * {
@ -80,7 +79,8 @@
::selection { ::selection {
text-shadow: none; text-shadow: none;
background: var(--selection); background: var(--fg) !important;
color: var(--bg) !important;
} }
html, html,

View file

@ -1,17 +1,21 @@
.keyword { .keyword {
font-weight: bold; font-weight: bold;
color: var(--keyword); color: var(--darker-gray);
} }
.token.operator, .token.operator,
.token.punctuation, .token.punctuation,
.token.string,
.token.number,
.token.builtin, .token.builtin,
.token.variable { .token.variable {
color: var(--token); color: var(--token);
} }
.token.string,
.token.number,
.token.boolean {
color: var(--darker-gray);
}
.token.comment { .token.comment {
color: var(--comment); color: var(--comment);
} }
@ -22,3 +26,4 @@
.token.attr-name { .token.attr-name {
color: var(--name); color: var(--name);
} }

View file

@ -70,6 +70,8 @@ export const codeFileExtensions = [
"cxx", "cxx",
"go", "go",
"h", "h",
"m",
"ha",
"hpp", "hpp",
"htm", "htm",
"html", "html",
@ -93,6 +95,7 @@ export const codeFileExtensions = [
"rb", "rb",
"rs", "rs",
"s", "s",
"sh",
"sass", "sass",
"scala", "scala",
"scss", "scss",
@ -109,7 +112,8 @@ export const codeFileExtensions = [
"xml", "xml",
"y", "y",
"yaml", "yaml",
"zig" "fish",
] ]
export const allowedFileExtensions = [ export const allowedFileExtensions = [

View file

@ -26,20 +26,16 @@ export async function getHtmlFromFile({
} }
const type = fileType() const type = fileType()
let contentToRender: string = content || "" let contentToRender: string = content || ""
if (!renderAsMarkdown.includes(type)) { if (!renderAsMarkdown.includes(type)) {
contentToRender = ` contentToRender = `
~~~${type} ~~~${type}
${content} ${content}
~~~ ~~~
` `
} else { } else {
contentToRender = "\n" + content contentToRender = "\n" + content
} }
const html = markdown(contentToRender, { const html = markdown(contentToRender)
})
return html return html
} }

View file

@ -19,6 +19,7 @@
"@mdx-js/react": "^2.1.5", "@mdx-js/react": "^2.1.5",
"@next-auth/prisma-adapter": "^1.0.5", "@next-auth/prisma-adapter": "^1.0.5",
"@prisma/client": "^4.6.1", "@prisma/client": "^4.6.1",
"@types/prismjs": "^1.26.0",
"@wcj/markdown-to-html": "^2.1.2", "@wcj/markdown-to-html": "^2.1.2",
"bcrypt": "^5.1.0", "bcrypt": "^5.1.0",
"client-zip": "2.2.1", "client-zip": "2.2.1",
@ -33,6 +34,7 @@
"next-mdx-remote": "^4.2.0", "next-mdx-remote": "^4.2.0",
"next-themes": "npm:@wits/next-themes@0.2.7", "next-themes": "npm:@wits/next-themes@0.2.7",
"prism-react-renderer": "^1.3.5", "prism-react-renderer": "^1.3.5",
"prismjs": "^1.29.0",
"rc-table": "7.24.1", "rc-table": "7.24.1",
"react": "18.2.0", "react": "18.2.0",
"react-datepicker": "4.8.0", "react-datepicker": "4.8.0",

View file

@ -1,47 +1,28 @@
import { withMethods } from "@lib/api-middleware/with-methods" import { withMethods } from "@lib/api-middleware/with-methods"
import { getHtmlFromFile } from "@lib/server/get-html-from-drift-file" import { getHtmlFromFile } from "@lib/server/get-html-from-drift-file"
import { parseQueryParam } from "@lib/server/parse-query-param" import { parseQueryParam } from "@lib/server/parse-query-param"
import { prisma } from "@lib/server/prisma"
import { NextApiRequest, NextApiResponse } from "next" import { NextApiRequest, NextApiResponse } from "next"
export default withMethods( export default withMethods(
["GET"], ["POST"],
async (req: NextApiRequest, res: NextApiResponse) => { async (req: NextApiRequest, res: NextApiResponse) => {
const query = req.query const body = req.body
const fileId = parseQueryParam(query.fileId) const content = parseQueryParam(body.content)
const content = parseQueryParam(query.content) const title = parseQueryParam(body.title) || "Untitled"
const title = parseQueryParam(query.title) || "Untitled"
if (fileId && (content || title)) { if (!content || !title) {
return res.status(400).json({ error: "Too many arguments" }) return res.status(400).json({ error: "Missing arguments" })
} }
if (fileId) { const renderedHTML = await getHtmlFromFile({
const file = await prisma.file.findUnique({ title,
where: { content
id: fileId })
}
})
if (!file) { res.setHeader("Content-Type", "text/plain")
return res.status(404).json({ error: "File not found" }) res.setHeader("Cache-Control", "public, max-age=4800")
} res.status(200).write(renderedHTML)
res.end()
return res.json(file.html) return
} else {
if (!content || !title) {
return res.status(400).json({ error: "Missing arguments" })
}
const renderedHTML = await getHtmlFromFile({
title,
content
})
res.setHeader("Content-Type", "text/plain")
res.status(200).write(renderedHTML)
res.end()
return
}
} }
) )

View file

@ -1,6 +1,7 @@
import { NextApiRequest, NextApiResponse } from "next" import { NextApiRequest, NextApiResponse } from "next"
import { prisma } from "@lib/server/prisma" import { prisma } from "@lib/server/prisma"
import { parseQueryParam } from "@lib/server/parse-query-param" import { parseQueryParam } from "@lib/server/parse-query-param"
import { withMethods } from "@lib/api-middleware/with-methods"
const getRawFile = async (req: NextApiRequest, res: NextApiResponse) => { const getRawFile = async (req: NextApiRequest, res: NextApiResponse) => {
const file = await prisma.file.findUnique({ const file = await prisma.file.findUnique({
@ -17,6 +18,6 @@ const getRawFile = async (req: NextApiRequest, res: NextApiResponse) => {
res.setHeader("Cache-Control", "public, max-age=4800") res.setHeader("Cache-Control", "public, max-age=4800")
res.status(200).write(file.html) res.status(200).write(file.html)
res.end() res.end()
} }
export default getRawFile
export default withMethods(["GET"], getRawFile)

View file

@ -17,7 +17,6 @@ async function handleGet(req: NextApiRequest, res: NextApiResponse<any>) {
const id = parseQueryParam(req.query.id) const id = parseQueryParam(req.query.id)
const files = req.query.files ? parseQueryParam(req.query.files) : true const files = req.query.files ? parseQueryParam(req.query.files) : true
console.log("post id is", id)
if (!id) { if (!id) {
return res.status(400).json({ error: "Missing id" }) return res.status(400).json({ error: "Missing id" })
} }

View file

@ -1,9 +1,7 @@
// a nextjs api handerl // a nextjs api handerl
import config from "@lib/config" import config from "@lib/config"
import { getHtmlFromMarkdown } from "@lib/remark-plugins"
import { getHtmlFromFile } from "@lib/server/get-html-from-drift-file" import { getHtmlFromFile } from "@lib/server/get-html-from-drift-file"
import markdown from "@wcj/markdown-to-html"
import { NextApiRequest, NextApiResponse } from "next" import { NextApiRequest, NextApiResponse } from "next"

View file

@ -13,6 +13,7 @@ specifiers:
'@types/jsonwebtoken': ^8.5.9 '@types/jsonwebtoken': ^8.5.9
'@types/marked': ^4.0.7 '@types/marked': ^4.0.7
'@types/node': 17.0.23 '@types/node': 17.0.23
'@types/prismjs': ^1.26.0
'@types/react': 18.0.9 '@types/react': 18.0.9
'@types/react-datepicker': 4.4.1 '@types/react-datepicker': 4.4.1
'@types/react-dom': 18.0.3 '@types/react-dom': 18.0.3
@ -38,6 +39,7 @@ specifiers:
prettier: 2.6.2 prettier: 2.6.2
prism-react-renderer: ^1.3.5 prism-react-renderer: ^1.3.5
prisma: ^4.6.1 prisma: ^4.6.1
prismjs: ^1.29.0
rc-table: 7.24.1 rc-table: 7.24.1
react: 18.2.0 react: 18.2.0
react-datepicker: 4.8.0 react-datepicker: 4.8.0
@ -71,6 +73,7 @@ dependencies:
'@mdx-js/react': 2.1.5_react@18.2.0 '@mdx-js/react': 2.1.5_react@18.2.0
'@next-auth/prisma-adapter': 1.0.5_2pl3b2nwmjya7el2zbe6cwkney '@next-auth/prisma-adapter': 1.0.5_2pl3b2nwmjya7el2zbe6cwkney
'@prisma/client': 4.6.1_prisma@4.6.1 '@prisma/client': 4.6.1_prisma@4.6.1
'@types/prismjs': 1.26.0
'@wcj/markdown-to-html': 2.1.2 '@wcj/markdown-to-html': 2.1.2
bcrypt: 5.1.0 bcrypt: 5.1.0
client-zip: 2.2.1 client-zip: 2.2.1
@ -85,6 +88,7 @@ dependencies:
next-mdx-remote: 4.2.0_biqbaboplfbrettd7655fr4n2y next-mdx-remote: 4.2.0_biqbaboplfbrettd7655fr4n2y
next-themes: /@wits/next-themes/0.2.7_hsmqkug4agizydugca45idewda next-themes: /@wits/next-themes/0.2.7_hsmqkug4agizydugca45idewda
prism-react-renderer: 1.3.5_react@18.2.0 prism-react-renderer: 1.3.5_react@18.2.0
prismjs: 1.29.0
rc-table: 7.24.1_biqbaboplfbrettd7655fr4n2y rc-table: 7.24.1_biqbaboplfbrettd7655fr4n2y
react: 18.2.0 react: 18.2.0
react-datepicker: 4.8.0_biqbaboplfbrettd7655fr4n2y react-datepicker: 4.8.0_biqbaboplfbrettd7655fr4n2y
@ -4381,6 +4385,11 @@ packages:
dependencies: dependencies:
'@prisma/engines': 4.6.1 '@prisma/engines': 4.6.1
/prismjs/1.29.0:
resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==}
engines: {node: '>=6'}
dev: false
/process-nextick-args/2.0.1: /process-nextick-args/2.0.1:
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
dev: true dev: true