Add context menu for audios

This commit is contained in:
mrilyew 2024-11-30 15:35:43 +03:00
parent 2cff603e11
commit 1d719309c7
15 changed files with 270 additions and 25 deletions

View file

@ -97,9 +97,12 @@ final class AudioPresenter extends OpenVKPresenter
$this->template->club = $owner < 0 ? $entity : NULL;
$this->template->isMy = ($owner > 0 && ($entity->getId() === $this->user->id));
$this->template->isMyClub = ($owner < 0 && $entity->canBeModifiedBy($this->user->identity));
} else {
$audios = $this->audios->getPopular();
$audiosCount = $audios->size();
} else if ($mode === 'alone_audio') {
$audios = [$this->template->alone_audio];
$audiosCount = 1;
$this->template->owner = $this->user->identity;
$this->template->ownerId = $this->user->id;
}
// $this->renderApp("owner=$owner");
@ -271,6 +274,19 @@ final class AudioPresenter extends OpenVKPresenter
}
}
function renderAloneAudio(int $owner_id, int $audio_id): void
{
$this->assertUserLoggedIn();
$found_audio = $this->audios->get($audio_id);
if(!$found_audio || $found_audio->isDeleted() || !$found_audio->canBeViewedBy($this->user->identity)) {
$this->notFound();
}
$this->template->alone_audio = $found_audio;
$this->renderList(NULL, 'alone_audio');
}
function renderListen(int $id): void
{
if ($_SERVER["REQUEST_METHOD"] === "POST") {
@ -762,6 +778,15 @@ final class AudioPresenter extends OpenVKPresenter
$audios = $stream->page($page, 10);
$audiosCount = $stream->size();
break;
case 'alone_audio':
$found_audio = $this->audios->get($ctx_id);
if(!$found_audio || $found_audio->isDeleted() || !$found_audio->canBeViewedBy($this->user->identity)) {
$this->flashFail("err", "Error", "Not found", 89, true);
}
$audios = [$found_audio];
$audiosCount = 1;
break;
}
$pagesCount = ceil($audiosCount / $perPage);

View file

@ -11,6 +11,8 @@
{_audio_new}
{elseif $mode == 'popular'}
{_audio_popular}
{elseif $mode == 'alone_audio'}
{$alone_audio->getName()}
{else}
{if $ownerId > 0}
{_playlists} {$owner->getMorphedName("genitive", false)}
@ -47,6 +49,10 @@
»
{if $isMy}{_my_playlists}{else}{_playlists}{/if}
</div>
<div n:if="$mode == 'alone_audio'">
{_my_audios_small}
</div>
{/block}
{block content}
@ -62,6 +68,12 @@
entity_id: {$ownerId},
page: {$page}
}
{elseif $mode == 'alone_audio'}
window.__current_page_audio_context = {
name: 'alone_audio',
entity_id: {$alone_audio->getId()},
page: 1
}
{/if}
</script>

View file

@ -1,4 +1,4 @@
<div n:class="bigPlayer, $tidy ? tidy">
<div n:class="bigPlayer, ctx_place, $tidy ? tidy">
<div class="bigPlayerWrapper">
<div class="playButtons">
<div class="playButton musicIcon" data-tip='simple' data-title="{_play_tip} [Space]"></div>

View file

@ -3,7 +3,7 @@
{php $isAvailable = $audio->isAvailable()}
{php $performers = $audio->getPerformers()}
{php $editable = isset($thisUser) && $audio->canBeModifiedBy($thisUser)}
<div id="audioEmbed-{$id}" data-realid="{$audio->getId()}" {if $hideButtons}data-prettyid="{$audio->getPrettyId()}"{/if} data-name="{$audio->getName()}" data-genre="{$audio->getGenre()}" n:class="audioEmbed, !$isAvailable ? processed, $isWithdrawn ? withdrawn" data-length="{$audio->getLength()}" data-keys="{json_encode($audio->getKeys())}" data-url="{$audio->getURL()}">
<div id="audioEmbed-{$id}" data-realid="{$audio->getId()}" {if $hideButtons}data-prettyid="{$audio->getPrettyId()}"{/if} data-name="{$audio->getName()}" data-genre="{$audio->getGenre()}" n:class="audioEmbed, ctx_place, !$isAvailable ? processed, $isWithdrawn ? withdrawn" data-length="{$audio->getLength()}" data-keys="{json_encode($audio->getKeys())}" data-url="{$audio->getURL()}">
<audio class="audio" />
<div id="miniplayer" class="audioEntry">

View file

@ -203,6 +203,8 @@ routes:
handler: "Audio->list"
- url: "/audio{num}/listen"
handler: "Audio->listen"
- url: "/audio{num}_{num}"
handler: "Audio->aloneAudio"
- url: "/audios/search"
handler: "Audio->search"
- url: "/audios/newPlaylist"

View file

@ -34,7 +34,7 @@
box-shadow: 1px 0px 8px 0px rgba(34, 60, 80, 0.2);
position: sticky;
top: 0px;
z-index: 1;
z-index: 10;
}
/* for search */

View file

@ -139,7 +139,7 @@ body.dimmed > .dimmer #absolute_territory {
}
.miniplayer {
position: absolute;
position: fixed;
background: rgba(54, 54, 54, 0.95);
border-radius: 3px;
min-width: 299px;

View file

@ -3859,3 +3859,36 @@ hr {
.tippy-box[data-animation='up_down'][data-state='visible'] {
inset: auto auto 0px 0px;
}
.ctx_place {
position: relative;
}
#ctx_menu {
position: absolute;
z-index: 100;
background: #f7f7f7;
border: 1px solid #d8d8d8;
display: flex;
flex-direction: column;
min-width: 185px;
}
#ctx_menu a {
padding: 6px 6px 6px 26px;
color: black;
position: relative;
}
#ctx_menu a:hover {
background: #e9e9e9;
}
#ctx_menu a.pressed::before {
content: '✓';
display: inline-block;
position: absolute;
left: 9px;
top: 3px;
font-size: 15px;
}

View file

