feat: three-dot context menu on song rows + search shows songs as rows
- Add ⋮ button to every song-item triggering the context menu (same as right-click) - Show song-actions always visible on touch devices via @media (hover:none) - Search results: songs now render as makeSongItem rows instead of cards (artists/albums remain as horizontal card rows) - Fix Android app opening system settings on every launch after notification permission denial — now only redirects to settings once using notif_settings_shown flag Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
7bec106350
commit
27eb209945
4 files changed
+22
-14
No files matched your search
@@ -206,10 +206,13 @@ class MainActivity : AppCompatActivity(), MusicService.StateListener {
|
|||||||
ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
|
ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
|
||||||
== PackageManager.PERMISSION_GRANTED -> return
|
== PackageManager.PERMISSION_GRANTED -> return
|
||||||
|
|
||||||
// System won't show the dialog again after one denial — send user to settings.
|
// System won't show the dialog again after one denial — send user to settings once.
|
||||||
!ActivityCompat.shouldShowRequestPermissionRationale(
|
!ActivityCompat.shouldShowRequestPermissionRationale(
|
||||||
this, Manifest.permission.POST_NOTIFICATIONS
|
this, Manifest.permission.POST_NOTIFICATIONS
|
||||||
) && _notifPermissionAskedBefore() -> openNotificationSettings()
|
) && _notifPermissionAskedBefore() && !_notifSettingsShownBefore() -> {
|
||||||
|
markNotifSettingsShown()
|
||||||
|
openNotificationSettings()
|
||||||
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
markNotifPermissionAsked()
|
markNotifPermissionAsked()
|
||||||
@@ -225,9 +228,10 @@ class MainActivity : AppCompatActivity(), MusicService.StateListener {
|
|||||||
) {
|
) {
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||||
if (requestCode == 42 &&
|
if (requestCode == 42 &&
|
||||||
grantResults.firstOrNull() == PackageManager.PERMISSION_DENIED
|
grantResults.firstOrNull() == PackageManager.PERMISSION_DENIED &&
|
||||||
|
!_notifSettingsShownBefore()
|
||||||
) {
|
) {
|
||||||
// Denied — open settings so the user can flip the toggle themselves.
|
markNotifSettingsShown()
|
||||||
openNotificationSettings()
|
openNotificationSettings()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -247,4 +251,11 @@ class MainActivity : AppCompatActivity(), MusicService.StateListener {
|
|||||||
private fun markNotifPermissionAsked() =
|
private fun markNotifPermissionAsked() =
|
||||||
getSharedPreferences("ns_prefs", MODE_PRIVATE).edit()
|
getSharedPreferences("ns_prefs", MODE_PRIVATE).edit()
|
||||||
.putBoolean("notif_asked", true).apply()
|
.putBoolean("notif_asked", true).apply()
|
||||||
|
|
||||||
|
private fun _notifSettingsShownBefore() =
|
||||||
|
getSharedPreferences("ns_prefs", MODE_PRIVATE).getBoolean("notif_settings_shown", false)
|
||||||
|
|
||||||
|
private fun markNotifSettingsShown() =
|
||||||
|
getSharedPreferences("ns_prefs", MODE_PRIVATE).edit()
|
||||||
|
.putBoolean("notif_settings_shown", true).apply()
|
||||||
}
|
}
|
||||||
@@ -55,11 +55,13 @@ export function makeSongItem(s, idx, list, { noArtistLink = false, noAlbumLink =
|
|||||||
<div class="song-actions">
|
<div class="song-actions">
|
||||||
<button class="icon-btn${s._starred?' starred':''}" title="Favorit"><svg width="12" height="12" viewBox="0 0 24 24" fill="${s._starred?'currentColor':'none'}" stroke="currentColor" stroke-width="2"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg></button>
|
<button class="icon-btn${s._starred?' starred':''}" title="Favorit"><svg width="12" height="12" viewBox="0 0 24 24" fill="${s._starred?'currentColor':'none'}" stroke="currentColor" stroke-width="2"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg></button>
|
||||||
<button class="icon-btn" title="Download"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg></button>
|
<button class="icon-btn" title="Download"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg></button>
|
||||||
|
<button class="icon-btn song-menu-btn" title="Mehr Optionen"><svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><circle cx="12" cy="5" r="1.5"/><circle cx="12" cy="12" r="1.5"/><circle cx="12" cy="19" r="1.5"/></svg></button>
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
const btns = el.querySelectorAll('.icon-btn');
|
const btns = el.querySelectorAll('.icon-btn');
|
||||||
btns[0].onclick = (e) => { e.stopPropagation(); toggleStar(s, btns[0]); };
|
btns[0].onclick = (e) => { e.stopPropagation(); toggleStar(s, btns[0]); };
|
||||||
btns[1].onclick = (e) => { e.stopPropagation(); dlSongLocal(s); };
|
btns[1].onclick = (e) => { e.stopPropagation(); dlSongLocal(s); };
|
||||||
|
btns[2].onclick = (e) => { e.stopPropagation(); showCtxMenu(e, s, list, idx); };
|
||||||
el.onclick = () => playQueue(list, idx);
|
el.onclick = () => playQueue(list, idx);
|
||||||
el.addEventListener('contextmenu', e => { e.preventDefault(); showCtxMenu(e, s, list, idx); });
|
el.addEventListener('contextmenu', e => { e.preventDefault(); showCtxMenu(e, s, list, idx); });
|
||||||
return el;
|
return el;
|
||||||
|
|||||||
+4
-10
@@ -735,16 +735,10 @@ export async function doLocalSearch(q) {
|
|||||||
s.appendChild(g); el.appendChild(s);
|
s.appendChild(g); el.appendChild(s);
|
||||||
}
|
}
|
||||||
if(songs.length) {
|
if(songs.length) {
|
||||||
const s = mkSection('Titel');
|
const sec = mkSection('Titel');
|
||||||
const g = document.createElement('div'); g.className = 'scroll-row';
|
const list = document.createElement('div'); list.className = 'song-list';
|
||||||
songs.forEach((so,i) => {
|
songs.forEach((so,i) => list.appendChild(makeSongItem(so, i, songs)));
|
||||||
const cv = coverUrl(so.coverArt, 256);
|
sec.appendChild(list); el.appendChild(sec);
|
||||||
const c = makeCard(so.title, so.artist||'', cv, '🎵');
|
|
||||||
c.querySelector('.play-btn').onclick = (e) => { e.stopPropagation(); playQueue(songs, i); };
|
|
||||||
c.onclick = () => playQueue(songs, i);
|
|
||||||
g.appendChild(c);
|
|
||||||
});
|
|
||||||
s.appendChild(g); el.appendChild(s);
|
|
||||||
}
|
}
|
||||||
} catch(e) { el.innerHTML = `<p style="color:var(--danger)">${e.message}</p>`; }
|
} catch(e) { el.innerHTML = `<p style="color:var(--danger)">${e.message}</p>`; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -189,6 +189,7 @@ nav{flex:1;overflow-y:auto;padding:4px 10px}
|
|||||||
.song-dur{font-size:11px;color:var(--text3);font-family:var(--mono)}
|
.song-dur{font-size:11px;color:var(--text3);font-family:var(--mono)}
|
||||||
.song-actions{display:flex;gap:2px;opacity:0;transition:opacity .15s}
|
.song-actions{display:flex;gap:2px;opacity:0;transition:opacity .15s}
|
||||||
.song-item:hover .song-actions{opacity:1}
|
.song-item:hover .song-actions{opacity:1}
|
||||||
|
@media (hover:none){.song-actions{opacity:1}}
|
||||||
/* ── PLAYLIST DRAG & DROP ── */
|
/* ── PLAYLIST DRAG & DROP ── */
|
||||||
.song-list.pl-draggable .song-item{grid-template-columns:18px 32px 44px 1fr auto}
|
.song-list.pl-draggable .song-item{grid-template-columns:18px 32px 44px 1fr auto}
|
||||||
.pl-drag-handle{display:flex;align-items:center;justify-content:center;color:var(--text3);opacity:0;cursor:grab;transition:opacity .15s;touch-action:none}
|
.pl-drag-handle{display:flex;align-items:center;justify-content:center;color:var(--text3);opacity:0;cursor:grab;transition:opacity .15s;touch-action:none}
|
||||||
|
|||||||
Reference in new issue
Block a user