diff --git a/public/images/Intersex-inclusive_pride_flag.png b/public/images/Intersex-inclusive_pride_flag.png
new file mode 100644
index 0000000..a6663be
Binary files /dev/null and b/public/images/Intersex-inclusive_pride_flag.png differ
diff --git a/public/images/signal-username-qr-code.png b/public/images/signal-username-qr-code.png
new file mode 100644
index 0000000..84d5b56
Binary files /dev/null and b/public/images/signal-username-qr-code.png differ
diff --git a/src/app/(normal)/about/page.tsx b/src/app/(normal)/about/page.tsx
new file mode 100644
index 0000000..ccf89da
--- /dev/null
+++ b/src/app/(normal)/about/page.tsx
@@ -0,0 +1,58 @@
+import { MailIcon, MessageCircleIcon, RadioIcon, WavesIcon } from "lucide-react";
+import Image from "next/image";
+import Link from "next/link";
+
+export default function AboutPage() {
+ return (
+
- {category && (
- <>
-
- Category: {category.name}
-
- {category.description}
-
-
-
- >
- )}
+
+
+ Category: {category.name}
+
+ {category.description}
+
+
+
);
}
\ No newline at end of file
diff --git a/src/app/(normal)/galleries/[gallerySlug]/[albumSlug]/[imageId]/page.tsx b/src/app/(normal)/galleries/[gallerySlug]/[albumSlug]/[imageId]/page.tsx
index 174597a..2d0bbfc 100644
--- a/src/app/(normal)/galleries/[gallerySlug]/[albumSlug]/[imageId]/page.tsx
+++ b/src/app/(normal)/galleries/[gallerySlug]/[albumSlug]/[imageId]/page.tsx
@@ -1,5 +1,5 @@
+import ImageCard from "@/components/cards/ImageCard";
import ArtistInfoBox from "@/components/images/ArtistInfoBox";
-import GlowingImageWithToggle from "@/components/images/GlowingImageWithToggle";
import ImageMetadataBox from "@/components/images/ImageMetadataBox";
import prisma from "@/lib/prisma";
@@ -11,11 +11,11 @@ export default async function ImagePage({ params }: { params: { gallerySlug: str
id: imageId
},
include: {
- artist: { include: { socials: true } },
+ artist: {
+ include: { socials: { where: { isVisible: true }, orderBy: [{ sortIndex: 'asc' }, { isPrimary: "desc" }] } }
+ },
colors: { include: { color: true } },
- extractColors: { include: { extract: true } },
- palettes: { include: { palette: { include: { items: true } } } },
- album: true,
+ album: { include: { gallery: true } },
categories: true,
tags: true,
variants: true,
@@ -26,82 +26,27 @@ export default async function ImagePage({ params }: { params: { gallerySlug: str
if (!image) return
Image not found
- const resizedVariant = image.variants.find(v => v.type === "resized");
-
return (
- {image && (
-
-
-
{image.imageName}
-
- {resizedVariant &&
-
- }
-
- {image.artist && }
-
-
-
Image Metadata
-
-
Name: {image.imageName}
- {image.altText &&
Alt Text: {image.altText}
}
- {image.description &&
Description: {image.description}
}
- {/*
NSFW: {image.nsfw ? "Yes" : "No"}
-
Upload Date: {new Date(image.uploadDate).toLocaleDateString()}
- {image.source &&
Source: {image.source}
}
- {image.fileType &&
File Type: {image.fileType}
}
- {image.fileSize &&
Size: {(image.fileSize / 1024).toFixed(1)} KB
}
- {image.creationDate && (
-
Created: {new Date(image.creationDate).toLocaleDateString()}
- )}
- {image.creationYear && image.creationMonth && (
-
Creation: {image.creationMonth}/{image.creationYear}
- )} */}
-
- {/* {image.metadata && (
- <>
-
Metadata
-
Width: {image.metadata.width}px
-
Height: {image.metadata.height}px
-
Format: {image.metadata.format}
-
Color Space: {image.metadata.space}
-
Channels: {image.metadata.channels}
- {image.metadata.bitsPerSample && (
-
Bits Per Sample: {image.metadata.bitsPerSample}
- )}
- {image.metadata.hasAlpha !== null && (
-
Has Alpha: {image.metadata.hasAlpha ? "Yes" : "No"}
- )}
- >
- )} */}
-
- {/* {image.stats && (
- <>
-
Statistics
-
Entropy: {image.stats.entropy.toFixed(2)}
-
Sharpness: {image.stats.sharpness.toFixed(2)}
-
Dominant Color: rgb({image.stats.dominantR}, {image.stats.dominantG}, {image.stats.dominantB})
-
Is Opaque: {image.stats.isOpaque ? "Yes" : "No"}
- >
- )} */}
-
-
-
-
+
+
+
{image.imageName}
- )}
+
+
+
);
}
\ No newline at end of file
diff --git a/src/app/(normal)/galleries/[gallerySlug]/[albumSlug]/page.tsx b/src/app/(normal)/galleries/[gallerySlug]/[albumSlug]/page.tsx
index 49c7eff..7921263 100644
--- a/src/app/(normal)/galleries/[gallerySlug]/[albumSlug]/page.tsx
+++ b/src/app/(normal)/galleries/[gallerySlug]/[albumSlug]/page.tsx
@@ -1,4 +1,4 @@
-import ImageList from "@/components/lists/ImageList";
+import AlbumImageList from "@/components/lists/AlbumImageList";
import prisma from "@/lib/prisma";
export default async function GalleryPage({ params }: { params: { gallerySlug: string, albumSlug: string } }) {
@@ -21,7 +21,10 @@ export default async function GalleryPage({ params }: { params: { gallerySlug: s
},
},
include: {
- images: { include: { colors: { include: { color: true } } } },
+ images: {
+ include: { colors: { include: { color: true } } },
+ orderBy: [{ sortIndex: 'asc' }, { imageName: 'asc' }]
+ },
}
})
@@ -35,7 +38,7 @@ export default async function GalleryPage({ params }: { params: { gallerySlug: s
{album.description}
-
+
>
)}
diff --git a/src/app/(normal)/galleries/[gallerySlug]/page.tsx b/src/app/(normal)/galleries/[gallerySlug]/page.tsx
index 374ad1d..c946304 100644
--- a/src/app/(normal)/galleries/[gallerySlug]/page.tsx
+++ b/src/app/(normal)/galleries/[gallerySlug]/page.tsx
@@ -9,7 +9,11 @@ export default async function GalleryPage({ params }: { params: { gallerySlug: s
slug: gallerySlug
},
include: {
- albums: { where: { images: { some: {} } }, include: { coverImage: { include: { colors: { include: { color: true } } } } } }
+ albums: {
+ where: { images: { some: {} } },
+ include: { coverImage: { include: { colors: { include: { color: true } } } } },
+ orderBy: { sortIndex: 'asc' }
+ }
}
})
diff --git a/src/app/(normal)/tags/[id]/page.tsx b/src/app/(normal)/tags/[id]/page.tsx
new file mode 100644
index 0000000..5382ade
--- /dev/null
+++ b/src/app/(normal)/tags/[id]/page.tsx
@@ -0,0 +1,38 @@
+import ImageList from "@/components/lists/ImageList";
+import prisma from "@/lib/prisma";
+
+export default async function TagsSinglePage({ params }: { params: { id: string } }) {
+ const { id } = await params;
+
+ const tag = await prisma.tag.findUnique({
+ where: {
+ id,
+ },
+ include: {
+ images: {
+ include: {
+ variants: true,
+ album: { include: { gallery: true } },
+ colors: { include: { color: true } }
+ },
+ orderBy: [{ sortIndex: 'asc' }, { imageName: 'asc' }]
+ },
+ }
+ });
+
+ if (!tag) {
+ throw new Error("Tag not found")
+ }
+
+ return (
+
+
+ Tag: {tag.name}
+
+ {tag.description}
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/components/cards/GlowingBorderWrapper.tsx b/src/components/cards/GlowingBorderWrapper.tsx
index b28c081..ac3543c 100644
--- a/src/components/cards/GlowingBorderWrapper.tsx
+++ b/src/components/cards/GlowingBorderWrapper.tsx
@@ -9,6 +9,7 @@ interface GlowingBorderWrapperProps {
className?: string
animate?: boolean
colors?: string[]
+ size?: "default" | "large"
}
export function GlowingBorderWrapper({
@@ -16,6 +17,7 @@ export function GlowingBorderWrapper({
className,
animate = true,
colors = [],
+ size = "default"
}: GlowingBorderWrapperProps) {
const { theme } = useTheme()
@@ -25,10 +27,16 @@ export function GlowingBorderWrapper({
? "rgba(255,255,255,0.2), rgba(255,255,255,0.05)"
: "rgba(0,0,0,0.1), rgba(0,0,0,0.03)"
+ const padding = size === "large" ? "p-[6px]" : "p-[2px]"
+ const roundedOuter = size === "large" ? "rounded-xl" : "rounded-lg"
+ const roundedInner = size === "large" ? "rounded-lg" : "rounded-md"
+
return (
-
{children}
+
{children}
)
}
diff --git a/src/components/cards/ImageCard.tsx b/src/components/cards/ImageCard.tsx
index 1eb595b..8fc3d3e 100644
--- a/src/components/cards/ImageCard.tsx
+++ b/src/components/cards/ImageCard.tsx
@@ -1,6 +1,6 @@
"use client"
-import { type Color, type Image, type ImageColor } from "@/generated/prisma"
+import { ImageVariant, type Color, type Image, type ImageColor } from "@/generated/prisma"
import { useGlobalSettings } from "@/hooks/useGlobalSettings"
import clsx from "clsx"
import { EyeOffIcon } from "lucide-react"
@@ -10,32 +10,55 @@ import { GlowingBorderWrapper } from "./GlowingBorderWrapper"
import { TagBadge } from "./TagBadge"
type ImageWithColors = Image & {
- colors?: (ImageColor & { color: Color })[]
+ colors?: (ImageColor & { color: Color })[],
+ variants?: ImageVariant[]
}
type ImageCardProps = {
- image: ImageWithColors,
- gallerySlug?: string
- albumSlug?: string
+ image: ImageWithColors;
+ gallerySlug?: string;
+ albumSlug?: string;
+ size?: "default" | "large";
+ disableLink?: boolean;
}
-export default function ImageCard({ image, gallerySlug, albumSlug }: ImageCardProps) {
+export default function ImageCard({
+ image,
+ gallerySlug,
+ albumSlug,
+ size = "default",
+ disableLink = false,
+}: ImageCardProps) {
const { showNSFW, animateGlow } = useGlobalSettings()
+ const shouldBlur = image.nsfw && !showNSFW
- const href = `/galleries/${gallerySlug}/${albumSlug}/${image.id}`
+ const isLarge = size === "large"
+ const href = isLarge
+ ? `/raw/${image.id}`
+ : `/galleries/${gallerySlug}/${albumSlug}/${image.id}`
const borderColors =
image.colors?.map((c) => c.color?.hex).filter((hex): hex is string => Boolean(hex)) ?? []
- const shouldBlur = image.nsfw && !showNSFW
+ const thumbnailSrc = `/api/image/thumbnails/${image.fileKey}.webp`
+ const resizedVariant = image.variants?.find((v) => v.type === "resized")
+ const resizedSrc = resizedVariant ? `/api/image/${resizedVariant.s3Key}` : undefined
- const content = (
+ const imageSrc = isLarge && resizedSrc ? resizedSrc : thumbnailSrc
+ const width = isLarge && resizedVariant?.width ? resizedVariant.width : undefined
+ const height = isLarge && resizedVariant?.height ? resizedVariant.height : undefined
+
+ const imageElement = (
-
+
{image.nsfw && (
@@ -49,19 +72,29 @@ export default function ImageCard({ image, gallerySlug, albumSlug }: ImageCardPr
)}
-
-
{image.imageName}
-
+ {!isLarge && (
+
+
{image.imageName}
+
+ )}
)
- return (
+
+ return disableLink ? (
+ animateGlow && borderColors.length > 0 ? (
+
{imageElement}
+ ) : (
+ imageElement
+ )
+ ) : (
{animateGlow && borderColors.length > 0 ? (
-
{content}
+
{imageElement}
) : (
- content
+ imageElement
)}
)
}
+
diff --git a/src/components/images/ArtistInfoBox.tsx b/src/components/images/ArtistInfoBox.tsx
index 68e496e..5f03510 100644
--- a/src/components/images/ArtistInfoBox.tsx
+++ b/src/components/images/ArtistInfoBox.tsx
@@ -1,9 +1,7 @@
-// components/images/ArtistInfoBox.tsx
"use client"
import { Artist, Social } from "@/generated/prisma"
import { getSocialIcon } from "@/utils/socialIconMap"
-import { UserIcon } from "lucide-react"
import Link from "next/link"
type ArtistWithItems = Artist & {
@@ -13,9 +11,8 @@ type ArtistWithItems = Artist & {
export default function ArtistInfoBox({ artist }: { artist: ArtistWithItems }) {
return (
-
-
-
+
+
{artist.displayName}
@@ -34,9 +31,6 @@ export default function ArtistInfoBox({ artist }: { artist: ArtistWithItems }) {
>
{social.platform}: {social.handle}
- {social.isPrimary && (
-
(main)
- )}
)
diff --git a/src/components/images/ImageMetadataBox.tsx b/src/components/images/ImageMetadataBox.tsx
index 2a250e2..6b0488b 100644
--- a/src/components/images/ImageMetadataBox.tsx
+++ b/src/components/images/ImageMetadataBox.tsx
@@ -1,23 +1,67 @@
-// components/images/ImageMetadataBox.tsx
"use client"
import { Album, Category, Tag } from "@/generated/prisma"
-import { FolderIcon, LayersIcon, TagIcon } from "lucide-react"
+import { CalendarDaysIcon, FolderIcon, LayersIcon, QuoteIcon, TagIcon } from "lucide-react"
import Link from "next/link"
type Props = {
album: Album | null
categories: Category[]
tags: Tag[]
+ creationDate?: Date | null
+ creationYear?: number | null
+ creationMonth?: number | null
+ altText?: string | null
+ description?: string | null
}
-export default function ImageMetadataBox({ album, categories, tags }: Props) {
+export default function ImageMetadataBox({
+ album,
+ categories,
+ tags,
+ creationDate,
+ creationYear,
+ creationMonth,
+ altText,
+ description
+}: Props) {
+ const creationLabel = creationDate
+ ? new Date(creationDate).toLocaleDateString("en-US", {
+ year: "numeric",
+ month: "long",
+ day: "numeric",
+ })
+ : creationYear && creationMonth
+ ? `${creationMonth.toString().padStart(2, "0")}/${creationYear}`
+ : null
+
return (
- {album && (
+ {altText && (
-
-
+
+ {altText}
+
+ )}
+
+ {description && (
+
+
+ {description}
+
+ )}
+
+ {creationLabel && (
+
+
+ {creationLabel}
+
+ )}
+
+ {album && (
+
+
+
{album.name}
@@ -25,12 +69,12 @@ export default function ImageMetadataBox({ album, categories, tags }: Props) {
{categories.length > 0 && (
-
+
{categories.map((cat) => (
{cat.name}
@@ -40,12 +84,12 @@ export default function ImageMetadataBox({ album, categories, tags }: Props) {
{tags.length > 0 && (
-
+
{tags.map((tag) => (
{tag.name}
diff --git a/src/components/lists/AlbumImageList.tsx b/src/components/lists/AlbumImageList.tsx
new file mode 100644
index 0000000..b0f291e
--- /dev/null
+++ b/src/components/lists/AlbumImageList.tsx
@@ -0,0 +1,16 @@
+import { Image } from "@/generated/prisma";
+import ImageCard from "../cards/ImageCard";
+
+export default function AlbumImageList({ images, gallerySlug, albumSlug }: { images: Image[], gallerySlug: string, albumSlug: string }) {
+ if (!images.length) return
There are no images here!
;
+
+ return (
+
+
+ {images.map((image) => (
+
+ ))}
+
+
+ );
+}
diff --git a/src/components/lists/GalleryList.tsx b/src/components/lists/GalleryList.tsx
index 61c1716..d9d317e 100644
--- a/src/components/lists/GalleryList.tsx
+++ b/src/components/lists/GalleryList.tsx
@@ -12,6 +12,7 @@ export default async function GalleryList() {
},
},
},
+ orderBy: { sortIndex: 'asc' }
});
if (!galleries.length) return
There are no galleries here!
;
diff --git a/src/components/lists/ImageList.tsx b/src/components/lists/ImageList.tsx
index 5eeb3ed..a39ccad 100644
--- a/src/components/lists/ImageList.tsx
+++ b/src/components/lists/ImageList.tsx
@@ -1,15 +1,23 @@
-import { Image } from "@/generated/prisma";
+import { Album, Gallery, Image } from "@/generated/prisma";
import ImageCard from "../cards/ImageCard";
-export default function ImageList({ images, gallerySlug, albumSlug }: { images: Image[], gallerySlug: string, albumSlug: string }) {
+type ImageWithItems = Image & {
+ album: Album & {
+ gallery: Gallery | null
+ } | null
+}
+
+export default function ImageList({ images }: { images: ImageWithItems[] }) {
if (!images.length) return
There are no images here!
;
return (
- {images.map((image) => (
-
- ))}
+ {images.map((image) => {
+ const gallerySlug = image.album?.gallery?.slug ? image.album.gallery.slug : ''
+ const albumSlug = image.album?.slug ? image.album.slug : ''
+ return ( )
+ })}
);