Add ability to clear upcoming song queue with single click.

This commit is contained in:
Buster "Silver Eagle" Neece 2021-09-05 17:46:33 -05:00
parent a35e8bf27d
commit 8d3cab6e76
No known key found for this signature in database
GPG Key ID: 6D9E12FF03411F4E
6 changed files with 98 additions and 43 deletions

View File

@ -5,6 +5,8 @@ release channel, you can take advantage of these new features and fixes.
## New Features/Changes
- You can now clear the entire upcoming song queue with a single button click.
## Code Quality/Technical Changes
- A number of security fixes are being incorporated into the software as of this version. See below for details.

View File

@ -240,6 +240,9 @@ return static function (RouteCollectorProxy $app) {
$group->get('', Controller\Api\Stations\QueueController::class . ':listAction')
->setName('api:stations:queue');
$group->post('/clear', Controller\Api\Stations\QueueController::class . ':clearAction')
->setName('api:stations:queue:clear');
$group->get('/{id}', Controller\Api\Stations\QueueController::class . ':getAction')
->setName('api:stations:queue:record');

View File

@ -4,44 +4,49 @@
<b-card-header header-bg-variant="primary-dark">
<h2 class="card-title" key="lang_queue" v-translate>Upcoming Song Queue</h2>
</b-card-header>
<div class="pt-3">
<data-table ref="datatable" id="station_queue" :fields="fields" :api-url="listUrl" handle-client-side>
<template #cell(actions)="row">
<b-button-group>
<b-button v-if="row.item.log" size="sm" variant="primary" @click.prevent="doShowLogs(row.item.log)">
<translate key="lang_btn_logs">Logs</translate>
</b-button>
<b-button size="sm" variant="danger" @click.prevent="doDelete(row.item.links.self)">
<translate key="lang_btn_delete">Delete</translate>
</b-button>
</b-button-group>
</template>
<template #cell(song_title)="row">
<div v-if="row.item.autodj_custom_uri">
{{ row.item.autodj_custom_uri }}
</div>
<div v-else-if="row.item.song.title">
<b>{{ row.item.song.title }}</b><br>
{{ row.item.song.artist }}
</div>
<div v-else>
{{ row.item.song.text }}
</div>
</template>
<template #cell(cued_at)="row">
{{ formatTime(row.item.cued_at) }}
</template>
<template #cell(source)="row">
<div v-if="row.item.is_request">
<translate key="lang_source_request">Listener Request</translate>
</div>
<div v-else-if="row.item.playlist">
<translate key="lang_source_playlist">Playlist: </translate>
{{ row.item.playlist }}
</div>
</template>
</data-table>
<div class="card-actions">
<b-button variant="outline-danger" @click="doClear()">
<icon icon="remove"></icon>
<translate key="lang_btn_clear_requests">Clear Upcoming Song Queue</translate>
</b-button>
</div>
<data-table ref="datatable" id="station_queue" :fields="fields" :api-url="listUrl" handle-client-side>
<template #cell(actions)="row">
<b-button-group>
<b-button v-if="row.item.log" size="sm" variant="primary"
@click.prevent="doShowLogs(row.item.log)">
<translate key="lang_btn_logs">Logs</translate>
</b-button>
<b-button size="sm" variant="danger" @click.prevent="doDelete(row.item.links.self)">
<translate key="lang_btn_delete">Delete</translate>
</b-button>
</b-button-group>
</template>
<template #cell(song_title)="row">
<div v-if="row.item.autodj_custom_uri">
{{ row.item.autodj_custom_uri }}
</div>
<div v-else-if="row.item.song.title">
<b>{{ row.item.song.title }}</b><br>
{{ row.item.song.artist }}
</div>
<div v-else>
{{ row.item.song.text }}
</div>
</template>
<template #cell(cued_at)="row">
{{ formatTime(row.item.cued_at) }}
</template>
<template #cell(source)="row">
<div v-if="row.item.is_request">
<translate key="lang_source_request">Listener Request</translate>
</div>
<div v-else-if="row.item.playlist">
<translate key="lang_source_playlist">Playlist: </translate>
{{ row.item.playlist }}
</div>
</template>
</data-table>
</b-card>
<queue-logs-modal ref="logs_modal"></queue-logs-modal>
@ -52,22 +57,24 @@
import DataTable from '../Common/DataTable';
import QueueLogsModal from './Queue/LogsModal';
import handleAxiosError from '../Function/handleAxiosError';
import Icon from "../Common/Icon";
export default {
name: 'StationPlaylists',
components: { QueueLogsModal, DataTable },
components: {QueueLogsModal, DataTable, Icon},
props: {
listUrl: String,
clearUrl: String,
locale: String,
stationTimeZone: String
},
data () {
data() {
return {
fields: [
{ key: 'actions', label: this.$gettext('Actions'), sortable: false },
{ key: 'song_title', isRowHeader: true, label: this.$gettext('Song Title'), sortable: false },
{ key: 'cued_at', label: this.$gettext('Cued On'), sortable: false },
{ key: 'source', label: this.$gettext('Source'), sortable: false }
{key: 'actions', label: this.$gettext('Actions'), sortable: false},
{key: 'song_title', isRowHeader: true, label: this.$gettext('Song Title'), sortable: false},
{key: 'cued_at', label: this.$gettext('Cued On'), sortable: false},
{key: 'source', label: this.$gettext('Source'), sortable: false}
]
};
},
@ -99,6 +106,28 @@ export default {
this.axios.delete(url).then((resp) => {
notify('<b>' + resp.data.message + '</b>', 'success');
this.$refs.datatable.refresh();
}).catch((err) => {
handleAxiosError(err);
});
}
});
},
doClear() {
let buttonText = this.$gettext('Clear');
let buttonConfirmText = this.$gettext('Clear upcoming song queue?');
Swal.fire({
title: buttonConfirmText,
confirmButtonText: buttonText,
confirmButtonColor: '#e64942',
showCancelButton: true,
focusCancel: true
}).then((result) => {
if (result.value) {
this.axios.post(this.clearUrl).then((resp) => {
notify('<b>' + resp.data.message + '</b>', 'success');
this.$refs.datatable.refresh();
}).catch((err) => {
handleAxiosError(err);

View File

@ -134,4 +134,12 @@ class QueueController extends AbstractStationApiCrudController
return $apiResponse;
}
public function clearAction(ServerRequest $request, Response $response): ResponseInterface
{
$station = $request->getStation();
$this->queueRepo->clearUpcomingQueue($station);
return $response->withJson(Entity\Api\Status::deleted());
}
}

View File

@ -96,6 +96,18 @@ class StationQueueRepository extends Repository
return $this->getUpcomingQuery($station)->execute();
}
public function clearUpcomingQueue(Entity\Station $station): void
{
$this->em->createQuery(
<<<'DQL'
DELETE FROM App\Entity\StationQueue sq
WHERE sq.station = :station
AND sq.sent_to_autodj = 0
DQL
)->setParameter('station', $station)
->execute();
}
public function getUpcomingQuery(Entity\Station $station): Query
{
return $this->getUpcomingBaseQuery($station)->getQuery();

View File

@ -5,6 +5,7 @@ $this->layout('main', ['title' => __('Upcoming Song Queue'), 'manual' => true]);
/** @var App\Http\RouterInterface $router */
$props = [
'listUrl' => (string)$router->fromHere('api:stations:queue'),
'clearUrl' => (string)$router->fromHere('api:stations:queue:clear'),
'locale' => substr($customization->getLocale(), 0, 2),
'stationTimeZone' => $stationTz,
];