Update icon beside the search box after the update dialog is dismissed
Shown as soon as an update is offered, so Later, a backdrop tap or a failed download all leave a one-tap way to update; it targets the newest build and disappears while updating. The search form and box now shrink so the icon never pushes Search off a phone screen.
This commit is contained in:
@@ -10519,6 +10519,7 @@ document.querySelectorAll('.chip').forEach((c) => {
|
||||
|
||||
// Mini now-playing bar
|
||||
$('miniPlayBtn').addEventListener('click', (e) => { e.stopPropagation(); Player.toggle(); });
|
||||
$('updateIconBtn').addEventListener('click', updateFromIcon);
|
||||
$('miniBar').addEventListener('click', () => {
|
||||
setHomeOverPlayer(false);
|
||||
hideMiniBar();
|
||||
@@ -11102,16 +11103,37 @@ async function hardReloadUI() {
|
||||
let _updateBannerShown = false;
|
||||
let _updating = false;
|
||||
let _updateToast = null;
|
||||
|
||||
// After "Later" (or a failed download) an update icon sits beside the search
|
||||
// box until the user takes the update; it always targets the newest build.
|
||||
function setUpdateIcon(on) {
|
||||
const b = $('updateIconBtn');
|
||||
if (b) b.classList.toggle('hidden', !on);
|
||||
}
|
||||
async function updateFromIcon() {
|
||||
if (_updating) return;
|
||||
setUpdateIcon(false);
|
||||
let target = null;
|
||||
try { target = await fetchServerBuild(); } catch { /* offline */ }
|
||||
if (!target || target === (RUNNING_BUILD || _knownBuildTag)) {
|
||||
if (!target) { toast('Offline — try the update again when connected'); setUpdateIcon(true); }
|
||||
return;
|
||||
}
|
||||
await applyUpdate(target);
|
||||
}
|
||||
|
||||
function showUpdateBanner(target) {
|
||||
// Only show the dialog once per page load
|
||||
if (_updateBannerShown) return;
|
||||
_updateBannerShown = true;
|
||||
// However the dialog is dismissed (Later, backdrop), the icon remains.
|
||||
setUpdateIcon(true);
|
||||
|
||||
// Show a modal dialog instead of a fleeting toast so the user can't miss it.
|
||||
const body = document.createElement('p');
|
||||
body.textContent = 'A new version of YT Player is available. Click "Refresh UI" to reload the page with the latest build. All your playlists and history are stored locally and will be preserved.';
|
||||
showModal('⬆ Update available', body, [
|
||||
{ label: 'Later', onClick: closeModal },
|
||||
{ label: 'Later', onClick: () => { closeModal(); setUpdateIcon(true); } },
|
||||
{ label: 'Refresh UI', primary: true, onClick: async () => {
|
||||
closeModal();
|
||||
await applyUpdate(target);
|
||||
@@ -11185,7 +11207,8 @@ async function applyUpdate(target, { quiet = false } = {}) {
|
||||
_updating = false;
|
||||
writeUpdateState(null);
|
||||
if (_updateToast) { _updateToast.remove(); _updateToast = null; }
|
||||
toast('⚠ Update didn’t download (' + (err && err.message ? err.message : 'connection problem') + '). It will be offered again.', { duration: 6000 });
|
||||
toast('⚠ Update didn’t download (' + (err && err.message ? err.message : 'connection problem') + '). Tap the update icon to try again.', { duration: 6000 });
|
||||
setUpdateIcon(true);
|
||||
// Let the next poll re-offer it.
|
||||
_updateBannerShown = false;
|
||||
}
|
||||
|
||||
@@ -100,6 +100,10 @@
|
||||
placeholder="Search YouTube… (no login, no ads)"
|
||||
autocomplete="off"
|
||||
/>
|
||||
<!-- Shown after "Later" on the update dialog: one tap updates -->
|
||||
<button id="updateIconBtn" type="button" class="update-icon-btn hidden" title="Update available — tap to update" aria-label="Update available — tap to update">
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M20 12a8 8 0 1 1-2.34-5.66M20 4v4.5h-4.5"/><path d="M12 8v5M9.5 11l2.5 2.5 2.5-2.5"/></svg>
|
||||
</button>
|
||||
<button type="submit" class="search-btn">Search</button>
|
||||
<div id="searchRecent" class="search-recent hidden" role="listbox" aria-label="Recent searches"></div>
|
||||
</form>
|
||||
|
||||
@@ -4287,3 +4287,19 @@ body.landscape-fs .player-stage { touch-action: none; } /* fullscreen (real or t
|
||||
.player-pane.home-over #artFallback { visibility: hidden; }
|
||||
.player-pane.home-over .related-panel { display: none; }
|
||||
.player-pane.home-over > :not(.player-stage):not(#serviceMode) { display: none !important; }
|
||||
|
||||
/* Pending-update icon beside the search box (shown after the update dialog) */
|
||||
.update-icon-btn {
|
||||
flex: none; width: 44px; height: 44px; padding: 0; align-self: center;
|
||||
display: grid; place-items: center; cursor: pointer;
|
||||
border-radius: 50%; border: 1px solid var(--accent);
|
||||
background: color-mix(in srgb, var(--accent) 16%, var(--bg-2)); color: var(--accent-bright);
|
||||
box-shadow: 0 0 0 0 var(--accent-glow); animation: updPulse 2.4s ease-in-out infinite;
|
||||
}
|
||||
.update-icon-btn svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
|
||||
.update-icon-btn.hidden { display: none; }
|
||||
@keyframes updPulse { 0%, 100% { box-shadow: 0 0 0 0 var(--accent-glow); } 50% { box-shadow: 0 0 0 6px transparent; } }
|
||||
@media (prefers-reduced-motion: reduce) { .update-icon-btn { animation: none; } }
|
||||
/* The search box gives up width for the update icon instead of pushing Search off screen */
|
||||
.search-form { min-width: 0; }
|
||||
.search-form #searchInput { flex: 1 1 auto; min-width: 0; }
|
||||
|
||||
Reference in New Issue
Block a user