Skip to content

Commit

Permalink
Add progress bar for Export All progress
Browse files Browse the repository at this point in the history
  • Loading branch information
watsonbox committed Nov 12, 2020
1 parent ed2130e commit 08c66c0
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 51 deletions.
26 changes: 22 additions & 4 deletions src/App.scss
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@
h1 a { color: black; }
h1 a:hover { color: black; text-decoration: none; }

nav.paginator:nth-child(1) {
margin-top: -74px;
}

table {
float: left;
}
Expand All @@ -27,6 +23,28 @@ table {
display: none;
}

#playlistsHeader {
display: flex;
flex-direction: row-reverse;

.progress {
flex-grow: 1;
margin: 20px 20px 20px 0;
height: 30px;

.progress-bar {
white-space: nowrap;
padding: 4px 10px;
text-align: left;

// Transitioning when resetting looks weird
&[aria-valuenow="1"] {
transition: none;
}
}
}
}

@keyframes spinner {
to {transform: rotate(360deg);}
}
Expand Down
60 changes: 47 additions & 13 deletions src/components/PlaylistTable.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from "react"
import $ from "jquery" // TODO: Remove jQuery dependency
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { ProgressBar } from "react-bootstrap"

import PlaylistRow from "./PlaylistRow"
import Paginator from "./Paginator"
Expand All @@ -11,10 +11,17 @@ class PlaylistTable extends React.Component {
state = {
playlists: [],
playlistCount: 0,
likedSongsLimit: 0,
likedSongsCount: 0,
likedSongs: {
limit: 0,
count: 0
},
nextURL: null,
prevURL: null
prevURL: null,
progressBar: {
show: false,
label: "",
value: 0
}
}

loadPlaylists = (url) => {
Expand Down Expand Up @@ -64,8 +71,10 @@ class PlaylistTable extends React.Component {

// FIXME: Handle unmounting
this.setState({
likedSongsLimit: likedTracksResponse.limit,
likedSongsCount: likedTracksResponse.total
likedSongs: {
limit: likedTracksResponse.limit,
count: likedTracksResponse.total
}
})
}

Expand All @@ -82,19 +91,40 @@ class PlaylistTable extends React.Component {
})
}

exportPlaylists = () => {
PlaylistsExporter.export(this.props.accessToken, this.state.playlistCount, this.state.likedSongsLimit, this.state.likedSongsCount);
handleLoadedPlaylistsCountChanged = (count) => {
this.setState({
progressBar: {
show: true,
label: "Loading playlists...",
value: count
}
})
}

handleExportedPlaylistsCountChanged = (count) => {
this.setState({
progressBar: {
show: true,
label: count >= this.state.playlistCount ? "Done!" : "Exporting tracks...",
value: count
}
})
}

componentDidMount() {
this.loadPlaylists(this.props.url);
}

render() {
if (this.state.playlists.length > 0) {
const progressBar = <ProgressBar striped active={this.state.progressBar.value < this.state.playlistCount} now={this.state.progressBar.value} max={this.state.playlistCount} label={this.state.progressBar.label} />

if (this.state.playlistCount > 0) {
return (
<div id="playlists">
<Paginator nextURL={this.state.nextURL} prevURL={this.state.prevURL} loadPlaylists={this.loadPlaylists}/>
<div id="playlistsHeader">
<Paginator nextURL={this.state.nextURL} prevURL={this.state.prevURL} loadPlaylists={this.loadPlaylists}/>
{this.state.progressBar.show && progressBar}
</div>
<table className="table table-hover">
<thead>
<tr>
Expand All @@ -105,9 +135,13 @@ class PlaylistTable extends React.Component {
<th style={{width: "120px"}}>Public?</th>
<th style={{width: "120px"}}>Collaborative?</th>
<th style={{width: "100px"}} className="text-right">
<button className="btn btn-default btn-xs" type="submit" onClick={this.exportPlaylists}>
<span className="fa fa-file-archive"></span><FontAwesomeIcon icon={['far', 'file-archive']}/> Export All
</button>
<PlaylistsExporter
accessToken={this.props.accessToken}
onLoadedPlaylistsCountChanged={this.handleLoadedPlaylistsCountChanged}
onExportedPlaylistsCountChanged={this.handleExportedPlaylistsCountChanged}
playlistCount={this.state.playlistCount}
likedSongs={this.state.likedSongs}
/>
</th>
</tr>
</thead>
Expand Down
19 changes: 16 additions & 3 deletions src/components/PlaylistsExporter.jsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import React from "react"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { saveAs } from "file-saver"
import JSZip from "jszip"

import PlaylistExporter from "./PlaylistExporter"
import { apiCall } from "helpers"

// Handles exporting all playlist data as a zip file
let PlaylistsExporter = {
export: async function(accessToken, playlistCount, likedSongsLimit, likedSongsCount) {

class PlaylistsExporter extends React.Component {
async export(accessToken, playlistCount, likedSongsLimit, likedSongsCount) {
var playlistFileNames = []
var playlistCsvExports = []

Expand All @@ -24,6 +25,7 @@ let PlaylistsExporter = {

let playlistPromises = requests.map((request, index) => {
return apiCall(request, accessToken).then((response) => {
this.props.onLoadedPlaylistsCountChanged((index + 1) * limit)
return response
})
})
Expand All @@ -45,6 +47,7 @@ let PlaylistsExporter = {
return PlaylistExporter.csvData(accessToken, playlist).then((csvData) => {
playlistFileNames.push(PlaylistExporter.fileName(playlist))
playlistCsvExports.push(csvData)
this.props.onExportedPlaylistsCountChanged(index + 1)
})
})

Expand All @@ -61,6 +64,16 @@ let PlaylistsExporter = {
})
})
}

exportPlaylists = () => {
this.export(this.props.accessToken, this.props.playlistCount, this.props.likedSongs.limit, this.props.likedSongs.count)
}

render() {
return <button className="btn btn-default btn-xs" type="submit" onClick={this.exportPlaylists}>
<span className="fa fa-file-archive"></span><FontAwesomeIcon icon={['far', 'file-archive']}/> Export All
</button>
}
}

export default PlaylistsExporter
66 changes: 35 additions & 31 deletions src/components/__snapshots__/PlaylistTable.test.jsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -4,44 +4,48 @@ exports[`playlist loading 1`] = `
<div
id="playlists"
>
<nav
className="paginator text-right"
<div
id="playlistsHeader"
>
<ul
className="pagination pagination-sm"
<nav
className="paginator text-right"
>
<li
className="disabled"
<ul
className="pagination pagination-sm"
>
<a
aria-label="Previous"
href="#"
onClick={[Function]}
<li
className="disabled"
>
<span
aria-hidden="true"
<a
aria-label="Previous"
href="#"
onClick={[Function]}
>
«
</span>
</a>
</li>
<li
className=""
>
<a
aria-label="Next"
href="#"
onClick={[Function]}
<span
aria-hidden="true"
>
«
</span>
</a>
</li>
<li
className=""
>
<span
aria-hidden="true"
<a
aria-label="Next"
href="#"
onClick={[Function]}
>
»
</span>
</a>
</li>
</ul>
</nav>
<span
aria-hidden="true"
>
»
</span>
</a>
</li>
</ul>
</nav>
</div>
<table
className="table table-hover"
>
Expand Down

0 comments on commit 08c66c0

Please sign in to comment.