Keep a whole search, and say what is new in it
A search here is more than the words in the box: it is the source, the channel, every filter that channel offers, and the query together. The bookmark in the search bar keeps all of it, the one in the header opens the list, and tapping a row puts the whole thing back. Each saved search remembers the ids of the newest videos it showed the last time it was opened. Once a day -- at startup, when the tab is looked at again, and on a slow heartbeat for tabs left open -- the client asks each one's first page again and counts how many of those videos it has never shown. That count is the badge, on the row and summed in the header, and it stops at 9+. Opening a saved search is viewing it, so the count goes out and the mark moves to what is on screen now. Nothing new leaves the device: the check is the same listing request the grid would have made, and everything else is localStorage. Also: handleSearch's "put this in the box without searching for it" half is now App.videos.setSearchBox, which is what applying a saved search needs. tests/smoke_saved_searches.py covers it end to end against a fixture listing whose ids the test shifts to mean "new uploads": 46 checks. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MPZFnNdHbPGDTqQUNiE4ZN
This commit is contained in:
@@ -19,12 +19,26 @@
|
||||
autocomplete="off" role="combobox" aria-expanded="false"
|
||||
aria-controls="search-suggest" aria-autocomplete="list">
|
||||
<button class="search-clear-btn" id="search-clear-btn" type="button" aria-label="Clear search" title="Clear search">✕</button>
|
||||
<button class="search-save-btn" id="search-save-btn" type="button" aria-pressed="false"
|
||||
aria-label="Save this search" title="Save this search">
|
||||
<svg class="icon-line" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="M6.5 4.75h11a.75.75 0 0 1 .75.75v13.3l-6.25-4.1-6.25 4.1V5.5a.75.75 0 0 1 .75-.75z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div id="search-suggest" class="search-suggest" role="listbox" aria-label="Recent searches" hidden></div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button class="icon-btn reload-toggle" id="reload-channel-btn" title="Reload Channel">
|
||||
<img class="icon-svg" src="https://cdn.jsdelivr.net/npm/heroicons@2.0.13/24/outline/arrow-path.svg" alt="Reload">
|
||||
</button>
|
||||
<button class="icon-btn saved-toggle" id="saved-searches-btn" type="button"
|
||||
title="Saved searches" aria-label="Saved searches" aria-haspopup="dialog" aria-expanded="false">
|
||||
<svg class="icon-line" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="M8 3.75h8a1 1 0 0 1 1 1V17l-5-3.2L7 17V4.75a1 1 0 0 1 1-1z"/>
|
||||
<path d="M18.5 6.5h.5a1 1 0 0 1 1 1v12.75l-3-2" stroke-opacity="0.55"/>
|
||||
</svg>
|
||||
<span class="saved-badge" id="saved-searches-badge" aria-hidden="true" hidden></span>
|
||||
</button>
|
||||
<button class="icon-btn menu-toggle" onclick="toggleDrawer('menu')" title="Menu">
|
||||
<img class="icon-svg" src="https://cdn.jsdelivr.net/npm/heroicons@2.0.13/24/outline/bars-3.svg" alt="Menu">
|
||||
</button>
|
||||
@@ -229,6 +243,29 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="saved-panel" class="saved-panel" aria-hidden="true">
|
||||
<div class="saved-box" role="dialog" aria-modal="true" aria-labelledby="saved-panel-title">
|
||||
<div class="saved-head">
|
||||
<div class="saved-head-text">
|
||||
<h3 id="saved-panel-title">Saved searches</h3>
|
||||
<p class="saved-head-note" id="saved-head-note"></p>
|
||||
</div>
|
||||
<div class="saved-head-actions">
|
||||
<button id="saved-check-btn" class="btn-link" type="button">Check now</button>
|
||||
<button id="saved-close-btn" class="close-btn" type="button" aria-label="Close">✕</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="saved-list" class="saved-list"></div>
|
||||
<div id="saved-empty" class="saved-empty">
|
||||
Nothing saved yet. Pick a channel, set your filters, type a search —
|
||||
then press the bookmark in the search bar to keep the whole thing.
|
||||
</div>
|
||||
<div class="saved-foot">
|
||||
<button id="saved-add-btn" class="btn-secondary saved-add" type="button">Save the current search</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="privacy-veil" class="privacy-veil" aria-hidden="true"></div>
|
||||
|
||||
<script src="static/js/state.js"></script>
|
||||
@@ -241,6 +278,7 @@
|
||||
<script src="static/js/sqlite.js"></script>
|
||||
<script src="static/js/hottubBackup.js"></script>
|
||||
<script src="static/js/videos.js"></script>
|
||||
<script src="static/js/savedSearches.js"></script>
|
||||
<script src="static/js/feed.js"></script>
|
||||
<script src="static/js/ui.js"></script>
|
||||
<script src="static/js/version.js"></script>
|
||||
|
||||
Reference in New Issue
Block a user