feat(webapp): a "What's new" page anchored at your last visit (BEA-65) - #109
Open
ssowonny wants to merge 2 commits into
Open
feat(webapp): a "What's new" page anchored at your last visit (BEA-65)#109ssowonny wants to merge 2 commits into
ssowonny wants to merge 2 commits into
Conversation
The change feed is flat reverse-chron with no notion of when you last looked, so "what happened while I was away" is date arithmetic in your head — and cheap agents write more files per day than that scales to. Server: `?since=<RFC3339>` on GET /api/p/<id>/history, one case in the existing filter switch. It sits after the kinds[] classification (which must keep walking every op, or a filtered view relabels an edit as an add) and before the sort, so next_cursor is minted from the filtered list and paging a since-feed terminates on its own oldest match. Client: /<project-id>/since — a real VIEW_ROUTES entry — renders the existing HistoryView with that filter and a header line. The anchor is a per-(account, project) localStorage marker, read once from a useState initializer and stamped after entries render: read it any later and the page empties itself while you are reading it. SinceView is the only caller of stampVisit, so no other page moves the marker. Known ceiling: the marker is per browser, not per account — laptop and phone keep separate last-visit times. The server-side marker is the larger follow-up.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TL;DR
/<project-id>/since): everything that changed since you were last here, agent runs still folded into one card each.localStoragelast-visit marker plus a five-line?since=filter on the history API. That's the whole feature — the page below the header isHistoryViewunchanged.Closes BEA-65.
What it looks like
Revisit, once the marker has advanced:
Server
?since=<RFC3339>onGET /api/p/<id>/history— one case in the filter switch that already handlespath/prefix, and a parse next to?n=(unparseable → 400invalid since, same style asinvalid n/invalid cursor). Absent, the response is byte-for-byte what it was.Placement is the whole correctness story, and it is unchanged from the plan:
Filtering after
kinds[]keeps add-vs-edit labelling identical to the full feed; filtering before the sort meansnext_cursoris minted from the filtered list, so paging a since-feed terminates on its own oldest match instead of walking the whole project.TestHistorySincecovers: only-newer, strictly-after (an op at exactly the marker is excluded), composition withprefix=andpath=, a very old marker being identical to no marker at all, cursor paging to exhaustion, and the 400.Client
sincejoinsVIEW_ROUTES/ViewName— a real route, so deep link, reload, back/forward and SPA fallback all come free.lib/lastVisit.ts— the firstlocalStoragein the frontend. Keyedbdrive.lastVisit.<account>.<project>(the one deviation from the spec, called out in the plan: two people on one laptop shouldn't share a last-visit time). Every access is try/caught, so private mode degrades to "always the last 7 days" rather than a white screen.SinceView— ~80 lines: reads the baseline from auseStateinitializer (frozen for the mount), renders the header line, and stamps behind auseRefguard so "Load more" can't restamp. It is the only caller ofstampVisit.HistoryViewgains three optional props (since,emptyText,onLoaded).sinceis part ofqs, which is also thequeryKey, so a revisit with a fresh baseline refetches instead of reusing the previous visit's page. With all three absent/historyrenders exactly as before.clockicon) above History; crumb, active state, styles.Not done, per the issue's Out column: no unread badge, no subtree scoping, no "mark all as read", no server-side marker.
Architecture changes
architecture/webapp-frontend.md:router'sVIEW_ROUTESgainssince;componentsgainsSinceView;libgainslastVisit.ts— the one storage-touching module in a folder documented as pure, so its note says so. No class was removed and no relationship changed.webapp-server.mdnames no history types andoverview.mdno new package, so neither changes.flowchart TB Browser["<div style='text-align:left'><b>Browser</b><br/>folder listing, file view<br/>per-view routes</div>"] router["<div style='text-align:left'><b>router</b><br/><span style='background:#22c55e55;padding:0 4px;border-radius:3px'>✅ +VIEW_ROUTES dashboard history <b>since</b> install settings</span><br/>+LEGACY_VIEWS insights to dashboard<br/>+parseRoute(url, mode) Route<br/>+urlForView / encodePath / decodePath</div>"] components["<div style='text-align:left'><b>components</b><br/>FileView FolderListing FileTree<br/>HistoryView HistoryRow DiffView<br/><span style='background:#22c55e55;padding:0 4px;border-radius:3px'>✅ SinceView</span><br/>Insights ShareDialog Palette shell ...</div>"] lib["<div style='text-align:left'><b>lib</b><br/>+diff.ts splitLines lcsDiff diffText<br/>+runs.ts groupRuns runFileCount<br/><span style='background:#22c55e55;padding:0 4px;border-radius:3px'>✅ +lastVisit.ts lastVisit stampVisit (localStorage)</span><br/>+heat.ts / sniff.ts / utils.ts</div>"] Marker["lastVisit.ts is the one storage-touching<br/>module in an otherwise pure lib/ —<br/>the only localStorage in the frontend.<br/>SinceView is its only writer, so no<br/>other page can move the marker."] Browser --> router Browser --> components components --> lib lib -.- Marker classDef added fill:#22c55e22,stroke:#22c55e,stroke-width:2px classDef noteBox fill:#88888822,stroke:#888888,stroke-dasharray:2 2 class Marker noteBoxVerification
go build ./...,go vet ./...,go test ./...— all pass, including the newTestHistorySince.npm run e2e— 109 passed, including the newe2e/since.spec.ts(first-visit 7-day fallback, a change landing then the revisit going empty, other pages not stamping, the baseline holding still while mounted, and the nav row / hard reload).npm run buildrun andinternal/webapp/staticcommitted;frontend/check-dist.shreports fresh.One note on the plan: it proposed two PRs (server, then frontend) and said one was fine — this is the one-PR version, since the e2e spec needs both halves anyway. Nothing else deviates.
Build session
(only works on the machine this ran on)
🤖 Generated with Claude Code