Refactor image display in activities, resource, and user pages to use ImageGrid component

This commit is contained in:
2025-06-24 16:28:48 +08:00
parent 7bb04cffad
commit 167cb617b8
4 changed files with 159 additions and 30 deletions

View File

@@ -51,3 +51,156 @@ export function SquareImage({ image }: { image: Image }) {
</> </>
); );
} }
export function VerticalImage({ image }: { image: Image }) {
let cover = false;
const imgAspectRatio = image.width / image.height;
if (imgAspectRatio < 0.8 && imgAspectRatio > 0.5) {
cover = true;
}
return (
<>
<div
className="w-full bg-base-200 rounded-lg cursor-pointer aspect-[9/16]"
onClick={() => {
const dialog = document.getElementById(
`image-dialog-${image.id}`,
) as HTMLDialogElement;
dialog.showModal();
}}
>
<img
src={network.getImageUrl(image.id)}
alt={"image"}
className={`w-full h-full rounded-lg ${cover ? "object-cover" : "object-contain"}`}
/>
</div>
<dialog id={`image-dialog-${image.id}`} className="modal">
<div
className={"w-screen h-screen flex items-center justify-center"}
onClick={() => {
const dialog = document.getElementById(
`image-dialog-${image.id}`,
) as HTMLDialogElement;
dialog.close();
}}
>
<img
src={network.getImageUrl(image.id)}
alt={"image"}
className={`object-contain max-w-screen max-h-screen modal-box`}
style={{
padding: 0,
margin: 0,
backgroundColor: "transparent",
boxShadow: "none",
}}
/>
</div>
</dialog>
</>
);
}
export function HorizontalImage({ image }: { image: Image }) {
let cover = false;
const imgAspectRatio = image.width / image.height;
if (imgAspectRatio > 1.2 && imgAspectRatio < 2) {
cover = true;
}
return (
<>
<div
className="w-full aspect-video bg-base-200 rounded-lg cursor-pointer"
onClick={() => {
const dialog = document.getElementById(
`image-dialog-${image.id}`,
) as HTMLDialogElement;
dialog.showModal();
}}
>
<img
src={network.getImageUrl(image.id)}
alt={"image"}
className={`w-full h-full rounded-lg ${cover ? "object-cover" : "object-contain"}`}
/>
</div>
<dialog id={`image-dialog-${image.id}`} className="modal">
<div
className={"w-screen h-screen flex items-center justify-center"}
onClick={() => {
const dialog = document.getElementById(
`image-dialog-${image.id}`,
) as HTMLDialogElement;
dialog.close();
}}
>
<img
src={network.getImageUrl(image.id)}
alt={"image"}
className={`object-contain max-w-screen max-h-screen modal-box`}
style={{
padding: 0,
margin: 0,
backgroundColor: "transparent",
boxShadow: "none",
}}
/>
</div>
</dialog>
</>
);
}
export function ImageGrid({ images }: { images: Image[] }) {
let verticalCount = 0;
let horizontalCount = 0;
for (const image of images) {
const imgAspectRatio = image.width / image.height;
if (imgAspectRatio < 0.8) {
verticalCount++;
} else if (imgAspectRatio > 1.2) {
horizontalCount++;
}
}
if (verticalCount / images.length > 0.5) {
return (
<div
className={
"grid grid-cols-4 sm:grid-cols-6 md:grid-cols-7 lg:grid-cols-8 gap-2 px-1 py-2"
}
>
{images.map((image) => (
<VerticalImage key={image.id} image={image} />
))}
</div>
);
} else if (horizontalCount / images.length > 0.5) {
return (
<div
className={
"grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-2 px-1 py-2"
}
>
{images.map((image) => (
<HorizontalImage key={image.id} image={image} />
))}
</div>
);
} else {
return (
<div
className={
"grid grid-cols-3 sm:grid-cols-5 md:grid-cols-6 lg:grid-cols-8 gap-2 px-1 py-2"
}
>
{images.map((image) => (
<SquareImage key={image.id} image={image} />
))}
</div>
);
}
}

View File

@@ -6,7 +6,7 @@ import { useTranslation } from "react-i18next";
import { MdArrowRight } from "react-icons/md"; import { MdArrowRight } from "react-icons/md";
import { useNavigate } from "react-router"; import { useNavigate } from "react-router";
import Loading from "../components/loading.tsx"; import Loading from "../components/loading.tsx";
import {SquareImage} from "../components/image.tsx"; import { ImageGrid } from "../components/image.tsx";
export default function ActivitiesPage() { export default function ActivitiesPage() {
const [activities, setActivities] = useState<Activity[]>([]); const [activities, setActivities] = useState<Activity[]>([]);
@@ -98,15 +98,7 @@ function ActivityCard({ activity }: { activity: Activity }) {
<div className={"text-sm mx-1 whitespace-pre-wrap"}> <div className={"text-sm mx-1 whitespace-pre-wrap"}>
{activity.comment?.content} {activity.comment?.content}
</div> </div>
<div <ImageGrid images={activity.comment?.images ?? []} />
className={
"grid grid-cols-3 sm:grid-cols-5 md:grid-cols-6 lg:grid-cols-8 gap-2 px-1 py-2"
}
>
{(activity.comment?.images ?? []).map((image) => (
<SquareImage key={image.id} image={image} />
))}
</div>
<div className={"flex items-center mt-1"}> <div className={"flex items-center mt-1"}>
<MdArrowRight /> <MdArrowRight />
<span className={"text-sm text-base-content/80"}> <span className={"text-sm text-base-content/80"}>

View File

@@ -47,7 +47,7 @@ import Button from "../components/button.tsx";
import Badge, { BadgeAccent } from "../components/badge.tsx"; import Badge, { BadgeAccent } from "../components/badge.tsx";
import Input, { TextArea } from "../components/input.tsx"; import Input, { TextArea } from "../components/input.tsx";
import { useAppContext } from "../components/AppContext.tsx"; import { useAppContext } from "../components/AppContext.tsx";
import { SquareImage } from "../components/image.tsx"; import { ImageGrid, SquareImage } from "../components/image.tsx";
export default function ResourcePage() { export default function ResourcePage() {
const params = useParams(); const params = useParams();
@@ -1398,15 +1398,7 @@ function CommentTile({ comment }: { comment: Comment }) {
</div> </div>
)} )}
</div> </div>
<div <ImageGrid images={comment.images} />
className={
"grid grid-cols-3 sm:grid-cols-5 md:grid-cols-6 lg:grid-cols-8 gap-2 p-2"
}
>
{comment.images.map((image) => (
<SquareImage key={image.id} image={image} />
))}
</div>
{app.user?.id === comment.user.id && ( {app.user?.id === comment.user.id && (
<div className={"flex flex-row-reverse"}> <div className={"flex flex-row-reverse"}>
<DeleteCommentDialog commentId={comment.id} /> <DeleteCommentDialog commentId={comment.id} />

View File

@@ -7,7 +7,7 @@ import ResourcesView from "../components/resources_view";
import Loading from "../components/loading"; import Loading from "../components/loading";
import Pagination from "../components/pagination"; import Pagination from "../components/pagination";
import { MdOutlineArrowRight } from "react-icons/md"; import { MdOutlineArrowRight } from "react-icons/md";
import {SquareImage} from "../components/image.tsx"; import { ImageGrid } from "../components/image.tsx";
export default function UserPage() { export default function UserPage() {
const [user, setUser] = useState<User | null>(null); const [user, setUser] = useState<User | null>(null);
@@ -208,15 +208,7 @@ function CommentTile({ comment }: { comment: CommentWithResource }) {
<div className={"p-2 whitespace-pre-wrap text-sm"}> <div className={"p-2 whitespace-pre-wrap text-sm"}>
{limitArticleLength(comment.content, 200)} {limitArticleLength(comment.content, 200)}
</div> </div>
<div <ImageGrid images={comment.images} />
className={
"grid grid-cols-3 sm:grid-cols-5 md:grid-cols-6 lg:grid-cols-8 gap-2 p-2"
}
>
{comment.images.map((image) => (
<SquareImage key={image.id} image={image} />
))}
</div>
<a <a
className="text-sm text-base-content/80 p-1 hover:text-primary cursor-pointer transition-all" className="text-sm text-base-content/80 p-1 hover:text-primary cursor-pointer transition-all"
onClick={() => { onClick={() => {