grain.social is a photo sharing platform built on atproto.
1import { GalleryView } from "$lexicon/types/social/grain/gallery/defs.ts";
2import { isPhotoView } from "$lexicon/types/social/grain/photo/defs.ts";
3import { AtUri } from "@atproto/syntax";
4import { ModerationDecsion } from "../lib/moderation.ts";
5import { CommentsButton } from "../modules/comments.tsx";
6import { EditGalleryButton } from "./EditGalleryDialog.tsx";
7import { FavoriteButton } from "./FavoriteButton.tsx";
8import { GalleryInfo } from "./GalleryInfo.tsx";
9import { GalleryLayout } from "./GalleryLayout.tsx";
10import { ModerationWrapper } from "./ModerationWrapper.tsx";
11import { ShareGalleryDialogButton } from "./ShareGalleryDialog.tsx";
12
13export function GalleryPage({
14 gallery,
15 currentUserDid,
16 modDecision,
17}: Readonly<{
18 gallery: GalleryView;
19 currentUserDid?: string;
20 modDecision?: ModerationDecsion;
21}>) {
22 const isCreator = currentUserDid === gallery.creator.did;
23 const isLoggedIn = !!currentUserDid;
24 const galleryItems = gallery.items?.filter(isPhotoView) ?? [];
25 return (
26 <div class="px-4" id="gallery-page">
27 <div id="dialog-target" />
28 <div class="flex flex-col sm:flex-row sm:items-center sm:justify-between mt-4 mb-2">
29 <GalleryInfo gallery={gallery} />
30 {isLoggedIn && isCreator
31 ? (
32 <div class="flex self-start gap-2 w-full flex-col sm:flex-row sm:justify-end">
33 <EditGalleryButton gallery={gallery} />
34 <div class="flex flex-row gap-2 w-full sm:w-fit">
35 <FavoriteButton class="flex-1" gallery={gallery} />
36 <CommentsButton class="flex-1" gallery={gallery} />
37 <ShareGalleryDialogButton class="flex-1" gallery={gallery} />
38 </div>
39 </div>
40 )
41 : null}
42 {!isCreator
43 ? (
44 <div class="flex self-start gap-2 flex-row w-full sm:w-fit">
45 <FavoriteButton gallery={gallery} />
46 <CommentsButton class="flex-1" gallery={gallery} />
47 <ShareGalleryDialogButton class="flex-1" gallery={gallery} />
48 </div>
49 )
50 : null}
51 </div>
52 {isLoggedIn && isCreator && gallery.items?.length === 0
53 ? (
54 <div
55 hx-get={`/dialogs/gallery/${new AtUri(gallery.uri).rkey}/photos`}
56 hx-trigger="load"
57 hx-target="#dialog-target"
58 hx-swap="innerHTML"
59 />
60 )
61 : null}
62 {
63 <ModerationWrapper moderationDecision={modDecision} class="mb-2">
64 <GalleryLayout
65 layoutButtons={
66 <>
67 <GalleryLayout.ModeButton mode="justified" />
68 <GalleryLayout.ModeButton mode="masonry" />
69 </>
70 }
71 >
72 <GalleryLayout.Container>
73 {galleryItems?.length
74 ? galleryItems.map((photo) => (
75 <GalleryLayout.Item
76 key={photo.cid}
77 photo={photo}
78 gallery={gallery}
79 />
80 ))
81 : null}
82 </GalleryLayout.Container>
83 </GalleryLayout>
84 </ModerationWrapper>
85 }
86 </div>
87 );
88}