mirror of https://github.com/renzynx/bliss.git
improve files upload ui, and files fetching
This commit is contained in:
parent
958ec11366
commit
541ee30239
|
@ -71,6 +71,7 @@ export class UsersController {
|
|||
(req.session as CustomSession).userId,
|
||||
{
|
||||
skip: +skip,
|
||||
// @ts-ignore
|
||||
take: take === "all" ? "all" : +take,
|
||||
currentPage: +currentPage,
|
||||
sort,
|
||||
|
|
|
@ -365,7 +365,7 @@ export class UsersService implements IUserService {
|
|||
search = "",
|
||||
}: {
|
||||
skip: number;
|
||||
take: number | "all";
|
||||
take: number;
|
||||
currentPage: number;
|
||||
sort?: string;
|
||||
search?: string;
|
||||
|
@ -375,36 +375,13 @@ export class UsersService implements IUserService {
|
|||
if (!user) {
|
||||
throw new UnauthorizedException("not authorized");
|
||||
}
|
||||
const total = await this.prisma.file.count({
|
||||
where: {
|
||||
userId: id,
|
||||
},
|
||||
});
|
||||
const total = await this.prisma.file.count({ where: { userId: id } });
|
||||
|
||||
if (take === "all") {
|
||||
const files = await this.prisma.file.findMany({
|
||||
where: {
|
||||
userId: id,
|
||||
filename: {
|
||||
contains: search,
|
||||
},
|
||||
slug: {
|
||||
contains: search,
|
||||
},
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: sort === "newest" ? "asc" : "desc",
|
||||
},
|
||||
});
|
||||
let finalSkip!: number;
|
||||
let finalTake!: number;
|
||||
|
||||
return {
|
||||
files,
|
||||
totalFiles: total,
|
||||
pages: 1,
|
||||
};
|
||||
}
|
||||
|
||||
const pages = Math.ceil(total / take);
|
||||
// @ts-ignore
|
||||
const pages = take === "all" ? 1 : Math.ceil(total / take);
|
||||
|
||||
//[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
|
||||
// ^^
|
||||
|
@ -412,10 +389,8 @@ export class UsersService implements IUserService {
|
|||
// 1 to 10 is page 1
|
||||
// total = 20, take = 10, skip = 0
|
||||
|
||||
let finalSkip!: number;
|
||||
let finalTake!: number;
|
||||
|
||||
if (sort === "newest") {
|
||||
// @ts-ignore
|
||||
if (sort === "newest" && take !== "all") {
|
||||
// total = 20, take = 10, skip = 0 page: 1
|
||||
// total = 20, take = 12, skip = 10 page: 2 error
|
||||
// take = total - skip
|
||||
|
@ -427,65 +402,20 @@ export class UsersService implements IUserService {
|
|||
finalTake = take;
|
||||
}
|
||||
} else {
|
||||
finalSkip = skip;
|
||||
finalTake = take;
|
||||
}
|
||||
|
||||
if (search) {
|
||||
const files = await this.prisma.file
|
||||
.findMany({
|
||||
where: {
|
||||
userId: id,
|
||||
OR: [
|
||||
{
|
||||
filename: {
|
||||
contains: search,
|
||||
},
|
||||
},
|
||||
{
|
||||
slug: {
|
||||
contains: search,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: sort === "newest" ? "asc" : "desc",
|
||||
},
|
||||
})
|
||||
.then((files) => {
|
||||
switch (sort) {
|
||||
case "largest":
|
||||
files.sort((a, b) => b.size - a.size);
|
||||
break;
|
||||
case "smallest":
|
||||
files.sort((a, b) => a.size - b.size);
|
||||
break;
|
||||
case "a-z":
|
||||
files.sort((a, b) => a.filename.localeCompare(b.filename));
|
||||
break;
|
||||
case "z-a":
|
||||
files.sort((a, b) => b.filename.localeCompare(a.filename));
|
||||
break;
|
||||
}
|
||||
|
||||
return files.map((file) => ({
|
||||
...file,
|
||||
size: formatBytes(file.size),
|
||||
}));
|
||||
});
|
||||
|
||||
return {
|
||||
files,
|
||||
totalFiles: files.length,
|
||||
totalPages: 1,
|
||||
};
|
||||
// @ts-ignore
|
||||
finalSkip = take === "all" ? 0 : skip;
|
||||
// @ts-ignore
|
||||
finalTake = take === "all" ? total : take;
|
||||
}
|
||||
|
||||
const files = await this.prisma.file
|
||||
.findMany({
|
||||
where: {
|
||||
userId: id,
|
||||
OR: [
|
||||
{ filename: { contains: search } },
|
||||
{ slug: { contains: search } },
|
||||
],
|
||||
},
|
||||
skip: finalSkip,
|
||||
take: finalTake,
|
||||
|
@ -507,6 +437,8 @@ export class UsersService implements IUserService {
|
|||
case "z-a":
|
||||
files.sort((a, b) => b.filename.localeCompare(a.filename));
|
||||
break;
|
||||
case "newest":
|
||||
take !== total && files.reverse();
|
||||
}
|
||||
return files.map((file) => ({
|
||||
...file,
|
||||
|
|
|
@ -103,6 +103,7 @@ const FileViewer = () => {
|
|||
onChange={(value) => {
|
||||
if (!files?.length) return;
|
||||
setLimit(value === 'all' ? 'all' : parseInt(value!));
|
||||
value === 'all' ? setPage(1) : null;
|
||||
}}
|
||||
/>
|
||||
<Select
|
||||
|
@ -162,7 +163,7 @@ const FileViewer = () => {
|
|||
<SimpleGrid
|
||||
cols={3}
|
||||
breakpoints={[
|
||||
{ maxWidth: 1280, cols: 3, spacing: 'md' },
|
||||
{ maxWidth: 1024, cols: 2, spacing: 'md' },
|
||||
{ maxWidth: 840, cols: 2, spacing: 'sm' },
|
||||
{ maxWidth: 600, cols: 1, spacing: 'sm' },
|
||||
]}
|
||||
|
|
|
@ -1,27 +1,30 @@
|
|||
import { Paper, Progress, Stack, Text } from '@mantine/core';
|
||||
import { Group, Paper, Progress, Stack, Text } from '@mantine/core';
|
||||
import { FC } from 'react';
|
||||
|
||||
const ProgressCard: FC<{ progress: number; filename: string }> = ({
|
||||
filename,
|
||||
progress,
|
||||
}) => {
|
||||
const ProgressCard: FC<{
|
||||
progress: number;
|
||||
filename: string;
|
||||
speed: string;
|
||||
}> = ({ filename, progress, speed }) => {
|
||||
return (
|
||||
<>
|
||||
<Paper withBorder p="lg" my="xs" sx={{ width: '100%' }}>
|
||||
<Stack>
|
||||
<Text size="lg" weight="bold">
|
||||
{filename}
|
||||
</Text>
|
||||
<Progress
|
||||
size="xl"
|
||||
sections={[
|
||||
{
|
||||
value: progress,
|
||||
color: progress === 100 ? 'teal' : 'blue',
|
||||
label: progress === 100 ? 'Done' : `${progress}%`,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<Stack justify="space-between">
|
||||
<Group position="apart">
|
||||
<Text
|
||||
size="md"
|
||||
weight="bold"
|
||||
sx={{
|
||||
msWordBreak: 'break-all',
|
||||
}}
|
||||
>
|
||||
{filename.length > 67 ? filename.slice(0, 67) + '...' : filename}
|
||||
</Text>
|
||||
<Text size="sm" color="dimmed">
|
||||
{speed} - {progress}%
|
||||
</Text>
|
||||
</Group>
|
||||
<Progress size="sm" value={progress} />
|
||||
</Stack>
|
||||
</Paper>
|
||||
</>
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
import { Button, Group, Text, useMantineTheme } from '@mantine/core';
|
||||
import {
|
||||
Button,
|
||||
Group,
|
||||
SimpleGrid,
|
||||
Text,
|
||||
useMantineTheme,
|
||||
} from '@mantine/core';
|
||||
import { Dropzone } from '@mantine/dropzone';
|
||||
import { IconCheck, IconCloudUpload, IconDownload, IconX } from '@tabler/icons';
|
||||
import dynamic from 'next/dynamic';
|
||||
|
@ -11,6 +17,7 @@ import { userAtom } from '@lib/atoms';
|
|||
import { showNotification } from '@mantine/notifications';
|
||||
const ProgressCard = dynamic(() => import('./ProgressCard'));
|
||||
import { ACCEPT_TYPE } from '@lib/constants';
|
||||
import { formatBytes } from '@lib/utils';
|
||||
|
||||
const UploadZone = () => {
|
||||
const [user] = useAtom(userAtom);
|
||||
|
@ -39,10 +46,19 @@ const UploadZone = () => {
|
|||
Authorization: user?.apiKey,
|
||||
};
|
||||
axios
|
||||
.post(API_URL + API_ROUTES.UPLOAD_FILE, data, { headers })
|
||||
.post(API_URL + API_ROUTES.UPLOAD_FILE, data, {
|
||||
headers,
|
||||
onUploadProgress: (evt) => {
|
||||
// @ts-ignore
|
||||
// prettier-ignore
|
||||
file.speed = evt.loaded / ((new Date().getTime() - file.start) / 1000);
|
||||
},
|
||||
})
|
||||
.then((response) => {
|
||||
const file = files[currentFileIndex!];
|
||||
const filesize = file.size;
|
||||
// @ts-ignore
|
||||
file.start = new Date().getTime();
|
||||
const chunks = Math.ceil(filesize / CHUNK_SIZE) - 1;
|
||||
const isLastChunk = currentChunkIndex === chunks;
|
||||
if (isLastChunk) {
|
||||
|
@ -61,8 +77,8 @@ const UploadZone = () => {
|
|||
const isLastFile = currentFileIndex === files.length - 1;
|
||||
|
||||
if (isLastFile) {
|
||||
setCurrentFileIndex(null);
|
||||
setLastUploadedFileIndex(currentFileIndex);
|
||||
setCurrentFileIndex(null);
|
||||
}
|
||||
} else {
|
||||
setCurrentChunkIndex(currentChunkIndex! + 1);
|
||||
|
@ -187,26 +203,34 @@ const UploadZone = () => {
|
|||
Select files
|
||||
</Button>
|
||||
</div>
|
||||
{files.map((file, idx) => {
|
||||
let progress = 0;
|
||||
// @ts-ignore
|
||||
if (file.final) {
|
||||
progress = 100;
|
||||
} else {
|
||||
const uploading = idx === currentFileIndex;
|
||||
const chunks = Math.ceil(file.size / CHUNK_SIZE);
|
||||
|
||||
if (uploading) {
|
||||
const rounder = (currentChunkIndex! / chunks) * 100;
|
||||
progress = +rounder.toFixed(2);
|
||||
<SimpleGrid cols={2} breakpoints={[{ maxWidth: 768, cols: 1 }]}>
|
||||
{files.map((file, idx) => {
|
||||
let progress = 0;
|
||||
// @ts-ignore
|
||||
if (file.final) {
|
||||
progress = 100;
|
||||
} else {
|
||||
progress = 0;
|
||||
const uploading = idx === currentFileIndex;
|
||||
const chunks = Math.ceil(file.size / CHUNK_SIZE);
|
||||
|
||||
if (uploading) {
|
||||
const rounder = (currentChunkIndex! / chunks) * 100;
|
||||
progress = +rounder.toFixed(2);
|
||||
} else {
|
||||
progress = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (
|
||||
<ProgressCard key={idx} filename={file.name} progress={progress} />
|
||||
);
|
||||
})}
|
||||
return (
|
||||
<ProgressCard
|
||||
key={idx}
|
||||
filename={file.name}
|
||||
progress={progress}
|
||||
// @ts-ignore
|
||||
speed={file.speed ? `${formatBytes(file.speed)}/s` : 'Waiting...'}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</SimpleGrid>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -80,3 +80,14 @@ export const serializeURL = (url: string) => {
|
|||
// remove http(s)://
|
||||
return url.replace(/(^\w+:|^)\/\//, '');
|
||||
};
|
||||
|
||||
export const formatBytes = (bytes: number, decimals = 2) => {
|
||||
if (bytes === 0) return '0 Bytes';
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(1024));
|
||||
return parseFloat(
|
||||
(bytes / Math.pow(1024, i)).toFixed(decimals < 0 ? 0 : decimals)
|
||||
)
|
||||
.toString()
|
||||
.concat(` ${sizes[i]}`);
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue