Просмотр исходного кода

fix(ui): iOS 9 emoji fallback -- use ASCII symbols where glyphs are missing

Media-control emoji (U+23EE U+23F8 U+23F9 U+23ED) and 🔊 🔇 📋 🚫
are absent from the iOS 9 system font and render as empty boxes on iPad 2.

Added isLegacyIOS detection (iOS <= 9 via UA) and a SYM symbol table
that picks between emoji (modern) and plain-text fallbacks for iOS 9:
  prev=<< pause=|| stop=■ next=>> volOn=vol volOff=mut playlist=PL skip=✕

Static buttons replaced at boot; dynamic updates (play/pause, mute)
use SYM throughout. Modern browsers completely unaffected.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
master
Jan Svabenik 1 месяц назад
Родитель
Сommit
dfa6a34093
1 измененных файлов: 45 добавлений и 3 удалений
  1. +45
    -3
      web/static/app.js

+ 45
- 3
web/static/app.js Просмотреть файл

@@ -29,6 +29,36 @@ function apiFetch(url, opts) {
// String.prototype.padStart not available in iOS < 10. // String.prototype.padStart not available in iOS < 10.
function pad2(n) { return n < 10 ? '0' + n : '' + n; } function pad2(n) { return n < 10 ? '0' + n : '' + n; }


// iOS version detection — used to pick emoji vs. ASCII symbols.
// Media-control emoji (⏮ ⏸ ⏹ ⏭) and several other glyphs are absent on iOS 9.
var isLegacyIOS = (function() {
var m = navigator.userAgent.match(/OS (\d+)_/);
return /iPad|iPhone/.test(navigator.userAgent) && m && parseInt(m[1], 10) <= 9;
}());

// Symbol set: modern emoji for capable browsers, plain text for iOS ≤ 9.
var SYM = isLegacyIOS ? {
prev: '<<',
play: '▶', // U+25B6 — works on iOS 9
pause: '||',
stop: '■', // U+25A0 — works on iOS 9
next: '>>',
volOn: 'vol',
volOff: 'mut',
playlist:'PL',
skip: '✕' // U+2715 — works on iOS 9
} : {
prev: '⏮',
play: '▶',
pause: '⏸',
stop: '⏹',
next: '⏭',
volOn: '🔊',
volOff: '🔇',
playlist:'📋',
skip: '🚫'
};

// NodeList.prototype.forEach not available in iOS < 10. // NodeList.prototype.forEach not available in iOS < 10.
// Wrap querySelectorAll results in a real Array before iterating. // Wrap querySelectorAll results in a real Array before iterating.
function qsa(selector, root) { function qsa(selector, root) {
@@ -158,7 +188,7 @@ function applyStatus(st) {
timeLength.textContent = '-0:00'; timeLength.textContent = '-0:00';
} }


btnPlay.textContent = st.state === 'playing' ? '⏸' : '▶';
btnPlay.textContent = st.state === 'playing' ? SYM.pause : SYM.play;
winampPlaying = st.state === 'playing'; winampPlaying = st.state === 'playing';


if (typeof st.volume === 'number') { if (typeof st.volume === 'number') {
@@ -170,7 +200,7 @@ function applyStatus(st) {


// ── Controls ────────────────────────────────────────────────────────────────── // ── Controls ──────────────────────────────────────────────────────────────────
btnPlay.addEventListener('click', function() { btnPlay.addEventListener('click', function() {
var playing = btnPlay.textContent === '⏸';
var playing = btnPlay.textContent === SYM.pause;
send({ cmd: playing ? 'pause' : 'play' }); send({ cmd: playing ? 'pause' : 'play' });
}); });


@@ -225,7 +255,7 @@ function updateVolumeFill(muted) {
volumeFill.classList.toggle('muted', muted); volumeFill.classList.toggle('muted', muted);
} }
function updateMuteBtn(muted) { function updateMuteBtn(muted) {
btnMute.textContent = muted ? '🔇' : '🔊';
btnMute.textContent = muted ? SYM.volOff : SYM.volOn;
btnMute.classList.toggle('muted', muted); btnMute.classList.toggle('muted', muted);
} }


@@ -528,5 +558,17 @@ function scrollToCurrentTrack() {
} }


// ── Boot ────────────────────────────────────────────────────────────────────── // ── Boot ──────────────────────────────────────────────────────────────────────

// Replace emoji with legacy symbols on iOS 9 where the glyphs are missing.
if (isLegacyIOS) {
$('btn-prev').textContent = SYM.prev;
$('btn-play').textContent = SYM.play;
$('btn-stop').textContent = SYM.stop;
$('btn-next').textContent = SYM.next;
$('btn-mute').textContent = SYM.volOn;
$('btn-show-playlist').textContent = SYM.playlist;
$('btn-kill').textContent = SYM.skip + ' Überspringen';
}

connect(); connect();
renderFrame(); renderFrame();

Загрузка…
Отмена
Сохранить