forked from rc/aircox
upgrade vue and assets
This commit is contained in:
24
assets/README.md
Normal file
24
assets/README.md
Normal file
@ -0,0 +1,24 @@
|
||||
# aircox-assets
|
||||
|
||||
## Project setup
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compiles and hot-reloads for development
|
||||
```
|
||||
npm run serve
|
||||
```
|
||||
|
||||
### Compiles and minifies for production
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Lints and fixes files
|
||||
```
|
||||
npm run lint
|
||||
```
|
||||
|
||||
### Customize configuration
|
||||
See [Configuration Reference](https://cli.vuejs.org/config/).
|
@ -1,29 +0,0 @@
|
||||
|
||||
.navbar .navbar-brand {
|
||||
padding-right: 1em;
|
||||
}
|
||||
|
||||
.navbar .navbar-brand img {
|
||||
margin: 0em 0.4em;
|
||||
margin-top: 0.3em;
|
||||
max-height: 3em;
|
||||
}
|
||||
|
||||
.breadcrumbs {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.results > #result_list {
|
||||
width: 100%;
|
||||
margin: 1em 0em;
|
||||
}
|
||||
|
||||
|
||||
ul.menu-list li {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.submit-row a.deletelink {
|
||||
height: 35px;
|
||||
}
|
||||
|
@ -1,9 +0,0 @@
|
||||
import App from 'public/app';
|
||||
import AStatistics from './statistics.vue';
|
||||
|
||||
export default {
|
||||
...App,
|
||||
components: {...App.components, AStatistics},
|
||||
}
|
||||
|
||||
|
@ -1,25 +0,0 @@
|
||||
import '@fortawesome/fontawesome-free/css/all.min.css'
|
||||
import '@fortawesome/fontawesome-free/css/fontawesome.min.css'
|
||||
|
||||
import AdminApp from './app';
|
||||
import './admin.scss';
|
||||
|
||||
window.aircox_admin = {
|
||||
/**
|
||||
* Filter items in the parent navbar-dropdown for provided key event on text input
|
||||
*/
|
||||
filter_menu: function(event) {
|
||||
var filter = new RegExp(event.target.value, 'gi');
|
||||
var container = event.target.closest('.navbar-dropdown');
|
||||
|
||||
if(event.target.value)
|
||||
for(var item of container.querySelectorAll('a.navbar-item'))
|
||||
item.style.display = item.innerHTML.search(filter) == -1 ? 'none' : null;
|
||||
else
|
||||
for(var item of container.querySelectorAll('a.navbar-item'))
|
||||
item.style.display = null;
|
||||
},
|
||||
}
|
||||
|
||||
window.AdminApp = AdminApp
|
||||
|
5
assets/babel.config.js
Normal file
5
assets/babel.config.js
Normal file
@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
]
|
||||
}
|
19
assets/jsconfig.json
Normal file
19
assets/jsconfig.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"module": "esnext",
|
||||
"baseUrl": "./",
|
||||
"moduleResolution": "node",
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"src/*"
|
||||
]
|
||||
},
|
||||
"lib": [
|
||||
"esnext",
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"scripthost"
|
||||
]
|
||||
}
|
||||
}
|
47
assets/package.json
Normal file
47
assets/package.json
Normal file
@ -0,0 +1,47 @@
|
||||
{
|
||||
"name": "aircox-assets",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"sideEffects": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^6.0.0",
|
||||
"core-js": "^3.8.3",
|
||||
"vue": "^3.2.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.16",
|
||||
"@babel/eslint-parser": "^7.12.16",
|
||||
"@vue/cli-plugin-babel": "~5.0.0",
|
||||
"@vue/cli-plugin-eslint": "~5.0.0",
|
||||
"@vue/cli-service": "~5.0.0",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-plugin-vue": "^8.0.3",
|
||||
"sass-loader": "^12.6.0",
|
||||
"bulma": "^0.9.3"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:vue/vue3-essential",
|
||||
"eslint:recommended"
|
||||
],
|
||||
"parserOptions": {
|
||||
"parser": "@babel/eslint-parser"
|
||||
},
|
||||
"rules": {}
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not dead",
|
||||
"not ie 11"
|
||||
]
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
import AAutocomplete from './autocomplete'
|
||||
import AEpisode from './episode'
|
||||
import APlayer from './player'
|
||||
import APlaylist from './playlist'
|
||||
import ASoundItem from './soundItem'
|
||||
|
||||
const App = {
|
||||
el: '#app',
|
||||
delimiters: ['[[', ']]'],
|
||||
|
||||
computed: {
|
||||
player() { return window.aircox.player; },
|
||||
},
|
||||
|
||||
components: {AAutocomplete, AEpisode, APlaylist, ASoundItem},
|
||||
}
|
||||
|
||||
export const PlayerApp = {
|
||||
el: '#player',
|
||||
components: {APlayer},
|
||||
}
|
||||
|
||||
export default App
|
||||
|
||||
|
BIN
assets/public/logo.png
Normal file
BIN
assets/public/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.1 KiB |
1
assets/public/vue.esm-browser.js
Symbolic link
1
assets/public/vue.esm-browser.js
Symbolic link
@ -0,0 +1 @@
|
||||
../node_modules/vue/dist/vue.esm-browser.js
|
1
assets/public/vue.esm-browser.prod.js
Symbolic link
1
assets/public/vue.esm-browser.prod.js
Symbolic link
@ -0,0 +1 @@
|
||||
../node_modules/vue/dist/vue.esm-browser.prod.js
|
15
assets/src/admin.js
Normal file
15
assets/src/admin.js
Normal file
@ -0,0 +1,15 @@
|
||||
import './assets/admin.scss'
|
||||
import './index.js'
|
||||
|
||||
import App from './app';
|
||||
import {admin as components} from './components'
|
||||
|
||||
const AdminApp = {
|
||||
...App,
|
||||
components: {...App.components, ...components},
|
||||
}
|
||||
export default AdminApp;
|
||||
|
||||
|
||||
window.App = AdminApp
|
||||
|
20
assets/src/app.js
Normal file
20
assets/src/app.js
Normal file
@ -0,0 +1,20 @@
|
||||
import components from './components'
|
||||
|
||||
const App = {
|
||||
el: '#app',
|
||||
delimiters: ['[[', ']]'],
|
||||
components: {...components},
|
||||
|
||||
computed: {
|
||||
player() { return window.aircox.player; },
|
||||
},
|
||||
}
|
||||
|
||||
export const PlayerApp = {
|
||||
el: '#player',
|
||||
components: {...components},
|
||||
}
|
||||
|
||||
export default App
|
||||
|
||||
|
@ -15,11 +15,11 @@ export default class Builder {
|
||||
/**
|
||||
* Fetch app from remote and mount application.
|
||||
*/
|
||||
fetch(url, {el='app', ...options}={}) {
|
||||
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')
|
||||
let app = doc.querySelector(el)
|
||||
content = app ? app.innerHTML : content
|
||||
return this.mount({content, title: doc.title, reset:true, url })
|
||||
})
|
||||
@ -102,7 +102,7 @@ export default class Builder {
|
||||
else
|
||||
options = {...options, method: target.method, body: formData}
|
||||
}
|
||||
this.fetch(url, options).then(_ => this.historySave(url))
|
||||
this.fetch(url, options).then(() => this.historySave(url))
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
31
assets/src/assets/admin.scss
Normal file
31
assets/src/assets/admin.scss
Normal file
@ -0,0 +1,31 @@
|
||||
|
||||
#app.admin {
|
||||
.navbar .navbar-brand {
|
||||
padding-right: 1em;
|
||||
}
|
||||
|
||||
.navbar .navbar-brand img {
|
||||
margin: 0em 0.4em;
|
||||
margin-top: 0.3em;
|
||||
max-height: 3em;
|
||||
}
|
||||
|
||||
.breadcrumbs {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.results > #result_list {
|
||||
width: 100%;
|
||||
margin: 1em 0em;
|
||||
}
|
||||
|
||||
|
||||
ul.menu-list li {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.submit-row a.deletelink {
|
||||
height: 35px;
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,10 @@
|
||||
@import "~bulma/sass/utilities/_all.sass";
|
||||
@import "~bulma/sass/components/dropdown.sass";
|
||||
|
||||
@import './admin.scss';
|
||||
|
||||
$body-background-color: $light;
|
||||
|
||||
@import "~buefy/src/scss/components/_autocomplete.scss";
|
||||
@import "~bulma";
|
||||
|
||||
//-- helpers/modifiers
|
@ -1,9 +1,8 @@
|
||||
<template>
|
||||
<div class="control">
|
||||
<datalist :id="listId">
|
||||
<template v-for="item in items" :key="item.path">
|
||||
<option :value="item[field]"></option>
|
||||
</template>
|
||||
<option v-for="item in items" :key="item.path"
|
||||
:value="item[field]"></option>
|
||||
</datalist>
|
||||
<input type="text" :name="name" :placeholder="placeholder"
|
||||
:list="listId" @keyup="onKeyUp"/>
|
@ -5,12 +5,12 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Set} from './model';
|
||||
import Sound from './sound';
|
||||
import Page from './page';
|
||||
import {Set} from '../model';
|
||||
import Sound from '../sound';
|
||||
import APage from './APage';
|
||||
|
||||
export default {
|
||||
extends: Page,
|
||||
extends: APage,
|
||||
|
||||
data() {
|
||||
return {
|
@ -2,7 +2,7 @@
|
||||
<div>
|
||||
<slot name="header"></slot>
|
||||
<ul :class="listClass">
|
||||
<template v-for="(item,index) in items">
|
||||
<template v-for="(item,index) in items" :key="index">
|
||||
<li :class="itemClass" @click="select(index)">
|
||||
<slot name="item" :selected="index == selectedIndex" :set="set" :index="index" :item="item"></slot>
|
||||
</li>
|
||||
@ -13,6 +13,7 @@
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
emits: ['select', 'unselect'],
|
||||
data() {
|
||||
return {
|
||||
selectedIndex: this.defaultIndex,
|
||||
@ -42,17 +43,7 @@ export default {
|
||||
find(pred) { return this.set.find(pred) },
|
||||
findIndex(pred) { return this.set.findIndex(pred) },
|
||||
|
||||
/**
|
||||
* Add items to list, return index of the first provided item.
|
||||
*/
|
||||
push(item, ...items) {
|
||||
let index = this.set.push(item);
|
||||
for(var item of items)
|
||||
this.set.push(item);
|
||||
return index;
|
||||
},
|
||||
|
||||
remove(index, select=False) {
|
||||
remove(index, select=false) {
|
||||
this.set.remove(index);
|
||||
if(index < this.selectedIndex)
|
||||
this.selectedIndex--;
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="player">
|
||||
<div :class="['player-panels', panel ? 'is-open' : '']">
|
||||
<Playlist ref="pin" class="player-panel menu" v-show="panel == 'pin'"
|
||||
<APlaylist ref="pin" class="player-panel menu" v-show="panel == 'pin'"
|
||||
name="Pinned"
|
||||
:actions="['page']"
|
||||
:editable="true" :player="self" :set="sets.pin" @select="togglePlay('pin', $event.index)"
|
||||
@ -12,8 +12,8 @@
|
||||
Pinned
|
||||
</p>
|
||||
</template>
|
||||
</Playlist>
|
||||
<Playlist ref="queue" class="player-panel menu" v-show="panel == 'queue'"
|
||||
</APlaylist>
|
||||
<APlaylist ref="queue" class="player-panel menu" v-show="panel == 'queue'"
|
||||
:actions="['page']"
|
||||
:editable="true" :player="self" :set="sets.queue" @select="togglePlay('queue', $event.index)"
|
||||
listClass="menu-list" itemClass="menu-item">
|
||||
@ -23,7 +23,7 @@
|
||||
Playlist
|
||||
</p>
|
||||
</template>
|
||||
</Playlist>
|
||||
</APlaylist>
|
||||
</div>
|
||||
|
||||
<div class="player-bar media">
|
||||
@ -39,9 +39,9 @@
|
||||
</div>
|
||||
<div class="media-content">
|
||||
<slot name="content" :loaded='loaded' :live='live'></slot>
|
||||
<Progress v-if="loaded && duration" :value="currentTime" :max="this.duration"
|
||||
<AProgress v-if="loaded && duration" :value="currentTime" :max="this.duration"
|
||||
:format="displayTime"
|
||||
@select="audio.currentTime = $event"></Progress>
|
||||
@select="audio.currentTime = $event"></AProgress>
|
||||
</div>
|
||||
<div class="media-right">
|
||||
<button class="button has-text-weight-bold" v-if="loaded" @click="play()">
|
||||
@ -69,12 +69,11 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Vue, { ref } from 'vue';
|
||||
import Live from './live';
|
||||
import Playlist from './playlist';
|
||||
import Progress from './progress';
|
||||
import Sound from './sound';
|
||||
import {Set} from './model';
|
||||
import Live from '../live';
|
||||
import Sound from '../sound';
|
||||
import {Set} from '../model';
|
||||
import APlaylist from './APlaylist';
|
||||
import AProgress from './AProgress';
|
||||
|
||||
|
||||
export const State = {
|
||||
@ -84,15 +83,17 @@ export const State = {
|
||||
}
|
||||
|
||||
export default {
|
||||
components: { APlaylist, AProgress },
|
||||
|
||||
data() {
|
||||
let audio = new Audio();
|
||||
audio.addEventListener('ended', e => this.onState(e));
|
||||
audio.addEventListener('pause', e => this.onState(e));
|
||||
audio.addEventListener('playing', e => this.onState(e));
|
||||
audio.addEventListener('timeupdate', e => {
|
||||
audio.addEventListener('timeupdate', () => {
|
||||
this.currentTime = this.audio.currentTime;
|
||||
});
|
||||
audio.addEventListener('durationchange', e => {
|
||||
audio.addEventListener('durationchange', () => {
|
||||
this.duration = Number.isFinite(this.audio.duration) ? this.audio.duration : null;
|
||||
});
|
||||
|
||||
@ -273,13 +274,12 @@ export default {
|
||||
if(event.type == 'ended' && (!this.playlist || this.playlist.selectNext() == -1))
|
||||
this.play();
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.load();
|
||||
},
|
||||
|
||||
components: { Playlist, Progress },
|
||||
}
|
||||
</script>
|
||||
|
@ -2,18 +2,19 @@
|
||||
<div>
|
||||
<slot name="header"></slot>
|
||||
<ul :class="listClass">
|
||||
<li v-for="(item,index) in items" :class="itemClass" @click="!hasAction('play') && select(index)">
|
||||
<li v-for="(item,index) in items" :class="itemClass" @click="!hasAction('play') && select(index)"
|
||||
:key="index">
|
||||
<a :class="index == selectedIndex ? 'is-active' : ''">
|
||||
<SoundItem
|
||||
<ASoundItem
|
||||
:data="item" :index="index" :player="player" :set="set"
|
||||
@togglePlay="togglePlay(index)"
|
||||
:actions="actions">
|
||||
<template v-slot:actions="{loaded,set}">
|
||||
<template v-slot:actions="{}">
|
||||
<button class="button" v-if="editable" @click.stop="remove(index,true)">
|
||||
<span class="icon is-small"><span class="fa fa-minus"></span></span>
|
||||
</button>
|
||||
</template>
|
||||
</SoundItem>
|
||||
</ASoundItem>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
@ -21,11 +22,13 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import List from './list';
|
||||
import SoundItem from './soundItem';
|
||||
import AList from './AList';
|
||||
import ASoundItem from './ASoundItem';
|
||||
|
||||
export default {
|
||||
extends: List,
|
||||
extends: AList,
|
||||
emits: [...AList.emits, 'remove'],
|
||||
components: { ASoundItem },
|
||||
|
||||
props: {
|
||||
actions: Array,
|
||||
@ -53,6 +56,5 @@ export default {
|
||||
this.select(index)
|
||||
},
|
||||
},
|
||||
components: { List, SoundItem },
|
||||
}
|
||||
</script>
|
@ -21,7 +21,7 @@
|
||||
</slot>
|
||||
</div>
|
||||
<div class="media-right">
|
||||
<button class="button" v-if="player.sets.pin != $parent.set" @click.stop="player.togglePin(item)">
|
||||
<button class="button" v-if="player && player.sets.pin != $parent.set" @click.stop="player.togglePin(item)">
|
||||
<span class="icon is-small">
|
||||
<span :class="(pinned ? '' : 'has-text-grey-light ') + 'fa fa-thumbtack'"></span>
|
||||
</span>
|
||||
@ -31,16 +31,16 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Model from './model';
|
||||
import Sound from './sound';
|
||||
import Model from '../model';
|
||||
import Sound from '../sound';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
data: {type: Object, default: x => {}},
|
||||
data: {type: Object, default: () => {}},
|
||||
name: String,
|
||||
player: Object,
|
||||
page_url: String,
|
||||
actions: {type:Array, default: x => []},
|
||||
actions: {type:Array, default: () => []},
|
||||
index: {type:Number, default: null},
|
||||
},
|
||||
|
@ -5,8 +5,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
const splitReg = new RegExp(`,\s*`, 'g');
|
||||
const splitReg = new RegExp(',\\s*', 'g');
|
||||
|
||||
export default {
|
||||
data() {
|
||||
@ -20,7 +19,6 @@ export default {
|
||||
const items = this.$el.querySelectorAll('input[name="data"]:checked')
|
||||
const counts = {};
|
||||
|
||||
console.log(items)
|
||||
for(var item of items)
|
||||
if(item.value)
|
||||
for(var tag of item.value.split(splitReg))
|
||||
@ -28,12 +26,13 @@ export default {
|
||||
this.counts = counts;
|
||||
},
|
||||
|
||||
onclick(event) {
|
||||
onclick() {
|
||||
// TODO: row click => check checkbox
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
console.log(this.counts)
|
||||
this.$refs.form.addEventListener('change', () => this.update())
|
||||
this.update()
|
||||
}
|
57
assets/src/components/AStreamer.vue
Normal file
57
assets/src/components/AStreamer.vue
Normal file
@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<div>
|
||||
<slot :streamer="streamer" :streamers="streamers" :Sound="Sound"
|
||||
:sources="sources" :fetchStreamers="fetchStreamers"></slot>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Sound from '../sound';
|
||||
import {setEcoInterval} from '../utils';
|
||||
|
||||
import Streamer from '../streamer';
|
||||
|
||||
|
||||
export default {
|
||||
props: {
|
||||
apiUrl: String,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
// current streamer
|
||||
streamer: null,
|
||||
// all streamers
|
||||
streamers: [],
|
||||
// fetch interval id
|
||||
fetchInterval: null,
|
||||
Sound: Sound,
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
sources() {
|
||||
var sources = this.streamer ? this.streamer.sources : [];
|
||||
return sources.filter(s => s.data)
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
fetchStreamers() {
|
||||
Streamer.fetch(this.apiUrl, {many:true}).then(streamers => {
|
||||
this.streamers = streamers
|
||||
this.streamer = streamers ? streamers[0] : null
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.fetchStreamers();
|
||||
this.fetchInterval = setEcoInterval(() => this.streamer && this.streamer.fetch(), 5000)
|
||||
},
|
||||
|
||||
unmounted() {
|
||||
if(this.fetchInterval !== null)
|
||||
clearInterval(this.fetchInterval)
|
||||
}
|
||||
}
|
||||
</script>
|
23
assets/src/components/index.js
Normal file
23
assets/src/components/index.js
Normal file
@ -0,0 +1,23 @@
|
||||
import AAutocomplete from './AAutocomplete.vue'
|
||||
import AEpisode from './AEpisode.vue'
|
||||
import AList from './AList.vue'
|
||||
import APage from './APage.vue'
|
||||
import APlayer from './APlayer.vue'
|
||||
import APlaylist from './APlaylist.vue'
|
||||
import AProgress from './AProgress.vue'
|
||||
import ASoundItem from './ASoundItem.vue'
|
||||
import AStatistics from './AStatistics.vue'
|
||||
import AStreamer from './AStreamer.vue'
|
||||
|
||||
/**
|
||||
* Core components
|
||||
*/
|
||||
export default {
|
||||
AAutocomplete, AEpisode, AList, APage, APlayer, APlaylist,
|
||||
AProgress, ASoundItem,
|
||||
}
|
||||
|
||||
export const admin = {
|
||||
AStatistics, AStreamer,
|
||||
}
|
||||
|
8
assets/src/core.js
Normal file
8
assets/src/core.js
Normal file
@ -0,0 +1,8 @@
|
||||
import './index.js'
|
||||
import App from './app.js'
|
||||
|
||||
export default App
|
||||
|
||||
window.App = App
|
||||
|
||||
|
@ -13,7 +13,7 @@ import Builder from './appBuilder'
|
||||
import Sound from './sound'
|
||||
import {Set} from './model'
|
||||
|
||||
import './styles.scss'
|
||||
import './assets/styles.scss'
|
||||
|
||||
|
||||
window.aircox = {
|
||||
@ -32,14 +32,17 @@ window.aircox = {
|
||||
/**
|
||||
* Initialize main application and player.
|
||||
*/
|
||||
init(props=null, {config=null, builder=null, initPlayer=true}={}) {
|
||||
init(props=null, {config=null, builder=null, initPlayer=true, hotReload=false}={}) {
|
||||
builder = builder || this.builder
|
||||
this.builder = builder
|
||||
if(config)
|
||||
builder.config = config
|
||||
if(config || window.App)
|
||||
builder.config = config || window.App
|
||||
builder.title = document.title
|
||||
builder.mount({props})
|
||||
|
||||
if(hotReload)
|
||||
builder.enableHotReload(hotReload)
|
||||
|
||||
if(initPlayer) {
|
||||
let playerBuilder = this.playerBuilder
|
||||
playerBuilder.mount()
|
||||
@ -47,14 +50,3 @@ window.aircox = {
|
||||
},
|
||||
}
|
||||
|
||||
/*
|
||||
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,4 @@
|
||||
import {setEcoTimeout} from 'public/utils';
|
||||
import {setEcoTimeout} from './utils';
|
||||
import Model from './model';
|
||||
|
||||
export default class Live {
|
||||
@ -38,7 +38,7 @@ export default class Live {
|
||||
|
||||
refresh() {
|
||||
const promise = this.fetch();
|
||||
promise.then(data => {
|
||||
promise.then(() => {
|
||||
if(promise != this.promise)
|
||||
return [];
|
||||
|
@ -1,15 +1,24 @@
|
||||
|
||||
function getCookie(name) {
|
||||
/**
|
||||
* Return cookie with provided key
|
||||
*/
|
||||
function getCookie(key) {
|
||||
if(document.cookie && document.cookie !== '') {
|
||||
const cookie = document.cookie.split(';')
|
||||
.find(c => c.trim().startsWith(name + '='))
|
||||
.find(c => c.trim().startsWith(key + '='))
|
||||
return cookie ? decodeURIComponent(cookie.split('=')[1]) : null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* CSRF token provided by Django
|
||||
*/
|
||||
var csrfToken = null;
|
||||
|
||||
/**
|
||||
* Get CSRF token
|
||||
*/
|
||||
export function getCsrf() {
|
||||
if(csrfToken === null)
|
||||
csrfToken = getCookie('csrftoken')
|
||||
@ -18,9 +27,17 @@ export function getCsrf() {
|
||||
|
||||
|
||||
// TODO: prevent duplicate simple fetch
|
||||
/**
|
||||
* Provide interface used to fetch and manipulate objects.
|
||||
*/
|
||||
export default class Model {
|
||||
constructor(data, {url=null}={}) {
|
||||
/**
|
||||
* Instanciate model with provided data and options.
|
||||
* By default `url` is taken from `data.url_`.
|
||||
*/
|
||||
constructor(data, {url=null, ...options}={}) {
|
||||
this.url = url || data.url_;
|
||||
this.options = options;
|
||||
this.commit(data);
|
||||
}
|
||||
|
||||
@ -45,8 +62,13 @@ export default class Model {
|
||||
}
|
||||
}
|
||||
|
||||
static fromList(items, args=null) {
|
||||
return items ? items.map(d => new this(d, args)) : []
|
||||
/**
|
||||
* Return model instances for the provided list of model data.
|
||||
* @param {Array} items: array of data
|
||||
* @param {Object} options: options passed down to all model instances
|
||||
*/
|
||||
static fromList(items, options=null) {
|
||||
return items ? items.map(d => new this(d, options)) : []
|
||||
}
|
||||
|
||||
/**
|
@ -1,4 +1,4 @@
|
||||
import Model, {Set} from './model';
|
||||
import Model from './model';
|
||||
|
||||
|
||||
export default class Sound extends Model {
|
@ -1,5 +1,5 @@
|
||||
import Model from 'public/model';
|
||||
import {setEcoInterval} from 'public/utils';
|
||||
import Model from './model';
|
||||
import {setEcoInterval} from './utils';
|
||||
|
||||
|
||||
export class Streamer extends Model {
|
||||
@ -18,6 +18,8 @@ export class Streamer extends Model {
|
||||
}
|
||||
}
|
||||
|
||||
export default Streamer;
|
||||
|
||||
export class Request extends Model {
|
||||
static getId(data) { return data.rid; }
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import AdminApp from 'admin/app';
|
||||
import Model, {Set} from 'public/model';
|
||||
import Sound from 'public/sound';
|
||||
import {setEcoInterval} from 'public/utils';
|
||||
import AdminApp from '../admin';
|
||||
import Model from '../model';
|
||||
import Sound from '../sound';
|
||||
import {setEcoInterval} from '../utils';
|
||||
|
||||
import {Streamer, Queue} from './controllers';
|
||||
|
@ -1,16 +1,19 @@
|
||||
import AdminApp from 'admin/app';
|
||||
import Model, {Set} from 'public/model';
|
||||
import Sound from 'public/sound';
|
||||
import {setEcoInterval} from 'public/utils';
|
||||
<template>
|
||||
<div>
|
||||
<slot :streamer="streamer" :streamers="streamers" :Sound="Sound"
|
||||
:sources="sources" :fetchStreamers="fetchStreamers"></slot>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import AdminApp from '../admin';
|
||||
import Sound from '../sound';
|
||||
import {setEcoInterval} from '../utils';
|
||||
|
||||
import {Streamer, Queue} from './controllers';
|
||||
import {Streamer} from './controllers';
|
||||
|
||||
|
||||
export const StreamerApp = {
|
||||
...AdminApp,
|
||||
|
||||
export default {
|
||||
props: {
|
||||
...(AdminApp.props || {}),
|
||||
apiUrl: String,
|
||||
},
|
||||
|
||||
@ -27,8 +30,6 @@ export const StreamerApp = {
|
||||
},
|
||||
|
||||
computed: {
|
||||
...(AdminApp.computed || {}),
|
||||
|
||||
sources() {
|
||||
var sources = this.streamer ? this.streamer.sources : [];
|
||||
return sources.filter(s => s.data)
|
||||
@ -36,8 +37,6 @@ export const StreamerApp = {
|
||||
},
|
||||
|
||||
methods: {
|
||||
...(AdminApp.methods || {}),
|
||||
|
||||
fetchStreamers() {
|
||||
Streamer.fetch(this.apiUrl, {many:true}).then(streamers => {
|
||||
this.streamers = streamers
|
||||
@ -56,6 +55,4 @@ export const StreamerApp = {
|
||||
clearInterval(this.fetchInterval)
|
||||
}
|
||||
}
|
||||
|
||||
window.StreamerApp = StreamerApp
|
||||
|
||||
</script>
|
@ -1,12 +1,15 @@
|
||||
|
||||
|
||||
/**
|
||||
* Run function with provided args only if document is not hidden
|
||||
*/
|
||||
export function setEcoTimeout(func, ...args) {
|
||||
return setTimeout((...args) => {
|
||||
!document.hidden && func(...args)
|
||||
}, ...args)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Run function at specific interval only if document is not hidden
|
||||
*/
|
||||
export function setEcoInterval(func, ...args) {
|
||||
return setInterval((...args) => {
|
||||
!document.hidden && func(...args)
|
22
assets/vue.config.js
Normal file
22
assets/vue.config.js
Normal file
@ -0,0 +1,22 @@
|
||||
const path = require('path');
|
||||
|
||||
const { defineConfig } = require('@vue/cli-service')
|
||||
module.exports = defineConfig({
|
||||
transpileDependencies: true,
|
||||
outputDir: path.resolve('../aircox/static/aircox'),
|
||||
publicPath: './',
|
||||
runtimeCompiler: true,
|
||||
filenameHashing: false,
|
||||
|
||||
css: {
|
||||
extract: true,
|
||||
loaderOptions: {
|
||||
sass: { sourceMap: true },
|
||||
}
|
||||
},
|
||||
|
||||
pages: {
|
||||
core: { entry: 'src/core.js', },
|
||||
admin: { entry: 'src/admin.js' },
|
||||
}
|
||||
})
|
Reference in New Issue
Block a user