From bd7441b366fb167e42047e424208c3fbc7ed19d4 Mon Sep 17 00:00:00 2001 From: ergosteur Date: Sun, 16 Aug 2026 19:56:34 -0400 Subject: [PATCH] fix: count the grid in the post header, and qualify the Instagram claim MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The header rendered allPosts.length, which is pre-dedupe — 0ct0ber19 showed "303 posts" over a 300-tile grid. Instagram's counter equals its grid, so count the grid. Also correct CLAUDE.md. v1.7.0 claimed the grid holds everything "as on Instagram"; Instagram actually includes a reel in the grid only when the creator shared it to feed, per post. Measured live: official_artms has 21 reels in its first 34 grid tiles, 0ct0ber19 has 1 in 214. Archives carry no such flag, so showing everything approximates the behaviour rather than reproducing it. Records the DOM trap that caused the wrong reading in the first place: grid reels link to /reel//, not //p//. Co-Authored-By: Claude Opus 5 --- CLAUDE.md | 6 +++++- package-lock.json | 4 ++-- package.json | 2 +- src/App.tsx | 9 ++++++++- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 9ef41e6..de162d7 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -76,7 +76,11 @@ Images over 1MiB are downscaled in a Web Worker via `OffscreenCanvas`. The queue ### Profile tabs (`src/lib/post-tabs.ts`) -The grid holds **everything**, reels included, and the Reels tab is a *filtered view* of that same set — as on Instagram. Only the Reels tab filters. The tabs were mutually exclusive until v1.7.0, which hid a lot: 1100 of `loonatheworld`'s 3813 posts and 533 of `for.heejin`'s 1225 never appeared in the grid at all. +The grid holds **everything**, reels included, and the Reels tab is a *filtered view* of that same set. Only the Reels tab filters. The tabs were mutually exclusive until v1.7.0, which hid a lot: 1100 of `loonatheworld`'s 3813 posts and 533 of `for.heejin`'s 1225 never appeared in the grid at all. + +This *approximates* Instagram rather than matching it. Instagram's grid includes a reel only if the creator shared it to feed — a per-post choice, measured live on 2026-08-16: `official_artms` had 21 reels in its first 34 grid tiles, `0ct0ber19` just 1 in 214. That flag appears nowhere in an archive (JD2 stores no metadata, and Instaloader's `product_type` says what a post *is*, not whether it was shared to feed), so showing everything is the closest reachable behaviour. Instagram's "N posts" counter equals its grid, which is why the header counts `postsForTab(allPosts, 'posts')` and not `allPosts` — the raw list still holds both copies of a double-fetched post. + +When checking the live site, note that grid reels link to `/reel//` while ordinary posts link to `//p//`. Matching only `/p/` silently drops every reel, which once produced a confident and completely wrong conclusion that Instagram never shows reels in the grid. Deciding *what is a reel* has no good answer for most archives. Instagram's own marker is `product_type` on the post's GraphQL node (`clips` = reel, `feed` = ordinary feed video, `igtv`, `story`) — `__typename` is `GraphVideo` for all three, and aspect ratio does not separate them either. But: diff --git a/package-lock.json b/package-lock.json index 3ffc189..721b94a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "instaarchive-viewer", - "version": "1.7.0", + "version": "1.7.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "instaarchive-viewer", - "version": "1.7.0", + "version": "1.7.1", "dependencies": { "@tailwindcss/vite": "^4.1.14", "@vitejs/plugin-react": "^5.0.4", diff --git a/package.json b/package.json index 65b74e1..be6d47a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "instaarchive-viewer", "private": true, - "version": "1.7.0", + "version": "1.7.1", "type": "module", "scripts": { "dev": "vite --port=3000 --host=0.0.0.0", diff --git a/src/App.tsx b/src/App.tsx index 290aac4..fc0eb9b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -179,6 +179,13 @@ export default function App() { */ const filteredPosts = useMemo(() => postsForTab(allPosts, activeTab), [allPosts, activeTab]); + /** + * Instagram's "N posts" counter equals what its grid holds, so count the grid + * rather than `allPosts` — the raw list still holds both copies of any post + * fetched into two directories. + */ + const totalPosts = useMemo(() => postsForTab(allPosts, 'posts').length, [allPosts]); + /** Story highlights, grouped into the circles shown under the bio. */ const highlightGroups = useMemo(() => { const groups = new Map(); @@ -519,7 +526,7 @@ export default function App() { {allProfilePics.length > 1 && } -
{allPosts.length} posts
{(followerCount || 0).toLocaleString()} followers
{(followingCount || 0).toLocaleString()} following
+
{totalPosts.toLocaleString()} posts
{(followerCount || 0).toLocaleString()} followers
{(followingCount || 0).toLocaleString()} following
{fullName || `@${username}`}
{bio || 'Archived profile viewer for local files.'}
{externalUrl && {externalUrl.replace(/^https?:\/\/(www\.)?/, '')}}