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
|
// Mini now-playing bar
|
||||||
$('miniPlayBtn').addEventListener('click', (e) => { e.stopPropagation(); Player.toggle(); });
|
$('miniPlayBtn').addEventListener('click', (e) => { e.stopPropagation(); Player.toggle(); });
|
||||||
|
$('updateIconBtn').addEventListener('click', updateFromIcon);
|
||||||
$('miniBar').addEventListener('click', () => {
|
$('miniBar').addEventListener('click', () => {
|
||||||
setHomeOverPlayer(false);
|
setHomeOverPlayer(false);
|
||||||
hideMiniBar();
|
hideMiniBar();
|
||||||
@@ -11102,16 +11103,37 @@ async function hardReloadUI() {
|
|||||||
let _updateBannerShown = false;
|
let _updateBannerShown = false;
|
||||||
let _updating = false;
|
let _updating = false;
|
||||||
let _updateToast = null;
|
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) {
|
function showUpdateBanner(target) {
|
||||||
// Only show the dialog once per page load
|
// Only show the dialog once per page load
|
||||||
if (_updateBannerShown) return;
|
if (_updateBannerShown) return;
|
||||||
_updateBannerShown = true;
|
_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.
|
// Show a modal dialog instead of a fleeting toast so the user can't miss it.
|
||||||
const body = document.createElement('p');
|
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.';
|
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, [
|
showModal('⬆ Update available', body, [
|
||||||
{ label: 'Later', onClick: closeModal },
|
{ label: 'Later', onClick: () => { closeModal(); setUpdateIcon(true); } },
|
||||||
{ label: 'Refresh UI', primary: true, onClick: async () => {
|
{ label: 'Refresh UI', primary: true, onClick: async () => {
|
||||||
closeModal();
|
closeModal();
|
||||||
await applyUpdate(target);
|
await applyUpdate(target);
|
||||||
@@ -11185,7 +11207,8 @@ async function applyUpdate(target, { quiet = false } = {}) {
|
|||||||
_updating = false;
|
_updating = false;
|
||||||
writeUpdateState(null);
|
writeUpdateState(null);
|
||||||
if (_updateToast) { _updateToast.remove(); _updateToast = 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.
|
// Let the next poll re-offer it.
|
||||||
_updateBannerShown = false;
|
_updateBannerShown = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,6 +100,10 @@
|
|||||||
placeholder="Search YouTube… (no login, no ads)"
|
placeholder="Search YouTube… (no login, no ads)"
|
||||||
autocomplete="off"
|
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>
|
<button type="submit" class="search-btn">Search</button>
|
||||||
<div id="searchRecent" class="search-recent hidden" role="listbox" aria-label="Recent searches"></div>
|
<div id="searchRecent" class="search-recent hidden" role="listbox" aria-label="Recent searches"></div>
|
||||||
</form>
|
</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 #artFallback { visibility: hidden; }
|
||||||
.player-pane.home-over .related-panel { display: none; }
|
.player-pane.home-over .related-panel { display: none; }
|
||||||
.player-pane.home-over > :not(.player-stage):not(#serviceMode) { display: none !important; }
|
.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