diff --git a/.eslintrc.json b/.eslintrc.json
index 1e346ec..d4d0bee 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -1,12 +1,36 @@
{
- "extends": ["next", "next/core-web-vitals"],
+ "extends": [
+ "next",
+ "next/core-web-vitals"
+ ],
"rules": {
- "indent": ["error", 2, { "SwitchCase": 1 }],
- "linebreak-style": ["error", "unix"],
- "quotes": ["error", "single"],
- "semi": ["error", "always"],
- "comma-dangle": ["error", "always-multiline"],
- "jsx-quotes": ["error", "prefer-single"],
+ "indent": [
+ "error",
+ 2,
+ {
+ "SwitchCase": 1
+ }
+ ],
+ "linebreak-style": [
+ "error",
+ "unix"
+ ],
+ "quotes": [
+ "error",
+ "single"
+ ],
+ "semi": [
+ "error",
+ "always"
+ ],
+ "comma-dangle": [
+ "error",
+ "always-multiline"
+ ],
+ "jsx-quotes": [
+ "error",
+ "prefer-single"
+ ],
"react/prop-types": "off",
"react-hooks/rules-of-hooks": "off",
"react-hooks/exhaustive-deps": "off",
@@ -20,6 +44,8 @@
"react/react-in-jsx-scope": "off",
"react/require-render-return": "error",
"react/style-prop-object": "warn",
- "@next/next/no-img-element": "off"
+ "@next/next/no-img-element": "off",
+ "jsx-a11y/alt-text": "off",
+ "react/display-name": "off"
}
}
\ No newline at end of file
diff --git a/src/components/File.tsx b/src/components/File.tsx
index 12a249e..d358744 100644
--- a/src/components/File.tsx
+++ b/src/components/File.tsx
@@ -1,35 +1,49 @@
-import { Button, Card, Group, Image as MImage, Modal, Title } from '@mantine/core';
+import { Button, Card, Grid, Group, Image as MImage, Modal, Stack, Text, Title, useMantineTheme } from '@mantine/core';
import { useClipboard } from '@mantine/hooks';
import { useNotifications } from '@mantine/notifications';
import useFetch from 'hooks/useFetch';
import { useState } from 'react';
-import { CopyIcon, CrossIcon, DeleteIcon, StarIcon } from './icons';
+import Type from './Type';
+import { CalendarIcon, CopyIcon, CrossIcon, DeleteIcon, FileIcon, HashIcon, ImageIcon, StarIcon } from './icons';
+import MutedText from './MutedText';
+
+export function FileMeta({ Icon, title, subtitle }) {
+ return (
+
+
+
+ {title}
+ {subtitle}
+
+
+ );
+}
export default function File({ image, updateImages }) {
const [open, setOpen] = useState(false);
- const [t] = useState(image.mimetype.split('/')[0]);
const notif = useNotifications();
const clipboard = useClipboard();
-
+ const theme = useMantineTheme();
+
const handleDelete = async () => {
const res = await useFetch('/api/user/files', 'DELETE', { id: image.id });
if (!res.error) {
updateImages(true);
notif.showNotification({
- title: 'Image Deleted',
+ title: 'File Deleted',
message: '',
color: 'green',
icon: ,
});
} else {
notif.showNotification({
- title: 'Failed to delete image',
+ title: 'Failed to delete file',
message: res.error,
color: 'red',
icon: ,
});
}
-
+
setOpen(false);
};
@@ -53,13 +67,6 @@ export default function File({ image, updateImages }) {
});
};
- const Type = (props) => {
- return {
- 'video': ,
- 'image': ,
- 'audio': ,
- }[t];
- };
return (
<>
@@ -67,11 +74,27 @@ export default function File({ image, updateImages }) {
opened={open}
onClose={() => setOpen(false)}
title={
{image.file}}
+ size='xl'
+ overlayBlur={3}
+ overlayColor={theme.colorScheme === 'dark' ? theme.colors.dark[6] : 'white'}
>
-
+
+
+
+
+
+
+
+
+
+
@@ -81,8 +104,9 @@ export default function File({ image, updateImages }) {
setOpen(true)}
diff --git a/src/components/ImagesTable.tsx b/src/components/ImagesTable.tsx
index 8a4ec2b..b74ea4b 100644
--- a/src/components/ImagesTable.tsx
+++ b/src/components/ImagesTable.tsx
@@ -1,6 +1,4 @@
-/* eslint-disable jsx-a11y/alt-text */
/* eslint-disable react/jsx-key */
-/* eslint-disable react/display-name */
// Code taken from https://codesandbox.io/s/eojw8 and is modified a bit (the entire src/components/table directory)
import {
ActionIcon,
@@ -86,17 +84,14 @@ export default function ImagesTable({
const getPageRecordInfo = () => {
const firstRowNum = pageIndex * pageSize + 1;
- const totalRows = serverSideDataSource ? total : rows.length;
+ const totalRows = rows.length;
const currLastRowNum = (pageIndex + 1) * pageSize;
let lastRowNum = currLastRowNum < totalRows ? currLastRowNum : totalRows;
return `${firstRowNum} - ${lastRowNum} of ${totalRows}`;
};
- const getPageCount = () => {
- const totalRows = serverSideDataSource ? total : rows.length;
- return Math.ceil(totalRows / pageSize);
- };
+ const getPageCount = () => Math.ceil(rows.length / pageSize);
const handlePageChange = (pageNum) => gotoPage(pageNum - 1);
diff --git a/src/components/Link.tsx b/src/components/Link.tsx
index e45b0dc..596892d 100644
--- a/src/components/Link.tsx
+++ b/src/components/Link.tsx
@@ -1,76 +1,3 @@
-/// https://github.com/mui-org/material-ui/blob/next/examples/nextjs/src/Link.js
-/* eslint-disable jsx-a11y/anchor-has-content */
-import { Text } from '@mantine/core';
-import clsx from 'clsx';
-import NextLink from 'next/link';
-import { useRouter } from 'next/router';
-import { forwardRef } from 'react';
-
-export const NextLinkComposed = forwardRef(function NextLinkComposed(props: any, ref) {
- const { to, linkAs, href, replace, scroll, passHref, shallow, prefetch, locale, ...other } =
- props;
-
- return (
-
-
-
- );
-});
-
-// A styled version of the Next.js Link component:
-// https://nextjs.org/docs/#with-link
-const Link = forwardRef(function Link(props: any, ref) {
- const {
- activeClassName = 'active',
- as: linkAs,
- className: classNameProps,
- href,
- noLinkStyle,
- role, // Link don't have roles.
- ...other
- } = props;
-
- const router = useRouter();
- const pathname = typeof href === 'string' ? href : href.pathname;
- const className = clsx(classNameProps, {
- [activeClassName]: router.pathname === pathname && activeClassName,
- });
-
- const isExternal =
- typeof href === 'string' && (href.indexOf('http') === 0 || href.indexOf('mailto:') === 0);
-
- if (isExternal) {
- if (noLinkStyle) {
- return ;
- }
-
- return ;
- }
-
- if (noLinkStyle) {
- return ;
- }
-
- return (
-
- );
-});
+import { NextLink as Link } from '@mantine/next';
export default Link;
\ No newline at end of file
diff --git a/src/components/Type.tsx b/src/components/Type.tsx
new file mode 100644
index 0000000..be1a958
--- /dev/null
+++ b/src/components/Type.tsx
@@ -0,0 +1,46 @@
+import { Group, Image, Stack, Text } from '@mantine/core';
+import { Prism } from '@mantine/prism';
+import { useEffect, useState } from 'react';
+import { AudioIcon, FileIcon, PlayIcon, TypeIcon, VideoIcon } from './icons';
+import MutedText from './MutedText';
+
+function Placeholder({ text, Icon, ...props }) {
+ return (
+
+
+ {text}
+
+ } {...props} />
+ );
+}
+
+export default function Type({ file, popup = false, ...props }){
+ const type = (file.type || file.mimetype).split('/')[0];
+ const name = (file.name || file.file);
+
+ const [text, setText] = useState('');
+
+ if (type === 'text') {
+ useEffect(() => {
+ (async () => {
+ const res = await fetch('/r/' + name);
+ const text = await res.text();
+
+ setText(text);
+ })();
+ }, []);
+ }
+
+ return popup ? {
+ 'video': ,
+ 'image': ,
+ 'audio': ,
+ 'text': {text},
+ }[type] : {
+ 'video': ,
+ 'image': ,
+ 'audio': ,
+ 'text': ,
+ }[type];
+};
\ No newline at end of file
diff --git a/src/components/dropzone/DropzoneFile.tsx b/src/components/dropzone/DropzoneFile.tsx
index a592bab..82e3546 100644
--- a/src/components/dropzone/DropzoneFile.tsx
+++ b/src/components/dropzone/DropzoneFile.tsx
@@ -1,18 +1,12 @@
-/* eslint-disable jsx-a11y/alt-text */
import React from 'react';
-import { Image, Table, Tooltip, Badge, useMantineTheme } from '@mantine/core';
+import { Table, Tooltip, Badge, useMantineTheme } from '@mantine/core';
+import Type from 'components/Type';
export function FilePreview({ file }: { file: File }) {
- const Type = props => {
- return {
- 'video': ,
- 'image': ,
- 'audio': ,
- }[file.type.split('/')[0]];
- };
-
return (
;
+}
\ No newline at end of file
diff --git a/src/components/icons/CalendarIcon.tsx b/src/components/icons/CalendarIcon.tsx
new file mode 100644
index 0000000..c334f2e
--- /dev/null
+++ b/src/components/icons/CalendarIcon.tsx
@@ -0,0 +1,5 @@
+import { Calendar } from 'react-feather';
+
+export default function CalendarIcon({ ...props }) {
+ return ;
+}
\ No newline at end of file
diff --git a/src/components/icons/HashIcon.tsx b/src/components/icons/HashIcon.tsx
new file mode 100644
index 0000000..25d49fa
--- /dev/null
+++ b/src/components/icons/HashIcon.tsx
@@ -0,0 +1,5 @@
+import { Hash } from 'react-feather';
+
+export default function HashIcon({ ...props }) {
+ return ;
+}
\ No newline at end of file
diff --git a/src/components/icons/PlayIcon.tsx b/src/components/icons/PlayIcon.tsx
new file mode 100644
index 0000000..0820a94
--- /dev/null
+++ b/src/components/icons/PlayIcon.tsx
@@ -0,0 +1,5 @@
+import { Play } from 'react-feather';
+
+export default function PlayIcon({ ...props }) {
+ return ;
+}
\ No newline at end of file
diff --git a/src/components/icons/VideoIcon.tsx b/src/components/icons/VideoIcon.tsx
new file mode 100644
index 0000000..ac6cf63
--- /dev/null
+++ b/src/components/icons/VideoIcon.tsx
@@ -0,0 +1,5 @@
+import { Video } from 'react-feather';
+
+export default function VideoIcon({ ...props }) {
+ return ;
+}
\ No newline at end of file
diff --git a/src/components/icons/index.tsx b/src/components/icons/index.tsx
index c145827..aedd607 100644
--- a/src/components/icons/index.tsx
+++ b/src/components/icons/index.tsx
@@ -16,6 +16,11 @@ import EnterIcon from './EnterIcon';
import PlusIcon from './PlusIcon';
import ImageIcon from './ImageIcon';
import StarIcon from './StarIcon';
+import AudioIcon from './AudioIcon';
+import VideoIcon from './VideoIcon';
+import PlayIcon from './PlayIcon';
+import CalendarIcon from './CalendarIcon';
+import HashIcon from './HashIcon';
export {
ActivityIcon,
@@ -36,4 +41,9 @@ export {
PlusIcon,
ImageIcon,
StarIcon,
+ AudioIcon,
+ VideoIcon,
+ PlayIcon,
+ CalendarIcon,
+ HashIcon,
};
\ No newline at end of file
diff --git a/src/components/pages/UploadText.tsx b/src/components/pages/UploadText.tsx
index bc988cd..2bdb056 100644
--- a/src/components/pages/UploadText.tsx
+++ b/src/components/pages/UploadText.tsx
@@ -42,6 +42,8 @@ export default function Upload() {
req.open('POST', '/api/upload');
req.setRequestHeader('Authorization', user.token);
+ req.setRequestHeader('UploadText', 'true');
+
req.send(body);
};
@@ -51,7 +53,7 @@ export default function Upload() {
setValue(e.target.value.trim())}
+ onChange={e => setValue(e.target.value)}
/>
diff --git a/src/pages/api/upload.ts b/src/pages/api/upload.ts
index 312dbf5..c88a85a 100644
--- a/src/pages/api/upload.ts
+++ b/src/pages/api/upload.ts
@@ -79,7 +79,7 @@ async function handler(req: NextApiReq, res: NextApiRes) {
const image = await prisma.image.create({
data: {
file: `${fileName}.${ext}`,
- mimetype: file.mimetype,
+ mimetype: req.headers.uploadtext ? 'text/plain' : file.mimetype,
userId: user.id,
embed: !!req.headers.embed,
format,
diff --git a/src/pages/api/user/files.ts b/src/pages/api/user/files.ts
index 5a6bec1..810915d 100644
--- a/src/pages/api/user/files.ts
+++ b/src/pages/api/user/files.ts
@@ -79,7 +79,7 @@ async function handler(req: NextApiReq, res: NextApiRes) {
// @ts-ignore
images.map(image => image.url = `/r/${image.file}`);
- if (req.query.filter && req.query.filter === 'media') images = images.filter(x => /^(video|audio|image)/.test(x.mimetype));
+ if (req.query.filter && req.query.filter === 'media') images = images.filter(x => /^(video|audio|image|text)/.test(x.mimetype));
return res.json(req.query.paged ? chunk(images, 16) : images);
}