client: improve theming variable consistency

This commit is contained in:
Max Leiter 2022-03-24 15:12:54 -07:00
parent 2823c217ea
commit e5f467b26a
No known key found for this signature in database
GPG key ID: A3512F2F2F17EBDA
5 changed files with 213 additions and 195 deletions

View file

@ -28,6 +28,21 @@ const App = ({
accents_8: 'var(--darkest-gray)',
border: 'var(--light-gray)',
},
expressiveness: {
dropdownBoxShadow: '0 0 0 1px var(--light-gray)',
shadowSmall: '0 0 0 1px var(--light-gray)',
shadowLarge: '0 0 0 1px var(--light-gray)',
shadowMedium: '0 0 0 1px var(--light-gray)',
},
layout: {
gap: 'var(--gap)',
gapHalf: 'var(--gap-half)',
gapQuarter: 'var(--gap-quarter)',
gapNegative: 'var(--gap-negative)',
gapHalfNegative: 'var(--gap-half-negative)',
gapQuarterNegative: 'var(--gap-quarter-negative)',
radius: 'var(--radius)',
},
font: {
mono: 'var(--font-mono)',
sans: 'var(--font-sans)',

View file

@ -130,7 +130,7 @@ const Post = () => {
}, [title])
return (
<div style={{ marginBottom: 150 }}>
<div style={{ paddingBottom: 150 }}>
<Title title={title} onChange={onChangeTitle} />
<FileDropzone setDocs={uploadDocs} />
<EditDocumentList onPaste={onPaste} docs={docs} updateDocTitle={updateDocTitle} updateDocContent={updateDocContent} removeDoc={removeDoc} />

View file

@ -8,6 +8,9 @@
--gap-half: 0.5rem;
--gap: 1rem;
--gap-double: 2rem;
--gap-negative: calc(-1 * var(--gap));
--gap-half-negative: calc(-1 * var(--gap-half));
--gap-quarter-negative: calc(-1 * var(--gap-quarter));
--small-gap: 4rem;
--big-gap: 4rem;
--main-content: 55rem;

View file

@ -11,226 +11,227 @@ import markdown from "@lib/render-markdown"
export const posts = Router()
const postVisibilitySchema = (value: string) => {
if (value === "public" || value === "private") {
return value
} else {
throw new Error("Invalid post visibility")
}
if (value === "public" || value === "private" ||
value === "unlisted" || value === "protected") {
return value
} else {
throw new Error("Invalid post visibility")
}
}
posts.post(
"/create",
jwt,
celebrate({
body: {
title: Joi.string().required().allow("", null),
files: Joi.any().required(),
visibility: Joi.string()
.custom(postVisibilitySchema, "valid visibility")
.required(),
userId: Joi.string().required(),
password: Joi.string().optional()
}
}),
async (req, res, next) => {
try {
let hashedPassword: string = ""
if (req.body.visibility === "protected") {
hashedPassword = crypto
.createHash("sha256")
.update(req.body.password)
.digest("hex")
}
"/create",
jwt,
celebrate({
body: {
title: Joi.string().required().allow("", null),
files: Joi.any().required(),
visibility: Joi.string()
.custom(postVisibilitySchema, "valid visibility")
.required(),
userId: Joi.string().required(),
password: Joi.string().optional()
}
}),
async (req, res, next) => {
try {
let hashedPassword: string = ""
if (req.body.visibility === "protected") {
hashedPassword = crypto
.createHash("sha256")
.update(req.body.password)
.digest("hex")
}
const newPost = new Post({
title: req.body.title,
visibility: req.body.visibility,
password: hashedPassword
})
const newPost = new Post({
title: req.body.title,
visibility: req.body.visibility,
password: hashedPassword
})
await newPost.save()
await newPost.$add("users", req.body.userId)
const newFiles = await Promise.all(
req.body.files.map(async (file) => {
const html = getHtmlFromFile(file)
const newFile = new File({
title: file.title || "",
content: file.content,
sha: crypto
.createHash("sha256")
.update(file.content)
.digest("hex")
.toString(),
html
})
await newPost.save()
await newPost.$add("users", req.body.userId)
const newFiles = await Promise.all(
req.body.files.map(async (file) => {
const html = getHtmlFromFile(file)
const newFile = new File({
title: file.title || "",
content: file.content,
sha: crypto
.createHash("sha256")
.update(file.content)
.digest("hex")
.toString(),
html
})
await newFile.$set("user", req.body.userId)
await newFile.$set("post", newPost.id)
await newFile.save()
return newFile
})
)
await newFile.$set("user", req.body.userId)
await newFile.$set("post", newPost.id)
await newFile.save()
return newFile
})
)
await Promise.all(
newFiles.map((file) => {
newPost.$add("files", file.id)
newPost.save()
})
)
await Promise.all(
newFiles.map((file) => {
newPost.$add("files", file.id)
newPost.save()
})
)
res.json(newPost)
} catch (e) {
next(e)
}
}
res.json(newPost)
} catch (e) {
next(e)
}
}
)
posts.get("/", secretKey, async (req, res, next) => {
try {
const posts = await Post.findAll({
attributes: ["id", "title", "visibility", "createdAt"]
})
try {
const posts = await Post.findAll({
attributes: ["id", "title", "visibility", "createdAt"]
})
res.json(posts)
} catch (e) {
next(e)
}
res.json(posts)
} catch (e) {
next(e)
}
})
posts.get("/mine", jwt, secretKey, async (req: UserJwtRequest, res, next) => {
if (!req.user) {
return res.status(401).json({ error: "Unauthorized" })
}
if (!req.user) {
return res.status(401).json({ error: "Unauthorized" })
}
try {
const user = await User.findByPk(req.user.id, {
include: [
{
model: Post,
as: "posts",
include: [
{
model: File,
as: "files"
}
]
}
]
})
if (!user) {
return res.status(404).json({ error: "User not found" })
}
return res.json(
user.posts?.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())
)
} catch (error) {
next(error)
}
try {
const user = await User.findByPk(req.user.id, {
include: [
{
model: Post,
as: "posts",
include: [
{
model: File,
as: "files"
}
]
}
]
})
if (!user) {
return res.status(404).json({ error: "User not found" })
}
return res.json(
user.posts?.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())
)
} catch (error) {
next(error)
}
})
posts.get(
"/:id",
celebrate({
params: {
id: Joi.string().required()
}
}),
async (req: UserJwtRequest, res, next) => {
try {
const post = await Post.findOne({
where: {
id: req.params.id
},
include: [
{
model: File,
as: "files",
attributes: [
"id",
"title",
"content",
"sha",
"createdAt",
"updatedAt"
]
},
{
model: User,
as: "users",
attributes: ["id", "username"]
}
]
})
"/:id",
celebrate({
params: {
id: Joi.string().required()
}
}),
async (req: UserJwtRequest, res, next) => {
try {
const post = await Post.findOne({
where: {
id: req.params.id
},
include: [
{
model: File,
as: "files",
attributes: [
"id",
"title",
"content",
"sha",
"createdAt",
"updatedAt"
]
},
{
model: User,
as: "users",
attributes: ["id", "username"]
}
]
})
if (!post) {
return res.status(404).json({ error: "Post not found" })
}
if (!post) {
return res.status(404).json({ error: "Post not found" })
}
// if public or unlisted, cache
if (post.visibility === "public" || post.visibility === "unlisted") {
res.set("Cache-Control", "public, max-age=4800")
}
// if public or unlisted, cache
if (post.visibility === "public" || post.visibility === "unlisted") {
res.set("Cache-Control", "public, max-age=4800")
}
if (post.visibility === "public" || post?.visibility === "unlisted") {
secretKey(req, res, () => {
res.json(post)
})
} else if (post.visibility === "private") {
jwt(req as UserJwtRequest, res, () => {
res.json(post)
})
} else if (post.visibility === "protected") {
const { password } = req.query
if (!password || typeof password !== "string") {
return jwt(req as UserJwtRequest, res, () => {
res.json(post)
})
}
const hash = crypto
.createHash("sha256")
.update(password)
.digest("hex")
.toString()
if (hash !== post.password) {
return res.status(400).json({ error: "Incorrect password." })
}
if (post.visibility === "public" || post?.visibility === "unlisted") {
secretKey(req, res, () => {
res.json(post)
})
} else if (post.visibility === "private") {
jwt(req as UserJwtRequest, res, () => {
res.json(post)
})
} else if (post.visibility === "protected") {
const { password } = req.query
if (!password || typeof password !== "string") {
return jwt(req as UserJwtRequest, res, () => {
res.json(post)
})
}
const hash = crypto
.createHash("sha256")
.update(password)
.digest("hex")
.toString()
if (hash !== post.password) {
return res.status(400).json({ error: "Incorrect password." })
}
res.json(post)
}
} catch (e) {
next(e)
}
}
res.json(post)
}
} catch (e) {
next(e)
}
}
)
function getHtmlFromFile(file: any) {
const renderAsMarkdown = [
"markdown",
"md",
"mdown",
"mkdn",
"mkd",
"mdwn",
"mdtxt",
"mdtext",
"text",
""
]
const fileType = () => {
const pathParts = file.title.split(".")
const language = pathParts.length > 1 ? pathParts[pathParts.length - 1] : ""
return language
}
const type = fileType()
let contentToRender: string = file.content || ""
const renderAsMarkdown = [
"markdown",
"md",
"mdown",
"mkdn",
"mkd",
"mdwn",
"mdtxt",
"mdtext",
"text",
""
]
const fileType = () => {
const pathParts = file.title.split(".")
const language = pathParts.length > 1 ? pathParts[pathParts.length - 1] : ""
return language
}
const type = fileType()
let contentToRender: string = file.content || ""
if (!renderAsMarkdown.includes(type)) {
contentToRender = `~~~${type}
if (!renderAsMarkdown.includes(type)) {
contentToRender = `~~~${type}
${file.content}
~~~`
} else {
contentToRender = "\n" + file.content
}
const html = markdown(contentToRender)
return html
} else {
contentToRender = "\n" + file.content
}
const html = markdown(contentToRender)
return html
}

View file

@ -2,7 +2,6 @@ import { createServer } from "http"
import { app } from "./app"
import config from "./lib/config"
import { sequelize } from "./lib/sequelize"
;(async () => {
await sequelize.sync({})
createServer(app).listen(config.port, () =>