Skip to content

Commit 08c66c0

Browse files
committed
Add progress bar for Export All progress
1 parent ed2130e commit 08c66c0

File tree

4 files changed

+120
-51
lines changed

4 files changed

+120
-51
lines changed

src/App.scss

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,6 @@
1515
h1 a { color: black; }
1616
h1 a:hover { color: black; text-decoration: none; }
1717

18-
nav.paginator:nth-child(1) {
19-
margin-top: -74px;
20-
}
21-
2218
table {
2319
float: left;
2420
}
@@ -27,6 +23,28 @@ table {
2723
display: none;
2824
}
2925

26+
#playlistsHeader {
27+
display: flex;
28+
flex-direction: row-reverse;
29+
30+
.progress {
31+
flex-grow: 1;
32+
margin: 20px 20px 20px 0;
33+
height: 30px;
34+
35+
.progress-bar {
36+
white-space: nowrap;
37+
padding: 4px 10px;
38+
text-align: left;
39+
40+
// Transitioning when resetting looks weird
41+
&[aria-valuenow="1"] {
42+
transition: none;
43+
}
44+
}
45+
}
46+
}
47+
3048
@keyframes spinner {
3149
to {transform: rotate(360deg);}
3250
}

src/components/PlaylistTable.jsx

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from "react"
22
import $ from "jquery" // TODO: Remove jQuery dependency
3-
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
3+
import { ProgressBar } from "react-bootstrap"
44

55
import PlaylistRow from "./PlaylistRow"
66
import Paginator from "./Paginator"
@@ -11,10 +11,17 @@ class PlaylistTable extends React.Component {
1111
state = {
1212
playlists: [],
1313
playlistCount: 0,
14-
likedSongsLimit: 0,
15-
likedSongsCount: 0,
14+
likedSongs: {
15+
limit: 0,
16+
count: 0
17+
},
1618
nextURL: null,
17-
prevURL: null
19+
prevURL: null,
20+
progressBar: {
21+
show: false,
22+
label: "",
23+
value: 0
24+
}
1825
}
1926

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

6572
// FIXME: Handle unmounting
6673
this.setState({
67-
likedSongsLimit: likedTracksResponse.limit,
68-
likedSongsCount: likedTracksResponse.total
74+
likedSongs: {
75+
limit: likedTracksResponse.limit,
76+
count: likedTracksResponse.total
77+
}
6978
})
7079
}
7180

@@ -82,19 +91,40 @@ class PlaylistTable extends React.Component {
8291
})
8392
}
8493

85-
exportPlaylists = () => {
86-
PlaylistsExporter.export(this.props.accessToken, this.state.playlistCount, this.state.likedSongsLimit, this.state.likedSongsCount);
94+
handleLoadedPlaylistsCountChanged = (count) => {
95+
this.setState({
96+
progressBar: {
97+
show: true,
98+
label: "Loading playlists...",
99+
value: count
100+
}
101+
})
102+
}
103+
104+
handleExportedPlaylistsCountChanged = (count) => {
105+
this.setState({
106+
progressBar: {
107+
show: true,
108+
label: count >= this.state.playlistCount ? "Done!" : "Exporting tracks...",
109+
value: count
110+
}
111+
})
87112
}
88113

89114
componentDidMount() {
90115
this.loadPlaylists(this.props.url);
91116
}
92117

93118
render() {
94-
if (this.state.playlists.length > 0) {
119+
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} />
120+
121+
if (this.state.playlistCount > 0) {
95122
return (
96123
<div id="playlists">
97-
<Paginator nextURL={this.state.nextURL} prevURL={this.state.prevURL} loadPlaylists={this.loadPlaylists}/>
124+
<div id="playlistsHeader">
125+
<Paginator nextURL={this.state.nextURL} prevURL={this.state.prevURL} loadPlaylists={this.loadPlaylists}/>
126+
{this.state.progressBar.show && progressBar}
127+
</div>
98128
<table className="table table-hover">
99129
<thead>
100130
<tr>
@@ -105,9 +135,13 @@ class PlaylistTable extends React.Component {
105135
<th style={{width: "120px"}}>Public?</th>
106136
<th style={{width: "120px"}}>Collaborative?</th>
107137
<th style={{width: "100px"}} className="text-right">
108-
<button className="btn btn-default btn-xs" type="submit" onClick={this.exportPlaylists}>
109-
<span className="fa fa-file-archive"></span><FontAwesomeIcon icon={['far', 'file-archive']}/> Export All
110-
</button>
138+
<PlaylistsExporter
139+
accessToken={this.props.accessToken}
140+
onLoadedPlaylistsCountChanged={this.handleLoadedPlaylistsCountChanged}
141+
onExportedPlaylistsCountChanged={this.handleExportedPlaylistsCountChanged}
142+
playlistCount={this.state.playlistCount}
143+
likedSongs={this.state.likedSongs}
144+
/>
111145
</th>
112146
</tr>
113147
</thead>

src/components/PlaylistsExporter.jsx

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1+
import React from "react"
2+
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
13
import { saveAs } from "file-saver"
24
import JSZip from "jszip"
35

46
import PlaylistExporter from "./PlaylistExporter"
57
import { apiCall } from "helpers"
68

79
// Handles exporting all playlist data as a zip file
8-
let PlaylistsExporter = {
9-
export: async function(accessToken, playlistCount, likedSongsLimit, likedSongsCount) {
10-
10+
class PlaylistsExporter extends React.Component {
11+
async export(accessToken, playlistCount, likedSongsLimit, likedSongsCount) {
1112
var playlistFileNames = []
1213
var playlistCsvExports = []
1314

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

2526
let playlistPromises = requests.map((request, index) => {
2627
return apiCall(request, accessToken).then((response) => {
28+
this.props.onLoadedPlaylistsCountChanged((index + 1) * limit)
2729
return response
2830
})
2931
})
@@ -45,6 +47,7 @@ let PlaylistsExporter = {
4547
return PlaylistExporter.csvData(accessToken, playlist).then((csvData) => {
4648
playlistFileNames.push(PlaylistExporter.fileName(playlist))
4749
playlistCsvExports.push(csvData)
50+
this.props.onExportedPlaylistsCountChanged(index + 1)
4851
})
4952
})
5053

@@ -61,6 +64,16 @@ let PlaylistsExporter = {
6164
})
6265
})
6366
}
67+
68+
exportPlaylists = () => {
69+
this.export(this.props.accessToken, this.props.playlistCount, this.props.likedSongs.limit, this.props.likedSongs.count)
70+
}
71+
72+
render() {
73+
return <button className="btn btn-default btn-xs" type="submit" onClick={this.exportPlaylists}>
74+
<span className="fa fa-file-archive"></span><FontAwesomeIcon icon={['far', 'file-archive']}/> Export All
75+
</button>
76+
}
6477
}
6578

6679
export default PlaylistsExporter

src/components/__snapshots__/PlaylistTable.test.jsx.snap

Lines changed: 35 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,44 +4,48 @@ exports[`playlist loading 1`] = `
44
<div
55
id="playlists"
66
>
7-
<nav
8-
className="paginator text-right"
7+
<div
8+
id="playlistsHeader"
99
>
10-
<ul
11-
className="pagination pagination-sm"
10+
<nav
11+
className="paginator text-right"
1212
>
13-
<li
14-
className="disabled"
13+
<ul
14+
className="pagination pagination-sm"
1515
>
16-
<a
17-
aria-label="Previous"
18-
href="#"
19-
onClick={[Function]}
16+
<li
17+
className="disabled"
2018
>
21-
<span
22-
aria-hidden="true"
19+
<a
20+
aria-label="Previous"
21+
href="#"
22+
onClick={[Function]}
2323
>
24-
«
25-
</span>
26-
</a>
27-
</li>
28-
<li
29-
className=""
30-
>
31-
<a
32-
aria-label="Next"
33-
href="#"
34-
onClick={[Function]}
24+
<span
25+
aria-hidden="true"
26+
>
27+
«
28+
</span>
29+
</a>
30+
</li>
31+
<li
32+
className=""
3533
>
36-
<span
37-
aria-hidden="true"
34+
<a
35+
aria-label="Next"
36+
href="#"
37+
onClick={[Function]}
3838
>
39-
»
40-
</span>
41-
</a>
42-
</li>
43-
</ul>
44-
</nav>
39+
<span
40+
aria-hidden="true"
41+
>
42+
»
43+
</span>
44+
</a>
45+
</li>
46+
</ul>
47+
</nav>
48+
</div>
4549
<table
4650
className="table table-hover"
4751
>

0 commit comments

Comments
 (0)