4
0
mirror of https://github.com/AzuraCast/AzuraCast.git synced 2024-06-13 04:36:41 +00:00

Fixes #6647 -- Make disabled stations render correctly in new Vue SPA.

This commit is contained in:
Buster Neece 2023-10-08 10:32:26 -05:00
parent 546e1ce4a8
commit ad69a8a694
No known key found for this signature in database
10 changed files with 250 additions and 202 deletions

View File

@ -42,6 +42,8 @@ release channel, you can take advantage of these new features and fixes.
- Fixed an issue preventing web updates from going through correctly.
- Fixed an issue where disabled stations wouldn't show their profile pages at all.
---
# AzuraCast 0.19.1 (Aug 21, 2023)

View File

@ -1,176 +1,34 @@
<template>
<profile-header
v-bind="pickProps(props, headerPanelProps)"
:station="profileInfo.station"
/>
<div
id="profile"
class="row row-of-cards"
<loading
v-if="isEnabled"
:loading="isLoading"
lazy
>
<div class="col-lg-7">
<template v-if="hasStarted">
<profile-now-playing
v-bind="pickProps(props, nowPlayingPanelProps)"
@api-call="makeApiCall"
/>
<profile-schedule
:schedule-items="profileInfo.schedule"
/>
<profile-streams
:station="profileInfo.station"
/>
</template>
<template v-else>
<now-playing-not-started-panel />
</template>
<profile-public-pages
v-bind="pickProps(props, {...publicPagesPanelProps,...embedModalProps})"
/>
</div>
<div class="col-lg-5">
<profile-requests
v-if="stationSupportsRequests"
v-bind="pickProps(props, requestsPanelProps)"
/>
<profile-streamers
v-if="stationSupportsStreamers"
v-bind="pickProps(props, streamersPanelProps)"
/>
<template v-if="hasActiveFrontend">
<profile-frontend
v-bind="pickProps(props, frontendPanelProps)"
:frontend-running="profileInfo.services.frontend_running"
@api-call="makeApiCall"
/>
</template>
<template v-if="hasActiveBackend">
<profile-backend
v-bind="pickProps(props, backendPanelProps)"
:backend-running="profileInfo.services.backend_running"
@api-call="makeApiCall"
/>
</template>
<template v-else>
<profile-backend-none />
</template>
</div>
</div>
<enabled-profile v-bind="state" />
</loading>
<station-disabled-panel v-else />
</template>
<script setup lang="ts">
import ProfileStreams from './Profile/StreamsPanel.vue';
import ProfileHeader from './Profile/HeaderPanel.vue';
import ProfileNowPlaying from './Profile/NowPlayingPanel.vue';
import ProfileSchedule from './Profile/SchedulePanel.vue';
import ProfileRequests from './Profile/RequestsPanel.vue';
import ProfileStreamers from './Profile/StreamersPanel.vue';
import ProfilePublicPages from './Profile/PublicPagesPanel.vue';
import ProfileFrontend from './Profile/FrontendPanel.vue';
import ProfileBackendNone from './Profile/BackendNonePanel.vue';
import ProfileBackend from './Profile/BackendPanel.vue';
import NowPlayingNotStartedPanel from "./Profile/NowPlayingNotStartedPanel.vue";
import {BackendAdapter, FrontendAdapter} from '~/entities/RadioAdapters';
import NowPlaying from '~/entities/NowPlaying';
import {computed} from "vue";
import {useAxios} from "~/vendor/axios";
import backendPanelProps from "./Profile/backendPanelProps";
import embedModalProps from "./Profile/embedModalProps";
import frontendPanelProps from "./Profile/frontendPanelProps";
import headerPanelProps from "./Profile/headerPanelProps";
import nowPlayingPanelProps from "./Profile/nowPlayingPanelProps";
import publicPagesPanelProps from "./Profile/publicPagesPanelProps";
import requestsPanelProps from "./Profile/requestsPanelProps";
import streamersPanelProps from "./Profile/streamersPanelProps";
import {pickProps} from "~/functions/pickProps";
import useRefreshableAsyncState from "~/functions/useRefreshableAsyncState";
import {useIntervalFn} from "@vueuse/core";
import {useSweetAlert} from "~/vendor/sweetalert";
import {useNotify} from "~/functions/useNotify";
import {useTranslate} from "~/vendor/gettext";
import {useAzuraCastStation} from "~/vendor/azuracast.ts";
import {useAxios} from "~/vendor/axios.ts";
import {useAsyncState} from "@vueuse/core";
import {getStationApiUrl} from "~/router.ts";
import StationDisabledPanel from "~/components/Stations/Profile/StationDisabledPanel.vue";
import Loading from "~/components/Common/Loading.vue";
import EnabledProfile from "~/components/Stations/Profile/EnabledProfile.vue";
const props = defineProps({
...backendPanelProps,
...embedModalProps,
...frontendPanelProps,
...headerPanelProps,
...nowPlayingPanelProps,
...publicPagesPanelProps,
...requestsPanelProps,
...streamersPanelProps,
profileApiUri: {
type: String,
required: true
},
stationSupportsRequests: {
type: Boolean,
required: true
},
stationSupportsStreamers: {
type: Boolean,
required: true
}
});
const hasActiveFrontend = computed(() => {
return props.frontendType !== FrontendAdapter.Remote;
});
const hasActiveBackend = computed(() => {
return props.backendType !== BackendAdapter.None;
});
const {isEnabled} = useAzuraCastStation();
const {axios} = useAxios();
const {state: profileInfo, execute: reloadProfile} = useRefreshableAsyncState(
() => axios.get(props.profileApiUri).then((r) => r.data),
{
station: {
...NowPlaying.station
},
services: {
backend_running: false,
frontend_running: false,
has_started: false,
needs_restart: false
},
schedule: []
}
);
const profileReloadTimeout = computed(() => {
return (!document.hidden) ? 15000 : 30000
});
useIntervalFn(
reloadProfile,
profileReloadTimeout
);
const {showAlert} = useSweetAlert();
const {notify} = useNotify();
const {$gettext} = useTranslate();
const makeApiCall = (uri) => {
showAlert({
title: $gettext('Are you sure?')
}).then((result) => {
if (!result.value) {
return;
const {isLoading, state} = useAsyncState(
async () => {
if (isEnabled) {
const r = await axios.get(getStationApiUrl('/vue/profile').value);
return await r.data;
}
axios.post(uri).then((resp) => {
notify(resp.data.formatted_message, {
variant: (resp.data.success) ? 'success' : 'warning'
});
});
});
};
return {};
},
{}
);
</script>

View File

@ -0,0 +1,176 @@
<template>
<profile-header
v-bind="pickProps(props, headerPanelProps)"
:station="profileInfo.station"
/>
<div
id="profile"
class="row row-of-cards"
>
<div class="col-lg-7">
<template v-if="hasStarted">
<profile-now-playing
v-bind="pickProps(props, nowPlayingPanelProps)"
@api-call="makeApiCall"
/>
<profile-schedule
:schedule-items="profileInfo.schedule"
/>
<profile-streams
:station="profileInfo.station"
/>
</template>
<template v-else>
<now-playing-not-started-panel />
</template>
<profile-public-pages
v-bind="pickProps(props, {...publicPagesPanelProps,...embedModalProps})"
/>
</div>
<div class="col-lg-5">
<profile-requests
v-if="stationSupportsRequests"
v-bind="pickProps(props, requestsPanelProps)"
/>
<profile-streamers
v-if="stationSupportsStreamers"
v-bind="pickProps(props, streamersPanelProps)"
/>
<template v-if="hasActiveFrontend">
<profile-frontend
v-bind="pickProps(props, frontendPanelProps)"
:frontend-running="profileInfo.services.frontend_running"
@api-call="makeApiCall"
/>
</template>
<template v-if="hasActiveBackend">
<profile-backend
v-bind="pickProps(props, backendPanelProps)"
:backend-running="profileInfo.services.backend_running"
@api-call="makeApiCall"
/>
</template>
<template v-else>
<profile-backend-none />
</template>
</div>
</div>
</template>
<script setup lang="ts">
import ProfileStreams from './StreamsPanel.vue';
import ProfileHeader from './HeaderPanel.vue';
import ProfileNowPlaying from './NowPlayingPanel.vue';
import ProfileSchedule from './SchedulePanel.vue';
import ProfileRequests from './RequestsPanel.vue';
import ProfileStreamers from './StreamersPanel.vue';
import ProfilePublicPages from './PublicPagesPanel.vue';
import ProfileFrontend from './FrontendPanel.vue';
import ProfileBackendNone from './BackendNonePanel.vue';
import ProfileBackend from './BackendPanel.vue';
import NowPlayingNotStartedPanel from "./NowPlayingNotStartedPanel.vue";
import {BackendAdapter, FrontendAdapter} from '~/entities/RadioAdapters';
import NowPlaying from '~/entities/NowPlaying';
import {computed} from "vue";
import {useAxios} from "~/vendor/axios";
import backendPanelProps from "./backendPanelProps";
import embedModalProps from "./embedModalProps";
import frontendPanelProps from "./frontendPanelProps";
import headerPanelProps from "./headerPanelProps";
import nowPlayingPanelProps from "./nowPlayingPanelProps";
import publicPagesPanelProps from "./publicPagesPanelProps";
import requestsPanelProps from "./requestsPanelProps";
import streamersPanelProps from "./streamersPanelProps";
import {pickProps} from "~/functions/pickProps";
import useRefreshableAsyncState from "~/functions/useRefreshableAsyncState";
import {useIntervalFn} from "@vueuse/core";
import {useSweetAlert} from "~/vendor/sweetalert";
import {useNotify} from "~/functions/useNotify";
import {useTranslate} from "~/vendor/gettext";
const props = defineProps({
...backendPanelProps,
...embedModalProps,
...frontendPanelProps,
...headerPanelProps,
...nowPlayingPanelProps,
...publicPagesPanelProps,
...requestsPanelProps,
...streamersPanelProps,
profileApiUri: {
type: String,
required: true
},
stationSupportsRequests: {
type: Boolean,
required: true
},
stationSupportsStreamers: {
type: Boolean,
required: true
}
});
const hasActiveFrontend = computed(() => {
return props.frontendType !== FrontendAdapter.Remote;
});
const hasActiveBackend = computed(() => {
return props.backendType !== BackendAdapter.None;
});
const {axios} = useAxios();
const {state: profileInfo, execute: reloadProfile} = useRefreshableAsyncState(
() => axios.get(props.profileApiUri).then((r) => r.data),
{
station: {
...NowPlaying.station
},
services: {
backend_running: false,
frontend_running: false,
has_started: false,
needs_restart: false
},
schedule: []
}
);
const profileReloadTimeout = computed(() => {
return (!document.hidden) ? 15000 : 30000
});
useIntervalFn(
reloadProfile,
profileReloadTimeout
);
const {showAlert} = useSweetAlert();
const {notify} = useNotify();
const {$gettext} = useTranslate();
const makeApiCall = (uri) => {
showAlert({
title: $gettext('Are you sure?')
}).then((result) => {
if (!result.value) {
return;
}
axios.post(uri).then((resp) => {
notify(resp.data.formatted_message, {
variant: (resp.data.success) ? 'success' : 'warning'
});
});
});
};
</script>

View File

@ -0,0 +1,44 @@
<template>
<div class="outside-card-header d-flex align-items-center">
<div class="flex-fill">
<h2 class="display-6 m-0">
{{ name }}
</h2>
</div>
<div
v-if="userAllowedForStation(StationPermission.Profile)"
class="flex-shrink-0 ms-3"
>
<router-link
class="btn btn-primary"
role="button"
:to="{name: 'stations:profile:edit'}"
>
<icon :icon="IconEdit" />
<span>
{{ $gettext('Edit Profile') }}
</span>
</router-link>
</div>
</div>
<card-page
id="station-disabled"
:title="$gettext('Station Disabled')"
>
<div class="card-body">
<p class="card-text">
{{ $gettext('Your station is currently not enabled for broadcasting. You can still manage media, playlists, and other station settings. To re-enable broadcasting, edit your station profile.') }}
</p>
</div>
</card-page>
</template>
<script setup lang="ts">
import {StationPermission, userAllowedForStation} from "~/acl.ts";
import {IconEdit} from "~/components/Common/icons.ts";
import Icon from "~/components/Common/Icon.vue";
import CardPage from "~/components/Common/CardPage.vue";
import {useAzuraCastStation} from "~/vendor/azuracast.ts";
const {name} = useAzuraCastStation();
</script>

View File

@ -6,8 +6,7 @@ export default function useStationsRoutes() {
{
path: '/',
component: () => import('~/components/Stations/Profile.vue'),
name: 'stations:index',
...populateComponentRemotely(getStationApiUrl('/vue/profile'))
name: 'stations:index'
},
{
path: '/branding',

View File

@ -11,6 +11,7 @@ export function setGlobalProps(newGlobalProps: AzuraCastConstants): void {
export interface AzuraCastStationConstants {
id: number | null,
name: string | null,
isEnabled: boolean | null,
shortName: string | null,
timezone: string | null,
offlineText: string | null,
@ -60,6 +61,7 @@ export function useAzuraCastStation(): AzuraCastStationConstants {
return (station !== null) ? station : {
id: null,
name: null,
isEnabled: null,
shortName: null,
timezone: null,
offlineText: null

View File

@ -24,11 +24,6 @@ final class IndexAction implements SingleActionInterface
): ResponseInterface {
$station = $request->getStation();
$view = $request->getView();
if (!$station->getIsEnabled()) {
return $view->renderToResponse($response, 'stations/profile_disabled');
}
$router = $request->getRouter();
$globalProps = $view->getGlobalProps();

View File

@ -227,6 +227,7 @@ final class View extends Engine
$this->globalProps->set('station', [
'id' => $station->getIdRequired(),
'name' => $station->getName(),
'isEnabled' => $station->getIsEnabled(),
'shortName' => $station->getShortName(),
'timezone' => $station->getTimezone(),
'offlineText' => $station->getBrandingConfig()->getOfflineText(),

View File

@ -1,19 +0,0 @@
<?php
/**
* @var App\Entity\Station $station
* @var App\Http\RouterInterface $router
*/
$this->layout('main', [
'title' =>
__('Station Broadcasting Disabled'),
]);
?>
<p>
<?= sprintf(
__(
'Your station is currently not enabled for broadcasting. You can still manage media, playlists, and other station settings. To re-enable broadcasting, <a href="%s">edit your station profile</a>.'
),
$router->fromHere('stations:profile:edit')
) ?></p>

View File

@ -1,10 +0,0 @@
<?php $this->layout('main', ['title' => __('Report Not Available'), 'manual' => true]) ?>
<div class="card">
<div class="card-header">
<h2 class="card-title"><?=__('Report Not Available') ?></h2>
</div>
<div class="card-body">
<p><?=__('This report is not available for this station, because the system administrator has chosen not to collect detailed IP-based listener information.') ?></p>
</div>
</div>