feat: adding button to delete image

This commit is contained in:
2025-08-30 20:46:05 +01:00
parent 00359e2e8d
commit 5d1c758451
6 changed files with 301 additions and 216 deletions

View File

@ -1,25 +1,105 @@
import { Component } from "solid-js"; import { Component, createSignal } from "solid-js";
import { base } from "../../network"; import { base } from "../../network";
import { A } from "@solidjs/router"; import { A } from "@solidjs/router";
import { Dialog } from "@kobalte/core";
type ImageComponentProps = {
ID: string;
onDelete: (id: string) => void;
}
export const ImageComponent: Component<ImageComponentProps> = (props) => {
const [isOpen, setIsOpen] = createSignal(false);
export const ImageComponent: Component<{ ID: string }> = (props) => {
return ( return (
<A href={`/image/${props.ID}`} class="w-full flex justify-center h-[300px]"> <>
<div class="relative w-full flex justify-center h-[300px]">
<A href={`/image/${props.ID}`} class="flex w-full">
<img <img
class="flex w-full object-cover rounded-xl" class="flex w-full object-cover rounded-xl"
src={`${base}/images/${props.ID}`} src={`${base}/images/${props.ID}`}
/> />
</A> </A>
<button
aria-label="Delete image"
class="absolute top-2 right-2 bg-gray-800 text-white rounded-full w-6 h-6 flex items-center justify-center hover:bg-red-600"
onClick={() => setIsOpen(true)}
>
×
</button>
</div>
<Dialog.Root open={isOpen()} onOpenChange={setIsOpen}>
<Dialog.Portal>
<Dialog.Overlay class="fixed inset-0 bg-black bg-opacity-50" />
<Dialog.Content class="fixed top-1/2 left-1/2 max-w-md w-full p-6 bg-white rounded shadow-lg transform -translate-x-1/2 -translate-y-1/2">
<Dialog.Title class="text-lg font-bold mb-2">
Confirm Delete
</Dialog.Title>
<Dialog.Description class="mb-4">
Are you sure you want to delete this image?
</Dialog.Description>
<div class="flex justify-end gap-2">
<Dialog.CloseButton>
<button class="px-4 py-2 bg-gray-300 rounded hover:bg-gray-400">
Cancel
</button>
</Dialog.CloseButton>
<button class="px-4 py-2 bg-red-600 text-white rounded hover:bg-red-700" onClick={() => props.onDelete(props.ID)}>
Confirm
</button>
</div>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
</>
); );
}; };
export const ImageComponentFullHeight: Component<{ ID: string }> = (props) => { export const ImageComponentFullHeight: Component<ImageComponentProps> = (props) => {
const [isOpen, setIsOpen] = createSignal(false);
return ( return (
<A href={`/image/${props.ID}`} class="w-full flex justify-center"> <>
<div class="relative w-full flex justify-center">
<A href={`/image/${props.ID}`} class="flex w-full">
<img <img
class="flex w-full object-cover rounded-xl" class="flex w-full object-cover rounded-xl"
src={`${base}/images/${props.ID}`} src={`${base}/images/${props.ID}`}
/> />
</A> </A>
<button
aria-label="Delete image"
class="absolute top-2 right-2 bg-gray-800 text-white rounded-full w-6 h-6 flex items-center justify-center hover:bg-red-600"
onClick={() => setIsOpen(true)}
>
×
</button>
</div>
<Dialog.Root open={isOpen()} onOpenChange={setIsOpen}>
<Dialog.Portal>
<Dialog.Overlay class="fixed inset-0 bg-black bg-opacity-50" />
<Dialog.Content class="fixed top-1/2 left-1/2 max-w-md w-full p-6 bg-white rounded shadow-lg transform -translate-x-1/2 -translate-y-1/2">
<Dialog.Title class="text-lg font-bold mb-2">
Confirm Delete
</Dialog.Title>
<Dialog.Description class="mb-4">
Are you sure you want to delete this image?
</Dialog.Description>
<div class="flex justify-end gap-2">
<Dialog.CloseButton>
<button class="px-4 py-2 bg-gray-300 rounded hover:bg-gray-400">
Cancel
</button>
</Dialog.CloseButton>
<button class="px-4 py-2 bg-red-600 text-white rounded hover:bg-red-700" onClick={() => props.onDelete(props.ID)}>
Confirm
</button>
</div>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
</>
); );
}; };

