Fix #612: Improved accessibility by using main/section/nav tags and aria-labels in most critical places

This commit is contained in:
Eliot Berriot 2018-11-19 23:33:22 +01:00
parent 9005ebbd6d
commit 29171853b3
No known key found for this signature in database
GPG key ID: DD6965E2476E5C27
42 changed files with 1266 additions and 1122 deletions

View file

@ -1,10 +1,10 @@
<template>
<div>
<main>
<div v-if="isLoading" class="ui vertical segment" v-title="">
<div :class="['ui', 'centered', 'active', 'inline', 'loader']"></div>
</div>
<template v-if="album">
<div :class="['ui', 'head', {'with-background': album.cover.original}, 'vertical', 'center', 'aligned', 'stripe', 'segment']" :style="headerStyle" v-title="album.title">
<section :class="['ui', 'head', {'with-background': album.cover.original}, 'vertical', 'center', 'aligned', 'stripe', 'segment']" :style="headerStyle" v-title="album.title">
<div class="segment-content">
<h2 class="ui center aligned icon header">
<i class="circular inverted sound yellow icon"></i>
@ -38,86 +38,93 @@
<translate>View on MusicBrainz</translate>
</a>
</div>
</div>
<div class="ui vertical stripe segment">
</section>
<section class="ui vertical stripe segment">
<h2>
<translate>Tracks</translate>
</h2>
<track-table v-if="album" :artist="album.artist" :display-position="true" :tracks="album.tracks"></track-table>
</div>
<div class="ui vertical stripe segment">
</section>
<section class="ui vertical stripe segment">
<h2>
<translate>User libraries</translate>
</h2>
<library-widget :url="'albums/' + id + '/libraries/'">
<translate slot="subtitle">This album is present in the following libraries:</translate>
</library-widget>
</div>
</section>
</template>
</div>
</main>
</template>
<script>
import axios from 'axios'
import logger from '@/logging'
import backend from '@/audio/backend'
import PlayButton from '@/components/audio/PlayButton'
import TrackTable from '@/components/audio/track/Table'
import LibraryWidget from '@/components/federation/LibraryWidget'
import axios from "axios"
import logger from "@/logging"
import backend from "@/audio/backend"
import PlayButton from "@/components/audio/PlayButton"
import TrackTable from "@/components/audio/track/Table"
import LibraryWidget from "@/components/federation/LibraryWidget"
const FETCH_URL = 'albums/'
const FETCH_URL = "albums/"
export default {
props: ['id'],
props: ["id"],
components: {
PlayButton,
TrackTable,
LibraryWidget
},
data () {
data() {
return {
isLoading: true,
album: null
}
},
created () {
created() {
this.fetchData()
},
methods: {
fetchData () {
fetchData() {
var self = this
this.isLoading = true
let url = FETCH_URL + this.id + '/'
let url = FETCH_URL + this.id + "/"
logger.default.debug('Fetching album "' + this.id + '"')
axios.get(url).then((response) => {
axios.get(url).then(response => {
self.album = backend.Album.clean(response.data)
self.isLoading = false
})
}
},
computed: {
labels () {
labels() {
return {
title: this.$gettext('Album')
title: this.$gettext("Album")
}
},
wikipediaUrl () {
return 'https://en.wikipedia.org/w/index.php?search=' + encodeURI(this.album.title + ' ' + this.album.artist.name)
wikipediaUrl() {
return (
"https://en.wikipedia.org/w/index.php?search=" +
encodeURI(this.album.title + " " + this.album.artist.name)
)
},
musicbrainzUrl () {
musicbrainzUrl() {
if (this.album.mbid) {
return 'https://musicbrainz.org/release/' + this.album.mbid
return "https://musicbrainz.org/release/" + this.album.mbid
}
},
headerStyle () {
headerStyle() {
if (!this.album.cover.original) {
return ''
return ""
}
return 'background-image: url(' + this.$store.getters['instance/absoluteUrl'](this.album.cover.original) + ')'
return (
"background-image: url(" +
this.$store.getters["instance/absoluteUrl"](this.album.cover.original) +
")"
)
}
},
watch: {
id () {
id() {
this.fetchData()
}
}
@ -126,5 +133,4 @@ export default {
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
</style>

View file

@ -1,10 +1,10 @@
<template>
<div v-title="labels.title">
<main v-title="labels.title">
<div v-if="isLoading" class="ui vertical segment">
<div :class="['ui', 'centered', 'active', 'inline', 'loader']"></div>
</div>
<template v-if="artist">
<div :class="['ui', 'head', {'with-background': cover}, 'vertical', 'center', 'aligned', 'stripe', 'segment']" :style="headerStyle" v-title="artist.name">
<section :class="['ui', 'head', {'with-background': cover}, 'vertical', 'center', 'aligned', 'stripe', 'segment']" :style="headerStyle" v-title="artist.name">
<div class="segment-content">
<h2 class="ui center aligned icon header">
<i class="circular inverted users violet icon"></i>
@ -36,11 +36,11 @@
<translate>View on MusicBrainz</translate>
</a>
</div>
</div>
<div v-if="isLoadingAlbums" class="ui vertical stripe segment">
</section>
<section v-if="isLoadingAlbums" class="ui vertical stripe segment">
<div :class="['ui', 'centered', 'active', 'inline', 'loader']"></div>
</div>
<div v-else-if="albums && albums.length > 0" class="ui vertical stripe segment">
</section>
<section v-else-if="albums && albums.length > 0" class="ui vertical stripe segment">
<h2>
<translate>Albums by this artist</translate>
</h2>
@ -49,38 +49,38 @@
<album-card :mode="'rich'" class="fluid" :album="album"></album-card>
</div>
</div>
</div>
<div v-if="tracks.length > 0" class="ui vertical stripe segment">
</section>
<section v-if="tracks.length > 0" class="ui vertical stripe segment">
<h2>
<translate>Tracks by this artist</translate>
</h2>
<track-table :display-position="true" :tracks="tracks"></track-table>
</div>
<div class="ui vertical stripe segment">
</section>
<section class="ui vertical stripe segment">
<h2>
<translate>User libraries</translate>
</h2>
<library-widget :url="'artists/' + id + '/libraries/'">
<translate slot="subtitle">This artist is present in the following libraries:</translate>
</library-widget>
</div>
</section>
</template>
</div>
</main>
</template>
<script>
import _ from 'lodash'
import axios from 'axios'
import logger from '@/logging'
import backend from '@/audio/backend'
import AlbumCard from '@/components/audio/album/Card'
import RadioButton from '@/components/radios/Button'
import PlayButton from '@/components/audio/PlayButton'
import TrackTable from '@/components/audio/track/Table'
import LibraryWidget from '@/components/federation/LibraryWidget'
import _ from "lodash"
import axios from "axios"
import logger from "@/logging"
import backend from "@/audio/backend"
import AlbumCard from "@/components/audio/album/Card"
import RadioButton from "@/components/radios/Button"
import PlayButton from "@/components/audio/PlayButton"
import TrackTable from "@/components/audio/track/Table"
import LibraryWidget from "@/components/federation/LibraryWidget"
export default {
props: ['id'],
props: ["id"],
components: {
AlbumCard,
RadioButton,
@ -88,7 +88,7 @@ export default {
TrackTable,
LibraryWidget
},
data () {
data() {
return {
isLoading: true,
isLoadingAlbums: true,
@ -99,54 +99,63 @@ export default {
tracks: []
}
},
created () {
created() {
this.fetchData()
},
methods: {
fetchData () {
fetchData() {
var self = this
this.isLoading = true
logger.default.debug('Fetching artist "' + this.id + '"')
axios.get('tracks/', {params: {artist: this.id}}).then((response) => {
axios.get("tracks/", { params: { artist: this.id } }).then(response => {
self.tracks = response.data.results
self.totalTracks = response.data.count
})
axios.get('artists/' + this.id + '/').then((response) => {
axios.get("artists/" + this.id + "/").then(response => {
self.artist = response.data
self.isLoading = false
self.isLoadingAlbums = true
axios.get('albums/', {params: {artist: self.id, ordering: '-release_date'}}).then((response) => {
self.totalAlbums = response.data.count
let parsed = JSON.parse(JSON.stringify(response.data.results))
self.albums = parsed.map((album) => {
return backend.Album.clean(album)
axios
.get("albums/", {
params: { artist: self.id, ordering: "-release_date" }
})
.then(response => {
self.totalAlbums = response.data.count
let parsed = JSON.parse(JSON.stringify(response.data.results))
self.albums = parsed.map(album => {
return backend.Album.clean(album)
})
self.isLoadingAlbums = false
})
self.isLoadingAlbums = false
})
})
}
},
computed: {
labels () {
labels() {
return {
title: this.$gettext('Artist')
title: this.$gettext("Artist")
}
},
isPlayable () {
return this.artist.albums.filter((a) => {
return a.is_playable
}).length > 0
isPlayable() {
return (
this.artist.albums.filter(a => {
return a.is_playable
}).length > 0
)
},
wikipediaUrl () {
return 'https://en.wikipedia.org/w/index.php?search=' + encodeURI(this.artist.name)
wikipediaUrl() {
return (
"https://en.wikipedia.org/w/index.php?search=" +
encodeURI(this.artist.name)
)
},
musicbrainzUrl () {
musicbrainzUrl() {
if (this.artist.mbid) {
return 'https://musicbrainz.org/artist/' + this.artist.mbid
return "https://musicbrainz.org/artist/" + this.artist.mbid
}
},
allTracks () {
allTracks() {
let tracks = []
this.albums.forEach(album => {
album.tracks.forEach(track => {
@ -155,22 +164,28 @@ export default {
})
return tracks
},
cover () {
return this.artist.albums.filter(album => {
return album.cover
}).map(album => {
return album.cover
})[0]
cover() {
return this.artist.albums
.filter(album => {
return album.cover
})
.map(album => {
return album.cover
})[0]
},
headerStyle () {
headerStyle() {
if (!this.cover || !this.cover.original) {
return ''
return ""
}
return 'background-image: url(' + this.$store.getters['instance/absoluteUrl'](this.cover.original) + ')'
return (
"background-image: url(" +
this.$store.getters["instance/absoluteUrl"](this.cover.original) +
")"
)
}
},
watch: {
id () {
id() {
this.fetchData()
}
}

View file

@ -1,6 +1,6 @@
<template>
<div v-title="labels.title">
<div class="ui vertical stripe segment">
<main v-title="labels.title">
<section class="ui vertical stripe segment">
<h2 class="ui header">
<translate>Browsing artists</translate>
</h2>
@ -64,60 +64,59 @@
:total="result.count"
></pagination>
</div>
</div>
</div>
</section>
</main>
</template>
<script>
import axios from 'axios'
import _ from 'lodash'
import $ from 'jquery'
import axios from "axios"
import _ from "lodash"
import $ from "jquery"
import logger from '@/logging'
import logger from "@/logging"
import OrderingMixin from '@/components/mixins/Ordering'
import PaginationMixin from '@/components/mixins/Pagination'
import TranslationsMixin from '@/components/mixins/Translations'
import ArtistCard from '@/components/audio/artist/Card'
import Pagination from '@/components/Pagination'
import OrderingMixin from "@/components/mixins/Ordering"
import PaginationMixin from "@/components/mixins/Pagination"
import TranslationsMixin from "@/components/mixins/Translations"
import ArtistCard from "@/components/audio/artist/Card"
import Pagination from "@/components/Pagination"
const FETCH_URL = 'artists/'
const FETCH_URL = "artists/"
export default {
mixins: [OrderingMixin, PaginationMixin, TranslationsMixin],
props: {
defaultQuery: {type: String, required: false, default: ''}
defaultQuery: { type: String, required: false, default: "" }
},
components: {
ArtistCard,
Pagination
},
data () {
let defaultOrdering = this.getOrderingFromString(this.defaultOrdering || '-creation_date')
data() {
let defaultOrdering = this.getOrderingFromString(
this.defaultOrdering || "-creation_date"
)
return {
isLoading: true,
result: null,
page: parseInt(this.defaultPage),
query: this.defaultQuery,
paginateBy: parseInt(this.defaultPaginateBy || 12),
orderingDirection: defaultOrdering.direction || '+',
orderingDirection: defaultOrdering.direction || "+",
ordering: defaultOrdering.field,
orderingOptions: [
['creation_date', 'creation_date'],
['name', 'name']
]
orderingOptions: [["creation_date", "creation_date"], ["name", "name"]]
}
},
created () {
created() {
this.fetchData()
},
mounted () {
$('.ui.dropdown').dropdown()
mounted() {
$(".ui.dropdown").dropdown()
},
computed: {
labels () {
let searchPlaceholder = this.$gettext('Enter an artist name...')
let title = this.$gettext('Artists')
labels() {
let searchPlaceholder = this.$gettext("Enter an artist name...")
let title = this.$gettext("Artists")
return {
searchPlaceholder,
title
@ -125,7 +124,7 @@ export default {
}
},
methods: {
updateQueryString: _.debounce(function () {
updateQueryString: _.debounce(function() {
this.$router.replace({
query: {
query: this.query,
@ -135,7 +134,7 @@ export default {
}
})
}, 500),
fetchData: _.debounce(function () {
fetchData: _.debounce(function() {
var self = this
this.isLoading = true
let url = FETCH_URL
@ -144,36 +143,36 @@ export default {
page_size: this.paginateBy,
name__icontains: this.query,
ordering: this.getOrderingAsString(),
playable: 'true'
playable: "true"
}
logger.default.debug('Fetching artists')
axios.get(url, {params: params}).then((response) => {
logger.default.debug("Fetching artists")
axios.get(url, { params: params }).then(response => {
self.result = response.data
self.isLoading = false
})
}, 500),
selectPage: function (page) {
selectPage: function(page) {
this.page = page
}
},
watch: {
page () {
page() {
this.updateQueryString()
this.fetchData()
},
paginateBy () {
paginateBy() {
this.updateQueryString()
this.fetchData()
},
ordering () {
ordering() {
this.updateQueryString()
this.fetchData()
},
orderingDirection () {
orderingDirection() {
this.updateQueryString()
this.fetchData()
},
query () {
query() {
this.updateQueryString()
this.fetchData()
}

View file

@ -1,6 +1,6 @@
<template>
<div v-title="labels.title">
<div class="ui vertical stripe segment">
<main v-title="labels.title">
<section class="ui vertical stripe segment">
<div class="ui stackable three column grid">
<div class="column">
<track-widget :url="'history/listenings/'" :filters="{scope: 'user', ordering: '-creation_date'}">
@ -26,23 +26,23 @@
</album-widget>
</div>
</div>
</div>
</div>
</section>
</main>
</template>
<script>
import axios from 'axios'
import Search from '@/components/audio/Search'
import logger from '@/logging'
import ArtistCard from '@/components/audio/artist/Card'
import TrackWidget from '@/components/audio/track/Widget'
import AlbumWidget from '@/components/audio/album/Widget'
import PlaylistWidget from '@/components/playlists/Widget'
import axios from "axios"
import Search from "@/components/audio/Search"
import logger from "@/logging"
import ArtistCard from "@/components/audio/artist/Card"
import TrackWidget from "@/components/audio/track/Widget"
import AlbumWidget from "@/components/audio/album/Widget"
import PlaylistWidget from "@/components/playlists/Widget"
const ARTISTS_URL = 'artists/'
const ARTISTS_URL = "artists/"
export default {
name: 'library',
name: "library",
components: {
Search,
ArtistCard,
@ -50,35 +50,35 @@ export default {
AlbumWidget,
PlaylistWidget
},
data () {
data() {
return {
artists: [],
isLoadingArtists: false
}
},
created () {
created() {
this.fetchArtists()
},
computed: {
labels () {
labels() {
return {
title: this.$gettext('Home')
title: this.$gettext("Home")
}
}
},
methods: {
fetchArtists () {
fetchArtists() {
var self = this
this.isLoadingArtists = true
let params = {
ordering: '-creation_date',
ordering: "-creation_date",
playable: true
}
let url = ARTISTS_URL
logger.default.time('Loading latest artists')
axios.get(url, {params: params}).then((response) => {
logger.default.time("Loading latest artists")
axios.get(url, { params: params }).then(response => {
self.artists = response.data.results
logger.default.timeEnd('Loading latest artists')
logger.default.timeEnd("Loading latest artists")
self.isLoadingArtists = false
})
}

View file

@ -1,6 +1,6 @@
<template>
<div class="main library pusher">
<div class="ui secondary pointing menu">
<nav class="ui secondary pointing menu" role="navigation" :aria-label="labels.secondaryMenu">
<router-link class="ui item" to="/library" exact>
<translate>Browse</translate>
</router-link>
@ -13,7 +13,7 @@
<router-link class="ui item" to="/library/playlists" exact>
<translate>Playlists</translate>
</router-link>
</div>
</nav>
<router-view :key="$route.fullPath"></router-view>
</div>
</template>
@ -21,8 +21,16 @@
<script>
export default {
computed: {
showImports () {
return this.$store.state.auth.availablePermissions['upload'] || this.$store.state.auth.availablePermissions['library']
showImports() {
return (
this.$store.state.auth.availablePermissions["upload"] ||
this.$store.state.auth.availablePermissions["library"]
)
},
labels() {
return {
secondaryMenu: this.$gettext("Secondary menu")
}
}
}
}
@ -30,7 +38,7 @@ export default {
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="scss">
@import '../../style/vendor/media';
@import "../../style/vendor/media";
.library {
.ui.segment.head {
@ -46,18 +54,16 @@ export default {
}
&.with-background {
.header {
&, .sub {
&,
.sub {
text-shadow: 0 1px 0 rgba(0, 0, 0, 0.8);
color: white !important;
}
}
.segment-content {
background-color: rgba(0, 0, 0, 0.5)
background-color: rgba(0, 0, 0, 0.5);
}
}
}
}
</style>

View file

@ -1,6 +1,6 @@
<template>
<div v-title="labels.title">
<div class="ui vertical stripe segment">
<main v-title="labels.title">
<section class="ui vertical stripe segment">
<h2 class="ui header">
<translate>Browsing radios</translate>
</h2>
@ -86,60 +86,59 @@
:total="result.count"
></pagination>
</div>
</div>
</div>
</section>
</main>
</template>
<script>
import axios from 'axios'
import _ from 'lodash'
import $ from 'jquery'
import axios from "axios"
import _ from "lodash"
import $ from "jquery"
import logger from '@/logging'
import logger from "@/logging"
import OrderingMixin from '@/components/mixins/Ordering'
import PaginationMixin from '@/components/mixins/Pagination'
import TranslationsMixin from '@/components/mixins/Translations'
import RadioCard from '@/components/radios/Card'
import Pagination from '@/components/Pagination'
import OrderingMixin from "@/components/mixins/Ordering"
import PaginationMixin from "@/components/mixins/Pagination"
import TranslationsMixin from "@/components/mixins/Translations"
import RadioCard from "@/components/radios/Card"
import Pagination from "@/components/Pagination"
const FETCH_URL = 'radios/radios/'
const FETCH_URL = "radios/radios/"
export default {
mixins: [OrderingMixin, PaginationMixin, TranslationsMixin],
props: {
defaultQuery: {type: String, required: false, default: ''}
defaultQuery: { type: String, required: false, default: "" }
},
components: {
RadioCard,
Pagination
},
data () {
let defaultOrdering = this.getOrderingFromString(this.defaultOrdering || '-creation_date')
data() {
let defaultOrdering = this.getOrderingFromString(
this.defaultOrdering || "-creation_date"
)
return {
isLoading: true,
result: null,
page: parseInt(this.defaultPage),
query: this.defaultQuery,
paginateBy: parseInt(this.defaultPaginateBy || 12),
orderingDirection: defaultOrdering.direction || '+',
orderingDirection: defaultOrdering.direction || "+",
ordering: defaultOrdering.field,
orderingOptions: [
['creation_date', 'creation_date'],
['name', 'name']
]
orderingOptions: [["creation_date", "creation_date"], ["name", "name"]]
}
},
created () {
created() {
this.fetchData()
},
mounted () {
$('.ui.dropdown').dropdown()
mounted() {
$(".ui.dropdown").dropdown()
},
computed: {
labels () {
let searchPlaceholder = this.$gettext('Enter a radio name...')
let title = this.$gettext('Radios')
labels() {
let searchPlaceholder = this.$gettext("Enter a radio name...")
let title = this.$gettext("Radios")
return {
searchPlaceholder,
title
@ -147,7 +146,7 @@ export default {
}
},
methods: {
updateQueryString: _.debounce(function () {
updateQueryString: _.debounce(function() {
this.$router.replace({
query: {
query: this.query,
@ -157,7 +156,7 @@ export default {
}
})
}, 500),
fetchData: _.debounce(function () {
fetchData: _.debounce(function() {
var self = this
this.isLoading = true
let url = FETCH_URL
@ -167,34 +166,34 @@ export default {
name__icontains: this.query,
ordering: this.getOrderingAsString()
}
logger.default.debug('Fetching radios')
axios.get(url, {params: params}).then((response) => {
logger.default.debug("Fetching radios")
axios.get(url, { params: params }).then(response => {
self.result = response.data
self.isLoading = false
})
}, 500),
selectPage: function (page) {
selectPage: function(page) {
this.page = page
}
},
watch: {
page () {
page() {
this.updateQueryString()
this.fetchData()
},
paginateBy () {
paginateBy() {
this.updateQueryString()
this.fetchData()
},
ordering () {
ordering() {
this.updateQueryString()
this.fetchData()
},
orderingDirection () {
orderingDirection() {
this.updateQueryString()
this.fetchData()
},
query () {
query() {
this.updateQueryString()
this.fetchData()
}

View file

@ -1,10 +1,10 @@
<template>
<div>
<main>
<div v-if="isLoadingTrack" class="ui vertical segment" v-title="labels.title">
<div :class="['ui', 'centered', 'active', 'inline', 'loader']"></div>
</div>
<template v-if="track">
<div :class="['ui', 'head', {'with-background': cover}, 'vertical', 'center', 'aligned', 'stripe', 'segment']" :style="headerStyle" v-title="track.title">
<section :class="['ui', 'head', {'with-background': cover}, 'vertical', 'center', 'aligned', 'stripe', 'segment']" :style="headerStyle" v-title="track.title">
<div class="segment-content">
<h2 class="ui center aligned icon header">
<i class="circular inverted music orange icon"></i>
@ -49,8 +49,8 @@
<translate>Download</translate>
</a>
</div>
</div>
<div class="ui vertical stripe center aligned segment" v-if="upload">
</section>
<section class="ui vertical stripe center aligned segment" v-if="upload">
<h2 class="ui header"><translate>Track information</translate></h2>
<table class="ui very basic collapsing celled center aligned table">
<tbody>
@ -100,8 +100,8 @@
</tr>
</tbody>
</table>
</div>
<div class="ui vertical stripe center aligned segment">
</section>
<section class="ui vertical stripe center aligned segment">
<h2>
<translate>Lyrics</translate>
</h2>
@ -117,41 +117,40 @@
<translate>Search on lyrics.wikia.com</translate>
</a>
</template>
</div>
<div class="ui vertical stripe segment">
</section>
<section class="ui vertical stripe segment">
<h2>
<translate>User libraries</translate>
</h2>
<library-widget :url="'tracks/' + id + '/libraries/'">
<translate slot="subtitle">This track is present in the following libraries:</translate>
</library-widget>
</div>
</section>
</template>
</div>
</main>
</template>
<script>
import time from "@/utils/time"
import axios from "axios"
import url from "@/utils/url"
import logger from "@/logging"
import PlayButton from "@/components/audio/PlayButton"
import TrackFavoriteIcon from "@/components/favorites/TrackFavoriteIcon"
import TrackPlaylistIcon from "@/components/playlists/TrackPlaylistIcon"
import LibraryWidget from "@/components/federation/LibraryWidget"
import time from '@/utils/time'
import axios from 'axios'
import url from '@/utils/url'
import logger from '@/logging'
import PlayButton from '@/components/audio/PlayButton'
import TrackFavoriteIcon from '@/components/favorites/TrackFavoriteIcon'
import TrackPlaylistIcon from '@/components/playlists/TrackPlaylistIcon'
import LibraryWidget from '@/components/federation/LibraryWidget'
const FETCH_URL = 'tracks/'
const FETCH_URL = "tracks/"
export default {
props: ['id'],
props: ["id"],
components: {
PlayButton,
TrackPlaylistIcon,
TrackFavoriteIcon,
LibraryWidget
},
data () {
data() {
return {
time,
isLoadingTrack: true,
@ -160,78 +159,94 @@ export default {
lyrics: null
}
},
created () {
created() {
this.fetchData()
this.fetchLyrics()
},
methods: {
fetchData () {
fetchData() {
var self = this
this.isLoadingTrack = true
let url = FETCH_URL + this.id + '/'
let url = FETCH_URL + this.id + "/"
logger.default.debug('Fetching track "' + this.id + '"')
axios.get(url).then((response) => {
axios.get(url).then(response => {
self.track = response.data
self.isLoadingTrack = false
})
},
fetchLyrics () {
fetchLyrics() {
var self = this
this.isLoadingLyrics = true
let url = FETCH_URL + this.id + '/lyrics/'
let url = FETCH_URL + this.id + "/lyrics/"
logger.default.debug('Fetching lyrics for track "' + this.id + '"')
axios.get(url).then((response) => {
self.lyrics = response.data
self.isLoadingLyrics = false
}, (response) => {
console.error('No lyrics available')
self.isLoadingLyrics = false
})
axios.get(url).then(
response => {
self.lyrics = response.data
self.isLoadingLyrics = false
},
response => {
console.error("No lyrics available")
self.isLoadingLyrics = false
}
)
}
},
computed: {
labels () {
labels() {
return {
title: this.$gettext('Track')
title: this.$gettext("Track")
}
},
upload () {
upload() {
if (this.track.uploads) {
return this.track.uploads[0]
}
},
wikipediaUrl () {
return 'https://en.wikipedia.org/w/index.php?search=' + encodeURI(this.track.title + ' ' + this.track.artist.name)
wikipediaUrl() {
return (
"https://en.wikipedia.org/w/index.php?search=" +
encodeURI(this.track.title + " " + this.track.artist.name)
)
},
musicbrainzUrl () {
musicbrainzUrl() {
if (this.track.mbid) {
return 'https://musicbrainz.org/recording/' + this.track.mbid
return "https://musicbrainz.org/recording/" + this.track.mbid
}
},
downloadUrl () {
let u = this.$store.getters['instance/absoluteUrl'](this.upload.listen_url)
downloadUrl() {
let u = this.$store.getters["instance/absoluteUrl"](
this.upload.listen_url
)
if (this.$store.state.auth.authenticated) {
u = url.updateQueryString(u, 'jwt', encodeURI(this.$store.state.auth.token))
u = url.updateQueryString(
u,
"jwt",
encodeURI(this.$store.state.auth.token)
)
}
return u
},
lyricsSearchUrl () {
let base = 'http://lyrics.wikia.com/wiki/Special:Search?query='
let query = this.track.artist.name + ' ' + this.track.title
lyricsSearchUrl() {
let base = "http://lyrics.wikia.com/wiki/Special:Search?query="
let query = this.track.artist.name + " " + this.track.title
return base + encodeURI(query)
},
cover () {
cover() {
return null
},
headerStyle () {
headerStyle() {
if (!this.cover) {
return ''
return ""
}
return 'background-image: url(' + this.$store.getters['instance/absoluteUrl'](this.cover) + ')'
return (
"background-image: url(" +
this.$store.getters["instance/absoluteUrl"](this.cover) +
")"
)
}
},
watch: {
id () {
id() {
this.fetchData()
}
}

View file

@ -1,7 +1,7 @@
<template>
<div class="ui vertical stripe segment" v-title="labels.title">
<div>
<div>
<section>
<h2 class="ui header">
<translate>Builder</translate>
</h2>
@ -87,28 +87,28 @@
</h3>
<track-table v-if="checkResult.candidates.sample" :tracks="checkResult.candidates.sample"></track-table>
</template>
</div>
</section>
</div>
</div>
</template>
<script>
import axios from 'axios'
import $ from 'jquery'
import _ from 'lodash'
import BuilderFilter from './Filter'
import TrackTable from '@/components/audio/track/Table'
import RadioButton from '@/components/radios/Button'
import axios from "axios"
import $ from "jquery"
import _ from "lodash"
import BuilderFilter from "./Filter"
import TrackTable from "@/components/audio/track/Table"
import RadioButton from "@/components/radios/Button"
export default {
props: {
id: {required: false}
id: { required: false }
},
components: {
BuilderFilter,
TrackTable,
RadioButton
},
data: function () {
data: function() {
return {
isLoading: false,
success: false,
@ -116,12 +116,12 @@ export default {
currentFilterType: null,
filters: [],
checkResult: null,
radioName: '',
radioDesc: '',
radioName: "",
radioDesc: "",
isPublic: true
}
},
created: function () {
created: function() {
let self = this
this.fetchFilters().then(() => {
if (self.id) {
@ -129,18 +129,18 @@ export default {
}
})
},
mounted () {
$('.ui.dropdown').dropdown()
mounted() {
$(".ui.dropdown").dropdown()
},
methods: {
fetchFilters: function () {
fetchFilters: function() {
let self = this
let url = 'radios/radios/filters/'
return axios.get(url).then((response) => {
let url = "radios/radios/filters/"
return axios.get(url).then(response => {
self.availableFilters = response.data
})
},
add () {
add() {
this.filters.push({
config: {},
filter: this.currentFilter,
@ -148,23 +148,25 @@ export default {
})
this.fetchCandidates()
},
updateConfig (index, field, value) {
updateConfig(index, field, value) {
this.filters[index].config[field] = value
this.fetchCandidates()
},
deleteFilter (index) {
deleteFilter(index) {
this.filters.splice(index, 1)
this.fetchCandidates()
},
fetch: function () {
fetch: function() {
let self = this
self.isLoading = true
let url = 'radios/radios/' + this.id + '/'
axios.get(url).then((response) => {
let url = "radios/radios/" + this.id + "/"
axios.get(url).then(response => {
self.filters = response.data.config.map(f => {
return {
config: f,
filter: this.availableFilters.filter(e => { return e.type === f.type })[0],
filter: this.availableFilters.filter(e => {
return e.type === f.type
})[0],
hash: +new Date()
}
})
@ -174,24 +176,22 @@ export default {
self.isLoading = false
})
},
fetchCandidates: function () {
fetchCandidates: function() {
let self = this
let url = 'radios/radios/validate/'
let url = "radios/radios/validate/"
let final = this.filters.map(f => {
let c = _.clone(f.config)
c.type = f.filter.type
return c
})
final = {
'filters': [
{'type': 'group', filters: final}
]
filters: [{ type: "group", filters: final }]
}
axios.post(url, final).then((response) => {
axios.post(url, final).then(response => {
self.checkResult = response.data.filters[0]
})
},
save: function () {
save: function() {
let self = this
self.success = false
self.isLoading = true
@ -202,24 +202,24 @@ export default {
return c
})
final = {
'name': this.radioName,
'description': this.radioDesc,
'is_public': this.isPublic,
'config': final
name: this.radioName,
description: this.radioDesc,
is_public: this.isPublic,
config: final
}
if (this.id) {
let url = 'radios/radios/' + this.id + '/'
axios.put(url, final).then((response) => {
let url = "radios/radios/" + this.id + "/"
axios.put(url, final).then(response => {
self.isLoading = false
self.success = true
})
} else {
let url = 'radios/radios/'
axios.post(url, final).then((response) => {
let url = "radios/radios/"
axios.post(url, final).then(response => {
self.success = true
self.isLoading = false
self.$router.push({
name: 'library.radios.detail',
name: "library.radios.detail",
params: {
id: response.data.id
}
@ -229,30 +229,28 @@ export default {
}
},
computed: {
labels () {
let title = this.$gettext('Radio Builder')
labels() {
let title = this.$gettext("Radio Builder")
let placeholder = {
'name': this.$gettext('My awesome radio'),
'description': this.$gettext('My awesome description')
name: this.$gettext("My awesome radio"),
description: this.$gettext("My awesome description")
}
return {
title,
placeholder
}
},
canSave: function () {
return (
this.radioName.length > 0 && this.checkErrors.length === 0
)
canSave: function() {
return this.radioName.length > 0 && this.checkErrors.length === 0
},
checkErrors: function () {
checkErrors: function() {
if (!this.checkResult) {
return []
}
let errors = this.checkResult.errors
return errors
},
currentFilter: function () {
currentFilter: function() {
let self = this
return this.availableFilters.filter(e => {
return e.type === self.currentFilterType
@ -261,7 +259,7 @@ export default {
},
watch: {
filters: {
handler: function () {
handler: function() {
this.fetchCandidates()
},
deep: true