feat: new-since-last-visit filter with keyboard-reachable filters - #35
Merged
Conversation
The listing now marks stories that were not present on the previous load and can be narrowed to just those. The filter runs in the browser on purpose: the pages are served public with one ETag shared by every visitor, so per-visitor state cannot live on the server without sessions or giving up caching. The baseline is the set of story ids from the last load rather than a posting timestamp -- a story is often hours old by the time its score lifts it into the listing, and filtering on when it was posted would hide exactly those arrivals. Refreshes within five minutes of each other keep the same marks instead of clearing them before they have been read, and the seen map expires after seven days so it cannot grow without bound. The control ships hidden and is revealed only once the script has confirmed it can write to storage, so a visitor without JS never sees a dead toggle. Date dividers are now rebuilt from the visible rows on every filter change. Inserting them once left them stranded above stories from another day as soon as rows were hidden, and the rebuild also retires the textContent dedupe hack. j/k reach the filters: past the first story they step up to the new-since toggle and then the score pills, landing on the score already in effect, where h/l walks the row and space applies it. h/l is clamped inside a filter row so it cannot drop focus into the heading behind it, keystrokes skip hidden rows, and ? opens a cheatsheet of the keys each page actually binds. UIkit strips the focus ring from subnav links, which left the whole walk invisible, so it is restored for keyboard focus only.
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.
What
Two things, both about getting through the listing faster.
New since last visit
The listing marks stories that were not there on your previous load, and a pill under the score filter narrows the page to just those.
It runs in the browser on purpose: the pages are served
publicwith one ETag shared by every visitor, so per-visitor state cannot live on the server without sessions or giving up caching.localStorageunderhn.visit.v1and expires after seven days.hiddenand is revealed only after the script confirms it can write to storage, so a visitor without JS or in Safari private mode never sees a toggle that does nothing.Date dividers are now rebuilt from the visible rows on every filter change. Inserting them once left them stranded above stories from another day as soon as rows were hidden; the rebuild also retires the old
textContent.includesdedupe hack.Keyboard-reachable filters
kpast the first story steps up into the filters — the new-since pill, then the score pills, landing on the score already in effect — whereh/lwalks the row andspaceapplies it.jwalks back down.h/lis clamped inside a filter row, so stepping past0or1000cannot drop focus into the page heading behind it.?opens a cheatsheet listing the keys each page actually binds (the listing advertises the filter shortcut, story pages do not).Testing
bundle exec rake test— 127 runs, 0 failures. The Ruby tests are structural guards (control ships hidden, row ids present, nav hooks intact); behaviour was driven end-to-end in headless Chrome over CDP with real key events, in both colour schemes:j→ story,k→ toggle,k→ active score pill,h h→ clamped at0,space→ navigates / toggles,?→ opens,?/Esc→ closes, nav keys stand down while it is openThe README documents a console snippet for exercising the filter without waiting for HN to post something.