Update components that use v-model values to Vue3 "Futureproof" them.
This commit is contained in:
parent
2798e926e4
commit
6cb6236fa7
|
@ -1,10 +1,9 @@
|
|||
import axios from 'axios';
|
||||
import VueAxios from 'vue-axios';
|
||||
import usePinia from './vendor/pinia';
|
||||
import installPinia from './vendor/pinia';
|
||||
import gettext from './vendor/gettext';
|
||||
import {createApp} from "vue";
|
||||
import useBootstrapVue from "./vendor/bootstrapVue";
|
||||
import useSweetAlert from "./vendor/sweetalert";
|
||||
import installBootstrapVue from "./vendor/bootstrapVue";
|
||||
import installSweetAlert from "./vendor/sweetalert";
|
||||
import installAxios from "~/vendor/axios";
|
||||
|
||||
export default function (component, options) {
|
||||
return function (el, props) {
|
||||
|
@ -24,61 +23,16 @@ export default function (component, options) {
|
|||
vueApp.use(gettext);
|
||||
|
||||
/* Axios */
|
||||
|
||||
// Configure auto-CSRF on requests
|
||||
if (typeof App.api_csrf !== 'undefined') {
|
||||
axios.defaults.headers.common['X-API-CSRF'] = App.api_csrf;
|
||||
}
|
||||
|
||||
vueApp.use(VueAxios, axios);
|
||||
|
||||
vueApp.mixin({
|
||||
mounted() {
|
||||
const handleAxiosError = (error) => {
|
||||
const {$gettext} = gettext;
|
||||
|
||||
let notifyMessage = $gettext('An error occurred and your request could not be completed.');
|
||||
if (error.response) {
|
||||
// Request made and server responded
|
||||
notifyMessage = error.response.data.message;
|
||||
console.error(notifyMessage);
|
||||
} else if (error.request) {
|
||||
// The request was made but no response was received
|
||||
console.error(error.request);
|
||||
} else {
|
||||
// Something happened in setting up the request that triggered an Error
|
||||
console.error('Error', error.message);
|
||||
}
|
||||
|
||||
if (typeof this.$notifyError === 'function') {
|
||||
this.$notifyError(notifyMessage);
|
||||
}
|
||||
};
|
||||
|
||||
axios.interceptors.request.use((config) => {
|
||||
return config;
|
||||
}, (error) => {
|
||||
handleAxiosError(error);
|
||||
return Promise.reject(error);
|
||||
});
|
||||
|
||||
axios.interceptors.response.use((response) => {
|
||||
return response;
|
||||
}, (error) => {
|
||||
handleAxiosError(error);
|
||||
return Promise.reject(error);
|
||||
});
|
||||
}
|
||||
});
|
||||
installAxios(vueApp);
|
||||
|
||||
/* Pinia */
|
||||
usePinia(vueApp);
|
||||
installPinia(vueApp);
|
||||
|
||||
/* Bootstrap Vue */
|
||||
useBootstrapVue(vueApp);
|
||||
installBootstrapVue(vueApp);
|
||||
|
||||
/* SweetAlert */
|
||||
useSweetAlert(vueApp);
|
||||
installSweetAlert(vueApp);
|
||||
|
||||
vueApp.mount(el);
|
||||
};
|
||||
|
|
|
@ -2,38 +2,50 @@
|
|||
<b-input v-bind="$attrs" type="time" v-model="timeCode" pattern="[0-9]{2}:[0-9]{2}" placeholder="13:45"></b-input>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
|
||||
import {computed} from "vue";
|
||||
import _ from 'lodash';
|
||||
|
||||
export default {
|
||||
props: ['value'],
|
||||
computed: {
|
||||
timeCode: {
|
||||
get () {
|
||||
return this.parseTimeCode(this.value);
|
||||
},
|
||||
set (newValue) {
|
||||
this.$emit('input', this.convertToTimeCode(newValue));
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
parseTimeCode (timeCode) {
|
||||
if (timeCode !== '' && timeCode !== null) {
|
||||
timeCode = _.padStart(timeCode, 4, '0');
|
||||
return timeCode.substr(0, 2) + ':' + timeCode.substr(2);
|
||||
}
|
||||
const props = defineProps({
|
||||
modelValue: String
|
||||
});
|
||||
|
||||
return null;
|
||||
},
|
||||
convertToTimeCode (time) {
|
||||
if (_.isEmpty(time)) {
|
||||
return null;
|
||||
}
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
|
||||
let timeParts = time.split(':');
|
||||
return (100 * parseInt(timeParts[0], 10)) + parseInt(timeParts[1], 10);
|
||||
}
|
||||
const parseTimeCode = (timeCode) => {
|
||||
if (timeCode !== '' && timeCode !== null) {
|
||||
timeCode = _.padStart(timeCode, 4, '0');
|
||||
return timeCode.substring(0, 2) + ':' + timeCode.substring(2);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const convertToTimeCode = (time) => {
|
||||
if (_.isEmpty(time)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let timeParts = time.split(':');
|
||||
return (100 * parseInt(timeParts[0], 10)) + parseInt(timeParts[1], 10);
|
||||
}
|
||||
|
||||
const timeCode = computed({
|
||||
get: () => {
|
||||
return parseTimeCode(props.modelValue);
|
||||
},
|
||||
set: (newValue) => {
|
||||
emit('update:modelValue', convertToTimeCode(newValue));
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
model: {
|
||||
prop: 'modelValue',
|
||||
event: 'update:modelValue'
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -13,36 +13,34 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import Icon from "~/components/Common/Icon";
|
||||
import {onMounted, ref} from "vue";
|
||||
import {get, set, useVModel} from "@vueuse/core";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: String
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
|
||||
const initial = ref(75);
|
||||
onMounted(() => {
|
||||
set(initial, props.modelValue);
|
||||
});
|
||||
|
||||
const volume = useVModel(props, 'modelValue', emit);
|
||||
|
||||
const reset = () => {
|
||||
set(volume, get(initial));
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
components: {Icon},
|
||||
name: "VolumeSlider",
|
||||
emits: "input",
|
||||
props: ['value'],
|
||||
data() {
|
||||
return {
|
||||
initial: 75
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initial = this.value;
|
||||
},
|
||||
computed: {
|
||||
volume: {
|
||||
get() {
|
||||
return this.value
|
||||
},
|
||||
set(newValue) {
|
||||
this.$emit('input', newValue);
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
reset() {
|
||||
this.volume = this.initial;
|
||||
}
|
||||
model: {
|
||||
prop: 'modelValue',
|
||||
event: 'update:modelValue'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<b-tab :title="langTitle">
|
||||
<b-tab :title="$gettext('Intro')">
|
||||
<b-form-group>
|
||||
<b-form-row>
|
||||
<b-form-group class="col-md-6" label-for="intro_file">
|
||||
|
@ -7,10 +7,12 @@
|
|||
{{ $gettext('Select Intro File') }}
|
||||
</template>
|
||||
<template #description>
|
||||
{{ $gettext('This introduction file should exactly match the bitrate and format of the mount point itself.') }}
|
||||
{{
|
||||
$gettext('This introduction file should exactly match the bitrate and format of the mount point itself.')
|
||||
}}
|
||||
</template>
|
||||
|
||||
<flow-upload :target-url="targetUrl" :valid-mime-types="acceptMimeTypes"
|
||||
<flow-upload :target-url="targetUrl" :valid-mime-types="['audio/*']"
|
||||
@success="onFileSuccess"></flow-upload>
|
||||
</b-form-group>
|
||||
|
||||
|
@ -38,56 +40,57 @@
|
|||
</b-tab>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import FlowUpload from '~/components/Common/FlowUpload';
|
||||
|
||||
export default {
|
||||
name: 'MountFormIntro',
|
||||
components: {FlowUpload},
|
||||
props: {
|
||||
value: Object,
|
||||
recordHasIntro: Boolean,
|
||||
editIntroUrl: String,
|
||||
newIntroUrl: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
hasIntro: this.recordHasIntro,
|
||||
acceptMimeTypes: ['audio/*']
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
recordHasIntro(newValue) {
|
||||
this.hasIntro = newValue;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
langTitle() {
|
||||
return this.$gettext('Intro');
|
||||
},
|
||||
targetUrl() {
|
||||
return (this.editIntroUrl)
|
||||
? this.editIntroUrl
|
||||
: this.newIntroUrl;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onFileSuccess(file, message) {
|
||||
this.hasIntro = true;
|
||||
if (!this.editIntroUrl) {
|
||||
this.$emit('input', message);
|
||||
}
|
||||
},
|
||||
deleteIntro() {
|
||||
if (this.editIntroUrl) {
|
||||
this.axios.delete(this.editIntroUrl).then(() => {
|
||||
this.hasIntro = false;
|
||||
});
|
||||
} else {
|
||||
this.hasIntro = false;
|
||||
this.$emit('input', null);
|
||||
}
|
||||
}
|
||||
import {computed, toRef} from "vue";
|
||||
import {useAxios} from "~/vendor/axios";
|
||||
import {set} from "@vueuse/core";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: Object,
|
||||
recordHasIntro: Boolean,
|
||||
editIntroUrl: String,
|
||||
newIntroUrl: String
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
|
||||
const hasIntro = toRef(props, 'recordHasIntro');
|
||||
|
||||
const targetUrl = computed(() => {
|
||||
return (props.editIntroUrl)
|
||||
? props.editIntroUrl
|
||||
: props.newIntroUrl;
|
||||
});
|
||||
|
||||
const onFileSuccess = (file, message) => {
|
||||
set(hasIntro, true);
|
||||
|
||||
if (!props.editIntroUrl) {
|
||||
emit('update:modelValue', message);
|
||||
}
|
||||
};
|
||||
|
||||
const {axios} = useAxios();
|
||||
|
||||
const deleteIntro = () => {
|
||||
if (props.editIntroUrl) {
|
||||
axios.delete(props.editIntroUrl).then(() => {
|
||||
set(hasIntro, false);
|
||||
});
|
||||
} else {
|
||||
set(hasIntro, false);
|
||||
emit('update:modelValue', null);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
model: {
|
||||
prop: 'modelValue',
|
||||
event: 'update:modelValue'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<b-tab :title="langTitle">
|
||||
<b-tab :title="$gettext('Artwork')">
|
||||
<b-form-group>
|
||||
<b-row>
|
||||
<b-col md="8">
|
||||
|
@ -8,7 +8,9 @@
|
|||
{{ $gettext('Select PNG/JPG artwork file') }}
|
||||
</template>
|
||||
<template #description>
|
||||
{{ $gettext('Artwork must be a minimum size of 1400 x 1400 pixels and a maximum size of 3000 x 3000 pixels for Apple Podcasts.') }}
|
||||
{{
|
||||
$gettext('Artwork must be a minimum size of 1400 x 1400 pixels and a maximum size of 3000 x 3000 pixels for Apple Podcasts.')
|
||||
}}
|
||||
</template>
|
||||
<b-form-file id="edit_form_art" accept="image/jpeg, image/png"
|
||||
@input="uploadNewArt"></b-form-file>
|
||||
|
@ -28,58 +30,66 @@
|
|||
</b-tab>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
import {computed, ref, toRef} from "vue";
|
||||
import {get, set} from "@vueuse/core";
|
||||
import {useAxios} from "~/vendor/axios";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: Object,
|
||||
artworkSrc: String,
|
||||
editArtUrl: String,
|
||||
newArtUrl: String,
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
|
||||
const artworkSrc = toRef(props, 'artworkSrc');
|
||||
const localSrc = ref(null);
|
||||
|
||||
const src = computed(() => {
|
||||
return get(localSrc) ?? get(artworkSrc);
|
||||
});
|
||||
|
||||
const {axios} = useAxios();
|
||||
|
||||
const uploadNewArt = (file) => {
|
||||
if (!(file instanceof File)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let fileReader = new FileReader();
|
||||
fileReader.addEventListener('load', () => {
|
||||
set(localSrc, fileReader.result);
|
||||
}, false);
|
||||
fileReader.readAsDataURL(file);
|
||||
|
||||
let url = (props.editArtUrl) ? props.editArtUrl : props.newArtUrl;
|
||||
let formData = new FormData();
|
||||
formData.append('art', file);
|
||||
|
||||
axios.post(url, formData).then((resp) => {
|
||||
emit('update:modelValue', resp.data);
|
||||
});
|
||||
};
|
||||
|
||||
const deleteArt = () => {
|
||||
if (props.editArtUrl) {
|
||||
axios.delete(props.editArtUrl).then(() => {
|
||||
set(localSrc, null);
|
||||
});
|
||||
} else {
|
||||
set(localSrc, null);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'PodcastCommonArtwork',
|
||||
props: {
|
||||
value: Object,
|
||||
artworkSrc: String,
|
||||
editArtUrl: String,
|
||||
newArtUrl: String,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
localSrc: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
langTitle() {
|
||||
return this.$gettext('Artwork');
|
||||
},
|
||||
src() {
|
||||
return this.localSrc ?? this.artworkSrc;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
uploadNewArt(file) {
|
||||
if (!(file instanceof File)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let fileReader = new FileReader();
|
||||
fileReader.addEventListener('load', () => {
|
||||
this.localSrc = fileReader.result;
|
||||
}, false);
|
||||
fileReader.readAsDataURL(file);
|
||||
|
||||
let url = (this.editArtUrl) ? this.editArtUrl : this.newArtUrl;
|
||||
let formData = new FormData();
|
||||
formData.append('art', file);
|
||||
|
||||
this.axios.post(url, formData).then((resp) => {
|
||||
this.$emit('input', resp.data);
|
||||
});
|
||||
},
|
||||
deleteArt () {
|
||||
if (this.editArtUrl) {
|
||||
this.axios.delete(this.editArtUrl).then(() => {
|
||||
this.localSrc = '';
|
||||
});
|
||||
} else {
|
||||
this.localSrc = '';
|
||||
}
|
||||
}
|
||||
model: {
|
||||
prop: 'modelValue',
|
||||
event: 'update:modelValue'
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<b-tab :title="langTitle">
|
||||
<b-tab :title="$gettext('Media')">
|
||||
<b-form-group>
|
||||
<b-form-row>
|
||||
<b-form-group class="col-md-6" label-for="media_file">
|
||||
|
@ -12,7 +12,7 @@
|
|||
}}
|
||||
</template>
|
||||
|
||||
<flow-upload :target-url="targetUrl" :valid-mime-types="acceptMimeTypes"
|
||||
<flow-upload :target-url="targetUrl" :valid-mime-types="['audio/x-m4a', 'audio/mpeg']"
|
||||
@success="onFileSuccess"></flow-upload>
|
||||
</b-form-group>
|
||||
|
||||
|
@ -40,59 +40,56 @@
|
|||
</b-tab>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import FlowUpload from '~/components/Common/FlowUpload';
|
||||
import {computed, toRef} from "vue";
|
||||
import {set} from "@vueuse/core";
|
||||
import {useAxios} from "~/vendor/axios";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: Object,
|
||||
recordHasMedia: Boolean,
|
||||
downloadUrl: String,
|
||||
editMediaUrl: String,
|
||||
newMediaUrl: String
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
|
||||
const hasMedia = toRef(props, 'recordHasMedia');
|
||||
|
||||
const targetUrl = computed(() => {
|
||||
return (props.editMediaUrl)
|
||||
? props.editMediaUrl
|
||||
: props.newMediaUrl;
|
||||
});
|
||||
|
||||
const onFileSuccess = (file, message) => {
|
||||
set(hasMedia, true);
|
||||
if (!props.editMediaUrl) {
|
||||
emit('update:modelValue', message);
|
||||
}
|
||||
};
|
||||
|
||||
const {axios} = useAxios();
|
||||
|
||||
const deleteMedia = () => {
|
||||
if (props.editMediaUrl) {
|
||||
axios.delete(props.editMediaUrl).then(() => {
|
||||
set(hasMedia, false);
|
||||
});
|
||||
} else {
|
||||
set(hasMedia, false);
|
||||
emit('update:modelValue', null);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'EpisodeFormMedia',
|
||||
components: {FlowUpload},
|
||||
props: {
|
||||
value: Object,
|
||||
recordHasMedia: Boolean,
|
||||
downloadUrl: String,
|
||||
editMediaUrl: String,
|
||||
newMediaUrl: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
hasMedia: this.recordHasMedia,
|
||||
acceptMimeTypes: ['audio/x-m4a', 'audio/mpeg']
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
watch: {
|
||||
recordHasMedia(newValue) {
|
||||
this.hasMedia = newValue;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
langTitle() {
|
||||
return this.$gettext('Media');
|
||||
},
|
||||
targetUrl() {
|
||||
return (this.editMediaUrl)
|
||||
? this.editMediaUrl
|
||||
: this.newMediaUrl;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onFileSuccess (file, message) {
|
||||
this.hasMedia = true;
|
||||
if (!this.editMediaUrl) {
|
||||
this.$emit('input', message);
|
||||
}
|
||||
},
|
||||
deleteMedia () {
|
||||
if (this.editMediaUrl) {
|
||||
this.axios.delete(this.editMediaUrl).then(() => {
|
||||
this.hasMedia = false;
|
||||
});
|
||||
} else {
|
||||
this.hasMedia = false;
|
||||
this.$emit('input', null);
|
||||
}
|
||||
}
|
||||
model: {
|
||||
prop: 'modelValue',
|
||||
event: 'update:modelValue'
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<b-tab :title="langTitle">
|
||||
<b-tab :title="$gettext('Artwork')">
|
||||
<b-form-group>
|
||||
<b-row>
|
||||
<b-col md="8">
|
||||
|
@ -8,14 +8,16 @@
|
|||
{{ $gettext('Select PNG/JPG artwork file') }}
|
||||
</template>
|
||||
<template #description>
|
||||
{{ $gettext('This image will be used as the default album art when this streamer is live.') }}
|
||||
{{
|
||||
$gettext('This image will be used as the default album art when this streamer is live.')
|
||||
}}
|
||||
</template>
|
||||
<b-form-file id="edit_form_art" accept="image/jpeg, image/png"
|
||||
@input="uploadNewArt"></b-form-file>
|
||||
</b-form-group>
|
||||
</b-col>
|
||||
<b-col md="4" v-if="src && src !== ''">
|
||||
<b-img :src="src" :alt="langTitle" rounded fluid></b-img>
|
||||
<b-img :src="src" :alt="$gettext('Artwork')" rounded fluid></b-img>
|
||||
|
||||
<div class="buttons pt-3">
|
||||
<b-button block variant="danger" @click="deleteArt">
|
||||
|
@ -28,58 +30,65 @@
|
|||
</b-tab>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {computed, ref, toRef} from "vue";
|
||||
import {get, set} from "@vueuse/core";
|
||||
import {useAxios} from "~/vendor/axios";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: Object,
|
||||
artworkSrc: String,
|
||||
editArtUrl: String,
|
||||
newArtUrl: String,
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
|
||||
const artworkSrc = toRef(props, 'artworkSrc');
|
||||
const localSrc = ref(null);
|
||||
|
||||
const src = computed(() => {
|
||||
return get(localSrc) ?? get(artworkSrc);
|
||||
});
|
||||
|
||||
const {axios} = useAxios();
|
||||
|
||||
const uploadNewArt = (file) => {
|
||||
if (!(file instanceof File)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let fileReader = new FileReader();
|
||||
fileReader.addEventListener('load', () => {
|
||||
set(localSrc, fileReader.result);
|
||||
}, false);
|
||||
fileReader.readAsDataURL(file);
|
||||
|
||||
let url = (props.editArtUrl) ? props.editArtUrl : props.newArtUrl;
|
||||
let formData = new FormData();
|
||||
formData.append('art', file);
|
||||
|
||||
axios.post(url, formData).then((resp) => {
|
||||
emit('update:modelValue', resp.data);
|
||||
});
|
||||
};
|
||||
|
||||
const deleteArt = () => {
|
||||
if (props.editArtUrl) {
|
||||
axios.delete(props.editArtUrl).then(() => {
|
||||
set(localSrc, null);
|
||||
});
|
||||
} else {
|
||||
set(localSrc, null);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'StreamersFormArtwork',
|
||||
props: {
|
||||
value: Object,
|
||||
artworkSrc: String,
|
||||
editArtUrl: String,
|
||||
newArtUrl: String,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
localSrc: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
langTitle() {
|
||||
return this.$gettext('Artwork');
|
||||
},
|
||||
src() {
|
||||
return this.localSrc ?? this.artworkSrc;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
uploadNewArt(file) {
|
||||
if (!(file instanceof File)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let fileReader = new FileReader();
|
||||
fileReader.addEventListener('load', () => {
|
||||
this.localSrc = fileReader.result;
|
||||
}, false);
|
||||
fileReader.readAsDataURL(file);
|
||||
|
||||
let url = (this.editArtUrl) ? this.editArtUrl : this.newArtUrl;
|
||||
let formData = new FormData();
|
||||
formData.append('art', file);
|
||||
|
||||
this.axios.post(url, formData).then((resp) => {
|
||||
this.$emit('input', resp.data);
|
||||
});
|
||||
},
|
||||
deleteArt() {
|
||||
if (this.editArtUrl) {
|
||||
this.axios.delete(this.editArtUrl).then(() => {
|
||||
this.localSrc = '';
|
||||
});
|
||||
} else {
|
||||
this.localSrc = '';
|
||||
}
|
||||
}
|
||||
model: {
|
||||
prop: 'modelValue',
|
||||
event: 'update:modelValue'
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import {createApp} from 'vue';
|
||||
import InlinePlayer from '~/components/InlinePlayer.vue';
|
||||
import usePinia from '../vendor/pinia';
|
||||
import installPinia from '../vendor/pinia';
|
||||
import gettext from "../vendor/gettext";
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
|
@ -14,7 +14,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||
inlineApp.use(gettext);
|
||||
|
||||
/* Pinia */
|
||||
usePinia(inlineApp);
|
||||
installPinia(inlineApp);
|
||||
|
||||
inlineApp.mount('#radio-player-controls');
|
||||
});
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
import axios from "axios";
|
||||
import VueAxios from "vue-axios";
|
||||
import gettext from "~/vendor/gettext";
|
||||
import {inject} from "vue";
|
||||
|
||||
/* Composition API Axios utilities */
|
||||
export function useAxios() {
|
||||
return {
|
||||
axios: inject('axios')
|
||||
};
|
||||
}
|
||||
|
||||
export default function installAxios(vueApp) {
|
||||
// Configure auto-CSRF on requests
|
||||
if (typeof App.api_csrf !== 'undefined') {
|
||||
axios.defaults.headers.common['X-API-CSRF'] = App.api_csrf;
|
||||
}
|
||||
|
||||
vueApp.use(VueAxios, axios);
|
||||
|
||||
vueApp.provide('axios', axios);
|
||||
|
||||
vueApp.mixin({
|
||||
mounted() {
|
||||
const handleAxiosError = (error) => {
|
||||
const {$gettext} = gettext;
|
||||
|
||||
let notifyMessage = $gettext('An error occurred and your request could not be completed.');
|
||||
if (error.response) {
|
||||
// Request made and server responded
|
||||
notifyMessage = error.response.data.message;
|
||||
console.error(notifyMessage);
|
||||
} else if (error.request) {
|
||||
// The request was made but no response was received
|
||||
console.error(error.request);
|
||||
} else {
|
||||
// Something happened in setting up the request that triggered an Error
|
||||
console.error('Error', error.message);
|
||||
}
|
||||
|
||||
if (typeof this.$notifyError === 'function') {
|
||||
this.$notifyError(notifyMessage);
|
||||
}
|
||||
};
|
||||
|
||||
axios.interceptors.request.use((config) => {
|
||||
return config;
|
||||
}, (error) => {
|
||||
handleAxiosError(error);
|
||||
return Promise.reject(error);
|
||||
});
|
||||
|
||||
axios.interceptors.response.use((response) => {
|
||||
return response;
|
||||
}, (error) => {
|
||||
handleAxiosError(error);
|
||||
return Promise.reject(error);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
|
@ -1,10 +1,130 @@
|
|||
import {BootstrapVue} from 'bootstrap-vue';
|
||||
|
||||
import 'bootstrap-vue/dist/bootstrap-vue.css';
|
||||
import {inject} from "vue";
|
||||
import gettext from "~/vendor/gettext";
|
||||
|
||||
export default function useBootstrapVue(vueApp) {
|
||||
/* Composition API BootstrapVue utilities */
|
||||
export function useNotify() {
|
||||
const $bvToast = inject('bvToast');
|
||||
const {$gettext} = gettext;
|
||||
|
||||
const notify = function (message = null, options = {}) {
|
||||
if (!!document.hidden) {
|
||||
return;
|
||||
}
|
||||
|
||||
const defaults = {
|
||||
variant: 'default',
|
||||
toaster: 'b-toaster-top-right',
|
||||
autoHideDelay: 3000,
|
||||
solid: true
|
||||
};
|
||||
|
||||
$bvToast.toast(message, {...defaults, ...options});
|
||||
};
|
||||
|
||||
const notifyError = (message = null, options = {}) => {
|
||||
if (message === null) {
|
||||
message = $gettext('An error occurred and your request could not be completed.');
|
||||
}
|
||||
|
||||
const defaults = {
|
||||
variant: 'danger',
|
||||
title: $gettext('Error')
|
||||
};
|
||||
|
||||
notify(message, {...defaults, ...options});
|
||||
|
||||
return message;
|
||||
};
|
||||
|
||||
const notifySuccess = (message = null, options = {}) => {
|
||||
if (message === null) {
|
||||
message = $gettext('Changes saved.');
|
||||
}
|
||||
|
||||
const defaults = {
|
||||
variant: 'success',
|
||||
title: $gettext('Success')
|
||||
};
|
||||
|
||||
notify(message, {...defaults, ...options});
|
||||
|
||||
return message;
|
||||
};
|
||||
|
||||
const LOADING_TOAST_ID = 'toast-loading';
|
||||
|
||||
const showLoading = (message = null, options = {}) => {
|
||||
if (message === null) {
|
||||
message = $gettext('Applying changes...');
|
||||
}
|
||||
|
||||
const defaults = {
|
||||
id: LOADING_TOAST_ID,
|
||||
variant: 'warning',
|
||||
title: $gettext('Please wait...'),
|
||||
autoHideDelay: 10000,
|
||||
isStatus: true
|
||||
};
|
||||
|
||||
notify(message, {...defaults, ...options});
|
||||
return message;
|
||||
};
|
||||
|
||||
const hideLoading = () => {
|
||||
$bvToast.hide(LOADING_TOAST_ID);
|
||||
};
|
||||
|
||||
let $isAxiosLoading = false;
|
||||
let $axiosLoadCount = 0;
|
||||
|
||||
const setLoading = (isLoading) => {
|
||||
let prevIsLoading = $isAxiosLoading;
|
||||
if (isLoading) {
|
||||
$axiosLoadCount++;
|
||||
$isAxiosLoading = true;
|
||||
} else if ($axiosLoadCount > 0) {
|
||||
$axiosLoadCount--;
|
||||
$isAxiosLoading = ($axiosLoadCount > 0);
|
||||
}
|
||||
|
||||
// Handle state changes
|
||||
if (!prevIsLoading && $isAxiosLoading) {
|
||||
showLoading();
|
||||
} else if (prevIsLoading && !$isAxiosLoading) {
|
||||
hideLoading();
|
||||
}
|
||||
};
|
||||
|
||||
const wrapWithLoading = (promise) => {
|
||||
setLoading(true);
|
||||
|
||||
promise.finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
|
||||
return promise;
|
||||
};
|
||||
|
||||
return {
|
||||
notify,
|
||||
notifyError,
|
||||
notifySuccess,
|
||||
showLoading,
|
||||
hideLoading,
|
||||
setLoading,
|
||||
wrapWithLoading
|
||||
};
|
||||
}
|
||||
|
||||
export default function installBootstrapVue(vueApp) {
|
||||
vueApp.use(BootstrapVue);
|
||||
|
||||
vueApp.provide('bvToast', vueApp.config.globalProperties.$bvToast);
|
||||
vueApp.provide('bvModal', vueApp.config.globalProperties.$bvModal);
|
||||
|
||||
vueApp.config.globalProperties.$notify = function (message = null, options = {}) {
|
||||
if (!!document.hidden) {
|
||||
return;
|
||||
|
|
|
@ -2,6 +2,6 @@ import {createPinia} from 'pinia';
|
|||
|
||||
const pinia = createPinia();
|
||||
|
||||
export default function usePinia(vueApp) {
|
||||
export default function installPinia(vueApp) {
|
||||
vueApp.use(pinia);
|
||||
}
|
||||
|
|
|
@ -9,10 +9,6 @@ const swalCustom = Swal.mixin({
|
|||
showCancelButton: true,
|
||||
});
|
||||
|
||||
export function showAlert(options = {}) {
|
||||
return swalCustom.fire(options);
|
||||
}
|
||||
|
||||
const swalConfirmDelete = swalCustom.mixin({
|
||||
title: $gettext('Delete Record?'),
|
||||
confirmButtonText: $gettext('Delete'),
|
||||
|
@ -20,11 +16,26 @@ const swalConfirmDelete = swalCustom.mixin({
|
|||
focusCancel: true
|
||||
});
|
||||
|
||||
export function confirmDelete(options = {}) {
|
||||
return swalConfirmDelete.fire(options);
|
||||
export function useSweetAlert() {
|
||||
const showAlert = (options = {}) => {
|
||||
return swalCustom.fire(options);
|
||||
}
|
||||
|
||||
const confirmDelete = (options = {}) => {
|
||||
return swalConfirmDelete.fire(options);
|
||||
}
|
||||
|
||||
return {
|
||||
showAlert,
|
||||
confirmDelete
|
||||
};
|
||||
}
|
||||
|
||||
export default function useSweetAlert(vueApp) {
|
||||
vueApp.config.globalProperties.$swal = showAlert;
|
||||
vueApp.config.globalProperties.$confirmDelete = confirmDelete;
|
||||
export default function installSweetAlert(vueApp) {
|
||||
vueApp.config.globalProperties.$swal = (options = {}) => {
|
||||
return swalCustom.fire(options);
|
||||
};
|
||||
vueApp.config.globalProperties.$confirmDelete = (options = {}) => {
|
||||
return swalConfirmDelete.fire(options);
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue