More uniform home page spacing, close mobile menu on click
This commit is contained in:
parent
a84dad1dde
commit
72633c6ad2
18 changed files with 164 additions and 230 deletions
|
@ -1,5 +1,4 @@
|
||||||
.card {
|
.card {
|
||||||
margin: var(--gap) auto;
|
|
||||||
padding: var(--gap);
|
padding: var(--gap);
|
||||||
border: 1px solid var(--lighter-gray);
|
border: 1px solid var(--lighter-gray);
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
|
|
|
@ -252,7 +252,7 @@ const Post = ({
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ paddingBottom: 200 }}>
|
<div className={styles.root}>
|
||||||
<Title title={title} onChange={onChangeTitle} />
|
<Title title={title} onChange={onChangeTitle} />
|
||||||
<Description description={description} onChange={onChangeDescription} />
|
<Description description={description} onChange={onChangeDescription} />
|
||||||
<FileDropzone setDocs={uploadDocs} />
|
<FileDropzone setDocs={uploadDocs} />
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
.root {
|
||||||
|
padding-bottom: 200px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--gap);
|
||||||
|
}
|
||||||
|
|
||||||
.buttons {
|
.buttons {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -19,7 +26,6 @@
|
||||||
|
|
||||||
.description {
|
.description {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: var(--gap);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 650px) {
|
@media screen and (max-width: 650px) {
|
||||||
|
|
|
@ -23,7 +23,7 @@ type props = {
|
||||||
const Title = ({ onChange, title }: props) => {
|
const Title = ({ onChange, title }: props) => {
|
||||||
return (
|
return (
|
||||||
<div className={styles.title}>
|
<div className={styles.title}>
|
||||||
<h1>Drift</h1>
|
<h1 style={{ margin: 0, padding: 0 }}>Drift</h1>
|
||||||
<Input
|
<Input
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
value={title}
|
value={title}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin-bottom: var(--gap);
|
gap: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 650px) {
|
@media screen and (max-width: 650px) {
|
||||||
|
|
|
@ -10,8 +10,8 @@ type TitleProps = {
|
||||||
loading?: boolean
|
loading?: boolean
|
||||||
displayName?: string
|
displayName?: string
|
||||||
visibility?: string
|
visibility?: string
|
||||||
createdAt?: Date
|
createdAt?: string
|
||||||
expiresAt?: Date
|
expiresAt?: string
|
||||||
authorId?: string
|
authorId?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
.card {
|
.card {
|
||||||
margin: var(--gap) auto;
|
|
||||||
padding: var(--gap);
|
padding: var(--gap);
|
||||||
border: 1px solid var(--light-gray);
|
border: 1px solid var(--light-gray);
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
|
|
|
@ -20,7 +20,7 @@ type Props = {
|
||||||
preview: string
|
preview: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const DownloadButton = ({ rawLink }: { rawLink?: string }) => {
|
const DownloadButtons = ({ rawLink }: { rawLink?: string }) => {
|
||||||
return (
|
return (
|
||||||
<div className={styles.actionWrapper}>
|
<div className={styles.actionWrapper}>
|
||||||
<ButtonGroup className={styles.actions}>
|
<ButtonGroup className={styles.actions}>
|
||||||
|
@ -59,12 +59,6 @@ const Document = ({
|
||||||
skeleton,
|
skeleton,
|
||||||
id
|
id
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const rawLink = () => {
|
|
||||||
if (id) {
|
|
||||||
return `/file/raw/${id}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (skeleton) {
|
if (skeleton) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -98,7 +92,7 @@ const Document = ({
|
||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
<div className={styles.descriptionContainer}>
|
<div className={styles.descriptionContainer}>
|
||||||
<DownloadButton rawLink={rawLink()} />
|
<DownloadButtons rawLink={`/api/file/raw/${id}`} />
|
||||||
<DocumentTabs
|
<DocumentTabs
|
||||||
defaultTab={initialTab}
|
defaultTab={initialTab}
|
||||||
preview={preview}
|
preview={preview}
|
||||||
|
|
|
@ -57,6 +57,7 @@ const getPost = async (id: string) => {
|
||||||
|
|
||||||
if (post.visibility === "protected" && !isAuthorOrAdmin) {
|
if (post.visibility === "protected" && !isAuthorOrAdmin) {
|
||||||
return {
|
return {
|
||||||
|
// TODO: remove this. It's temporary to appease typescript
|
||||||
post: {
|
post: {
|
||||||
visibility: "protected",
|
visibility: "protected",
|
||||||
id: post.id,
|
id: post.id,
|
||||||
|
@ -64,6 +65,7 @@ const getPost = async (id: string) => {
|
||||||
parentId: "",
|
parentId: "",
|
||||||
title: "",
|
title: "",
|
||||||
createdAt: new Date("1970-01-01"),
|
createdAt: new Date("1970-01-01"),
|
||||||
|
expiresAt: new Date("1970-01-01"),
|
||||||
author: {
|
author: {
|
||||||
displayName: "",
|
displayName: "",
|
||||||
},
|
},
|
||||||
|
@ -108,7 +110,8 @@ const PostView = async ({
|
||||||
/>
|
/>
|
||||||
<PostTitle
|
<PostTitle
|
||||||
title={post.title}
|
title={post.title}
|
||||||
createdAt={post.createdAt}
|
createdAt={post.createdAt.toString()}
|
||||||
|
expiresAt={post.expiresAt?.toString()}
|
||||||
displayName={post.author?.displayName || ""}
|
displayName={post.author?.displayName || ""}
|
||||||
visibility={post.visibility}
|
visibility={post.visibility}
|
||||||
authorId={post.authorId}
|
authorId={post.authorId}
|
||||||
|
|
|
@ -45,7 +45,6 @@
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.selectContent {
|
.selectContent {
|
||||||
width: auto;
|
width: auto;
|
||||||
height: 18px;
|
height: 18px;
|
||||||
|
@ -64,6 +63,10 @@
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.contentWrapper {
|
||||||
|
background: var(--bg);
|
||||||
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 768px) {
|
@media only screen and (max-width: 768px) {
|
||||||
.wrapper [data-tab="github"] {
|
.wrapper [data-tab="github"] {
|
||||||
display: none;
|
display: none;
|
||||||
|
@ -71,6 +74,7 @@
|
||||||
|
|
||||||
.mobile {
|
.mobile {
|
||||||
margin-top: var(--gap);
|
margin-top: var(--gap);
|
||||||
|
margin-bottom: var(--gap);
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,6 +83,23 @@
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dropdownItem:not(:first-child):not(:last-child) :global(button) {
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdownItem:first-child :global(button) {
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdownItem:last-child :global(button) {
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.dropdownItem a,
|
.dropdownItem a,
|
||||||
.dropdownItem button {
|
.dropdownItem button {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -22,7 +22,7 @@ import {
|
||||||
} from "react-feather"
|
} from "react-feather"
|
||||||
import * as DropdownMenu from "@radix-ui/react-dropdown-menu"
|
import * as DropdownMenu from "@radix-ui/react-dropdown-menu"
|
||||||
import buttonStyles from "@components/button/button.module.css"
|
import buttonStyles from "@components/button/button.module.css"
|
||||||
import { useEffect, useMemo, useState } from "react"
|
import { useMemo } from "react"
|
||||||
|
|
||||||
type Tab = {
|
type Tab = {
|
||||||
name: string
|
name: string
|
||||||
|
@ -155,17 +155,13 @@ const Header = () => {
|
||||||
]
|
]
|
||||||
}, [isAdmin, resolvedTheme, isSignedIn, setTheme])
|
}, [isAdmin, resolvedTheme, isSignedIn, setTheme])
|
||||||
|
|
||||||
// // TODO: this should not be necessary.
|
|
||||||
// if (!clientHydrated) {
|
|
||||||
// return (
|
|
||||||
// <header>
|
|
||||||
// <div className={styles.tabs}>{getPages(true).map(getButton)}</div>
|
|
||||||
// </header>
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
|
|
||||||
const buttons = pages.map(getButton)
|
const buttons = pages.map(getButton)
|
||||||
|
|
||||||
|
// TODO: this is a hack to close the radix ui menu when a next link is clicked
|
||||||
|
const onClick = () => {
|
||||||
|
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className={clsx(styles.header, {
|
<header className={clsx(styles.header, {
|
||||||
[styles.loading]: isLoading,
|
[styles.loading]: isLoading,
|
||||||
|
@ -188,6 +184,7 @@ const Header = () => {
|
||||||
<DropdownMenu.Item
|
<DropdownMenu.Item
|
||||||
key={button?.key}
|
key={button?.key}
|
||||||
className={styles.dropdownItem}
|
className={styles.dropdownItem}
|
||||||
|
onClick={onClick}
|
||||||
>
|
>
|
||||||
{button}
|
{button}
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
|
|
|
@ -2,15 +2,6 @@ import clsx from "clsx"
|
||||||
import React from "react"
|
import React from "react"
|
||||||
import styles from "./input.module.css"
|
import styles from "./input.module.css"
|
||||||
|
|
||||||
type RequireOnlyOne<T, Keys extends keyof T = keyof T> = Pick<
|
|
||||||
T,
|
|
||||||
Exclude<keyof T, Keys>
|
|
||||||
> &
|
|
||||||
{
|
|
||||||
[K in Keys]-?: Required<Pick<T, K>> &
|
|
||||||
Partial<Record<Exclude<Keys, K>, undefined>>
|
|
||||||
}[Keys]
|
|
||||||
|
|
||||||
type Props = React.HTMLProps<HTMLInputElement> & {
|
type Props = React.HTMLProps<HTMLInputElement> & {
|
||||||
label?: string
|
label?: string
|
||||||
width?: number | string
|
width?: number | string
|
||||||
|
@ -18,8 +9,30 @@ type Props = React.HTMLProps<HTMLInputElement> & {
|
||||||
labelClassName?: string
|
labelClassName?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type InputProps = RequireOnlyOne<Props, "label" | "aria-label">
|
// we have two special rules on top of the props:
|
||||||
|
// if onChange or value is passed, we require both
|
||||||
|
// if label is passed, we forbid aria-label and vice versa
|
||||||
|
type InputProps = Omit<Props, "onChange" | "value" | "label" | "aria-label"> &
|
||||||
|
(
|
||||||
|
| {
|
||||||
|
onChange: Props["onChange"]
|
||||||
|
value: Props["value"]
|
||||||
|
} // if onChange or value is passed, we require both
|
||||||
|
| {
|
||||||
|
onChange?: never
|
||||||
|
value?: never
|
||||||
|
}
|
||||||
|
) &
|
||||||
|
(
|
||||||
|
| {
|
||||||
|
label: Props["label"]
|
||||||
|
"aria-label"?: never
|
||||||
|
} // if label is passed, we forbid aria-label and vice versa
|
||||||
|
| {
|
||||||
|
label?: never
|
||||||
|
"aria-label": Props["aria-label"]
|
||||||
|
}
|
||||||
|
)
|
||||||
// eslint-disable-next-line react/display-name
|
// eslint-disable-next-line react/display-name
|
||||||
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
||||||
({ label, className, width, height, labelClassName, ...props }, ref) => {
|
({ label, className, width, height, labelClassName, ...props }, ref) => {
|
||||||
|
|
|
@ -51,12 +51,6 @@
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 768px) {
|
|
||||||
.wrapper {
|
|
||||||
margin-bottom: var(--gap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.input:disabled {
|
.input:disabled {
|
||||||
background-color: var(--lighter-gray);
|
background-color: var(--lighter-gray);
|
||||||
color: var(--fg);
|
color: var(--fg);
|
||||||
|
|
|
@ -2,14 +2,20 @@
|
||||||
|
|
||||||
import styles from "./post-list.module.css"
|
import styles from "./post-list.module.css"
|
||||||
import ListItem from "./list-item"
|
import ListItem from "./list-item"
|
||||||
import { ChangeEvent, useCallback, useEffect, useState } from "react"
|
import {
|
||||||
import useDebounce from "@lib/hooks/use-debounce"
|
ChangeEvent,
|
||||||
|
useCallback,
|
||||||
|
useDeferredValue,
|
||||||
|
useEffect,
|
||||||
|
useState
|
||||||
|
} from "react"
|
||||||
import Link from "@components/link"
|
import Link from "@components/link"
|
||||||
import type { PostWithFiles } from "@lib/server/prisma"
|
import type { PostWithFiles } from "@lib/server/prisma"
|
||||||
import Input from "@components/input"
|
import Input from "@components/input"
|
||||||
import Button from "@components/button"
|
import Button from "@components/button"
|
||||||
import { useToasts } from "@components/toasts"
|
import { useToasts } from "@components/toasts"
|
||||||
import { ListItemSkeleton } from "./list-item-skeleton"
|
import { ListItemSkeleton } from "./list-item-skeleton"
|
||||||
|
import debounce from "lodash.debounce"
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
initialPosts: string | PostWithFiles[]
|
initialPosts: string | PostWithFiles[]
|
||||||
|
@ -32,8 +38,6 @@ const PostList = ({
|
||||||
const [hasMorePosts, setHasMorePosts] = useState(morePosts)
|
const [hasMorePosts, setHasMorePosts] = useState(morePosts)
|
||||||
const { setToast } = useToasts()
|
const { setToast } = useToasts()
|
||||||
|
|
||||||
const debouncedSearchValue = useDebounce(search, 200)
|
|
||||||
|
|
||||||
const loadMoreClick = useCallback(
|
const loadMoreClick = useCallback(
|
||||||
(e: React.MouseEvent<HTMLButtonElement>) => {
|
(e: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
@ -56,36 +60,30 @@ const PostList = ({
|
||||||
[posts, hasMorePosts]
|
[posts, hasMorePosts]
|
||||||
)
|
)
|
||||||
|
|
||||||
// update posts on search
|
const onSearch = (query: string) => {
|
||||||
useEffect(() => {
|
setSearching(true)
|
||||||
if (debouncedSearchValue) {
|
async function fetchPosts() {
|
||||||
setSearching(true)
|
const res = await fetch(
|
||||||
async function fetchPosts() {
|
`/api/post/search?q=${encodeURIComponent(query)}&userId=${userId}`,
|
||||||
const res = await fetch(
|
{
|
||||||
`/api/post/search?q=${encodeURIComponent(
|
method: "GET",
|
||||||
debouncedSearchValue
|
headers: {
|
||||||
)}&userId=${userId}`,
|
"Content-Type": "application/json"
|
||||||
{
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
const json = await res.json()
|
)
|
||||||
setPosts(json)
|
const json = await res.json()
|
||||||
setSearching(false)
|
setPosts(json)
|
||||||
}
|
setSearching(false)
|
||||||
fetchPosts()
|
|
||||||
} else {
|
|
||||||
setPosts(initialPosts)
|
|
||||||
}
|
}
|
||||||
// TODO: fix cyclical dependency issue
|
fetchPosts()
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
}
|
||||||
}, [debouncedSearchValue, userId])
|
|
||||||
|
const debouncedSearch = debounce(onSearch, 500)
|
||||||
|
|
||||||
const handleSearchChange = (e: ChangeEvent<HTMLInputElement>) => {
|
const handleSearchChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
setSearchValue(e.target.value)
|
setSearchValue(e.target.value)
|
||||||
|
debouncedSearch(e.target.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
const deletePost = useCallback(
|
const deletePost = useCallback(
|
||||||
|
@ -117,10 +115,11 @@ const PostList = ({
|
||||||
disabled={!posts}
|
disabled={!posts}
|
||||||
style={{ maxWidth: 300 }}
|
style={{ maxWidth: 300 }}
|
||||||
aria-label="Search"
|
aria-label="Search"
|
||||||
|
value={search}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{!posts && <p style={{ color: "var(--warning)" }}>Failed to load.</p>}
|
{!posts && <p style={{ color: "var(--warning)" }}>Failed to load.</p>}
|
||||||
{!posts?.length && (
|
{searching && (
|
||||||
<ul>
|
<ul>
|
||||||
<ListItemSkeleton />
|
<ListItemSkeleton />
|
||||||
<ListItemSkeleton />
|
<ListItemSkeleton />
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@next-auth/prisma-adapter": "^1.0.5",
|
"@next-auth/prisma-adapter": "^1.0.5",
|
||||||
"@next/eslint-plugin-next": "13.0.5-canary.3",
|
"@next/eslint-plugin-next": "13.0.5-canary.3",
|
||||||
"@prisma/client": "^4.6.1",
|
"@prisma/client": "^4.7.1",
|
||||||
"@radix-ui/react-dialog": "^1.0.2",
|
"@radix-ui/react-dialog": "^1.0.2",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.0.1",
|
"@radix-ui/react-dropdown-menu": "^2.0.1",
|
||||||
"@radix-ui/react-popover": "^1.0.2",
|
"@radix-ui/react-popover": "^1.0.2",
|
||||||
|
@ -26,6 +26,7 @@
|
||||||
"bcrypt": "^5.1.0",
|
"bcrypt": "^5.1.0",
|
||||||
"client-zip": "2.2.1",
|
"client-zip": "2.2.1",
|
||||||
"jest": "^29.3.1",
|
"jest": "^29.3.1",
|
||||||
|
"lodash.debounce": "^4.0.8",
|
||||||
"next": "13.0.7-canary.1",
|
"next": "13.0.7-canary.1",
|
||||||
"next-auth": "^4.18.0",
|
"next-auth": "^4.18.0",
|
||||||
"prisma": "^4.7.1",
|
"prisma": "^4.7.1",
|
||||||
|
@ -41,6 +42,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@next/bundle-analyzer": "12.1.6",
|
"@next/bundle-analyzer": "12.1.6",
|
||||||
"@types/bcrypt": "^5.0.0",
|
"@types/bcrypt": "^5.0.0",
|
||||||
|
"@types/lodash.debounce": "^4.0.7",
|
||||||
"@types/node": "17.0.23",
|
"@types/node": "17.0.23",
|
||||||
"@types/react": "18.0.9",
|
"@types/react": "18.0.9",
|
||||||
"@types/react-datepicker": "4.4.1",
|
"@types/react-datepicker": "4.4.1",
|
||||||
|
|
|
@ -4,13 +4,14 @@ specifiers:
|
||||||
'@next-auth/prisma-adapter': ^1.0.5
|
'@next-auth/prisma-adapter': ^1.0.5
|
||||||
'@next/bundle-analyzer': 12.1.6
|
'@next/bundle-analyzer': 12.1.6
|
||||||
'@next/eslint-plugin-next': 13.0.5-canary.3
|
'@next/eslint-plugin-next': 13.0.5-canary.3
|
||||||
'@prisma/client': ^4.6.1
|
'@prisma/client': ^4.7.1
|
||||||
'@radix-ui/react-dialog': ^1.0.2
|
'@radix-ui/react-dialog': ^1.0.2
|
||||||
'@radix-ui/react-dropdown-menu': ^2.0.1
|
'@radix-ui/react-dropdown-menu': ^2.0.1
|
||||||
'@radix-ui/react-popover': ^1.0.2
|
'@radix-ui/react-popover': ^1.0.2
|
||||||
'@radix-ui/react-tabs': ^1.0.1
|
'@radix-ui/react-tabs': ^1.0.1
|
||||||
'@radix-ui/react-tooltip': ^1.0.2
|
'@radix-ui/react-tooltip': ^1.0.2
|
||||||
'@types/bcrypt': ^5.0.0
|
'@types/bcrypt': ^5.0.0
|
||||||
|
'@types/lodash.debounce': ^4.0.7
|
||||||
'@types/node': 17.0.23
|
'@types/node': 17.0.23
|
||||||
'@types/react': 18.0.9
|
'@types/react': 18.0.9
|
||||||
'@types/react-datepicker': 4.4.1
|
'@types/react-datepicker': 4.4.1
|
||||||
|
@ -25,6 +26,7 @@ specifiers:
|
||||||
eslint: 8.27.0
|
eslint: 8.27.0
|
||||||
eslint-config-next: 13.0.3
|
eslint-config-next: 13.0.3
|
||||||
jest: ^29.3.1
|
jest: ^29.3.1
|
||||||
|
lodash.debounce: ^4.0.8
|
||||||
next: 13.0.7-canary.1
|
next: 13.0.7-canary.1
|
||||||
next-auth: ^4.18.0
|
next-auth: ^4.18.0
|
||||||
next-unused: 0.0.6
|
next-unused: 0.0.6
|
||||||
|
@ -43,9 +45,9 @@ specifiers:
|
||||||
typescript-plugin-css-modules: 3.4.0
|
typescript-plugin-css-modules: 3.4.0
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
'@next-auth/prisma-adapter': 1.0.5_qwexivae5olc6wqfcmxswm7qjy
|
'@next-auth/prisma-adapter': 1.0.5_hpttyne5hky44pj2anoxcmv4zm
|
||||||
'@next/eslint-plugin-next': 13.0.5-canary.3
|
'@next/eslint-plugin-next': 13.0.5-canary.3
|
||||||
'@prisma/client': 4.6.1_prisma@4.7.1
|
'@prisma/client': 4.7.1_prisma@4.7.1
|
||||||
'@radix-ui/react-dialog': 1.0.2_jbvntnid6ohjelon6ccj5dhg2u
|
'@radix-ui/react-dialog': 1.0.2_jbvntnid6ohjelon6ccj5dhg2u
|
||||||
'@radix-ui/react-dropdown-menu': 2.0.1_jbvntnid6ohjelon6ccj5dhg2u
|
'@radix-ui/react-dropdown-menu': 2.0.1_jbvntnid6ohjelon6ccj5dhg2u
|
||||||
'@radix-ui/react-popover': 1.0.2_jbvntnid6ohjelon6ccj5dhg2u
|
'@radix-ui/react-popover': 1.0.2_jbvntnid6ohjelon6ccj5dhg2u
|
||||||
|
@ -56,6 +58,7 @@ dependencies:
|
||||||
bcrypt: 5.1.0
|
bcrypt: 5.1.0
|
||||||
client-zip: 2.2.1
|
client-zip: 2.2.1
|
||||||
jest: 29.3.1_@types+node@17.0.23
|
jest: 29.3.1_@types+node@17.0.23
|
||||||
|
lodash.debounce: 4.0.8
|
||||||
next: 13.0.7-canary.1_biqbaboplfbrettd7655fr4n2y
|
next: 13.0.7-canary.1_biqbaboplfbrettd7655fr4n2y
|
||||||
next-auth: 4.18.0_ihvxcpofhpc4k2aqfys2drrlkq
|
next-auth: 4.18.0_ihvxcpofhpc4k2aqfys2drrlkq
|
||||||
prisma: 4.7.1
|
prisma: 4.7.1
|
||||||
|
@ -74,6 +77,7 @@ optionalDependencies:
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@next/bundle-analyzer': 12.1.6
|
'@next/bundle-analyzer': 12.1.6
|
||||||
'@types/bcrypt': 5.0.0
|
'@types/bcrypt': 5.0.0
|
||||||
|
'@types/lodash.debounce': 4.0.7
|
||||||
'@types/node': 17.0.23
|
'@types/node': 17.0.23
|
||||||
'@types/react': 18.0.9
|
'@types/react': 18.0.9
|
||||||
'@types/react-datepicker': 4.4.1_biqbaboplfbrettd7655fr4n2y
|
'@types/react-datepicker': 4.4.1_biqbaboplfbrettd7655fr4n2y
|
||||||
|
@ -785,13 +789,13 @@ packages:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@next-auth/prisma-adapter/1.0.5_qwexivae5olc6wqfcmxswm7qjy:
|
/@next-auth/prisma-adapter/1.0.5_hpttyne5hky44pj2anoxcmv4zm:
|
||||||
resolution: {integrity: sha512-VqMS11IxPXrPGXw6Oul6jcyS/n8GLOWzRMrPr3EMdtD6eOalM6zz05j08PcNiis8QzkfuYnCv49OvufTuaEwYQ==}
|
resolution: {integrity: sha512-VqMS11IxPXrPGXw6Oul6jcyS/n8GLOWzRMrPr3EMdtD6eOalM6zz05j08PcNiis8QzkfuYnCv49OvufTuaEwYQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@prisma/client': '>=2.26.0 || >=3'
|
'@prisma/client': '>=2.26.0 || >=3'
|
||||||
next-auth: ^4
|
next-auth: ^4
|
||||||
dependencies:
|
dependencies:
|
||||||
'@prisma/client': 4.6.1_prisma@4.7.1
|
'@prisma/client': 4.7.1_prisma@4.7.1
|
||||||
next-auth: 4.18.0_ihvxcpofhpc4k2aqfys2drrlkq
|
next-auth: 4.18.0_ihvxcpofhpc4k2aqfys2drrlkq
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
@ -969,8 +973,8 @@ packages:
|
||||||
/@popperjs/core/2.11.6:
|
/@popperjs/core/2.11.6:
|
||||||
resolution: {integrity: sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==}
|
resolution: {integrity: sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==}
|
||||||
|
|
||||||
/@prisma/client/4.6.1_prisma@4.7.1:
|
/@prisma/client/4.7.1_prisma@4.7.1:
|
||||||
resolution: {integrity: sha512-M1+NNrMzqaOIxT7PBGcTs3IZo7d1EW/+gVQd4C4gUgWBDGgD9AcIeZnUSidgWClmpMSgVUdnVORjsWWGUameYA==}
|
resolution: {integrity: sha512-/GbnOwIPtjiveZNUzGXOdp7RxTEkHL4DZP3vBaFNadfr6Sf0RshU5EULFzVaSi9i9PIK9PYd+1Rn7z2B2npb9w==}
|
||||||
engines: {node: '>=14.17'}
|
engines: {node: '>=14.17'}
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -979,12 +983,12 @@ packages:
|
||||||
prisma:
|
prisma:
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@prisma/engines-version': 4.6.1-3.694eea289a8462c80264df36757e4fdc129b1b32
|
'@prisma/engines-version': 4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c
|
||||||
prisma: 4.7.1
|
prisma: 4.7.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@prisma/engines-version/4.6.1-3.694eea289a8462c80264df36757e4fdc129b1b32:
|
/@prisma/engines-version/4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c:
|
||||||
resolution: {integrity: sha512-HUCmkXAU2jqp2O1RvNtbE+seLGLyJGEABZS/R38rZjSAafAy0WzBuHq+tbZMnD+b5OSCsTVtIPVcuvx1ySxcWQ==}
|
resolution: {integrity: sha512-Bd4LZ+WAnUHOq31e9X/ihi5zPlr4SzTRwUZZYxvWOxlerIZ7HJlVa9zXpuKTKLpI9O1l8Ec4OYCKsivWCs5a3Q==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@prisma/engines/4.7.1:
|
/@prisma/engines/4.7.1:
|
||||||
|
@ -1519,6 +1523,16 @@ packages:
|
||||||
resolution: {integrity: sha512-DUlIj2nk0YnJdlWgsFuVKcX27MLW0KbKmGVoUHmFr+74FYYNUDAaj9ZqTADvsbE8rfxuVmSFc7KczYn5Y09ozg==}
|
resolution: {integrity: sha512-DUlIj2nk0YnJdlWgsFuVKcX27MLW0KbKmGVoUHmFr+74FYYNUDAaj9ZqTADvsbE8rfxuVmSFc7KczYn5Y09ozg==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@types/lodash.debounce/4.0.7:
|
||||||
|
resolution: {integrity: sha512-X1T4wMZ+gT000M2/91SYj0d/7JfeNZ9PeeOldSNoE/lunLeQXKvkmIumI29IaKMotU/ln/McOIvgzZcQ/3TrSA==}
|
||||||
|
dependencies:
|
||||||
|
'@types/lodash': 4.14.191
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@types/lodash/4.14.191:
|
||||||
|
resolution: {integrity: sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/mdast/3.0.10:
|
/@types/mdast/3.0.10:
|
||||||
resolution: {integrity: sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==}
|
resolution: {integrity: sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -4584,6 +4598,10 @@ packages:
|
||||||
resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==}
|
resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/lodash.debounce/4.0.8:
|
||||||
|
resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/lodash.memoize/4.1.2:
|
/lodash.memoize/4.1.2:
|
||||||
resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==}
|
resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
|
@ -1,109 +0,0 @@
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "SequelizeMeta" (
|
|
||||||
"name" TEXT NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "SequelizeMeta_pkey" PRIMARY KEY ("name")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "files" (
|
|
||||||
"id" TEXT NOT NULL,
|
|
||||||
"title" TEXT NOT NULL,
|
|
||||||
"content" TEXT NOT NULL,
|
|
||||||
"sha" TEXT NOT NULL,
|
|
||||||
"html" TEXT NOT NULL,
|
|
||||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
||||||
"deletedAt" TIMESTAMP(3),
|
|
||||||
"userId" TEXT NOT NULL,
|
|
||||||
"postId" TEXT NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "files_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "posts" (
|
|
||||||
"id" TEXT NOT NULL,
|
|
||||||
"title" TEXT NOT NULL,
|
|
||||||
"visibility" TEXT NOT NULL,
|
|
||||||
"password" TEXT,
|
|
||||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
||||||
"deletedAt" TIMESTAMP(3),
|
|
||||||
"expiresAt" TIMESTAMP(3),
|
|
||||||
"parentId" TEXT,
|
|
||||||
"description" TEXT,
|
|
||||||
"authorId" TEXT NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "posts_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "accounts" (
|
|
||||||
"id" TEXT NOT NULL,
|
|
||||||
"userId" TEXT NOT NULL,
|
|
||||||
"type" TEXT NOT NULL,
|
|
||||||
"provider" TEXT NOT NULL,
|
|
||||||
"providerAccountId" TEXT NOT NULL,
|
|
||||||
"refresh_token" TEXT,
|
|
||||||
"access_token" TEXT,
|
|
||||||
"expires_at" INTEGER,
|
|
||||||
"token_type" TEXT,
|
|
||||||
"scope" TEXT,
|
|
||||||
"id_token" TEXT,
|
|
||||||
"session_state" TEXT,
|
|
||||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"refresh_token_expires_in" INTEGER,
|
|
||||||
|
|
||||||
CONSTRAINT "accounts_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "Session" (
|
|
||||||
"id" TEXT NOT NULL,
|
|
||||||
"sessionToken" TEXT NOT NULL,
|
|
||||||
"userId" TEXT NOT NULL,
|
|
||||||
"expires" TIMESTAMP(3) NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "Session_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "users" (
|
|
||||||
"id" TEXT NOT NULL,
|
|
||||||
"name" TEXT,
|
|
||||||
"email" TEXT,
|
|
||||||
"emailVerified" TIMESTAMP(3),
|
|
||||||
"image" TEXT,
|
|
||||||
"username" TEXT,
|
|
||||||
"role" TEXT DEFAULT 'user',
|
|
||||||
"password" TEXT,
|
|
||||||
|
|
||||||
CONSTRAINT "users_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "verification_tokens" (
|
|
||||||
"identifier" TEXT NOT NULL,
|
|
||||||
"token" TEXT NOT NULL,
|
|
||||||
"expires" TIMESTAMP(3) NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "accounts_provider_providerAccountId_key" ON "accounts"("provider", "providerAccountId");
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "Session_sessionToken_key" ON "Session"("sessionToken");
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "users_email_key" ON "users"("email");
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "users_username_key" ON "users"("username");
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "verification_tokens_token_key" ON "verification_tokens"("token");
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "verification_tokens_identifier_token_key" ON "verification_tokens"("identifier", "token");
|
|
|
@ -1,15 +1,14 @@
|
||||||
generator client {
|
generator client {
|
||||||
provider = "prisma-client-js"
|
provider = "prisma-client-js"
|
||||||
previewFeatures = ["referentialIntegrity", "fullTextSearch"]
|
previewFeatures = ["fullTextSearch"]
|
||||||
}
|
}
|
||||||
|
|
||||||
datasource db {
|
datasource db {
|
||||||
provider = "postgresql"
|
provider = "postgresql"
|
||||||
url = env("DATABASE_URL")
|
url = env("DATABASE_URL")
|
||||||
referentialIntegrity = "prisma"
|
relationMode = "prisma"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
model SequelizeMeta {
|
model SequelizeMeta {
|
||||||
name String @id
|
name String @id
|
||||||
}
|
}
|
||||||
|
@ -18,7 +17,7 @@ model File {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
title String
|
title String
|
||||||
content String
|
content String
|
||||||
sha String
|
sha String
|
||||||
html String
|
html String
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
|
@ -27,6 +26,7 @@ model File {
|
||||||
postId String
|
postId String
|
||||||
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
|
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
@@index([postId, userId, id])
|
||||||
@@map("files")
|
@@map("files")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,37 +41,35 @@ model Post {
|
||||||
expiresAt DateTime?
|
expiresAt DateTime?
|
||||||
parentId String?
|
parentId String?
|
||||||
description String?
|
description String?
|
||||||
author User? @relation(fields: [authorId], references: [id])
|
|
||||||
authorId String
|
authorId String
|
||||||
files File[]
|
files File[]
|
||||||
|
author User? @relation(fields: [authorId], references: [id])
|
||||||
|
|
||||||
|
@@index([authorId, id])
|
||||||
@@map("posts")
|
@@map("posts")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next auth stuff, from https://next-auth.js.org/adapters/prisma
|
|
||||||
|
|
||||||
model Account {
|
model Account {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
userId String
|
userId String
|
||||||
type String
|
type String
|
||||||
provider String
|
provider String
|
||||||
providerAccountId String
|
providerAccountId String
|
||||||
refresh_token String? @db.Text
|
refresh_token String?
|
||||||
access_token String? @db.Text
|
access_token String?
|
||||||
expires_at Int?
|
expires_at Int?
|
||||||
token_type String?
|
token_type String?
|
||||||
scope String?
|
scope String?
|
||||||
id_token String? @db.Text
|
id_token String?
|
||||||
session_state String?
|
session_state String?
|
||||||
createdAt DateTime @default(now()) @map(name: "created_at")
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
updatedAt DateTime @default(now()) @map(name: "updated_at")
|
updatedAt DateTime @default(now()) @map("updated_at")
|
||||||
// https://next-auth.js.org/providers/github
|
|
||||||
refresh_token_expires_in Int?
|
refresh_token_expires_in Int?
|
||||||
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
||||||
|
|
||||||
@@unique([provider, providerAccountId])
|
@@unique([provider, providerAccountId])
|
||||||
@@map(name: "accounts")
|
@@index([userId, providerAccountId], map: "accounts_provider_account_id")
|
||||||
|
@@map("accounts")
|
||||||
}
|
}
|
||||||
|
|
||||||
model Session {
|
model Session {
|
||||||
|
@ -80,6 +78,9 @@ model Session {
|
||||||
userId String
|
userId String
|
||||||
expires DateTime
|
expires DateTime
|
||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
@@index([userId, expires], map: "sessions_user_id_expires")
|
||||||
|
@@map("sessions")
|
||||||
}
|
}
|
||||||
|
|
||||||
model User {
|
model User {
|
||||||
|
@ -88,16 +89,13 @@ model User {
|
||||||
email String? @unique
|
email String? @unique
|
||||||
emailVerified DateTime?
|
emailVerified DateTime?
|
||||||
image String?
|
image String?
|
||||||
|
role String? @default("user")
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
|
displayName String?
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
|
posts Post[]
|
||||||
accounts Account[]
|
accounts Account[]
|
||||||
sessions Session[]
|
sessions Session[]
|
||||||
|
|
||||||
// custom fields
|
|
||||||
posts Post[]
|
|
||||||
role String? @default("user")
|
|
||||||
displayName String?
|
|
||||||
|
|
||||||
@@map("users")
|
@@map("users")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue