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.

feat: add compact count formatting for large numbers

Extract compactCount util (1K, 1.2M) and apply to gallery fav counts,
comment fav counts, and profile popover stats.

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

+14 -9
+2 -2
app/lib/components/molecules/Comment.svelte
··· 4 4 import { createMutation, useQueryClient } from '@tanstack/svelte-query' 5 5 import Avatar from '../atoms/Avatar.svelte' 6 6 import RichText from '../atoms/RichText.svelte' 7 - import { relativeTime } from '$lib/utils' 7 + import { relativeTime, compactCount } from '$lib/utils' 8 8 import { viewer, requireAuth } from '$lib/stores' 9 9 import { VolumeX, Heart } from 'lucide-svelte' 10 10 ··· 92 92 <div class="meta"> 93 93 <span class="time">{timeStr}</span> 94 94 {#if displayFavCount > 0} 95 - <span class="fav-count">{displayFavCount} {displayFavCount === 1 ? 'fav' : 'favs'}</span> 95 + <span class="fav-count">{compactCount(displayFavCount)} {displayFavCount === 1 ? 'fav' : 'favs'}</span> 96 96 {/if} 97 97 {#if onReply} 98 98 <button class="meta-btn" onclick={() => onReply?.(comment.replyTo ?? comment.uri, comment.author?.handle ?? '')}>Reply</button>
+3 -2
app/lib/components/molecules/FavoriteButton.svelte
··· 3 3 import { callXrpc } from '$hatk/client' 4 4 import { Heart } from 'lucide-svelte' 5 5 import { requireAuth } from '$lib/stores' 6 + import { compactCount } from '$lib/utils' 6 7 7 8 let { 8 9 galleryUri, ··· 82 83 onclick={() => requireAuth() && !createFavMut.isPending && !deleteFavMut.isPending && favUri && favUri !== 'pending' && deleteFavMut.mutate(favUri)} 83 84 > 84 85 <Heart size={22} fill="currentColor" /> 85 - {#if displayCount > 0}<span class="stat-count">{displayCount}</span>{/if} 86 + {#if displayCount > 0}<span class="stat-count">{compactCount(displayCount)}</span>{/if} 86 87 </button> 87 88 {:else} 88 89 <button ··· 92 93 onclick={() => requireAuth() && !createFavMut.isPending && !deleteFavMut.isPending && !isFaved && createFavMut.mutate()} 93 94 > 94 95 <Heart size={22} /> 95 - {#if displayCount > 0}<span class="stat-count">{displayCount}</span>{/if} 96 + {#if displayCount > 0}<span class="stat-count">{compactCount(displayCount)}</span>{/if} 96 97 </button> 97 98 {/if} 98 99
+2 -5
app/lib/components/molecules/ProfilePopover.svelte
··· 3 3 import type { GrainActorDefsProfileViewDetailed, GetKnownFollowersFollowerItem } from '$hatk/client' 4 4 import { actorProfileQuery, knownFollowersQuery, storyAuthorsQuery } from '$lib/queries' 5 5 import { viewer } from '$lib/stores' 6 + import { compactCount } from '$lib/utils' 6 7 import Avatar from '../atoms/Avatar.svelte' 7 8 import FollowButton from './FollowButton.svelte' 8 9 ··· 56 57 }, 200) 57 58 } 58 59 59 - function formatCount(n: number): string { 60 - if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M` 61 - if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K` 62 - return String(n) 63 - } 60 + const formatCount = compactCount 64 61 </script> 65 62 66 63 <!-- svelte-ignore a11y_no_static_element_interactions -->
+7
app/lib/utils.ts
··· 38 38 39 39 return date.toLocaleDateString("en-US", { month: "short", day: "numeric" }); 40 40 } 41 + 42 + /** Compact number formatting: 1K, 1.2M, etc. */ 43 + export function compactCount(n: number): string { 44 + if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1).replace(/\.0$/, '')}M` 45 + if (n >= 1_000) return `${(n / 1_000).toFixed(1).replace(/\.0$/, '')}K` 46 + return String(n) 47 + }