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

Configure Feed

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

refactor: remove focus photo from comments UI

The focus photo feature (highlighting which photo a comment is about)
is removed from the client. Lexicon field retained for compatibility.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

+7 -81
+2 -2
app/lib/components/molecules/GalleryCard.svelte
··· 19 19 import { createQuery, useQueryClient } from '@tanstack/svelte-query' 20 20 import { EyeOff, AlertTriangle, Info } from 'lucide-svelte' 21 21 22 - let { gallery, onCommentClick }: { gallery: GalleryView; onCommentClick?: (focusPhoto: PhotoView | null) => void } = $props() 22 + let { gallery, onCommentClick }: { gallery: GalleryView; onCommentClick?: () => void } = $props() 23 23 24 24 const queryClient = useQueryClient() 25 25 const isOwner = $derived($viewer?.did === gallery.creator?.did) ··· 252 252 253 253 <div class="engagement"> 254 254 <FavoriteButton galleryUri={gallery.uri} viewerFav={gallery.viewer?.fav ?? null} {favCount} bind:favorite={doFavorite} /> 255 - <button class="stat" type="button" onclick={() => requireAuth() && onCommentClick?.(photos[currentIndex] ?? null)}> 255 + <button class="stat" type="button" onclick={() => requireAuth() && onCommentClick?.()}> 256 256 <MessageCircle size={20} /> 257 257 {#if commentCount > 0}<span class="stat-count">{commentCount}</span>{/if} 258 258 </button>
-52
app/lib/components/organisms/CommentSheet.svelte
··· 11 11 let { 12 12 open = false, 13 13 galleryUri, 14 - focusPhotoUri = null, 15 - focusPhotoThumb = null, 16 14 onClose, 17 15 }: { 18 16 open: boolean 19 17 galleryUri: string 20 - focusPhotoUri?: string | null 21 - focusPhotoThumb?: string | null 22 18 onClose: () => void 23 19 } = $props() 24 20 ··· 31 27 let inputValue = $state('') 32 28 let replyToUri = $state<string | null>(null) 33 29 let replyToHandle = $state<string | null>(null) 34 - let localFocusUri = $state<string | null>(null) 35 - let localFocusThumb = $state<string | null>(null) 36 30 let error = $state<string | null>(null) 37 31 let inputEl = $state<HTMLInputElement | null>(null) 38 32 let sheetEl = $state<HTMLDivElement | null>(null) ··· 76 70 // Load comments when sheet opens 77 71 $effect(() => { 78 72 if (open && galleryUri) { 79 - localFocusUri = focusPhotoUri 80 - localFocusThumb = focusPhotoThumb 81 73 loadComments() 82 74 } 83 75 }) ··· 141 133 subject: galleryUri, 142 134 ...(facets ? { facets } : {}), 143 135 ...(replyToUri ? { replyTo: replyToUri } : {}), 144 - ...(localFocusUri ? { focus: localFocusUri } : {}), 145 136 createdAt: now, 146 137 }, 147 138 }) ··· 160 151 cid: '', 161 152 }, 162 153 replyTo: replyToUri ?? undefined, 163 - ...(localFocusUri && localFocusThumb 164 - ? { focus: { uri: localFocusUri, cid: '', thumb: localFocusThumb, fullsize: '', aspectRatio: { width: 1, height: 1 } } } 165 - : {}), 166 154 createdAt: now, 167 155 } as any 168 156 ··· 204 192 inputEl?.focus() 205 193 } 206 194 207 - function clearFocus() { 208 - localFocusUri = null 209 - localFocusThumb = null 210 - } 211 - 212 195 function cancelReply() { 213 196 replyToUri = null 214 197 replyToHandle = null ··· 264 247 <div class="input-bar"> 265 248 {#if $viewer} 266 249 <Avatar did={$viewer.did} src={$viewer.avatar} size={28} /> 267 - {/if} 268 - {#if localFocusThumb} 269 - <div class="focus-indicator"> 270 - <img src={localFocusThumb} alt="Focus target" /> 271 - <button class="clear-focus" onclick={clearFocus}>&times;</button> 272 - </div> 273 250 {/if} 274 251 <div class="input-wrapper"> 275 252 <input ··· 405 382 padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px)); 406 383 border-top: 1px solid var(--border); 407 384 flex-shrink: 0; 408 - } 409 - .focus-indicator { 410 - position: relative; 411 - flex-shrink: 0; 412 - display: flex; 413 - align-items: center; 414 - } 415 - .focus-indicator img { 416 - width: 28px; 417 - height: 28px; 418 - border-radius: 4px; 419 - object-fit: cover; 420 - } 421 - .clear-focus { 422 - position: absolute; 423 - top: -4px; 424 - right: -4px; 425 - width: 16px; 426 - height: 16px; 427 - border-radius: 50%; 428 - background: var(--text-primary); 429 - color: var(--bg-root); 430 - border: none; 431 - cursor: pointer; 432 - font-size: 10px; 433 - display: flex; 434 - align-items: center; 435 - justify-content: center; 436 - padding: 0; 437 385 } 438 386 .input-wrapper { 439 387 flex: 1;
+3 -14
app/lib/components/organisms/FeedList.svelte
··· 1 1 <script lang="ts"> 2 - import type { GalleryView, PhotoView } from '$hatk/client' 2 + import type { GalleryView } from '$hatk/client' 3 3 import GalleryCard from '../molecules/GalleryCard.svelte' 4 4 import CommentSheet from './CommentSheet.svelte' 5 5 import SuggestedFollows from './SuggestedFollows.svelte' ··· 32 32 // Comment sheet state 33 33 let commentSheetOpen = $state(false) 34 34 let commentGalleryUri = $state('') 35 - let commentFocusUri = $state<string | null>(null) 36 - let commentFocusThumb = $state<string | null>(null) 37 35 38 - function openComments(gallery: GalleryView, focusPhoto: PhotoView | null) { 36 + function openComments(gallery: GalleryView) { 39 37 if (!$isAuthenticated) return 40 38 commentGalleryUri = gallery.uri 41 - if (focusPhoto) { 42 - commentFocusUri = focusPhoto.uri 43 - commentFocusThumb = focusPhoto.thumb ?? null 44 - } else { 45 - commentFocusUri = null 46 - commentFocusThumb = null 47 - } 48 39 commentSheetOpen = true 49 40 } 50 41 ··· 103 94 </div> 104 95 {:else} 105 96 {#each items as item, i (`${item.uri}:${i}`)} 106 - <GalleryCard gallery={item} onCommentClick={(photo) => openComments(item, photo)} /> 97 + <GalleryCard gallery={item} onCommentClick={() => openComments(item)} /> 107 98 {#if i === 4 && $isAuthenticated} 108 99 <SuggestedFollows /> 109 100 {/if} ··· 112 103 <CommentSheet 113 104 open={commentSheetOpen} 114 105 galleryUri={commentGalleryUri} 115 - focusPhotoUri={commentFocusUri} 116 - focusPhotoThumb={commentFocusThumb} 117 106 onClose={() => { commentSheetOpen = false }} 118 107 /> 119 108
+2 -13
app/routes/profile/[did]/gallery/[rkey]/+page.svelte
··· 6 6 import CommentSheet from '$lib/components/organisms/CommentSheet.svelte' 7 7 import OGMeta from '$lib/components/atoms/OGMeta.svelte' 8 8 import BskyIcon from '$lib/components/atoms/BskyIcon.svelte' 9 - import type { GalleryView, PhotoView } from '$hatk/client' 9 + import type { GalleryView } from '$hatk/client' 10 10 11 11 let { data } = $props() 12 12 ··· 18 18 const bskyUrl = $derived((gallery as any)?.crossPost?.url ?? null) 19 19 20 20 let commentSheetOpen = $state(false) 21 - let focusPhotoUri = $state<string | null>(null) 22 - let focusPhotoThumb = $state<string | null>(null) 23 21 24 - function openComments(focusPhoto: PhotoView | null) { 25 - if (focusPhoto) { 26 - focusPhotoUri = focusPhoto.uri 27 - focusPhotoThumb = focusPhoto.thumb ?? null 28 - } else { 29 - focusPhotoUri = null 30 - focusPhotoThumb = null 31 - } 22 + function openComments() { 32 23 commentSheetOpen = true 33 24 } 34 25 </script> ··· 58 49 <CommentSheet 59 50 open={commentSheetOpen} 60 51 galleryUri={gallery.uri} 61 - {focusPhotoUri} 62 - {focusPhotoThumb} 63 52 onClose={() => { commentSheetOpen = false }} 64 53 /> 65 54 {/if}