Move badges to common, move profile to Vue3 Comp API.

This commit is contained in:
Buster Neece 2022-12-17 23:47:50 -06:00
parent 27e5be80ee
commit b663eb5736
No known key found for this signature in database
GPG Key ID: F1D2E64A0005E80E
15 changed files with 502 additions and 503 deletions

View File

@ -136,7 +136,7 @@ import AccountTwoFactorModal from "./Account/TwoFactorModal";
import AccountEditModal from "./Account/EditModal";
import Avatar from "~/components/Common/Avatar";
import InfoCard from "~/components/Common/InfoCard";
import EnabledBadge from "~/components/Stations/Profile/Common/EnabledBadge.vue";
import EnabledBadge from "~/components/Common/Badges/EnabledBadge.vue";
export default {
name: 'Account',

View File

@ -115,13 +115,17 @@ import {DateTime} from 'luxon';
import formatFileSize from "~/functions/formatFileSize";
import AdminBackupsConfigureModal from "~/components/Admin/Backups/ConfigureModal";
import AdminBackupsRunBackupModal from "~/components/Admin/Backups/RunBackupModal";
import EnabledBadge from "~/components/Stations/Profile/Common/EnabledBadge.vue";
import EnabledBadge from "~/components/Common/Badges/EnabledBadge.vue";
export default {
name: 'AdminBackups',
components: {
EnabledBadge,
AdminBackupsRunBackupModal, AdminBackupsConfigureModal, AdminBackupsLastOutputModal, DataTable, Icon
AdminBackupsRunBackupModal,
AdminBackupsConfigureModal,
AdminBackupsLastOutputModal,
DataTable,
Icon
},
props: {
listUrl: String,

View File

@ -32,6 +32,12 @@
</template>
<script>
export default {
inheritAttrs: false
};
</script>
<script setup>
import ProfileStreams from './Profile/StreamsPanel';
import ProfileHeader, {profileHeaderProps} from './Profile/HeaderPanel';
import ProfileNowPlaying, {profileNowPlayingProps} from './Profile/NowPlayingPanel';
@ -45,75 +51,58 @@ import ProfileBackend, {profileBackendProps} from './Profile/BackendPanel';
import {profileEmbedModalProps} from './Profile/EmbedModal';
import {BACKEND_NONE, FRONTEND_REMOTE} from '~/components/Entity/RadioAdapters.js';
import NowPlaying from '~/components/Entity/NowPlaying';
import {computed, onMounted, shallowRef} from "vue";
import {useAxios} from "~/vendor/axios";
export default {
inheritAttrs: false,
components: {
ProfileBackend,
ProfileBackendNone,
ProfileFrontend,
ProfilePublicPages,
ProfileStreamers,
ProfileRequests,
ProfileSchedule,
ProfileNowPlaying,
ProfileHeader,
ProfileStreams
},
mixins: [
profileHeaderProps,
profileNowPlayingProps,
profileRequestsProps,
profileStreamersProps,
profilePublicProps,
profileFrontendProps,
profileBackendProps,
profileEmbedModalProps
],
props: {
profileApiUri: String,
stationTimeZone: String,
stationSupportsRequests: Boolean,
stationSupportsStreamers: Boolean
},
data() {
return {
np: {
...NowPlaying,
loading: true,
services: {
backend_running: false,
frontend_running: false
},
schedule: []
}
};
},
mounted() {
this.checkNowPlaying();
},
computed: {
hasActiveFrontend() {
return this.frontendType !== FRONTEND_REMOTE;
},
hasActiveBackend() {
return this.backendType !== BACKEND_NONE;
},
},
methods: {
checkNowPlaying() {
this.axios.get(this.profileApiUri).then((response) => {
let np = response.data;
np.loading = false;
this.np = np;
const props = defineProps({
...profileHeaderProps,
...profileNowPlayingProps,
...profileRequestsProps,
...profileStreamersProps,
...profilePublicProps,
...profileFrontendProps,
...profileBackendProps,
...profileEmbedModalProps,
profileApiUri: String,
stationTimeZone: String,
stationSupportsRequests: Boolean,
stationSupportsStreamers: Boolean
});
setTimeout(this.checkNowPlaying, (!document.hidden) ? 15000 : 30000);
}).catch((error) => {
if (!error.response || error.response.data.code !== 403) {
setTimeout(this.checkNowPlaying, (!document.hidden) ? 30000 : 120000);
}
});
const np = shallowRef({
...NowPlaying,
loading: true,
services: {
backend_running: false,
frontend_running: false
},
schedule: []
});
const hasActiveFrontend = computed(() => {
return props.frontendType !== FRONTEND_REMOTE;
});
const hasActiveBackend = computed(() => {
return props.backendType !== BACKEND_NONE;
});
const {axios} = useAxios();
const checkNowPlaying = () => {
axios.get(props.profileApiUri).then((response) => {
let np_new = response.data;
np_new.loading = false;
np.value = np_new;
setTimeout(checkNowPlaying, (!document.hidden) ? 15000 : 30000);
}).catch((error) => {
if (!error.response || error.response.data.code !== 403) {
setTimeout(checkNowPlaying, (!document.hidden) ? 30000 : 120000);
}
}
};
});
}
onMounted(checkNowPlaying);
</script>

View File

@ -38,53 +38,68 @@
</template>
<script>
import {BACKEND_LIQUIDSOAP} from '~/components/Entity/RadioAdapters.js';
import Icon from '~/components/Common/Icon';
import RunningBadge from "./Common/RunningBadge.vue";
export const profileBackendProps = {
props: {
numSongs: Number,
numPlaylists: Number,
backendType: String,
hasStarted: Boolean,
userCanManageBroadcasting: Boolean,
userCanManageMedia: Boolean,
manageMediaUri: String,
managePlaylistsUri: String,
backendRestartUri: String,
backendStartUri: String,
backendStopUri: String
}
numSongs: Number,
numPlaylists: Number,
backendType: String,
hasStarted: Boolean,
userCanManageBroadcasting: Boolean,
userCanManageMedia: Boolean,
manageMediaUri: String,
managePlaylistsUri: String,
backendRestartUri: String,
backendStartUri: String,
backendStopUri: String
};
export default {
inheritAttrs: false,
components: {RunningBadge, Icon},
mixins: [profileBackendProps],
props: {
np: Object
},
computed: {
langTotalTracks() {
let numSongsRaw = this.$ngettext('%{numSongs} uploaded song', '%{numSongs} uploaded songs', this.numSongs);
let numSongs = this.$gettextInterpolate(numSongsRaw, {numSongs: this.numSongs});
let numPlaylistsRaw = this.$ngettext('%{numPlaylists} playlist', '%{numPlaylists} playlists', this.numPlaylists);
let numPlaylists = this.$gettextInterpolate(numPlaylistsRaw, { numPlaylists: this.numPlaylists });
let translated = this.$gettext('LiquidSoap is currently shuffling from %{songs} and %{playlists}.');
return this.$gettextInterpolate(translated, {
songs: numSongs,
playlists: numPlaylists
});
},
backendName () {
if (this.backendType === BACKEND_LIQUIDSOAP) {
return 'Liquidsoap';
}
return '';
}
}
};
}
</script>
<script setup>
import {BACKEND_LIQUIDSOAP} from '~/components/Entity/RadioAdapters.js';
import Icon from '~/components/Common/Icon';
import RunningBadge from "~/components/Common/Badges/RunningBadge.vue";
import gettext from "~/vendor/gettext";
import {computed} from "vue";
const props = defineProps({
...profileBackendProps,
np: Object
});
const {$gettext, $ngettext} = gettext;
const langTotalTracks = computed(() => {
let numSongs = $ngettext(
'%{numSongs} uploaded song',
'%{numSongs} uploaded songs',
props.numSongs,
{numSongs: props.numSongs}
);
let numPlaylists = $ngettext(
'%{numPlaylists} playlist',
'%{numPlaylists} playlists',
props.numPlaylists,
{numPlaylists: props.numPlaylists}
);
return $gettext(
'LiquidSoap is currently shuffling from %{songs} and %{playlists}.',
{
songs: numSongs,
playlists: numPlaylists
}
);
});
const backendName = computed(() => {
if (props.backendType === BACKEND_LIQUIDSOAP) {
return 'Liquidsoap';
}
return '';
});
</script>

View File

@ -1,5 +1,5 @@
<template>
<b-modal size="lg" id="embed_modal" ref="modal" :title="langTitle" hide-footer no-enforce-focus>
<b-modal size="lg" id="embed_modal" ref="modal" :title="$gettext('Embed Widgets')" hide-footer no-enforce-focus>
<b-row>
<b-col md="7">
<b-card class="mb-3" no-body>
@ -9,7 +9,7 @@
<b-card-body>
<b-row>
<b-col md="6">
<b-form-group :label="langEmbedType">
<b-form-group :label="$gettext('Widget Type')">
<b-form-radio-group
id="embed_type"
v-model="selectedType"
@ -20,7 +20,7 @@
</b-form-group>
</b-col>
<b-col md="6">
<b-form-group :label="langTheme">
<b-form-group :label="$gettext('Theme')">
<b-form-radio-group
id="embed_theme"
v-model="selectedTheme"
@ -61,133 +61,140 @@
</template>
<script>
import CopyToClipboardButton from '~/components/Common/CopyToClipboardButton';
export const profileEmbedModalProps = {
props: {
stationSupportsStreamers: Boolean,
stationSupportsRequests: Boolean,
enablePublicPage: Boolean,
enableStreamers: Boolean,
enableOnDemand: Boolean,
enableRequests: Boolean,
publicPageEmbedUri: String,
publicOnDemandEmbedUri: String,
publicRequestEmbedUri: String,
publicHistoryEmbedUri: String,
publicScheduleEmbedUri: String
}
stationSupportsStreamers: Boolean,
stationSupportsRequests: Boolean,
enablePublicPage: Boolean,
enableStreamers: Boolean,
enableOnDemand: Boolean,
enableRequests: Boolean,
publicPageEmbedUri: String,
publicOnDemandEmbedUri: String,
publicRequestEmbedUri: String,
publicHistoryEmbedUri: String,
publicScheduleEmbedUri: String
};
export default {
inheritAttrs: false,
components: {CopyToClipboardButton},
mixins: [profileEmbedModalProps],
data() {
let types = [
{
value: 'player',
text: this.$gettext('Radio Player')
},
{
value: 'history',
text: this.$gettext('History')
},
{
value: 'schedule',
text: this.$gettext('Schedule')
}
];
if (this.stationSupportsRequests && this.enableRequests) {
types.push({
value: 'requests',
text: this.$gettext('Requests')
});
}
if (this.enableOnDemand) {
types.push({
value: 'ondemand',
text: this.$gettext('On-Demand Media')
});
}
return {
selectedType: 'player',
types: types,
selectedTheme: 'light',
themes: [
{
value: 'light',
text: this.$gettext('Light')
},
{
value: 'dark',
text: this.$gettext('Dark')
}
]
};
},
computed: {
langTitle() {
return this.$gettext('Embed Widgets');
},
langEmbedType() {
return this.$gettext('Widget Type');
},
langTheme() {
return this.$gettext('Theme');
},
baseEmbedUrl() {
switch (this.selectedType) {
case 'history':
return this.publicHistoryEmbedUri;
case 'ondemand':
return this.publicOnDemandEmbedUri;
case 'requests':
return this.publicRequestEmbedUri;
case 'schedule':
return this.publicScheduleEmbedUri;
case 'player':
default:
return this.publicPageEmbedUri;
}
},
embedUrl() {
return this.baseEmbedUrl + '?theme=' + this.selectedTheme;
},
embedHeight() {
switch (this.selectedType) {
case 'ondemand':
return '400px';
case 'requests':
return '850px';
case 'history':
return '300px';
case 'schedule':
return '800px'
case 'player':
default:
return '150px';
}
},
embedCode() {
return '<iframe src="' + this.embedUrl + '" frameborder="0" allowtransparency="true" style="width: 100%; min-height: ' + this.embedHeight + '; border: 0;"></iframe>';
}
},
methods: {
open() {
this.$refs.modal.show();
}
}
};
inheritAttrs: false
}
</script>
<script setup>
import CopyToClipboardButton from '~/components/Common/CopyToClipboardButton';
import {computed, ref} from "vue";
import gettext from "~/vendor/gettext";
const props = defineProps({
...profileEmbedModalProps
});
const selectedType = ref('player');
const selectedTheme = ref('light');
const {$gettext} = gettext;
const types = computed(() => {
let types = [
{
value: 'player',
text: $gettext('Radio Player')
},
{
value: 'history',
text: $gettext('History')
},
{
value: 'schedule',
text: $gettext('Schedule')
}
];
if (props.stationSupportsRequests && props.enableRequests) {
types.push({
value: 'requests',
text: $gettext('Requests')
});
}
if (props.enableOnDemand) {
types.push({
value: 'ondemand',
text: $gettext('On-Demand Media')
});
}
return types;
});
const themes = computed(() => {
return [
{
value: 'light',
text: $gettext('Light')
},
{
value: 'dark',
text: $gettext('Dark')
}
];
});
const baseEmbedUrl = computed(() => {
switch (selectedType.value) {
case 'history':
return props.publicHistoryEmbedUri;
case 'ondemand':
return props.publicOnDemandEmbedUri;
case 'requests':
return props.publicRequestEmbedUri;
case 'schedule':
return props.publicScheduleEmbedUri;
case 'player':
default:
return props.publicPageEmbedUri;
}
});
const embedUrl = computed(() => {
return baseEmbedUrl.value + '?theme=' + selectedTheme.value;
});
const embedHeight = computed(() => {
switch (selectedType.value) {
case 'ondemand':
return '400px';
case 'requests':
return '850px';
case 'history':
return '300px';
case 'schedule':
return '800px'
case 'player':
default:
return '150px';
}
});
const embedCode = computed(() => {
return '<iframe src="' + embedUrl.value + '" frameborder="0" allowtransparency="true" style="width: 100%; min-height: ' + embedHeight.value + '; border: 0;"></iframe>';
});
const modal = ref(); // Template Ref
const open = () => {
modal.value.show();
};
defineExpose({
open
});
</script>

View File

@ -93,45 +93,42 @@
</template>
<script>
import {FRONTEND_ICECAST, FRONTEND_SHOUTCAST} from '~/components/Entity/RadioAdapters.js';
import CopyToClipboardButton from '~/components/Common/CopyToClipboardButton';
import Icon from '~/components/Common/Icon';
import RunningBadge from "./Common/RunningBadge.vue";
export const profileFrontendProps = {
props: {
frontendType: String,
frontendAdminUri: String,
frontendAdminPassword: String,
frontendSourcePassword: String,
frontendRelayPassword: String,
frontendRestartUri: String,
frontendStartUri: String,
frontendStopUri: String,
hasStarted: Boolean,
userCanManageBroadcasting: Boolean
}
frontendType: String,
frontendAdminUri: String,
frontendAdminPassword: String,
frontendSourcePassword: String,
frontendRelayPassword: String,
frontendRestartUri: String,
frontendStartUri: String,
frontendStopUri: String,
hasStarted: Boolean,
userCanManageBroadcasting: Boolean
};
export default {
inheritAttrs: false,
components: {RunningBadge, Icon, CopyToClipboardButton},
mixins: [profileFrontendProps],
props: {
np: Object
},
computed: {
frontendName() {
if (this.frontendType === FRONTEND_ICECAST) {
return 'Icecast';
} else if (this.frontendType === FRONTEND_SHOUTCAST) {
return 'Shoutcast';
}
return '';
},
isIcecast () {
return this.frontendType === FRONTEND_ICECAST;
}
}
inheritAttrs: false
};
</script>
<script setup>
import {FRONTEND_ICECAST, FRONTEND_SHOUTCAST} from '~/components/Entity/RadioAdapters.js';
import CopyToClipboardButton from '~/components/Common/CopyToClipboardButton';
import Icon from '~/components/Common/Icon';
import RunningBadge from "~/components/Common/Badges/RunningBadge.vue";
import {computed} from "vue";
const props = defineProps({
...profileFrontendProps,
np: Object
});
const frontendName = computed(() => {
if (props.frontendType === FRONTEND_ICECAST) {
return 'Icecast';
} else if (props.frontendType === FRONTEND_SHOUTCAST) {
return 'Shoutcast';
}
return '';
});
</script>

View File

@ -17,24 +17,24 @@
</template>
<script>
import Icon from '~/components/Common/Icon';
import PlayButton from "../../Common/PlayButton";
export const profileHeaderProps = {
props: {
stationName: String,
stationDescription: String,
userCanManageProfile: Boolean,
manageProfileUri: String
}
stationName: String,
stationDescription: String,
userCanManageProfile: Boolean,
manageProfileUri: String
};
export default {
inheritAttrs: false,
props: {
np: Object
},
components: {PlayButton, Icon},
mixins: [profileHeaderProps]
inheritAttrs: false
};
</script>
<script setup>
import Icon from '~/components/Common/Icon';
import PlayButton from "~/components/Common/PlayButton.vue";
const props = defineProps({
...profileHeaderProps,
np: Object
});
</script>

View File

@ -124,87 +124,77 @@
</template>
<script>
import {BACKEND_LIQUIDSOAP} from '~/components/Entity/RadioAdapters.js';
import Icon from '~/components/Common/Icon';
export const profileNowPlayingProps = {
props: {
backendType: String,
userCanManageBroadcasting: Boolean,
backendSkipSongUri: String,
backendDisconnectStreamerUri: String
}
backendType: String,
userCanManageBroadcasting: Boolean,
backendSkipSongUri: String,
backendDisconnectStreamerUri: String
};
export default {
inheritAttrs: false,
components: {Icon},
mixins: [profileNowPlayingProps],
props: {
np: Object
},
data() {
return {
npElapsed: 0,
clockInterval: null
};
},
mounted() {
this.clockInterval = setInterval(this.iterateTimer, 1000);
},
computed: {
langListeners() {
let translated = this.$ngettext('%{listeners} Listener', '%{listeners} Listeners', this.np.listeners.total);
return this.$gettextInterpolate(translated, {listeners: this.np.listeners.total});
},
isLiquidsoap() {
return this.backendType === BACKEND_LIQUIDSOAP;
},
timeDisplay() {
let time_played = this.npElapsed;
let time_total = this.np.now_playing.duration;
if (!time_total) {
return null;
}
if (time_played > time_total) {
time_played = time_total;
}
return this.formatTime(time_played) + ' / ' + this.formatTime(time_total);
}
},
methods: {
iterateTimer() {
let current_time = Math.floor(Date.now() / 1000);
let np_elapsed = current_time - this.np.now_playing.played_at;
if (np_elapsed < 0) {
np_elapsed = 0;
} else if (np_elapsed >= this.np.now_playing.duration) {
np_elapsed = this.np.now_playing.duration;
}
this.npElapsed = np_elapsed;
},
formatTime(time) {
let sec_num = parseInt(time, 10);
let hours = Math.floor(sec_num / 3600);
let minutes = Math.floor((sec_num - (hours * 3600)) / 60);
let seconds = sec_num - (hours * 3600) - (minutes * 60);
if (hours < 10) {
hours = '0' + hours;
}
if (minutes < 10) {
minutes = '0' + minutes;
}
if (seconds < 10) {
seconds = '0' + seconds;
}
return (hours !== '00' ? hours + ':' : '') + minutes + ':' + seconds;
}
}
inheritAttrs: false
};
</script>
<script setup>
import {BACKEND_LIQUIDSOAP} from '~/components/Entity/RadioAdapters.js';
import Icon from '~/components/Common/Icon';
import {computed, onMounted, ref} from "vue";
import {useIntervalFn} from "@vueuse/core";
import gettext from "~/vendor/gettext";
import formatTime from "~/functions/formatTime";
const props = defineProps({
...profileNowPlayingProps,
np: Object
});
const npElapsed = ref(0);
onMounted(() => {
useIntervalFn(
() => {
let current_time = Math.floor(Date.now() / 1000);
let np_elapsed = current_time - props.np.now_playing.played_at;
if (np_elapsed < 0) {
np_elapsed = 0;
} else if (np_elapsed >= props.np.now_playing.duration) {
np_elapsed = props.np.now_playing.duration;
}
npElapsed.value = np_elapsed;
},
1000
);
});
const {$ngettext} = gettext;
const langListeners = computed(() => {
return $ngettext(
'%{listeners} Listener',
'%{listeners} Listeners',
props.np.listeners.total,
{listeners: props.np.listeners.total}
);
});
const isLiquidsoap = computed(() => {
return props.backendType === BACKEND_LIQUIDSOAP;
});
const timeDisplay = computed(() => {
let time_played = npElapsed.value;
let time_total = props.np.now_playing.duration;
if (!time_total) {
return null;
}
if (time_played > time_total) {
time_played = time_total;
}
return formatTime(time_played) + ' / ' + formatTime(time_total);
});
</script>

View File

@ -50,7 +50,8 @@
<icon icon="code"></icon>
{{ $gettext('Embed Widgets') }}
</a>
<a class="btn btn-outline-danger" :data-confirm-title="langDisablePublicPages" :href="togglePublicPageUri">
<a class="btn btn-outline-danger" :data-confirm-title="$gettext('Disable public pages?')"
:href="togglePublicPageUri">
<icon icon="close"></icon>
{{ $gettext('Disable') }}
</a>
@ -65,7 +66,8 @@
</h3>
</div>
<div class="card-actions" v-if="userCanManageProfile">
<a class="btn btn-outline-success" :data-confirm-title="langEnablePublicPages" :href="togglePublicPageUri">
<a class="btn btn-outline-success" :data-confirm-title="$gettext('Enable public pages?')"
:href="togglePublicPageUri">
<icon icon="check"></icon>
{{ $gettext('Enable') }}
</a>
@ -75,44 +77,43 @@
</template>
<script>
import EmbedModal, {profileEmbedModalProps} from './EmbedModal';
import Icon from '~/components/Common/Icon';
import EnabledBadge from "./Common/EnabledBadge.vue";
import {profileEmbedModalProps} from './EmbedModal';
export const profilePublicProps = {
props: {
stationSupportsStreamers: Boolean,
stationSupportsRequests: Boolean,
enablePublicPage: Boolean,
enableStreamers: Boolean,
enableOnDemand: Boolean,
enableRequests: Boolean,
userCanManageProfile: Boolean,
publicPageUri: String,
publicWebDjUri: String,
publicOnDemandUri: String,
publicPodcastsUri: String,
publicScheduleUri: String,
togglePublicPageUri: String
}
stationSupportsStreamers: Boolean,
stationSupportsRequests: Boolean,
enablePublicPage: Boolean,
enableStreamers: Boolean,
enableOnDemand: Boolean,
enableRequests: Boolean,
userCanManageProfile: Boolean,
publicPageUri: String,
publicWebDjUri: String,
publicOnDemandUri: String,
publicPodcastsUri: String,
publicScheduleUri: String,
togglePublicPageUri: String
};
export default {
inheritAttrs: false,
components: {EnabledBadge, Icon, EmbedModal},
mixins: [profilePublicProps, profileEmbedModalProps],
computed: {
langDisablePublicPages() {
return this.$gettext('Disable public pages?');
},
langEnablePublicPages() {
return this.$gettext('Enable public pages?');
},
},
methods: {
doOpenEmbed () {
this.$refs.embed_modal.open();
}
}
inheritAttrs: false
};
</script>
<script setup>
import Icon from '~/components/Common/Icon';
import EnabledBadge from "~/components/Common/Badges/EnabledBadge.vue";
import {ref} from "vue";
import EmbedModal from "~/components/Stations/Profile/EmbedModal.vue";
const props = defineProps({
...profilePublicProps,
...profileEmbedModalProps
});
const embed_modal = ref(); // Template Ref
const doOpenEmbed = () => {
embed_modal.value.open();
};
</script>

View File

@ -12,7 +12,8 @@
<icon icon="assignment"></icon>
{{ $gettext('View') }}
</a>
<a class="btn btn-outline-danger" v-if="userCanManageProfile" :data-confirm-title="langDisableRequests" :href="requestsToggleUri">
<a class="btn btn-outline-danger" v-if="userCanManageProfile"
:data-confirm-title="$gettext('Disable song requests?')" :href="requestsToggleUri">
<icon icon="close"></icon>
{{ $gettext('Disable') }}
</a>
@ -26,7 +27,8 @@
</h3>
</div>
<div class="card-actions" v-if="userCanManageProfile">
<a class="btn btn-outline-success" :data-confirm-title="langEnableRequests" :href="requestsToggleUri">
<a class="btn btn-outline-success" :data-confirm-title="$gettext('Enable song requests?')"
:href="requestsToggleUri">
<icon icon="check"></icon>
{{ $gettext('Enable') }}
</a>
@ -36,30 +38,23 @@
</template>
<script>
import Icon from '~/components/Common/Icon';
import EnabledBadge from "./Common/EnabledBadge.vue";
export const profileRequestsProps = {
props: {
enableRequests: Boolean,
userCanManageReports: Boolean,
userCanManageProfile: Boolean,
requestsViewUri: String,
requestsToggleUri: String
}
enableRequests: Boolean,
userCanManageReports: Boolean,
userCanManageProfile: Boolean,
requestsViewUri: String,
requestsToggleUri: String
};
export default {
inheritAttrs: false,
components: {EnabledBadge, Icon},
mixins: [profileRequestsProps],
computed: {
langDisableRequests() {
return this.$gettext('Disable song requests?');
},
langEnableRequests() {
return this.$gettext('Enable song requests?');
}
}
inheritAttrs: false
};
</script>
<script setup>
import Icon from '~/components/Common/Icon';
const props = defineProps({
...profileRequestsProps
});
</script>

View File

@ -40,48 +40,51 @@
</template>
<script>
import {DateTime} from "luxon";
import _ from "lodash";
export default {
inheritAttrs: false,
props: {
scheduleItems: Array,
stationTimeZone: String
},
computed: {
processedScheduleItems() {
const now = DateTime.now().setZone(this.stationTimeZone);
return _.map(this.scheduleItems, (row) => {
const start_moment = DateTime.fromSeconds(row.start_timestamp).setZone(this.stationTimeZone);
const end_moment = DateTime.fromSeconds(row.end_timestamp).setZone(this.stationTimeZone);
row.time_until = start_moment.toRelative();
if (start_moment.hasSame(now, 'day')) {
row.start_formatted = start_moment.toLocaleString(
{...DateTime.TIME_SIMPLE, ...App.time_config}
);
} else {
row.start_formatted = start_moment.toLocaleString(
{...DateTime.DATETIME_MED, ...App.time_config}
);
}
if (end_moment.hasSame(start_moment, 'day')) {
row.end_formatted = end_moment.toLocaleString(
{...DateTime.TIME_SIMPLE, ...App.time_config}
);
} else {
row.end_formatted = end_moment.toLocaleString(
{...DateTime.DATETIME_MED, ...App.time_config}
);
}
return row;
});
}
}
inheritAttrs: false
};
</script>
<script setup>
import {DateTime} from "luxon";
import _ from "lodash";
import {computed} from "vue";
const props = defineProps({
scheduleItems: Array,
stationTimeZone: String
});
const processedScheduleItems = computed(() => {
const now = DateTime.now().setZone(props.stationTimeZone);
return _.map(props.scheduleItems, (row) => {
const start_moment = DateTime.fromSeconds(row.start_timestamp).setZone(props.stationTimeZone);
const end_moment = DateTime.fromSeconds(row.end_timestamp).setZone(props.stationTimeZone);
row.time_until = start_moment.toRelative();
if (start_moment.hasSame(now, 'day')) {
row.start_formatted = start_moment.toLocaleString(
{...DateTime.TIME_SIMPLE, ...App.time_config}
);
} else {
row.start_formatted = start_moment.toLocaleString(
{...DateTime.DATETIME_MED, ...App.time_config}
);
}
if (end_moment.hasSame(start_moment, 'day')) {
row.end_formatted = end_moment.toLocaleString(
{...DateTime.TIME_SIMPLE, ...App.time_config}
);
} else {
row.end_formatted = end_moment.toLocaleString(
{...DateTime.DATETIME_MED, ...App.time_config}
);
}
return row;
});
});
</script>

View File

@ -12,7 +12,8 @@
<icon icon="settings"></icon>
{{ $gettext('Manage') }}
</a>
<a class="btn btn-outline-danger" v-if="userCanManageProfile" :data-confirm-title="langDisableStreamers" :href="streamersToggleUri">
<a class="btn btn-outline-danger" v-if="userCanManageProfile"
:data-confirm-title="$gettext('Disable streamers?')" :href="streamersToggleUri">
<icon icon="close"></icon>
{{ $gettext('Disable') }}
</a>
@ -26,7 +27,8 @@
</h3>
</div>
<div class="card-actions" v-if="userCanManageProfile">
<a class="btn btn-outline-success" :data-confirm-title="langEnableStreamers" :href="streamersToggleUri">
<a class="btn btn-outline-success" :data-confirm-title="$gettext('Enable streamers?')"
:href="streamersToggleUri">
<icon icon="check"></icon>
{{ $gettext('Enable') }}
</a>
@ -36,30 +38,24 @@
</template>
<script>
import Icon from '~/components/Common/Icon';
import EnabledBadge from "./Common/EnabledBadge.vue";
export const profileStreamersProps = {
props: {
enableStreamers: Boolean,
userCanManageProfile: Boolean,
userCanManageStreamers: Boolean,
streamersViewUri: String,
streamersToggleUri: String
}
enableStreamers: Boolean,
userCanManageProfile: Boolean,
userCanManageStreamers: Boolean,
streamersViewUri: String,
streamersToggleUri: String
};
export default {
inheritAttrs: false,
components: {EnabledBadge, Icon},
mixins: [profileStreamersProps],
computed: {
langDisableStreamers() {
return this.$gettext('Disable streamers?');
},
langEnableStreamers() {
return this.$gettext('Enable streamers?');
}
}
inheritAttrs: false
};
</script>
<script setup>
import Icon from "~/components/Common/Icon.vue";
import EnabledBadge from "~/components/Common/Badges/EnabledBadge.vue";
const props = defineProps({
...profileStreamersProps
});
</script>

View File

@ -102,14 +102,16 @@
</template>
<script>
export default {
inheritAttrs: false
};
</script>
<script setup>
import Icon from '~/components/Common/Icon';
import PlayButton from "~/components/Common/PlayButton";
export default {
inheritAttrs: false,
components: {PlayButton, Icon},
props: {
np: Object
}
};
const props = defineProps({
np: Object
});
</script>