Playback history
This commit is contained in:
@ -8,7 +8,7 @@ from frontend import IrisFrontend
|
|||||||
from http import RequestHandler
|
from http import RequestHandler
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
__version__ = '2.11.3'
|
__version__ = '2.11.4'
|
||||||
|
|
||||||
##
|
##
|
||||||
# Core extension class
|
# Core extension class
|
||||||
|
|||||||
@ -45,6 +45,7 @@ export default class Track extends React.Component{
|
|||||||
var track = this.props.track;
|
var track = this.props.track;
|
||||||
var className = 'list-item track';
|
var className = 'list-item track';
|
||||||
if( typeof(track.selected) !== 'undefined' && track.selected ) className += ' selected';
|
if( typeof(track.selected) !== 'undefined' && track.selected ) className += ' selected';
|
||||||
|
if( typeof(track.type) !== 'undefined' ) className += ' '+track.type;
|
||||||
if( track.playing ) className += ' playing';
|
if( track.playing ) className += ' playing';
|
||||||
|
|
||||||
var album = '-'
|
var album = '-'
|
||||||
@ -56,6 +57,41 @@ export default class Track extends React.Component{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (track.type){
|
||||||
|
|
||||||
|
case 'history':
|
||||||
|
var track_columns = (
|
||||||
|
<span>
|
||||||
|
<span className="col name">
|
||||||
|
{ track.name ? track.name : <span className="grey-text">{track.uri}</span> }
|
||||||
|
</span>
|
||||||
|
<span className="col played_at">
|
||||||
|
{ track.played_at ? <span><Dater type="ago" data={track.played_at} /> ago</span> : null }
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
var track_columns = (
|
||||||
|
<span>
|
||||||
|
<span className="col name">
|
||||||
|
{ track.name ? track.name : <span className="grey-text">{track.uri}</span> }
|
||||||
|
</span>
|
||||||
|
<span className="col artists">
|
||||||
|
{ track.artists ? <ArtistSentence artists={track.artists} /> : '-' }
|
||||||
|
</span>
|
||||||
|
<span className="col album">
|
||||||
|
{album}
|
||||||
|
</span>
|
||||||
|
<span className="col duration">
|
||||||
|
{ track.duration_ms ? <Dater type="length" data={track.duration_ms} /> : null }
|
||||||
|
{ track.length ? <Dater type="length" data={track.length} /> : null }
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={className}
|
className={className}
|
||||||
@ -67,19 +103,7 @@ export default class Track extends React.Component{
|
|||||||
onContextMenu={ e => this.handleContextMenu(e) }>
|
onContextMenu={ e => this.handleContextMenu(e) }>
|
||||||
{ this.props.track.selected ? <FontAwesome name="check" className="select-state" fixedWidth /> : null }
|
{ this.props.track.selected ? <FontAwesome name="check" className="select-state" fixedWidth /> : null }
|
||||||
{ this.props.track.playing ? <FontAwesome name="play" className="play-state" fixedWidth /> : null }
|
{ this.props.track.playing ? <FontAwesome name="play" className="play-state" fixedWidth /> : null }
|
||||||
<span className="col name">
|
{ track_columns }
|
||||||
{ track.name ? track.name : <span className="grey-text">{track.uri}</span> }
|
|
||||||
</span>
|
|
||||||
<span className="col artists">
|
|
||||||
{ track.artists ? <ArtistSentence artists={track.artists} /> : '-' }
|
|
||||||
</span>
|
|
||||||
<span className="col album">
|
|
||||||
{album}
|
|
||||||
</span>
|
|
||||||
<span className="col duration">
|
|
||||||
{ track.duration_ms ? <Dater type="length" data={track.duration_ms} /> : null }
|
|
||||||
{ track.length ? <Dater type="length" data={track.length} /> : null }
|
|
||||||
</span>
|
|
||||||
{ this.props.show_source_icon ? <FontAwesome className="source" name={helpers.sourceIcon(track.uri)} /> : null }
|
{ this.props.show_source_icon ? <FontAwesome className="source" name={helpers.sourceIcon(track.uri)} /> : null }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -257,15 +257,28 @@ class TrackList extends React.Component{
|
|||||||
|
|
||||||
renderHeader(){
|
renderHeader(){
|
||||||
if( this.props.noheader ) return null
|
if( this.props.noheader ) return null
|
||||||
|
|
||||||
return (
|
switch (this.props.context){
|
||||||
<div className="list-item header track">
|
|
||||||
<span className="col name">Name</span>
|
case 'history':
|
||||||
<span className="col artists">Artists</span>
|
return (
|
||||||
<span className="col album">Album</span>
|
<div className="list-item header track">
|
||||||
<span className="col duration">Length</span>
|
<span className="col name">Name</span>
|
||||||
</div>
|
<span className="col played_at">Started playing</span>
|
||||||
)
|
</div>
|
||||||
|
)
|
||||||
|
break
|
||||||
|
|
||||||
|
default:
|
||||||
|
return (
|
||||||
|
<div className="list-item header track">
|
||||||
|
<span className="col name">Name</span>
|
||||||
|
<span className="col artists">Artists</span>
|
||||||
|
<span className="col album">Album</span>
|
||||||
|
<span className="col duration">Length</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render(){
|
render(){
|
||||||
@ -273,7 +286,7 @@ class TrackList extends React.Component{
|
|||||||
|
|
||||||
let self = this;
|
let self = this;
|
||||||
return (
|
return (
|
||||||
<div className="track-list">
|
<div className={this.props.context+" track-list"}>
|
||||||
{ this.renderHeader() }
|
{ this.renderHeader() }
|
||||||
{
|
{
|
||||||
this.state.tracks.map(
|
this.state.tracks.map(
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import Artist from './views/Artist'
|
|||||||
import Playlist from './views/Playlist'
|
import Playlist from './views/Playlist'
|
||||||
import User from './views/User'
|
import User from './views/User'
|
||||||
import Queue from './views/Queue'
|
import Queue from './views/Queue'
|
||||||
|
import QueueHistory from './views/QueueHistory'
|
||||||
import Settings from './views/Settings'
|
import Settings from './views/Settings'
|
||||||
import Debug from './views/Debug'
|
import Debug from './views/Debug'
|
||||||
import Search from './views/Search'
|
import Search from './views/Search'
|
||||||
@ -54,6 +55,7 @@ ReactDOM.render(
|
|||||||
|
|
||||||
<IndexRoute component={Queue} />
|
<IndexRoute component={Queue} />
|
||||||
<Route path="queue" component={Queue} />
|
<Route path="queue" component={Queue} />
|
||||||
|
<Route path="queue-history" component={QueueHistory} />
|
||||||
<Route path="settings" component={Settings} />
|
<Route path="settings" component={Settings} />
|
||||||
<Route path="debug" component={Debug} />
|
<Route path="debug" component={Debug} />
|
||||||
|
|
||||||
|
|||||||
@ -225,4 +225,11 @@ export function getSearchResults(query, uris = null, fields = ['any']){
|
|||||||
fields: fields
|
fields: fields
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getQueueHistory(){
|
||||||
|
return {
|
||||||
|
type: 'MOPIDY_INSTRUCT',
|
||||||
|
call: 'history.getHistory'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -163,7 +163,6 @@ const MopidyMiddleware = (function(){
|
|||||||
instruct( socket, store, action.call, action.value )
|
instruct( socket, store, action.call, action.value )
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// send an instruction to the websocket
|
|
||||||
case 'MOPIDY_DEBUG':
|
case 'MOPIDY_DEBUG':
|
||||||
instruct( socket, store, action.call, action.value )
|
instruct( socket, store, action.call, action.value )
|
||||||
.then( response => {
|
.then( response => {
|
||||||
@ -171,7 +170,29 @@ const MopidyMiddleware = (function(){
|
|||||||
})
|
})
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// send an instruction to the websocket
|
case 'MOPIDY_URISCHEMES':
|
||||||
|
var uri_schemes = action.data
|
||||||
|
var remove = ['http','https','mms','rtmp','rtmps','rtsp','sc','spotify']
|
||||||
|
|
||||||
|
// remove all our ignored types
|
||||||
|
for( var i = 0; i < remove.length; i++ ){
|
||||||
|
var index = uri_schemes.indexOf(remove[i])
|
||||||
|
if( index > -1 ) uri_schemes.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// append with ':' to make them a mopidy URI
|
||||||
|
for( var i = 0; i < uri_schemes.length; i++ ){
|
||||||
|
uri_schemes[i] = uri_schemes[i] +':'
|
||||||
|
}
|
||||||
|
|
||||||
|
store.dispatch({ type: 'MOPIDY_URISCHEMES_FILTERED', data: uri_schemes });
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* General playback
|
||||||
|
**/
|
||||||
|
|
||||||
case 'MOPIDY_NEXT':
|
case 'MOPIDY_NEXT':
|
||||||
var icon = false
|
var icon = false
|
||||||
if( store.getState().ui.current_track ){
|
if( store.getState().ui.current_track ){
|
||||||
@ -212,24 +233,6 @@ const MopidyMiddleware = (function(){
|
|||||||
})
|
})
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'MOPIDY_URISCHEMES':
|
|
||||||
var uri_schemes = action.data
|
|
||||||
var remove = ['http','https','mms','rtmp','rtmps','rtsp','sc','spotify']
|
|
||||||
|
|
||||||
// remove all our ignored types
|
|
||||||
for( var i = 0; i < remove.length; i++ ){
|
|
||||||
var index = uri_schemes.indexOf(remove[i])
|
|
||||||
if( index > -1 ) uri_schemes.splice(index, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// append with ':' to make them a mopidy URI
|
|
||||||
for( var i = 0; i < uri_schemes.length; i++ ){
|
|
||||||
uri_schemes[i] = uri_schemes[i] +':'
|
|
||||||
}
|
|
||||||
|
|
||||||
store.dispatch({ type: 'MOPIDY_URISCHEMES_FILTERED', data: uri_schemes });
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'MOPIDY_ENQUEUE_URIS_NEXT':
|
case 'MOPIDY_ENQUEUE_URIS_NEXT':
|
||||||
|
|
||||||
var current_track = store.getState().ui.current_track
|
var current_track = store.getState().ui.current_track
|
||||||
|
|||||||
@ -62,6 +62,22 @@ export default function reducer(mopidy = {}, action){
|
|||||||
time_position: action.data
|
time_position: action.data
|
||||||
});
|
});
|
||||||
|
|
||||||
|
case 'MOPIDY_HISTORY':
|
||||||
|
var history = []
|
||||||
|
for (var i = 0; i < action.data.length; i++){
|
||||||
|
history.push(Object.assign(
|
||||||
|
{},
|
||||||
|
action.data[i][1],
|
||||||
|
{
|
||||||
|
played_at: action.data[i][0],
|
||||||
|
type: 'history'
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}
|
||||||
|
return Object.assign({}, mopidy, {
|
||||||
|
queue_history: history
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asset-oriented actions
|
* Asset-oriented actions
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
import React, { PropTypes } from 'react'
|
import React, { PropTypes } from 'react'
|
||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
|
import { hashHistory } from 'react-router'
|
||||||
import { bindActionCreators } from 'redux'
|
import { bindActionCreators } from 'redux'
|
||||||
import FontAwesome from 'react-fontawesome'
|
import FontAwesome from 'react-fontawesome'
|
||||||
|
|
||||||
@ -58,14 +59,18 @@ class Queue extends React.Component{
|
|||||||
render(){
|
render(){
|
||||||
var actions = (
|
var actions = (
|
||||||
<span>
|
<span>
|
||||||
<button onClick={() => this.props.uiActions.openModal('edit_radio')}>
|
<button onClick={e => this.props.uiActions.openModal('edit_radio')}>
|
||||||
<FontAwesome name="spotify" />
|
<FontAwesome name="spotify" />
|
||||||
Radio
|
Radio
|
||||||
</button>
|
</button>
|
||||||
<button onClick={() => this.props.mopidyActions.clearTracklist()}>
|
<button onClick={e => this.props.mopidyActions.clearTracklist()}>
|
||||||
<FontAwesome name="trash" />
|
<FontAwesome name="trash" />
|
||||||
Clear
|
Clear
|
||||||
</button>
|
</button>
|
||||||
|
<button onClick={e => hashHistory.push(global.baseURL+'queue-history')}>
|
||||||
|
<FontAwesome name="history" />
|
||||||
|
History
|
||||||
|
</button>
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
88
src/js/views/QueueHistory.js
Executable file
88
src/js/views/QueueHistory.js
Executable file
@ -0,0 +1,88 @@
|
|||||||
|
|
||||||
|
import React, { PropTypes } from 'react'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
import { hashHistory } from 'react-router'
|
||||||
|
import { bindActionCreators } from 'redux'
|
||||||
|
import FontAwesome from 'react-fontawesome'
|
||||||
|
|
||||||
|
import TrackList from '../components/TrackList'
|
||||||
|
import SidebarToggleButton from '../components/SidebarToggleButton'
|
||||||
|
import Header from '../components/Header'
|
||||||
|
|
||||||
|
import * as uiActions from '../services/ui/actions'
|
||||||
|
import * as pusherActions from '../services/pusher/actions'
|
||||||
|
import * as spotifyActions from '../services/spotify/actions'
|
||||||
|
import * as mopidyActions from '../services/mopidy/actions'
|
||||||
|
|
||||||
|
class QueueHistory extends React.Component{
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount(){
|
||||||
|
this.loadHistory()
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps){
|
||||||
|
if( !this.props.mopidy_connected && nextProps.mopidy_connected ){
|
||||||
|
this.loadHistory(nextProps)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loadHistory(props = this.props){
|
||||||
|
if (props.mopidy_connected){
|
||||||
|
this.props.mopidyActions.getQueueHistory()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render(){
|
||||||
|
var actions = (
|
||||||
|
<span>
|
||||||
|
<button onClick={e => hashHistory.push(global.baseURL+'queue')}>
|
||||||
|
<FontAwesome name="reply" />
|
||||||
|
Back
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="view playback-history-view">
|
||||||
|
<Header icon="play" title="Playback history" actions={actions} />
|
||||||
|
|
||||||
|
<section className="list-wrapper">
|
||||||
|
<TrackList
|
||||||
|
show_source_icon={true}
|
||||||
|
context="history"
|
||||||
|
tracks={this.props.queue_history} />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export our component
|
||||||
|
*
|
||||||
|
* We also integrate our global store, using connect()
|
||||||
|
**/
|
||||||
|
|
||||||
|
const mapStateToProps = (state, ownProps) => {
|
||||||
|
return {
|
||||||
|
mopidy_connected: state.mopidy.connected,
|
||||||
|
queue_history: (state.mopidy.queue_history ? state.mopidy.queue_history : [])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => {
|
||||||
|
return {
|
||||||
|
uiActions: bindActionCreators(uiActions, dispatch),
|
||||||
|
pusherActions: bindActionCreators(pusherActions, dispatch),
|
||||||
|
spotifyActions: bindActionCreators(spotifyActions, dispatch),
|
||||||
|
mopidyActions: bindActionCreators(mopidyActions, dispatch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(QueueHistory)
|
||||||
@ -1,154 +1,164 @@
|
|||||||
|
|
||||||
.list-item {
|
.list-wrapper {
|
||||||
|
|
||||||
-webkit-touch-callout: none;
|
.list-item {
|
||||||
-webkit-user-select: none;
|
|
||||||
-khtml-user-select: none;
|
|
||||||
-moz-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
|
|
||||||
display: block;
|
-webkit-touch-callout: none;
|
||||||
position: relative;
|
-webkit-user-select: none;
|
||||||
padding: 9px 10px 9px 20px;
|
-khtml-user-select: none;
|
||||||
border-top: 1px solid $faint_grey;
|
-moz-user-select: none;
|
||||||
border-bottom: 1px solid $faint_grey;
|
-ms-user-select: none;
|
||||||
margin-bottom: -1px;
|
user-select: none;
|
||||||
@include clearfix;
|
|
||||||
|
|
||||||
.source {
|
|
||||||
color: $mid_grey;
|
|
||||||
position: absolute;
|
|
||||||
top: 10px;
|
|
||||||
right: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.selected {
|
|
||||||
background: $yellow !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not(.header):hover {
|
|
||||||
background: $faint_grey;
|
|
||||||
cursor: pointer;
|
|
||||||
border-color: $faint_grey;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.header {
|
|
||||||
font-size: 10px;
|
|
||||||
color: #CCCCCC;
|
|
||||||
text-transform: uppercase;
|
|
||||||
border-top: 0;
|
|
||||||
padding-bottom: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
&.track {
|
|
||||||
padding-left: 40px;
|
|
||||||
|
|
||||||
.col {
|
|
||||||
&.name { width: 40%; }
|
|
||||||
&.artists,
|
|
||||||
&.album { width: 26%; }
|
|
||||||
}
|
|
||||||
|
|
||||||
.select-state {
|
|
||||||
position: absolute;
|
|
||||||
top: 12px;
|
|
||||||
left: 15px;
|
|
||||||
font-size: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.play-state {
|
|
||||||
display: none;
|
|
||||||
position: absolute;
|
|
||||||
top: 13px;
|
|
||||||
left: 15px;
|
|
||||||
font-size: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.playing {
|
|
||||||
font-weight: 600;
|
|
||||||
.play-state {
|
|
||||||
display: block;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.playing.selected {
|
|
||||||
.play-state {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
:root .dragging &:hover {
|
|
||||||
border-top: 3px solid $blue;
|
|
||||||
margin-top: -3px;
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@include responsive( $bp_medium ){
|
|
||||||
padding: 7px 10px 7px 20px;
|
|
||||||
|
|
||||||
.col {
|
|
||||||
&.name { width: 50%; }
|
|
||||||
&.artists { width: 50%; }
|
|
||||||
&.album { width: 50%; padding-left: 50%; color: $mid_grey; }
|
|
||||||
&.duration { display: none; }
|
|
||||||
}
|
|
||||||
|
|
||||||
&.header {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.select-state,
|
|
||||||
.play-state {
|
|
||||||
top: 11px;
|
|
||||||
left: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
&.playlist {
|
|
||||||
|
|
||||||
.col {
|
|
||||||
&.name {
|
|
||||||
width: 30%;
|
|
||||||
}
|
|
||||||
&.owner {
|
|
||||||
width: 20%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.artist-list {
|
|
||||||
.artist {
|
|
||||||
display: block;
|
display: block;
|
||||||
border: 0;
|
position: relative;
|
||||||
margin-bottom: 5px;
|
padding: 9px 10px 9px 20px;
|
||||||
|
border-top: 1px solid $faint_grey;
|
||||||
|
border-bottom: 1px solid $faint_grey;
|
||||||
|
margin-bottom: -1px;
|
||||||
@include clearfix;
|
@include clearfix;
|
||||||
|
|
||||||
.thumbnail {
|
.source {
|
||||||
@include animate();
|
color: $mid_grey;
|
||||||
width: 50px;
|
position: absolute;
|
||||||
margin-right: 15px;
|
top: 10px;
|
||||||
float: left;
|
right: 15px;
|
||||||
border: 3px solid transparent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.name {
|
&.selected {
|
||||||
display: block;
|
background: $yellow !important;
|
||||||
padding-top: 20px;
|
}
|
||||||
|
|
||||||
|
&:not(.header):hover {
|
||||||
|
background: $faint_grey;
|
||||||
|
cursor: pointer;
|
||||||
|
border-color: $faint_grey;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.header {
|
||||||
|
font-size: 10px;
|
||||||
|
color: #CCCCCC;
|
||||||
|
text-transform: uppercase;
|
||||||
|
border-top: 0;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.track {
|
||||||
|
padding-left: 40px;
|
||||||
|
|
||||||
|
.col {
|
||||||
|
&.name { width: 40%; }
|
||||||
|
&.artists,
|
||||||
|
&.album { width: 26%; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-state {
|
||||||
|
position: absolute;
|
||||||
|
top: 12px;
|
||||||
|
left: 15px;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.play-state {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 13px;
|
||||||
|
left: 15px;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.playing {
|
||||||
|
font-weight: 600;
|
||||||
|
.play-state {
|
||||||
|
display: block;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.playing.selected {
|
||||||
|
.play-state {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:root .dragging &:hover {
|
||||||
|
border-top: 3px solid $blue;
|
||||||
|
margin-top: -3px;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include responsive( $bp_medium ){
|
||||||
|
padding: 7px 10px 7px 20px;
|
||||||
|
|
||||||
|
.col {
|
||||||
|
&.name { width: 50%; }
|
||||||
|
&.artists { width: 50%; }
|
||||||
|
&.album { width: 50%; padding-left: 50%; color: $mid_grey; }
|
||||||
|
&.duration { display: none; }
|
||||||
|
}
|
||||||
|
|
||||||
|
&.header {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-state,
|
||||||
|
.play-state {
|
||||||
|
top: 11px;
|
||||||
|
left: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
&.playlist {
|
||||||
|
|
||||||
|
.col {
|
||||||
|
&.name {
|
||||||
|
width: 30%;
|
||||||
|
}
|
||||||
|
&.owner {
|
||||||
|
width: 20%;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
a.artist:hover {
|
.artist-list {
|
||||||
.thumbnail {
|
.artist {
|
||||||
border-color: $turquoise;
|
display: block;
|
||||||
opacity: 0.8;
|
border: 0;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
@include clearfix;
|
||||||
|
|
||||||
|
.thumbnail {
|
||||||
|
@include animate();
|
||||||
|
width: 50px;
|
||||||
|
margin-right: 15px;
|
||||||
|
float: left;
|
||||||
|
border: 3px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
display: block;
|
||||||
|
padding-top: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a.artist:hover {
|
||||||
|
.thumbnail {
|
||||||
|
border-color: $turquoise;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.history.track-list {
|
||||||
|
.list-item {
|
||||||
|
.col{
|
||||||
|
&.name { width: 60%; }
|
||||||
|
&.played_at { width: 20%; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user