fix: show loading component state while images arent fully loaded (#2609)
* ✨ (ProfilePictureForm): add loading state to handle initial loading state * ♻️ (use-get-profile-pictures.ts): refactor profile pictures query to process data on the server side ♻️ (ProfilePictureForm): simplify state management by removing redundant loading state * ♻️ (use-get-profile-pictures.ts): rename ProfilePicturesResponse to ProfilePicturesQueryResponse for clarity ♻️ (use-preload-images.tsx): add loading check to useEffect to prevent unnecessary execution ♻️ (profilePictureChooserComponent): update profilePictures prop type to handle undefined and add loading to usePreloadImages ♻️ (ProfilePictureForm): remove unnecessary state and use response directly from useGetProfilePicturesQuery * 🐛 (use-preload-images.tsx): add missing dependency 'loading' to useEffect dependency array to ensure images are preloaded correctly
This commit is contained in:
parent
0df06c01b0
commit
7174e6ef7d
4 changed files with 39 additions and 45 deletions
|
|
@ -6,31 +6,46 @@ import { UseRequestProcessor } from "../../services/request-processor";
|
|||
|
||||
interface ProfilePicturesQueryParams {}
|
||||
|
||||
export interface ProfilePicturesResponse {
|
||||
export interface ProfilePicturesQueryResponse {
|
||||
files: string[];
|
||||
}
|
||||
|
||||
export const useGetProfilePicturesQuery: useQueryFunctionType<
|
||||
ProfilePicturesQueryParams,
|
||||
ProfilePicturesResponse
|
||||
{ [key: string]: string[] }
|
||||
> = () => {
|
||||
const { query } = UseRequestProcessor();
|
||||
|
||||
const getProfilePicturesFn = async () => {
|
||||
const response = await api.get<ProfilePicturesResponse>(
|
||||
`${getURL("FILES")}/profile_pictures/list`,
|
||||
);
|
||||
const getProfilePicturesFn =
|
||||
async (): Promise<ProfilePicturesQueryResponse> => {
|
||||
const response = await api.get<ProfilePicturesQueryResponse>(
|
||||
`${getURL("FILES")}/profile_pictures/list`,
|
||||
);
|
||||
|
||||
return response.data;
|
||||
return response.data;
|
||||
};
|
||||
|
||||
const responseFn = async () => {
|
||||
const data = await getProfilePicturesFn();
|
||||
|
||||
const profilePictures = {};
|
||||
|
||||
data?.files?.forEach((profile_picture) => {
|
||||
const [folder, path] = profile_picture.split("/");
|
||||
|
||||
if (profilePictures[folder]) {
|
||||
profilePictures[folder].push(path);
|
||||
} else {
|
||||
profilePictures[folder] = [path];
|
||||
}
|
||||
});
|
||||
|
||||
return profilePictures;
|
||||
};
|
||||
|
||||
const queryResult = query(
|
||||
["useGetProfilePicturesQuery"],
|
||||
getProfilePicturesFn,
|
||||
{
|
||||
placeholderData: keepPreviousData,
|
||||
},
|
||||
);
|
||||
const queryResult = query(["useGetProfilePicturesQuery"], responseFn, {
|
||||
placeholderData: keepPreviousData,
|
||||
});
|
||||
|
||||
return queryResult;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { BASE_URL_API } from "../../../../../../../../../constants/constants";
|
|||
const usePreloadImages = (
|
||||
profilePictures: { [key: string]: string[] },
|
||||
setImagesLoaded: (value: boolean) => void,
|
||||
loading: boolean,
|
||||
) => {
|
||||
const preloadImages = async (imageUrls) => {
|
||||
return Promise.all(
|
||||
|
|
@ -20,6 +21,7 @@ const usePreloadImages = (
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) return;
|
||||
const imageArray: string[] = [];
|
||||
|
||||
Object.keys(profilePictures).flatMap((folder) =>
|
||||
|
|
@ -33,7 +35,7 @@ const usePreloadImages = (
|
|||
preloadImages(imageArray).then(() => {
|
||||
setImagesLoaded(true);
|
||||
});
|
||||
}, [profilePictures]);
|
||||
}, [profilePictures, loading]);
|
||||
|
||||
return;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { cn } from "../../../../../../../../utils/utils";
|
|||
import usePreloadImages from "./hooks/use-preload-images";
|
||||
|
||||
type ProfilePictureChooserComponentProps = {
|
||||
profilePictures: { [key: string]: string[] };
|
||||
profilePictures: { [key: string]: string[] } | undefined;
|
||||
loading: boolean;
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
|
|
@ -29,21 +29,21 @@ export default function ProfilePictureChooserComponent({
|
|||
}
|
||||
}, [ref, value]);
|
||||
|
||||
usePreloadImages(profilePictures, setImagesLoaded);
|
||||
usePreloadImages(profilePictures!, setImagesLoaded, loading);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col justify-center gap-2">
|
||||
{loading || !imagesLoaded ? (
|
||||
<Loading />
|
||||
) : (
|
||||
Object.keys(profilePictures).map((folder, idx) => (
|
||||
Object.keys(profilePictures!).map((folder, idx) => (
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="edit-flow-arrangement">
|
||||
<span className="font-normal">{folder}</span>
|
||||
</div>
|
||||
<div className="block overflow-hidden">
|
||||
<div className="flex items-center gap-1 overflow-x-auto rounded-lg bg-muted px-1 custom-scroll">
|
||||
{profilePictures[folder].map((path, idx) => (
|
||||
{profilePictures![folder].map((path, idx) => (
|
||||
<Button
|
||||
ref={value === folder + "/" + path ? ref : undefined}
|
||||
unstyled
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
import {
|
||||
ProfilePicturesResponse,
|
||||
useGetProfilePicturesQuery,
|
||||
} from "@/controllers/API/queries/files";
|
||||
import { useGetProfilePicturesQuery } from "@/controllers/API/queries/files";
|
||||
import * as Form from "@radix-ui/react-form";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Button } from "../../../../../../components/ui/button";
|
||||
|
|
@ -20,7 +17,7 @@ type ProfilePictureFormComponentProps = {
|
|||
profilePicture: string;
|
||||
handleInput: (event: any) => void;
|
||||
handlePatchProfilePicture: (gradient: string) => void;
|
||||
handleGetProfilePictures: () => ProfilePicturesResponse | undefined;
|
||||
handleGetProfilePictures: () => undefined;
|
||||
userData: any;
|
||||
};
|
||||
const ProfilePictureFormComponent = ({
|
||||
|
|
@ -30,28 +27,8 @@ const ProfilePictureFormComponent = ({
|
|||
handleGetProfilePictures,
|
||||
userData,
|
||||
}: ProfilePictureFormComponentProps) => {
|
||||
const [profilePictures, setProfilePictures] = useState<{
|
||||
[key: string]: string[];
|
||||
}>({});
|
||||
|
||||
const { data: response, isFetching } = useGetProfilePicturesQuery({});
|
||||
|
||||
useEffect(() => {
|
||||
if (response?.files) {
|
||||
response?.files?.forEach((profile_picture) => {
|
||||
const [folder, path] = profile_picture.split("/");
|
||||
setProfilePictures((prev) => {
|
||||
if (prev[folder]) {
|
||||
prev[folder].push(path);
|
||||
} else {
|
||||
prev[folder] = [path];
|
||||
}
|
||||
return prev;
|
||||
});
|
||||
});
|
||||
}
|
||||
}, [response]);
|
||||
|
||||
return (
|
||||
<Form.Root
|
||||
onSubmit={(event) => {
|
||||
|
|
@ -69,7 +46,7 @@ const ProfilePictureFormComponent = ({
|
|||
<CardContent>
|
||||
<div className="py-2">
|
||||
<ProfilePictureChooserComponent
|
||||
profilePictures={profilePictures}
|
||||
profilePictures={response}
|
||||
loading={isFetching}
|
||||
value={
|
||||
profilePicture == ""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue