client: lint and minor scroll button/file explorer adjustments
This commit is contained in:
parent
887ecfabbc
commit
0815d43ee8
10 changed files with 454 additions and 427 deletions
|
@ -23,7 +23,7 @@
|
|||
transition: var(--transition);
|
||||
border-radius: var(--radius);
|
||||
margin: 0;
|
||||
padding: 2px var(--gap);
|
||||
padding: 0 0;
|
||||
}
|
||||
|
||||
.content li:hover,
|
||||
|
@ -32,9 +32,11 @@
|
|||
}
|
||||
|
||||
.content li a {
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: min-content;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
padding: var(--gap-half) var(--gap);
|
||||
color: var(--dark-gray);
|
||||
}
|
||||
|
||||
.button {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { Button, Link, Text, Popover } from '@geist-ui/core'
|
||||
import FileIcon from '@geist-ui/icons/fileText'
|
||||
import CodeIcon from '@geist-ui/icons/fileLambda'
|
||||
import CodeIcon from '@geist-ui/icons/fileFunction'
|
||||
import styles from './dropdown.module.css'
|
||||
import { useCallback, useEffect, useState } from "react"
|
||||
import { useCallback, useEffect, useRef, useState } from "react"
|
||||
import { codeFileExtensions } from "@lib/constants"
|
||||
import ChevronDown from '@geist-ui/icons/chevronDown'
|
||||
import ShiftBy from "@components/shift-by"
|
||||
|
@ -20,6 +20,16 @@ const FileDropdown = ({
|
|||
}) => {
|
||||
const [expanded, setExpanded] = useState(false)
|
||||
const [items, setItems] = useState<Item[]>([])
|
||||
|
||||
const onOpen = useCallback(() => {
|
||||
setExpanded(true)
|
||||
}, [])
|
||||
|
||||
const onClose = useCallback(() => {
|
||||
setExpanded(false)
|
||||
// contentRef.current?.focus()
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
const newItems = files.map(file => {
|
||||
const extension = file.title.split('.').pop()
|
||||
|
@ -40,22 +50,22 @@ const FileDropdown = ({
|
|||
|
||||
const content = useCallback(() => (<ul className={styles.content}>
|
||||
{items.map(item => (
|
||||
<li key={item.id} onClick={() => setExpanded(false)}>
|
||||
<Link color={false} href={`#${item.title}`}>
|
||||
<li key={item.id} onClick={onClose}>
|
||||
<a href={`#${item.title}`}>
|
||||
<ShiftBy y={5}><span className={styles.fileIcon}>
|
||||
{item.icon}</span></ShiftBy>
|
||||
<span className={styles.fileTitle}>{item.title ? item.title : 'Untitled'}</span>
|
||||
</Link>
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
), [items])
|
||||
), [items, onClose])
|
||||
|
||||
// a list of files with an icon and a title
|
||||
return (
|
||||
<Button auto onClick={() => setExpanded(!expanded)} className={styles.button} iconRight={<ChevronDown />}>
|
||||
<Popover content={content} visible={expanded} trigger="click" hideArrow={true}>
|
||||
{files.length} {files.length === 1 ? 'file' : 'files'}
|
||||
<Button auto onClick={onOpen} className={styles.button} iconRight={<ChevronDown />}>
|
||||
<Popover tabIndex={0} content={content} visible={expanded} trigger="click" hideArrow={true}>
|
||||
Jump to {files.length} {files.length === 1 ? 'file' : 'files'}
|
||||
</Popover>
|
||||
</Button >
|
||||
)
|
||||
|
|
|
@ -54,7 +54,7 @@ const PostPage = ({ post }: Props) => {
|
|||
{/* {!isLoading && <PostFileExplorer files={post.files} />} */}
|
||||
<div className={styles.header}>
|
||||
<span className={styles.title}>
|
||||
<Text h2>{post.title}</Text>
|
||||
<Text h3>{post.title}</Text>
|
||||
<div className={styles.badges}>
|
||||
<VisibilityBadge visibility={post.visibility} />
|
||||
<Badge type="secondary"><Tooltip text={formattedTime}>{time}</Tooltip></Badge>
|
||||
|
|
|
@ -11,9 +11,13 @@
|
|||
align-items: center;
|
||||
}
|
||||
|
||||
.header .title h3 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.header .title .badges > * {
|
||||
margin-left: var(--gap);
|
||||
margin-bottom: var(--gap-quarter);
|
||||
}
|
||||
|
||||
.header .buttons {
|
||||
|
@ -42,6 +46,6 @@
|
|||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
width: 80%;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,13 +14,15 @@ const ScrollToTop = () => {
|
|||
return () => window.removeEventListener('scroll', handleScroll)
|
||||
}, [])
|
||||
|
||||
const onClick = () => {
|
||||
const onClick = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
// blur the button
|
||||
e.currentTarget.blur()
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'row', width: '100%', height: 24, justifyContent: 'flex-end' }}>
|
||||
<Tooltip text="Scroll to Top" className={`${styles['scroll-up']} ${shouldShow ? styles['scroll-up-shown'] : ''}`}>
|
||||
<Tooltip hideArrow text="Scroll to Top" className={`${styles['scroll-up']} ${shouldShow ? styles['scroll-up-shown'] : ''}`}>
|
||||
<Button aria-label='Scroll to Top' onClick={onClick} style={{ background: 'var(--light-gray)' }} auto >
|
||||
<Spacer height={2 / 3} inline width={0} />
|
||||
<ChevronUp />
|
||||
|
|
|
@ -1,141 +1,141 @@
|
|||
export const allowedFileTypes = [
|
||||
'application/json',
|
||||
'application/x-javascript',
|
||||
'application/xhtml+xml',
|
||||
'application/xml',
|
||||
'text/xml',
|
||||
'text/plain',
|
||||
'text/html',
|
||||
'text/csv',
|
||||
'text/tab-separated-values',
|
||||
'text/x-c',
|
||||
'text/x-c++',
|
||||
'text/x-csharp',
|
||||
'text/x-java',
|
||||
'text/x-javascript',
|
||||
'text/x-php',
|
||||
'text/x-python',
|
||||
'text/x-ruby',
|
||||
'text/x-scala',
|
||||
'text/x-swift',
|
||||
'text/x-typescript',
|
||||
'text/x-vb',
|
||||
'text/x-vbscript',
|
||||
'text/x-yaml',
|
||||
'text/x-c++',
|
||||
'text/x-c#',
|
||||
'text/mathml',
|
||||
'text/x-markdown',
|
||||
'text/markdown',
|
||||
"application/json",
|
||||
"application/x-javascript",
|
||||
"application/xhtml+xml",
|
||||
"application/xml",
|
||||
"text/xml",
|
||||
"text/plain",
|
||||
"text/html",
|
||||
"text/csv",
|
||||
"text/tab-separated-values",
|
||||
"text/x-c",
|
||||
"text/x-c++",
|
||||
"text/x-csharp",
|
||||
"text/x-java",
|
||||
"text/x-javascript",
|
||||
"text/x-php",
|
||||
"text/x-python",
|
||||
"text/x-ruby",
|
||||
"text/x-scala",
|
||||
"text/x-swift",
|
||||
"text/x-typescript",
|
||||
"text/x-vb",
|
||||
"text/x-vbscript",
|
||||
"text/x-yaml",
|
||||
"text/x-c++",
|
||||
"text/x-c#",
|
||||
"text/mathml",
|
||||
"text/x-markdown",
|
||||
"text/markdown"
|
||||
]
|
||||
|
||||
export const allowedFileNames = [
|
||||
'Makefile',
|
||||
'README',
|
||||
'Dockerfile',
|
||||
'Jenkinsfile',
|
||||
'LICENSE',
|
||||
'.env',
|
||||
'.gitignore',
|
||||
'.gitattributes',
|
||||
'.env.example',
|
||||
'.env.development',
|
||||
'.env.production',
|
||||
'.env.test',
|
||||
'.env.staging',
|
||||
'.env.development.local',
|
||||
'yarn.lock',
|
||||
'.bash',
|
||||
'.bashrc',
|
||||
'.bash_profile',
|
||||
'.bash_logout',
|
||||
'.profile',
|
||||
'.fish_prompt',
|
||||
'.zshrc',
|
||||
'.zsh',
|
||||
'.zprofile',
|
||||
'go',
|
||||
"Makefile",
|
||||
"README",
|
||||
"Dockerfile",
|
||||
"Jenkinsfile",
|
||||
"LICENSE",
|
||||
".env",
|
||||
".gitignore",
|
||||
".gitattributes",
|
||||
".env.example",
|
||||
".env.development",
|
||||
".env.production",
|
||||
".env.test",
|
||||
".env.staging",
|
||||
".env.development.local",
|
||||
"yarn.lock",
|
||||
".bash",
|
||||
".bashrc",
|
||||
".bash_profile",
|
||||
".bash_logout",
|
||||
".profile",
|
||||
".fish_prompt",
|
||||
".zshrc",
|
||||
".zsh",
|
||||
".zprofile",
|
||||
"go"
|
||||
]
|
||||
|
||||
export const allowedFileExtensions = [
|
||||
'json',
|
||||
'js',
|
||||
'jsx',
|
||||
'ts',
|
||||
'tsx',
|
||||
'c',
|
||||
'cpp',
|
||||
'c++',
|
||||
'c#',
|
||||
'java',
|
||||
'php',
|
||||
'py',
|
||||
'rb',
|
||||
'scala',
|
||||
'swift',
|
||||
'vb',
|
||||
'vbscript',
|
||||
'yaml',
|
||||
'less',
|
||||
'stylus',
|
||||
'styl',
|
||||
'sass',
|
||||
'scss',
|
||||
'lock',
|
||||
'md',
|
||||
'markdown',
|
||||
'txt',
|
||||
'html',
|
||||
'htm',
|
||||
'css',
|
||||
'csv',
|
||||
'log',
|
||||
'sql',
|
||||
'xml',
|
||||
'webmanifest',
|
||||
'vue',
|
||||
'vuex',
|
||||
'rs',
|
||||
'zig',
|
||||
"json",
|
||||
"js",
|
||||
"jsx",
|
||||
"ts",
|
||||
"tsx",
|
||||
"c",
|
||||
"cpp",
|
||||
"c++",
|
||||
"c#",
|
||||
"java",
|
||||
"php",
|
||||
"py",
|
||||
"rb",
|
||||
"scala",
|
||||
"swift",
|
||||
"vb",
|
||||
"vbscript",
|
||||
"yaml",
|
||||
"less",
|
||||
"stylus",
|
||||
"styl",
|
||||
"sass",
|
||||
"scss",
|
||||
"lock",
|
||||
"md",
|
||||
"markdown",
|
||||
"txt",
|
||||
"html",
|
||||
"htm",
|
||||
"css",
|
||||
"csv",
|
||||
"log",
|
||||
"sql",
|
||||
"xml",
|
||||
"webmanifest",
|
||||
"vue",
|
||||
"vuex",
|
||||
"rs",
|
||||
"zig"
|
||||
]
|
||||
|
||||
export const codeFileExtensions = [
|
||||
'json',
|
||||
'js',
|
||||
'jsx',
|
||||
'ts',
|
||||
'tsx',
|
||||
'c',
|
||||
'cpp',
|
||||
'c++',
|
||||
'c#',
|
||||
'java',
|
||||
'php',
|
||||
'py',
|
||||
'rb',
|
||||
'scala',
|
||||
'swift',
|
||||
'vb',
|
||||
'vbscript',
|
||||
'yaml',
|
||||
'less',
|
||||
'stylus',
|
||||
'styl',
|
||||
'sass',
|
||||
'scss',
|
||||
'html',
|
||||
'htm',
|
||||
'css',
|
||||
'asm',
|
||||
's',
|
||||
'sh',
|
||||
'bat',
|
||||
'cmd',
|
||||
'sql',
|
||||
'xml',
|
||||
'rust',
|
||||
'h',
|
||||
'zig',
|
||||
'rs',
|
||||
'go'
|
||||
"json",
|
||||
"js",
|
||||
"jsx",
|
||||
"ts",
|
||||
"tsx",
|
||||
"c",
|
||||
"cpp",
|
||||
"c++",
|
||||
"c#",
|
||||
"java",
|
||||
"php",
|
||||
"py",
|
||||
"rb",
|
||||
"scala",
|
||||
"swift",
|
||||
"vb",
|
||||
"vbscript",
|
||||
"yaml",
|
||||
"less",
|
||||
"stylus",
|
||||
"styl",
|
||||
"sass",
|
||||
"scss",
|
||||
"html",
|
||||
"htm",
|
||||
"css",
|
||||
"asm",
|
||||
"s",
|
||||
"sh",
|
||||
"bat",
|
||||
"cmd",
|
||||
"sql",
|
||||
"xml",
|
||||
"rust",
|
||||
"h",
|
||||
"zig",
|
||||
"rs",
|
||||
"go"
|
||||
]
|
|
@ -11,8 +11,7 @@ const renderMarkdown: NextApiHandler = async (req, res) => {
|
|||
Authorization: `Bearer ${req.cookies["drift-token"]}`
|
||||
}
|
||||
})
|
||||
if (file.status
|
||||
!== 200) {
|
||||
if (file.status !== 200) {
|
||||
return res.status(404).json({ error: "File not found" })
|
||||
}
|
||||
|
||||
|
|
|
@ -16,19 +16,19 @@ app.use("/posts", posts)
|
|||
app.use("/users", users)
|
||||
app.use("/files", files)
|
||||
|
||||
app.get('/welcome', secretKey, (req, res) => {
|
||||
const introContent = process.env.WELCOME_CONTENT;
|
||||
const introTitle = process.env.WELCOME_TITLE;
|
||||
app.get("/welcome", secretKey, (req, res) => {
|
||||
const introContent = process.env.WELCOME_CONTENT
|
||||
const introTitle = process.env.WELCOME_TITLE
|
||||
|
||||
if (!introContent || !introTitle) {
|
||||
return res.status(500).json({ error: 'Missing welcome content' });
|
||||
return res.status(500).json({ error: "Missing welcome content" })
|
||||
}
|
||||
|
||||
return res.json({
|
||||
title: introTitle,
|
||||
content: introContent,
|
||||
rendered: markdown(introContent)
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
app.use(errors())
|
||||
|
|
|
@ -12,8 +12,12 @@ import { Op } from "sequelize"
|
|||
export const posts = Router()
|
||||
|
||||
const postVisibilitySchema = (value: string) => {
|
||||
if (value === "public" || value === "private" ||
|
||||
value === "unlisted" || value === "protected") {
|
||||
if (
|
||||
value === "public" ||
|
||||
value === "private" ||
|
||||
value === "unlisted" ||
|
||||
value === "protected"
|
||||
) {
|
||||
return value
|
||||
} else {
|
||||
throw new Error("Invalid post visibility")
|
||||
|
@ -99,7 +103,9 @@ posts.get("/", secretKey, async (req, res, next) => {
|
|||
}
|
||||
})
|
||||
|
||||
posts.get("/mine", jwt,
|
||||
posts.get(
|
||||
"/mine",
|
||||
jwt,
|
||||
celebrate({
|
||||
query: {
|
||||
page: Joi.number().integer().min(1).default(1).optional()
|
||||
|
@ -124,7 +130,7 @@ posts.get("/mine", jwt,
|
|||
model: File,
|
||||
as: "files",
|
||||
attributes: ["id", "title", "createdAt"]
|
||||
},
|
||||
}
|
||||
],
|
||||
attributes: ["id", "title", "visibility", "createdAt"]
|
||||
}
|
||||
|
@ -134,14 +140,18 @@ posts.get("/mine", jwt,
|
|||
return res.status(404).json({ error: "User not found" })
|
||||
}
|
||||
return res.json(
|
||||
user.posts?.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime()).slice((parsedPage - 1) * 10, parsedPage * 10)
|
||||
user.posts
|
||||
?.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())
|
||||
.slice((parsedPage - 1) * 10, parsedPage * 10)
|
||||
)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
posts.get("/search",
|
||||
posts.get(
|
||||
"/search",
|
||||
jwt,
|
||||
celebrate({
|
||||
query: {
|
||||
|
|
Loading…
Reference in a new issue