@ -217,7 +217,7 @@ window.player = new class {
form_data.append('context_entity', this.context.object.entity_id)
break
case 'classic_search_context':
// todo rifictir
// tidi riwriti
form_data.append('context', this.context.object.name)
form_data.append('context_entity', JSON.stringify({
'order': this.context.object.order,
@ -228,6 +228,9 @@ window.player = new class {
'query': this.context.object.query,
}))
break
case 'alone_audio':
form_data.append('context', this.context.object.name)
form_data.append('context_entity', this.context.object.entity_id)
}
form_data.append('page', page)
@ -395,6 +398,7 @@ window.player = new class {
async shuffle() {
this.tracks.sort(() => Math.random() - 0.59)
await this.setTrack(this.tracks.at(0).id)
this.play()
}
isAtAudiosPage() {
@ -447,7 +451,7 @@ window.player = new class {
this.__updateFace()
}
} else {
this.ajClose()
this.ajClose(false)
this.is_closed = false
if(this.tracks.length < 1) {
if(window.__current_page_audio_context) {
@ -622,9 +626,12 @@ window.player = new class {
}
}
ajClose() {
ajClose(pause = true) {
this.is_closed = true
this.pause()
if(pause) {
this.pause()
}
u('#ajax_audio_player').addClass('hidden')
}
@ -637,7 +644,7 @@ window.player = new class {
const previous_time_x = localStorage.getItem('audio.lastX') ?? 100
const previous_time_y = localStorage.getItem('audio.lastY') ?? scrollY
const miniplayer_template = u(`
<div id='ajax_audio_player'>
<div id='ajax_audio_player' class='ctx_place'>
<div id='aj_player'>
<div id='aj_player_internal_controls'>
<div id='aj_player_play'>
@ -691,7 +698,7 @@ window.player = new class {
$('#ajax_audio_player').draggable({
cursor: 'grabbing',
containment: 'window',
cancel: '#aj_player_track .selectableTrack, #aj_player_volume .selectableTrack',
cancel: '#aj_player_track, #aj_player_volume, #aj_player_buttons',
stop: function(e) {
if(window.player.ajaxPlayer.length > 0) {
const left = parseInt(window.player.ajaxPlayer.nodes[0].style.left)
@ -1022,6 +1029,127 @@ u(document).on("drop", '.audiosContainer', function(e) {
}
})
u(document).on('contextmenu', '.bigPlayer, .audioEmbed, #ajax_audio_player', (e) => {
e.preventDefault()
u('#ctx_menu').remove()
const ctx_type = u(e.target).closest('.bigPlayer, #ajax_audio_player').length > 0 ? 'main_player' : 'mini_player'
const parent = e.target.closest('.ctx_place')
if(!parent) {
return
}
const rect = parent.getBoundingClientRect()
let x, y;
let rx = rect.x + window.scrollX, ry = rect.y + window.scrollY
x = e.pageX - rx
y = e.pageY - ry
const ctx_u = u(`
<div id='ctx_menu' style='top:${y}px;left:${x}px;' data-type='ctx_type'>
<a id='audio_ctx_copy'>${tr('copy_link_to_audio')}</a>
${ctx_type == 'main_player' ? `
<a id='audio_ctx_repeat' ${window.player.audioPlayer.loop ? `class='pressed'` : ''}>${tr('repeat_tip')}</a>
<a id='audio_ctx_shuffle'>${tr('shuffle_tip')}</a>
<a id='audio_ctx_mute' ${window.player.audioPlayer.muted ? `class='pressed'` : ''}>${tr('mute_tip_noun')}</a>
` : ''}
${ctx_type == 'mini_player' ? `
<a id='audio_ctx_play_next'>${tr('audio_ctx_play_next')}</a>
` : ''}
<a id='audio_ctx_add_to_group'>${tr('audio_ctx_add_to_group')}</a>
<a id='audio_ctx_add_to_playlist'>${tr('audio_ctx_add_to_playlist')}</a>
${ctx_type == 'main_player' ? `<a href='https://github.com/mrilyew' target='_blank'>BigPlayer v1.1 by MrIlyew</a>` : ''}
</div>
`)
u(parent).append(ctx_u)
ctx_u.find('#audio_ctx_copy').on('click', async (e) => {
if(ctx_type == 'main_player') {
if(window.player.current_track_id == 0) {
makeError(tr('copy_link_to_audio_error_not_selected_track'), 'Red', 4000, 80)
return
}
const url = location.origin + `/audio${window.openvk.current_id}_${window.player.current_track_id}`
await copyToClipboard(url)
} else {
const url = location.origin + `/audio${window.openvk.current_id}_${u(e.target).closest('.audioEmbed').attr('data-realid')}`
await copyToClipboard(url)
}
})
ctx_u.find('#audio_ctx_repeat').on('click', () => {
if(window.player.current_track_id == 0) {
return
}
if(!window.player.audioPlayer.loop) {
window.player.audioPlayer.loop = true
window.player.uiPlayer.find('.repeatButton').addClass('pressed')
} else {
window.player.audioPlayer.loop = false
window.player.uiPlayer.find('.repeatButton').removeClass('pressed')
}
})
ctx_u.find('#audio_ctx_shuffle').on('click', async () => {
if(window.player.current_track_id == 0) {
return
}
await window.player.shuffle()
})
ctx_u.find('#audio_ctx_mute').on('click', async () => {
if(window.player.current_track_id == 0) {
return
}
window.player.uiPlayer.find('.deviceButton').toggleClass('pressed')
window.player.audioPlayer.muted = window.player.uiPlayer.find('.deviceButton').hasClass('pressed')
})
ctx_u.find('#audio_ctx_add_to_group').on('click', async () => {
if(ctx_type == 'main_player') {
if(window.player.current_track_id == 0) {
return
}
__showAudioAddDialog(window.player.current_track_id)
} else {
__showAudioAddDialog(Number(u(e.target).closest('.audioEmbed').attr('data-realid')))
}
})
ctx_u.find('#audio_ctx_add_to_playlist').on('click', async () => {
if(ctx_type == 'main_player') {
if(window.player.current_track_id == 0) {
return
}
__showAudioAddDialog(window.player.current_track_id, 'playlist')
} else {
__showAudioAddDialog(Number(u(e.target).closest('.audioEmbed').attr('data-realid')), 'playlist')
}
})
ctx_u.find('#audio_ctx_play_next').on('click', (ev) => {
const current_id = window.player.current_track_id
const move_id = Number(u(e.target).closest('.audioEmbed').attr('data-realid'))
if(current_id == 0) {
return
}
if(current_id == move_id) {
return
}
const current_index = window.player.__findTrack(current_id, true)
const next_track = window.player.__findTrack(move_id)
const next_track_player = u(`.audioEmbed[data-realid='${window.player.nextTrack.id}']`)
const moving_track_player = u(`.audioEmbed[data-realid='${move_id}']`)
window.player.tracks.splice(current_index + 1, 0, next_track)
if(next_track_player.length > 0 && moving_track_player.length > 0) {
next_track_player.nodes[0].outerHTML = moving_track_player.nodes[0].outerHTML + next_track_player.nodes[0].outerHTML
moving_track_player.remove()
}
})
})
u(document).on("click", ".musicIcon.edit-icon", (e) => {
const player = e.target.closest(".audioEmbed")
const id = Number(player.dataset.realid)
@ -1241,9 +1369,7 @@ $(document).on("click", ".musicIcon.remove-icon-group", (e) => {
})
})
$(document).on("click", ".musicIcon.add-icon-group", async (ev) => {
let current_tab = 'club';
const id = Number(ev.target.dataset.id)
function __showAudioAddDialog(id, current_tab = 'club') {
const body = `
<div id='_addAudioAdditional'>
<div id='_tabs'>
@ -1443,6 +1569,10 @@ $(document).on("click", ".musicIcon.add-icon-group", async (ev) => {
e.preventDefault()
}
})
}
$(document).on("click", ".musicIcon.add-icon-group", async (ev) => {
const id = Number(ev.target.dataset.id)
__showAudioAddDialog(id)
})
$(document).on("click", ".musicIcon.add-icon", (e) => {

View file

@ -436,7 +436,7 @@ async function OpenVideo(video_arr = [], init_player = true)
u('.miniplayer').remove()
})
$('.miniplayer').draggable({cursor: 'grabbing', containment: 'body', cancel: '.miniplayer-body'})
$('.miniplayer').draggable({cursor: 'grabbing', containment: 'window', cancel: '.miniplayer-body'})
$('.miniplayer').resizable({
maxHeight: 2000,
maxWidth: 3000,
@ -2405,14 +2405,15 @@ function setStatusEditorShown(shown) {
document.getElementById("status_editor").style.display = shown ? "block" : "none";
}
document.addEventListener("click", event => {
u(document).on('click', (event) => {
u('#ctx_menu').remove()
if(u('#status_editor').length < 1) {
return
}
if(!event.target.closest("#status_editor") && !event.target.closest("#page_status_text"))
setStatusEditorShown(false);
});
})
u(document).on('click', '#page_status_text', (e) => {
setStatusEditorShown(true)

View file

@ -102,12 +102,14 @@ class CMessageBox {
u('body').removeClass('dimmed')
u('html').attr('style', 'overflow-y:scroll')
this.getNode().attr('style', 'display: none;')
this.hidden = true
}
reveal() {
u('body').addClass('dimmed')
u('html').attr('style', 'overflow-y:hidden')
this.getNode().attr('style', 'display: block;')
this.hidden = false
}
static toggleLoader() {

View file

@ -48,7 +48,13 @@ window.router = new class {
}
__closeMsgs() {
window.messagebox_stack.forEach(msg => msg.close())
window.messagebox_stack.forEach(msg => {
if(msg.hidden) {
return
}
msg.close()
})
}
__appendPage(parsed_content) {
@ -218,6 +224,11 @@ u(document).on('click', 'a', async (e) => {
return
}
if(target.download != null) {
console.log('AJAX | Skipped because its download')
return
}
if(!dom_url || dom_url == '#' || dom_url.indexOf('javascript:') != -1) {
console.log('AJAX | Skipped because its anchor or function call')
return
@ -308,7 +319,6 @@ u(document).on('submit', 'form', async (e) => {
history.pushState({'from_router': 1}, '', __new_url)
}
console.log(form_res)
window.router.__appendPage(parsed_content)
await window.router.__integratePage()

View file

@ -222,3 +222,19 @@ function serializeForm(form)
return fd
}
async function copyToClipboard(text) {
let fallback = () => {
prompt(text);
}
if(typeof navigator.clipboard == "undefined") {
fallback()
} else {
try {
await navigator.clipboard.writeText(text)
} catch(e) {
fallback()
}
}
}

View file

@ -995,9 +995,16 @@
"repeat_tip" = "Repeat";
"shuffle_tip" = "Shuffle";
"mute_tip" = "Mute";
"mute_tip_noun" = "Muted";
"playlist_hide_from_search" = "Unlisted";
"confirm_deleting_audio" = "Do you sure want to delete this audio?";
"copy_link_to_audio" = "Copy link to clipboard";
"copy_link_to_audio_error_not_selected_track" = "Track was not selected";
"audio_ctx_add_to_group" = "Add to group";
"audio_ctx_add_to_playlist" = "Add to playlist";
"audio_ctx_play_next" = "Play next";
/* Notifications */
"feedback" = "Feedback";

View file

@ -950,9 +950,16 @@
"repeat_tip" = "Повторение";
"shuffle_tip" = "Перемешать";
"mute_tip" = "Заглушить";
"mute_tip_noun" = "Заглушено";
"playlist_hide_from_search" = "Не показывать в поиске";
"confirm_deleting_audio" = "Вы действительно хотите полностью удалить аудиозапись?";
"copy_link_to_audio" = "Копировать ссылку на аудио";
"copy_link_to_audio_error_not_selected_track" = "Трек не выбран";
"audio_ctx_add_to_group" = "Добавить в группу";
"audio_ctx_add_to_playlist" = "Добавить в плейлист";
"audio_ctx_play_next" = "Воспроизвести следующим";
/* Notifications */
"feedback" = "Ответы";