#26: mise à jour #40
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
<script src="{% static "aircox/vendor.js" %}"></script>
|
<script src="{% static "aircox/vendor.js" %}"></script>
|
||||||
<script src="{% static "aircox/admin.js" %}"></script>
|
<script src="{% static "aircox/admin.js" %}"></script>
|
||||||
|
<script src="{% static "aircox/public.js" %}"></script>
|
||||||
|
|
||||||
{% block extrastyle %}{% endblock %}
|
{% block extrastyle %}{% endblock %}
|
||||||
|
|
||||||
|
@ -172,5 +173,7 @@
|
||||||
</div>
|
</div>
|
||||||
<!-- END Container -->
|
<!-- END Container -->
|
||||||
|
|
||||||
|
{% block outside_bottom %}{% endblock %}
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -6,8 +6,9 @@
|
||||||
<script src="{% static "aircox/streamer.js" %}"></script>
|
<script src="{% static "aircox/streamer.js" %}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}{{ block.super }}
|
{% block content %}
|
||||||
<div id="app" data-api-url="{% url "admin:api:streamer-list" %}">
|
{{ block.super }}
|
||||||
|
<div id="app" v-if="streamers" data-api-url="{% url "admin:api:streamer-list" %}">
|
||||||
<div class="navbar toolbar">
|
<div class="navbar toolbar">
|
||||||
<div class="navbar-start">
|
<div class="navbar-start">
|
||||||
<span class="navbar-item control">
|
<span class="navbar-item control">
|
||||||
|
@ -38,3 +39,7 @@
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block outside_bottom %}
|
||||||
|
<div id="player">{% include "aircox/widgets/player.html" %}</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,8 @@
|
||||||
import Vue from 'vue';
|
import App from 'public/app';
|
||||||
|
import 'public';
|
||||||
|
|
||||||
import './admin.scss';
|
import './admin.scss';
|
||||||
|
import AStatistics from './statistics.vue';
|
||||||
import Statistics from './statistics.vue';
|
|
||||||
|
|
||||||
Vue.component('a-statistics', Statistics)
|
|
||||||
|
|
||||||
import 'public';
|
|
||||||
|
|
||||||
window.aircox_admin = {
|
window.aircox_admin = {
|
||||||
/**
|
/**
|
||||||
|
@ -23,7 +19,11 @@ window.aircox_admin = {
|
||||||
for(var item of container.querySelectorAll('a.navbar-item'))
|
for(var item of container.querySelectorAll('a.navbar-item'))
|
||||||
item.style.display = null;
|
item.style.display = null;
|
||||||
},
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
window.aircox.builder.config = {
|
||||||
|
...App,
|
||||||
|
components: {...App.components, AStatistics},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,95 +1,25 @@
|
||||||
import Vue from 'vue';
|
import AAutocomplete from './autocomplete'
|
||||||
|
import AEpisode from './episode'
|
||||||
|
import APlayer from './player'
|
||||||
|
import APlaylist from './playlist'
|
||||||
|
import ASoundItem from './soundItem'
|
||||||
|
|
||||||
export const defaultConfig = {
|
const App = {
|
||||||
el: '#app',
|
el: '#app',
|
||||||
delimiters: ['[[', ']]'],
|
delimiters: ['[[', ']]'],
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
player() { return window.aircox.player; },
|
player() { return window.aircox.player; },
|
||||||
},
|
},
|
||||||
|
|
||||||
|
components: {AAutocomplete, AEpisode, APlayer, APlaylist, ASoundItem},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const PlayerApp = {
|
||||||
export default class AppBuilder {
|
el: '#player',
|
||||||
constructor(config={}) {
|
components: {APlayer},
|
||||||
this._config = config;
|
|
||||||
this.title = null;
|
|
||||||
this.app = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get config() {
|
export default App
|
||||||
let config = this._config instanceof Function ? this._config() : this._config;
|
|
||||||
for(var k of new Set([...Object.keys(config || {}), ...Object.keys(defaultConfig)])) {
|
|
||||||
if(!config[k] && defaultConfig[k])
|
|
||||||
config[k] = defaultConfig[k]
|
|
||||||
else if(config[k] instanceof Object)
|
|
||||||
config[k] = {...defaultConfig[k], ...config[k]}
|
|
||||||
}
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
set config(value) {
|
|
||||||
this._config = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy() {
|
|
||||||
self.app && self.app.$destroy();
|
|
||||||
self.app = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
fetch(url, {el='app', ...options}={}) {
|
|
||||||
return fetch(url, options).then(response => response.text())
|
|
||||||
.then(content => {
|
|
||||||
let doc = new DOMParser().parseFromString(content, 'text/html');
|
|
||||||
let app = doc.getElementById('app');
|
|
||||||
content = app ? app.innerHTML : content;
|
|
||||||
return this.load({sync: true, content, title: doc.title, url })
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
load({async=false,content=null,title=null,el='app'}={}) {
|
|
||||||
var self = this;
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
let func = () => {
|
|
||||||
try {
|
|
||||||
let config = self.config;
|
|
||||||
const el = document.querySelector(config.el);
|
|
||||||
if(!el)
|
|
||||||
return reject(`Error: can't get element ${config.el}`)
|
|
||||||
|
|
||||||
if(content)
|
|
||||||
el.innerHTML = content
|
|
||||||
if(title)
|
|
||||||
document.title = title;
|
|
||||||
this.app = new Vue(config);
|
|
||||||
window.scroll(0, 0);
|
|
||||||
resolve(self.app)
|
|
||||||
} catch(error) {
|
|
||||||
self.destroy();
|
|
||||||
reject(error)
|
|
||||||
}};
|
|
||||||
async ? window.addEventListener('load', func) : func();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Save application state into browser history
|
|
||||||
historySave(url,replace=false) {
|
|
||||||
const el = document.querySelector(this.config.el);
|
|
||||||
const state = {
|
|
||||||
content: el.innerHTML,
|
|
||||||
title: document.title,
|
|
||||||
};
|
|
||||||
|
|
||||||
if(replace)
|
|
||||||
history.replaceState(state, '', url)
|
|
||||||
else
|
|
||||||
history.pushState(state, '', url)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Load application from browser history's state
|
|
||||||
historyLoad(state) {
|
|
||||||
return this.load({ content: state.content, title: state.title });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
137
assets/public/appBuilder.js
Normal file
137
assets/public/appBuilder.js
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
import {createApp} from 'vue'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class used to handle Vue applications. It provides way to load
|
||||||
|
* remote application and update history.
|
||||||
|
*/
|
||||||
|
export default class Builder {
|
||||||
|
constructor(config={}) {
|
||||||
|
this.config = config
|
||||||
|
this.title = null
|
||||||
|
this.app = null
|
||||||
|
this.vm = null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch app from remote and mount application.
|
||||||
|
*/
|
||||||
|
fetch(url, {el='app', ...options}={}) {
|
||||||
|
return fetch(url, options).then(response => response.text())
|
||||||
|
.then(content => {
|
||||||
|
let doc = new DOMParser().parseFromString(content, 'text/html')
|
||||||
|
let app = doc.getElementById('app')
|
||||||
|
content = app ? app.innerHTML : content
|
||||||
|
return this.mount({content, title: doc.title, reset:true, url })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mount application, using `create_app` if required.
|
||||||
|
*
|
||||||
|
* @param {String} options.content: replace app container content with it
|
||||||
|
* @param {String} options.title: set DOM document title.
|
||||||
|
* @param {String} [options.el=this.config.el]: mount application on this element (querySelector argument)
|
||||||
|
* @param {Boolean} [reset=False]: if True, force application recreation.
|
||||||
|
* @return `app.mount`'s result.
|
||||||
|
*/
|
||||||
|
mount({content=null, title=null, el=null, reset=false}={}) {
|
||||||
|
try {
|
||||||
|
this.unmount()
|
||||||
|
|
||||||
|
let config = this.config
|
||||||
|
if(el === null)
|
||||||
|
el = config.el
|
||||||
|
|
||||||
|
if(reset || !this.app)
|
||||||
|
this.app = this.createApp({title,content,el,...config})
|
||||||
|
this.vm = this.app.mount(el)
|
||||||
|
window.scroll(0, 0)
|
||||||
|
return this.vm
|
||||||
|
} catch(error) {
|
||||||
|
this.unmount()
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createApp({el, title=null, content=null, ...config}) {
|
||||||
|
const container = document.querySelector(el)
|
||||||
|
if(!container)
|
||||||
|
throw `Error: can't get element ${el}`
|
||||||
|
if(content)
|
||||||
|
container.innerHTML = content
|
||||||
|
if(title)
|
||||||
|
document.title = title
|
||||||
|
return createApp(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
unmount() {
|
||||||
|
this.app && this.app.unmount()
|
||||||
|
this.app = null
|
||||||
|
this.vm = null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable hot reload: catch page change in order to fetch them and
|
||||||
|
* load page without actually leaving current one.
|
||||||
|
*/
|
||||||
|
enableHotReload(node=null, historySave=true) {
|
||||||
|
if(historySave)
|
||||||
|
this.historySave(document.location, true)
|
||||||
|
node.addEventListener('click', event => this._onPageChange(event), true)
|
||||||
|
node.addEventListener('submit', event => this._onPageChange(event), true)
|
||||||
|
node.addEventListener('popstate', event => this._onPopState(event), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
_onPageChange(event) {
|
||||||
|
let submit = event.type == 'submit';
|
||||||
|
let target = submit || event.target.tagName == 'A'
|
||||||
|
? event.target : event.target.closest('a');
|
||||||
|
if(!target || target.hasAttribute('target'))
|
||||||
|
return;
|
||||||
|
|
||||||
|
let url = submit ? target.getAttribute('action') || ''
|
||||||
|
: target.getAttribute('href');
|
||||||
|
if(url===null || !(url === '' || url.startsWith('/') || url.startsWith('?')))
|
||||||
|
return;
|
||||||
|
|
||||||
|
let options = {};
|
||||||
|
if(submit) {
|
||||||
|
let formData = new FormData(event.target);
|
||||||
|
if(target.method == 'get')
|
||||||
|
url += '?' + (new URLSearchParams(formData)).toString();
|
||||||
|
else
|
||||||
|
options = {...options, method: target.method, body: formData}
|
||||||
|
}
|
||||||
|
this.fetch(url, options).then(_ => this.historySave(url))
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
_onPopState(event) {
|
||||||
|
if(event.state && event.state.content)
|
||||||
|
// document.title = this.title;
|
||||||
|
this.historyLoad(event.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Save application state into browser history
|
||||||
|
historySave(url,replace=false) {
|
||||||
|
const el = document.querySelector(this.config.el)
|
||||||
|
const state = {
|
||||||
|
content: el.innerHTML,
|
||||||
|
title: document.title,
|
||||||
|
}
|
||||||
|
|
||||||
|
if(replace)
|
||||||
|
history.replaceState(state, '', url)
|
||||||
|
else
|
||||||
|
history.pushState(state, '', url)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load application from browser history's state
|
||||||
|
historyLoad(state) {
|
||||||
|
return this.mount({ content: state.content, title: state.title })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,8 +12,7 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import debounce from 'lodash/debounce'
|
import debounce from 'lodash/debounce'
|
||||||
import {Autocomplete} from 'buefy/dist/components/autocomplete';
|
import {Autocomplete} from 'buefy/dist/components/autocomplete'
|
||||||
import Vue from 'vue';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
|
@ -31,25 +30,25 @@ export default {
|
||||||
data: [],
|
data: [],
|
||||||
selected: null,
|
selected: null,
|
||||||
isFetching: false,
|
isFetching: false,
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
onSelect(option) {
|
onSelect(option) {
|
||||||
console.log('selected', option)
|
console.log('selected', option)
|
||||||
Vue.set(this, 'selected', option);
|
this.selected = option
|
||||||
this.$emit('select', option);
|
this.$emit('select', option)
|
||||||
},
|
},
|
||||||
|
|
||||||
fetch: debounce(function(query) {
|
fetch: debounce(function(query) {
|
||||||
if(!query)
|
if(!query)
|
||||||
return;
|
return
|
||||||
|
|
||||||
this.isFetching = true;
|
this.isFetching = true
|
||||||
this.model.fetchAll(this.url.replace('${query}', query))
|
this.model.fetchAll(this.url.replace('${query}', query))
|
||||||
.then(data => {
|
.then(data => {
|
||||||
this.data = data;
|
this.data = data
|
||||||
this.isFetching = false;
|
this.isFetching = false
|
||||||
}, data => { this.isFetching = false; Promise.reject(data) })
|
}, data => { this.isFetching = false; Promise.reject(data) })
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,93 +3,37 @@
|
||||||
* administration interface)
|
* administration interface)
|
||||||
*/
|
*/
|
||||||
//-- vendor
|
//-- vendor
|
||||||
import Vue from 'vue';
|
import '@fortawesome/fontawesome-free/css/all.min.css'
|
||||||
|
import '@fortawesome/fontawesome-free/css/fontawesome.min.css'
|
||||||
import '@fortawesome/fontawesome-free/css/all.min.css';
|
|
||||||
import '@fortawesome/fontawesome-free/css/fontawesome.min.css';
|
|
||||||
|
|
||||||
|
|
||||||
//-- aircox
|
//-- aircox
|
||||||
import AppBuilder from './app';
|
import App, {PlayerApp} from './app'
|
||||||
import Sound from './sound';
|
import Builder from './appBuilder'
|
||||||
import {Set} from './model';
|
import Sound from './sound'
|
||||||
|
import {Set} from './model'
|
||||||
import './styles.scss';
|
|
||||||
|
|
||||||
import Autocomplete from './autocomplete';
|
|
||||||
import Episode from './episode';
|
|
||||||
import Player from './player';
|
|
||||||
import Playlist from './playlist';
|
|
||||||
import SoundItem from './soundItem';
|
|
||||||
|
|
||||||
Vue.component('a-autocomplete', Autocomplete)
|
|
||||||
Vue.component('a-episode', Episode)
|
|
||||||
Vue.component('a-player', Player)
|
|
||||||
Vue.component('a-playlist', Playlist)
|
|
||||||
Vue.component('a-sound-item', SoundItem)
|
|
||||||
|
|
||||||
|
import './styles.scss'
|
||||||
|
|
||||||
window.aircox = {
|
window.aircox = {
|
||||||
// main application
|
// main application
|
||||||
appBuilder: null,
|
builder: new Builder(App),
|
||||||
appConfig: {},
|
get app() { return this.builder.app },
|
||||||
get app() { return this.appBuilder.app },
|
|
||||||
|
|
||||||
// player application
|
// player application
|
||||||
playerBuilder: null,
|
playerBuilder: new Builder(PlayerApp),
|
||||||
get playerApp() { return this.playerBuilder && this.playerBuilder.app },
|
get playerApp() { return this.playerBuilder && this.playerBuilder.app },
|
||||||
get player() { return this.playerApp && this.playerApp.$refs.player },
|
get player() { return this.playerBuilder.vm && this.playerBuilder.vm.$refs.player },
|
||||||
|
|
||||||
// Handle hot-reload (link click and form submits).
|
|
||||||
onPageFetch(event) {
|
|
||||||
let submit = event.type == 'submit';
|
|
||||||
let target = submit || event.target.tagName == 'A'
|
|
||||||
? event.target : event.target.closest('a');
|
|
||||||
if(!target || target.hasAttribute('target'))
|
|
||||||
return;
|
|
||||||
|
|
||||||
let url = submit ? target.getAttribute('action') || ''
|
|
||||||
: target.getAttribute('href');
|
|
||||||
if(url===null || !(url === '' || url.startsWith('/') || url.startsWith('?')))
|
|
||||||
return;
|
|
||||||
|
|
||||||
let options = {};
|
|
||||||
if(submit) {
|
|
||||||
let formData = new FormData(event.target);
|
|
||||||
if(target.method == 'get')
|
|
||||||
url += '?' + (new URLSearchParams(formData)).toString();
|
|
||||||
else {
|
|
||||||
options['method'] = target.method;
|
|
||||||
options['body'] = formData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.appBuilder.fetch(url, options).then(app => {
|
|
||||||
this.appBuilder.historySave(url);
|
|
||||||
});
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
},
|
|
||||||
|
|
||||||
Set: Set, Sound: Sound,
|
Set: Set, Sound: Sound,
|
||||||
};
|
|
||||||
window.Vue = Vue;
|
|
||||||
|
|
||||||
|
|
||||||
aircox.playerBuilder = new AppBuilder({el: '#player'});
|
|
||||||
aircox.playerBuilder.load({async:true});
|
|
||||||
aircox.appBuilder = new AppBuilder(x => window.aircox.appConfig);
|
|
||||||
aircox.appBuilder.load({async:true}).then(app => {
|
|
||||||
aircox.appBuilder.historySave(document.location, true);
|
|
||||||
|
|
||||||
//-- load page hooks
|
|
||||||
window.addEventListener('click', event => aircox.onPageFetch(event), true);
|
|
||||||
window.addEventListener('submit', event => aircox.onPageFetch(event), true);
|
|
||||||
window.addEventListener('popstate', event => {
|
|
||||||
if(event.state && event.state.content) {
|
|
||||||
document.title = aircox.appBuilder.title;
|
|
||||||
aircox.appBuilder.historyLoad(event.state);
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
window.addEventListener('load', e => {
|
||||||
|
const [app, player] = [aircox.builder, aircox.playerBuilder]
|
||||||
|
app.title = document.title
|
||||||
|
app.mount()
|
||||||
|
app.enableHotReload(window)
|
||||||
|
|
||||||
|
player.mount()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import Vue from 'vue';
|
|
||||||
|
|
||||||
function getCookie(name) {
|
function getCookie(name) {
|
||||||
if(document.cookie && document.cookie !== '') {
|
if(document.cookie && document.cookie !== '') {
|
||||||
|
@ -82,7 +81,7 @@ export default class Model {
|
||||||
*/
|
*/
|
||||||
commit(data) {
|
commit(data) {
|
||||||
this.id = this.constructor.getId(data);
|
this.id = this.constructor.getId(data);
|
||||||
Vue.set(this, 'data', data);
|
this.data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -123,12 +122,12 @@ export class Set {
|
||||||
/**
|
/**
|
||||||
* Fetch multiple items from server
|
* Fetch multiple items from server
|
||||||
*/
|
*/
|
||||||
static fetch(url, options=null, args=null) {
|
static fetch(model, url, options=null, args=null) {
|
||||||
options = this.getOptions(options)
|
options = this.getOptions(options)
|
||||||
return fetch(url, options)
|
return fetch(url, options)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => (data instanceof Array ? data : data.results)
|
.then(data => (data instanceof Array ? data : data.results)
|
||||||
.map(d => new this.model(d, {url: url, ...args})))
|
.map(d => new model(d, {url: url, ...args})))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import Vue from 'vue';
|
|
||||||
|
|
||||||
import Model, {Set} from 'public/model';
|
import Model, {Set} from 'public/model';
|
||||||
import {setEcoInterval} from 'public/utils';
|
import {setEcoInterval} from 'public/utils';
|
||||||
|
|
||||||
|
@ -61,7 +59,7 @@ export class Source extends Model {
|
||||||
if(!this.data.remaining || !this.isPlaying)
|
if(!this.data.remaining || !this.isPlaying)
|
||||||
return;
|
return;
|
||||||
const delta = (Date.now() - this.commitDate) / 1000;
|
const delta = (Date.now() - this.commitDate) / 1000;
|
||||||
Vue.set(this, 'remaining', this.data.remaining - delta)
|
this.remaining = this.data.remaining - delta
|
||||||
}
|
}
|
||||||
|
|
||||||
commit(data) {
|
commit(data) {
|
||||||
|
@ -70,7 +68,7 @@ export class Source extends Model {
|
||||||
|
|
||||||
this.commitDate = Date.now()
|
this.commitDate = Date.now()
|
||||||
super.commit(data)
|
super.commit(data)
|
||||||
Vue.set(this, 'remaining', data.remaining)
|
this.remaining = data.remaining
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import Vue from 'vue';
|
import App from 'public/app';
|
||||||
|
import Model, {Set} from 'public/model';
|
||||||
import {setAppConfig} from 'public/app';
|
|
||||||
import Model from 'public/model';
|
|
||||||
import Sound from 'public/sound';
|
import Sound from 'public/sound';
|
||||||
import {setEcoInterval} from 'public/utils';
|
import {setEcoInterval} from 'public/utils';
|
||||||
|
|
||||||
import {Streamer, Queue} from './controllers';
|
import {Streamer, Queue} from './controllers';
|
||||||
|
|
||||||
window.aircox.appConfig = {
|
window.aircox.builder.config = {
|
||||||
|
...App,
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
// current streamer
|
// current streamer
|
||||||
|
@ -16,12 +16,13 @@ window.aircox.appConfig = {
|
||||||
streamers: [],
|
streamers: [],
|
||||||
// fetch interval id
|
// fetch interval id
|
||||||
fetchInterval: null,
|
fetchInterval: null,
|
||||||
|
|
||||||
Sound: Sound,
|
Sound: Sound,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
...(App.computed || {}),
|
||||||
|
|
||||||
apiUrl() {
|
apiUrl() {
|
||||||
return this.$el && this.$el.dataset.apiUrl;
|
return this.$el && this.$el.dataset.apiUrl;
|
||||||
},
|
},
|
||||||
|
@ -33,11 +34,12 @@ window.aircox.appConfig = {
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
...(App.methods || {}),
|
||||||
|
|
||||||
fetchStreamers() {
|
fetchStreamers() {
|
||||||
Streamer.Set.fetch(this.apiUrl)
|
Set.fetch(Streamer, this.apiUrl).then(streamers => {
|
||||||
.then(streamers => {
|
this.streamers = streamers
|
||||||
Vue.set(this, 'streamers', streamers);
|
this.streamer = streamers ? streamers[0] : null
|
||||||
Vue.set(this, 'streamer', streamers ? streamers[0] : null);
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
34
package.json
34
package.json
|
@ -6,26 +6,24 @@
|
||||||
"author": "bkfox",
|
"author": "bkfox",
|
||||||
"license": "AGPL",
|
"license": "AGPL",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@fortawesome/fontawesome-free": "^5.13.0",
|
"@fortawesome/fontawesome-free": "^6.0.0",
|
||||||
"buefy": "^0.9.3",
|
"buefy": "^0.9.19",
|
||||||
"bulma": "^0.7.5",
|
"bulma": "^0.9.3",
|
||||||
"css-loader": "^2.1.1",
|
"css-loader": "^6.7.1",
|
||||||
"extract-text-webpack-plugin": "^4.0.0-beta.0",
|
"file-loader": "^6.2.0",
|
||||||
"file-loader": "^3.0.1",
|
"lodash": "^4.17.21",
|
||||||
"lodash": "^4.17.15",
|
"mini-css-extract-plugin": "^2.6.0",
|
||||||
"mini-css-extract-plugin": "^0.5.0",
|
"node-sass": "^7.0.1",
|
||||||
"node-sass": "^4.14.1",
|
"sass-loader": "^12.6.0",
|
||||||
"sass-loader": "^7.3.1",
|
"style-loader": "^3.3.1",
|
||||||
"style-loader": "^0.23.1",
|
"vue-loader": "^17.0.0",
|
||||||
"ttf-loader": "^1.0.2",
|
"vue-style-loader": "^4.1.3",
|
||||||
"vue-loader": "^15.9.3",
|
"vue-template-compiler": "^2.6.14",
|
||||||
"vue-style-loader": "^4.1.2",
|
"webpack": "^5.70.0",
|
||||||
"vue-template-compiler": "^2.6.12",
|
"webpack-cli": "^4.9.2"
|
||||||
"webpack": "^4.43.0",
|
|
||||||
"webpack-cli": "^3.3.11"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"vue": "^2.6.12"
|
"vue": "^3.2.31"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "webpack --config webpack.config.js"
|
"build": "webpack --config webpack.config.js"
|
||||||
|
|
|
@ -86,7 +86,7 @@ module.exports = (env, argv) => Object({
|
||||||
|
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
vue: 'vue/dist/vue.esm.browser.js',
|
vue: 'vue/dist/vue.esm-browser.js',
|
||||||
},
|
},
|
||||||
modules: [
|
modules: [
|
||||||
'./assets',
|
'./assets',
|
||||||
|
|
Loading…
Reference in New Issue
Block a user