Verified Commit bb3ed760 authored by Eliot Berriot's avatar Eliot Berriot
Browse files

Merge branch 'release/0.4'

parents c7d6ad95 a38ca1ed
import Vue from 'vue'
import config from '@/config'
import axios from 'axios'
import logger from '@/logging'
const REMOVE_URL = config.API_URL + 'favorites/tracks/remove/'
const FAVORITES_URL = config.API_URL + 'favorites/tracks/'
export default {
namespaced: true,
state: {
......@@ -35,16 +31,14 @@ export default {
set ({commit, state}, {id, value}) {
commit('track', {id, value})
if (value) {
let resource = Vue.resource(FAVORITES_URL)
resource.save({}, {'track': id}).then((response) => {
return axios.post('favorites/tracks/', {'track': id}).then((response) => {
logger.default.info('Successfully added track to favorites')
}, (response) => {
logger.default.info('Error while adding track to favorites')
commit('track', {id, value: !value})
})
} else {
let resource = Vue.resource(REMOVE_URL)
resource.delete({}, {'track': id}).then((response) => {
return axios.post('favorites/tracks/remove/', {'track': id}).then((response) => {
logger.default.info('Successfully removed track from favorites')
}, (response) => {
logger.default.info('Error while removing track from favorites')
......@@ -57,9 +51,8 @@ export default {
},
fetch ({dispatch, state, commit}, url) {
// will fetch favorites by batches from API to have them locally
url = url || FAVORITES_URL
let resource = Vue.resource(url)
resource.get().then((response) => {
url = url || 'favorites/tracks/'
return axios.get(url).then((response) => {
logger.default.info('Fetched a batch of ' + response.data.results.length + ' favorites')
response.data.results.forEach(result => {
commit('track', {id: result.track, value: true})
......
......@@ -4,6 +4,7 @@ import createPersistedState from 'vuex-persistedstate'
import favorites from './favorites'
import auth from './auth'
import instance from './instance'
import queue from './queue'
import radios from './radios'
import player from './player'
......@@ -14,6 +15,7 @@ export default new Vuex.Store({
modules: {
auth,
favorites,
instance,
queue,
radios,
player
......@@ -37,7 +39,6 @@ export default new Vuex.Store({
key: 'player',
paths: [
'player.looping',
'player.playing',
'player.volume',
'player.duration',
'player.errored'],
......@@ -45,21 +46,6 @@ export default new Vuex.Store({
return mutation.type.startsWith('player/') && mutation.type !== 'player/currentTime'
}
}),
createPersistedState({
key: 'progress',
paths: ['player.currentTime'],
filter: (mutation) => {
let delay = 10
return mutation.type === 'player/currentTime' && parseInt(mutation.payload) % delay === 0
},
reducer: (state) => {
return {
player: {
currentTime: state.player.currentTime
}
}
}
}),
createPersistedState({
key: 'queue',
filter: (mutation) => {
......
import axios from 'axios'
import logger from '@/logging'
import _ from 'lodash'
export default {
namespaced: true,
state: {
settings: {
raven: {
front_enabled: {
value: false
},
front_dsn: {
value: null
}
}
}
},
mutations: {
settings: (state, value) => {
_.merge(state.settings, value)
}
},
actions: {
// Send a request to the login URL and save the returned JWT
fetchSettings ({commit}) {
return axios.get('instance/settings/').then(response => {
logger.default.info('Successfully fetched instance settings')
let sections = {}
response.data.forEach(e => {
sections[e.section] = {}
})
response.data.forEach(e => {
sections[e.section][e.name] = e
})
commit('settings', sections)
}, response => {
logger.default.error('Error while fetching settings', response.data)
})
}
}
}
import Vue from 'vue'
import config from '@/config'
import axios from 'axios'
import logger from '@/logging'
import time from '@/utils/time'
......@@ -61,8 +60,8 @@ export default {
}
},
actions: {
incrementVolume (context, value) {
context.commit('volume', context.state.volume + value)
incrementVolume ({commit, state}, value) {
commit('volume', state.volume + value)
},
stop (context) {
},
......@@ -70,9 +69,7 @@ export default {
commit('playing', !state.playing)
},
trackListened ({commit}, track) {
let url = config.API_URL + 'history/listenings/'
let resource = Vue.resource(url)
resource.save({}, {'track': track.id}).then((response) => {}, (response) => {
return axios.post('history/listenings/', {'track': track.id}).then((response) => {}, (response) => {
logger.default.error('Could not record track in history')
})
},
......
......@@ -55,33 +55,32 @@ export default {
}
},
actions: {
append (context, {track, index, skipPlay}) {
index = index || context.state.tracks.length
if (index > context.state.tracks.length - 1) {
append ({commit, state, dispatch}, {track, index, skipPlay}) {
index = index || state.tracks.length
if (index > state.tracks.length - 1) {
// we simply push to the end
context.commit('insert', {track, index: context.state.tracks.length})
commit('insert', {track, index: state.tracks.length})
} else {
// we insert the track at given position
context.commit('insert', {track, index})
commit('insert', {track, index})
}
if (!skipPlay) {
context.dispatch('resume')
dispatch('resume')
}
// this.cache()
},
appendMany (context, {tracks, index}) {
appendMany ({state, dispatch}, {tracks, index}) {
logger.default.info('Appending many tracks to the queue', tracks.map(e => { return e.title }))
if (context.state.tracks.length === 0) {
if (state.tracks.length === 0) {
index = 0
} else {
index = index || context.state.tracks.length
index = index || state.tracks.length
}
tracks.forEach((t) => {
context.dispatch('append', {track: t, index: index, skipPlay: true})
dispatch('append', {track: t, index: index, skipPlay: true})
index += 1
})
context.dispatch('resume')
dispatch('resume')
},
cleanTrack ({state, dispatch, commit}, index) {
......@@ -100,14 +99,14 @@ export default {
}
},
resume (context) {
if (context.state.ended | context.rootState.player.errored) {
context.dispatch('next')
resume ({state, dispatch, rootState}) {
if (state.ended | rootState.player.errored) {
dispatch('next')
}
},
previous (context) {
if (context.state.currentIndex > 0) {
context.dispatch('currentIndex', context.state.currentIndex - 1)
previous ({state, dispatch}) {
if (state.currentIndex > 0) {
dispatch('currentIndex', state.currentIndex - 1)
}
},
next ({state, dispatch, commit, rootState}) {
......
import Vue from 'vue'
import config from '@/config'
import axios from 'axios'
import logger from '@/logging'
const CREATE_RADIO_URL = config.API_URL + 'radios/sessions/'
const GET_TRACK_URL = config.API_URL + 'radios/tracks/'
export default {
namespaced: true,
state: {
......@@ -39,13 +35,12 @@ export default {
},
actions: {
start ({commit, dispatch}, {type, objectId, customRadioId}) {
let resource = Vue.resource(CREATE_RADIO_URL)
var params = {
radio_type: type,
related_object_id: objectId,
custom_radio: customRadioId
}
resource.save({}, params).then((response) => {
return axios.post('radios/sessions/', params).then((response) => {
logger.default.info('Successfully started radio ', type)
commit('current', {type, objectId, session: response.data.id, customRadioId})
commit('running', true)
......@@ -62,12 +57,10 @@ export default {
if (!state.running) {
return
}
let resource = Vue.resource(GET_TRACK_URL)
var params = {
session: state.current.session
}
let promise = resource.save({}, params)
promise.then((response) => {
return axios.post('radios/tracks/', params).then((response) => {
logger.default.info('Adding track to queue from radio')
dispatch('queue/append', {track: response.data.track}, {root: true})
}, (response) => {
......
@charset "UTF-8";
// _ _ _ _ _
// (_) | | | | | (_)
// _ _ __ ___| |_ _ __| | ___ _ __ ___ ___ __| |_ __ _
// | | '_ \ / __| | | | |/ _` |/ _ \ | '_ ` _ \ / _ \/ _` | |/ _` |
// | | | | | (__| | |_| | (_| | __/ | | | | | | __/ (_| | | (_| |
// |_|_| |_|\___|_|\__,_|\__,_|\___| |_| |_| |_|\___|\__,_|_|\__,_|
//
// Simple, elegant and maintainable media queries in Sass
// v1.4.9
//
// http://include-media.com
//
// Authors: Eduardo Boucas (@eduardoboucas)
// Hugo Giraudel (@hugogiraudel)
//
// This project is licensed under the terms of the MIT license
////
/// include-media library public configuration
/// @author Eduardo Boucas
/// @access public
////
///
/// Creates a list of global breakpoints
///
/// @example scss - Creates a single breakpoint with the label `phone`
/// $breakpoints: ('phone': 320px);
///
$breakpoints: (
'phone': 320px,
'tablet': 768px,
'desktop': 1024px
) !default;
///
/// Creates a list of static expressions or media types
///
/// @example scss - Creates a single media type (screen)
/// $media-expressions: ('screen': 'screen');
///
/// @example scss - Creates a static expression with logical disjunction (OR operator)
/// $media-expressions: (
/// 'retina2x': '(-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi)'
/// );
///
$media-expressions: (
'screen': 'screen',
'print': 'print',
'handheld': 'handheld',
'landscape': '(orientation: landscape)',
'portrait': '(orientation: portrait)',
'retina2x': '(-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi), (min-resolution: 2dppx)',
'retina3x': '(-webkit-min-device-pixel-ratio: 3), (min-resolution: 350dpi), (min-resolution: 3dppx)'
) !default;
///
/// Defines a number to be added or subtracted from each unit when declaring breakpoints with exclusive intervals
///
/// @example scss - Interval for pixels is defined as `1` by default
/// @include media('>128px') {}
///
/// /* Generates: */
/// @media (min-width: 129px) {}
///
/// @example scss - Interval for ems is defined as `0.01` by default
/// @include media('>20em') {}
///
/// /* Generates: */
/// @media (min-width: 20.01em) {}
///
/// @example scss - Interval for rems is defined as `0.1` by default, to be used with `font-size: 62.5%;`
/// @include media('>2.0rem') {}
///
/// /* Generates: */
/// @media (min-width: 2.1rem) {}
///
$unit-intervals: (
'px': 1,
'em': 0.01,
'rem': 0.1,
'': 0
) !default;
///
/// Defines whether support for media queries is available, useful for creating separate stylesheets
/// for browsers that don't support media queries.
///
/// @example scss - Disables support for media queries
/// $im-media-support: false;
/// @include media('>=tablet') {
/// .foo {
/// color: tomato;
/// }
/// }
///
/// /* Generates: */
/// .foo {
/// color: tomato;
/// }
///
$im-media-support: true !default;
///
/// Selects which breakpoint to emulate when support for media queries is disabled. Media queries that start at or
/// intercept the breakpoint will be displayed, any others will be ignored.
///
/// @example scss - This media query will show because it intercepts the static breakpoint
/// $im-media-support: false;
/// $im-no-media-breakpoint: 'desktop';
/// @include media('>=tablet') {
/// .foo {
/// color: tomato;
/// }
/// }
///
/// /* Generates: */
/// .foo {
/// color: tomato;
/// }
///
/// @example scss - This media query will NOT show because it does not intercept the desktop breakpoint
/// $im-media-support: false;
/// $im-no-media-breakpoint: 'tablet';
/// @include media('>=desktop') {
/// .foo {
/// color: tomato;
/// }
/// }
///
/// /* No output */
///
$im-no-media-breakpoint: 'desktop' !default;
///
/// Selects which media expressions are allowed in an expression for it to be used when media queries
/// are not supported.
///
/// @example scss - This media query will show because it intercepts the static breakpoint and contains only accepted media expressions
/// $im-media-support: false;
/// $im-no-media-breakpoint: 'desktop';
/// $im-no-media-expressions: ('screen');
/// @include media('>=tablet', 'screen') {
/// .foo {
/// color: tomato;
/// }
/// }
///
/// /* Generates: */
/// .foo {
/// color: tomato;
/// }
///
/// @example scss - This media query will NOT show because it intercepts the static breakpoint but contains a media expression that is not accepted
/// $im-media-support: false;
/// $im-no-media-breakpoint: 'desktop';
/// $im-no-media-expressions: ('screen');
/// @include media('>=tablet', 'retina2x') {
/// .foo {
/// color: tomato;
/// }
/// }
///
/// /* No output */
///
$im-no-media-expressions: ('screen', 'portrait', 'landscape') !default;
////
/// Cross-engine logging engine
/// @author Hugo Giraudel
/// @access private
////
///
/// Log a message either with `@error` if supported
/// else with `@warn`, using `feature-exists('at-error')`
/// to detect support.
///
/// @param {String} $message - Message to log
///
@function im-log($message) {
@if feature-exists('at-error') {
@error $message;
} @else {
@warn $message;
$_: noop();
}
@return $message;
}
///
/// Wrapper mixin for the log function so it can be used with a more friendly
/// API than `@if im-log('..') {}` or `$_: im-log('..')`. Basically, use the function
/// within functions because it is not possible to include a mixin in a function
/// and use the mixin everywhere else because it's much more elegant.
///
/// @param {String} $message - Message to log
///
@mixin log($message) {
@if im-log($message) {}
}
///
/// Function with no `@return` called next to `@warn` in Sass 3.3
/// to trigger a compiling error and stop the process.
///
@function noop() {}
///
/// Determines whether a list of conditions is intercepted by the static breakpoint.
///
/// @param {Arglist} $conditions - Media query conditions
///
/// @return {Boolean} - Returns true if the conditions are intercepted by the static breakpoint
///
@function im-intercepts-static-breakpoint($conditions...) {
$no-media-breakpoint-value: map-get($breakpoints, $im-no-media-breakpoint);
@if not $no-media-breakpoint-value {
@if im-log('`#{$im-no-media-breakpoint}` is not a valid breakpoint.') {}
}
@each $condition in $conditions {
@if not map-has-key($media-expressions, $condition) {
$operator: get-expression-operator($condition);
$prefix: get-expression-prefix($operator);
$value: get-expression-value($condition, $operator);
@if ($prefix == 'max' and $value <= $no-media-breakpoint-value) or
($prefix == 'min' and $value > $no-media-breakpoint-value) {
@return false;
}
} @else if not index($im-no-media-expressions, $condition) {
@return false;
}
}
@return true;
}
////
/// Parsing engine
/// @author Hugo Giraudel
/// @access private
////
///
/// Get operator of an expression
///
/// @param {String} $expression - Expression to extract operator from
///
/// @return {String} - Any of `>=`, `>`, `<=`, `<`, `≥`, `≤`
///
@function get-expression-operator($expression) {
@each $operator in ('>=', '>', '<=', '<', '≥', '≤') {
@if str-index($expression, $operator) {
@return $operator;
}
}
// It is not possible to include a mixin inside a function, so we have to
// rely on the `im-log(..)` function rather than the `log(..)` mixin. Because
// functions cannot be called anywhere in Sass, we need to hack the call in
// a dummy variable, such as `$_`. If anybody ever raise a scoping issue with
// Sass 3.3, change this line in `@if im-log(..) {}` instead.
$_: im-log('No operator found in `#{$expression}`.');
}
///
/// Get dimension of an expression, based on a found operator
///
/// @param {String} $expression - Expression to extract dimension from
/// @param {String} $operator - Operator from `$expression`
///
/// @return {String} - `width` or `height` (or potentially anything else)
///
@function get-expression-dimension($expression, $operator) {
$operator-index: str-index($expression, $operator);
$parsed-dimension: str-slice($expression, 0, $operator-index - 1);
$dimension: 'width';
@if str-length($parsed-dimension) > 0 {
$dimension: $parsed-dimension;
}
@return $dimension;
}
///
/// Get dimension prefix based on an operator
///
/// @param {String} $operator - Operator
///
/// @return {String} - `min` or `max`
///
@function get-expression-prefix($operator) {
@return if(index(('<', '<=', '≤'), $operator), 'max', 'min');
}
///
/// Get value of an expression, based on a found operator
///
/// @param {String} $expression - Expression to extract value from
/// @param {String} $operator - Operator from `$expression`
///
/// @return {Number} - A numeric value
///
@function get-expression-value($expression, $operator) {
$operator-index: str-index($expression, $operator);
$value: str-slice($expression, $operator-index + str-length($operator));
@if map-has-key($breakpoints, $value) {
$value: map-get($breakpoints, $value);
} @else {
$value: to-number($value);
}
$interval: map-get($unit-intervals, unit($value));
@if not $interval {
// It is not possible to include a mixin inside a function, so we have to
// rely on the `im-log(..)` function rather than the `log(..)` mixin. Because
// functions cannot be called anywhere in Sass, we need to hack the call in
// a dummy variable, such as `$_`. If anybody ever raise a scoping issue with
// Sass 3.3, change this line in `@if im-log(..) {}` instead.
$_: im-log('Unknown unit `#{unit($value)}`.');
}
@if $operator == '>' {
$value: $value + $interval;
} @else if $operator == '<' {