grain.social is a photo sharing platform built on atproto. grain.social
atproto photography appview
50
fork

Configure Feed

Select the types of activity you want to include in your feed.

at cf239b042fb1666e335622f822f15e5bd6834c2c 128 lines 3.7 kB view raw
1import { GalleryView } from "$lexicon/types/social/grain/gallery/defs.ts"; 2import { AtUri } from "@atproto/syntax"; 3import { Button } from "./Button.tsx"; 4import { Dialog } from "./Dialog.tsx"; 5import { Input } from "./Input.tsx"; 6 7export function GallerySelectDialog( 8 { photoUri, userDid, galleries }: Readonly< 9 { 10 photoUri?: string; 11 userDid: string; 12 galleries: GalleryView[]; 13 } 14 >, 15) { 16 return ( 17 <Dialog id="gallery-select-dialog"> 18 <Dialog.Content class="min-h-[calc(100vh-100px)] overflow-hidden flex flex-col gap-4"> 19 <Dialog.X class="fill-zinc-950 dark:fill-zinc-50" /> 20 <Dialog.Title> 21 {photoUri ? "Add to gallery" : "Select gallery"} 22 </Dialog.Title> 23 24 <form> 25 <Input 26 type="text" 27 name="q" 28 placeholder="Enter gallery name or select from the list" 29 hx-get={`/dialogs/gallery/${userDid}/select`} 30 hx-target="#search-results" 31 hx-trigger="input changed delay:500ms, keyup[key=='Enter']" 32 hx-swap="innerHTML" 33 autoFocus 34 /> 35 </form> 36 37 <div id="search-results" class="flex-1 overflow-y-auto"> 38 <GallerySelectDialogSearchResults 39 photoUri={photoUri} 40 galleries={galleries} 41 /> 42 </div> 43 <Dialog.Close variant="secondary"> 44 Close 45 </Dialog.Close> 46 </Dialog.Content> 47 </Dialog> 48 ); 49} 50 51export function GallerySelectDialogSearchResults( 52 { photoUri, galleries }: { 53 photoUri?: string; 54 galleries: GalleryView[]; 55 }, 56) { 57 return ( 58 galleries.length > 0 59 ? ( 60 <ul class="divide-zinc-200 dark:divide-zinc-800 divide-y"> 61 {galleries.map((gallery) => ( 62 <li 63 key={gallery.cid} 64 class="w-full hover:bg-zinc-200 dark:hover:bg-zinc-800" 65 > 66 {photoUri 67 ? ( 68 <button 69 type="button" 70 hx-put={addToGalleryActionLink(photoUri, gallery.uri)} 71 hx-swap="none" 72 class="block text-left w-full px-2 py-4" 73 > 74 {gallery.title} 75 <div class="text-sm text-zinc-600 dark:text-zinc-500"> 76 {gallery.description} 77 </div> 78 </button> 79 ) 80 : ( 81 <a 82 href={uploadPageLink(gallery.uri)} 83 class="block w-full px-2 py-4" 84 > 85 {gallery.title} 86 <div class="text-sm text-zinc-600 dark:text-zinc-500"> 87 {gallery.description} 88 </div> 89 </a> 90 )} 91 </li> 92 ))} 93 </ul> 94 ) 95 : <p>No galleries found.</p> 96 ); 97} 98 99function addToGalleryActionLink(photoUri: string, galleryUri: string) { 100 const photoRKey = new AtUri(photoUri).rkey; 101 const galleryRkey = new AtUri(galleryUri).rkey; 102 return `/actions/gallery/${galleryRkey}/add-photo/${photoRKey}?page=upload`; 103} 104 105function uploadPageLink(galleryUri: string) { 106 const rkey = new AtUri(galleryUri).rkey; 107 return `/upload?gallery=${rkey}`; 108} 109 110export function GallerySelectDialogButton( 111 { userDid }: Readonly<{ userDid: string }>, 112) { 113 return ( 114 <Button 115 type="button" 116 variant="secondary" 117 class="w-full sm:w-fit" 118 hx-get={`/dialogs/gallery/${userDid}/select`} 119 hx-trigger="click" 120 hx-target="#layout" 121 hx-swap="afterbegin" 122 _="on click halt" 123 > 124 <i class="fa fa-filter mr-2" /> 125 Filter by gallery 126 </Button> 127 ); 128}