Continued work on playlist management UI
This commit is contained in:
parent
f4db10fbc3
commit
fb653a477e
|
|
@ -50,6 +50,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. %>
|
|||
<%# panels %>
|
||||
<script src="/js/channel/panels/emotePanel.js"></script>
|
||||
<script src="/js/channel/panels/queuePanel.js"></script>
|
||||
<script src="/js/channel/panels/queuePanel/playlistManager.js"></script>
|
||||
<%# main client %>
|
||||
<script src="/js/channel/channel.js"></script>
|
||||
</footer>
|
||||
|
|
|
|||
21
src/views/partial/popup/addToPlaylist.ejs
Normal file
21
src/views/partial/popup/addToPlaylist.ejs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<%# Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 Rainbownapkin and the TTN Community
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. %>
|
||||
<%# <link rel="stylesheet" type="text/css" href="/css/popup/schedule.css"> %>
|
||||
<h3 class="popup-title">Add to Playlist From URL</h3>
|
||||
<div>
|
||||
<input id="playlist-add-media-popup-prompt" placeholder="Media URL">
|
||||
<button class="positive-button" id="playlist-add-media-popup-button">Add to playlist</button>
|
||||
</div>
|
||||
|
|
@ -224,6 +224,26 @@ div.dragging-queue-entry{
|
|||
resize: vertical;
|
||||
}
|
||||
|
||||
.queue-playlist-media-div{
|
||||
margin: 0 0.15em;
|
||||
padding: 0 0.15em;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.queue-playlist-add-url-button i.bi-link-45deg{
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
.queue-playlist-add-url-button i{
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 0.5em;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* date */
|
||||
#queue-control-date{
|
||||
display: flex;
|
||||
|
|
|
|||
|
|
@ -560,11 +560,20 @@ div.archived p{
|
|||
color: var(--accent1-alt0);
|
||||
}
|
||||
|
||||
.queue-playlist-control:not(.danger-button):not(:hover){
|
||||
.queue-playlist-control:not(.danger-button):not(:hover).queue-playlist-control:not(.positive-button):not(:hover){
|
||||
background-color: var(--bg1-alt0);
|
||||
color: var(--accent1);
|
||||
}
|
||||
|
||||
|
||||
.queue-playlist-media-div.not-first{
|
||||
border-top: var(--bg1) solid 1px;
|
||||
}
|
||||
|
||||
.queue-playlist-control:not(.queue-playlist-queue-random-button){
|
||||
border-left: var(--accent1-alt0) solid 1px;
|
||||
}
|
||||
|
||||
/* altcha theming*/
|
||||
div.altcha{
|
||||
box-shadow: 4px 4px 1px var(--bg1-alt0) inset;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,18 @@
|
|||
/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 Rainbownapkin and the TTN Community
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.*/
|
||||
class queuePanel extends panelObj{
|
||||
constructor(client, panelDocument){
|
||||
//Call derived constructor
|
||||
|
|
@ -1031,296 +1046,6 @@ class queuePanel extends panelObj{
|
|||
}
|
||||
}
|
||||
|
||||
class playlistManager{
|
||||
constructor(client, panelDocument, queuePanel){
|
||||
//Set client
|
||||
this.client = client;
|
||||
//Set panel document
|
||||
this.panelDocument = panelDocument;
|
||||
//Set parent queue panel
|
||||
this.queuePanel = queuePanel;
|
||||
|
||||
//Define Listeners
|
||||
this.defineListeners();
|
||||
}
|
||||
|
||||
defineListeners(){
|
||||
this.client.socket.on("chanPlaylists", this.renderChannelPlaylists.bind(this));
|
||||
}
|
||||
|
||||
docSwitch(){
|
||||
//Grab menus
|
||||
this.channelPlaylistDiv = this.panelDocument.querySelector("#queue-channel-playlist-div");
|
||||
|
||||
//Grab controls
|
||||
this.createPlaylistSpan = this.panelDocument.querySelector('#queue-add-playlist-span');
|
||||
this.channelPlaylistLabel = this.panelDocument.querySelector('#queue-channel-playlist-span');
|
||||
this.channelPlaylistCaret = this.panelDocument.querySelector('#queue-channel-playlist-toggle');
|
||||
|
||||
//Setup Input
|
||||
this.setupInput();
|
||||
}
|
||||
|
||||
setupInput(){
|
||||
this.createPlaylistSpan.addEventListener('click', (event)=>{new newPlaylistPopup(event, this.client, this.queuePanel.ownerDoc)})
|
||||
this.channelPlaylistLabel.addEventListener('click', this.toggleChannelPlaylists.bind(this));
|
||||
}
|
||||
|
||||
/* queue control button functions */
|
||||
toggleChannelPlaylists(event){
|
||||
//If the div is hidden
|
||||
if(this.channelPlaylistDiv.style.display == 'none'){
|
||||
//Light up the button
|
||||
this.channelPlaylistLabel.classList.add('positive');
|
||||
//Flip the caret
|
||||
this.channelPlaylistCaret.classList.replace('bi-caret-right-fill', 'bi-caret-down-fill');
|
||||
//Show the div
|
||||
this.channelPlaylistDiv.style.display = '';
|
||||
}else{
|
||||
//Unlight the button
|
||||
this.channelPlaylistLabel.classList.remove('positive');
|
||||
//Flip the caret
|
||||
this.channelPlaylistCaret.classList.replace('bi-caret-down-fill', 'bi-caret-right-fill');
|
||||
//Hide the div
|
||||
this.channelPlaylistDiv.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
renderChannelPlaylists(data){
|
||||
//Clear channel playlist div
|
||||
this.channelPlaylistDiv.innerHTML = '';
|
||||
|
||||
//For every playlist sent down from the server
|
||||
for(let playlistIndex in data){
|
||||
//Get playlist from data
|
||||
const playlist = data[playlistIndex];
|
||||
|
||||
//Create a new playlist div
|
||||
const playlistDiv = document.createElement('div');
|
||||
//Set it's class
|
||||
playlistDiv.classList.add('queue-playlist-div');
|
||||
|
||||
//Set playlist div dataset
|
||||
playlistDiv.dataset.name = playlist.name;
|
||||
|
||||
//Create span to hold playlist entry line contents
|
||||
const playlistSpan = document.createElement('span');
|
||||
//Set classes
|
||||
playlistSpan.classList.add('queue-playlist-span');
|
||||
|
||||
//If this isn't our first rodeo
|
||||
if(playlistIndex != 0){
|
||||
//make note
|
||||
playlistSpan.classList.add('not-first');
|
||||
}
|
||||
|
||||
//pre-render and keep this so we can use it later
|
||||
const mediaContainer = renderMedia();
|
||||
|
||||
//Append items to playlist entry line
|
||||
playlistSpan.appendChild(renderLabels());
|
||||
playlistSpan.appendChild(renderControls());
|
||||
|
||||
//Append items to playlist div
|
||||
playlistDiv.appendChild(playlistSpan);
|
||||
playlistDiv.appendChild(mediaContainer);
|
||||
|
||||
//Append current playlist div to the channel playlists div
|
||||
this.channelPlaylistDiv.appendChild(playlistDiv);
|
||||
|
||||
//aux rendering functions
|
||||
function renderLabels(){
|
||||
//Create playlist label span
|
||||
const playlistLabels = document.createElement('span');
|
||||
//Set it's class
|
||||
playlistLabels.classList.add('queue-playlist-labels-span');
|
||||
|
||||
//create playlist title span
|
||||
const playlistTitleSpan = document.createElement('span');
|
||||
//Set class
|
||||
playlistTitleSpan.classList.add('queue-playlist-title-span', 'interactive');
|
||||
|
||||
//Create playlist title caret
|
||||
const playlistTitleCaret = document.createElement('i');
|
||||
//Set class
|
||||
playlistTitleCaret.classList.add('bi-caret-right-fill');
|
||||
|
||||
//Create playlist title label
|
||||
const playlistTitle = document.createElement('p');
|
||||
//Set it's class
|
||||
playlistTitle.classList.add('queue-playlist-title');
|
||||
//Unescape Sanatized Enteties and safely inject as plaintext
|
||||
playlistTitle.innerText = utils.unescapeEntities(playlist.name);
|
||||
|
||||
//Construct playlist title span
|
||||
playlistTitleSpan.appendChild(playlistTitleCaret);
|
||||
playlistTitleSpan.appendChild(playlistTitle);
|
||||
|
||||
//Create playlist count label
|
||||
const playlistCount = document.createElement('p');
|
||||
//Set it's class
|
||||
playlistCount.classList.add('queue-playlist-count');
|
||||
//List video count
|
||||
playlistCount.innerText = `Count: ${playlist.media.length}`;
|
||||
|
||||
//Append items to playlist labels span
|
||||
playlistLabels.appendChild(playlistTitleSpan);
|
||||
playlistLabels.appendChild(playlistCount);
|
||||
|
||||
//Define input listeners
|
||||
playlistTitleSpan.addEventListener('click', toggleMedia.bind(this));
|
||||
|
||||
//return playlistLabels
|
||||
return playlistLabels;
|
||||
|
||||
function toggleMedia(){
|
||||
//If the div is hidden
|
||||
if(mediaContainer.style.display == 'none'){
|
||||
//Light up the button
|
||||
playlistTitleSpan.classList.add('positive');
|
||||
//Flip the caret
|
||||
playlistTitleCaret.classList.replace('bi-caret-right-fill', 'bi-caret-down-fill');
|
||||
//Show the div
|
||||
mediaContainer.style.display = '';
|
||||
}else{
|
||||
//Unlight the button
|
||||
playlistTitleSpan.classList.remove('positive');
|
||||
//Flip the caret
|
||||
playlistTitleCaret.classList.replace('bi-caret-down-fill', 'bi-caret-right-fill');
|
||||
//Hide the div
|
||||
mediaContainer.style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function renderControls(){
|
||||
//Create playlist control span
|
||||
const playlistControls = document.createElement('span');
|
||||
//Set it's class
|
||||
playlistControls.classList.add('queue-playlist-control-span');
|
||||
|
||||
//Create queue all button
|
||||
const playlistQueueRandomButton = document.createElement('button');
|
||||
//Set it's classes
|
||||
playlistQueueRandomButton.classList.add('queue-playlist-queue-all-button', 'queue-playlist-control');
|
||||
//Inject text content
|
||||
playlistQueueRandomButton.textContent = 'Queue Random';
|
||||
|
||||
//Create queue all button
|
||||
const playlistQueueAllButton = document.createElement('button');
|
||||
//Set it's classes
|
||||
playlistQueueAllButton.classList.add('queue-playlist-queue-all-button', 'queue-playlist-control');
|
||||
//Inject text content
|
||||
playlistQueueAllButton.textContent = 'Queue All';
|
||||
|
||||
//Create delete button
|
||||
const playlistDeleteButton = document.createElement('button');
|
||||
//Set it's classes
|
||||
playlistDeleteButton.classList.add('queue-playlist-delete-button', 'queue-playlist-control', 'danger-button', 'bi-trash-fill');
|
||||
|
||||
//Append items to playlist control span
|
||||
playlistControls.appendChild(playlistQueueRandomButton);
|
||||
playlistControls.appendChild(playlistQueueAllButton);
|
||||
playlistControls.appendChild(playlistDeleteButton);
|
||||
|
||||
//Define input event listeners
|
||||
playlistQueueAllButton.addEventListener('click', queueAll);
|
||||
playlistDeleteButton.addEventListener('click', deletePlaylist);
|
||||
|
||||
return playlistControls;
|
||||
}
|
||||
|
||||
function renderMedia(){
|
||||
//Create media container div
|
||||
const mediaContainer = document.createElement('div');
|
||||
//Set classes
|
||||
mediaContainer.classList.add('queue-playlist-media-container-div');
|
||||
//Auto-hide media container
|
||||
mediaContainer.style.display = 'none';
|
||||
|
||||
for(let media of playlist.media){
|
||||
//Create media div
|
||||
const mediaDiv = document.createElement('div');
|
||||
//Set class
|
||||
mediaDiv.classList.add('queue-playlist-media-div');
|
||||
|
||||
//Create media title
|
||||
const mediaTitle = document.createElement('p');
|
||||
//Set class
|
||||
mediaTitle.classList.add('queue-playlist-media-title');
|
||||
//Inject text content
|
||||
mediaTitle.innerText = utils.unescapeEntities(media.title);
|
||||
|
||||
//Append items to media div
|
||||
mediaDiv.appendChild(mediaTitle);
|
||||
|
||||
//Append media div to media container
|
||||
mediaContainer.appendChild(mediaDiv);
|
||||
}
|
||||
|
||||
//return media container
|
||||
return mediaContainer;
|
||||
}
|
||||
|
||||
//playlist control functions
|
||||
function queueAll(){
|
||||
client.socket.emit('queueChannelPlaylist', {playlist: playlist.name});
|
||||
}
|
||||
|
||||
function deletePlaylist(){
|
||||
client.socket.emit('deleteChannelPlaylist', {playlist: playlist.name});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class newPlaylistPopup{
|
||||
constructor(event, client, doc){
|
||||
//Set Client
|
||||
this.client = client;
|
||||
|
||||
//Create media popup and call async constructor when done
|
||||
//unfortunately we cant call constructors asyncronously, and we cant call back to this from super, so we can't extend this as it stands :(
|
||||
this.popup = new canopyUXUtils.popup('/newPlaylist', true, this.asyncConstructor.bind(this), doc);
|
||||
}
|
||||
|
||||
asyncConstructor(){
|
||||
this.name = this.popup.contentDiv.querySelector('#queue-create-playlist-popup-name');
|
||||
this.defaultTitles = this.popup.contentDiv.querySelector('#queue-create-playlist-popup-default-titles');
|
||||
this.location = this.popup.contentDiv.querySelector('#queue-create-playlist-popup-location');
|
||||
this.saveButton = this.popup.contentDiv.querySelector('#queue-create-playlist-popup-save');
|
||||
|
||||
this.setupInput();
|
||||
}
|
||||
|
||||
setupInput(){
|
||||
//Setup input
|
||||
this.saveButton.addEventListener('click', this.createPlaylist.bind(this));
|
||||
this.popup.popupDiv.addEventListener('keydown', this.createPlaylist.bind(this));
|
||||
}
|
||||
|
||||
createPlaylist(event){
|
||||
//If we clicked or hit enter
|
||||
if(event.key == null || event.key == "Enter"){
|
||||
|
||||
//If we're saving to the channel
|
||||
if(this.location.value == 'channel'){
|
||||
//Tell the server to create a new channel playlist
|
||||
this.client.socket.emit('createChannelPlaylist', {
|
||||
playlist: this.name.value,
|
||||
defaultTitles: this.defaultTitles.value.split('\n')
|
||||
})
|
||||
}
|
||||
|
||||
//Close the popup
|
||||
this.popup.closePopup();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class schedulePopup{
|
||||
constructor(event, client, url, title, cb, doc){
|
||||
//Set Client
|
||||
|
|
|
|||
441
www/js/channel/panels/queuePanel/playlistManager.js
Normal file
441
www/js/channel/panels/queuePanel/playlistManager.js
Normal file
|
|
@ -0,0 +1,441 @@
|
|||
/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 Rainbownapkin and the TTN Community
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.*/
|
||||
class playlistManager{
|
||||
constructor(client, panelDocument, queuePanel){
|
||||
//Set client
|
||||
this.client = client;
|
||||
//Set panel document
|
||||
this.panelDocument = panelDocument;
|
||||
//Set parent queue panel
|
||||
this.queuePanel = queuePanel;
|
||||
|
||||
//Define Listeners
|
||||
this.defineListeners();
|
||||
}
|
||||
|
||||
defineListeners(){
|
||||
this.client.socket.on("chanPlaylists", this.renderChannelPlaylists.bind(this));
|
||||
}
|
||||
|
||||
docSwitch(){
|
||||
//Grab menus
|
||||
this.channelPlaylistDiv = this.panelDocument.querySelector("#queue-channel-playlist-div");
|
||||
|
||||
//Grab controls
|
||||
this.createPlaylistSpan = this.panelDocument.querySelector('#queue-add-playlist-span');
|
||||
this.channelPlaylistLabel = this.panelDocument.querySelector('#queue-channel-playlist-span');
|
||||
this.channelPlaylistCaret = this.panelDocument.querySelector('#queue-channel-playlist-toggle');
|
||||
|
||||
//Setup Input
|
||||
this.setupInput();
|
||||
}
|
||||
|
||||
setupInput(){
|
||||
this.createPlaylistSpan.addEventListener('click', (event)=>{new newPlaylistPopup(event, this.client, this.queuePanel.ownerDoc)})
|
||||
this.channelPlaylistLabel.addEventListener('click', this.toggleChannelPlaylists.bind(this));
|
||||
}
|
||||
|
||||
/* queue control button functions */
|
||||
toggleChannelPlaylists(event){
|
||||
//If the div is hidden
|
||||
if(this.channelPlaylistDiv.style.display == 'none'){
|
||||
//Light up the button
|
||||
this.channelPlaylistLabel.classList.add('positive');
|
||||
//Flip the caret
|
||||
this.channelPlaylistCaret.classList.replace('bi-caret-right-fill', 'bi-caret-down-fill');
|
||||
//Show the div
|
||||
this.channelPlaylistDiv.style.display = '';
|
||||
}else{
|
||||
//Unlight the button
|
||||
this.channelPlaylistLabel.classList.remove('positive');
|
||||
//Flip the caret
|
||||
this.channelPlaylistCaret.classList.replace('bi-caret-down-fill', 'bi-caret-right-fill');
|
||||
//Hide the div
|
||||
this.channelPlaylistDiv.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
//Keeping everything in one function was super lazy when 'this.' exists, ESPECIALLY when writing a class!
|
||||
//Really not sure what I was thinking here, this functions a bit of a stinker, and I'll probably re-write it sooner than later...
|
||||
renderChannelPlaylists(data){
|
||||
//Clear channel playlist div
|
||||
this.channelPlaylistDiv.innerHTML = '';
|
||||
|
||||
//For every playlist sent down from the server
|
||||
for(let playlistIndex in data){
|
||||
//Get playlist from data
|
||||
const playlist = data[playlistIndex];
|
||||
|
||||
//Create a new playlist div
|
||||
const playlistDiv = document.createElement('div');
|
||||
//Set it's class
|
||||
playlistDiv.classList.add('queue-playlist-div');
|
||||
|
||||
//Set playlist div dataset
|
||||
playlistDiv.dataset.name = playlist.name;
|
||||
|
||||
//Create span to hold playlist entry line contents
|
||||
const playlistSpan = document.createElement('span');
|
||||
//Set classes
|
||||
playlistSpan.classList.add('queue-playlist-span');
|
||||
|
||||
//If this isn't our first rodeo
|
||||
if(playlistIndex != 0){
|
||||
//make note
|
||||
playlistSpan.classList.add('not-first');
|
||||
}
|
||||
|
||||
//pre-render and keep this so we can use it later
|
||||
const mediaContainer = renderMedia();
|
||||
|
||||
//Append items to playlist entry line
|
||||
playlistSpan.appendChild((renderLabels.bind(this))());
|
||||
playlistSpan.appendChild((renderControls.bind(this))());
|
||||
|
||||
//Append items to playlist div
|
||||
playlistDiv.appendChild(playlistSpan);
|
||||
playlistDiv.appendChild(mediaContainer);
|
||||
|
||||
//Append current playlist div to the channel playlists div
|
||||
this.channelPlaylistDiv.appendChild(playlistDiv);
|
||||
|
||||
//aux rendering functions
|
||||
function renderLabels(){
|
||||
//Create playlist label span
|
||||
const playlistLabels = document.createElement('span');
|
||||
//Set it's class
|
||||
playlistLabels.classList.add('queue-playlist-labels-span');
|
||||
|
||||
//create playlist title span
|
||||
const playlistTitleSpan = document.createElement('span');
|
||||
//Set class
|
||||
playlistTitleSpan.classList.add('queue-playlist-title-span', 'interactive');
|
||||
|
||||
//Create playlist title caret
|
||||
const playlistTitleCaret = document.createElement('i');
|
||||
//Set class
|
||||
playlistTitleCaret.classList.add('bi-caret-right-fill');
|
||||
|
||||
//Create playlist title label
|
||||
const playlistTitle = document.createElement('p');
|
||||
//Set it's class
|
||||
playlistTitle.classList.add('queue-playlist-title');
|
||||
//Unescape Sanatized Enteties and safely inject as plaintext
|
||||
playlistTitle.innerText = utils.unescapeEntities(playlist.name);
|
||||
|
||||
//Construct playlist title span
|
||||
playlistTitleSpan.appendChild(playlistTitleCaret);
|
||||
playlistTitleSpan.appendChild(playlistTitle);
|
||||
|
||||
//Create playlist count label
|
||||
const playlistCount = document.createElement('p');
|
||||
//Set it's class
|
||||
playlistCount.classList.add('queue-playlist-count');
|
||||
//List video count
|
||||
playlistCount.innerText = `Count: ${playlist.media.length}`;
|
||||
|
||||
//Append items to playlist labels span
|
||||
playlistLabels.appendChild(playlistTitleSpan);
|
||||
playlistLabels.appendChild(playlistCount);
|
||||
|
||||
//Define input listeners
|
||||
playlistTitleSpan.addEventListener('click', toggleMedia.bind(this));
|
||||
|
||||
//return playlistLabels
|
||||
return playlistLabels;
|
||||
|
||||
function toggleMedia(){
|
||||
//If the div is hidden
|
||||
if(mediaContainer.style.display == 'none'){
|
||||
//Light up the button
|
||||
playlistTitleSpan.classList.add('positive');
|
||||
//Flip the caret
|
||||
playlistTitleCaret.classList.replace('bi-caret-right-fill', 'bi-caret-down-fill');
|
||||
//Show the div
|
||||
mediaContainer.style.display = '';
|
||||
}else{
|
||||
//Unlight the button
|
||||
playlistTitleSpan.classList.remove('positive');
|
||||
//Flip the caret
|
||||
playlistTitleCaret.classList.replace('bi-caret-down-fill', 'bi-caret-right-fill');
|
||||
//Hide the div
|
||||
mediaContainer.style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function renderControls(){
|
||||
//Create playlist control span
|
||||
const playlistControls = document.createElement('span');
|
||||
//Set it's class
|
||||
playlistControls.classList.add('queue-playlist-control-span');
|
||||
|
||||
//Create queue all button
|
||||
const playlistQueueRandomButton = document.createElement('button');
|
||||
//Set it's classes
|
||||
playlistQueueRandomButton.classList.add('queue-playlist-queue-random-button', 'queue-playlist-control');
|
||||
//Inject text content
|
||||
playlistQueueRandomButton.textContent = 'Random';
|
||||
//Set title
|
||||
playlistQueueRandomButton.title = 'Queue Random Item from Playlist';
|
||||
|
||||
//Create queue all button
|
||||
const playlistQueueAllButton = document.createElement('button');
|
||||
//Set it's classes
|
||||
playlistQueueAllButton.classList.add('queue-playlist-queue-all-button', 'queue-playlist-control');
|
||||
//Inject text content
|
||||
playlistQueueAllButton.textContent = 'All';
|
||||
//Set title
|
||||
playlistQueueAllButton.title = 'Queue Entire Playlist';
|
||||
|
||||
//Create add from URL button
|
||||
const playlistAddURLButton = document.createElement('button');
|
||||
//Set it's classes
|
||||
playlistAddURLButton.classList.add('queue-playlist-add-url-button', 'queue-playlist-control', 'positive-button');
|
||||
//Set Tile
|
||||
playlistAddURLButton.title = 'Add To Playlist From URL'
|
||||
|
||||
//Create playlist icons (we're using two so we're putting them inside the button :P)
|
||||
const playlistAddIcon = document.createElement('i');
|
||||
const playlistLinkIcon = document.createElement('i');
|
||||
//set classes
|
||||
playlistAddIcon.classList.add('bi-plus-lg');
|
||||
playlistLinkIcon.classList.add('bi-link-45deg');
|
||||
|
||||
playlistAddURLButton.appendChild(playlistAddIcon);
|
||||
playlistAddURLButton.appendChild(playlistLinkIcon);
|
||||
|
||||
//Create default titles button
|
||||
const playlistDefaultTitlesButton = document.createElement('button');
|
||||
//Set classes
|
||||
playlistDefaultTitlesButton.classList.add('queue-playlist-add-url-button', 'queue-playlist-control', 'bi-tags-fill', 'positive-button');
|
||||
//Set title
|
||||
playlistDefaultTitlesButton.title = 'Change Default Titles'
|
||||
|
||||
//Create rename button
|
||||
const playlistRenameButton = document.createElement('button');
|
||||
//Set it's classes
|
||||
playlistRenameButton.classList.add('queue-playlist-add-url-button', 'queue-playlist-control', 'bi-input-cursor-text', 'positive-button');
|
||||
//Set title
|
||||
playlistRenameButton.title = 'Rename Playlist'
|
||||
|
||||
|
||||
//Create delete button
|
||||
const playlistDeleteButton = document.createElement('button');
|
||||
//Set it's classes
|
||||
playlistDeleteButton.classList.add('queue-playlist-delete-button', 'queue-playlist-control', 'danger-button', 'bi-trash-fill');
|
||||
//Set title
|
||||
playlistDeleteButton.title = 'Delete Playlist'
|
||||
|
||||
//Append items to playlist control span
|
||||
playlistControls.appendChild(playlistQueueRandomButton);
|
||||
playlistControls.appendChild(playlistQueueAllButton);
|
||||
playlistControls.appendChild(playlistAddURLButton);
|
||||
playlistControls.appendChild(playlistDefaultTitlesButton);
|
||||
playlistControls.appendChild(playlistRenameButton);
|
||||
playlistControls.appendChild(playlistDeleteButton);
|
||||
|
||||
//Define input event listeners
|
||||
playlistAddURLButton.addEventListener('click', (event)=>{new addURLPopup(event, playlist.name, this.client, this.queuePanel.ownerDoc)})
|
||||
playlistQueueAllButton.addEventListener('click', queueAll);
|
||||
playlistDeleteButton.addEventListener('click', deletePlaylist);
|
||||
|
||||
return playlistControls;
|
||||
}
|
||||
|
||||
function renderMedia(){
|
||||
//Create media container div
|
||||
const mediaContainer = document.createElement('div');
|
||||
//Set classes
|
||||
mediaContainer.classList.add('queue-playlist-media-container-div');
|
||||
//Auto-hide media container
|
||||
mediaContainer.style.display = 'none';
|
||||
|
||||
for(let mediaIndex in playlist.media){
|
||||
//Grab media object from playlist
|
||||
const media = playlist.media[mediaIndex];
|
||||
|
||||
//Create media div
|
||||
const mediaDiv = document.createElement('div');
|
||||
//Set class
|
||||
mediaDiv.classList.add('queue-playlist-media-div');
|
||||
|
||||
//If this isn't our first rodeo
|
||||
if(mediaIndex != 0){
|
||||
mediaDiv.classList.add('not-first');
|
||||
}
|
||||
|
||||
//Create media title
|
||||
const mediaTitle = document.createElement('p');
|
||||
//Set class
|
||||
mediaTitle.classList.add('queue-playlist-media-title');
|
||||
//Inject text content
|
||||
mediaTitle.innerText = utils.unescapeEntities(media.title);
|
||||
|
||||
//Append items to media div
|
||||
mediaDiv.appendChild(mediaTitle);
|
||||
|
||||
//Append media div to media container
|
||||
mediaContainer.appendChild(mediaDiv);
|
||||
}
|
||||
|
||||
//return media container
|
||||
return mediaContainer;
|
||||
}
|
||||
|
||||
//playlist control functions
|
||||
function queueAll(){
|
||||
client.socket.emit('queueChannelPlaylist', {playlist: playlist.name});
|
||||
}
|
||||
|
||||
function deletePlaylist(){
|
||||
client.socket.emit('deleteChannelPlaylist', {playlist: playlist.name});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class newPlaylistPopup{
|
||||
constructor(event, client, doc){
|
||||
//Set Client
|
||||
this.client = client;
|
||||
|
||||
//Create media popup and call async constructor when done
|
||||
//unfortunately we cant call constructors asyncronously, and we cant call back to this from super, so we can't extend this as it stands :(
|
||||
this.popup = new canopyUXUtils.popup('/newPlaylist', true, this.asyncConstructor.bind(this), doc);
|
||||
}
|
||||
|
||||
asyncConstructor(){
|
||||
this.name = this.popup.contentDiv.querySelector('#queue-create-playlist-popup-name');
|
||||
this.defaultTitles = this.popup.contentDiv.querySelector('#queue-create-playlist-popup-default-titles');
|
||||
this.location = this.popup.contentDiv.querySelector('#queue-create-playlist-popup-location');
|
||||
this.saveButton = this.popup.contentDiv.querySelector('#queue-create-playlist-popup-save');
|
||||
|
||||
this.setupInput();
|
||||
}
|
||||
|
||||
setupInput(){
|
||||
//Setup input
|
||||
this.saveButton.addEventListener('click', this.createPlaylist.bind(this));
|
||||
this.popup.popupDiv.addEventListener('keydown', this.createPlaylist.bind(this));
|
||||
}
|
||||
|
||||
createPlaylist(event){
|
||||
//If we clicked or hit enter
|
||||
if(event.key == null || event.key == "Enter"){
|
||||
|
||||
//If we're saving to the channel
|
||||
if(this.location.value == 'channel'){
|
||||
//Tell the server to create a new channel playlist
|
||||
this.client.socket.emit('createChannelPlaylist', {
|
||||
playlist: this.name.value,
|
||||
defaultTitles: this.defaultTitles.value.split('\n')
|
||||
})
|
||||
}
|
||||
|
||||
//Close the popup
|
||||
this.popup.closePopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class addURLPopup{
|
||||
constructor(event, playlist, client, doc){
|
||||
//Set Client
|
||||
this.client = client;
|
||||
|
||||
//Set playlist
|
||||
this.playlist = playlist
|
||||
|
||||
//Create media popup and call async constructor when done
|
||||
//unfortunately we cant call constructors asyncronously, and we cant call back to this from super, so we can't extend this as it stands :(
|
||||
this.popup = new canopyUXUtils.popup('/addToPlaylist', true, this.asyncConstructor.bind(this), doc);
|
||||
}
|
||||
|
||||
asyncConstructor(){
|
||||
this.urlPrompt = this.popup.contentDiv.querySelector('#playlist-add-media-popup-prompt');
|
||||
this.addButton = this.popup.contentDiv.querySelector('#playlist-add-media-popup-button');
|
||||
|
||||
this.setupInput();
|
||||
}
|
||||
|
||||
setupInput(){
|
||||
//Setup input
|
||||
this.addButton.addEventListener('click', this.addToPlaylist.bind(this));
|
||||
this.popup.popupDiv.addEventListener('keydown', this.addToPlaylist.bind(this));
|
||||
}
|
||||
|
||||
addToPlaylist(event){
|
||||
//If we clicked or hit enter
|
||||
if(event.key == null || event.key == "Enter"){
|
||||
|
||||
//Tell the server to create a new channel playlist
|
||||
this.client.socket.emit('addToChannelPlaylist', {
|
||||
playlist: this.playlist,
|
||||
url: this.urlPrompt.value
|
||||
});
|
||||
|
||||
//Close the popup
|
||||
this.popup.closePopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class defaultTitlesPopup{
|
||||
constructor(event, playlist, titles, client, doc){
|
||||
//Set Client
|
||||
this.client = client;
|
||||
|
||||
//Set playlist
|
||||
this.playlist = playlist
|
||||
|
||||
//Set title string
|
||||
this.titles = titles.join('\n');
|
||||
|
||||
//Create media popup and call async constructor when done
|
||||
//unfortunately we cant call constructors asyncronously, and we cant call back to this from super, so we can't extend this as it stands :(
|
||||
this.popup = new canopyUXUtils.popup('/addToPlaylist', true, this.asyncConstructor.bind(this), doc);
|
||||
}
|
||||
|
||||
asyncConstructor(){
|
||||
this.urlPrompt = this.popup.contentDiv.querySelector('#playlist-add-media-popup-prompt');
|
||||
this.addButton = this.popup.contentDiv.querySelector('#playlist-add-media-popup-button');
|
||||
|
||||
this.setupInput();
|
||||
}
|
||||
|
||||
setupInput(){
|
||||
//Setup input
|
||||
this.addButton.addEventListener('click', this.addToPlaylist.bind(this));
|
||||
this.popup.popupDiv.addEventListener('keydown', this.addToPlaylist.bind(this));
|
||||
}
|
||||
|
||||
addToPlaylist(event){
|
||||
//If we clicked or hit enter
|
||||
if(event.key == null || event.key == "Enter"){
|
||||
|
||||
//Tell the server to create a new channel playlist
|
||||
this.client.socket.emit('addToChannelPlaylist', {
|
||||
playlist: this.playlist,
|
||||
url: this.urlPrompt.value
|
||||
});
|
||||
|
||||
//Close the popup
|
||||
this.popup.closePopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -360,6 +360,7 @@ class canopyUXUtils{
|
|||
this.content = content;
|
||||
this.ajaxPopup = ajaxPopup;
|
||||
this.cb = cb;
|
||||
console.log(doc);
|
||||
this.doc = doc;
|
||||
//define popup nodes
|
||||
this.createPopup();
|
||||
|
|
|
|||
Loading…
Reference in a new issue