Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add gallery row #76

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions app/(explorer)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import Link from "next/link";

import { Metadata } from "next";
import CtaLink from "../../components/CtaLink";
import Footer from "../../components/Footer";

import GalleryPreview from "../../components/GalleryPreview";
import GalleryRow from "../../components/GalleryRow";
import Header from "../../components/Header";
import Intro from "../../components/Intro";
import SearchBar from "../../components/SearchBar";
import { Metadata } from "next";
import { mimeTypes } from "../../lib/utils";

export const metadata: Metadata = {
title: "Hiro Ordinals Explorer | ordinals.hiro.so",
Expand All @@ -18,7 +21,7 @@ export default function Home() {
return (
<>
<Header />
<main className="mx-auto flex min-h-screen max-w-5xl flex-col items-center justify-between space-y-6 p-6">
<main className="mx-auto flex min-h-screen w-full max-w-5xl flex-col items-center justify-between space-y-6 p-6">
{/* Intro Section */}
<div className="mx-auto max-w-2xl space-y-10">
{/* todo: wrap in motion */}
Expand Down Expand Up @@ -49,6 +52,27 @@ export default function Home() {
</div>
</div>

{/* Gallery Section */}
<div className="w-full">
<div className="mt-20 flex justify-between">
<p className="text-sm uppercase">Latest Image Inscriptions</p>
<Link
href="/explore?f=image"
className="text-sm text-neutral-500 hover:underline"
>
Explore &rarr;
</Link>
</div>
<div className="mt-4" />
{/* <div className="mx-auto mb-4 mt-3 h-12 w-0 border border-dashed border-l-black" /> */}
<GalleryRow query={mimeTypes.image.map((i) => ["mime_type", i])} />
{/* <div className="mt-16 flex justify-around">
<CtaLink href="/explore">
Explore all, sort, and filter &rarr;
</CtaLink>
</div> */}
</div>

<div className="w-full py-8">
<hr className="border-dashed border-neutral-200" />
</div>
Expand Down
2 changes: 1 addition & 1 deletion components/CtaLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const CtaLink = (props: {
<Link
href={props.href}
className={cn(
"cta-link rounded-[4px] border-2 border-black bg-neutral-500 px-[24px] py-[15px] text-white",
"cta-link rounded-[4px] border-2 border-black bg-neutral-500 px-[24px] py-[15px] text-white transition-colors hover:bg-black",
props.className
)}
>
Expand Down
31 changes: 4 additions & 27 deletions components/GalleryFull.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,15 @@
"use client";

import useSWR from "swr";
import { API_URL } from "../lib/constants";
import { fetcher } from "../lib/utils";
import { InscriptionResponse, ListResponse } from "../lib/types";
import InscriptionCard from "./inscriptions/InscriptionCard";
import { fetcher, mimeTypes } from "../lib/utils";
import Error from "./Error";
import InscriptionCard from "./inscriptions/InscriptionCard";

// const limit = 60; // LCM of 3, 4, 5, 6
const limit = 20; // todo: increase limit on api end

// todo: add more common mime types
// From https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types
const mimeTypes = {
// Safe Images https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types#image_types
image: [
"image/apng",
"image/avif",
"image/gif",
"image/jpg",
"image/jpeg",
"image/png",
"image/svg+xml",
"image/webp",
],
audio: ["audio/midi", "audio/mod", "audio/mpeg"],
video: ["video/mp4", "video/webm"],
text: ["text/html", "text/markdown", "text/plain"],
binary: [
"application/epub+zip",
"application/json",
"application/pdf",
"application/pgp-signature",
],
} as const;

export type V1InscriptionsOptions = {
page: number;
order: string;
Expand Down
10 changes: 6 additions & 4 deletions components/GalleryPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import { useEffect } from "react";
import useSWR from "swr";

import { API_URL } from "../lib/constants";
import { fetcher } from "../lib/utils";
import { cn, fetcher } from "../lib/utils";
import { lastInscriptionDataAtom } from "../lib/store";
import { InscriptionResponse, ListResponse } from "../lib/types";
import InscriptionCard from "./inscriptions/InscriptionCard";

const limit = 8

const GalleryPreview = () => {
const setLastInscriptionData = useSetAtom(lastInscriptionDataAtom);

Expand All @@ -29,13 +31,13 @@ const GalleryPreview = () => {

if (error) return <span>Something went wrong ʕ•̠͡•ʔ</span>;

const previews = data ? data.results : Array(12).fill(null); // skeleton values
const previews = data ? data.results : Array(limit).fill(null); // skeleton values

return (
<>
<div className="grid grid-cols-3 gap-4 md:grid-cols-4">
{previews.slice(0, 12).map((i, index) => (
<InscriptionCard key={index} inscription={i} />
{previews.slice(0, limit).map((i, index) => (
<InscriptionCard key={index} inscription={i} className={cn(index >= 6 && "hidden md:block")}/>
))}
</div>
{/* todo: re-add figma link to full feed */}
Expand Down
51 changes: 51 additions & 0 deletions components/GalleryRow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"use client";

import { motion, useScroll, useTransform } from "framer-motion";
import { useRef } from "react";
import useSWR from "swr";
import { API_URL } from "../lib/constants";
import { InscriptionResponse, ListResponse } from "../lib/types";
import { fetcher } from "../lib/utils";
import InscriptionCard from "./inscriptions/InscriptionCard";

const GalleryRow = ({ query }: { query?: string[][] }) => {
const rowRef = useRef<HTMLDivElement>(null);
const { scrollXProgress } = useScroll({
container: rowRef,
} as any); // todo: update framer motion, remove any

const opacityLeft = useTransform(scrollXProgress, [0, 0.05, 1], [0, 1, 1]);
const opacityRight = useTransform(scrollXProgress, [0, 0.95, 1], [1, 1, 0]);

const params = new URLSearchParams(query);
const { data, error, isLoading } = useSWR<ListResponse<InscriptionResponse>>(
`${API_URL}/inscriptions?${params?.toString() ?? ""}`,
fetcher
);

if (error) return <span>Something went wrong ʕ•̠͡•ʔ</span>;

const previews = data ? data.results : Array(12).fill(null); // skeleton values

return (
<div className="relative">
<div className="flex flex-row gap-4 overflow-scroll" ref={rowRef}>
{previews.slice(0, 12).map((i, index) => (
<div className="mb-3 shrink-0 grow-0 basis-1/5" key={index}>
<InscriptionCard inscription={i} className="w-full" />
</div>
))}
</div>
<motion.div
style={{ opacity: opacityLeft }}
className="absolute bottom-0 left-0 top-0 w-20 bg-gradient-to-l from-transparent to-white"
/>
<motion.div
style={{ opacity: opacityRight }}
className="absolute bottom-0 right-0 top-0 w-20 bg-gradient-to-r from-transparent to-white"
/>
</div>
);
};

export default GalleryRow;
14 changes: 12 additions & 2 deletions components/inscriptions/InscriptionCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,22 @@ import InscriptionRender from "./InscriptionRender";
const InscriptionCard = ({
inscription,
light = false,
className,
}: {
inscription?: InscriptionResponse;
light?: boolean;
className?: string;
}) => {
if (!inscription?.id)
return (
// make the grid take up the maximal space, even when a grid item is empty
// todo: double-check skeleton styles are the same as the real ones
<div className="space-y-2 rounded-md border sm:p-2 md:space-y-3 md:p-3 lg:space-y-5 lg:p-5">
<div
className={cn(
"block space-y-2 rounded-md border sm:p-2 md:space-y-3 md:p-3 lg:space-y-5 lg:p-5",
className
)}
>
<div className="aspect-square rounded-[4px]" />
<div
className={cn(
Expand All @@ -33,7 +40,10 @@ const InscriptionCard = ({
return (
<Link
href={`/inscription/${inscription.id}`}
className="space-y-2 rounded-md border sm:p-2 md:space-y-3 md:p-3 lg:space-y-5 lg:p-5"
className={cn(
"block space-y-2 rounded-md border sm:p-2 md:space-y-3 md:p-3 lg:space-y-5 lg:p-5",
className
)}
>
<div className="aspect-square w-full overflow-hidden rounded-[4px]">
<InscriptionRender
Expand Down
25 changes: 25 additions & 0 deletions lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,31 @@
import { ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";

// todo: add more common mime types
// From https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types
export const mimeTypes = {
// Safe Images https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types#image_types
image: [
"image/apng",
"image/avif",
"image/gif",
"image/jpg",
"image/jpeg",
"image/png",
"image/svg+xml",
"image/webp",
],
audio: ["audio/midi", "audio/mod", "audio/mpeg"],
video: ["video/mp4", "video/webm"],
text: ["text/html", "text/markdown", "text/plain"],
binary: [
"application/epub+zip",
"application/json",
"application/pdf",
"application/pgp-signature",
],
} as const;

export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
Expand Down
Loading