Skip to content

Commit

Permalink
Add progress bar (#5)
Browse files Browse the repository at this point in the history
* Add progress bar

* Remove useless setTimeout
  • Loading branch information
cyyynthia authored and Aetheryx committed Nov 29, 2018
1 parent 0672755 commit 782d1b0
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 19 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
.idea
node_modules
src/Aethcord/plugins/stylemanager/styles
!src/Aethcord/plugins/stylemanager/styles/codeblocks.css
!src/Aethcord/plugins/stylemanager/styles/spotifyModal.css
121 changes: 104 additions & 17 deletions src/Aethcord/plugins/spotify/Modal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,24 @@ module.exports = class Modal extends React.Component {
super();

this.state = {
showDurations: false,
currentItem: {
name: '',
artists: [ '' ],
img: '',
url: ''
url: '',
duration: 0
},
progress: 0,
progressAt: new Date().getTime(),
isPlaying: true,
volume: 0,
deviceID: '',

renderInterval: null,
seekListeners: {
seek: null,
stop: null
},
displayState: 'hide'
};
}
Expand All @@ -30,17 +38,21 @@ module.exports = class Modal extends React.Component {
name: playerState.item.name,
artists: playerState.item.artists.map(artist => artist.name),
img: playerState.item.album.images[0].url,
url: playerState.item.external_urls.spotify
url: playerState.item.external_urls.spotify,
duration: playerState.item.duration_ms
},
progress: playerState.progress_ms,
progressAt: new Date().getTime(),
isPlaying: playerState.is_playing,
volume: playerState.device.volume_percent,
deviceID: playerState.device.id,
displayState: 'show',
displayState: 'show'
});
}
}

async componentDidMount () {
this.setState({ renderInterval: setInterval(() => this.forceUpdate(), 1000) });
this.props.main.on('event', async (data) => {
switch (data.type) {
case 'PLAYER_STATE_CHANGED':
Expand All @@ -61,22 +73,35 @@ module.exports = class Modal extends React.Component {
);
}

componentWillUnmount () {
if (this.state.seekListeners.seek !== null) document.removeEventListener('mousemove', this.state.seekListeners.seek);
if (this.state.seekListeners.stop !== null) document.removeEventListener('mouseup', this.state.seekListeners.stop);
if (this.state.renderInterval !== null) clearInterval(this.state.renderInterval)
}

onButtonClick (method, ...args) {
return SpotifyPlayer[method](...args)
.then(() => true)
.then(() => true);
}

render () {
const { currentItem, isPlaying } = this.state;
const artists = concat(currentItem.artists);

let progress = this.state.progress;
if (this.state.isPlaying) progress = this.state.progress + (new Date().getTime() - this.state.progressAt);
const current = progress / this.state.currentItem.duration * 100;
let className = 'container-2Thooq aethcord-spotify';
if (this.state.showDurations || this.state.seekListeners.seek !== null) {
className += ' expend';
}
const style = this.state.displayState === 'hide'
? { opacity: 0, marginBottom: '-300px' }
: {};

return (
<div
className='container-2Thooq'
className={className}
id='aethcord-spotify-modal'
onContextMenu={this.injectContextMenu.bind(this)}
style={style}
Expand All @@ -86,8 +111,8 @@ module.exports = class Modal extends React.Component {
style={{ backgroundImage: `url("${currentItem.img}")` }}
/>
<div className='accountDetails-3k9g4n nameTag-m8r81H'>
<span class="username">{currentItem.name}</span>
<span class="discriminator">{artists ? `by ${artists}` : ''}</span>
<span className="username">{currentItem.name}</span>
<span className="discriminator">{artists ? `by ${artists}` : ''}</span>
</div>

<div className='flex-11O1GKY directionRow-3v3tfG justifyStart-2NDFzi alignStretch-DpGPf3 noWrap-3jynv6'>
Expand All @@ -109,18 +134,80 @@ module.exports = class Modal extends React.Component {
onClick={() => this.onButtonClick('next')}
/>
</div>
<div
className='aethcord-spotify-seek' onMouseEnter={() => this.setState({ showDurations: true })}
onMouseLeave={() => this.setState({ showDurations: false })}
>
<div className='aethcord-spotify-seek-durations'>
<span className='aethcord-spotify-seek-duration'>{this.formatTime(progress)}</span>
<span className='aethcord-spotify-seek-duration'>{this.formatTime(this.state.currentItem.duration)}</span>
</div>
<div className='aethcord-spotify-seek-bar' onMouseDown={(e) => this.startSeek(e)}>
<span className='aethcord-spotify-seek-bar-progress' style={{ width: current + '%' }}/>
<span className='aethcord-spotify-seek-bar-cursor' style={{ left: current + '%' }}/>
</div>
</div>
</div>
);
}

formatTime (time) {
time = Math.round(time / 1000);
let hours = Math.floor(time / 3600) % 24;
let minutes = Math.floor(time / 60) % 60;
let seconds = time % 60;
return [ hours, minutes, seconds ]
.map(v => v < 10 ? '0' + v : v)
.filter((v, i) => v !== '00' || i > 0)
.join(':');
}

startSeek (e) {
SpotifyPlayer.pause();
const seekListener = this.seek.bind(this);
const stopSeekListener = this.endSeek.bind(this);
document.addEventListener('mousemove', seekListener);
document.addEventListener('mouseup', stopSeekListener);
this.setState({
seekListeners: {
seek: seekListener,
stop: stopSeekListener
}
});
this.seek(e)
}

async endSeek () {
document.removeEventListener('mousemove', this.state.seekListeners.seek);
document.removeEventListener('mouseup', this.state.seekListeners.stop);
this.setState({
seekListeners: {
seek: null,
stop: null
}
});

await SpotifyPlayer.seek(this.state.progress);
SpotifyPlayer.play();
}

seek (e) {
const boundingClientRect = document.querySelector('.aethcord-spotify-seek-bar').getBoundingClientRect();
const mouseX = e.clientX;
const delta = mouseX - boundingClientRect.x;
const seek = delta / boundingClientRect.width;
this.setState({ progress: Math.round(this.state.currentItem.duration * seek) });
}

async injectContextMenu (event) {
const { pageX, pageY } = event;

contextMenu.openContextMenu(event, () =>
React.createElement(ContextMenu, {
pageX, pageY,
pageX,
pageY,
itemGroups: [
[{
[ {
type: 'submenu',
name: 'Devices',
getItems: () => SpotifyPlayer.getDevices()
Expand All @@ -134,9 +221,9 @@ module.exports = class Modal extends React.Component {
onClick: () => this.onButtonClick('setActiveDevice', device.id)
}))
)
}],
} ],

[{
[ {
type: 'submenu',
name: 'Playlists',
getItems: () => SpotifyPlayer.getPlaylists()
Expand All @@ -150,9 +237,9 @@ module.exports = class Modal extends React.Component {
})
}))
)
}],
} ],

[{
[ {
type: 'button',
name: 'Send URL to channel',
onClick: () =>
Expand All @@ -165,9 +252,9 @@ module.exports = class Modal extends React.Component {
name: 'Copy URL',
onClick: () =>
clipboard.writeText(this.state.currentItem.url)
}],
} ],

[{
[ {
type: 'slider',
name: 'Volume',
defaultValue: this.state.volume,
Expand All @@ -179,4 +266,4 @@ module.exports = class Modal extends React.Component {
})
);
}
};
};
11 changes: 9 additions & 2 deletions src/Aethcord/plugins/spotify/SpotifyPlayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module.exports = {
accessToken: null,

genericRequest (request) {
request.set('Authorization', `Bearer ${this.accessToken}`)
request.set('Authorization', `Bearer ${this.accessToken}`);

return request
.catch(async (err) => {
Expand All @@ -20,7 +20,7 @@ module.exports = {

console.error(err.body);
throw err;
})
});
},

getPlaylists () {
Expand All @@ -39,6 +39,13 @@ module.exports = {
return this.genericRequest(put(`${this.BASE_PLAYER_URL}/pause`));
},

seek (position) {
return this.genericRequest(
put(`${this.BASE_PLAYER_URL}/seek`)
.query('position_ms', position)
);
},

next () {
return this.genericRequest(post(`${this.BASE_PLAYER_URL}/next`));
},
Expand Down
58 changes: 58 additions & 0 deletions src/Aethcord/plugins/stylemanager/styles/spotifyModal.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
.container-2Thooq {
position: relative;
}

.container-2Thooq .discriminator {
max-width: 84px;
overflow: hidden;
Expand All @@ -18,3 +22,57 @@
#aethcord-spotify-modal {
transition: 0.5s;
}

.aethcord-spotify-seek {
position: absolute;
bottom: -1px;
left: 0;
right: 0;
display: flex;
flex-direction: column;
}

.aethcord-spotify-seek-durations {
transition: opacity .3s;
opacity: 0;
height: 15px;
display: flex;
font-size: 12px;
padding-left: 5px;
padding-right: 5px;
justify-content: space-between;
}

.aethcord-spotify-seek-bar {
cursor: pointer;
height: 2px;
}

.aethcord-spotify-seek-bar-progress {
height: 2px;
display: block;
background-color: #1ed860;
}

.aethcord-spotify-seek-bar-cursor {
transition: opacity .3s;
background-color: #fff;
position: absolute;
bottom: -3px;
opacity: 0;
z-index: 6;
width: 8px;
height: 8px;
border-radius: 50%;
transform: translateX(-50%);
}

.aethcord-spotify.expend {
padding-bottom: 12px;
}

.aethcord-spotify.expend .aethcord-spotify-seek-durations,
.aethcord-spotify.expend .aethcord-spotify-seek-bar-cursor
{
opacity: 1;
}

0 comments on commit 782d1b0

Please sign in to comment.