mirror of
https://github.com/diced/zipline.git
synced 2025-05-11 18:36:02 +02:00
delete images, new image viewer, etc
This commit is contained in:
parent
239dadaefd
commit
961b2721c8
8 changed files with 183 additions and 17 deletions
BIN
favicon.ico
BIN
favicon.ico
Binary file not shown.
Before Width: | Height: | Size: 279 KiB After Width: | Height: | Size: 279 KiB |
Binary file not shown.
Before Width: | Height: | Size: 20 KiB |
Binary file not shown.
Before Width: | Height: | Size: 21 KiB |
Binary file not shown.
Before Width: | Height: | Size: 12 KiB |
Binary file not shown.
Before Width: | Height: | Size: 12 KiB |
|
@ -4,6 +4,7 @@ import {
|
|||
FastifyInstanceToken,
|
||||
Inject,
|
||||
GET,
|
||||
DELETE
|
||||
} from 'fastify-decorators';
|
||||
import { Repository } from 'typeorm';
|
||||
import { Image } from '../entities/Image';
|
||||
|
@ -21,8 +22,41 @@ export class ImagesController {
|
|||
|
||||
private images: Repository<Image> = this.instance.orm.getRepository(Image);
|
||||
|
||||
@GET('/')
|
||||
async allImages(req: FastifyRequest, reply: FastifyReply) {
|
||||
if (!req.cookies.zipline) throw new LoginError('Not logged in.');
|
||||
|
||||
const images = await this.images.find({
|
||||
where: {
|
||||
user: readBaseCookie(req.cookies.zipline),
|
||||
},
|
||||
});
|
||||
|
||||
return reply.send(images);
|
||||
}
|
||||
|
||||
@DELETE('/:id')
|
||||
async deleteImage(req: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply) {
|
||||
if (!req.cookies.zipline) throw new LoginError('Not logged in.');
|
||||
|
||||
const image = await this.images.findOne({
|
||||
where: {
|
||||
user: readBaseCookie(req.cookies.zipline),
|
||||
id: req.params.id
|
||||
}
|
||||
});
|
||||
|
||||
if (!image) throw new Error('No image');
|
||||
|
||||
this.images.delete({
|
||||
id: req.params.id
|
||||
});
|
||||
|
||||
return reply.send(image);
|
||||
}
|
||||
|
||||
@GET('/recent')
|
||||
async loginStatus(req: FastifyRequest, reply: FastifyReply) {
|
||||
async recentImages(req: FastifyRequest, reply: FastifyReply) {
|
||||
if (!req.cookies.zipline) throw new LoginError('Not logged in.');
|
||||
|
||||
const images = await this.images.find({
|
||||
|
@ -33,4 +67,24 @@ export class ImagesController {
|
|||
|
||||
return reply.send(images.slice(1).slice(-3).reverse());
|
||||
}
|
||||
|
||||
@GET('/chunk')
|
||||
async pages(req: FastifyRequest, reply: FastifyReply) {
|
||||
if (!req.cookies.zipline) throw new LoginError('Not logged in.');
|
||||
|
||||
const images = await this.images.find({
|
||||
where: {
|
||||
user: readBaseCookie(req.cookies.zipline),
|
||||
},
|
||||
});
|
||||
|
||||
function chunk(array: Image[], size: number) {
|
||||
if (!array) return [];
|
||||
const f = array.slice(0, size);
|
||||
if (!f.length) return array;
|
||||
return [f].concat(chunk(array.slice(size, array.length), size));
|
||||
}
|
||||
const chunks = chunk(images, 20);
|
||||
return reply.send(chunks);
|
||||
}
|
||||
}
|
||||
|
|
115
src/pages/images.tsx
Normal file
115
src/pages/images.tsx
Normal file
|
@ -0,0 +1,115 @@
|
|||
import React from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
import Card from '@material-ui/core/Card';
|
||||
import GridList from '@material-ui/core/GridList';
|
||||
import GridListTile from '@material-ui/core/GridListTile';
|
||||
import Popover from '@material-ui/core/Popover';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import Pagination from '@material-ui/lab/Pagination';
|
||||
import DeleteIcon from '@material-ui/icons/Delete';
|
||||
import UI from '../components/UI';
|
||||
import UIPlaceholder from '../components/UIPlaceholder';
|
||||
import { makeStyles } from '@material-ui/core';
|
||||
import { store } from '../lib/store';
|
||||
import { Image } from '../entities/Image';
|
||||
import { ConfigUploader } from '../lib/Config';
|
||||
|
||||
const useStyles = makeStyles({
|
||||
margin: {
|
||||
margin: '5px',
|
||||
},
|
||||
padding: {
|
||||
padding: '10px',
|
||||
},
|
||||
});
|
||||
|
||||
export default function Images({ config }: { config: ConfigUploader }) {
|
||||
const classes = useStyles();
|
||||
const router = useRouter();
|
||||
const state = store.getState();
|
||||
const [chunks, setChunks] = React.useState([]);
|
||||
const [images, setImages] = React.useState<Image[]>([]);
|
||||
const [selectedImage, setSelectedImage] = React.useState<Image>(null);
|
||||
const [anchorEl, setAnchorEl] = React.useState(null);
|
||||
|
||||
if (typeof window === 'undefined') return <UIPlaceholder />;
|
||||
if (!state.loggedIn) router.push('/login');
|
||||
else {
|
||||
|
||||
const getChunkedImages = async () => {
|
||||
const chunks = await (await fetch('/api/images/chunk')).json();
|
||||
console.log(chunks);
|
||||
if (!chunks.error) setChunks(chunks);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
getChunkedImages();
|
||||
changePage(null, 1);
|
||||
}, []);
|
||||
|
||||
const changePage = (event, p: number) => {
|
||||
const page = chunks[p - 1];
|
||||
if (page) setImages(page);
|
||||
};
|
||||
|
||||
const setImageOpenPopover = (e, d: Image) => {
|
||||
setAnchorEl(e.currentTarget);
|
||||
setSelectedImage(d);
|
||||
};
|
||||
|
||||
const handleDeleteImage = async () => {
|
||||
setAnchorEl(null);
|
||||
if (!selectedImage) return;
|
||||
const d = await (await fetch(`/api/images/${selectedImage.id}`, {
|
||||
method: 'DELETE'
|
||||
})).json();
|
||||
if (!d.error) {
|
||||
getChunkedImages();
|
||||
changePage(null, 1);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<UI>
|
||||
<Card elevation={3} className={classes.padding}>
|
||||
<GridList cols={3}>
|
||||
{images.map(d => {
|
||||
const t = new URL(window.location.href);
|
||||
t.pathname = `${config ? config.route : '/u'}/${d.file}`;
|
||||
return (
|
||||
<GridListTile key={d.id} cols={1}>
|
||||
<img src={t.toString()} onClick={(e) => setImageOpenPopover(e, d)} />
|
||||
</GridListTile>
|
||||
);
|
||||
})}
|
||||
</GridList>
|
||||
<Pagination count={chunks.length} onChange={changePage} />
|
||||
</Card>
|
||||
<Popover
|
||||
open={Boolean(anchorEl)}
|
||||
anchorEl={anchorEl}
|
||||
anchorOrigin={{
|
||||
vertical: 'center',
|
||||
horizontal: 'center',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'center',
|
||||
horizontal: 'center',
|
||||
}}
|
||||
onClose={() => setAnchorEl(null)}
|
||||
disableRestoreFocus
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
startIcon={<DeleteIcon />}
|
||||
onClick={handleDeleteImage}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</Popover>
|
||||
</UI >
|
||||
);
|
||||
}
|
||||
return <UIPlaceholder />;
|
||||
}
|
|
@ -11,6 +11,8 @@ import { makeStyles } from '@material-ui/core';
|
|||
import { store } from '../lib/store';
|
||||
import { Image } from '../entities/Image';
|
||||
import { ConfigUploader } from '../lib/Config';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { UPDATE_USER, LOGOUT } from '../lib/reducer';
|
||||
|
||||
const useStyles = makeStyles({
|
||||
margin: {
|
||||
|
@ -25,15 +27,19 @@ export default function Index({ config }: { config: ConfigUploader }) {
|
|||
const classes = useStyles();
|
||||
const router = useRouter();
|
||||
const state = store.getState();
|
||||
const [recentImages, setRecentImages] = React.useState([]);
|
||||
const [images, setImages] = React.useState([]);
|
||||
|
||||
if (typeof window !== 'undefined' && !state.loggedIn) router.push('/login');
|
||||
if (typeof window === 'undefined') return <UIPlaceholder />;
|
||||
if (!state.loggedIn) router.push('/login');
|
||||
else {
|
||||
|
||||
React.useEffect(() => {
|
||||
(async () => {
|
||||
const images = await (await fetch('/api/images/recent')).json();
|
||||
if (!images.error) setImages(images);
|
||||
const recentImages = await (await fetch('/api/images/recent')).json();
|
||||
if (!recentImages.error) setRecentImages(recentImages);
|
||||
|
||||
const allImages = await (await fetch('/api/images')).json();
|
||||
if (!allImages.error) setImages(allImages);
|
||||
})();
|
||||
}, []);
|
||||
|
||||
|
@ -44,13 +50,13 @@ export default function Index({ config }: { config: ConfigUploader }) {
|
|||
Welcome back, {state.user.username}
|
||||
</Typography>
|
||||
<Typography color='textSecondary'>
|
||||
You have <b>2</b> images
|
||||
You have <b>{images.length}</b> images
|
||||
</Typography>
|
||||
<Typography variant='h5'>
|
||||
Recent Images
|
||||
</Typography>
|
||||
<GridList cols={3}>
|
||||
{images.map(d => {
|
||||
{recentImages.map(d => {
|
||||
const t = new URL(window.location.href);
|
||||
t.pathname = `${config ? config.route : '/u'}/${d.file}`;
|
||||
return (
|
||||
|
@ -69,13 +75,4 @@ export default function Index({ config }: { config: ConfigUploader }) {
|
|||
);
|
||||
}
|
||||
return <UIPlaceholder />;
|
||||
}
|
||||
|
||||
// Index.getInitialProps = async ({ req }) => {
|
||||
// console.log(req);
|
||||
// const baseUrl = req ? `http://${req.headers.host}` : '';
|
||||
// const images = await (await fetch(baseUrl + '/api/images/recent')).json();
|
||||
// const config = await (await fetch(baseUrl + '/api/config/uploader')).json();
|
||||
// if (images.error || config.error) return { images: [], config: null };
|
||||
// return { images, config };
|
||||
// };
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue