feat(share): display muliple images (#28)
* fix(workflows): user can click multiple times on run even while loading * feat(share): display muliple images
This commit is contained in:
		
							parent
							
								
									17653508c5
								
							
						
					
					
						commit
						2cfad8bef6
					
				@ -80,7 +80,9 @@
 | 
				
			|||||||
  /* @apply rounded-lg p-2 overflow-x-scroll */
 | 
					  /* @apply rounded-lg p-2 overflow-x-scroll */
 | 
				
			||||||
  @apply  p-2 max-w-full overflow-auto w-full
 | 
					  @apply  p-2 max-w-full overflow-auto w-full
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
					.vsc-controller{
 | 
				
			||||||
 | 
					  position: absolute;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@layer base {
 | 
					@layer base {
 | 
				
			||||||
  * {
 | 
					  * {
 | 
				
			||||||
    @apply border-border;
 | 
					    @apply border-border;
 | 
				
			||||||
 | 
				
			|||||||
@ -60,6 +60,7 @@ import { callServerPromise } from "./callServerPromise";
 | 
				
			|||||||
import fetcher from "./fetcher";
 | 
					import fetcher from "./fetcher";
 | 
				
			||||||
import { ButtonAction } from "@/components/ButtonActionLoader";
 | 
					import { ButtonAction } from "@/components/ButtonActionLoader";
 | 
				
			||||||
import { editWorkflowOnMachine } from "@/server/editWorkflowOnMachine";
 | 
					import { editWorkflowOnMachine } from "@/server/editWorkflowOnMachine";
 | 
				
			||||||
 | 
					import { VisualizeImagesGrid } from "@/components/VisualizeImagesGrid";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function VersionSelect({
 | 
					export function VersionSelect({
 | 
				
			||||||
  workflow,
 | 
					  workflow,
 | 
				
			||||||
@ -169,19 +170,21 @@ export function useSelectedMachine(
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type PublicRunStore = {
 | 
					type PublicRunStore = {
 | 
				
			||||||
  image: string;
 | 
					  image: {
 | 
				
			||||||
 | 
					    url: string;
 | 
				
			||||||
 | 
					  }[] | null;
 | 
				
			||||||
  loading: boolean;
 | 
					  loading: boolean;
 | 
				
			||||||
  runId: string;
 | 
					  runId: string;
 | 
				
			||||||
  status: string;
 | 
					  status: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  setImage: (image: string) => void;
 | 
					  setImage: (image: { url: string; }[]) => void;
 | 
				
			||||||
  setLoading: (loading: boolean) => void;
 | 
					  setLoading: (loading: boolean) => void;
 | 
				
			||||||
  setRunId: (runId: string) => void;
 | 
					  setRunId: (runId: string) => void;
 | 
				
			||||||
  setStatus: (status: string) => void;
 | 
					  setStatus: (status: string) => void;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const publicRunStore = create<PublicRunStore>((set) => ({
 | 
					export const publicRunStore = create<PublicRunStore>((set) => ({
 | 
				
			||||||
  image: "",
 | 
					  image: null,
 | 
				
			||||||
  loading: false,
 | 
					  loading: false,
 | 
				
			||||||
  runId: "",
 | 
					  runId: "",
 | 
				
			||||||
  status: "",
 | 
					  status: "",
 | 
				
			||||||
@ -205,7 +208,10 @@ export function PublicRunOutputs(props: {
 | 
				
			|||||||
        console.log(res?.status);
 | 
					        console.log(res?.status);
 | 
				
			||||||
        if (res) setStatus(res.status);
 | 
					        if (res) setStatus(res.status);
 | 
				
			||||||
        if (res && res.status === "success") {
 | 
					        if (res && res.status === "success") {
 | 
				
			||||||
          setImage(res.outputs[0]?.data.images[0].url);
 | 
					          const imageURLs = res.outputs[0]?.data.images.map((item: { url: string; }) => {
 | 
				
			||||||
 | 
					            return { url: item.url };
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					          setImage(imageURLs);
 | 
				
			||||||
          setLoading(false);
 | 
					          setLoading(false);
 | 
				
			||||||
          clearInterval(interval);
 | 
					          clearInterval(interval);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -214,30 +220,25 @@ export function PublicRunOutputs(props: {
 | 
				
			|||||||
    return () => clearInterval(interval);
 | 
					    return () => clearInterval(interval);
 | 
				
			||||||
  }, [runId]);
 | 
					  }, [runId]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  if (loading) {
 | 
				
			||||||
    <div className="border border-gray-200 w-full square h-[400px] rounded-lg relative">
 | 
					    return (
 | 
				
			||||||
      {!loading && !image && props.preview && props.preview.length > 0 && (
 | 
					      <div className="border border-gray-200 w-full h-[400px] square rounded-lg relative p-4 ">
 | 
				
			||||||
        <>
 | 
					 | 
				
			||||||
          <img
 | 
					 | 
				
			||||||
            className="w-full h-full object-contain"
 | 
					 | 
				
			||||||
            src={props.preview[0]?.url}
 | 
					 | 
				
			||||||
            alt="Generated image"
 | 
					 | 
				
			||||||
          />
 | 
					 | 
				
			||||||
        </>
 | 
					 | 
				
			||||||
      )}
 | 
					 | 
				
			||||||
      {!loading && image && (
 | 
					 | 
				
			||||||
        <img
 | 
					 | 
				
			||||||
          className="w-full h-full object-contain"
 | 
					 | 
				
			||||||
          src={image}
 | 
					 | 
				
			||||||
          alt="Generated image"
 | 
					 | 
				
			||||||
        />
 | 
					 | 
				
			||||||
      )}
 | 
					 | 
				
			||||||
      {loading && (
 | 
					 | 
				
			||||||
        <div className="absolute top-0 left-0 w-full h-full flex items-center justify-center gap-2">
 | 
					        <div className="absolute top-0 left-0 w-full h-full flex items-center justify-center gap-2">
 | 
				
			||||||
          {status} <LoadingIcon />
 | 
					          {status} <LoadingIcon />
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					        <Skeleton className="w-full h-full" />
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div className="border border-gray-200 w-full min-h-[400px] square rounded-lg relative p-4 ">
 | 
				
			||||||
 | 
					      {!image && props.preview && props.preview.length > 0 &&
 | 
				
			||||||
 | 
					        <VisualizeImagesGrid images={props.preview} />
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      {image && (
 | 
				
			||||||
 | 
					        <VisualizeImagesGrid images={image} />
 | 
				
			||||||
      )}
 | 
					      )}
 | 
				
			||||||
      {loading && <Skeleton className="w-full h-full" />}
 | 
					 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										42
									
								
								web/src/components/VisualizeImagesGrid.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								web/src/components/VisualizeImagesGrid.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					type imagesType = {
 | 
				
			||||||
 | 
					  url: string;
 | 
				
			||||||
 | 
					  width?: number;
 | 
				
			||||||
 | 
					  height?: number;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					type VisualizeImagesGridProps = {
 | 
				
			||||||
 | 
					  images: imagesType[];
 | 
				
			||||||
 | 
					  layout?: 'justify-between' | 'justify-center' | 'justify-start' | 'justify-end';
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					export function VisualizeImagesGrid({ images, layout }: VisualizeImagesGridProps) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div className={`flex gap-4 flex-wrap ${layout || 'justify-center'}`}>
 | 
				
			||||||
 | 
					      <>
 | 
				
			||||||
 | 
					        {images && images.length > 0 &&
 | 
				
			||||||
 | 
					          images.map(item => {
 | 
				
			||||||
 | 
					            if (!item) {
 | 
				
			||||||
 | 
					              return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (item?.url.endsWith(".mp4") || item?.url.endsWith(".webm")) {
 | 
				
			||||||
 | 
					              return (
 | 
				
			||||||
 | 
					                <video key={item?.url} controls autoPlay className="rounded-xl" style={{ maxHeight: item.height || 370, maxWidth: item.width || "auto" }}>
 | 
				
			||||||
 | 
					                  <source src={item?.url} type="video/mp4" />
 | 
				
			||||||
 | 
					                  <source src={item?.url} type="video/webm" />
 | 
				
			||||||
 | 
					                  Your browser does not support the video tag.
 | 
				
			||||||
 | 
					                </video>
 | 
				
			||||||
 | 
					              );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return <img
 | 
				
			||||||
 | 
					              key={item?.url}
 | 
				
			||||||
 | 
					              className="object-contain overflow-hidden rounded-xl"
 | 
				
			||||||
 | 
					              src={item?.url}
 | 
				
			||||||
 | 
					              alt="Generated image"
 | 
				
			||||||
 | 
					              style={{ maxHeight: item.height || 370, maxWidth: item.width || "auto" }}
 | 
				
			||||||
 | 
					            />;
 | 
				
			||||||
 | 
					          })
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      </>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user