Skip to content

feat(anipic): revamp image pages, SEO gallery flow, and landing experience#25

Open
CodesWithSubham wants to merge 1 commit into
mainfrom
feat/anipic
Open

feat(anipic): revamp image pages, SEO gallery flow, and landing experience#25
CodesWithSubham wants to merge 1 commit into
mainfrom
feat/anipic

Conversation

@CodesWithSubham
Copy link
Copy Markdown
Collaborator

Introduce a complete ImagePages system for AniPic with a new gallery architecture, landing page sections, cursor-based SEO pagination, image detail improvements, and loading UX enhancements.

Landing page

  • Add full hero section with search bar, headline, and CTA buttons
  • Add features section highlighting core platform capabilities
  • Add popular tags cloud with image counts
  • Add trending, most downloaded, and recent image strip sections
  • Add cached landing page data loader

Gallery & tag pages

  • Replace numeric pagination with cursor-based SEO URLs
  • Add dedicated /gallery route as the main browsing page
  • Pre-generate gallery and tag cursor pages statically at build time
  • Support infinite scrolling from preloaded cursor positions
  • Add filter bar with search and sort controls directly in the grid
  • Render first tag page directly without redirecting
  • Pre-generate top tag cursor pages statically

Cursor generation

  • Add aggregation-based cursor generator using $setWindowFields + $documentNumber
  • Generate cursors for the full collection without document limits
  • Fetch only boundary _ids for improved efficiency
  • Add cache-tag-based invalidation support

Image detail pages

  • Add zoomable lightbox with scroll/pinch zoom and drag-to-pan
  • Add share action with Web Share API and clipboard fallback
  • Add metadata sidebar with resolution, downloads, and upload date
  • Add related images section based on shared tags
  • Include createdAt in image payloads
  • Add modal preview route support for image overlays

Skeleton loading system

  • Add reusable Shimmer animation primitive
  • Add responsive masonry skeleton loaders matching grid breakpoints
  • Add route-level loading UIs for home, gallery, tag, and image pages
  • Add detailed image page skeleton layout
  • Replace legacy ImageContent loader system

API & data layer

  • Fix GET route incorrectly reading from req.body instead of searchParams
  • Make nextCursor nullable in API response schema
  • Add new image loaders, schemas, types, utilities, and secure download flow
  • Add cached image and landing-page data utilities

UI & infrastructure

  • Add new ImagePages app route with shared layout and modal support
  • Add MasonryImageGrid, FilterBar, ImageCard, ModalBackdrop, ZoomableImage, and ImageClientAction components
  • Add useColumnCount hook for responsive masonry layouts
  • Add secure server-side download action with token flow
  • Remove legacy sno-based image pages and deprecated loaders
  • Update AniPic model, upload actions, and package metadata

…ience

Introduce a complete ImagePages system for AniPic with a new gallery architecture, landing page sections, cursor-based SEO pagination, image detail improvements, and loading UX enhancements.

Landing page
- Add full hero section with search bar, headline, and CTA buttons
- Add features section highlighting core platform capabilities
- Add popular tags cloud with image counts
- Add trending, most downloaded, and recent image strip sections
- Add cached landing page data loader

Gallery & tag pages
- Replace numeric pagination with cursor-based SEO URLs
- Add dedicated /gallery route as the main browsing page
- Pre-generate gallery and tag cursor pages statically at build time
- Support infinite scrolling from preloaded cursor positions
- Add filter bar with search and sort controls directly in the grid
- Render first tag page directly without redirecting
- Pre-generate top tag cursor pages statically

Cursor generation
- Add aggregation-based cursor generator using $setWindowFields + $documentNumber
- Generate cursors for the full collection without document limits
- Fetch only boundary _ids for improved efficiency
- Add cache-tag-based invalidation support

Image detail pages
- Add zoomable lightbox with scroll/pinch zoom and drag-to-pan
- Add share action with Web Share API and clipboard fallback
- Add metadata sidebar with resolution, downloads, and upload date
- Add related images section based on shared tags
- Include createdAt in image payloads
- Add modal preview route support for image overlays

Skeleton loading system
- Add reusable Shimmer animation primitive
- Add responsive masonry skeleton loaders matching grid breakpoints
- Add route-level loading UIs for home, gallery, tag, and image pages
- Add detailed image page skeleton layout
- Replace legacy ImageContent loader system

API & data layer
- Fix GET route incorrectly reading from req.body instead of searchParams
- Make nextCursor nullable in API response schema
- Add new image loaders, schemas, types, utilities, and secure download flow
- Add cached image and landing-page data utilities

UI & infrastructure
- Add new ImagePages app route with shared layout and modal support
- Add MasonryImageGrid, FilterBar, ImageCard, ModalBackdrop, ZoomableImage, and ImageClientAction components
- Add useColumnCount hook for responsive masonry layouts
- Add secure server-side download action with token flow
- Remove legacy sno-based image pages and deprecated loaders
- Update AniPic model, upload actions, and package metadata
Copilot AI review requested due to automatic review settings May 10, 2026 12:17
@vercel
Copy link
Copy Markdown

