feat: add collection create\edit api request

This commit is contained in:
Kentai Radiquum 2024-08-17 22:51:06 +05:00
parent 5cde53c1d3
commit 9f3ca2e6d9
Signed by: Radiquum
GPG key ID: 858E8EE696525EED
4 changed files with 140 additions and 48 deletions

View file

@ -1,6 +1,7 @@
import { NextResponse, NextRequest } from "next/server";
import { fetchDataViaGet, fetchDataViaPost } from "../utils";
import { API_URL } from "../config";
import { buffer } from "stream/consumers";
export async function GET(
req: NextRequest,
@ -25,14 +26,24 @@ export async function POST(
) {
const { endpoint } = params;
let API_V2: boolean | string =
req.nextUrl.searchParams.get("API_V2") || false;
req.nextUrl.searchParams.get("API_V2") || false;
if (API_V2 === "true") {
req.nextUrl.searchParams.delete("API_V2");
}
const query = req.nextUrl.searchParams.toString();
const url = `${API_URL}/${endpoint.join("/")}${query ? `?${query}` : ""}`;
const body = JSON.stringify( await req.json());
let body;
const ReqContentTypeHeader = req.headers.get("Content-Type") || "";
let ResContentTypeHeader = "";
const response = await fetchDataViaPost(url, body, API_V2);
if (ReqContentTypeHeader.split(";")[0] == "multipart/form-data") {
ResContentTypeHeader = ReqContentTypeHeader;
body = await req.arrayBuffer();
} else {
ResContentTypeHeader = "application/json; charset=UTF-8";
body = JSON.stringify(await req.json());
}
const response = await fetchDataViaPost(url, body, API_V2, ResContentTypeHeader);
return NextResponse.json(response);
}

View file

@ -28,11 +28,16 @@ export const fetchDataViaGet = async (
export const fetchDataViaPost = async (
url: string,
body: string,
API_V2: string | boolean = false
API_V2: string | boolean = false,
contentType: string = ""
) => {
if (API_V2) {
HEADERS["API-Version"] = "v2";
}
if (contentType != "") {
HEADERS["Content-Type"] = contentType;
}
try {
const response = await fetch(url, {
method: "POST",

View file

@ -2,13 +2,11 @@ import React, { useRef } from "react";
import Cropper, { ReactCropperElement } from "react-cropper";
import "cropperjs/dist/cropper.css";
import { Button, Modal } from "flowbite-react";
import { b64toBlob } from "#/api/utils";
type Props = {
src: string;
setSrc: (src: string) => void;
setTempSrc: (src: string) => void;
setImageData: (src: string) => void;
isOpen: boolean;
setIsOpen: (isOpen: boolean) => void;
height: number;
@ -24,36 +22,16 @@ export const CropModal: React.FC<Props> = (props) => {
const getCropData = () => {
if (typeof cropperRef.current?.cropper !== "undefined") {
props.setSrc(cropperRef.current?.cropper.getCroppedCanvas().toDataURL());
let block = cropperRef.current?.cropper
.getCroppedCanvas({
width: props.width,
height: props.height,
maxWidth: props.width,
maxHeight: props.height,
})
.toDataURL("image/jpeg", props.quality)
.split(";");
let contentType = block[0].split(":")[1];
let realData = block[1].split(",")[1];
const blob = b64toBlob(realData, contentType);
const handleFileRead = (e, fileReader) => {
const content = fileReader.result;
props.setImageData(content);
};
const handleFileText = (file) => {
const fileReader = new FileReader();
fileReader.onloadend = (e) => {
handleFileRead(e, fileReader);
};
fileReader.readAsText(file);
};
handleFileText(blob);
props.setSrc(
cropperRef.current?.cropper
.getCroppedCanvas({
width: props.width,
height: props.height,
maxWidth: props.width,
maxHeight: props.height,
})
.toDataURL("image/jpeg", props.quality)
);
props.setTempSrc("");
}
};
@ -103,7 +81,7 @@ export const CropModal: React.FC<Props> = (props) => {
onClick={() => {
props.setSrc(null);
props.setTempSrc(null);
props.setImageData(null);
// props.setImageData(null);
props.setIsOpen(false);
}}
>

View file

@ -1,5 +1,4 @@
"use client";
import useSWR from "swr";
import useSWRInfinite from "swr/infinite";
import { useUserStore } from "#/store/auth";
import { useEffect, useState, useCallback } from "react";
@ -17,6 +16,7 @@ import {
} from "flowbite-react";
import { ReleaseLink } from "#/components/ReleaseLink/ReleaseLink";
import { CropModal } from "#/components/CropModal/CropModal";
import { b64toBlob } from "#/api/utils";
const fetcher = async (url: string) => {
const res = await fetch(url);
@ -39,7 +39,7 @@ export const CreateCollectionPage = () => {
const [edit, setEdit] = useState(false);
const [imageData, setImageData] = useState<string>(null);
// const [imageData, setImageData] = useState<File | Blob>(null);
const [imageUrl, setImageUrl] = useState<string>(null);
const [tempImageUrl, setTempImageUrl] = useState<string>(null);
const [isPrivate, setIsPrivate] = useState(false);
@ -59,19 +59,59 @@ export const CreateCollectionPage = () => {
const collection_id = searchParams.get("id") || null;
const mode = searchParams.get("mode") || null;
const [isSending, setIsSending] = useState(false);
useEffect(() => {
async function _checkMode() {
if (mode === "edit" && collection_id) {
setIsSending(true);
const res = await fetch(
`${ENDPOINTS.collection.base}/${collection_id}?token=${userStore.token}`
);
const data = await res.json();
let addedReleasesIdsArray = [];
let addedReleasesArray = [];
for (let i = 0; i < 4; i++) {
const res = await fetch(
`${ENDPOINTS.collection.base}/${collection_id}/releases/${i}?token=${userStore.token}`
);
const data = await res.json();
if (data.content.length > 0) {
data.content.forEach((release) => {
if (!addedReleasesIds.includes(release.id)) {
addedReleasesIdsArray.push(release.id);
addedReleasesArray.push(release);
}
});
} else {
setAddedReleases(addedReleasesArray);
setAddedReleasesIds(addedReleasesIdsArray);
break;
}
}
if (
mode === "edit" &&
userStore.user.id == data.collection.creator.id
) {
setEdit(true);
setCollectionInfo({
title: data.collection.title,
description: data.collection.description,
});
setStringLength({
title: data.collection.title.length,
description: data.collection.description.length,
});
setIsPrivate(data.collection.is_private);
setImageUrl(data.collection.image);
setIsSending(false);
}
}
}
@ -108,13 +148,65 @@ export const CreateCollectionPage = () => {
function submit(e) {
e.preventDefault();
console.log(collectionInfo.title.length);
async function _createCollection() {
const url =
mode === "edit"
? `${ENDPOINTS.collection.edit}/${collection_id}?token=${userStore.token}`
: `${ENDPOINTS.collection.create}?token=${userStore.token}`;
console.log({
...collectionInfo,
private: isPrivate,
image: imageData,
});
const res = await fetch(url, {
method: "POST",
body: JSON.stringify({
...collectionInfo,
is_private: isPrivate,
private: isPrivate,
releases: addedReleasesIds,
}),
});
const data = await res.json();
if (data.code == 5) {
alert("Вы превысили допустимый еженедельный лимит создания коллекций!");
return;
}
if (imageUrl && !imageUrl.startsWith("http")) {
let block = imageUrl.split(";");
let contentType = block[0].split(":")[1];
let realData = block[1].split(",")[1];
const blob = b64toBlob(realData, contentType);
const formData = new FormData();
formData.append("image", blob, "cropped.jpg");
formData.append("name", "image");
const uploadRes = await fetch(
`${ENDPOINTS.collection.editImage}/${data.collection.id}?token=${userStore.token}`,
{
method: "POST",
body: formData,
}
);
const uploadData = await uploadRes.json();
}
router.push(`/collection/${data.collection.id}`);
}
if (
collectionInfo.title.length >= 10 &&
addedReleasesIds.length >= 1 &&
userStore.token
) {
// setIsSending(true);
_createCollection();
} else if (collectionInfo.title.length < 10) {
alert("Необходимо ввести название коллекции не менее 10 символов");
} else if (!userStore.token) {
alert("Для создания коллекции необходимо войти в аккаунт");
} else if (addedReleasesIds.length < 1) {
alert("Необходимо добавить хотя бы один релиз в коллекцию");
}
}
function _deleteRelease(release: any) {
@ -233,13 +325,19 @@ export const CreateCollectionPage = () => {
<Checkbox
id="private"
name="private"
color="blue"
checked={isPrivate}
onChange={(e) => setIsPrivate(e.target.checked)}
/>
<Label htmlFor="private" value="Приватная коллекция" />
</div>
</div>
<Button color={"blue"} className="mt-4" type="submit">
<Button
color={"blue"}
className="mt-4"
type="submit"
disabled={isSending}
>
{edit ? "Обновить" : "Создать"}
</Button>
</div>
@ -292,10 +390,10 @@ export const CreateCollectionPage = () => {
src={tempImageUrl}
setSrc={setImageUrl}
setTempSrc={setTempImageUrl}
setImageData={setImageData}
// setImageData={setImageData}
aspectRatio={600 / 337}
guides={false}
quality={0.9}
quality={100}
isOpen={cropModalOpen}
setIsOpen={setCropModalOpen}
forceAspect={true}