Added stop signal to not let the request happen twice

This commit is contained in:
Lucas Oliveira 2024-06-07 18:17:39 -03:00
commit 73511c72ca
5 changed files with 120 additions and 76 deletions

View file

@ -365,8 +365,21 @@ export async function uploadFile(
return await api.post(`${BASE_URL_API}files/upload/${id}`, formData); return await api.post(`${BASE_URL_API}files/upload/${id}`, formData);
} }
export async function getProfilePictures(): Promise<ProfilePicturesTypeAPI> { export async function getProfilePictures(
return api.get(`${BASE_URL_API}files/list/profile_pictures`); abortSignal,
): Promise<ProfilePicturesTypeAPI | null> {
try {
const res = await api.get(`${BASE_URL_API}images/list/profile_pictures`, {
signal: abortSignal,
});
if (res.status === 200) {
return res.data;
}
} catch (error) {
throw error;
}
return null;
} }
export async function postCustomComponent( export async function postCustomComponent(

View file

@ -3,11 +3,12 @@ import { PROFILE_PICTURES_GET_ERROR_ALERT } from "../../../../../../../../../con
import { getProfilePictures } from "../../../../../../../../../controllers/API"; import { getProfilePictures } from "../../../../../../../../../controllers/API";
const useGetProfilePictures = (setErrorData) => { const useGetProfilePictures = (setErrorData) => {
const handleGetProfilePictures = async () => { const handleGetProfilePictures = async (abortSignal) => {
try { try {
const profilePictures = await getProfilePictures(); const profilePictures = await getProfilePictures(abortSignal);
return profilePictures.files; return profilePictures!.files;
} catch (error) { } catch (error) {
console.log(error);
setErrorData({ setErrorData({
title: PROFILE_PICTURES_GET_ERROR_ALERT, title: PROFILE_PICTURES_GET_ERROR_ALERT,
list: [(error as any)?.response?.data?.detail], list: [(error as any)?.response?.data?.detail],
@ -16,7 +17,7 @@ const useGetProfilePictures = (setErrorData) => {
} }
}; };
return handleGetProfilePictures; return { handleGetProfilePictures };
}; };
export default useGetProfilePictures; export default useGetProfilePictures;

View file

@ -11,48 +11,31 @@ import HorizontalScrollFadeComponent from "../../../../../../../../components/ho
import LoadingComponent from "../../../../../../../../components/loadingComponent"; import LoadingComponent from "../../../../../../../../components/loadingComponent";
import Loading from "../../../../../../../../components/ui/loading"; import Loading from "../../../../../../../../components/ui/loading";
export default function ProfilePictureChooserComponent({ value, onChange }) { type ProfilePictureChooserComponentProps = {
const setErrorData = useAlertStore((state) => state.setErrorData); profilePictures: { [key: string]: string[] };
const getProfilePictures = useGetProfilePictures({ setErrorData }); loading: boolean;
value: string;
const [profilePictures, setProfilePictures] = useState<string[][]>([]); onChange: (value: string) => void;
const [loading, setLoading] = useState(true); };
useEffect(() => {
getProfilePictures()
.then((data) => {
if (data) {
data.forEach((profile_picture) => {
const [folder, path] = profile_picture.split("/");
setProfilePictures((prev) => {
if (prev[folder]) {
prev[folder].push(path);
} else {
prev[folder] = [path];
}
return prev;
});
setLoading(false);
});
}
})
.catch(() => {
setLoading(false);
});
});
export default function ProfilePictureChooserComponent({
profilePictures,
loading,
value,
onChange,
}: ProfilePictureChooserComponentProps) {
return ( return (
<div className="flex flex-col justify-center gap-2"> <div className="flex flex-col justify-center gap-2">
{loading ? ( {loading ? (
<Loading /> <Loading />
) : ( ) : (
profilePictures.map((folder, idx) => ( Object.keys(profilePictures).map((folder, idx) => (
<Label> <Label>
<div className="edit-flow-arrangement"> <div className="edit-flow-arrangement">
<span className="font-medium">{folder}</span> <span className="font-medium">{folder}</span>
</div> </div>
<HorizontalScrollFadeComponent> <HorizontalScrollFadeComponent>
{folder.map((path, idx) => ( {profilePictures[folder].map((path, idx) => (
<img <img
key={idx} key={idx}
src={`${BACKEND_URL.slice( src={`${BACKEND_URL.slice(

View file

@ -10,59 +10,102 @@ import {
} from "../../../../../../components/ui/card"; } from "../../../../../../components/ui/card";
import { gradients } from "../../../../../../utils/styleUtils"; import { gradients } from "../../../../../../utils/styleUtils";
import ProfilePictureChooserComponent from "./components/profilePictureChooserComponent"; import ProfilePictureChooserComponent from "./components/profilePictureChooserComponent";
import { useEffect, useState } from "react";
import { GenericAbortSignal } from "axios";
type ProfilePictureFormComponentProps = { type ProfilePictureFormComponentProps = {
profilePicture: string; profilePicture: string;
handleInput: (event: any) => void; handleInput: (event: any) => void;
handlePatchProfilePicture: (gradient: string) => void; handlePatchProfilePicture: (gradient: string) => void;
handleGetProfilePictures: (
abortSignal: GenericAbortSignal | undefined,
) => Promise<string[]>;
userData: any; userData: any;
}; };
const ProfilePictureFormComponent = ({ const ProfilePictureFormComponent = ({
profilePicture, profilePicture,
handleInput, handleInput,
handlePatchProfilePicture, handlePatchProfilePicture,
handleGetProfilePictures,
userData, userData,
}: ProfilePictureFormComponentProps) => { }: ProfilePictureFormComponentProps) => {
const [profilePictures, setProfilePictures] = useState<{
[key: string]: string[];
}>({});
const [loading, setLoading] = useState(true);
useEffect(() => {
const abortController = new AbortController();
handleGetProfilePictures(abortController.signal)
.then((data) => {
if (data) {
data.forEach((profile_picture) => {
const [folder, path] = profile_picture.split("/");
setProfilePictures((prev) => {
if (prev[folder]) {
prev[folder].push(path);
} else {
prev[folder] = [path];
}
return prev;
});
setLoading(false);
});
}
})
.catch(() => {
setLoading(false);
});
/*
Abort the request as it isn't needed anymore, the component being
unmounted. It helps avoid, among other things, the well-known "can't
perform a React state update on an unmounted component" warning.
*/
return () => abortController.abort();
}, []);
return ( return (
<> <Form.Root
<Form.Root onSubmit={(event) => {
onSubmit={(event) => { handlePatchProfilePicture(profilePicture);
handlePatchProfilePicture(profilePicture); event.preventDefault();
event.preventDefault(); }}
}} >
> <Card x-chunk="dashboard-04-chunk-1">
<Card x-chunk="dashboard-04-chunk-1"> <CardHeader>
<CardHeader> <CardTitle>Profile Picture</CardTitle>
<CardTitle>Profile Picture</CardTitle> <CardDescription>
<CardDescription> Choose the image that appears as your profile picture.
Choose the image that appears as your profile picture. </CardDescription>
</CardDescription> </CardHeader>
</CardHeader> <CardContent>
<CardContent> <div className="py-2">
<div className="py-2"> <ProfilePictureChooserComponent
<ProfilePictureChooserComponent profilePictures={profilePictures}
value={ loading={loading}
profilePicture == "" value={
? userData?.profile_image ?? profilePicture == ""
gradients[ ? userData?.profile_image ??
parseInt(userData?.id ?? "", 30) % gradients.length gradients[
] parseInt(userData?.id ?? "", 30) % gradients.length
: profilePicture ]
} : profilePicture
onChange={(value) => { }
handleInput({ target: { name: "profilePicture", value } }); onChange={(value) => {
}} handleInput({ target: { name: "profilePicture", value } });
/> }}
</div> />
</CardContent> </div>
<CardFooter className="border-t px-6 py-4"> </CardContent>
<Form.Submit asChild> <CardFooter className="border-t px-6 py-4">
<Button type="submit">Save</Button> <Form.Submit asChild>
</Form.Submit> <Button type="submit">Save</Button>
</CardFooter> </Form.Submit>
</Card> </CardFooter>
</Form.Root> </Card>
</> </Form.Root>
); );
}; };
export default ProfilePictureFormComponent; export default ProfilePictureFormComponent;

View file

@ -17,6 +17,7 @@ import GeneralPageHeaderComponent from "./components/GeneralPageHeader";
import PasswordFormComponent from "./components/PasswordForm"; import PasswordFormComponent from "./components/PasswordForm";
import ProfilePictureFormComponent from "./components/ProfilePictureForm"; import ProfilePictureFormComponent from "./components/ProfilePictureForm";
import StoreApiKeyFormComponent from "./components/StoreApiKeyForm"; import StoreApiKeyFormComponent from "./components/StoreApiKeyForm";
import useGetProfilePictures from "./components/ProfilePictureForm/components/profilePictureChooserComponent/hooks/use-get-profile-pictures";
export default function GeneralPage() { export default function GeneralPage() {
const setCurrentFlowId = useFlowsManagerStore( const setCurrentFlowId = useFlowsManagerStore(
@ -50,6 +51,8 @@ export default function GeneralPage() {
setErrorData, setErrorData,
); );
const { handleGetProfilePictures } = useGetProfilePictures(setErrorData);
const { handlePatchProfilePicture } = usePatchProfilePicture( const { handlePatchProfilePicture } = usePatchProfilePicture(
setSuccessData, setSuccessData,
setErrorData, setErrorData,
@ -82,6 +85,7 @@ export default function GeneralPage() {
profilePicture={profilePicture} profilePicture={profilePicture}
handleInput={handleInput} handleInput={handleInput}
handlePatchProfilePicture={handlePatchProfilePicture} handlePatchProfilePicture={handlePatchProfilePicture}
handleGetProfilePictures={handleGetProfilePictures}
userData={userData} userData={userData}
/> />