View File

@ -3,12 +3,11 @@ import {
type Component, type Component,
type ParentProps, type ParentProps,
createContext, createContext,
createEffect,
createMemo, createMemo,
createResource, createResource,
useContext, useContext,
} from "solid-js"; } from "solid-js";
import { getUserImages, JustTheImageWhatAreTheseNames } from "../network"; import { deleteImage, getUserImages, JustTheImageWhatAreTheseNames } from "../network";
export type SearchImageStore = { export type SearchImageStore = {
imagesByDate: Accessor< imagesByDate: Accessor<
@ -24,16 +23,13 @@ export type SearchImageStore = {
>; >;
onRefetchImages: () => void; onRefetchImages: () => void;
onDeleteImage: (imageID: string) => void;
}; };
const SearchImageContext = createContext<SearchImageStore>(); const SearchImageContext = createContext<SearchImageStore>();
export const SearchImageContextProvider: Component<ParentProps> = (props) => { export const SearchImageContextProvider: Component<ParentProps> = (props) => {
const [data, { refetch }] = createResource(getUserImages); const [data, { refetch }] = createResource(getUserImages);
createEffect(() => {
console.log(data());
});
const sortedImages = createMemo<ReturnType<SearchImageStore["imagesByDate"]>>( const sortedImages = createMemo<ReturnType<SearchImageStore["imagesByDate"]>>(
() => { () => {
const d = data(); const d = data();
@ -73,6 +69,9 @@ export const SearchImageContextProvider: Component<ParentProps> = (props) => {
userImages: () => data()?.userImages ?? [], userImages: () => data()?.userImages ?? [],
processingImages, processingImages,
onRefetchImages: refetch, onRefetchImages: refetch,
onDeleteImage: (imageID: string) => {
deleteImage(imageID).then(refetch);
}
}} }}
> >
{props.children} {props.children}

View File

@ -3,6 +3,7 @@ import { Component, For } from "solid-js";
import { createVirtualizer } from "@tanstack/solid-virtual"; import { createVirtualizer } from "@tanstack/solid-virtual";
import { ImageComponent } from "@components/image"; import { ImageComponent } from "@components/image";
import { chunkRows } from "./chunk"; import { chunkRows } from "./chunk";
import { deleteImage } from "@network/index";
type ImageOrDate = type ImageOrDate =
| { type: "image"; ID: string[] } | { type: "image"; ID: string[] }
@ -11,7 +12,7 @@ type ImageOrDate =
export const AllImages: Component = () => { export const AllImages: Component = () => {
let scrollRef: HTMLDivElement | undefined; let scrollRef: HTMLDivElement | undefined;
const { imagesByDate } = useSearchImageContext(); const { imagesByDate, onDeleteImage } = useSearchImageContext();
const items = () => { const items = () => {
const items: Array<ImageOrDate> = []; const items: Array<ImageOrDate> = [];
@ -47,7 +48,7 @@ export const AllImages: Component = () => {
const item = items()[i.index]; const item = items()[i.index];
if (item.type === "image") { if (item.type === "image") {
return ( return (
<For each={item.ID}>{(id) => <ImageComponent ID={id} />}</For> <For each={item.ID}>{(id) => <ImageComponent ID={id} onDelete={onDeleteImage} />}</For>
); );
} else { } else {
return ( return (

View File

@ -1,11 +1,12 @@
import { Component, For } from "solid-js"; import { Component, For } from "solid-js";
import { ImageComponent } from "@components/image"; import { ImageComponent } from "@components/image";
import { useSearchImageContext } from "@contexts/SearchImageContext"; import { useSearchImageContext } from "@contexts/SearchImageContext";
import { deleteImage } from "@network/index";
const NUMBER_OF_MAX_RECENT_IMAGES = 10; const NUMBER_OF_MAX_RECENT_IMAGES = 10;
export const Recent: Component = () => { export const Recent: Component = () => {
const { userImages } = useSearchImageContext(); const { userImages, onDeleteImage } = useSearchImageContext();
const latestImages = () => const latestImages = () =>
userImages() userImages()
@ -20,7 +21,7 @@ export const Recent: Component = () => {
<h2 class="text-xl font-bold">Recent Screenshots</h2> <h2 class="text-xl font-bold">Recent Screenshots</h2>
<div class="grid grid-cols-3 gap-4 place-items-center"> <div class="grid grid-cols-3 gap-4 place-items-center">
<For each={latestImages()}> <For each={latestImages()}>
{(image) => <ImageComponent ID={image.ImageID} />} {(image) => <ImageComponent ID={image.ImageID} onDelete={onDeleteImage} />}
</For> </For>
</div> </div>
</div> </div>

View File

@ -4,18 +4,19 @@ import { useParams } from "@solidjs/router";
import { For, type Component } from "solid-js"; import { For, type Component } from "solid-js";
import SolidjsMarkdown from "solidjs-markdown"; import SolidjsMarkdown from "solidjs-markdown";
import { ListCard } from "@components/list-card"; import { ListCard } from "@components/list-card";
import { deleteImage } from "@network/index";
export const ImagePage: Component = () => { export const ImagePage: Component = () => {
const { imageId } = useParams<{ imageId: string }>(); const { imageId } = useParams<{ imageId: string }>();
const { userImages, lists } = useSearchImageContext(); const { userImages, lists, onDeleteImage } = useSearchImageContext();
const image = () => userImages().find((i) => i.ImageID === imageId); const image = () => userImages().find((i) => i.ImageID === imageId);
return ( return (
<main class="flex flex-col items-center gap-4"> <main class="flex flex-col items-center gap-4">
<div class="w-full bg-white rounded-xl p-4"> <div class="w-full bg-white rounded-xl p-4">
<ImageComponentFullHeight ID={imageId} /> <ImageComponentFullHeight ID={imageId} onDelete={onDeleteImage()} />
</div> </div>
<div class="w-full bg-white rounded-xl p-4 flex flex-col gap-4"> <div class="w-full bg-white rounded-xl p-4 flex flex-col gap-4">
<h2 class="font-bold text-2xl">Description</h2> <h2 class="font-bold text-2xl">Description</h2>

View File

@ -2,12 +2,15 @@ import { Component, createSignal, For } from "solid-js";
import { Search } from "@kobalte/core/search"; import { Search } from "@kobalte/core/search";
import { IconSearch } from "@tabler/icons-solidjs"; import { IconSearch } from "@tabler/icons-solidjs";
import { useSearch } from "./search"; import { useSearch } from "./search";
import { JustTheImageWhatAreTheseNames } from "@network/index"; import { deleteImage, JustTheImageWhatAreTheseNames } from "@network/index";
import { ImageComponent } from "@components/image"; import { ImageComponent } from "@components/image";
import { useSearchImageContext } from "@contexts/SearchImageContext";
export const SearchPage: Component = () => { export const SearchPage: Component = () => {
const fuse = useSearch(); const fuse = useSearch();
const { onDeleteImage } = useSearchImageContext();
const [searchItems, setSearchItems] = const [searchItems, setSearchItems] =
createSignal<JustTheImageWhatAreTheseNames>([]); createSignal<JustTheImageWhatAreTheseNames>([]);
@ -38,7 +41,7 @@ export const SearchPage: Component = () => {
<Search.Content class="container relative w-full rounded-xl bg-white p-4 grid grid-cols-3 gap-4"> <Search.Content class="container relative w-full rounded-xl bg-white p-4 grid grid-cols-3 gap-4">
<Search.Arrow /> <Search.Arrow />
<For each={searchItems()}> <For each={searchItems()}>
{(item) => <ImageComponent ID={item.ImageID} />} {(item) => <ImageComponent ID={item.ImageID} onDelete={onDeleteImage} />}
</For> </For>
<Search.NoResult>No result found</Search.NoResult> <Search.NoResult>No result found</Search.NoResult>
</Search.Content> </Search.Content>