vercel Bot commented May 10, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
anix-lab-workspace-admin Ready Ready Preview, Comment May 10, 2026 0:18am
anix7-workspace-anipic Ready Ready Preview, Comment May 10, 2026 0:18am
anix7-workspace-i-used-for-short-url Ready Ready Preview, Comment May 10, 2026 0:18am
anix7-workspace-tools Ready Ready Preview, Comment May 10, 2026 0:18am
anix7-workspace-www-website Ready Ready Preview, Comment May 10, 2026 0:18am

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new “ImagePages” routing and data layer for AniPic, replacing the legacy sno-based image browsing/pages with a cursor-based gallery/tag architecture, a revamped landing experience, and improved loading UX (skeletons + masonry grid).

Changes:

  • Added a new image browsing system: /gallery, /tag/[tag], cursor routes, and /i/[id] image detail with modal overlay support.
  • Implemented cursor-based pagination + loaders: image list loader, cursor generation for sitemap/static params, and an /api/images endpoint for infinite scroll.
  • Added a new masonry-based UI grid, filter bar, and skeleton/shimmer primitives; removed legacy grid and numeric pagination pages.

Reviewed changes

Copilot reviewed 51 out of 52 changed files in this pull request and generated 13 comments.

Show a summary per file
File Description
pnpm-lock.yaml Locks new dependency additions (masonry virtualization).
apps/anipic/utils/const.ts Removes legacy page-size constant used by old pagination.
apps/anipic/package.json Adds @virtuoso.dev/masonry dependency for the new masonry grid.
apps/anipic/lib/db/models/AniPic.ts Updates AniPic schema (removes sno, adds cursor-friendly indexes, makes width/height required).
apps/anipic/hooks/useColumnCount.ts Adds responsive column-count hook for masonry layout.
apps/anipic/features/images/utils.ts Adds cursor encode/decode helpers.
apps/anipic/features/images/types.ts Introduces shared image/cursor types for the new image system.
apps/anipic/features/images/schemas.ts Adds Zod schemas for the images API request/response.
apps/anipic/features/images/loadLandingPageData.ts Adds cached landing-page aggregation/queries for homepage sections.
apps/anipic/features/images/loadImages.ts Implements cursor-based image loading (sorting/filtering/pagination).
apps/anipic/features/images/generateCursors.ts Adds Mongo aggregation-based cursor boundary generation + sitemap URL helpers.
apps/anipic/features/images/const.ts Centralizes new constants and base content filter.
apps/anipic/components/skeletons/Shimmer.tsx Adds shimmer primitive for skeleton UIs.
apps/anipic/components/skeletons/Shimmer.module.css Adds shimmer animation CSS.
apps/anipic/components/skeletons/MasonrySkeleton.tsx Adds responsive masonry skeleton matching grid breakpoints.
apps/anipic/components/skeletons/ImageDetailSkeleton.tsx Adds image detail page skeleton layout.
apps/anipic/components/skeletons/HomePageSkeleton.tsx Adds landing/home skeleton layout.
apps/anipic/components/skeletons/GalleryHeaderSkeleton.tsx Adds gallery header skeleton.
apps/anipic/components/skeletons/FilterBarSkeleton.tsx Adds filter bar skeleton.
apps/anipic/components/MasonryImageGrid.tsx Adds masonry grid with infinite scroll using /api/images.
apps/anipic/components/imageGrid.tsx Removes legacy column-based grid component using sno routes.
apps/anipic/components/ImageCard.tsx Adds new card renderer compatible with the masonry grid.
apps/anipic/components/FilterBar.tsx Adds sort/search UI that drives URL query params.
apps/anipic/app/upload/actions.ts Updates upload action to stop generating sno and rely on _id.
apps/anipic/app/sitemap.ts Replaces numeric pagination sitemap with cursor-based gallery/tag URLs.
apps/anipic/app/page.tsx Removes legacy homepage using numeric pagination and sno-based linking.
apps/anipic/app/api/images/route.ts Adds GET /api/images to serve cursor-based list data for infinite scroll.
apps/anipic/app/(ImagePages)/tag/[tag]/page.tsx Adds first tag page using new loader + masonry UI.
apps/anipic/app/(ImagePages)/tag/[tag]/loading.tsx Adds tag first-page loading skeletons.
apps/anipic/app/(ImagePages)/tag/[tag]/[cursor]/page.tsx Adds tag cursor page + static param generation.
apps/anipic/app/(ImagePages)/tag/[tag]/[cursor]/loading.tsx Adds tag cursor-page loading skeletons.
apps/anipic/app/(ImagePages)/page/[page]/page.tsx Removes legacy numbered pagination route.
apps/anipic/app/(ImagePages)/page.tsx Adds new landing page (hero/features/tags/strips) backed by cached loader.
apps/anipic/app/(ImagePages)/loading.tsx Adds route-level home loading UI.
apps/anipic/app/(ImagePages)/layout.tsx Introduces ImagePages layout with parallel modal slot.
apps/anipic/app/(ImagePages)/i/[sno]/page.tsx Removes old sno-based image detail page.
apps/anipic/app/(ImagePages)/i/[sno]/loading.tsx Removes old image loading UI tied to legacy loader.
apps/anipic/app/(ImagePages)/i/[sno]/ImageClientAction.tsx Removes old client actions for sno-based image pages.
apps/anipic/app/(ImagePages)/i/[sno]/action.ts Removes old download token action tied to sno.
apps/anipic/app/(ImagePages)/i/[id]/ZoomableImage.tsx Adds zoomable lightbox experience for image viewing.
apps/anipic/app/(ImagePages)/i/[id]/page.tsx Adds new _id-based image detail page with related images.
apps/anipic/app/(ImagePages)/i/[id]/loading.tsx Adds image detail loading skeleton.
apps/anipic/app/(ImagePages)/i/[id]/ImageClientAction.tsx Adds download/share client actions for new image detail pages.
apps/anipic/app/(ImagePages)/i/[id]/action.ts Adds secure server action to mint download token + increment downloads.
apps/anipic/app/(ImagePages)/i/[id]/_data.ts Adds cached server data loader for image detail payload.
apps/anipic/app/(ImagePages)/gallery/page.tsx Adds main gallery route with filterable masonry grid.
apps/anipic/app/(ImagePages)/gallery/loading.tsx Adds gallery loading skeletons.
apps/anipic/app/(ImagePages)/gallery/[cursor]/page.tsx Adds gallery cursor route + static param generation.
apps/anipic/app/(ImagePages)/gallery/[cursor]/loading.tsx Adds cursor gallery loading skeletons.
apps/anipic/app/(ImagePages)/@modal/default.tsx Adds default parallel-route modal slot.
apps/anipic/app/(ImagePages)/@modal/(.)i/[id]/page.tsx Adds modal overlay route for image previews.
apps/anipic/app/(ImagePages)/@modal/(.)i/[id]/ModalBackdrop.tsx Adds modal backdrop client component with close behavior.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +32 to +34
function buildCursorCondition(sort: ImageSort, decodedCursor: CursorPayload) {
const objectId = new Types.ObjectId(decodedCursor.id);

Comment on lines +82 to +85
if (skipId) {
const objectId = new Types.ObjectId(skipId);
baseFilter.$and.push({ _id: { $ne: objectId } });
}
Comment on lines +87 to +89
const total = await AniPic.countDocuments(baseFilter);

const sortQuery = ((): Record<string, 1 | -1> => {
Comment on lines +160 to +172
export async function getAllTags(shortBy: "name" | "count" = "name"): Promise<string[]> {
"use cache";
cacheLife("days");
cacheTag("anipicTags");

const AniPic = await getAniPicModel();

const tagAgg = await AniPic.aggregate<{ _id: string; count: number }>([
{ $match: BASE_FILTER },
{ $unwind: "$tags" },
{ $group: { _id: "$tags", count: { $sum: 1 } } },
{ $sort: shortBy === "count" ? { count: -1 } : { _id: 1 } },
]);
Comment on lines +20 to +29
export const ImageApiRequestBodySchema = z.object({
cursor: z.string().optional(),
tags: z
.string()
.transform((val) => (val ? val.split(",").filter(Boolean) : []))
.optional(),
q: z.string().optional(),
sort: z.enum(["latest", "popular", "views", "downloads"]).optional(),
skipId: z.string().optional(), // For related images, to exclude the current image
});
Comment on lines +38 to +43
const { tag } = await params;
const decoded = decodeURIComponent(tag);
return {
title: `#${capitalize(decoded)} AI Anime Art · AniPic`,
description: `Browse AI-generated anime images tagged "${decoded}" on AniPic.`,
alternates: { canonical: `/tag/${tag}` },
Comment on lines +11 to +20
interface Props {
params: Promise<{ cursor: string }>;
searchParams: Promise<{ sort?: string; q?: string; tags?: string }>;
}

export async function generateStaticParams() {
const cursors = await generateGalleryCursors(); // no limit — all pages

if (cursors.length === 0) return [{ cursor: "__empty__" }]; // dummy page to avoid build error when DB is empty
return cursors.map((cursor) => ({ cursor }));
Comment on lines +37 to +43
const { sort: rawSort, q, tags: rawTags } = await searchParams;

const sort = (rawSort as SortOption | undefined) ?? "latest";
const tags = rawTags ? rawTags.split(",").filter(Boolean) : [];

const { images, hasMore, nextCursor } = await loadImages({ cursor, sort, tags, q });

Comment on lines +11 to +27
// back after 300ms (to allow modal close animation to play)
const close = () => {
setIsClosing(true);
setTimeout(() => {
setIsClosing(false);
router.back();
}, 300);
};

// Close on Escape key
useEffect(() => {
const onKey = (e: KeyboardEvent) => {
if (e.key === "Escape") close();
};
document.addEventListener("keydown", onKey);
return () => document.removeEventListener("keydown", onKey);
}, [close]);
Comment on lines +20 to +24
const img = await AniPic.findOneAndUpdate(
{ _id: id, approved: true },
{ $inc: { downloads: 1 } },
{ new: true },
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants